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/>.
21 #include "system/passwd.h"
24 #include <asm/types.h>
25 #include <linux/smb_fs.h>
27 static pstring credentials;
28 static pstring my_netbios_name;
29 static pstring password;
30 static pstring username;
31 static pstring workgroup;
32 static pstring mpoint;
33 static pstring service;
34 static pstring options;
36 static struct in_addr dest_ip;
38 static int smb_port = 0;
41 static uid_t mount_uid;
42 static gid_t mount_gid;
44 static uint_t mount_fmask;
45 static uint_t mount_dmask;
46 static bool use_kerberos;
47 /* TODO: Add code to detect smbfs version in kernel */
48 static bool status32_smbfs = false;
50 static void usage(void);
52 static void exit_parent(int sig)
54 /* parent simply exits when child says go... */
58 static void daemonize(void)
63 signal( SIGTERM, exit_parent );
65 if ((child_pid = sys_fork()) < 0) {
66 DEBUG(0,("could not fork\n"));
71 j = waitpid( child_pid, &status, 0 );
73 if( EINTR == errno ) {
81 /* If we get here - the child exited with some error status */
82 if (WIFSIGNALED(status))
83 exit(128 + WTERMSIG(status));
85 exit(WEXITSTATUS(status));
88 signal( SIGTERM, SIG_DFL );
92 static void close_our_files(int client_fd)
97 getrlimit(RLIMIT_NOFILE,&limits);
98 for (i = 0; i< limits.rlim_max; i++) {
105 static void usr1_handler(int x)
111 /*****************************************************
112 return a connection to a server
113 *******************************************************/
114 static struct smbcli_state *do_connection(const char *the_service, bool unicode, int maxprotocol)
116 struct smbcli_state *c;
117 struct nmb_name called, calling;
123 if (the_service[0] != '\\' || the_service[1] != '\\') {
128 pstrcpy(server, the_service+2);
129 share = strchr_m(server,'\\');
139 make_nmb_name(&calling, my_netbios_name, 0x0);
140 choose_called_name(&called, server, 0x20);
144 if (have_ip) ip = dest_ip;
146 /* have to open a new connection */
147 if (!(c=smbcli_initialise(NULL)) || (smbcli_set_port(c, smb_port) != smb_port) ||
148 !smbcli_connect(c, server_n, &ip)) {
149 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
156 /* SPNEGO doesn't work till we get NTSTATUS error support */
157 /* But it is REQUIRED for kerberos authentication */
158 if(!use_kerberos) c->use_spnego = false;
160 /* The kernel doesn't yet know how to sign it's packets */
161 c->sign_info.allow_smb_signing = false;
163 /* Use kerberos authentication if specified */
164 c->use_kerberos = use_kerberos;
166 if (!smbcli_session_request(c, &calling, &called)) {
168 DEBUG(0,("%d: session request to %s failed (%s)\n",
169 sys_getpid(), called.name, smbcli_errstr(c)));
171 if ((p=strchr_m(called.name, '.'))) {
175 if (strcmp(called.name, "*SMBSERVER")) {
176 make_nmb_name(&called , "*SMBSERVER", 0x20);
182 DEBUG(4,("%d: session request ok\n", sys_getpid()));
184 if (!smbcli_negprot(c, unicode, maxprotocol)) {
185 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
191 char *pass = getpass("Password: ");
193 pstrcpy(password, pass);
197 /* This should be right for current smbfs. Future versions will support
198 large files as well as unicode and oplocks. */
199 if (status32_smbfs) {
200 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
201 CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
204 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
205 CAP_NT_FIND | CAP_STATUS32 |
206 CAP_LEVEL_II_OPLOCKS);
207 c->force_dos_errors = true;
210 if (!smbcli_session_setup(c, username,
211 password, strlen(password),
212 password, strlen(password),
214 /* if a password was not supplied then try again with a
216 if (password[0] || !username[0] ||
217 !smbcli_session_setup(c, "", "", 0, "", 0, workgroup)) {
218 DEBUG(0,("%d: session setup failed: %s\n",
219 sys_getpid(), smbcli_errstr(c)));
223 DEBUG(0,("Anonymous login successful\n"));
226 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
228 if (!smbcli_tconX(c, share, "?????", password, strlen(password)+1)) {
229 DEBUG(0,("%d: tree connect failed: %s\n",
230 sys_getpid(), smbcli_errstr(c)));
235 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
243 /****************************************************************************
244 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
245 Code blatently stolen from smbumount.c
247 ****************************************************************************/
248 static void smb_umount(const char *mount_point)
256 This routine only gets called to the scene of a disaster
257 to shoot the survivors... A connection that was working
258 has now apparently failed. We have an active mount point
259 (presumably) that we need to dump. If we get errors along
260 the way - make some noise, but we are already turning out
261 the lights to exit anyways...
263 if (umount(mount_point) != 0) {
264 DEBUG(0,("%d: Could not umount %s: %s\n",
265 sys_getpid(), mount_point, strerror(errno)));
269 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
270 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
276 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
277 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
278 sys_getpid(), strerror(errno)));
282 #define MOUNTED_TMP MOUNTED".tmp"
284 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
285 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
286 sys_getpid(), strerror(errno)));
291 while ((mnt = getmntent(mtab)) != NULL) {
292 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
293 addmntent(new_mtab, mnt);
299 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
300 DEBUG(0,("%d: Error changing mode of %s: %s\n",
301 sys_getpid(), MOUNTED_TMP, strerror(errno)));
307 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
308 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
309 sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
313 if (unlink(MOUNTED"~") == -1) {
314 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
321 * Call the smbfs ioctl to install a connection socket,
322 * then wait for a signal to reconnect. Note that we do
323 * not exit after open_sockets() or send_login() errors,
324 * as the smbfs mount would then have no way to recover.
326 static void send_fs_socket(struct loadparm_context *lp_ctx,
327 const char *the_service, const char *mount_point, struct smbcli_state *c)
329 int fd, closed = 0, res = 1;
330 pid_t parentpid = getppid();
331 struct smb_conn_opt conn_options;
333 memset(&conn_options, 0, sizeof(conn_options));
336 if ((fd = open(mount_point, O_RDONLY)) < 0) {
337 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
338 sys_getpid(), mount_point));
342 conn_options.fd = c->fd;
343 conn_options.protocol = c->protocol;
344 conn_options.case_handling = SMB_CASE_DEFAULT;
345 conn_options.max_xmit = c->max_xmit;
346 conn_options.server_uid = c->vuid;
347 conn_options.tid = c->cnum;
348 conn_options.secmode = c->sec_mode;
349 conn_options.rawmode = 0;
350 conn_options.sesskey = c->sesskey;
351 conn_options.maxraw = 0;
352 conn_options.capabilities = c->capabilities;
353 conn_options.serverzone = c->serverzone/60;
355 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
357 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
364 /* Ok... We are going to kill the parent. Now
365 is the time to break the process group... */
367 /* Send a signal to the parent to terminate */
368 kill(parentpid, SIGTERM);
374 /* This looks wierd but we are only closing the userspace
375 side, the connection has already been passed to smbfs and
376 it has increased the usage count on the socket.
378 If we don't do this we will "leak" sockets and memory on
379 each reconnection we have to make. */
384 /* redirect stdout & stderr since we can't know that
385 the library functions we use are using DEBUG. */
386 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
387 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
390 dup2(fd, STDOUT_FILENO);
391 dup2(fd, STDERR_FILENO);
395 /* here we are no longer interactive */
396 set_remote_machine_name("smbmount"); /* sneaky ... */
397 setup_logging("mount.smbfs", DEBUG_STDERR);
399 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
404 /* Wait for a signal from smbfs ... but don't continue
405 until we actually get a new connection. */
407 CatchSignal(SIGUSR1, &usr1_handler);
409 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
410 c = do_connection(the_service,
412 lp_cli_maxprotocol(lp_ctx));
416 smb_umount(mount_point);
417 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
425 static void init_mount(struct loadparm_context *lp_ctx)
427 char mount_point[MAXPATHLEN+1];
430 struct smbcli_state *c;
434 if (realpath(mpoint, mount_point) == NULL) {
435 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
440 c = do_connection(service, lp_unicode(lp_ctx), lp_cli_maxprotocol(lp_ctx));
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)) {
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(lp_ctx, 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 = 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[])
856 struct loadparm_context *lp_ctx;
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 if (getenv("USER")) {
875 pstrcpy(username,getenv("USER"));
877 if ((p=strchr_m(username,'%'))) {
879 pstrcpy(password,p+1);
881 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
886 if (getenv("PASSWD")) {
887 pstrcpy(password, getenv("PASSWD"));
891 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
896 if (*username == 0 && getenv("LOGNAME")) {
897 pstrcpy(username,getenv("LOGNAME"));
900 lp_ctx = global_loadparm = loadparm_init(talloc_autofree_context());
902 if (!lp_load(lp_ctx, dyn_CONFIGFILE)) {
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());
923 if (!*my_netbios_name) {
924 pstrcpy(my_netbios_name, myhostname());
926 strupper(my_netbios_name);