2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 1999
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "system/passwd.h"
25 #include <asm/types.h>
26 #include <linux/smb_fs.h>
28 extern BOOL in_client;
30 static pstring credentials;
31 static pstring my_netbios_name;
32 static pstring password;
33 static pstring username;
34 static pstring workgroup;
35 static pstring mpoint;
36 static pstring service;
37 static pstring options;
39 static struct ipv4_addr dest_ip;
41 static int smb_port = 0;
44 static uid_t mount_uid;
45 static gid_t mount_gid;
47 static uint_t mount_fmask;
48 static uint_t mount_dmask;
49 static BOOL use_kerberos;
50 /* TODO: Add code to detect smbfs version in kernel */
51 static BOOL status32_smbfs = False;
53 static void usage(void);
55 static void exit_parent(int sig)
57 /* parent simply exits when child says go... */
61 static void daemonize(void)
66 signal( SIGTERM, exit_parent );
68 if ((child_pid = sys_fork()) < 0) {
69 DEBUG(0,("could not fork\n"));
74 j = waitpid( child_pid, &status, 0 );
76 if( EINTR == errno ) {
84 /* If we get here - the child exited with some error status */
85 if (WIFSIGNALED(status))
86 exit(128 + WTERMSIG(status));
88 exit(WEXITSTATUS(status));
91 signal( SIGTERM, SIG_DFL );
95 static void close_our_files(int client_fd)
100 getrlimit(RLIMIT_NOFILE,&limits);
101 for (i = 0; i< limits.rlim_max; i++) {
108 static void usr1_handler(int x)
114 /*****************************************************
115 return a connection to a server
116 *******************************************************/
117 static struct smbcli_state *do_connection(char *the_service)
119 struct smbcli_state *c;
120 struct nmb_name called, calling;
126 if (the_service[0] != '\\' || the_service[1] != '\\') {
131 pstrcpy(server, the_service+2);
132 share = strchr_m(server,'\\');
142 make_nmb_name(&calling, my_netbios_name, 0x0);
143 choose_called_name(&called, server, 0x20);
147 if (have_ip) ip = dest_ip;
149 /* have to open a new connection */
150 if (!(c=smbcli_initialise(NULL)) || (smbcli_set_port(c, smb_port) != smb_port) ||
151 !smbcli_connect(c, server_n, &ip)) {
152 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
159 /* SPNEGO doesn't work till we get NTSTATUS error support */
160 /* But it is REQUIRED for kerberos authentication */
161 if(!use_kerberos) c->use_spnego = False;
163 /* The kernel doesn't yet know how to sign it's packets */
164 c->sign_info.allow_smb_signing = False;
166 /* Use kerberos authentication if specified */
167 c->use_kerberos = use_kerberos;
169 if (!smbcli_session_request(c, &calling, &called)) {
171 DEBUG(0,("%d: session request to %s failed (%s)\n",
172 sys_getpid(), called.name, smbcli_errstr(c)));
174 if ((p=strchr_m(called.name, '.'))) {
178 if (strcmp(called.name, "*SMBSERVER")) {
179 make_nmb_name(&called , "*SMBSERVER", 0x20);
185 DEBUG(4,("%d: session request ok\n", sys_getpid()));
187 if (!smbcli_negprot(c)) {
188 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
194 char *pass = getpass("Password: ");
196 pstrcpy(password, pass);
200 /* This should be right for current smbfs. Future versions will support
201 large files as well as unicode and oplocks. */
202 if (status32_smbfs) {
203 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
204 CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
207 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
208 CAP_NT_FIND | CAP_STATUS32 |
209 CAP_LEVEL_II_OPLOCKS);
210 c->force_dos_errors = True;
213 if (!smbcli_session_setup(c, username,
214 password, strlen(password),
215 password, strlen(password),
217 /* if a password was not supplied then try again with a
219 if (password[0] || !username[0] ||
220 !smbcli_session_setup(c, "", "", 0, "", 0, workgroup)) {
221 DEBUG(0,("%d: session setup failed: %s\n",
222 sys_getpid(), smbcli_errstr(c)));
226 DEBUG(0,("Anonymous login successful\n"));
229 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
231 if (!smbcli_send_tconX(c, share, "?????",
232 password, strlen(password)+1)) {
233 DEBUG(0,("%d: tree connect failed: %s\n",
234 sys_getpid(), smbcli_errstr(c)));
239 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
247 /****************************************************************************
248 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
249 Code blatently stolen from smbumount.c
251 ****************************************************************************/
252 static void smb_umount(char *mount_point)
260 This routine only gets called to the scene of a disaster
261 to shoot the survivors... A connection that was working
262 has now apparently failed. We have an active mount point
263 (presumably) that we need to dump. If we get errors along
264 the way - make some noise, but we are already turning out
265 the lights to exit anyways...
267 if (umount(mount_point) != 0) {
268 DEBUG(0,("%d: Could not umount %s: %s\n",
269 sys_getpid(), mount_point, strerror(errno)));
273 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
274 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
280 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
281 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
282 sys_getpid(), strerror(errno)));
286 #define MOUNTED_TMP MOUNTED".tmp"
288 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
289 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
290 sys_getpid(), strerror(errno)));
295 while ((mnt = getmntent(mtab)) != NULL) {
296 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
297 addmntent(new_mtab, mnt);
303 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
304 DEBUG(0,("%d: Error changing mode of %s: %s\n",
305 sys_getpid(), MOUNTED_TMP, strerror(errno)));
311 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
312 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
313 sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
317 if (unlink(MOUNTED"~") == -1) {
318 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
325 * Call the smbfs ioctl to install a connection socket,
326 * then wait for a signal to reconnect. Note that we do
327 * not exit after open_sockets() or send_login() errors,
328 * as the smbfs mount would then have no way to recover.
330 static void send_fs_socket(char *the_service, char *mount_point, struct smbcli_state *c)
332 int fd, closed = 0, res = 1;
333 pid_t parentpid = getppid();
334 struct smb_conn_opt conn_options;
336 memset(&conn_options, 0, sizeof(conn_options));
339 if ((fd = open(mount_point, O_RDONLY)) < 0) {
340 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
341 sys_getpid(), mount_point));
345 conn_options.fd = c->fd;
346 conn_options.protocol = c->protocol;
347 conn_options.case_handling = SMB_CASE_DEFAULT;
348 conn_options.max_xmit = c->max_xmit;
349 conn_options.server_uid = c->vuid;
350 conn_options.tid = c->cnum;
351 conn_options.secmode = c->sec_mode;
352 conn_options.rawmode = 0;
353 conn_options.sesskey = c->sesskey;
354 conn_options.maxraw = 0;
355 conn_options.capabilities = c->capabilities;
356 conn_options.serverzone = c->serverzone/60;
358 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
360 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
367 /* Ok... We are going to kill the parent. Now
368 is the time to break the process group... */
370 /* Send a signal to the parent to terminate */
371 kill(parentpid, SIGTERM);
377 /* This looks wierd but we are only closing the userspace
378 side, the connection has already been passed to smbfs and
379 it has increased the usage count on the socket.
381 If we don't do this we will "leak" sockets and memory on
382 each reconnection we have to make. */
387 /* redirect stdout & stderr since we can't know that
388 the library functions we use are using DEBUG. */
389 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
390 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
393 dup2(fd, STDOUT_FILENO);
394 dup2(fd, STDERR_FILENO);
398 /* here we are no longer interactive */
399 set_remote_machine_name("smbmount"); /* sneaky ... */
400 setup_logging("mount.smbfs", DEBUG_STDERR);
402 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
407 /* Wait for a signal from smbfs ... but don't continue
408 until we actually get a new connection. */
410 CatchSignal(SIGUSR1, &usr1_handler);
412 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
413 c = do_connection(the_service);
417 smb_umount(mount_point);
418 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
426 static void init_mount(void)
428 char mount_point[MAXPATHLEN+1];
431 struct smbcli_state *c;
435 if (realpath(mpoint, mount_point) == NULL) {
436 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
441 c = do_connection(service);
443 fprintf(stderr,"SMB connection failed\n");
448 Set up to return as a daemon child and wait in the parent
449 until the child say it's ready...
453 pstrcpy(svc2, service);
454 string_replace(svc2, '\\','/');
455 string_replace(svc2, ' ','_');
457 memset(args, 0, sizeof(args[0])*20);
460 args[i++] = "smbmnt";
462 args[i++] = mount_point;
470 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
472 args[i++] = smb_xstrdup(tmp);
475 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
477 args[i++] = smb_xstrdup(tmp);
480 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
482 args[i++] = smb_xstrdup(tmp);
485 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
487 args[i++] = smb_xstrdup(tmp);
494 if (sys_fork() == 0) {
497 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
499 if (file_exist(smbmnt_path, NULL)) {
500 execv(smbmnt_path, args);
502 "smbfs/init_mount: execv of %s failed. Error was %s.",
503 smbmnt_path, strerror(errno));
505 execvp("smbmnt", args);
507 "smbfs/init_mount: execv of %s failed. Error was %s.",
508 "smbmnt", strerror(errno));
514 if (waitpid(-1, &status, 0) == -1) {
515 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
516 /* FIXME: do some proper error handling */
520 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
521 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
522 /* FIXME: do some proper error handling */
524 } else if (WIFSIGNALED(status)) {
525 fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
529 /* Ok... This is the rubicon for that mount point... At any point
530 after this, if the connections fail and can not be reconstructed
531 for any reason, we will have to unmount the mount point. There
532 is no exit from the next call...
534 send_fs_socket(service, mount_point, c);
538 /****************************************************************************
539 get a password from a a file or file descriptor
540 exit on failure (from smbclient, move to libsmb or shared .c file?)
541 ****************************************************************************/
542 static void get_password_file(void)
546 BOOL close_it = False;
550 if ((p = getenv("PASSWD_FD")) != NULL) {
551 pstrcpy(spec, "descriptor ");
553 sscanf(p, "%d", &fd);
555 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
556 fd = open(p, O_RDONLY, 0);
559 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
560 spec, strerror(errno));
566 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
567 p && p - pass < sizeof(pass);) {
568 switch (read(fd, p, 1)) {
570 if (*p != '\n' && *p != '\0') {
571 *++p = '\0'; /* advance p, and null-terminate pass */
576 *p = '\0'; /* null-terminate it, just in case... */
577 p = NULL; /* then force the loop condition to become false */
580 fprintf(stderr, "Error reading password from file %s: %s\n",
581 spec, "empty password\n");
586 fprintf(stderr, "Error reading password from file %s: %s\n",
587 spec, strerror(errno));
591 pstrcpy(password, pass);
596 /****************************************************************************
597 get username and password from a credentials file
598 exit on failure (from smbclient, move to libsmb or shared .c file?)
599 ****************************************************************************/
600 static void read_credentials_file(char *filename)
605 char *ptr, *val, *param;
607 if ((auth=sys_fopen(filename, "r")) == NULL)
609 /* fail if we can't open the credentials file */
610 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
616 /* get a line from the file */
617 if (!fgets (buf, sizeof(buf), auth))
621 if ((len) && (buf[len-1]=='\n'))
629 /* break up the line into parameter & value.
630 will need to eat a little whitespace possibly */
632 if (!(ptr = strchr (buf, '=')))
637 /* eat leading white space */
638 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
641 if (strwicmp("password", param) == 0)
643 pstrcpy(password, val);
646 else if (strwicmp("username", param) == 0) {
647 pstrcpy(username, val);
650 memset(buf, 0, sizeof(buf));
656 /****************************************************************************
658 ****************************************************************************/
659 static void usage(void)
661 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
663 printf("Version %s\n\n",VERSION);
667 username=<arg> SMB username\n\
668 password=<arg> SMB password\n\
669 credentials=<filename> file with username/password\n\
670 krb use kerberos (active directory)\n\
671 netbiosname=<arg> source NetBIOS name\n\
672 uid=<arg> mount uid or username\n\
673 gid=<arg> mount gid or groupname\n\
674 port=<arg> remote SMB port number\n\
675 fmask=<arg> file umask\n\
676 dmask=<arg> directory umask\n\
677 debug=<arg> debug level\n\
678 ip=<arg> destination host or IP address\n\
679 workgroup=<arg> workgroup on destination\n\
680 sockopt=<arg> TCP socket options\n\
681 scope=<arg> NetBIOS scope\n\
682 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
683 codepage=<arg> server codepage (cp850)\n\
684 ttl=<arg> dircache time to live\n\
685 guest don't prompt for a password\n\
686 ro mount read-only\n\
687 rw mount read-write\n\
689 This command is designed to be run from within /bin/mount by giving\n\
690 the option '-t smbfs'. For example:\n\
691 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
696 /****************************************************************************
697 Argument parsing for mount.smbfs interface
698 mount will call us like this:
699 mount.smbfs device mountpoint -o <options>
701 <options> is never empty, containing at least rw or ro
702 ****************************************************************************/
703 static void parse_mount_smb(int argc, char **argv)
712 /* FIXME: This function can silently fail if the arguments are
713 * not in the expected order.
715 > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
716 > requires that one gives "-o" before further options like username=...
717 > . Without -o, the username=.. setting is *silently* ignored. I've
718 > spent about an hour trying to find out why I couldn't log in now..
723 if (argc < 2 || argv[1][0] == '-') {
728 pstrcpy(service, argv[1]);
729 pstrcpy(mpoint, argv[2]);
731 /* Convert any '/' characters in the service name to
733 string_replace(service, '/','\\');
737 opt = getopt(argc, argv, "o:");
746 * option parsing from nfsmount.c (util-linux-2.9u)
748 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
749 DEBUG(3, ("opts: %s\n", opts));
750 if ((opteq = strchr_m(opts, '='))) {
751 val = atoi(opteq + 1);
754 if (!strcmp(opts, "username") ||
755 !strcmp(opts, "logon")) {
758 pstrcpy(username,opteq+1);
759 if ((lp=strchr_m(username,'%'))) {
761 pstrcpy(password,lp+1);
763 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
765 if ((lp=strchr_m(username,'/'))) {
767 pstrcpy(workgroup,lp+1);
769 } else if(!strcmp(opts, "passwd") ||
770 !strcmp(opts, "password")) {
771 pstrcpy(password,opteq+1);
773 memset(opteq+1,'X',strlen(password));
774 } else if(!strcmp(opts, "credentials")) {
775 pstrcpy(credentials,opteq+1);
776 } else if(!strcmp(opts, "netbiosname")) {
777 pstrcpy(my_netbios_name,opteq+1);
778 } else if(!strcmp(opts, "uid")) {
779 mount_uid = nametouid(opteq+1);
780 } else if(!strcmp(opts, "gid")) {
781 mount_gid = nametogid(opteq+1);
782 } else if(!strcmp(opts, "port")) {
784 } else if(!strcmp(opts, "fmask")) {
785 mount_fmask = strtol(opteq+1, NULL, 8);
786 } else if(!strcmp(opts, "dmask")) {
787 mount_dmask = strtol(opteq+1, NULL, 8);
788 } else if(!strcmp(opts, "debug")) {
790 } else if(!strcmp(opts, "ip")) {
791 dest_ip = interpret_addr2(opteq+1);
792 if (is_zero_ip(dest_ip)) {
793 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
797 } else if(!strcmp(opts, "workgroup")) {
798 pstrcpy(workgroup,opteq+1);
799 } else if(!strcmp(opts, "sockopt")) {
800 lp_set_cmdline("socket options", opteq+1);
801 } else if(!strcmp(opts, "scope")) {
802 lp_set_cmdline("netbios scope", opteq+1);
804 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
809 if(!strcmp(opts, "nocaps")) {
810 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
812 } else if(!strcmp(opts, "guest")) {
815 } else if(!strcmp(opts, "krb")) {
820 fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
822 fprintf(stderr,"No kerberos support compiled in\n");
825 } else if(!strcmp(opts, "rw")) {
827 } else if(!strcmp(opts, "ro")) {
830 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
844 *(p-1) = 0; /* remove trailing , */
845 DEBUG(3,("passthrough options '%s'\n", options));
849 /****************************************************************************
851 ****************************************************************************/
852 int main(int argc,char *argv[])
860 /* here we are interactive, even if run from autofs */
861 setup_logging("mount.smbfs",DEBUG_STDERR);
863 #if 0 /* JRA - Urban says not needed ? */
864 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
865 is to not announce any unicode capabilities as current smbfs does
867 p = getenv("CLI_FORCE_ASCII");
868 if (p && !strcmp(p, "false"))
869 unsetenv("CLI_FORCE_ASCII");
871 setenv("CLI_FORCE_ASCII", "true", 1);
874 in_client = True; /* Make sure that we tell lp_load we are */
876 if (getenv("USER")) {
877 pstrcpy(username,getenv("USER"));
879 if ((p=strchr_m(username,'%'))) {
881 pstrcpy(password,p+1);
883 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
888 if (getenv("PASSWD")) {
889 pstrcpy(password,getenv("PASSWD"));
893 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
898 if (*username == 0 && getenv("LOGNAME")) {
899 pstrcpy(username,getenv("LOGNAME"));
902 if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
903 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
907 parse_mount_smb(argc, argv);
909 if (use_kerberos && !got_user) {
913 if (*credentials != 0) {
914 read_credentials_file(credentials);
917 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
919 if (*workgroup == 0) {
920 pstrcpy(workgroup,lp_workgroup());
924 if (!*my_netbios_name) {
925 pstrcpy(my_netbios_name, myhostname());
927 strupper(my_netbios_name);