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
;
28 extern pstring user_socket_options
;
30 static pstring credentials
;
31 static pstring my_netbios_name
;
32 static pstring password
;
33 static pstring username
;
34 static pstring workgroup
;
35 static pstring mpoint
;
36 static pstring service
;
37 static pstring options
;
39 static struct in_addr dest_ip
;
41 static int smb_port
= 0;
44 static uid_t mount_uid
;
45 static gid_t mount_gid
;
47 static unsigned mount_fmask
;
48 static unsigned mount_dmask
;
49 static BOOL use_kerberos
;
50 /* TODO: Add code to detect smbfs version in kernel */
51 static BOOL status32_smbfs
= False
;
52 static BOOL smbfs_has_unicode
= False
;
53 static BOOL smbfs_has_lfs
= 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 c
->capabilities
&= ~(CAP_NT_SMBS
| CAP_NT_FIND
| CAP_LEVEL_II_OPLOCKS
);
206 c
->capabilities
&= ~CAP_LARGE_FILES
;
207 if (!smbfs_has_unicode
)
208 c
->capabilities
&= ~CAP_UNICODE
;
209 if (!status32_smbfs
) {
210 c
->capabilities
&= ~CAP_STATUS32
;
211 c
->force_dos_errors
= True
;
214 if (!cli_session_setup(c
, username
,
215 password
, strlen(password
),
216 password
, strlen(password
),
218 /* if a password was not supplied then try again with a
220 if (password
[0] || !username
[0] ||
221 !cli_session_setup(c
, "", "", 0, "", 0, workgroup
)) {
222 DEBUG(0,("%d: session setup failed: %s\n",
223 sys_getpid(), cli_errstr(c
)));
227 DEBUG(0,("Anonymous login successful\n"));
230 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
232 if (!cli_send_tconX(c
, share
, "?????",
233 password
, strlen(password
)+1)) {
234 DEBUG(0,("%d: tree connect failed: %s\n",
235 sys_getpid(), cli_errstr(c
)));
240 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
248 /****************************************************************************
249 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
250 Code blatently stolen from smbumount.c
252 ****************************************************************************/
253 static void smb_umount(char *mount_point
)
261 This routine only gets called to the scene of a disaster
262 to shoot the survivors... A connection that was working
263 has now apparently failed. We have an active mount point
264 (presumably) that we need to dump. If we get errors along
265 the way - make some noise, but we are already turning out
266 the lights to exit anyways...
268 if (umount(mount_point
) != 0) {
269 DEBUG(0,("%d: Could not umount %s: %s\n",
270 sys_getpid(), mount_point
, strerror(errno
)));
274 if ((fd
= open(MOUNTED
"~", O_RDWR
|O_CREAT
|O_EXCL
, 0600)) == -1) {
275 DEBUG(0,("%d: Can't get "MOUNTED
"~ lock file", sys_getpid()));
281 if ((mtab
= setmntent(MOUNTED
, "r")) == NULL
) {
282 DEBUG(0,("%d: Can't open " MOUNTED
": %s\n",
283 sys_getpid(), strerror(errno
)));
287 #define MOUNTED_TMP MOUNTED".tmp"
289 if ((new_mtab
= setmntent(MOUNTED_TMP
, "w")) == NULL
) {
290 DEBUG(0,("%d: Can't open " MOUNTED_TMP
": %s\n",
291 sys_getpid(), strerror(errno
)));
296 while ((mnt
= getmntent(mtab
)) != NULL
) {
297 if (strcmp(mnt
->mnt_dir
, mount_point
) != 0) {
298 addmntent(new_mtab
, mnt
);
304 if (fchmod (fileno (new_mtab
), S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) < 0) {
305 DEBUG(0,("%d: Error changing mode of %s: %s\n",
306 sys_getpid(), MOUNTED_TMP
, strerror(errno
)));
312 if (rename(MOUNTED_TMP
, MOUNTED
) < 0) {
313 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
314 sys_getpid(), MOUNTED
, MOUNTED_TMP
, strerror(errno
)));
318 if (unlink(MOUNTED
"~") == -1) {
319 DEBUG(0,("%d: Can't remove "MOUNTED
"~", sys_getpid()));
326 * Call the smbfs ioctl to install a connection socket,
327 * then wait for a signal to reconnect. Note that we do
328 * not exit after open_sockets() or send_login() errors,
329 * as the smbfs mount would then have no way to recover.
331 static void send_fs_socket(char *the_service
, char *mount_point
, struct cli_state
*c
)
333 int fd
, closed
= 0, res
= 1;
334 pid_t parentpid
= getppid();
335 struct smb_conn_opt conn_options
;
337 memset(&conn_options
, 0, sizeof(conn_options
));
340 if ((fd
= open(mount_point
, O_RDONLY
)) < 0) {
341 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
342 sys_getpid(), mount_point
));
346 conn_options
.fd
= c
->fd
;
347 conn_options
.protocol
= c
->protocol
;
348 conn_options
.case_handling
= SMB_CASE_DEFAULT
;
349 conn_options
.max_xmit
= c
->max_xmit
;
350 conn_options
.server_uid
= c
->vuid
;
351 conn_options
.tid
= c
->cnum
;
352 conn_options
.secmode
= c
->sec_mode
;
353 conn_options
.rawmode
= 0;
354 conn_options
.sesskey
= c
->sesskey
;
355 conn_options
.maxraw
= 0;
356 conn_options
.capabilities
= c
->capabilities
;
357 conn_options
.serverzone
= c
->serverzone
/60;
359 res
= ioctl(fd
, SMB_IOC_NEWCONN
, &conn_options
);
361 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
368 /* Ok... We are going to kill the parent. Now
369 is the time to break the process group... */
371 /* Send a signal to the parent to terminate */
372 kill(parentpid
, SIGTERM
);
378 /* This looks wierd but we are only closing the userspace
379 side, the connection has already been passed to smbfs and
380 it has increased the usage count on the socket.
382 If we don't do this we will "leak" sockets and memory on
383 each reconnection we have to make. */
384 c
->smb_rw_error
= DO_NOT_DO_TDIS
;
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
[PATH_MAX
+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",SAMBA_VERSION_STRING
);
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 unicode use unicode when communicating with server\n\
687 lfs large file system support\n\
688 ttl=<arg> dircache time to live\n\
689 guest don't prompt for a password\n\
690 ro mount read-only\n\
691 rw mount read-write\n\
693 This command is designed to be run from within /bin/mount by giving\n\
694 the option '-t smbfs'. For example:\n\
695 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
700 /****************************************************************************
701 Argument parsing for mount.smbfs interface
702 mount will call us like this:
703 mount.smbfs device mountpoint -o <options>
705 <options> is never empty, containing at least rw or ro
706 ****************************************************************************/
707 static void parse_mount_smb(int argc
, char **argv
)
716 /* FIXME: This function can silently fail if the arguments are
717 * not in the expected order.
719 > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
720 > requires that one gives "-o" before further options like username=...
721 > . Without -o, the username=.. setting is *silently* ignored. I've
722 > spent about an hour trying to find out why I couldn't log in now..
727 if (argc
< 2 || argv
[1][0] == '-') {
732 pstrcpy(service
, argv
[1]);
733 pstrcpy(mpoint
, argv
[2]);
735 /* Convert any '/' characters in the service name to
737 string_replace(service
, '/','\\');
741 opt
= getopt(argc
, argv
, "o:");
750 * option parsing from nfsmount.c (util-linux-2.9u)
752 for (opts
= strtok(optarg
, ","); opts
; opts
= strtok(NULL
, ",")) {
753 DEBUG(3, ("opts: %s\n", opts
));
754 if ((opteq
= strchr_m(opts
, '='))) {
755 val
= atoi(opteq
+ 1);
758 if (!strcmp(opts
, "username") ||
759 !strcmp(opts
, "logon")) {
762 pstrcpy(username
,opteq
+1);
763 if ((lp
=strchr_m(username
,'%'))) {
765 pstrcpy(password
,lp
+1);
767 memset(strchr_m(opteq
+1,'%')+1,'X',strlen(password
));
769 if ((lp
=strchr_m(username
,'/'))) {
771 pstrcpy(workgroup
,lp
+1);
773 } else if(!strcmp(opts
, "passwd") ||
774 !strcmp(opts
, "password")) {
775 pstrcpy(password
,opteq
+1);
777 memset(opteq
+1,'X',strlen(password
));
778 } else if(!strcmp(opts
, "credentials")) {
779 pstrcpy(credentials
,opteq
+1);
780 } else if(!strcmp(opts
, "netbiosname")) {
781 pstrcpy(my_netbios_name
,opteq
+1);
782 } else if(!strcmp(opts
, "uid")) {
783 mount_uid
= nametouid(opteq
+1);
784 } else if(!strcmp(opts
, "gid")) {
785 mount_gid
= nametogid(opteq
+1);
786 } else if(!strcmp(opts
, "port")) {
788 } else if(!strcmp(opts
, "fmask")) {
789 mount_fmask
= strtol(opteq
+1, NULL
, 8);
790 } else if(!strcmp(opts
, "dmask")) {
791 mount_dmask
= strtol(opteq
+1, NULL
, 8);
792 } else if(!strcmp(opts
, "debug")) {
794 } else if(!strcmp(opts
, "ip")) {
795 dest_ip
= *interpret_addr2(opteq
+1);
796 if (is_zero_ip(dest_ip
)) {
797 fprintf(stderr
,"Can't resolve address %s\n", opteq
+1);
801 } else if(!strcmp(opts
, "workgroup")) {
802 pstrcpy(workgroup
,opteq
+1);
803 } else if(!strcmp(opts
, "sockopt")) {
804 pstrcpy(user_socket_options
,opteq
+1);
805 } else if(!strcmp(opts
, "scope")) {
806 set_global_scope(opteq
+1);
808 slprintf(p
, sizeof(pstring
) - (p
- options
) - 1, "%s=%s,", opts
, opteq
+1);
813 if(!strcmp(opts
, "nocaps")) {
814 fprintf(stderr
, "Unhandled option: %s\n", opteq
+1);
816 } else if(!strcmp(opts
, "guest")) {
819 } else if(!strcmp(opts
, "krb")) {
824 fprintf(stderr
, "Warning: kerberos support will only work for samba servers\n");
826 fprintf(stderr
,"No kerberos support compiled in\n");
829 } else if(!strcmp(opts
, "rw")) {
831 } else if(!strcmp(opts
, "ro")) {
833 } else if(!strcmp(opts
, "unicode")) {
834 smbfs_has_unicode
= True
;
835 } else if(!strcmp(opts
, "lfs")) {
836 smbfs_has_lfs
= True
;
838 strncpy(p
, opts
, sizeof(pstring
) - (p
- options
) - 1);
852 *(p
-1) = 0; /* remove trailing , */
853 DEBUG(3,("passthrough options '%s'\n", options
));
857 /****************************************************************************
859 ****************************************************************************/
860 int main(int argc
,char *argv
[])
868 /* here we are interactive, even if run from autofs */
869 setup_logging("mount.smbfs",True
);
871 #if 0 /* JRA - Urban says not needed ? */
872 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
873 is to not announce any unicode capabilities as current smbfs does
875 p
= getenv("CLI_FORCE_ASCII");
876 if (p
&& !strcmp(p
, "false"))
877 unsetenv("CLI_FORCE_ASCII");
879 setenv("CLI_FORCE_ASCII", "true", 1);
882 in_client
= True
; /* Make sure that we tell lp_load we are */
884 if (getenv("USER")) {
885 pstrcpy(username
,getenv("USER"));
887 if ((p
=strchr_m(username
,'%'))) {
889 pstrcpy(password
,p
+1);
891 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password
));
893 strupper_m(username
);
896 if (getenv("PASSWD")) {
897 pstrcpy(password
,getenv("PASSWD"));
901 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
906 if (*username
== 0 && getenv("LOGNAME")) {
907 pstrcpy(username
,getenv("LOGNAME"));
910 if (!lp_load(dyn_CONFIGFILE
,True
,False
,False
)) {
911 fprintf(stderr
, "Can't load %s - run testparm to debug it\n",
915 parse_mount_smb(argc
, argv
);
917 if (use_kerberos
&& !got_user
) {
921 if (*credentials
!= 0) {
922 read_credentials_file(credentials
);
925 DEBUG(3,("mount.smbfs started (version %s)\n", SAMBA_VERSION_STRING
));
927 if (*workgroup
== 0) {
928 pstrcpy(workgroup
,lp_workgroup());
932 if (!*my_netbios_name
) {
933 pstrcpy(my_netbios_name
, myhostname());
935 strupper_m(my_netbios_name
);