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
;
54 static BOOL smbfs_has_unicode
= False
;
55 static BOOL smbfs_has_lfs
= False
;
57 static void usage(void);
59 static void exit_parent(int sig
)
61 /* parent simply exits when child says go... */
65 static void daemonize(void)
70 signal( SIGTERM
, exit_parent
);
72 if ((child_pid
= sys_fork()) < 0) {
73 DEBUG(0,("could not fork\n"));
78 j
= waitpid( child_pid
, &status
, 0 );
80 if( EINTR
== errno
) {
88 /* If we get here - the child exited with some error status */
89 if (WIFSIGNALED(status
))
90 exit(128 + WTERMSIG(status
));
92 exit(WEXITSTATUS(status
));
95 signal( SIGTERM
, SIG_DFL
);
99 static void close_our_files(int client_fd
)
102 struct rlimit limits
;
104 getrlimit(RLIMIT_NOFILE
,&limits
);
105 for (i
= 0; i
< limits
.rlim_max
; i
++) {
112 static void usr1_handler(int x
)
118 /*****************************************************
119 return a connection to a server
120 *******************************************************/
121 static struct cli_state
*do_connection(char *the_service
)
124 struct nmb_name called
, calling
;
130 if (the_service
[0] != '\\' || the_service
[1] != '\\') {
135 pstrcpy(server
, the_service
+2);
136 share
= strchr_m(server
,'\\');
146 make_nmb_name(&calling
, my_netbios_name
, 0x0);
147 make_nmb_name(&called
, server
, 0x20);
151 if (have_ip
) ip
= dest_ip
;
153 /* have to open a new connection */
154 if (!(c
=cli_initialise(NULL
)) || (cli_set_port(c
, smb_port
) != smb_port
) ||
155 !cli_connect(c
, server_n
, &ip
)) {
156 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n
));
163 /* SPNEGO doesn't work till we get NTSTATUS error support */
164 /* But it is REQUIRED for kerberos authentication */
165 if(!use_kerberos
) c
->use_spnego
= False
;
167 /* The kernel doesn't yet know how to sign it's packets */
168 c
->sign_info
.allow_smb_signing
= False
;
170 /* Use kerberos authentication if specified */
171 c
->use_kerberos
= use_kerberos
;
173 if (!cli_session_request(c
, &calling
, &called
)) {
175 DEBUG(0,("%d: session request to %s failed (%s)\n",
176 sys_getpid(), called
.name
, cli_errstr(c
)));
178 if ((p
=strchr_m(called
.name
, '.'))) {
182 if (strcmp(called
.name
, "*SMBSERVER")) {
183 make_nmb_name(&called
, "*SMBSERVER", 0x20);
189 DEBUG(4,("%d: session request ok\n", sys_getpid()));
191 if (!cli_negprot(c
)) {
192 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
198 char *pass
= getpass("Password: ");
200 pstrcpy(password
, pass
);
204 /* This should be right for current smbfs. Future versions will support
205 large files as well as unicode and oplocks. */
206 c
->capabilities
&= ~(CAP_NT_SMBS
| CAP_NT_FIND
| CAP_LEVEL_II_OPLOCKS
);
208 c
->capabilities
&= ~CAP_LARGE_FILES
;
209 if (!smbfs_has_unicode
)
210 c
->capabilities
&= ~CAP_UNICODE
;
211 if (!status32_smbfs
) {
212 c
->capabilities
&= ~CAP_STATUS32
;
213 c
->force_dos_errors
= True
;
216 if (!cli_session_setup(c
, username
,
217 password
, strlen(password
),
218 password
, strlen(password
),
220 /* if a password was not supplied then try again with a
222 if (password
[0] || !username
[0] ||
223 !cli_session_setup(c
, "", "", 0, "", 0, workgroup
)) {
224 DEBUG(0,("%d: session setup failed: %s\n",
225 sys_getpid(), cli_errstr(c
)));
229 DEBUG(0,("Anonymous login successful\n"));
232 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
234 if (!cli_send_tconX(c
, share
, "?????",
235 password
, strlen(password
)+1)) {
236 DEBUG(0,("%d: tree connect failed: %s\n",
237 sys_getpid(), cli_errstr(c
)));
242 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
250 /****************************************************************************
251 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
252 Code blatently stolen from smbumount.c
254 ****************************************************************************/
255 static void smb_umount(char *mount_point
)
263 This routine only gets called to the scene of a disaster
264 to shoot the survivors... A connection that was working
265 has now apparently failed. We have an active mount point
266 (presumably) that we need to dump. If we get errors along
267 the way - make some noise, but we are already turning out
268 the lights to exit anyways...
270 if (umount(mount_point
) != 0) {
271 DEBUG(0,("%d: Could not umount %s: %s\n",
272 sys_getpid(), mount_point
, strerror(errno
)));
276 if ((fd
= open(MOUNTED
"~", O_RDWR
|O_CREAT
|O_EXCL
, 0600)) == -1) {
277 DEBUG(0,("%d: Can't get "MOUNTED
"~ lock file", sys_getpid()));
283 if ((mtab
= setmntent(MOUNTED
, "r")) == NULL
) {
284 DEBUG(0,("%d: Can't open " MOUNTED
": %s\n",
285 sys_getpid(), strerror(errno
)));
289 #define MOUNTED_TMP MOUNTED".tmp"
291 if ((new_mtab
= setmntent(MOUNTED_TMP
, "w")) == NULL
) {
292 DEBUG(0,("%d: Can't open " MOUNTED_TMP
": %s\n",
293 sys_getpid(), strerror(errno
)));
298 while ((mnt
= getmntent(mtab
)) != NULL
) {
299 if (strcmp(mnt
->mnt_dir
, mount_point
) != 0) {
300 addmntent(new_mtab
, mnt
);
306 if (fchmod (fileno (new_mtab
), S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) < 0) {
307 DEBUG(0,("%d: Error changing mode of %s: %s\n",
308 sys_getpid(), MOUNTED_TMP
, strerror(errno
)));
314 if (rename(MOUNTED_TMP
, MOUNTED
) < 0) {
315 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
316 sys_getpid(), MOUNTED
, MOUNTED_TMP
, strerror(errno
)));
320 if (unlink(MOUNTED
"~") == -1) {
321 DEBUG(0,("%d: Can't remove "MOUNTED
"~", sys_getpid()));
328 * Call the smbfs ioctl to install a connection socket,
329 * then wait for a signal to reconnect. Note that we do
330 * not exit after open_sockets() or send_login() errors,
331 * as the smbfs mount would then have no way to recover.
333 static void send_fs_socket(char *the_service
, char *mount_point
, struct cli_state
*c
)
335 int fd
, closed
= 0, res
= 1;
336 pid_t parentpid
= getppid();
337 struct smb_conn_opt conn_options
;
339 memset(&conn_options
, 0, sizeof(conn_options
));
342 if ((fd
= open(mount_point
, O_RDONLY
)) < 0) {
343 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
344 sys_getpid(), mount_point
));
348 conn_options
.fd
= c
->fd
;
349 conn_options
.protocol
= c
->protocol
;
350 conn_options
.case_handling
= SMB_CASE_DEFAULT
;
351 conn_options
.max_xmit
= c
->max_xmit
;
352 conn_options
.server_uid
= c
->vuid
;
353 conn_options
.tid
= c
->cnum
;
354 conn_options
.secmode
= c
->sec_mode
;
355 conn_options
.rawmode
= 0;
356 conn_options
.sesskey
= c
->sesskey
;
357 conn_options
.maxraw
= 0;
358 conn_options
.capabilities
= c
->capabilities
;
359 conn_options
.serverzone
= c
->serverzone
/60;
361 res
= ioctl(fd
, SMB_IOC_NEWCONN
, &conn_options
);
363 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
370 /* Ok... We are going to kill the parent. Now
371 is the time to break the process group... */
373 /* Send a signal to the parent to terminate */
374 kill(parentpid
, SIGTERM
);
380 /* This looks wierd but we are only closing the userspace
381 side, the connection has already been passed to smbfs and
382 it has increased the usage count on the socket.
384 If we don't do this we will "leak" sockets and memory on
385 each reconnection we have to make. */
386 c
->smb_rw_error
= DO_NOT_DO_TDIS
;
391 /* redirect stdout & stderr since we can't know that
392 the library functions we use are using DEBUG. */
393 if ( (fd
= open("/dev/null", O_WRONLY
)) < 0)
394 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
397 dup2(fd
, STDOUT_FILENO
);
398 dup2(fd
, STDERR_FILENO
);
402 /* here we are no longer interactive */
403 set_remote_machine_name("smbmount", False
); /* sneaky ... */
404 setup_logging("mount.smbfs", False
);
406 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service
, sys_getpid()));
411 /* Wait for a signal from smbfs ... but don't continue
412 until we actually get a new connection. */
414 CatchSignal(SIGUSR1
, &usr1_handler
);
416 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
417 c
= do_connection(the_service
);
421 smb_umount(mount_point
);
422 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
430 static void init_mount(void)
432 char mount_point
[PATH_MAX
+1];
439 if (realpath(mpoint
, mount_point
) == NULL
) {
440 fprintf(stderr
, "Could not resolve mount point %s\n", mpoint
);
445 c
= do_connection(service
);
447 fprintf(stderr
,"SMB connection failed\n");
452 Set up to return as a daemon child and wait in the parent
453 until the child say it's ready...
457 pstrcpy(svc2
, service
);
458 string_replace(svc2
, '\\','/');
459 string_replace(svc2
, ' ','_');
461 memset(args
, 0, sizeof(args
[0])*20);
464 args
[i
++] = "smbmnt";
466 args
[i
++] = mount_point
;
474 slprintf(tmp
, sizeof(tmp
)-1, "%d", mount_uid
);
476 args
[i
++] = smb_xstrdup(tmp
);
479 slprintf(tmp
, sizeof(tmp
)-1, "%d", mount_gid
);
481 args
[i
++] = smb_xstrdup(tmp
);
484 slprintf(tmp
, sizeof(tmp
)-1, "0%o", mount_fmask
);
486 args
[i
++] = smb_xstrdup(tmp
);
489 slprintf(tmp
, sizeof(tmp
)-1, "0%o", mount_dmask
);
491 args
[i
++] = smb_xstrdup(tmp
);
498 if (sys_fork() == 0) {
501 asprintf(&smbmnt_path
, "%s/smbmnt", dyn_BINDIR
);
503 if (file_exist(smbmnt_path
, NULL
)) {
504 execv(smbmnt_path
, args
);
506 "smbfs/init_mount: execv of %s failed. Error was %s.",
507 smbmnt_path
, strerror(errno
));
509 execvp("smbmnt", args
);
511 "smbfs/init_mount: execv of %s failed. Error was %s.",
512 "smbmnt", strerror(errno
));
518 if (waitpid(-1, &status
, 0) == -1) {
519 fprintf(stderr
,"waitpid failed: Error was %s", strerror(errno
) );
520 /* FIXME: do some proper error handling */
524 if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0) {
525 fprintf(stderr
,"smbmnt failed: %d\n", WEXITSTATUS(status
));
526 /* FIXME: do some proper error handling */
528 } else if (WIFSIGNALED(status
)) {
529 fprintf(stderr
, "smbmnt killed by signal %d\n", WTERMSIG(status
));
533 /* Ok... This is the rubicon for that mount point... At any point
534 after this, if the connections fail and can not be reconstructed
535 for any reason, we will have to unmount the mount point. There
536 is no exit from the next call...
538 send_fs_socket(service
, mount_point
, c
);
542 /****************************************************************************
543 get a password from a a file or file descriptor
544 exit on failure (from smbclient, move to libsmb or shared .c file?)
545 ****************************************************************************/
546 static void get_password_file(void)
550 BOOL close_it
= False
;
554 if ((p
= getenv("PASSWD_FD")) != NULL
) {
555 pstrcpy(spec
, "descriptor ");
557 sscanf(p
, "%d", &fd
);
559 } else if ((p
= getenv("PASSWD_FILE")) != NULL
) {
560 fd
= sys_open(p
, O_RDONLY
, 0);
563 fprintf(stderr
, "Error opening PASSWD_FILE %s: %s\n",
564 spec
, strerror(errno
));
570 for(p
= pass
, *p
= '\0'; /* ensure that pass is null-terminated */
571 p
&& p
- pass
< sizeof(pass
);) {
572 switch (read(fd
, p
, 1)) {
574 if (*p
!= '\n' && *p
!= '\0') {
575 *++p
= '\0'; /* advance p, and null-terminate pass */
580 *p
= '\0'; /* null-terminate it, just in case... */
581 p
= NULL
; /* then force the loop condition to become false */
584 fprintf(stderr
, "Error reading password from file %s: %s\n",
585 spec
, "empty password\n");
590 fprintf(stderr
, "Error reading password from file %s: %s\n",
591 spec
, strerror(errno
));
595 pstrcpy(password
, pass
);
600 /****************************************************************************
601 get username and password from a credentials file
602 exit on failure (from smbclient, move to libsmb or shared .c file?)
603 ****************************************************************************/
604 static void read_credentials_file(char *filename
)
609 char *ptr
, *val
, *param
;
611 if ((auth
=sys_fopen(filename
, "r")) == NULL
)
613 /* fail if we can't open the credentials file */
614 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
620 /* get a line from the file */
621 if (!fgets (buf
, sizeof(buf
), auth
))
625 if ((len
) && (buf
[len
-1]=='\n'))
633 /* break up the line into parameter & value.
634 will need to eat a little whitespace possibly */
636 if (!(ptr
= strchr (buf
, '=')))
641 /* eat leading white space */
642 while ((*val
!='\0') && ((*val
==' ') || (*val
=='\t')))
645 if (strwicmp("password", param
) == 0)
647 pstrcpy(password
, val
);
650 else if (strwicmp("username", param
) == 0) {
651 pstrcpy(username
, val
);
654 memset(buf
, 0, sizeof(buf
));
660 /****************************************************************************
662 ****************************************************************************/
663 static void usage(void)
665 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
667 printf("Version %s\n\n",SAMBA_VERSION_STRING
);
671 username=<arg> SMB username\n\
672 password=<arg> SMB password\n\
673 credentials=<filename> file with username/password\n\
674 krb use kerberos (active directory)\n\
675 netbiosname=<arg> source NetBIOS name\n\
676 uid=<arg> mount uid or username\n\
677 gid=<arg> mount gid or groupname\n\
678 port=<arg> remote SMB port number\n\
679 fmask=<arg> file umask\n\
680 dmask=<arg> directory umask\n\
681 debug=<arg> debug level\n\
682 ip=<arg> destination host or IP address\n\
683 workgroup=<arg> workgroup on destination\n\
684 sockopt=<arg> TCP socket options\n\
685 scope=<arg> NetBIOS scope\n\
686 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
687 codepage=<arg> server codepage (cp850)\n\
688 unicode use unicode when communicating with server\n\
689 lfs large file system support\n\
690 ttl=<arg> dircache time to live\n\
691 guest don't prompt for a password\n\
692 ro mount read-only\n\
693 rw mount read-write\n\
695 This command is designed to be run from within /bin/mount by giving\n\
696 the option '-t smbfs'. For example:\n\
697 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
702 /****************************************************************************
703 Argument parsing for mount.smbfs interface
704 mount will call us like this:
705 mount.smbfs device mountpoint -o <options>
707 <options> is never empty, containing at least rw or ro
708 ****************************************************************************/
709 static void parse_mount_smb(int argc
, char **argv
)
718 /* FIXME: This function can silently fail if the arguments are
719 * not in the expected order.
721 > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
722 > requires that one gives "-o" before further options like username=...
723 > . Without -o, the username=.. setting is *silently* ignored. I've
724 > spent about an hour trying to find out why I couldn't log in now..
729 if (argc
< 2 || argv
[1][0] == '-') {
734 pstrcpy(service
, argv
[1]);
735 pstrcpy(mpoint
, argv
[2]);
737 /* Convert any '/' characters in the service name to
739 string_replace(service
, '/','\\');
743 opt
= getopt(argc
, argv
, "o:");
752 * option parsing from nfsmount.c (util-linux-2.9u)
754 for (opts
= strtok(optarg
, ","); opts
; opts
= strtok(NULL
, ",")) {
755 DEBUG(3, ("opts: %s\n", opts
));
756 if ((opteq
= strchr_m(opts
, '='))) {
757 val
= atoi(opteq
+ 1);
760 if (!strcmp(opts
, "username") ||
761 !strcmp(opts
, "logon")) {
764 pstrcpy(username
,opteq
+1);
765 if ((lp
=strchr_m(username
,'%'))) {
767 pstrcpy(password
,lp
+1);
769 memset(strchr_m(opteq
+1,'%')+1,'X',strlen(password
));
771 if ((lp
=strchr_m(username
,'/'))) {
773 pstrcpy(workgroup
,lp
+1);
775 } else if(!strcmp(opts
, "passwd") ||
776 !strcmp(opts
, "password")) {
777 pstrcpy(password
,opteq
+1);
779 memset(opteq
+1,'X',strlen(password
));
780 } else if(!strcmp(opts
, "credentials")) {
781 pstrcpy(credentials
,opteq
+1);
782 } else if(!strcmp(opts
, "netbiosname")) {
783 pstrcpy(my_netbios_name
,opteq
+1);
784 } else if(!strcmp(opts
, "uid")) {
785 mount_uid
= nametouid(opteq
+1);
786 } else if(!strcmp(opts
, "gid")) {
787 mount_gid
= nametogid(opteq
+1);
788 } else if(!strcmp(opts
, "port")) {
790 } else if(!strcmp(opts
, "fmask")) {
791 mount_fmask
= strtol(opteq
+1, NULL
, 8);
792 } else if(!strcmp(opts
, "dmask")) {
793 mount_dmask
= strtol(opteq
+1, NULL
, 8);
794 } else if(!strcmp(opts
, "debug")) {
796 } else if(!strcmp(opts
, "ip")) {
797 dest_ip
= *interpret_addr2(opteq
+1);
798 if (is_zero_ip(dest_ip
)) {
799 fprintf(stderr
,"Can't resolve address %s\n", opteq
+1);
803 } else if(!strcmp(opts
, "workgroup")) {
804 pstrcpy(workgroup
,opteq
+1);
805 } else if(!strcmp(opts
, "sockopt")) {
806 pstrcpy(user_socket_options
,opteq
+1);
807 } else if(!strcmp(opts
, "scope")) {
808 set_global_scope(opteq
+1);
810 slprintf(p
, sizeof(pstring
) - (p
- options
) - 1, "%s=%s,", opts
, opteq
+1);
815 if(!strcmp(opts
, "nocaps")) {
816 fprintf(stderr
, "Unhandled option: %s\n", opteq
+1);
818 } else if(!strcmp(opts
, "guest")) {
821 } else if(!strcmp(opts
, "krb")) {
826 fprintf(stderr
, "Warning: kerberos support will only work for samba servers\n");
828 fprintf(stderr
,"No kerberos support compiled in\n");
831 } else if(!strcmp(opts
, "rw")) {
833 } else if(!strcmp(opts
, "ro")) {
835 } else if(!strcmp(opts
, "unicode")) {
836 smbfs_has_unicode
= True
;
837 } else if(!strcmp(opts
, "lfs")) {
838 smbfs_has_lfs
= True
;
840 strncpy(p
, opts
, sizeof(pstring
) - (p
- options
) - 1);
854 *(p
-1) = 0; /* remove trailing , */
855 DEBUG(3,("passthrough options '%s'\n", options
));
859 /****************************************************************************
861 ****************************************************************************/
862 int main(int argc
,char *argv
[])
870 /* here we are interactive, even if run from autofs */
871 setup_logging("mount.smbfs",True
);
873 #if 0 /* JRA - Urban says not needed ? */
874 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
875 is to not announce any unicode capabilities as current smbfs does
877 p
= getenv("CLI_FORCE_ASCII");
878 if (p
&& !strcmp(p
, "false"))
879 unsetenv("CLI_FORCE_ASCII");
881 setenv("CLI_FORCE_ASCII", "true", 1);
884 in_client
= True
; /* Make sure that we tell lp_load we are */
886 if (getenv("USER")) {
887 pstrcpy(username
,getenv("USER"));
889 if ((p
=strchr_m(username
,'%'))) {
891 pstrcpy(password
,p
+1);
893 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password
));
895 strupper_m(username
);
898 if (getenv("PASSWD")) {
899 pstrcpy(password
,getenv("PASSWD"));
903 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
908 if (*username
== 0 && getenv("LOGNAME")) {
909 pstrcpy(username
,getenv("LOGNAME"));
912 if (!lp_load(dyn_CONFIGFILE
,True
,False
,False
)) {
913 fprintf(stderr
, "Can't load %s - run testparm to debug it\n",
917 parse_mount_smb(argc
, argv
);
919 if (use_kerberos
&& !got_user
) {
923 if (*credentials
!= 0) {
924 read_credentials_file(credentials
);
927 DEBUG(3,("mount.smbfs started (version %s)\n", SAMBA_VERSION_STRING
));
929 if (*workgroup
== 0) {
930 pstrcpy(workgroup
,lp_workgroup());
934 if (!*my_netbios_name
) {
935 pstrcpy(my_netbios_name
, myhostname());
937 strupper_m(my_netbios_name
);