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 3 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, see <http://www.gnu.org/licenses/>.
23 #include <asm/types.h>
24 #include <linux/smb_fs.h>
26 extern bool in_client;
27 extern pstring user_socket_options;
31 static pstring credentials;
32 static pstring my_netbios_name;
33 static pstring password;
34 static pstring username;
35 static pstring workgroup;
36 static pstring mpoint;
37 static pstring service;
38 static pstring options;
40 static struct sockaddr_storage dest_ip;
42 static int smb_port = 0;
45 static uid_t mount_uid;
46 static gid_t mount_gid;
48 static unsigned mount_fmask;
49 static unsigned mount_dmask;
50 static bool use_kerberos;
51 /* TODO: Add code to detect smbfs version in kernel */
52 static bool status32_smbfs = False;
53 static bool smbfs_has_unicode = False;
54 static bool smbfs_has_lfs = False;
56 static void usage(void);
58 static void exit_parent(int sig)
60 /* parent simply exits when child says go... */
64 static void daemonize(void)
69 signal( SIGTERM, exit_parent );
71 if ((child_pid = sys_fork()) < 0) {
72 DEBUG(0,("could not fork\n"));
77 j = waitpid( child_pid, &status, 0 );
79 if( EINTR == errno ) {
87 /* If we get here - the child exited with some error status */
88 if (WIFSIGNALED(status))
89 exit(128 + WTERMSIG(status));
91 exit(WEXITSTATUS(status));
94 signal( SIGTERM, SIG_DFL );
98 static void close_our_files(int client_fd)
101 struct rlimit limits;
103 getrlimit(RLIMIT_NOFILE,&limits);
104 for (i = 0; i< limits.rlim_max; i++) {
111 static void usr1_handler(int x)
117 /*****************************************************
118 return a connection to a server
119 *******************************************************/
120 static struct cli_state *do_connection(char *the_service)
123 struct nmb_name called, calling;
125 struct sockaddr_storage ip;
129 if (the_service[0] != '\\' || the_service[1] != '\\') {
134 pstrcpy(server, the_service+2);
135 share = strchr_m(server,'\\');
145 make_nmb_name(&calling, my_netbios_name, 0x0);
146 make_nmb_name(&called , server, 0x20);
149 zero_addr(&ip, INADDR_ANY);
150 if (have_ip) ip = dest_ip;
152 /* have to open a new connection */
153 if (!(c=cli_initialise()) || (cli_set_port(c, smb_port) != smb_port) ||
154 !NT_STATUS_IS_OK(cli_connect(c, server_n, &ip))) {
155 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
162 /* SPNEGO doesn't work till we get NTSTATUS error support */
163 /* But it is REQUIRED for kerberos authentication */
164 if(!use_kerberos) c->use_spnego = False;
166 /* The kernel doesn't yet know how to sign it's packets */
167 c->sign_info.allow_smb_signing = False;
169 /* Use kerberos authentication if specified */
170 c->use_kerberos = use_kerberos;
172 if (!cli_session_request(c, &calling, &called)) {
174 DEBUG(0,("%d: session request to %s failed (%s)\n",
175 sys_getpid(), called.name, cli_errstr(c)));
177 if ((p=strchr_m(called.name, '.'))) {
181 if (strcmp(called.name, "*SMBSERVER")) {
182 make_nmb_name(&called , "*SMBSERVER", 0x20);
188 DEBUG(4,("%d: session request ok\n", sys_getpid()));
190 if (!cli_negprot(c)) {
191 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
197 char *pass = getpass("Password: ");
199 pstrcpy(password, pass);
203 /* This should be right for current smbfs. Future versions will support
204 large files as well as unicode and oplocks. */
205 c->capabilities &= ~(CAP_NT_SMBS | CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
207 c->capabilities &= ~CAP_LARGE_FILES;
208 if (!smbfs_has_unicode)
209 c->capabilities &= ~CAP_UNICODE;
210 if (!status32_smbfs) {
211 c->capabilities &= ~CAP_STATUS32;
212 c->force_dos_errors = True;
215 if (!NT_STATUS_IS_OK(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 !NT_STATUS_IS_OK(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. */
385 c->smb_rw_error = DO_NOT_DO_TDIS;
390 /* close the name cache so that close_our_files() doesn't steal its FD */
391 namecache_shutdown();
393 /* redirect stdout & stderr since we can't know that
394 the library functions we use are using DEBUG. */
395 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
396 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
399 dup2(fd, STDOUT_FILENO);
400 dup2(fd, STDERR_FILENO);
404 /* here we are no longer interactive */
405 set_remote_machine_name("smbmount", False); /* sneaky ... */
406 setup_logging("mount.smbfs", False);
408 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
413 /* Wait for a signal from smbfs ... but don't continue
414 until we actually get a new connection. */
416 CatchSignal(SIGUSR1, &usr1_handler);
418 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
419 c = do_connection(the_service);
423 smb_umount(mount_point);
424 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
432 static void init_mount(void)
434 char mount_point[PATH_MAX+1];
438 const char *args[20];
441 if (realpath(mpoint, mount_point) == NULL) {
442 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
447 c = do_connection(service);
449 fprintf(stderr,"SMB connection failed\n");
454 Set up to return as a daemon child and wait in the parent
455 until the child say it's ready...
459 pstrcpy(svc2, service);
460 string_replace(svc2, '\\','/');
461 string_replace(svc2, ' ','_');
463 memset(args, 0, sizeof(args[0])*20);
466 args[i++] = "smbmnt";
468 args[i++] = mount_point;
476 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
478 args[i++] = smb_xstrdup(tmp);
481 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
483 args[i++] = smb_xstrdup(tmp);
486 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
488 args[i++] = smb_xstrdup(tmp);
491 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
493 args[i++] = smb_xstrdup(tmp);
500 if (sys_fork() == 0) {
503 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
505 if (file_exist(smbmnt_path, NULL)) {
506 execv(smbmnt_path, (char * const *)args);
508 "smbfs/init_mount: execv of %s failed. Error was %s.",
509 smbmnt_path, strerror(errno));
511 execvp("smbmnt", (char * const *)args);
513 "smbfs/init_mount: execv of %s failed. Error was %s.",
514 "smbmnt", strerror(errno));
520 if (waitpid(-1, &status, 0) == -1) {
521 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
522 /* FIXME: do some proper error handling */
526 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
527 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
528 /* FIXME: do some proper error handling */
530 } else if (WIFSIGNALED(status)) {
531 fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
535 /* Ok... This is the rubicon for that mount point... At any point
536 after this, if the connections fail and can not be reconstructed
537 for any reason, we will have to unmount the mount point. There
538 is no exit from the next call...
540 send_fs_socket(service, mount_point, c);
544 /****************************************************************************
545 get a password from a a file or file descriptor
546 exit on failure (from smbclient, move to libsmb or shared .c file?)
547 ****************************************************************************/
548 static void get_password_file(void)
552 bool close_it = False;
556 if ((p = getenv("PASSWD_FD")) != NULL) {
557 pstrcpy(spec, "descriptor ");
559 sscanf(p, "%d", &fd);
561 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
562 fd = sys_open(p, O_RDONLY, 0);
565 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
566 spec, strerror(errno));
572 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
573 p && p - pass < sizeof(pass);) {
574 switch (read(fd, p, 1)) {
576 if (*p != '\n' && *p != '\0') {
577 *++p = '\0'; /* advance p, and null-terminate pass */
582 *p = '\0'; /* null-terminate it, just in case... */
583 p = NULL; /* then force the loop condition to become false */
586 fprintf(stderr, "Error reading password from file %s: %s\n",
587 spec, "empty password\n");
592 fprintf(stderr, "Error reading password from file %s: %s\n",
593 spec, strerror(errno));
597 pstrcpy(password, pass);
602 /****************************************************************************
603 get username and password from a credentials file
604 exit on failure (from smbclient, move to libsmb or shared .c file?)
605 ****************************************************************************/
606 static void read_credentials_file(char *filename)
611 char *ptr, *val, *param;
613 if ((auth=sys_fopen(filename, "r")) == NULL)
615 /* fail if we can't open the credentials file */
616 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
622 /* get a line from the file */
623 if (!fgets (buf, sizeof(buf), auth))
627 if ((len) && (buf[len-1]=='\n'))
635 /* break up the line into parameter & value.
636 will need to eat a little whitespace possibly */
638 if (!(ptr = strchr (buf, '=')))
643 /* eat leading white space */
644 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
647 if (strwicmp("password", param) == 0)
649 pstrcpy(password, val);
652 else if (strwicmp("username", param) == 0) {
653 pstrcpy(username, val);
656 memset(buf, 0, sizeof(buf));
662 /****************************************************************************
664 ****************************************************************************/
665 static void usage(void)
667 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
669 printf("Version %s\n\n",SAMBA_VERSION_STRING);
671 printf("Please be aware that smbfs is deprecated in favor of "
676 username=<arg> SMB username\n\
677 password=<arg> SMB password\n\
678 credentials=<filename> file with username/password\n\
679 krb use kerberos (active directory)\n\
680 netbiosname=<arg> source NetBIOS name\n\
681 uid=<arg> mount uid or username\n\
682 gid=<arg> mount gid or groupname\n\
683 port=<arg> remote SMB port number\n\
684 fmask=<arg> file umask\n\
685 dmask=<arg> directory umask\n\
686 debug=<arg> debug level\n\
687 ip=<arg> destination host or IP address\n\
688 workgroup=<arg> workgroup on destination\n\
689 sockopt=<arg> TCP socket options\n\
690 scope=<arg> NetBIOS scope\n\
691 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
692 codepage=<arg> server codepage (cp850)\n\
693 unicode use unicode when communicating with server\n\
694 lfs large file system support\n\
695 ttl=<arg> dircache time to live\n\
696 guest don't prompt for a password\n\
697 ro mount read-only\n\
698 rw mount read-write\n\
700 This command is designed to be run from within /bin/mount by giving\n\
701 the option '-t smbfs'. For example:\n\
702 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
707 /****************************************************************************
708 Argument parsing for mount.smbfs interface
709 mount will call us like this:
710 mount.smbfs device mountpoint -o <options>
712 <options> is never empty, containing at least rw or ro
713 ****************************************************************************/
714 static void parse_mount_smb(int argc, char **argv)
722 /* FIXME: This function can silently fail if the arguments are
723 * not in the expected order.
725 > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
726 > requires that one gives "-o" before further options like username=...
727 > . Without -o, the username=.. setting is *silently* ignored. I've
728 > spent about an hour trying to find out why I couldn't log in now..
733 if (argc < 2 || argv[1][0] == '-') {
738 pstrcpy(service, argv[1]);
739 pstrcpy(mpoint, argv[2]);
741 /* Convert any '/' characters in the service name to
743 string_replace(service, '/','\\');
747 opt = getopt(argc, argv, "o:");
756 * option parsing from nfsmount.c (util-linux-2.9u)
758 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
759 DEBUG(3, ("opts: %s\n", opts));
760 if ((opteq = strchr_m(opts, '='))) {
761 val = atoi(opteq + 1);
764 if (!strcmp(opts, "username") ||
765 !strcmp(opts, "logon")) {
768 pstrcpy(username,opteq+1);
769 if ((lp=strchr_m(username,'%'))) {
771 pstrcpy(password,lp+1);
773 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
775 if ((lp=strchr_m(username,'/'))) {
777 pstrcpy(workgroup,lp+1);
779 } else if(!strcmp(opts, "passwd") ||
780 !strcmp(opts, "password")) {
781 pstrcpy(password,opteq+1);
783 memset(opteq+1,'X',strlen(password));
784 } else if(!strcmp(opts, "credentials")) {
785 pstrcpy(credentials,opteq+1);
786 } else if(!strcmp(opts, "netbiosname")) {
787 pstrcpy(my_netbios_name,opteq+1);
788 } else if(!strcmp(opts, "uid")) {
789 mount_uid = nametouid(opteq+1);
790 } else if(!strcmp(opts, "gid")) {
791 mount_gid = nametogid(opteq+1);
792 } else if(!strcmp(opts, "port")) {
794 } else if(!strcmp(opts, "fmask")) {
795 mount_fmask = strtol(opteq+1, NULL, 8);
796 } else if(!strcmp(opts, "dmask")) {
797 mount_dmask = strtol(opteq+1, NULL, 8);
798 } else if(!strcmp(opts, "debug")) {
800 } else if(!strcmp(opts, "ip")) {
801 if (!interpret_string_addr(&dest_ip, opteq+1,
803 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
807 } else if(!strcmp(opts, "workgroup")) {
808 pstrcpy(workgroup,opteq+1);
809 } else if(!strcmp(opts, "sockopt")) {
810 pstrcpy(user_socket_options,opteq+1);
811 } else if(!strcmp(opts, "scope")) {
812 set_global_scope(opteq+1);
814 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
819 if(!strcmp(opts, "nocaps")) {
820 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
822 } else if(!strcmp(opts, "guest")) {
825 } else if(!strcmp(opts, "krb")) {
830 fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
832 fprintf(stderr,"No kerberos support compiled in\n");
835 } else if(!strcmp(opts, "rw")) {
837 } else if(!strcmp(opts, "ro")) {
839 } else if(!strcmp(opts, "unicode")) {
840 smbfs_has_unicode = True;
841 } else if(!strcmp(opts, "lfs")) {
842 smbfs_has_lfs = True;
844 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
858 *(p-1) = 0; /* remove trailing , */
859 DEBUG(3,("passthrough options '%s'\n", options));
863 /****************************************************************************
865 ****************************************************************************/
866 int main(int argc,char *argv[])
874 /* here we are interactive, even if run from autofs */
875 setup_logging("mount.smbfs",True);
877 #if 0 /* JRA - Urban says not needed ? */
878 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
879 is to not announce any unicode capabilities as current smbfs does
881 p = getenv("CLI_FORCE_ASCII");
882 if (p && !strcmp(p, "false"))
883 unsetenv("CLI_FORCE_ASCII");
885 setenv("CLI_FORCE_ASCII", "true", 1);
888 in_client = True; /* Make sure that we tell lp_load we are */
890 if (getenv("USER")) {
891 pstrcpy(username,getenv("USER"));
893 if ((p=strchr_m(username,'%'))) {
895 pstrcpy(password,p+1);
897 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
899 strupper_m(username);
902 if (getenv("PASSWD")) {
903 pstrcpy(password,getenv("PASSWD"));
907 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
912 if (*username == 0 && getenv("LOGNAME")) {
913 pstrcpy(username,getenv("LOGNAME"));
916 if (!lp_load(dyn_CONFIGFILE,True,False,False,True)) {
917 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
921 parse_mount_smb(argc, argv);
923 if (use_kerberos && !got_user) {
927 if (*credentials != 0) {
928 read_credentials_file(credentials);
931 DEBUG(3,("mount.smbfs started (version %s)\n", SAMBA_VERSION_STRING));
933 if (*workgroup == 0) {
934 pstrcpy(workgroup,lp_workgroup());
938 if (!*my_netbios_name) {
939 pstrcpy(my_netbios_name, myhostname());
941 strupper_m(my_netbios_name);