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
79 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
82 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
85 #define MOUNT_PASSWD_SIZE 128
86 #define DOMAIN_SIZE 64
88 /* currently maximum length of IPv6 address string */
89 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
92 * By default, mount.cifs follows the conventions set forth by /bin/mount
93 * for user mounts. That is, it requires that the mount be listed in
94 * /etc/fstab with the "user" option when run as an unprivileged user and
95 * mount.cifs is setuid root.
97 * Older versions of mount.cifs however were "looser" in this regard. When
98 * made setuid root, a user could run mount.cifs directly and mount any share
99 * on a directory owned by that user.
101 * The legacy behavior is now disabled by default. To reenable it, set the
102 * following #define to true.
104 #define CIFS_LEGACY_SETUID_CHECK 0
107 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
108 * flags by default. These defaults can be changed here.
110 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
112 const char *thisprogram;
115 static int got_password = 0;
116 static int got_user = 0;
117 static int got_domain = 0;
118 static int got_ip = 0;
119 static int got_unc = 0;
120 static int got_uid = 0;
121 static int got_gid = 0;
122 static char * user_name = NULL;
123 static char * mountpassword = NULL;
124 char * domain_name = NULL;
125 char * prefixpath = NULL;
127 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
128 * don't link to libreplace so need them here. */
130 /* like strncpy but does not 0 fill the buffer and always null
131 * terminates. bufsize is the size of the destination buffer */
134 static size_t strlcpy(char *d, const char *s, size_t bufsize)
136 size_t len = strlen(s);
138 if (bufsize <= 0) return 0;
139 if (len >= bufsize) len = bufsize-1;
146 /* like strncat but does not 0 fill the buffer and always null
147 * terminates. bufsize is the length of the buffer, which should
148 * be one more than the maximum resulting string length */
151 static size_t strlcat(char *d, const char *s, size_t bufsize)
153 size_t len1 = strlen(d);
154 size_t len2 = strlen(s);
155 size_t ret = len1 + len2;
157 if (len1+len2 >= bufsize) {
158 if (bufsize < (len1+1)) {
161 len2 = bufsize - (len1+1);
164 memcpy(d+len1, s, len2);
172 * If an unprivileged user is doing the mounting then we need to ensure
173 * that the entry is in /etc/fstab.
176 check_mountpoint(const char *progname, char *mountpoint)
181 /* does mountpoint exist and is it a directory? */
182 err = stat(mountpoint, &statbuf);
184 fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
185 mountpoint, strerror(errno));
189 if (!S_ISDIR(statbuf.st_mode)) {
190 fprintf(stderr, "%s: %s is not a directory!", progname,
195 #if CIFS_LEGACY_SETUID_CHECK
196 /* do extra checks on mountpoint for legacy setuid behavior */
197 if (!getuid() || geteuid())
200 if (statbuf.st_uid != getuid()) {
201 fprintf(stderr, "%s: %s is not owned by user\n", progname,
206 if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
207 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
211 #endif /* CIFS_LEGACY_SETUID_CHECK */
216 #if CIFS_LEGACY_SETUID_CHECK
218 check_fstab(const char *progname, char *mountpoint, char *devname,
223 #else /* CIFS_LEGACY_SETUID_CHECK */
225 check_fstab(const char *progname, char *mountpoint, char *devname,
231 /* make sure this mount is listed in /etc/fstab */
232 fstab = setmntent(_PATH_FSTAB, "r");
234 fprintf(stderr, "Couldn't open %s for reading!\n",
239 while((mnt = getmntent(fstab))) {
240 if (!strcmp(mountpoint, mnt->mnt_dir))
245 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
246 fprintf(stderr, "%s: permission denied: no match for "
247 "%s found in %s\n", progname, mountpoint,
253 * 'mount' munges the options from fstab before passing them
254 * to us. It is non-trivial to test that we have the correct
255 * set of options. We don't want to trust what the user
256 * gave us, so just take whatever is in /etc/fstab.
259 *options = strdup(mnt->mnt_opts);
262 #endif /* CIFS_LEGACY_SETUID_CHECK */
267 open nofollow - avoid symlink exposure?
268 get owner of dir see if matches self or if root
269 call system(umount argv) etc.
273 static char * check_for_domain(char **);
276 static void mount_cifs_usage(FILE *stream)
278 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
279 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
280 fprintf(stream, " to a local directory.\n\nOptions:\n");
281 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
282 fprintf(stream, "\nLess commonly used options:");
283 fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
284 fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
285 fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
286 fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
287 fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
288 fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
289 fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
290 fprintf(stream, "\n\nRarely used options:");
291 fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
292 fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
293 fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
294 fprintf(stream, "\n\nOptions are described in more detail in the manual page");
295 fprintf(stream, "\n\tman 8 mount.cifs\n");
296 fprintf(stream, "\nTo display the version number of the mount helper:");
297 fprintf(stream, "\n\t%s -V\n",thisprogram);
299 SAFE_FREE(mountpassword);
301 if (stream == stderr)
306 /* caller frees username if necessary */
307 static char * getusername(void) {
308 char *username = NULL;
309 struct passwd *password = getpwuid(getuid());
312 username = password->pw_name;
317 static int open_cred_file(char * file_name)
324 i = access(file_name, R_OK);
328 fs = fopen(file_name,"r");
331 line_buf = (char *)malloc(4096);
332 if(line_buf == NULL) {
337 while(fgets(line_buf,4096,fs)) {
338 /* parse line from credential file */
340 /* eat leading white space */
341 for(i=0;i<4086;i++) {
342 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
344 /* if whitespace - skip past it */
346 if (strncasecmp("username",line_buf+i,8) == 0) {
347 temp_val = strchr(line_buf + i,'=');
349 /* go past equals sign */
351 for(length = 0;length<4087;length++) {
352 if ((temp_val[length] == '\n')
353 || (temp_val[length] == '\0')) {
354 temp_val[length] = '\0';
359 fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
360 memset(line_buf,0,4096);
364 user_name = (char *)calloc(1 + length,1);
365 /* BB adding free of user_name string before exit,
366 not really necessary but would be cleaner */
367 strlcpy(user_name,temp_val, length+1);
370 } else if (strncasecmp("password",line_buf+i,8) == 0) {
371 temp_val = strchr(line_buf+i,'=');
373 /* go past equals sign */
375 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
376 if ((temp_val[length] == '\n')
377 || (temp_val[length] == '\0')) {
378 temp_val[length] = '\0';
382 if(length > MOUNT_PASSWD_SIZE) {
383 fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
384 memset(line_buf,0, 4096);
387 if(mountpassword == NULL) {
388 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
390 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
392 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
397 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
398 temp_val = strchr(line_buf+i,'=');
400 /* go past equals sign */
403 fprintf(stderr, "\nDomain %s\n",temp_val);
404 for(length = 0;length<DOMAIN_SIZE+1;length++) {
405 if ((temp_val[length] == '\n')
406 || (temp_val[length] == '\0')) {
407 temp_val[length] = '\0';
411 if(length > DOMAIN_SIZE) {
412 fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
415 if(domain_name == NULL) {
416 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
418 memset(domain_name,0,DOMAIN_SIZE);
420 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
433 static int get_password_from_file(int file_descript, char * filename)
439 if(mountpassword == NULL)
440 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
442 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
444 if (mountpassword == NULL) {
445 fprintf(stderr, "malloc failed\n");
449 if(filename != NULL) {
450 rc = access(filename, R_OK);
452 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
453 filename, strerror(errno));
456 file_descript = open(filename, O_RDONLY);
457 if(file_descript < 0) {
458 fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
459 strerror(errno),filename);
463 /* else file already open and fd provided */
465 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
466 rc = read(file_descript,&c,1);
468 fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
470 close(file_descript);
473 if(mountpassword[0] == 0) {
475 fprintf(stderr, "\nWarning: null password used since cifs password file empty");
478 } else /* read valid character */ {
479 if((c == 0) || (c == '\n')) {
480 mountpassword[i] = '\0';
483 mountpassword[i] = c;
486 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
487 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
491 if(filename != NULL) {
492 close(file_descript);
498 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
501 char * percent_char = NULL;
503 char * next_keyword = NULL;
511 if (!optionsp || !*optionsp)
516 fprintf(stderr, "parsing options: %s\n", data);
518 /* BB fixme check for separator override BB */
522 snprintf(user,sizeof(user),"%u",getuid());
524 snprintf(group,sizeof(group),"%u",getgid());
527 /* while ((data = strsep(&options, ",")) != NULL) { */
528 while(data != NULL) {
529 /* check if ends with trailing comma */
533 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
534 /* data = next keyword */
535 /* value = next value ie stuff after equal sign */
537 next_keyword = strchr(data,','); /* BB handle sep= */
539 /* temporarily null terminate end of keyword=value pair */
543 /* temporarily null terminate keyword to make keyword and value distinct */
544 if ((value = strchr(data, '=')) != NULL) {
549 if (strncmp(data, "users",5) == 0) {
550 if(!value || !*value) {
551 *filesys_flags |= MS_USERS;
554 } else if (strncmp(data, "user_xattr",10) == 0) {
555 /* do nothing - need to skip so not parsed as user name */
556 } else if (strncmp(data, "user", 4) == 0) {
558 if (!value || !*value) {
559 if(data[4] == '\0') {
560 *filesys_flags |= MS_USER;
563 fprintf(stderr, "username specified with no parameter\n");
565 return 1; /* needs_arg; */
568 if (strnlen(value, 260) < 260) {
570 percent_char = strchr(value,'%');
573 if(mountpassword == NULL)
574 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
577 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
580 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
581 /* remove password from username */
582 while(*percent_char != 0) {
588 /* this is only case in which the user
589 name buf is not malloc - so we have to
590 check for domain name embedded within
591 the user name here since the later
592 call to check_for_domain will not be
594 domain_name = check_for_domain(&value);
596 fprintf(stderr, "username too long\n");
601 } else if (strncmp(data, "pass", 4) == 0) {
602 if (!value || !*value) {
604 fprintf(stderr, "\npassword specified twice, ignoring second\n");
607 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
609 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
612 fprintf(stderr, "password too long\n");
616 } else if (strncmp(data, "sec", 3) == 0) {
618 if (!strncmp(value, "none", 4) ||
619 !strncmp(value, "krb5", 4))
622 } else if (strncmp(data, "ip", 2) == 0) {
623 if (!value || !*value) {
624 fprintf(stderr, "target ip address argument missing");
625 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
627 fprintf(stderr, "ip address %s override specified\n",value);
630 fprintf(stderr, "ip address too long\n");
634 } else if ((strncmp(data, "unc", 3) == 0)
635 || (strncmp(data, "target", 6) == 0)
636 || (strncmp(data, "path", 4) == 0)) {
637 if (!value || !*value) {
638 fprintf(stderr, "invalid path to network resource\n");
640 return 1; /* needs_arg; */
641 } else if(strnlen(value,5) < 5) {
642 fprintf(stderr, "UNC name too short");
645 if (strnlen(value, 300) < 300) {
647 if (strncmp(value, "//", 2) == 0) {
649 fprintf(stderr, "unc name specified twice, ignoring second\n");
652 } else if (strncmp(value, "\\\\", 2) != 0) {
653 fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
658 fprintf(stderr, "unc name specified twice, ignoring second\n");
663 fprintf(stderr, "CIFS: UNC name too long\n");
667 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
668 || (strncmp(data, "workg", 5) == 0)) {
669 /* note this allows for synonyms of "domain"
670 such as "DOM" and "dom" and "workgroup"
671 and "WORKGRP" etc. */
672 if (!value || !*value) {
673 fprintf(stderr, "CIFS: invalid domain name\n");
675 return 1; /* needs_arg; */
677 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
680 fprintf(stderr, "domain name too long\n");
684 } else if (strncmp(data, "cred", 4) == 0) {
685 if (value && *value) {
686 rc = open_cred_file(value);
688 fprintf(stderr, "error %d (%s) opening credential file %s\n",
689 rc, strerror(rc), value);
694 fprintf(stderr, "invalid credential file name specified\n");
698 } else if (strncmp(data, "uid", 3) == 0) {
699 if (value && *value) {
701 if (!isdigit(*value)) {
704 if (!(pw = getpwnam(value))) {
705 fprintf(stderr, "bad user name \"%s\"\n", value);
708 snprintf(user, sizeof(user), "%u", pw->pw_uid);
710 strlcpy(user,value,sizeof(user));
714 } else if (strncmp(data, "gid", 3) == 0) {
715 if (value && *value) {
717 if (!isdigit(*value)) {
720 if (!(gr = getgrnam(value))) {
721 fprintf(stderr, "bad group name \"%s\"\n", value);
724 snprintf(group, sizeof(group), "%u", gr->gr_gid);
726 strlcpy(group,value,sizeof(group));
730 /* fmask and dmask synonyms for people used to smbfs syntax */
731 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
732 if (!value || !*value) {
733 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
738 if (value[0] != '0') {
739 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
742 if (strcmp (data, "fmask") == 0) {
743 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
744 data = "file_mode"; /* BB fix this */
746 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
747 if (!value || !*value) {
748 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
753 if (value[0] != '0') {
754 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
757 if (strcmp (data, "dmask") == 0) {
758 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
761 /* the following eight mount options should be
762 stripped out from what is passed into the kernel
763 since these eight options are best passed as the
764 mount flags rather than redundantly to the kernel
765 and could generate spurious warnings depending on the
766 level of the corresponding cifs vfs kernel code */
767 } else if (strncmp(data, "nosuid", 6) == 0) {
768 *filesys_flags |= MS_NOSUID;
769 } else if (strncmp(data, "suid", 4) == 0) {
770 *filesys_flags &= ~MS_NOSUID;
771 } else if (strncmp(data, "nodev", 5) == 0) {
772 *filesys_flags |= MS_NODEV;
773 } else if ((strncmp(data, "nobrl", 5) == 0) ||
774 (strncmp(data, "nolock", 6) == 0)) {
775 *filesys_flags &= ~MS_MANDLOCK;
776 } else if (strncmp(data, "dev", 3) == 0) {
777 *filesys_flags &= ~MS_NODEV;
778 } else if (strncmp(data, "noexec", 6) == 0) {
779 *filesys_flags |= MS_NOEXEC;
780 } else if (strncmp(data, "exec", 4) == 0) {
781 *filesys_flags &= ~MS_NOEXEC;
782 } else if (strncmp(data, "guest", 5) == 0) {
783 user_name = (char *)calloc(1, 1);
786 } else if (strncmp(data, "ro", 2) == 0) {
787 *filesys_flags |= MS_RDONLY;
789 } else if (strncmp(data, "rw", 2) == 0) {
790 *filesys_flags &= ~MS_RDONLY;
792 } else if (strncmp(data, "remount", 7) == 0) {
793 *filesys_flags |= MS_REMOUNT;
794 } /* else if (strnicmp(data, "port", 4) == 0) {
795 if (value && *value) {
797 simple_strtoul(value, &value, 0);
799 } else if (strnicmp(data, "rsize", 5) == 0) {
800 if (value && *value) {
802 simple_strtoul(value, &value, 0);
804 } else if (strnicmp(data, "wsize", 5) == 0) {
805 if (value && *value) {
807 simple_strtoul(value, &value, 0);
809 } else if (strnicmp(data, "version", 3) == 0) {
811 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
812 } */ /* nothing to do on those four mount options above.
813 Just pass to kernel and ignore them here */
815 /* Copy (possibly modified) option to out */
816 word_len = strlen(data);
818 word_len += 1 + strlen(value);
820 out = (char *)realloc(out, out_len + word_len + 2);
827 strlcat(out, ",", out_len + word_len + 2);
832 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
834 snprintf(out + out_len, word_len + 1, "%s", data);
835 out_len = strlen(out);
841 /* special-case the uid and gid */
843 word_len = strlen(user);
845 out = (char *)realloc(out, out_len + word_len + 6);
852 strlcat(out, ",", out_len + word_len + 6);
855 snprintf(out + out_len, word_len + 5, "uid=%s", user);
856 out_len = strlen(out);
859 word_len = strlen(group);
861 out = (char *)realloc(out, out_len + 1 + word_len + 6);
868 strlcat(out, ",", out_len + word_len + 6);
871 snprintf(out + out_len, word_len + 5, "gid=%s", group);
872 out_len = strlen(out);
875 SAFE_FREE(*optionsp);
880 /* replace all (one or more) commas with double commas */
881 static void check_for_comma(char ** ppasswrd)
886 int number_of_commas = 0;
901 if(number_of_commas == 0)
903 if(number_of_commas > MOUNT_PASSWD_SIZE) {
904 /* would otherwise overflow the mount options buffer */
905 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
909 new_pass_buf = (char *)malloc(len+number_of_commas+1);
910 if(new_pass_buf == NULL)
913 for(i=0,j=0;i<len;i++,j++) {
914 new_pass_buf[j] = pass[i];
917 new_pass_buf[j] = pass[i];
920 new_pass_buf[len+number_of_commas] = 0;
922 SAFE_FREE(*ppasswrd);
923 *ppasswrd = new_pass_buf;
928 /* Usernames can not have backslash in them and we use
929 [BB check if usernames can have forward slash in them BB]
930 backslash as domain\user separator character
932 static char * check_for_domain(char **ppuser)
934 char * original_string;
944 original_string = *ppuser;
946 if (original_string == NULL)
949 original_len = strlen(original_string);
951 usernm = strchr(*ppuser,'/');
952 if (usernm == NULL) {
953 usernm = strchr(*ppuser,'\\');
959 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
965 if (domainnm[0] != 0) {
968 fprintf(stderr, "null domain\n");
970 len = strlen(domainnm);
971 /* reset domainm to new buffer, and copy
972 domain name into it */
973 domainnm = (char *)malloc(len+1);
977 strlcpy(domainnm,*ppuser,len+1);
979 /* move_string(*ppuser, usernm+1) */
980 len = strlen(usernm+1);
982 if(len >= original_len) {
983 /* should not happen */
987 for(i=0;i<original_len;i++) {
989 original_string[i] = usernm[i+1];
990 else /* stuff with commas to remove last parm */
991 original_string[i] = ',';
994 /* BB add check for more than one slash?
1002 /* replace all occurances of "from" in a string with "to" */
1003 static void replace_char(char *string, char from, char to, int maxlen)
1005 char *lastchar = string + maxlen;
1007 string = strchr(string, from);
1010 if (string >= lastchar)
1016 /* Note that caller frees the returned buffer if necessary */
1017 static struct addrinfo *
1018 parse_server(char ** punc_name)
1020 char * unc_name = *punc_name;
1021 int length = strnlen(unc_name, MAX_UNC_LEN);
1023 struct addrinfo *addrlist;
1026 if(length > (MAX_UNC_LEN - 1)) {
1027 fprintf(stderr, "mount error: UNC name too long");
1030 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1031 (strncasecmp("smb://", unc_name, 6) == 0)) {
1032 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1037 /* BB add code to find DFS root here */
1038 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1041 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1042 /* check for nfs syntax ie server:share */
1043 share = strchr(unc_name,':');
1045 *punc_name = (char *)malloc(length+3);
1046 if(*punc_name == NULL) {
1047 /* put the original string back if
1049 *punc_name = unc_name;
1053 strlcpy((*punc_name)+2,unc_name,length+1);
1054 SAFE_FREE(unc_name);
1055 unc_name = *punc_name;
1056 unc_name[length+2] = 0;
1057 goto continue_unc_parsing;
1059 fprintf(stderr, "mount error: improperly formatted UNC name.");
1060 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1064 continue_unc_parsing:
1069 /* allow for either delimiter between host and sharename */
1070 if ((share = strpbrk(unc_name, "/\\"))) {
1071 *share = 0; /* temporarily terminate the string */
1074 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1076 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1077 unc_name, gai_strerror(rc));
1081 *(share - 1) = '/'; /* put delimiter back */
1083 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1084 if ((prefixpath = strpbrk(share, "/\\"))) {
1085 *prefixpath = 0; /* permanently terminate the string */
1086 if (!strlen(++prefixpath))
1087 prefixpath = NULL; /* this needs to be done explicitly */
1091 fprintf(stderr, "ip address specified explicitly\n");
1094 /* BB should we pass an alternate version of the share name as Unicode */
1098 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1099 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1106 static struct option longopts[] = {
1107 { "all", 0, NULL, 'a' },
1108 { "help",0, NULL, 'h' },
1109 { "move",0, NULL, 'm' },
1110 { "bind",0, NULL, 'b' },
1111 { "read-only", 0, NULL, 'r' },
1112 { "ro", 0, NULL, 'r' },
1113 { "verbose", 0, NULL, 'v' },
1114 { "version", 0, NULL, 'V' },
1115 { "read-write", 0, NULL, 'w' },
1116 { "rw", 0, NULL, 'w' },
1117 { "options", 1, NULL, 'o' },
1118 { "type", 1, NULL, 't' },
1119 { "rsize",1, NULL, 'R' },
1120 { "wsize",1, NULL, 'W' },
1121 { "uid", 1, NULL, '1'},
1122 { "gid", 1, NULL, '2'},
1123 { "user",1,NULL,'u'},
1124 { "username",1,NULL,'u'},
1125 { "dom",1,NULL,'d'},
1126 { "domain",1,NULL,'d'},
1127 { "password",1,NULL,'p'},
1128 { "pass",1,NULL,'p'},
1129 { "credentials",1,NULL,'c'},
1130 { "port",1,NULL,'P'},
1131 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1132 { NULL, 0, NULL, 0 }
1135 /* convert a string to uppercase. return false if the string
1136 * wasn't ASCII. Return success on a NULL ptr */
1138 uppercase_string(char *string)
1144 /* check for unicode */
1145 if ((unsigned char) string[0] & 0x80)
1147 *string = toupper((unsigned char) *string);
1154 static void print_cifs_mount_version(void)
1156 printf("mount.cifs version: %s.%s%s\n",
1157 MOUNT_CIFS_VERSION_MAJOR,
1158 MOUNT_CIFS_VERSION_MINOR,
1159 MOUNT_CIFS_VENDOR_SUFFIX);
1162 int main(int argc, char ** argv)
1165 unsigned long flags = MS_MANDLOCK;
1166 char * orgoptions = NULL;
1167 char * share_name = NULL;
1168 const char * ipaddr = NULL;
1170 char * mountpoint = NULL;
1171 char * options = NULL;
1173 char * resolved_path = NULL;
1184 size_t options_size = 0;
1186 int retry = 0; /* set when we have to retry mount with uppercase */
1187 struct addrinfo *addrhead = NULL, *addr;
1188 struct utsname sysinfo;
1189 struct mntent mountent;
1190 struct sockaddr_in *addr4;
1191 struct sockaddr_in6 *addr6;
1194 /* setlocale(LC_ALL, "");
1195 bindtextdomain(PACKAGE, LOCALEDIR);
1196 textdomain(PACKAGE); */
1199 thisprogram = argv[0];
1201 mount_cifs_usage(stderr);
1203 if(thisprogram == NULL)
1204 thisprogram = "mount.cifs";
1207 /* BB add workstation name and domain and pass down */
1209 /* #ifdef _GNU_SOURCE
1210 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1214 share_name = strndup(argv[1], MAX_UNC_LEN);
1215 if (share_name == NULL) {
1216 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1219 mountpoint = argv[2];
1220 } else if (argc == 2) {
1221 if ((strcmp(argv[1], "-V") == 0) ||
1222 (strcmp(argv[1], "--version") == 0))
1224 print_cifs_mount_version();
1228 if ((strcmp(argv[1], "-h") == 0) ||
1229 (strcmp(argv[1], "-?") == 0) ||
1230 (strcmp(argv[1], "--help") == 0))
1231 mount_cifs_usage(stdout);
1233 mount_cifs_usage(stderr);
1235 mount_cifs_usage(stderr);
1239 /* add sharename in opts string as unc= parm */
1240 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1241 longopts, NULL)) != -1) {
1243 /* No code to do the following options yet */
1245 list_with_volumelabel = 1;
1248 volumelabel = optarg;
1255 case 'h': /* help */
1256 mount_cifs_usage(stdout);
1265 "option 'b' (MS_BIND) not supported\n");
1273 "option 'm' (MS_MOVE) not supported\n");
1277 orgoptions = strdup(optarg);
1279 case 'r': /* mount readonly */
1289 print_cifs_mount_version();
1292 flags &= ~MS_RDONLY;
1295 rsize = atoi(optarg) ;
1298 wsize = atoi(optarg);
1301 if (isdigit(*optarg)) {
1304 uid = strtoul(optarg, &ep, 10);
1306 fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1312 if (!(pw = getpwnam(optarg))) {
1313 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1321 if (isdigit(*optarg)) {
1324 gid = strtoul(optarg, &ep, 10);
1326 fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1332 if (!(gr = getgrnam(optarg))) {
1333 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1345 domain_name = optarg; /* BB fix this - currently ignored */
1349 if(mountpassword == NULL)
1350 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1353 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1357 get_password_from_file(0 /* stdin */,NULL);
1365 fprintf(stderr, "unknown mount option %c\n",c);
1366 mount_cifs_usage(stderr);
1370 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1371 mount_cifs_usage(stderr);
1374 /* make sure mountpoint is legit */
1375 rc = check_mountpoint(thisprogram, mountpoint);
1379 /* sanity check for unprivileged mounts */
1381 rc = check_fstab(thisprogram, mountpoint, dev_name,
1386 /* enable any default user mount flags */
1387 flags |= CIFS_SETUID_FLAGS;
1390 if (getenv("PASSWD")) {
1391 if(mountpassword == NULL)
1392 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1394 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1397 } else if (getenv("PASSWD_FD")) {
1398 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1399 } else if (getenv("PASSWD_FILE")) {
1400 get_password_from_file(0, getenv("PASSWD_FILE"));
1403 if (orgoptions && parse_options(&orgoptions, &flags)) {
1409 #if !CIFS_LEGACY_SETUID_CHECK
1410 if (!(flags & (MS_USERS|MS_USER))) {
1411 fprintf(stderr, "%s: permission denied\n", thisprogram);
1415 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1418 fprintf(stderr, "%s: not installed setuid - \"user\" "
1419 "CIFS mounts not supported.",
1426 flags &= ~(MS_USERS|MS_USER);
1428 addrhead = addr = parse_server(&share_name);
1429 if((addrhead == NULL) && (got_ip == 0)) {
1430 fprintf(stderr, "No ip address specified and hostname not found\n");
1435 /* BB save off path and pop after mount returns? */
1436 resolved_path = (char *)malloc(PATH_MAX+1);
1438 /* Note that if we can not canonicalize the name, we get
1439 another chance to see if it is valid when we chdir to it */
1440 if (realpath(mountpoint, resolved_path)) {
1441 mountpoint = resolved_path;
1445 /* Note that the password will not be retrieved from the
1446 USER env variable (ie user%password form) as there is
1447 already a PASSWD environment varaible */
1449 user_name = strdup(getenv("USER"));
1450 if (user_name == NULL)
1451 user_name = getusername();
1455 if(got_password == 0) {
1456 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1457 no good replacement yet. */
1458 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1459 if (!tmp_pass || !mountpassword) {
1460 fprintf(stderr, "Password not entered, exiting\n");
1463 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1466 /* FIXME launch daemon (handles dfs name resolution and credential change)
1467 remember to clear parms and overwrite password field before launching */
1469 optlen = strlen(orgoptions);
1474 optlen += strlen(share_name) + 4;
1476 fprintf(stderr, "No server share name specified\n");
1477 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1481 optlen += strlen(user_name) + 6;
1482 optlen += MAX_ADDRESS_LEN + 4;
1484 optlen += strlen(mountpassword) + 6;
1487 options_size = optlen + 10 + DOMAIN_SIZE;
1488 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 */);
1490 if(options == NULL) {
1491 fprintf(stderr, "Could not allocate memory for mount options\n");
1495 strlcpy(options, "unc=", options_size);
1496 strlcat(options,share_name,options_size);
1497 /* scan backwards and reverse direction of slash */
1498 temp = strrchr(options, '/');
1499 if(temp > options + 6)
1502 /* check for syntax like user=domain\user */
1504 domain_name = check_for_domain(&user_name);
1505 strlcat(options,",user=",options_size);
1506 strlcat(options,user_name,options_size);
1510 /* extra length accounted for in option string above */
1511 strlcat(options,",domain=",options_size);
1512 strlcat(options,domain_name,options_size);
1516 /* Commas have to be doubled, or else they will
1517 look like the parameter separator */
1518 /* if(sep is not set)*/
1520 check_for_comma(&mountpassword);
1521 strlcat(options,",pass=",options_size);
1522 strlcat(options,mountpassword,options_size);
1525 strlcat(options,",ver=",options_size);
1526 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1529 strlcat(options,",",options_size);
1530 strlcat(options,orgoptions,options_size);
1533 strlcat(options,",prefixpath=",options_size);
1534 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1537 fprintf(stderr, "\nmount.cifs kernel mount options %s \n",options);
1539 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1540 replace_char(dev_name, '\\', '/', strlen(share_name));
1542 if (!got_ip && addr) {
1543 strlcat(options, ",ip=", options_size);
1544 current_len = strnlen(options, options_size);
1545 optionstail = options + current_len;
1546 switch (addr->ai_addr->sa_family) {
1548 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1549 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1550 options_size - current_len);
1553 addr4 = (struct sockaddr_in *) addr->ai_addr;
1554 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1555 options_size - current_len);
1561 /* if the address looks bogus, try the next one */
1563 addr = addr->ai_next;
1571 if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1572 strlcat(options, "%", options_size);
1573 current_len = strnlen(options, options_size);
1574 optionstail = options + current_len;
1575 snprintf(optionstail, options_size - current_len, "%u",
1576 addr6->sin6_scope_id);
1579 if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1584 addr = addr->ai_next;
1590 fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1595 if (uppercase_string(dev_name) &&
1596 uppercase_string(share_name) &&
1597 uppercase_string(prefixpath)) {
1598 fprintf(stderr, "retrying with upper case share name\n");
1603 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1604 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1612 atexit(unlock_mtab);
1615 fprintf(stderr, "cannot lock mtab");
1618 pmntfile = setmntent(MOUNTED, "a+");
1620 fprintf(stderr, "could not update mount table\n");
1625 mountent.mnt_fsname = dev_name;
1626 mountent.mnt_dir = mountpoint;
1627 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1628 mountent.mnt_opts = (char *)malloc(220);
1629 if(mountent.mnt_opts) {
1630 char * mount_user = getusername();
1631 memset(mountent.mnt_opts,0,200);
1632 if(flags & MS_RDONLY)
1633 strlcat(mountent.mnt_opts,"ro",220);
1635 strlcat(mountent.mnt_opts,"rw",220);
1636 if(flags & MS_MANDLOCK)
1637 strlcat(mountent.mnt_opts,",mand",220);
1638 if(flags & MS_NOEXEC)
1639 strlcat(mountent.mnt_opts,",noexec",220);
1640 if(flags & MS_NOSUID)
1641 strlcat(mountent.mnt_opts,",nosuid",220);
1642 if(flags & MS_NODEV)
1643 strlcat(mountent.mnt_opts,",nodev",220);
1644 if(flags & MS_SYNCHRONOUS)
1645 strlcat(mountent.mnt_opts,",sync",220);
1648 strlcat(mountent.mnt_opts,
1650 strlcat(mountent.mnt_opts,
1655 mountent.mnt_freq = 0;
1656 mountent.mnt_passno = 0;
1657 rc = addmntent(pmntfile,&mountent);
1658 endmntent(pmntfile);
1660 SAFE_FREE(mountent.mnt_opts);
1665 int len = strlen(mountpassword);
1666 memset(mountpassword,0,len);
1667 SAFE_FREE(mountpassword);
1671 freeaddrinfo(addrhead);
1673 SAFE_FREE(orgoptions);
1674 SAFE_FREE(resolved_path);
1675 SAFE_FREE(share_name);