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.
24 #include <asm/types.h>
25 #include <linux/smb_fs.h>
27 extern BOOL in_client;
29 static pstring credentials;
30 static pstring my_netbios_name;
31 static pstring password;
32 static pstring username;
33 static pstring workgroup;
34 static pstring mpoint;
35 static pstring service;
36 static pstring options;
38 static struct in_addr dest_ip;
40 static int smb_port = 0;
43 static uid_t mount_uid;
44 static gid_t mount_gid;
46 static unsigned mount_fmask;
47 static unsigned mount_dmask;
48 static BOOL use_kerberos;
49 /* TODO: Add code to detect smbfs version in kernel */
50 static BOOL status32_smbfs = False;
52 static void usage(void);
54 static void exit_parent(int sig)
56 /* parent simply exits when child says go... */
60 static void daemonize(void)
65 signal( SIGTERM, exit_parent );
67 if ((child_pid = sys_fork()) < 0) {
68 DEBUG(0,("could not fork\n"));
73 j = waitpid( child_pid, &status, 0 );
75 if( EINTR == errno ) {
83 /* If we get here - the child exited with some error status */
84 if (WIFSIGNALED(status))
85 exit(128 + WTERMSIG(status));
87 exit(WEXITSTATUS(status));
90 signal( SIGTERM, SIG_DFL );
94 static void close_our_files(int client_fd)
99 getrlimit(RLIMIT_NOFILE,&limits);
100 for (i = 0; i< limits.rlim_max; i++) {
107 static void usr1_handler(int x)
113 /*****************************************************
114 return a connection to a server
115 *******************************************************/
116 static struct cli_state *do_connection(char *the_service)
119 struct nmb_name called, calling;
125 if (the_service[0] != '\\' || the_service[1] != '\\') {
130 pstrcpy(server, the_service+2);
131 share = strchr_m(server,'\\');
141 make_nmb_name(&calling, my_netbios_name, 0x0);
142 make_nmb_name(&called , server, 0x20);
146 if (have_ip) ip = dest_ip;
148 /* have to open a new connection */
149 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
150 !cli_connect(c, server_n, &ip)) {
151 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
158 /* SPNEGO doesn't work till we get NTSTATUS error support */
159 /* But it is REQUIRED for kerberos authentication */
160 if(!use_kerberos) c->use_spnego = False;
162 /* The kernel doesn't yet know how to sign it's packets */
163 c->sign_info.allow_smb_signing = False;
165 /* Use kerberos authentication if specified */
166 c->use_kerberos = use_kerberos;
168 if (!cli_session_request(c, &calling, &called)) {
170 DEBUG(0,("%d: session request to %s failed (%s)\n",
171 sys_getpid(), called.name, cli_errstr(c)));
173 if ((p=strchr_m(called.name, '.'))) {
177 if (strcmp(called.name, "*SMBSERVER")) {
178 make_nmb_name(&called , "*SMBSERVER", 0x20);
184 DEBUG(4,("%d: session request ok\n", sys_getpid()));
186 if (!cli_negprot(c)) {
187 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
193 char *pass = getpass("Password: ");
195 pstrcpy(password, pass);
199 /* This should be right for current smbfs. Future versions will support
200 large files as well as unicode and oplocks. */
201 if (status32_smbfs) {
202 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
203 CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
206 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
207 CAP_NT_FIND | CAP_STATUS32 |
208 CAP_LEVEL_II_OPLOCKS);
209 c->force_dos_errors = True;
212 if (!cli_session_setup(c, username,
213 password, strlen(password),
214 password, strlen(password),
216 /* if a password was not supplied then try again with a
218 if (password[0] || !username[0] ||
219 !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
220 DEBUG(0,("%d: session setup failed: %s\n",
221 sys_getpid(), cli_errstr(c)));
225 DEBUG(0,("Anonymous login successful\n"));
228 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
230 if (!cli_send_tconX(c, share, "?????",
231 password, strlen(password)+1)) {
232 DEBUG(0,("%d: tree connect failed: %s\n",
233 sys_getpid(), cli_errstr(c)));
238 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
246 /****************************************************************************
247 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
248 Code blatently stolen from smbumount.c
250 ****************************************************************************/
251 static void smb_umount(char *mount_point)
259 This routine only gets called to the scene of a disaster
260 to shoot the survivors... A connection that was working
261 has now apparently failed. We have an active mount point
262 (presumably) that we need to dump. If we get errors along
263 the way - make some noise, but we are already turning out
264 the lights to exit anyways...
266 if (umount(mount_point) != 0) {
267 DEBUG(0,("%d: Could not umount %s: %s\n",
268 sys_getpid(), mount_point, strerror(errno)));
272 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
273 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
279 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
280 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
281 sys_getpid(), strerror(errno)));
285 #define MOUNTED_TMP MOUNTED".tmp"
287 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
288 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
289 sys_getpid(), strerror(errno)));
294 while ((mnt = getmntent(mtab)) != NULL) {
295 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
296 addmntent(new_mtab, mnt);
302 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
303 DEBUG(0,("%d: Error changing mode of %s: %s\n",
304 sys_getpid(), MOUNTED_TMP, strerror(errno)));
310 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
311 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
312 sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
316 if (unlink(MOUNTED"~") == -1) {
317 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
324 * Call the smbfs ioctl to install a connection socket,
325 * then wait for a signal to reconnect. Note that we do
326 * not exit after open_sockets() or send_login() errors,
327 * as the smbfs mount would then have no way to recover.
329 static void send_fs_socket(char *the_service, char *mount_point, struct cli_state *c)
331 int fd, closed = 0, res = 1;
332 pid_t parentpid = getppid();
333 struct smb_conn_opt conn_options;
335 memset(&conn_options, 0, sizeof(conn_options));
338 if ((fd = open(mount_point, O_RDONLY)) < 0) {
339 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
340 sys_getpid(), mount_point));
344 conn_options.fd = c->fd;
345 conn_options.protocol = c->protocol;
346 conn_options.case_handling = SMB_CASE_DEFAULT;
347 conn_options.max_xmit = c->max_xmit;
348 conn_options.server_uid = c->vuid;
349 conn_options.tid = c->cnum;
350 conn_options.secmode = c->sec_mode;
351 conn_options.rawmode = 0;
352 conn_options.sesskey = c->sesskey;
353 conn_options.maxraw = 0;
354 conn_options.capabilities = c->capabilities;
355 conn_options.serverzone = c->serverzone/60;
357 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
359 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
366 /* Ok... We are going to kill the parent. Now
367 is the time to break the process group... */
369 /* Send a signal to the parent to terminate */
370 kill(parentpid, SIGTERM);
376 /* This looks wierd but we are only closing the userspace
377 side, the connection has already been passed to smbfs and
378 it has increased the usage count on the socket.
380 If we don't do this we will "leak" sockets and memory on
381 each reconnection we have to make. */
386 /* redirect stdout & stderr since we can't know that
387 the library functions we use are using DEBUG. */
388 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
389 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
392 dup2(fd, STDOUT_FILENO);
393 dup2(fd, STDERR_FILENO);
397 /* here we are no longer interactive */
398 set_remote_machine_name("smbmount"); /* sneaky ... */
399 setup_logging("mount.smbfs", DEBUG_STDERR);
401 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
406 /* Wait for a signal from smbfs ... but don't continue
407 until we actually get a new connection. */
409 CatchSignal(SIGUSR1, &usr1_handler);
411 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
412 c = do_connection(the_service);
416 smb_umount(mount_point);
417 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
425 static void init_mount(void)
427 char mount_point[MAXPATHLEN+1];
434 if (realpath(mpoint, mount_point) == NULL) {
435 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
440 c = do_connection(service);
442 fprintf(stderr,"SMB connection failed\n");
447 Set up to return as a daemon child and wait in the parent
448 until the child say it's ready...
452 pstrcpy(svc2, service);
453 string_replace(svc2, '\\','/');
454 string_replace(svc2, ' ','_');
456 memset(args, 0, sizeof(args[0])*20);
459 args[i++] = "smbmnt";
461 args[i++] = mount_point;
469 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
471 args[i++] = smb_xstrdup(tmp);
474 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
476 args[i++] = smb_xstrdup(tmp);
479 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
481 args[i++] = smb_xstrdup(tmp);
484 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
486 args[i++] = smb_xstrdup(tmp);
493 if (sys_fork() == 0) {
496 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
498 if (file_exist(smbmnt_path, NULL)) {
499 execv(smbmnt_path, args);
501 "smbfs/init_mount: execv of %s failed. Error was %s.",
502 smbmnt_path, strerror(errno));
504 execvp("smbmnt", args);
506 "smbfs/init_mount: execv of %s failed. Error was %s.",
507 "smbmnt", strerror(errno));
513 if (waitpid(-1, &status, 0) == -1) {
514 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
515 /* FIXME: do some proper error handling */
519 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
520 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
521 /* FIXME: do some proper error handling */
523 } else if (WIFSIGNALED(status)) {
524 fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
528 /* Ok... This is the rubicon for that mount point... At any point
529 after this, if the connections fail and can not be reconstructed
530 for any reason, we will have to unmount the mount point. There
531 is no exit from the next call...
533 send_fs_socket(service, mount_point, c);
537 /****************************************************************************
538 get a password from a a file or file descriptor
539 exit on failure (from smbclient, move to libsmb or shared .c file?)
540 ****************************************************************************/
541 static void get_password_file(void)
545 BOOL close_it = False;
549 if ((p = getenv("PASSWD_FD")) != NULL) {
550 pstrcpy(spec, "descriptor ");
552 sscanf(p, "%d", &fd);
554 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
555 fd = sys_open(p, O_RDONLY, 0);
558 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
559 spec, strerror(errno));
565 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
566 p && p - pass < sizeof(pass);) {
567 switch (read(fd, p, 1)) {
569 if (*p != '\n' && *p != '\0') {
570 *++p = '\0'; /* advance p, and null-terminate pass */
575 *p = '\0'; /* null-terminate it, just in case... */
576 p = NULL; /* then force the loop condition to become false */
579 fprintf(stderr, "Error reading password from file %s: %s\n",
580 spec, "empty password\n");
585 fprintf(stderr, "Error reading password from file %s: %s\n",
586 spec, strerror(errno));
590 pstrcpy(password, pass);
595 /****************************************************************************
596 get username and password from a credentials file
597 exit on failure (from smbclient, move to libsmb or shared .c file?)
598 ****************************************************************************/
599 static void read_credentials_file(char *filename)
604 char *ptr, *val, *param;
606 if ((auth=sys_fopen(filename, "r")) == NULL)
608 /* fail if we can't open the credentials file */
609 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
615 /* get a line from the file */
616 if (!fgets (buf, sizeof(buf), auth))
620 if ((len) && (buf[len-1]=='\n'))
628 /* break up the line into parameter & value.
629 will need to eat a little whitespace possibly */
631 if (!(ptr = strchr (buf, '=')))
636 /* eat leading white space */
637 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
640 if (strwicmp("password", param) == 0)
642 pstrcpy(password, val);
645 else if (strwicmp("username", param) == 0) {
646 pstrcpy(username, val);
649 memset(buf, 0, sizeof(buf));
655 /****************************************************************************
657 ****************************************************************************/
658 static void usage(void)
660 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
662 printf("Version %s\n\n",VERSION);
666 username=<arg> SMB username\n\
667 password=<arg> SMB password\n\
668 credentials=<filename> file with username/password\n\
669 krb use kerberos (active directory)\n\
670 netbiosname=<arg> source NetBIOS name\n\
671 uid=<arg> mount uid or username\n\
672 gid=<arg> mount gid or groupname\n\
673 port=<arg> remote SMB port number\n\
674 fmask=<arg> file umask\n\
675 dmask=<arg> directory umask\n\
676 debug=<arg> debug level\n\
677 ip=<arg> destination host or IP address\n\
678 workgroup=<arg> workgroup on destination\n\
679 sockopt=<arg> TCP socket options\n\
680 scope=<arg> NetBIOS scope\n\
681 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
682 codepage=<arg> server codepage (cp850)\n\
683 ttl=<arg> dircache time to live\n\
684 guest don't prompt for a password\n\
685 ro mount read-only\n\
686 rw mount read-write\n\
688 This command is designed to be run from within /bin/mount by giving\n\
689 the option '-t smbfs'. For example:\n\
690 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
695 /****************************************************************************
696 Argument parsing for mount.smbfs interface
697 mount will call us like this:
698 mount.smbfs device mountpoint -o <options>
700 <options> is never empty, containing at least rw or ro
701 ****************************************************************************/
702 static void parse_mount_smb(int argc, char **argv)
711 /* FIXME: This function can silently fail if the arguments are
712 * not in the expected order.
714 > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
715 > requires that one gives "-o" before further options like username=...
716 > . Without -o, the username=.. setting is *silently* ignored. I've
717 > spent about an hour trying to find out why I couldn't log in now..
722 if (argc < 2 || argv[1][0] == '-') {
727 pstrcpy(service, argv[1]);
728 pstrcpy(mpoint, argv[2]);
730 /* Convert any '/' characters in the service name to
732 string_replace(service, '/','\\');
736 opt = getopt(argc, argv, "o:");
745 * option parsing from nfsmount.c (util-linux-2.9u)
747 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
748 DEBUG(3, ("opts: %s\n", opts));
749 if ((opteq = strchr_m(opts, '='))) {
750 val = atoi(opteq + 1);
753 if (!strcmp(opts, "username") ||
754 !strcmp(opts, "logon")) {
757 pstrcpy(username,opteq+1);
758 if ((lp=strchr_m(username,'%'))) {
760 pstrcpy(password,lp+1);
762 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
764 if ((lp=strchr_m(username,'/'))) {
766 pstrcpy(workgroup,lp+1);
768 } else if(!strcmp(opts, "passwd") ||
769 !strcmp(opts, "password")) {
770 pstrcpy(password,opteq+1);
772 memset(opteq+1,'X',strlen(password));
773 } else if(!strcmp(opts, "credentials")) {
774 pstrcpy(credentials,opteq+1);
775 } else if(!strcmp(opts, "netbiosname")) {
776 pstrcpy(my_netbios_name,opteq+1);
777 } else if(!strcmp(opts, "uid")) {
778 mount_uid = nametouid(opteq+1);
779 } else if(!strcmp(opts, "gid")) {
780 mount_gid = nametogid(opteq+1);
781 } else if(!strcmp(opts, "port")) {
783 } else if(!strcmp(opts, "fmask")) {
784 mount_fmask = strtol(opteq+1, NULL, 8);
785 } else if(!strcmp(opts, "dmask")) {
786 mount_dmask = strtol(opteq+1, NULL, 8);
787 } else if(!strcmp(opts, "debug")) {
789 } else if(!strcmp(opts, "ip")) {
790 dest_ip = *interpret_addr2(opteq+1);
791 if (is_zero_ip(dest_ip)) {
792 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
796 } else if(!strcmp(opts, "workgroup")) {
797 pstrcpy(workgroup,opteq+1);
798 } else if(!strcmp(opts, "sockopt")) {
799 lp_set_cmdline("socket options", opteq+1);
800 } else if(!strcmp(opts, "scope")) {
801 lp_set_cmdline("netbios scope", opteq+1);
803 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
808 if(!strcmp(opts, "nocaps")) {
809 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
811 } else if(!strcmp(opts, "guest")) {
814 } else if(!strcmp(opts, "krb")) {
819 fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
821 fprintf(stderr,"No kerberos support compiled in\n");
824 } else if(!strcmp(opts, "rw")) {
826 } else if(!strcmp(opts, "ro")) {
829 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
843 *(p-1) = 0; /* remove trailing , */
844 DEBUG(3,("passthrough options '%s'\n", options));
848 /****************************************************************************
850 ****************************************************************************/
851 int main(int argc,char *argv[])
859 /* here we are interactive, even if run from autofs */
860 setup_logging("mount.smbfs",DEBUG_STDERR);
862 #if 0 /* JRA - Urban says not needed ? */
863 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
864 is to not announce any unicode capabilities as current smbfs does
866 p = getenv("CLI_FORCE_ASCII");
867 if (p && !strcmp(p, "false"))
868 unsetenv("CLI_FORCE_ASCII");
870 setenv("CLI_FORCE_ASCII", "true", 1);
873 in_client = True; /* Make sure that we tell lp_load we are */
875 if (getenv("USER")) {
876 pstrcpy(username,getenv("USER"));
878 if ((p=strchr_m(username,'%'))) {
880 pstrcpy(password,p+1);
882 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
887 if (getenv("PASSWD")) {
888 pstrcpy(password,getenv("PASSWD"));
892 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
897 if (*username == 0 && getenv("LOGNAME")) {
898 pstrcpy(username,getenv("LOGNAME"));
901 if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
902 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
906 parse_mount_smb(argc, argv);
908 if (use_kerberos && !got_user) {
912 if (*credentials != 0) {
913 read_credentials_file(credentials);
916 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
918 if (*workgroup == 0) {
919 pstrcpy(workgroup,lp_workgroup());
923 if (!*my_netbios_name) {
924 pstrcpy(my_netbios_name, myhostname());
926 strupper(my_netbios_name);