2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2008 Steve French (sfrench@us.ibm.com)
4 Copyright (C) 2008 Jeremy Allison (jra@samba.org)
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/>. */
29 #include <sys/types.h>
30 #include <sys/mount.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
45 #define MOUNT_CIFS_VERSION_MAJOR "1"
46 #define MOUNT_CIFS_VERSION_MINOR "13"
48 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
51 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
54 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
55 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
57 #define MOUNT_CIFS_VENDOR_SUFFIX ""
58 #endif /* _SAMBA_BUILD_ */
59 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
62 #include "include/config.h"
73 /* private flags - clear these before passing to kernel */
74 #define MS_USERS 0x40000000
75 #define MS_USER 0x80000000
77 #define MAX_UNC_LEN 1024
80 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
83 #define MOUNT_PASSWD_SIZE 128
84 #define DOMAIN_SIZE 64
86 /* currently maximum length of IPv6 address string */
87 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
90 * By default, mount.cifs follows the conventions set forth by /bin/mount
91 * for user mounts. That is, it requires that the mount be listed in
92 * /etc/fstab with the "user" option when run as an unprivileged user and
93 * mount.cifs is setuid root.
95 * Older versions of mount.cifs however were "looser" in this regard. When
96 * made setuid root, a user could run mount.cifs directly and mount any share
97 * on a directory owned by that user.
99 * The legacy behavior is now disabled by default. To reenable it, set the
100 * following #define to true.
102 #define CIFS_LEGACY_SETUID_CHECK 0
105 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
106 * flags by default. These defaults can be changed here.
108 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
110 const char *thisprogram;
113 static int got_password = 0;
114 static int got_user = 0;
115 static int got_domain = 0;
116 static int got_ip = 0;
117 static int got_unc = 0;
118 static int got_uid = 0;
119 static int got_gid = 0;
120 static char * user_name = NULL;
121 static char * mountpassword = NULL;
122 char * domain_name = NULL;
123 char * prefixpath = NULL;
124 const char *cifs_fstype = "cifs";
126 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
127 * don't link to libreplace so need them here. */
129 /* like strncpy but does not 0 fill the buffer and always null
130 * terminates. bufsize is the size of the destination buffer */
133 static size_t strlcpy(char *d, const char *s, size_t bufsize)
135 size_t len = strlen(s);
137 if (bufsize <= 0) return 0;
138 if (len >= bufsize) len = bufsize-1;
145 /* like strncat but does not 0 fill the buffer and always null
146 * terminates. bufsize is the length of the buffer, which should
147 * be one more than the maximum resulting string length */
150 static size_t strlcat(char *d, const char *s, size_t bufsize)
152 size_t len1 = strlen(d);
153 size_t len2 = strlen(s);
154 size_t ret = len1 + len2;
156 if (len1+len2 >= bufsize) {
157 if (bufsize < (len1+1)) {
160 len2 = bufsize - (len1+1);
163 memcpy(d+len1, s, len2);
171 * If an unprivileged user is doing the mounting then we need to ensure
172 * that the entry is in /etc/fstab.
175 check_mountpoint(const char *progname, char *mountpoint)
180 /* does mountpoint exist and is it a directory? */
181 err = stat(".", &statbuf);
183 fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
184 mountpoint, strerror(errno));
188 if (!S_ISDIR(statbuf.st_mode)) {
189 fprintf(stderr, "%s: %s is not a directory!", progname,
194 #if CIFS_LEGACY_SETUID_CHECK
195 /* do extra checks on mountpoint for legacy setuid behavior */
196 if (!getuid() || geteuid())
199 if (statbuf.st_uid != getuid()) {
200 fprintf(stderr, "%s: %s is not owned by user\n", progname,
205 if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
206 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
210 #endif /* CIFS_LEGACY_SETUID_CHECK */
215 #if CIFS_LEGACY_SETUID_CHECK
217 check_fstab(const char *progname, char *mountpoint, char *devname,
222 #else /* CIFS_LEGACY_SETUID_CHECK */
224 check_fstab(const char *progname, char *mountpoint, char *devname,
230 /* make sure this mount is listed in /etc/fstab */
231 fstab = setmntent(_PATH_FSTAB, "r");
233 fprintf(stderr, "Couldn't open %s for reading!\n",
238 while((mnt = getmntent(fstab))) {
239 if (!strcmp(mountpoint, mnt->mnt_dir))
244 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
245 fprintf(stderr, "%s: permission denied: no match for "
246 "%s found in %s\n", progname, mountpoint,
252 * 'mount' munges the options from fstab before passing them
253 * to us. It is non-trivial to test that we have the correct
254 * set of options. We don't want to trust what the user
255 * gave us, so just take whatever is in /etc/fstab.
258 *options = strdup(mnt->mnt_opts);
261 #endif /* CIFS_LEGACY_SETUID_CHECK */
266 open nofollow - avoid symlink exposure?
267 get owner of dir see if matches self or if root
268 call system(umount argv) etc.
272 static char * check_for_domain(char **);
275 static void mount_cifs_usage(FILE *stream)
277 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
278 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
279 fprintf(stream, " to a local directory.\n\nOptions:\n");
280 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
281 fprintf(stream, "\nLess commonly used options:");
282 fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
283 fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
284 fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
285 fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
286 fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
287 fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
288 fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
289 fprintf(stream, "\n\nRarely used options:");
290 fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
291 fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
292 fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
293 fprintf(stream, "\n\nOptions are described in more detail in the manual page");
294 fprintf(stream, "\n\tman 8 mount.cifs\n");
295 fprintf(stream, "\nTo display the version number of the mount helper:");
296 fprintf(stream, "\n\t%s -V\n",thisprogram);
298 SAFE_FREE(mountpassword);
300 if (stream == stderr)
305 /* caller frees username if necessary */
306 static char * getusername(void) {
307 char *username = NULL;
308 struct passwd *password = getpwuid(getuid());
311 username = password->pw_name;
316 static int open_cred_file(char * file_name)
323 i = access(file_name, R_OK);
327 fs = fopen(file_name,"r");
330 line_buf = (char *)malloc(4096);
331 if(line_buf == NULL) {
336 while(fgets(line_buf,4096,fs)) {
337 /* parse line from credential file */
339 /* eat leading white space */
340 for(i=0;i<4086;i++) {
341 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
343 /* if whitespace - skip past it */
345 if (strncasecmp("username",line_buf+i,8) == 0) {
346 temp_val = strchr(line_buf + i,'=');
348 /* go past equals sign */
350 for(length = 0;length<4087;length++) {
351 if ((temp_val[length] == '\n')
352 || (temp_val[length] == '\0')) {
353 temp_val[length] = '\0';
358 fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
359 memset(line_buf,0,4096);
363 user_name = (char *)calloc(1 + length,1);
364 /* BB adding free of user_name string before exit,
365 not really necessary but would be cleaner */
366 strlcpy(user_name,temp_val, length+1);
369 } else if (strncasecmp("password",line_buf+i,8) == 0) {
370 temp_val = strchr(line_buf+i,'=');
372 /* go past equals sign */
374 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
375 if ((temp_val[length] == '\n')
376 || (temp_val[length] == '\0')) {
377 temp_val[length] = '\0';
381 if(length > MOUNT_PASSWD_SIZE) {
382 fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
383 memset(line_buf,0, 4096);
386 if(mountpassword == NULL) {
387 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
389 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
391 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
396 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
397 temp_val = strchr(line_buf+i,'=');
399 /* go past equals sign */
402 fprintf(stderr, "\nDomain %s\n",temp_val);
403 for(length = 0;length<DOMAIN_SIZE+1;length++) {
404 if ((temp_val[length] == '\n')
405 || (temp_val[length] == '\0')) {
406 temp_val[length] = '\0';
410 if(length > DOMAIN_SIZE) {
411 fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
414 if(domain_name == NULL) {
415 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
417 memset(domain_name,0,DOMAIN_SIZE);
419 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
432 static int get_password_from_file(int file_descript, char * filename)
438 if(mountpassword == NULL)
439 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
441 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
443 if (mountpassword == NULL) {
444 fprintf(stderr, "malloc failed\n");
448 if(filename != NULL) {
449 rc = access(filename, R_OK);
451 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
452 filename, strerror(errno));
455 file_descript = open(filename, O_RDONLY);
456 if(file_descript < 0) {
457 fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
458 strerror(errno),filename);
462 /* else file already open and fd provided */
464 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
465 rc = read(file_descript,&c,1);
467 fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
469 close(file_descript);
472 if(mountpassword[0] == 0) {
474 fprintf(stderr, "\nWarning: null password used since cifs password file empty");
477 } else /* read valid character */ {
478 if((c == 0) || (c == '\n')) {
479 mountpassword[i] = '\0';
482 mountpassword[i] = c;
485 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
486 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
490 if(filename != NULL) {
491 close(file_descript);
497 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
500 char * percent_char = NULL;
502 char * next_keyword = NULL;
510 if (!optionsp || !*optionsp)
514 /* BB fixme check for separator override BB */
518 snprintf(user,sizeof(user),"%u",getuid());
520 snprintf(group,sizeof(group),"%u",getgid());
523 /* while ((data = strsep(&options, ",")) != NULL) { */
524 while(data != NULL) {
525 /* check if ends with trailing comma */
529 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
530 /* data = next keyword */
531 /* value = next value ie stuff after equal sign */
533 next_keyword = strchr(data,','); /* BB handle sep= */
535 /* temporarily null terminate end of keyword=value pair */
539 /* temporarily null terminate keyword to make keyword and value distinct */
540 if ((value = strchr(data, '=')) != NULL) {
545 if (strncmp(data, "users",5) == 0) {
546 if(!value || !*value) {
547 *filesys_flags |= MS_USERS;
550 } else if (strncmp(data, "user_xattr",10) == 0) {
551 /* do nothing - need to skip so not parsed as user name */
552 } else if (strncmp(data, "user", 4) == 0) {
554 if (!value || !*value) {
555 if(data[4] == '\0') {
556 *filesys_flags |= MS_USER;
559 fprintf(stderr, "username specified with no parameter\n");
561 return 1; /* needs_arg; */
564 if (strnlen(value, 260) < 260) {
566 percent_char = strchr(value,'%');
569 if(mountpassword == NULL)
570 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
573 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
576 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
577 /* remove password from username */
578 while(*percent_char != 0) {
584 /* this is only case in which the user
585 name buf is not malloc - so we have to
586 check for domain name embedded within
587 the user name here since the later
588 call to check_for_domain will not be
590 domain_name = check_for_domain(&value);
592 fprintf(stderr, "username too long\n");
597 } else if (strncmp(data, "pass", 4) == 0) {
598 if (!value || !*value) {
600 fprintf(stderr, "\npassword specified twice, ignoring second\n");
603 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
605 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
607 mountpassword = strndup(value, MOUNT_PASSWD_SIZE);
608 if (!mountpassword) {
609 fprintf(stderr, "mount.cifs error: %s", strerror(ENOMEM));
616 fprintf(stderr, "password too long\n");
621 } else if (strncmp(data, "sec", 3) == 0) {
623 if (!strncmp(value, "none", 4) ||
624 !strncmp(value, "krb5", 4))
627 } else if (strncmp(data, "ip", 2) == 0) {
628 if (!value || !*value) {
629 fprintf(stderr, "target ip address argument missing");
630 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
632 fprintf(stderr, "ip address %s override specified\n",value);
635 fprintf(stderr, "ip address too long\n");
639 } else if ((strncmp(data, "unc", 3) == 0)
640 || (strncmp(data, "target", 6) == 0)
641 || (strncmp(data, "path", 4) == 0)) {
642 if (!value || !*value) {
643 fprintf(stderr, "invalid path to network resource\n");
645 return 1; /* needs_arg; */
646 } else if(strnlen(value,5) < 5) {
647 fprintf(stderr, "UNC name too short");
650 if (strnlen(value, 300) < 300) {
652 if (strncmp(value, "//", 2) == 0) {
654 fprintf(stderr, "unc name specified twice, ignoring second\n");
657 } else if (strncmp(value, "\\\\", 2) != 0) {
658 fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
663 fprintf(stderr, "unc name specified twice, ignoring second\n");
668 fprintf(stderr, "CIFS: UNC name too long\n");
672 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
673 || (strncmp(data, "workg", 5) == 0)) {
674 /* note this allows for synonyms of "domain"
675 such as "DOM" and "dom" and "workgroup"
676 and "WORKGRP" etc. */
677 if (!value || !*value) {
678 fprintf(stderr, "CIFS: invalid domain name\n");
680 return 1; /* needs_arg; */
682 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
685 fprintf(stderr, "domain name too long\n");
689 } else if (strncmp(data, "cred", 4) == 0) {
690 if (value && *value) {
691 rc = open_cred_file(value);
693 fprintf(stderr, "error %d (%s) opening credential file %s\n",
694 rc, strerror(rc), value);
699 fprintf(stderr, "invalid credential file name specified\n");
703 } else if (strncmp(data, "uid", 3) == 0) {
704 if (value && *value) {
706 if (!isdigit(*value)) {
709 if (!(pw = getpwnam(value))) {
710 fprintf(stderr, "bad user name \"%s\"\n", value);
713 snprintf(user, sizeof(user), "%u", pw->pw_uid);
715 strlcpy(user,value,sizeof(user));
719 } else if (strncmp(data, "gid", 3) == 0) {
720 if (value && *value) {
722 if (!isdigit(*value)) {
725 if (!(gr = getgrnam(value))) {
726 fprintf(stderr, "bad group name \"%s\"\n", value);
729 snprintf(group, sizeof(group), "%u", gr->gr_gid);
731 strlcpy(group,value,sizeof(group));
735 /* fmask and dmask synonyms for people used to smbfs syntax */
736 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
737 if (!value || !*value) {
738 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
743 if (value[0] != '0') {
744 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
747 if (strcmp (data, "fmask") == 0) {
748 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
749 data = "file_mode"; /* BB fix this */
751 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
752 if (!value || !*value) {
753 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
758 if (value[0] != '0') {
759 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
762 if (strcmp (data, "dmask") == 0) {
763 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
766 /* the following eight mount options should be
767 stripped out from what is passed into the kernel
768 since these eight options are best passed as the
769 mount flags rather than redundantly to the kernel
770 and could generate spurious warnings depending on the
771 level of the corresponding cifs vfs kernel code */
772 } else if (strncmp(data, "nosuid", 6) == 0) {
773 *filesys_flags |= MS_NOSUID;
774 } else if (strncmp(data, "suid", 4) == 0) {
775 *filesys_flags &= ~MS_NOSUID;
776 } else if (strncmp(data, "nodev", 5) == 0) {
777 *filesys_flags |= MS_NODEV;
778 } else if ((strncmp(data, "nobrl", 5) == 0) ||
779 (strncmp(data, "nolock", 6) == 0)) {
780 *filesys_flags &= ~MS_MANDLOCK;
781 } else if (strncmp(data, "dev", 3) == 0) {
782 *filesys_flags &= ~MS_NODEV;
783 } else if (strncmp(data, "noexec", 6) == 0) {
784 *filesys_flags |= MS_NOEXEC;
785 } else if (strncmp(data, "exec", 4) == 0) {
786 *filesys_flags &= ~MS_NOEXEC;
787 } else if (strncmp(data, "guest", 5) == 0) {
788 user_name = (char *)calloc(1, 1);
791 } else if (strncmp(data, "ro", 2) == 0) {
792 *filesys_flags |= MS_RDONLY;
794 } else if (strncmp(data, "rw", 2) == 0) {
795 *filesys_flags &= ~MS_RDONLY;
797 } else if (strncmp(data, "remount", 7) == 0) {
798 *filesys_flags |= MS_REMOUNT;
799 } /* else if (strnicmp(data, "port", 4) == 0) {
800 if (value && *value) {
802 simple_strtoul(value, &value, 0);
804 } else if (strnicmp(data, "rsize", 5) == 0) {
805 if (value && *value) {
807 simple_strtoul(value, &value, 0);
809 } else if (strnicmp(data, "wsize", 5) == 0) {
810 if (value && *value) {
812 simple_strtoul(value, &value, 0);
814 } else if (strnicmp(data, "version", 3) == 0) {
816 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
817 } */ /* nothing to do on those four mount options above.
818 Just pass to kernel and ignore them here */
820 /* Copy (possibly modified) option to out */
821 word_len = strlen(data);
823 word_len += 1 + strlen(value);
825 out = (char *)realloc(out, out_len + word_len + 2);
832 strlcat(out, ",", out_len + word_len + 2);
837 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
839 snprintf(out + out_len, word_len + 1, "%s", data);
840 out_len = strlen(out);
846 /* special-case the uid and gid */
848 word_len = strlen(user);
850 out = (char *)realloc(out, out_len + word_len + 6);
857 strlcat(out, ",", out_len + word_len + 6);
860 snprintf(out + out_len, word_len + 5, "uid=%s", user);
861 out_len = strlen(out);
864 word_len = strlen(group);
866 out = (char *)realloc(out, out_len + 1 + word_len + 6);
873 strlcat(out, ",", out_len + word_len + 6);
876 snprintf(out + out_len, word_len + 5, "gid=%s", group);
877 out_len = strlen(out);
880 SAFE_FREE(*optionsp);
885 /* replace all (one or more) commas with double commas */
886 static void check_for_comma(char ** ppasswrd)
891 int number_of_commas = 0;
906 if(number_of_commas == 0)
908 if(number_of_commas > MOUNT_PASSWD_SIZE) {
909 /* would otherwise overflow the mount options buffer */
910 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
914 new_pass_buf = (char *)malloc(len+number_of_commas+1);
915 if(new_pass_buf == NULL)
918 for(i=0,j=0;i<len;i++,j++) {
919 new_pass_buf[j] = pass[i];
922 new_pass_buf[j] = pass[i];
925 new_pass_buf[len+number_of_commas] = 0;
927 SAFE_FREE(*ppasswrd);
928 *ppasswrd = new_pass_buf;
933 /* Usernames can not have backslash in them and we use
934 [BB check if usernames can have forward slash in them BB]
935 backslash as domain\user separator character
937 static char * check_for_domain(char **ppuser)
939 char * original_string;
949 original_string = *ppuser;
951 if (original_string == NULL)
954 original_len = strlen(original_string);
956 usernm = strchr(*ppuser,'/');
957 if (usernm == NULL) {
958 usernm = strchr(*ppuser,'\\');
964 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
970 if (domainnm[0] != 0) {
973 fprintf(stderr, "null domain\n");
975 len = strlen(domainnm);
976 /* reset domainm to new buffer, and copy
977 domain name into it */
978 domainnm = (char *)malloc(len+1);
982 strlcpy(domainnm,*ppuser,len+1);
984 /* move_string(*ppuser, usernm+1) */
985 len = strlen(usernm+1);
987 if(len >= original_len) {
988 /* should not happen */
992 for(i=0;i<original_len;i++) {
994 original_string[i] = usernm[i+1];
995 else /* stuff with commas to remove last parm */
996 original_string[i] = ',';
999 /* BB add check for more than one slash?
1000 strchr(*ppuser,'/');
1001 strchr(*ppuser,'\\')
1007 /* replace all occurances of "from" in a string with "to" */
1008 static void replace_char(char *string, char from, char to, int maxlen)
1010 char *lastchar = string + maxlen;
1012 string = strchr(string, from);
1015 if (string >= lastchar)
1021 /* Note that caller frees the returned buffer if necessary */
1022 static struct addrinfo *
1023 parse_server(char ** punc_name)
1025 char * unc_name = *punc_name;
1026 int length = strnlen(unc_name, MAX_UNC_LEN);
1028 struct addrinfo *addrlist;
1031 if(length > (MAX_UNC_LEN - 1)) {
1032 fprintf(stderr, "mount error: UNC name too long");
1035 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1036 (strncasecmp("smb://", unc_name, 6) == 0)) {
1037 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1042 /* BB add code to find DFS root here */
1043 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1046 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1047 /* check for nfs syntax ie server:share */
1048 share = strchr(unc_name,':');
1050 *punc_name = (char *)malloc(length+3);
1051 if(*punc_name == NULL) {
1052 /* put the original string back if
1054 *punc_name = unc_name;
1058 strlcpy((*punc_name)+2,unc_name,length+1);
1059 SAFE_FREE(unc_name);
1060 unc_name = *punc_name;
1061 unc_name[length+2] = 0;
1062 goto continue_unc_parsing;
1064 fprintf(stderr, "mount error: improperly formatted UNC name.");
1065 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1069 continue_unc_parsing:
1074 /* allow for either delimiter between host and sharename */
1075 if ((share = strpbrk(unc_name, "/\\"))) {
1076 *share = 0; /* temporarily terminate the string */
1079 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1081 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1082 unc_name, gai_strerror(rc));
1086 *(share - 1) = '/'; /* put delimiter back */
1088 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1089 if ((prefixpath = strpbrk(share, "/\\"))) {
1090 *prefixpath = 0; /* permanently terminate the string */
1091 if (!strlen(++prefixpath))
1092 prefixpath = NULL; /* this needs to be done explicitly */
1096 fprintf(stderr, "ip address specified explicitly\n");
1099 /* BB should we pass an alternate version of the share name as Unicode */
1103 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1104 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1111 static struct option longopts[] = {
1112 { "all", 0, NULL, 'a' },
1113 { "help",0, NULL, 'h' },
1114 { "move",0, NULL, 'm' },
1115 { "bind",0, NULL, 'b' },
1116 { "read-only", 0, NULL, 'r' },
1117 { "ro", 0, NULL, 'r' },
1118 { "verbose", 0, NULL, 'v' },
1119 { "version", 0, NULL, 'V' },
1120 { "read-write", 0, NULL, 'w' },
1121 { "rw", 0, NULL, 'w' },
1122 { "options", 1, NULL, 'o' },
1123 { "type", 1, NULL, 't' },
1124 { "rsize",1, NULL, 'R' },
1125 { "wsize",1, NULL, 'W' },
1126 { "uid", 1, NULL, '1'},
1127 { "gid", 1, NULL, '2'},
1128 { "user",1,NULL,'u'},
1129 { "username",1,NULL,'u'},
1130 { "dom",1,NULL,'d'},
1131 { "domain",1,NULL,'d'},
1132 { "password",1,NULL,'p'},
1133 { "pass",1,NULL,'p'},
1134 { "credentials",1,NULL,'c'},
1135 { "port",1,NULL,'P'},
1136 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1137 { NULL, 0, NULL, 0 }
1140 /* convert a string to uppercase. return false if the string
1141 * wasn't ASCII. Return success on a NULL ptr */
1143 uppercase_string(char *string)
1149 /* check for unicode */
1150 if ((unsigned char) string[0] & 0x80)
1152 *string = toupper((unsigned char) *string);
1159 static void print_cifs_mount_version(void)
1161 printf("mount.cifs version: %s.%s%s\n",
1162 MOUNT_CIFS_VERSION_MAJOR,
1163 MOUNT_CIFS_VERSION_MINOR,
1164 MOUNT_CIFS_VENDOR_SUFFIX);
1168 * This function borrowed from fuse-utils...
1170 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1171 * newlines embedded within the text fields. To make sure no one corrupts
1172 * the mtab, fail the mount if there are embedded newlines.
1174 static int check_newline(const char *progname, const char *name)
1177 for (s = "\n"; *s; s++) {
1178 if (strchr(name, *s)) {
1179 fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
1187 static int check_mtab(const char *progname, const char *devname,
1190 if (check_newline(progname, devname) == -1 ||
1191 check_newline(progname, dir) == -1)
1197 int main(int argc, char ** argv)
1200 unsigned long flags = MS_MANDLOCK;
1201 char * orgoptions = NULL;
1202 char * share_name = NULL;
1203 const char * ipaddr = NULL;
1205 char * mountpoint = NULL;
1206 char * options = NULL;
1208 char * resolved_path = NULL;
1219 size_t options_size = 0;
1221 int retry = 0; /* set when we have to retry mount with uppercase */
1222 struct addrinfo *addrhead = NULL, *addr;
1223 struct utsname sysinfo;
1224 struct mntent mountent;
1225 struct sockaddr_in *addr4;
1226 struct sockaddr_in6 *addr6;
1229 /* setlocale(LC_ALL, "");
1230 bindtextdomain(PACKAGE, LOCALEDIR);
1231 textdomain(PACKAGE); */
1234 thisprogram = argv[0];
1236 mount_cifs_usage(stderr);
1238 if(thisprogram == NULL)
1239 thisprogram = "mount.cifs";
1242 /* BB add workstation name and domain and pass down */
1244 /* #ifdef _GNU_SOURCE
1245 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1249 share_name = strndup(argv[1], MAX_UNC_LEN);
1250 if (share_name == NULL) {
1251 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1254 mountpoint = argv[2];
1255 } else if (argc == 2) {
1256 if ((strcmp(argv[1], "-V") == 0) ||
1257 (strcmp(argv[1], "--version") == 0))
1259 print_cifs_mount_version();
1263 if ((strcmp(argv[1], "-h") == 0) ||
1264 (strcmp(argv[1], "-?") == 0) ||
1265 (strcmp(argv[1], "--help") == 0))
1266 mount_cifs_usage(stdout);
1268 mount_cifs_usage(stderr);
1270 mount_cifs_usage(stderr);
1274 /* add sharename in opts string as unc= parm */
1275 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1276 longopts, NULL)) != -1) {
1278 /* No code to do the following options yet */
1280 list_with_volumelabel = 1;
1283 volumelabel = optarg;
1290 case 'h': /* help */
1291 mount_cifs_usage(stdout);
1300 "option 'b' (MS_BIND) not supported\n");
1308 "option 'm' (MS_MOVE) not supported\n");
1312 orgoptions = strdup(optarg);
1314 case 'r': /* mount readonly */
1324 print_cifs_mount_version();
1327 flags &= ~MS_RDONLY;
1330 rsize = atoi(optarg) ;
1333 wsize = atoi(optarg);
1336 if (isdigit(*optarg)) {
1339 uid = strtoul(optarg, &ep, 10);
1341 fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1347 if (!(pw = getpwnam(optarg))) {
1348 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1356 if (isdigit(*optarg)) {
1359 gid = strtoul(optarg, &ep, 10);
1361 fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1367 if (!(gr = getgrnam(optarg))) {
1368 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1380 domain_name = optarg; /* BB fix this - currently ignored */
1384 if(mountpassword == NULL)
1385 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1388 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1392 get_password_from_file(0 /* stdin */,NULL);
1400 fprintf(stderr, "unknown mount option %c\n",c);
1401 mount_cifs_usage(stderr);
1405 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1406 mount_cifs_usage(stderr);
1409 /* make sure mountpoint is legit */
1410 rc = chdir(mountpoint);
1412 fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
1418 rc = check_mountpoint(thisprogram, mountpoint);
1422 /* sanity check for unprivileged mounts */
1424 rc = check_fstab(thisprogram, mountpoint, dev_name,
1429 /* enable any default user mount flags */
1430 flags |= CIFS_SETUID_FLAGS;
1433 if (getenv("PASSWD")) {
1434 if(mountpassword == NULL)
1435 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1437 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1440 } else if (getenv("PASSWD_FD")) {
1441 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1442 } else if (getenv("PASSWD_FILE")) {
1443 get_password_from_file(0, getenv("PASSWD_FILE"));
1446 if (orgoptions && parse_options(&orgoptions, &flags)) {
1452 #if !CIFS_LEGACY_SETUID_CHECK
1453 if (!(flags & (MS_USERS|MS_USER))) {
1454 fprintf(stderr, "%s: permission denied\n", thisprogram);
1458 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1461 fprintf(stderr, "%s: not installed setuid - \"user\" "
1462 "CIFS mounts not supported.",
1469 flags &= ~(MS_USERS|MS_USER);
1471 addrhead = addr = parse_server(&share_name);
1472 if((addrhead == NULL) && (got_ip == 0)) {
1473 fprintf(stderr, "No ip address specified and hostname not found\n");
1478 /* BB save off path and pop after mount returns? */
1479 resolved_path = (char *)malloc(PATH_MAX+1);
1480 if (!resolved_path) {
1481 fprintf(stderr, "Unable to allocate memory.\n");
1486 /* Note that if we can not canonicalize the name, we get
1487 another chance to see if it is valid when we chdir to it */
1488 if(!realpath(".", resolved_path)) {
1489 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1490 mountpoint, strerror(errno));
1495 mountpoint = resolved_path;
1498 /* Note that the password will not be retrieved from the
1499 USER env variable (ie user%password form) as there is
1500 already a PASSWD environment varaible */
1502 user_name = strdup(getenv("USER"));
1503 if (user_name == NULL)
1504 user_name = getusername();
1508 if(got_password == 0) {
1509 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1510 no good replacement yet. */
1511 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1512 if (!tmp_pass || !mountpassword) {
1513 fprintf(stderr, "Password not entered, exiting\n");
1516 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1519 /* FIXME launch daemon (handles dfs name resolution and credential change)
1520 remember to clear parms and overwrite password field before launching */
1522 optlen = strlen(orgoptions);
1527 optlen += strlen(share_name) + 4;
1529 fprintf(stderr, "No server share name specified\n");
1530 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1534 optlen += strlen(user_name) + 6;
1535 optlen += MAX_ADDRESS_LEN + 4;
1537 optlen += strlen(mountpassword) + 6;
1540 options_size = optlen + 10 + DOMAIN_SIZE;
1541 options = (char *)malloc(options_size /* space for commas in password */ + 8 /* space for domain= , domain name itself was counted as part of the length username string above */);
1543 if(options == NULL) {
1544 fprintf(stderr, "Could not allocate memory for mount options\n");
1548 strlcpy(options, "unc=", options_size);
1549 strlcat(options,share_name,options_size);
1550 /* scan backwards and reverse direction of slash */
1551 temp = strrchr(options, '/');
1552 if(temp > options + 6)
1555 /* check for syntax like user=domain\user */
1557 domain_name = check_for_domain(&user_name);
1558 strlcat(options,",user=",options_size);
1559 strlcat(options,user_name,options_size);
1563 /* extra length accounted for in option string above */
1564 strlcat(options,",domain=",options_size);
1565 strlcat(options,domain_name,options_size);
1569 strlcat(options,",ver=",options_size);
1570 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1573 strlcat(options,",",options_size);
1574 strlcat(options,orgoptions,options_size);
1577 strlcat(options,",prefixpath=",options_size);
1578 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1581 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1582 replace_char(dev_name, '\\', '/', strlen(share_name));
1584 if (!got_ip && addr) {
1585 strlcat(options, ",ip=", options_size);
1586 current_len = strnlen(options, options_size);
1587 optionstail = options + current_len;
1588 switch (addr->ai_addr->sa_family) {
1590 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1591 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1592 options_size - current_len);
1595 addr4 = (struct sockaddr_in *) addr->ai_addr;
1596 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1597 options_size - current_len);
1603 /* if the address looks bogus, try the next one */
1605 addr = addr->ai_next;
1613 if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1614 strlcat(options, "%", options_size);
1615 current_len = strnlen(options, options_size);
1616 optionstail = options + current_len;
1617 snprintf(optionstail, options_size - current_len, "%u",
1618 addr6->sin6_scope_id);
1622 fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1624 if (mountpassword) {
1626 * Commas have to be doubled, or else they will
1627 * look like the parameter separator
1630 check_for_comma(&mountpassword);
1631 strlcat(options,",pass=",options_size);
1632 strlcat(options,mountpassword,options_size);
1634 fprintf(stderr, ",pass=********");
1638 fprintf(stderr, "\n");
1640 rc = check_mtab(thisprogram, dev_name, mountpoint);
1644 if (!fakemnt && mount(dev_name, ".", cifs_fstype, flags, options)) {
1649 addr = addr->ai_next;
1655 fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1660 if (uppercase_string(dev_name) &&
1661 uppercase_string(share_name) &&
1662 uppercase_string(prefixpath)) {
1663 fprintf(stderr, "retrying with upper case share name\n");
1668 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1669 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1677 atexit(unlock_mtab);
1680 fprintf(stderr, "cannot lock mtab");
1683 pmntfile = setmntent(MOUNTED, "a+");
1685 fprintf(stderr, "could not update mount table\n");
1690 mountent.mnt_fsname = dev_name;
1691 mountent.mnt_dir = mountpoint;
1692 mountent.mnt_type = (char *)(void *)cifs_fstype;
1693 mountent.mnt_opts = (char *)malloc(220);
1694 if(mountent.mnt_opts) {
1695 char * mount_user = getusername();
1696 memset(mountent.mnt_opts,0,200);
1697 if(flags & MS_RDONLY)
1698 strlcat(mountent.mnt_opts,"ro",220);
1700 strlcat(mountent.mnt_opts,"rw",220);
1701 if(flags & MS_MANDLOCK)
1702 strlcat(mountent.mnt_opts,",mand",220);
1703 if(flags & MS_NOEXEC)
1704 strlcat(mountent.mnt_opts,",noexec",220);
1705 if(flags & MS_NOSUID)
1706 strlcat(mountent.mnt_opts,",nosuid",220);
1707 if(flags & MS_NODEV)
1708 strlcat(mountent.mnt_opts,",nodev",220);
1709 if(flags & MS_SYNCHRONOUS)
1710 strlcat(mountent.mnt_opts,",sync",220);
1713 strlcat(mountent.mnt_opts,
1715 strlcat(mountent.mnt_opts,
1720 mountent.mnt_freq = 0;
1721 mountent.mnt_passno = 0;
1722 rc = addmntent(pmntfile,&mountent);
1723 endmntent(pmntfile);
1725 SAFE_FREE(mountent.mnt_opts);
1730 int len = strlen(mountpassword);
1731 memset(mountpassword,0,len);
1732 SAFE_FREE(mountpassword);
1736 freeaddrinfo(addrhead);
1738 SAFE_FREE(orgoptions);
1739 SAFE_FREE(resolved_path);
1740 SAFE_FREE(share_name);