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.
25 #include "module_dummy.h"
28 #include <asm/types.h>
29 #include <linux/smb_fs.h>
31 extern BOOL in_client;
32 extern pstring user_socket_options;
34 static pstring credentials;
35 static pstring my_netbios_name;
36 static pstring password;
37 static pstring username;
38 static pstring workgroup;
39 static pstring mpoint;
40 static pstring service;
41 static pstring options;
43 static struct in_addr dest_ip;
45 static int smb_port = 0;
48 static uid_t mount_uid;
49 static gid_t mount_gid;
51 static unsigned mount_fmask;
52 static unsigned mount_dmask;
53 static BOOL use_kerberos;
54 /* TODO: Add code to detect smbfs version in kernel */
55 static BOOL status32_smbfs = False;
56 static BOOL smbfs_has_unicode = False;
57 static BOOL smbfs_has_lfs = False;
59 static void usage(void);
61 static void exit_parent(int sig)
63 /* parent simply exits when child says go... */
67 static void daemonize(void)
72 signal( SIGTERM, exit_parent );
74 if ((child_pid = sys_fork()) < 0) {
75 DEBUG(0,("could not fork\n"));
80 j = waitpid( child_pid, &status, 0 );
82 if( EINTR == errno ) {
90 /* If we get here - the child exited with some error status */
91 if (WIFSIGNALED(status))
92 exit(128 + WTERMSIG(status));
94 exit(WEXITSTATUS(status));
97 signal( SIGTERM, SIG_DFL );
101 static void close_our_files(int client_fd)
104 struct rlimit limits;
106 getrlimit(RLIMIT_NOFILE,&limits);
107 for (i = 0; i< limits.rlim_max; i++) {
114 static void usr1_handler(int x)
120 /*****************************************************
121 return a connection to a server
122 *******************************************************/
123 static struct cli_state *do_connection(char *the_service)
126 struct nmb_name called, calling;
132 if (the_service[0] != '\\' || the_service[1] != '\\') {
137 pstrcpy(server, the_service+2);
138 share = strchr_m(server,'\\');
148 make_nmb_name(&calling, my_netbios_name, 0x0);
149 make_nmb_name(&called , server, 0x20);
153 if (have_ip) ip = dest_ip;
155 /* have to open a new connection */
156 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
157 !cli_connect(c, server_n, &ip)) {
158 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
165 /* SPNEGO doesn't work till we get NTSTATUS error support */
166 /* But it is REQUIRED for kerberos authentication */
167 if(!use_kerberos) c->use_spnego = False;
169 /* The kernel doesn't yet know how to sign it's packets */
170 c->sign_info.allow_smb_signing = False;
172 /* Use kerberos authentication if specified */
173 c->use_kerberos = use_kerberos;
175 if (!cli_session_request(c, &calling, &called)) {
177 DEBUG(0,("%d: session request to %s failed (%s)\n",
178 sys_getpid(), called.name, cli_errstr(c)));
180 if ((p=strchr_m(called.name, '.'))) {
184 if (strcmp(called.name, "*SMBSERVER")) {
185 make_nmb_name(&called , "*SMBSERVER", 0x20);
191 DEBUG(4,("%d: session request ok\n", sys_getpid()));
193 if (!cli_negprot(c)) {
194 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
200 char *pass = getpass("Password: ");
202 pstrcpy(password, pass);
206 /* This should be right for current smbfs. Future versions will support
207 large files as well as unicode and oplocks. */
208 c->capabilities &= ~(CAP_NT_SMBS | CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
210 c->capabilities &= ~CAP_LARGE_FILES;
211 if (!smbfs_has_unicode)
212 c->capabilities &= ~CAP_UNICODE;
213 if (!status32_smbfs) {
214 c->capabilities &= ~CAP_STATUS32;
215 c->force_dos_errors = True;
218 if (!cli_session_setup(c, username,
219 password, strlen(password),
220 password, strlen(password),
222 /* if a password was not supplied then try again with a
224 if (password[0] || !username[0] ||
225 !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
226 DEBUG(0,("%d: session setup failed: %s\n",
227 sys_getpid(), cli_errstr(c)));
231 DEBUG(0,("Anonymous login successful\n"));
234 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
236 if (!cli_send_tconX(c, share, "?????",
237 password, strlen(password)+1)) {
238 DEBUG(0,("%d: tree connect failed: %s\n",
239 sys_getpid(), cli_errstr(c)));
244 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
252 /****************************************************************************
253 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
254 Code blatently stolen from smbumount.c
256 ****************************************************************************/
257 static void smb_umount(char *mount_point)
265 This routine only gets called to the scene of a disaster
266 to shoot the survivors... A connection that was working
267 has now apparently failed. We have an active mount point
268 (presumably) that we need to dump. If we get errors along
269 the way - make some noise, but we are already turning out
270 the lights to exit anyways...
272 if (umount(mount_point) != 0) {
273 DEBUG(0,("%d: Could not umount %s: %s\n",
274 sys_getpid(), mount_point, strerror(errno)));
278 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
279 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
285 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
286 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
287 sys_getpid(), strerror(errno)));
291 #define MOUNTED_TMP MOUNTED".tmp"
293 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
294 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
295 sys_getpid(), strerror(errno)));
300 while ((mnt = getmntent(mtab)) != NULL) {
301 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
302 addmntent(new_mtab, mnt);
308 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
309 DEBUG(0,("%d: Error changing mode of %s: %s\n",
310 sys_getpid(), MOUNTED_TMP, strerror(errno)));
316 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
317 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
318 sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
322 if (unlink(MOUNTED"~") == -1) {
323 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
330 * Call the smbfs ioctl to install a connection socket,
331 * then wait for a signal to reconnect. Note that we do
332 * not exit after open_sockets() or send_login() errors,
333 * as the smbfs mount would then have no way to recover.
335 static void send_fs_socket(char *the_service, char *mount_point, struct cli_state *c)
337 int fd, closed = 0, res = 1;
338 pid_t parentpid = getppid();
339 struct smb_conn_opt conn_options;
341 memset(&conn_options, 0, sizeof(conn_options));
344 if ((fd = open(mount_point, O_RDONLY)) < 0) {
345 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
346 sys_getpid(), mount_point));
350 conn_options.fd = c->fd;
351 conn_options.protocol = c->protocol;
352 conn_options.case_handling = SMB_CASE_DEFAULT;
353 conn_options.max_xmit = c->max_xmit;
354 conn_options.server_uid = c->vuid;
355 conn_options.tid = c->cnum;
356 conn_options.secmode = c->sec_mode;
357 conn_options.rawmode = 0;
358 conn_options.sesskey = c->sesskey;
359 conn_options.maxraw = 0;
360 conn_options.capabilities = c->capabilities;
361 conn_options.serverzone = c->serverzone/60;
363 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
365 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
372 /* Ok... We are going to kill the parent. Now
373 is the time to break the process group... */
375 /* Send a signal to the parent to terminate */
376 kill(parentpid, SIGTERM);
382 /* This looks wierd but we are only closing the userspace
383 side, the connection has already been passed to smbfs and
384 it has increased the usage count on the socket.
386 If we don't do this we will "leak" sockets and memory on
387 each reconnection we have to make. */
392 /* redirect stdout & stderr since we can't know that
393 the library functions we use are using DEBUG. */
394 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
395 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
398 dup2(fd, STDOUT_FILENO);
399 dup2(fd, STDERR_FILENO);
403 /* here we are no longer interactive */
404 set_remote_machine_name("smbmount", False); /* sneaky ... */
405 setup_logging("mount.smbfs", False);
407 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
412 /* Wait for a signal from smbfs ... but don't continue
413 until we actually get a new connection. */
415 CatchSignal(SIGUSR1, &usr1_handler);
417 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
418 c = do_connection(the_service);
422 smb_umount(mount_point);
423 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
431 static void init_mount(void)
433 char mount_point[MAXPATHLEN+1];
440 if (realpath(mpoint, mount_point) == NULL) {
441 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
446 c = do_connection(service);
448 fprintf(stderr,"SMB connection failed\n");
453 Set up to return as a daemon child and wait in the parent
454 until the child say it's ready...
458 pstrcpy(svc2, service);
459 string_replace(svc2, '\\','/');
460 string_replace(svc2, ' ','_');
462 memset(args, 0, sizeof(args[0])*20);
465 args[i++] = "smbmnt";
467 args[i++] = mount_point;
475 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
477 args[i++] = smb_xstrdup(tmp);
480 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
482 args[i++] = smb_xstrdup(tmp);
485 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
487 args[i++] = smb_xstrdup(tmp);
490 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
492 args[i++] = smb_xstrdup(tmp);
499 if (sys_fork() == 0) {
502 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
504 if (file_exist(smbmnt_path, NULL)) {
505 execv(smbmnt_path, args);
507 "smbfs/init_mount: execv of %s failed. Error was %s.",
508 smbmnt_path, strerror(errno));
510 execvp("smbmnt", args);
512 "smbfs/init_mount: execv of %s failed. Error was %s.",
513 "smbmnt", strerror(errno));
519 if (waitpid(-1, &status, 0) == -1) {
520 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
521 /* FIXME: do some proper error handling */
525 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
526 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
527 /* FIXME: do some proper error handling */
529 } else if (WIFSIGNALED(status)) {
530 fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
534 /* Ok... This is the rubicon for that mount point... At any point
535 after this, if the connections fail and can not be reconstructed
536 for any reason, we will have to unmount the mount point. There
537 is no exit from the next call...
539 send_fs_socket(service, mount_point, c);
543 /****************************************************************************
544 get a password from a a file or file descriptor
545 exit on failure (from smbclient, move to libsmb or shared .c file?)
546 ****************************************************************************/
547 static void get_password_file(void)
551 BOOL close_it = False;
555 if ((p = getenv("PASSWD_FD")) != NULL) {
556 pstrcpy(spec, "descriptor ");
558 sscanf(p, "%d", &fd);
560 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
561 fd = sys_open(p, O_RDONLY, 0);
564 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
565 spec, strerror(errno));
571 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
572 p && p - pass < sizeof(pass);) {
573 switch (read(fd, p, 1)) {
575 if (*p != '\n' && *p != '\0') {
576 *++p = '\0'; /* advance p, and null-terminate pass */
581 *p = '\0'; /* null-terminate it, just in case... */
582 p = NULL; /* then force the loop condition to become false */
585 fprintf(stderr, "Error reading password from file %s: %s\n",
586 spec, "empty password\n");
591 fprintf(stderr, "Error reading password from file %s: %s\n",
592 spec, strerror(errno));
596 pstrcpy(password, pass);
601 /****************************************************************************
602 get username and password from a credentials file
603 exit on failure (from smbclient, move to libsmb or shared .c file?)
604 ****************************************************************************/
605 static void read_credentials_file(char *filename)
610 char *ptr, *val, *param;
612 if ((auth=sys_fopen(filename, "r")) == NULL)
614 /* fail if we can't open the credentials file */
615 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
621 /* get a line from the file */
622 if (!fgets (buf, sizeof(buf), auth))
626 if ((len) && (buf[len-1]=='\n'))
634 /* break up the line into parameter & value.
635 will need to eat a little whitespace possibly */
637 if (!(ptr = strchr (buf, '=')))
642 /* eat leading white space */
643 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
646 if (strwicmp("password", param) == 0)
648 pstrcpy(password, val);
651 else if (strwicmp("username", param) == 0) {
652 pstrcpy(username, val);
655 memset(buf, 0, sizeof(buf));
661 /****************************************************************************
663 ****************************************************************************/
664 static void usage(void)
666 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
668 printf("Version %s\n\n",VERSION);
672 username=<arg> SMB username\n\
673 password=<arg> SMB password\n\
674 credentials=<filename> file with username/password\n\
675 krb use kerberos (active directory)\n\
676 netbiosname=<arg> source NetBIOS name\n\
677 uid=<arg> mount uid or username\n\
678 gid=<arg> mount gid or groupname\n\
679 port=<arg> remote SMB port number\n\
680 fmask=<arg> file umask\n\
681 dmask=<arg> directory umask\n\
682 debug=<arg> debug level\n\
683 ip=<arg> destination host or IP address\n\
684 workgroup=<arg> workgroup on destination\n\
685 sockopt=<arg> TCP socket options\n\
686 scope=<arg> NetBIOS scope\n\
687 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
688 codepage=<arg> server codepage (cp850)\n\
689 unicode use unicode when communicating with server\n\
690 lfs large file system support\n\
691 ttl=<arg> dircache time to live\n\
692 guest don't prompt for a password\n\
693 ro mount read-only\n\
694 rw mount read-write\n\
696 This command is designed to be run from within /bin/mount by giving\n\
697 the option '-t smbfs'. For example:\n\
698 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
703 /****************************************************************************
704 Argument parsing for mount.smbfs interface
705 mount will call us like this:
706 mount.smbfs device mountpoint -o <options>
708 <options> is never empty, containing at least rw or ro
709 ****************************************************************************/
710 static void parse_mount_smb(int argc, char **argv)
719 /* FIXME: This function can silently fail if the arguments are
720 * not in the expected order.
722 > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
723 > requires that one gives "-o" before further options like username=...
724 > . Without -o, the username=.. setting is *silently* ignored. I've
725 > spent about an hour trying to find out why I couldn't log in now..
730 if (argc < 2 || argv[1][0] == '-') {
735 pstrcpy(service, argv[1]);
736 pstrcpy(mpoint, argv[2]);
738 /* Convert any '/' characters in the service name to
740 string_replace(service, '/','\\');
744 opt = getopt(argc, argv, "o:");
753 * option parsing from nfsmount.c (util-linux-2.9u)
755 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
756 DEBUG(3, ("opts: %s\n", opts));
757 if ((opteq = strchr_m(opts, '='))) {
758 val = atoi(opteq + 1);
761 if (!strcmp(opts, "username") ||
762 !strcmp(opts, "logon")) {
765 pstrcpy(username,opteq+1);
766 if ((lp=strchr_m(username,'%'))) {
768 pstrcpy(password,lp+1);
770 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
772 if ((lp=strchr_m(username,'/'))) {
774 pstrcpy(workgroup,lp+1);
776 } else if(!strcmp(opts, "passwd") ||
777 !strcmp(opts, "password")) {
778 pstrcpy(password,opteq+1);
780 memset(opteq+1,'X',strlen(password));
781 } else if(!strcmp(opts, "credentials")) {
782 pstrcpy(credentials,opteq+1);
783 } else if(!strcmp(opts, "netbiosname")) {
784 pstrcpy(my_netbios_name,opteq+1);
785 } else if(!strcmp(opts, "uid")) {
786 mount_uid = nametouid(opteq+1);
787 } else if(!strcmp(opts, "gid")) {
788 mount_gid = nametogid(opteq+1);
789 } else if(!strcmp(opts, "port")) {
791 } else if(!strcmp(opts, "fmask")) {
792 mount_fmask = strtol(opteq+1, NULL, 8);
793 } else if(!strcmp(opts, "dmask")) {
794 mount_dmask = strtol(opteq+1, NULL, 8);
795 } else if(!strcmp(opts, "debug")) {
797 } else if(!strcmp(opts, "ip")) {
798 dest_ip = *interpret_addr2(opteq+1);
799 if (is_zero_ip(dest_ip)) {
800 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
804 } else if(!strcmp(opts, "workgroup")) {
805 pstrcpy(workgroup,opteq+1);
806 } else if(!strcmp(opts, "sockopt")) {
807 pstrcpy(user_socket_options,opteq+1);
808 } else if(!strcmp(opts, "scope")) {
809 set_global_scope(opteq+1);
811 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
816 if(!strcmp(opts, "nocaps")) {
817 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
819 } else if(!strcmp(opts, "guest")) {
822 } else if(!strcmp(opts, "krb")) {
827 fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
829 fprintf(stderr,"No kerberos support compiled in\n");
832 } else if(!strcmp(opts, "rw")) {
834 } else if(!strcmp(opts, "ro")) {
836 } else if(!strcmp(opts, "unicode")) {
837 smbfs_has_unicode = True;
838 } else if(!strcmp(opts, "lfs")) {
839 smbfs_has_lfs = True;
841 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
855 *(p-1) = 0; /* remove trailing , */
856 DEBUG(3,("passthrough options '%s'\n", options));
860 /****************************************************************************
862 ****************************************************************************/
863 int main(int argc,char *argv[])
871 /* here we are interactive, even if run from autofs */
872 setup_logging("mount.smbfs",True);
874 #if 0 /* JRA - Urban says not needed ? */
875 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
876 is to not announce any unicode capabilities as current smbfs does
878 p = getenv("CLI_FORCE_ASCII");
879 if (p && !strcmp(p, "false"))
880 unsetenv("CLI_FORCE_ASCII");
882 setenv("CLI_FORCE_ASCII", "true", 1);
885 in_client = True; /* Make sure that we tell lp_load we are */
887 if (getenv("USER")) {
888 pstrcpy(username,getenv("USER"));
890 if ((p=strchr_m(username,'%'))) {
892 pstrcpy(password,p+1);
894 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
899 if (getenv("PASSWD")) {
900 pstrcpy(password,getenv("PASSWD"));
904 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
909 if (*username == 0 && getenv("LOGNAME")) {
910 pstrcpy(username,getenv("LOGNAME"));
913 if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
914 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
918 parse_mount_smb(argc, argv);
920 if (use_kerberos && !got_user) {
924 if (*credentials != 0) {
925 read_credentials_file(credentials);
928 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
930 if (*workgroup == 0) {
931 pstrcpy(workgroup,lp_workgroup());
935 if (!*my_netbios_name) {
936 pstrcpy(my_netbios_name, myhostname());
938 strupper(my_netbios_name);