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.
26 #include <asm/types.h>
27 #include <linux/smb_fs.h>
29 extern BOOL in_client;
30 extern pstring user_socket_options;
32 static pstring credentials;
33 static pstring my_netbios_name;
34 static pstring password;
35 static pstring username;
36 static pstring workgroup;
37 static pstring mpoint;
38 static pstring service;
39 static pstring options;
41 static struct in_addr dest_ip;
43 static int smb_port = 0;
46 static uid_t mount_uid;
47 static gid_t mount_gid;
49 static unsigned mount_fmask;
50 static unsigned mount_dmask;
51 static BOOL use_kerberos;
52 /* TODO: Add code to detect smbfs version in kernel */
53 static BOOL status32_smbfs = False;
55 static void usage(void);
57 static void exit_parent(int sig)
59 /* parent simply exits when child says go... */
63 static void daemonize(void)
68 signal( SIGTERM, exit_parent );
70 if ((child_pid = sys_fork()) < 0) {
71 DEBUG(0,("could not fork\n"));
76 j = waitpid( child_pid, &status, 0 );
78 if( EINTR == errno ) {
86 /* If we get here - the child exited with some error status */
87 if (WIFSIGNALED(status))
88 exit(128 + WTERMSIG(status));
90 exit(WEXITSTATUS(status));
93 signal( SIGTERM, SIG_DFL );
97 static void close_our_files(int client_fd)
100 struct rlimit limits;
102 getrlimit(RLIMIT_NOFILE,&limits);
103 for (i = 0; i< limits.rlim_max; i++) {
110 static void usr1_handler(int x)
116 /*****************************************************
117 return a connection to a server
118 *******************************************************/
119 static struct cli_state *do_connection(char *the_service)
122 struct nmb_name called, calling;
128 if (the_service[0] != '\\' || the_service[1] != '\\') {
133 pstrcpy(server, the_service+2);
134 share = strchr_m(server,'\\');
144 make_nmb_name(&calling, my_netbios_name, 0x0);
145 make_nmb_name(&called , server, 0x20);
149 if (have_ip) ip = dest_ip;
151 /* have to open a new connection */
152 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
153 !cli_connect(c, server_n, &ip)) {
154 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
161 /* SPNEGO doesn't work till we get NTSTATUS error support */
162 /* But it is REQUIRED for kerberos authentication */
163 if(!use_kerberos) c->use_spnego = False;
165 /* The kernel doesn't yet know how to sign it's packets */
166 c->sign_info.allow_smb_signing = False;
168 /* Use kerberos authentication if specified */
169 c->use_kerberos = use_kerberos;
171 if (!cli_session_request(c, &calling, &called)) {
173 DEBUG(0,("%d: session request to %s failed (%s)\n",
174 sys_getpid(), called.name, cli_errstr(c)));
176 if ((p=strchr_m(called.name, '.'))) {
180 if (strcmp(called.name, "*SMBSERVER")) {
181 make_nmb_name(&called , "*SMBSERVER", 0x20);
187 DEBUG(4,("%d: session request ok\n", sys_getpid()));
189 if (!cli_negprot(c)) {
190 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
196 char *pass = getpass("Password: ");
198 pstrcpy(password, pass);
202 /* This should be right for current smbfs. Future versions will support
203 large files as well as unicode and oplocks. */
204 if (status32_smbfs) {
205 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
206 CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
209 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
210 CAP_NT_FIND | CAP_STATUS32 |
211 CAP_LEVEL_II_OPLOCKS);
212 c->force_dos_errors = True;
215 if (!cli_session_setup(c, username,
216 password, strlen(password),
217 password, strlen(password),
219 /* if a password was not supplied then try again with a
221 if (password[0] || !username[0] ||
222 !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
223 DEBUG(0,("%d: session setup failed: %s\n",
224 sys_getpid(), cli_errstr(c)));
228 DEBUG(0,("Anonymous login successful\n"));
231 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
233 if (!cli_send_tconX(c, share, "?????",
234 password, strlen(password)+1)) {
235 DEBUG(0,("%d: tree connect failed: %s\n",
236 sys_getpid(), cli_errstr(c)));
241 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
249 /****************************************************************************
250 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
251 Code blatently stolen from smbumount.c
253 ****************************************************************************/
254 static void smb_umount(char *mount_point)
262 This routine only gets called to the scene of a disaster
263 to shoot the survivors... A connection that was working
264 has now apparently failed. We have an active mount point
265 (presumably) that we need to dump. If we get errors along
266 the way - make some noise, but we are already turning out
267 the lights to exit anyways...
269 if (umount(mount_point) != 0) {
270 DEBUG(0,("%d: Could not umount %s: %s\n",
271 sys_getpid(), mount_point, strerror(errno)));
275 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
276 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
282 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
283 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
284 sys_getpid(), strerror(errno)));
288 #define MOUNTED_TMP MOUNTED".tmp"
290 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
291 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
292 sys_getpid(), strerror(errno)));
297 while ((mnt = getmntent(mtab)) != NULL) {
298 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
299 addmntent(new_mtab, mnt);
305 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
306 DEBUG(0,("%d: Error changing mode of %s: %s\n",
307 sys_getpid(), MOUNTED_TMP, strerror(errno)));
313 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
314 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
315 sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
319 if (unlink(MOUNTED"~") == -1) {
320 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
327 * Call the smbfs ioctl to install a connection socket,
328 * then wait for a signal to reconnect. Note that we do
329 * not exit after open_sockets() or send_login() errors,
330 * as the smbfs mount would then have no way to recover.
332 static void send_fs_socket(char *the_service, char *mount_point, struct cli_state *c)
334 int fd, closed = 0, res = 1;
335 pid_t parentpid = getppid();
336 struct smb_conn_opt conn_options;
338 memset(&conn_options, 0, sizeof(conn_options));
341 if ((fd = open(mount_point, O_RDONLY)) < 0) {
342 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
343 sys_getpid(), mount_point));
347 conn_options.fd = c->fd;
348 conn_options.protocol = c->protocol;
349 conn_options.case_handling = SMB_CASE_DEFAULT;
350 conn_options.max_xmit = c->max_xmit;
351 conn_options.server_uid = c->vuid;
352 conn_options.tid = c->cnum;
353 conn_options.secmode = c->sec_mode;
354 conn_options.rawmode = 0;
355 conn_options.sesskey = c->sesskey;
356 conn_options.maxraw = 0;
357 conn_options.capabilities = c->capabilities;
358 conn_options.serverzone = c->serverzone/60;
360 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
362 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
369 /* Ok... We are going to kill the parent. Now
370 is the time to break the process group... */
372 /* Send a signal to the parent to terminate */
373 kill(parentpid, SIGTERM);
379 /* This looks wierd but we are only closing the userspace
380 side, the connection has already been passed to smbfs and
381 it has increased the usage count on the socket.
383 If we don't do this we will "leak" sockets and memory on
384 each reconnection we have to make. */
389 /* redirect stdout & stderr since we can't know that
390 the library functions we use are using DEBUG. */
391 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
392 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
395 dup2(fd, STDOUT_FILENO);
396 dup2(fd, STDERR_FILENO);
400 /* here we are no longer interactive */
401 set_remote_machine_name("smbmount", False); /* sneaky ... */
402 setup_logging("mount.smbfs", False);
404 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
409 /* Wait for a signal from smbfs ... but don't continue
410 until we actually get a new connection. */
412 CatchSignal(SIGUSR1, &usr1_handler);
414 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
415 c = do_connection(the_service);
419 smb_umount(mount_point);
420 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
428 static void init_mount(void)
430 char mount_point[MAXPATHLEN+1];
437 if (realpath(mpoint, mount_point) == NULL) {
438 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
443 c = do_connection(service);
445 fprintf(stderr,"SMB connection failed\n");
450 Set up to return as a daemon child and wait in the parent
451 until the child say it's ready...
455 pstrcpy(svc2, service);
456 string_replace(svc2, '\\','/');
457 string_replace(svc2, ' ','_');
459 memset(args, 0, sizeof(args[0])*20);
462 args[i++] = "smbmnt";
464 args[i++] = mount_point;
472 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
474 args[i++] = smb_xstrdup(tmp);
477 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
479 args[i++] = smb_xstrdup(tmp);
482 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
484 args[i++] = smb_xstrdup(tmp);
487 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
489 args[i++] = smb_xstrdup(tmp);
496 if (sys_fork() == 0) {
499 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
501 if (file_exist(smbmnt_path, NULL)) {
502 execv(smbmnt_path, args);
504 "smbfs/init_mount: execv of %s failed. Error was %s.",
505 smbmnt_path, strerror(errno));
507 execvp("smbmnt", args);
509 "smbfs/init_mount: execv of %s failed. Error was %s.",
510 "smbmnt", strerror(errno));
516 if (waitpid(-1, &status, 0) == -1) {
517 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
518 /* FIXME: do some proper error handling */
522 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
523 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
524 /* FIXME: do some proper error handling */
526 } else if (WIFSIGNALED(status)) {
527 fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
531 /* Ok... This is the rubicon for that mount point... At any point
532 after this, if the connections fail and can not be reconstructed
533 for any reason, we will have to unmount the mount point. There
534 is no exit from the next call...
536 send_fs_socket(service, mount_point, c);
540 /****************************************************************************
541 get a password from a a file or file descriptor
542 exit on failure (from smbclient, move to libsmb or shared .c file?)
543 ****************************************************************************/
544 static void get_password_file(void)
548 BOOL close_it = False;
552 if ((p = getenv("PASSWD_FD")) != NULL) {
553 pstrcpy(spec, "descriptor ");
555 sscanf(p, "%d", &fd);
557 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
558 fd = sys_open(p, O_RDONLY, 0);
561 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
562 spec, strerror(errno));
568 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
569 p && p - pass < sizeof(pass);) {
570 switch (read(fd, p, 1)) {
572 if (*p != '\n' && *p != '\0') {
573 *++p = '\0'; /* advance p, and null-terminate pass */
578 *p = '\0'; /* null-terminate it, just in case... */
579 p = NULL; /* then force the loop condition to become false */
582 fprintf(stderr, "Error reading password from file %s: %s\n",
583 spec, "empty password\n");
588 fprintf(stderr, "Error reading password from file %s: %s\n",
589 spec, strerror(errno));
593 pstrcpy(password, pass);
598 /****************************************************************************
599 get username and password from a credentials file
600 exit on failure (from smbclient, move to libsmb or shared .c file?)
601 ****************************************************************************/
602 static void read_credentials_file(char *filename)
607 char *ptr, *val, *param;
609 if ((auth=sys_fopen(filename, "r")) == NULL)
611 /* fail if we can't open the credentials file */
612 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
618 /* get a line from the file */
619 if (!fgets (buf, sizeof(buf), auth))
623 if ((len) && (buf[len-1]=='\n'))
631 /* break up the line into parameter & value.
632 will need to eat a little whitespace possibly */
634 if (!(ptr = strchr (buf, '=')))
639 /* eat leading white space */
640 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
643 if (strwicmp("password", param) == 0)
645 pstrcpy(password, val);
648 else if (strwicmp("username", param) == 0) {
649 pstrcpy(username, val);
652 memset(buf, 0, sizeof(buf));
658 /****************************************************************************
660 ****************************************************************************/
661 static void usage(void)
663 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
665 printf("Version %s\n\n",VERSION);
669 username=<arg> SMB username\n\
670 password=<arg> SMB password\n\
671 credentials=<filename> file with username/password\n\
672 krb use kerberos (active directory)\n\
673 netbiosname=<arg> source NetBIOS name\n\
674 uid=<arg> mount uid or username\n\
675 gid=<arg> mount gid or groupname\n\
676 port=<arg> remote SMB port number\n\
677 fmask=<arg> file umask\n\
678 dmask=<arg> directory umask\n\
679 debug=<arg> debug level\n\
680 ip=<arg> destination host or IP address\n\
681 workgroup=<arg> workgroup on destination\n\
682 sockopt=<arg> TCP socket options\n\
683 scope=<arg> NetBIOS scope\n\
684 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
685 codepage=<arg> server codepage (cp850)\n\
686 ttl=<arg> dircache time to live\n\
687 guest don't prompt for a password\n\
688 ro mount read-only\n\
689 rw mount read-write\n\
691 This command is designed to be run from within /bin/mount by giving\n\
692 the option '-t smbfs'. For example:\n\
693 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
698 /****************************************************************************
699 Argument parsing for mount.smbfs interface
700 mount will call us like this:
701 mount.smbfs device mountpoint -o <options>
703 <options> is never empty, containing at least rw or ro
704 ****************************************************************************/
705 static void parse_mount_smb(int argc, char **argv)
714 /* FIXME: This function can silently fail if the arguments are
715 * not in the expected order.
717 > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
718 > requires that one gives "-o" before further options like username=...
719 > . Without -o, the username=.. setting is *silently* ignored. I've
720 > spent about an hour trying to find out why I couldn't log in now..
725 if (argc < 2 || argv[1][0] == '-') {
730 pstrcpy(service, argv[1]);
731 pstrcpy(mpoint, argv[2]);
733 /* Convert any '/' characters in the service name to
735 string_replace(service, '/','\\');
739 opt = getopt(argc, argv, "o:");
748 * option parsing from nfsmount.c (util-linux-2.9u)
750 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
751 DEBUG(3, ("opts: %s\n", opts));
752 if ((opteq = strchr_m(opts, '='))) {
753 val = atoi(opteq + 1);
756 if (!strcmp(opts, "username") ||
757 !strcmp(opts, "logon")) {
760 pstrcpy(username,opteq+1);
761 if ((lp=strchr_m(username,'%'))) {
763 pstrcpy(password,lp+1);
765 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
767 if ((lp=strchr_m(username,'/'))) {
769 pstrcpy(workgroup,lp+1);
771 } else if(!strcmp(opts, "passwd") ||
772 !strcmp(opts, "password")) {
773 pstrcpy(password,opteq+1);
775 memset(opteq+1,'X',strlen(password));
776 } else if(!strcmp(opts, "credentials")) {
777 pstrcpy(credentials,opteq+1);
778 } else if(!strcmp(opts, "netbiosname")) {
779 pstrcpy(my_netbios_name,opteq+1);
780 } else if(!strcmp(opts, "uid")) {
781 mount_uid = nametouid(opteq+1);
782 } else if(!strcmp(opts, "gid")) {
783 mount_gid = nametogid(opteq+1);
784 } else if(!strcmp(opts, "port")) {
786 } else if(!strcmp(opts, "fmask")) {
787 mount_fmask = strtol(opteq+1, NULL, 8);
788 } else if(!strcmp(opts, "dmask")) {
789 mount_dmask = strtol(opteq+1, NULL, 8);
790 } else if(!strcmp(opts, "debug")) {
792 } else if(!strcmp(opts, "ip")) {
793 dest_ip = *interpret_addr2(opteq+1);
794 if (is_zero_ip(dest_ip)) {
795 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
799 } else if(!strcmp(opts, "workgroup")) {
800 pstrcpy(workgroup,opteq+1);
801 } else if(!strcmp(opts, "sockopt")) {
802 pstrcpy(user_socket_options,opteq+1);
803 } else if(!strcmp(opts, "scope")) {
804 set_global_scope(opteq+1);
806 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
811 if(!strcmp(opts, "nocaps")) {
812 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
814 } else if(!strcmp(opts, "guest")) {
817 } else if(!strcmp(opts, "krb")) {
822 fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
824 fprintf(stderr,"No kerberos support compiled in\n");
827 } else if(!strcmp(opts, "rw")) {
829 } else if(!strcmp(opts, "ro")) {
832 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
846 *(p-1) = 0; /* remove trailing , */
847 DEBUG(3,("passthrough options '%s'\n", options));
851 /****************************************************************************
853 ****************************************************************************/
854 int main(int argc,char *argv[])
862 /* here we are interactive, even if run from autofs */
863 setup_logging("mount.smbfs",True);
865 #if 0 /* JRA - Urban says not needed ? */
866 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
867 is to not announce any unicode capabilities as current smbfs does
869 p = getenv("CLI_FORCE_ASCII");
870 if (p && !strcmp(p, "false"))
871 unsetenv("CLI_FORCE_ASCII");
873 setenv("CLI_FORCE_ASCII", "true", 1);
876 in_client = True; /* Make sure that we tell lp_load we are */
878 if (getenv("USER")) {
879 pstrcpy(username,getenv("USER"));
881 if ((p=strchr_m(username,'%'))) {
883 pstrcpy(password,p+1);
885 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
890 if (getenv("PASSWD")) {
891 pstrcpy(password,getenv("PASSWD"));
895 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
900 if (*username == 0 && getenv("LOGNAME")) {
901 pstrcpy(username,getenv("LOGNAME"));
904 if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
905 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
909 parse_mount_smb(argc, argv);
911 if (use_kerberos && !got_user) {
915 if (*credentials != 0) {
916 read_credentials_file(credentials);
919 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
921 if (*workgroup == 0) {
922 pstrcpy(workgroup,lp_workgroup());
926 if (!*my_netbios_name) {
927 pstrcpy(my_netbios_name, myhostname());
929 strupper(my_netbios_name);