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/>. */
21 #endif /* HAVE_CONFIG_H */
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>
54 /* private flags - clear these before passing to kernel */
55 #define MS_USERS 0x40000000
56 #define MS_USER 0x80000000
58 #define MAX_UNC_LEN 1024
60 /* I believe that the kernel limits options data to a page */
61 #define MAX_OPTIONS_LEN 4096
64 * Maximum length of "share" portion of a UNC. I have no idea if this is at
65 * all valid. According to MSDN, the typical max length of any component is
66 * 255, so use that here.
68 #define MAX_SHARE_LEN 256
70 /* currently maximum length of IPv6 address string */
71 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
73 /* limit list of addresses to 16 max-size addrs */
74 #define MAX_ADDR_LIST_LEN (MAX_ADDRESS_LEN * 16)
77 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
80 #define MOUNT_PASSWD_SIZE 128
81 #define DOMAIN_SIZE 64
84 * value of the ver= option that gets passed to the kernel. Used to indicate
85 * behavioral changes introduced in the mount helper.
87 #define OPTIONS_VERSION "1"
90 * mount.cifs has been the subject of many "security" bugs that have arisen
91 * because of users and distributions installing it as a setuid root program.
92 * mount.cifs has not been audited for security. Thus, we strongly recommend
93 * that it not be installed setuid root. To make that abundantly clear,
94 * mount.cifs now check whether it's running setuid root and exit with an
95 * error if it is. If you wish to disable this check, then set the following
96 * #define to 1, but please realize that you do so at your own peril.
98 #define CIFS_DISABLE_SETUID_CHECK 0
101 * By default, mount.cifs follows the conventions set forth by /bin/mount
102 * for user mounts. That is, it requires that the mount be listed in
103 * /etc/fstab with the "user" option when run as an unprivileged user and
104 * mount.cifs is setuid root.
106 * Older versions of mount.cifs however were "looser" in this regard. When
107 * made setuid root, a user could run mount.cifs directly and mount any share
108 * on a directory owned by that user.
110 * The legacy behavior is now disabled by default. To reenable it, set the
111 * following #define to true.
113 #define CIFS_LEGACY_SETUID_CHECK 0
116 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
117 * flags by default. These defaults can be changed here.
119 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
121 /* struct for holding parsed mount info for use by privleged process */
122 struct parsed_mount_info {
124 char host[NI_MAXHOST];
125 char share[MAX_SHARE_LEN];
126 char prefix[PATH_MAX];
127 char options[MAX_OPTIONS_LEN];
128 char password[MOUNT_PASSWD_SIZE + 1];
129 char address_list[MAX_ADDR_LIST_LEN];
130 unsigned int got_password:1;
133 const char *thisprogram;
136 static int got_user = 0;
137 static int got_domain = 0;
138 static int got_ip = 0;
139 static int got_unc = 0;
140 static int got_uid = 0;
141 static int got_gid = 0;
142 static char * user_name = NULL;
143 char * domain_name = NULL;
144 char * prefixpath = NULL;
145 const char *cifs_fstype = "cifs";
147 #if CIFS_LEGACY_SETUID_CHECK
149 check_mountpoint(const char *progname, char *mountpoint)
151 /* do extra checks on mountpoint for legacy setuid behavior */
152 if (!getuid() || geteuid())
155 if (statbuf.st_uid != getuid()) {
156 fprintf(stderr, "%s: %s is not owned by user\n", progname,
161 if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
162 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
169 #else /* CIFS_LEGACY_SETUID_CHECK */
171 check_mountpoint(const char *progname, char *mountpoint)
175 #endif /* CIFS_LEGACY_SETUID_CHECK */
177 #if CIFS_DISABLE_SETUID_CHECK
183 #else /* CIFS_DISABLE_SETUID_CHECK */
187 if (getuid() && !geteuid()) {
188 printf("This mount.cifs program has been built with the "
189 "ability to run as a setuid root program disabled.\n"
190 "mount.cifs has not been well audited for security "
191 "holes. Therefore the Samba team does not recommend "
192 "installing it as a setuid root program.\n");
198 #endif /* CIFS_DISABLE_SETUID_CHECK */
200 #if CIFS_LEGACY_SETUID_CHECK
202 check_fstab(const char *progname, char *mountpoint, char *devname,
207 #else /* CIFS_LEGACY_SETUID_CHECK */
209 check_fstab(const char *progname, char *mountpoint, char *devname,
215 /* make sure this mount is listed in /etc/fstab */
216 fstab = setmntent(_PATH_FSTAB, "r");
218 fprintf(stderr, "Couldn't open %s for reading!\n",
223 while((mnt = getmntent(fstab))) {
224 if (!strcmp(mountpoint, mnt->mnt_dir))
229 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
230 fprintf(stderr, "%s: permission denied: no match for "
231 "%s found in %s\n", progname, mountpoint,
237 * 'mount' munges the options from fstab before passing them
238 * to us. It is non-trivial to test that we have the correct
239 * set of options. We don't want to trust what the user
240 * gave us, so just take whatever is in /etc/fstab.
243 *options = strdup(mnt->mnt_opts);
246 #endif /* CIFS_LEGACY_SETUID_CHECK */
251 open nofollow - avoid symlink exposure?
252 get owner of dir see if matches self or if root
253 call system(umount argv) etc.
257 static char * check_for_domain(char **);
261 mount_cifs_usage(FILE *stream)
263 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
264 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
265 fprintf(stream, " to a local directory.\n\nOptions:\n");
266 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
267 fprintf(stream, "\nLess commonly used options:");
268 fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
269 fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
270 fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
271 fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
272 fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
273 fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
274 fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
275 fprintf(stream, "\n\nRarely used options:");
276 fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
277 fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
278 fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
279 fprintf(stream, "\n\nOptions are described in more detail in the manual page");
280 fprintf(stream, "\n\tman 8 mount.cifs\n");
281 fprintf(stream, "\nTo display the version number of the mount helper:");
282 fprintf(stream, "\n\t%s -V\n",thisprogram);
284 if (stream == stderr)
289 /* caller frees username if necessary */
290 static char * getusername(void) {
291 char *username = NULL;
292 struct passwd *password = getpwuid(getuid());
295 username = password->pw_name;
300 static int open_cred_file(char *file_name, struct parsed_mount_info *parsed_info)
307 i = access(file_name, R_OK);
311 fs = fopen(file_name,"r");
314 line_buf = (char *)malloc(4096);
315 if(line_buf == NULL) {
320 while(fgets(line_buf,4096,fs)) {
321 /* parse line from credential file */
323 /* eat leading white space */
324 for(i=0;i<4086;i++) {
325 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
327 /* if whitespace - skip past it */
329 if (strncasecmp("username",line_buf+i,8) == 0) {
330 temp_val = strchr(line_buf + i,'=');
332 /* go past equals sign */
334 for(length = 0;length<4087;length++) {
335 if ((temp_val[length] == '\n')
336 || (temp_val[length] == '\0')) {
337 temp_val[length] = '\0';
342 fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
343 memset(line_buf,0,4096);
347 user_name = (char *)calloc(1 + length,1);
348 /* BB adding free of user_name string before exit,
349 not really necessary but would be cleaner */
350 strlcpy(user_name,temp_val, length+1);
353 } else if (strncasecmp("password",line_buf+i,8) == 0) {
354 temp_val = strchr(line_buf+i,'=');
356 /* go past equals sign */
358 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
359 if ((temp_val[length] == '\n')
360 || (temp_val[length] == '\0')) {
361 temp_val[length] = '\0';
365 if(length > MOUNT_PASSWD_SIZE) {
366 fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
367 memset(line_buf, 0, 4096);
370 strlcpy(parsed_info->password, temp_val, MOUNT_PASSWD_SIZE + 1);
371 parsed_info->got_password = 1;
373 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
374 temp_val = strchr(line_buf+i,'=');
376 /* go past equals sign */
379 fprintf(stderr, "\nDomain %s\n",temp_val);
380 for(length = 0;length<DOMAIN_SIZE+1;length++) {
381 if ((temp_val[length] == '\n')
382 || (temp_val[length] == '\0')) {
383 temp_val[length] = '\0';
387 if(length > DOMAIN_SIZE) {
388 fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
391 if(domain_name == NULL) {
392 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
394 memset(domain_name,0,DOMAIN_SIZE);
396 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
410 get_password_from_file(int file_descript, char *filename, struct parsed_mount_info *parsed_info)
416 if(filename != NULL) {
417 rc = access(filename, R_OK);
419 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
420 filename, strerror(errno));
423 file_descript = open(filename, O_RDONLY);
424 if(file_descript < 0) {
425 fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
426 strerror(errno),filename);
430 /* else file already open and fd provided */
432 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
433 rc = read(file_descript,&c,1);
435 fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
437 close(file_descript);
440 if(parsed_info->password[0] == 0) {
442 fprintf(stderr, "\nWarning: null password used since cifs password file empty");
445 } else /* read valid character */ {
446 if((c == 0) || (c == '\n')) {
447 parsed_info->password[i] = '\0';
450 parsed_info->password[i] = c;
453 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
454 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
457 parsed_info->got_password = 1;
458 if(filename != NULL) {
459 close(file_descript);
466 parse_options(const char *data, struct parsed_mount_info *parsed_info)
468 char *percent_char = NULL;
469 char *value = NULL, *equals = NULL;
470 char *next_keyword = NULL;
471 char *out = parsed_info->options;
472 unsigned long *filesys_flags = &parsed_info->flags;
479 /* make sure we're starting from beginning */
482 /* BB fixme check for separator override BB */
485 snprintf(user,sizeof(user),"%u",getuid());
487 snprintf(group,sizeof(group),"%u",getgid());
494 * format is keyword,keyword2=value2,keyword3=value3...
495 * data = next keyword
496 * value = next value ie stuff after equal sign
498 while (data && *data) {
499 next_keyword = strchr(data,','); /* BB handle sep= */
501 /* temporarily null terminate end of keyword=value pair */
505 /* temporarily null terminate keyword if there's a value */
507 if ((equals = strchr(data, '=')) != NULL) {
512 /* FIXME: turn into a token parser? */
513 if (strncmp(data, "users",5) == 0) {
514 if(!value || !*value) {
515 *filesys_flags |= MS_USERS;
518 } else if (strncmp(data, "user_xattr",10) == 0) {
519 /* do nothing - need to skip so not parsed as user name */
520 } else if (strncmp(data, "user", 4) == 0) {
522 if (!value || !*value) {
523 if(data[4] == '\0') {
524 *filesys_flags |= MS_USER;
527 fprintf(stderr, "username specified with no parameter\n");
531 if (strnlen(value, 260) < 260) {
533 percent_char = strchr(value,'%');
536 if(parsed_info->got_password)
537 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
538 parsed_info->got_password = 1;
540 strlcpy(parsed_info->password, percent_char, sizeof(parsed_info->password));
541 /* remove password from username */
542 while(*percent_char != 0) {
547 /* this is only case in which the user
548 name buf is not malloc - so we have to
549 check for domain name embedded within
550 the user name here since the later
551 call to check_for_domain will not be
553 domain_name = check_for_domain(&value);
555 fprintf(stderr, "username too long\n");
559 } else if (strncmp(data, "pass", 4) == 0) {
560 if (!value || !*value) {
561 if(parsed_info->got_password) {
562 fprintf(stderr, "\npassword specified twice, ignoring second\n");
564 parsed_info->got_password = 1;
565 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
566 if (parsed_info->got_password) {
567 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
569 strlcpy(parsed_info->password, value, MOUNT_PASSWD_SIZE + 1);
570 parsed_info->got_password = 1;
573 fprintf(stderr, "password too long\n");
577 } else if (strncmp(data, "sec", 3) == 0) {
579 if (!strncmp(value, "none", 4) ||
580 !strncmp(value, "krb5", 4))
581 parsed_info->got_password = 1;
583 } else if (strncmp(data, "ip", 2) == 0) {
584 if (!value || !*value) {
585 fprintf(stderr, "target ip address argument missing");
586 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
588 fprintf(stderr, "ip address %s override specified\n",value);
591 fprintf(stderr, "ip address too long\n");
594 } else if ((strncmp(data, "unc", 3) == 0)
595 || (strncmp(data, "target", 6) == 0)
596 || (strncmp(data, "path", 4) == 0)) {
597 if (!value || !*value) {
598 fprintf(stderr, "invalid path to network resource\n");
599 return EX_USAGE; /* needs_arg; */
600 } else if(strnlen(value,5) < 5) {
601 fprintf(stderr, "UNC name too short");
604 if (strnlen(value, 300) < 300) {
606 if (strncmp(value, "//", 2) == 0) {
608 fprintf(stderr, "unc name specified twice, ignoring second\n");
611 } else if (strncmp(value, "\\\\", 2) != 0) {
612 fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
616 fprintf(stderr, "unc name specified twice, ignoring second\n");
621 fprintf(stderr, "CIFS: UNC name too long\n");
624 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
625 || (strncmp(data, "workg", 5) == 0)) {
626 /* note this allows for synonyms of "domain"
627 such as "DOM" and "dom" and "workgroup"
628 and "WORKGRP" etc. */
629 if (!value || !*value) {
630 fprintf(stderr, "CIFS: invalid domain name\n");
633 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
636 fprintf(stderr, "domain name too long\n");
639 } else if (strncmp(data, "cred", 4) == 0) {
640 if (value && *value) {
641 rc = open_cred_file(value, parsed_info);
643 fprintf(stderr, "error %d (%s) opening credential file %s\n",
644 rc, strerror(rc), value);
648 fprintf(stderr, "invalid credential file name specified\n");
651 } else if (strncmp(data, "uid", 3) == 0) {
652 if (value && *value) {
654 if (!isdigit(*value)) {
657 if (!(pw = getpwnam(value))) {
658 fprintf(stderr, "bad user name \"%s\"\n", value);
661 snprintf(user, sizeof(user), "%u", pw->pw_uid);
663 strlcpy(user,value,sizeof(user));
667 } else if (strncmp(data, "gid", 3) == 0) {
668 if (value && *value) {
670 if (!isdigit(*value)) {
673 if (!(gr = getgrnam(value))) {
674 fprintf(stderr, "bad group name \"%s\"\n", value);
677 snprintf(group, sizeof(group), "%u", gr->gr_gid);
679 strlcpy(group,value,sizeof(group));
683 /* fmask and dmask synonyms for people used to smbfs syntax */
684 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
685 if (!value || !*value) {
686 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
690 if (value[0] != '0') {
691 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
694 if (strcmp (data, "fmask") == 0) {
695 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
696 data = "file_mode"; /* BB fix this */
698 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
699 if (!value || !*value) {
700 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
704 if (value[0] != '0') {
705 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
708 if (strcmp (data, "dmask") == 0) {
709 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
712 /* the following eight mount options should be
713 stripped out from what is passed into the kernel
714 since these eight options are best passed as the
715 mount flags rather than redundantly to the kernel
716 and could generate spurious warnings depending on the
717 level of the corresponding cifs vfs kernel code */
718 } else if (strncmp(data, "nosuid", 6) == 0) {
719 *filesys_flags |= MS_NOSUID;
720 } else if (strncmp(data, "suid", 4) == 0) {
721 *filesys_flags &= ~MS_NOSUID;
722 } else if (strncmp(data, "nodev", 5) == 0) {
723 *filesys_flags |= MS_NODEV;
724 } else if ((strncmp(data, "nobrl", 5) == 0) ||
725 (strncmp(data, "nolock", 6) == 0)) {
726 *filesys_flags &= ~MS_MANDLOCK;
727 } else if (strncmp(data, "dev", 3) == 0) {
728 *filesys_flags &= ~MS_NODEV;
729 } else if (strncmp(data, "noexec", 6) == 0) {
730 *filesys_flags |= MS_NOEXEC;
731 } else if (strncmp(data, "exec", 4) == 0) {
732 *filesys_flags &= ~MS_NOEXEC;
733 } else if (strncmp(data, "guest", 5) == 0) {
734 user_name = (char *)calloc(1, 1);
736 parsed_info->got_password = 1;
737 } else if (strncmp(data, "ro", 2) == 0) {
738 *filesys_flags |= MS_RDONLY;
740 } else if (strncmp(data, "rw", 2) == 0) {
741 *filesys_flags &= ~MS_RDONLY;
743 } else if (strncmp(data, "remount", 7) == 0) {
744 *filesys_flags |= MS_REMOUNT;
747 /* check size before copying option to buffer */
748 word_len = strlen(data);
750 word_len += 1 + strlen(value);
752 /* need 2 extra bytes for comma and null byte */
753 if (out_len + word_len + 2 > MAX_OPTIONS_LEN) {
754 fprintf(stderr, "Options string too long\n");
758 /* put back equals sign, if any */
762 /* go ahead and copy */
764 strlcat(out, ",", MAX_OPTIONS_LEN);
766 strlcat(out, data, MAX_OPTIONS_LEN);
767 out_len = strlen(out);
772 /* special-case the uid and gid */
774 word_len = strlen(user);
776 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
777 fprintf(stderr, "Options string too long\n");
782 strlcat(out, ",", out_len + word_len + 6);
785 snprintf(out + out_len, word_len + 5, "uid=%s", user);
786 out_len = strlen(out);
789 word_len = strlen(group);
791 if (out_len + 1 + word_len + 6 > MAX_OPTIONS_LEN) {
792 fprintf(stderr, "Options string too long\n");
797 strlcat(out, ",", out_len + word_len + 6);
800 snprintf(out + out_len, word_len + 5, "gid=%s", group);
801 out_len = strlen(out);
807 /* replace all (one or more) commas with double commas */
809 replace_commas(char *pass)
811 /* a little extra buffer to simplify conversion */
812 char tmpbuf[MOUNT_PASSWD_SIZE + 3];
815 /* don't do anything if there are no commas */
816 if (!strchr(pass, ','))
822 tmpbuf[j++] = pass[i++];
823 if (j > MOUNT_PASSWD_SIZE + 1) {
824 fprintf(stderr, "Converted password too long!\n");
829 strlcpy(pass, tmpbuf, MOUNT_PASSWD_SIZE + 1);
833 /* Usernames can not have backslash in them and we use
834 [BB check if usernames can have forward slash in them BB]
835 backslash as domain\user separator character
837 static char * check_for_domain(char **ppuser)
839 char * original_string;
849 original_string = *ppuser;
851 if (original_string == NULL)
854 original_len = strlen(original_string);
856 usernm = strchr(*ppuser,'/');
857 if (usernm == NULL) {
858 usernm = strchr(*ppuser,'\\');
864 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
870 if (domainnm[0] != 0) {
873 fprintf(stderr, "null domain\n");
875 len = strlen(domainnm);
876 /* reset domainm to new buffer, and copy
877 domain name into it */
878 domainnm = (char *)malloc(len+1);
882 strlcpy(domainnm,*ppuser,len+1);
884 /* move_string(*ppuser, usernm+1) */
885 len = strlen(usernm+1);
887 if(len >= original_len) {
888 /* should not happen */
892 for(i=0;i<original_len;i++) {
894 original_string[i] = usernm[i+1];
895 else /* stuff with commas to remove last parm */
896 original_string[i] = ',';
899 /* BB add check for more than one slash?
907 /* replace all occurances of "from" in a string with "to" */
908 static void replace_char(char *string, char from, char to, int maxlen)
910 char *lastchar = string + maxlen;
912 string = strchr(string, from);
915 if (string >= lastchar)
921 /* Note that caller frees the returned buffer if necessary */
922 static struct addrinfo *
923 parse_server(char **punc_name)
925 char *unc_name = *punc_name;
926 int length = strnlen(unc_name, MAX_UNC_LEN);
928 struct addrinfo *addrlist;
931 if(length > (MAX_UNC_LEN - 1)) {
932 fprintf(stderr, "mount error: UNC name too long\n");
937 fprintf(stderr, "mount error: UNC name too short\n");
941 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
942 (strncasecmp("smb://", unc_name, 6) == 0)) {
943 fprintf(stderr, "Mounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
947 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
948 /* check for nfs syntax ie server:share */
949 share = strchr(unc_name,':');
951 fprintf(stderr, "mount error: improperly formatted UNC name.");
952 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
956 *punc_name = (char *)malloc(length + 3);
957 if(*punc_name == NULL) {
958 *punc_name = unc_name;
963 strlcpy((*punc_name)+2, unc_name, length + 1);
965 unc_name = *punc_name;
966 unc_name[length+2] = 0;
974 * allow for either delimiter between host and sharename
975 * If there's not one, then the UNC is malformed
977 if (!(share = strpbrk(unc_name, "/\\"))) {
978 fprintf(stderr, "mount error: Malformed UNC\n");
982 *share = 0; /* temporarily terminate the string */
985 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
987 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
988 unc_name, gai_strerror(rc));
992 *(share - 1) = '/'; /* put delimiter back */
994 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
995 if ((prefixpath = strpbrk(share, "/\\"))) {
996 *prefixpath = 0; /* permanently terminate the string */
997 if (!strlen(++prefixpath))
998 prefixpath = NULL; /* this needs to be done explicitly */
1002 fprintf(stderr, "ip address specified explicitly\n");
1005 /* BB should we pass an alternate version of the share name as Unicode */
1011 get_pw_from_env(struct parsed_mount_info *parsed_info)
1015 if (getenv("PASSWD")) {
1016 strlcpy(parsed_info->password, getenv("PASSWD"), MOUNT_PASSWD_SIZE + 1);
1017 parsed_info->got_password = 1;
1018 } else if (getenv("PASSWD_FD"))
1019 rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL, parsed_info);
1020 else if (getenv("PASSWD_FILE"))
1021 rc = get_password_from_file(0, getenv("PASSWD_FILE"), parsed_info);
1026 static struct option longopts[] = {
1027 { "all", 0, NULL, 'a' },
1028 { "help",0, NULL, 'h' },
1029 { "move",0, NULL, 'm' },
1030 { "bind",0, NULL, 'b' },
1031 { "read-only", 0, NULL, 'r' },
1032 { "ro", 0, NULL, 'r' },
1033 { "verbose", 0, NULL, 'v' },
1034 { "version", 0, NULL, 'V' },
1035 { "read-write", 0, NULL, 'w' },
1036 { "rw", 0, NULL, 'w' },
1037 { "options", 1, NULL, 'o' },
1038 { "type", 1, NULL, 't' },
1039 { "uid", 1, NULL, '1'},
1040 { "gid", 1, NULL, '2'},
1041 { "user",1,NULL,'u'},
1042 { "username",1,NULL,'u'},
1043 { "dom",1,NULL,'d'},
1044 { "domain",1,NULL,'d'},
1045 { "password",1,NULL,'p'},
1046 { "pass",1,NULL,'p'},
1047 { "credentials",1,NULL,'c'},
1048 { "port",1,NULL,'P'},
1049 { NULL, 0, NULL, 0 }
1052 /* convert a string to uppercase. return false if the string
1053 * wasn't ASCII. Return success on a NULL ptr */
1055 uppercase_string(char *string)
1061 /* check for unicode */
1062 if ((unsigned char) string[0] & 0x80)
1064 *string = toupper((unsigned char) *string);
1071 static void print_cifs_mount_version(void)
1073 printf("mount.cifs version: %s\n", VERSION);
1077 * This function borrowed from fuse-utils...
1079 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1080 * newlines embedded within the text fields. To make sure no one corrupts
1081 * the mtab, fail the mount if there are embedded newlines.
1083 static int check_newline(const char *progname, const char *name)
1086 for (s = "\n"; *s; s++) {
1087 if (strchr(name, *s)) {
1088 fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
1096 static int check_mtab(const char *progname, const char *devname,
1099 if (check_newline(progname, devname) == -1 ||
1100 check_newline(progname, dir) == -1)
1106 int main(int argc, char ** argv)
1109 char * orgoptions = NULL;
1110 char * share_name = NULL;
1111 const char * ipaddr = NULL;
1112 char * mountpoint = NULL;
1113 char * options = NULL;
1115 char * resolved_path = NULL;
1117 char * dev_name = NULL;
1122 size_t options_size = MAX_OPTIONS_LEN;
1124 int retry = 0; /* set when we have to retry mount with uppercase */
1125 struct addrinfo *addrhead = NULL, *addr;
1126 struct mntent mountent;
1127 struct sockaddr_in *addr4 = NULL;
1128 struct sockaddr_in6 *addr6 = NULL;
1129 struct parsed_mount_info *parsed_info = NULL;
1135 /* setlocale(LC_ALL, "");
1136 bindtextdomain(PACKAGE, LOCALEDIR);
1137 textdomain(PACKAGE); */
1139 if (!argc || !argv) {
1140 rc = mount_cifs_usage(stderr);
1144 thisprogram = argv[0];
1145 if(thisprogram == NULL)
1146 thisprogram = "mount.cifs";
1148 parsed_info = calloc(1, sizeof(*parsed_info));
1150 fprintf(stderr, "Unable to allocate memory.\n");
1154 parsed_info->flags = MS_MANDLOCK;
1156 /* add sharename in opts string as unc= parm */
1157 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1158 longopts, NULL)) != -1) {
1160 /* No code to do the following options yet */
1162 list_with_volumelabel = 1;
1165 volumelabel = optarg;
1172 case 'h': /* help */
1173 rc = mount_cifs_usage(stdout);
1180 parsed_info->flags |= MS_BIND;
1183 "option 'b' (MS_BIND) not supported\n");
1188 parsed_info->flags |= MS_MOVE;
1191 "option 'm' (MS_MOVE) not supported\n");
1195 orgoptions = strndup(optarg, MAX_OPTIONS_LEN);
1201 case 'r': /* mount readonly */
1202 parsed_info->flags |= MS_RDONLY;
1208 print_cifs_mount_version();
1211 parsed_info->flags &= ~MS_RDONLY;
1214 if (isdigit(*optarg)) {
1217 uid = strtoul(optarg, &ep, 10);
1219 fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1226 if (!(pw = getpwnam(optarg))) {
1227 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1236 if (isdigit(*optarg)) {
1239 gid = strtoul(optarg, &ep, 10);
1241 fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1248 if (!(gr = getgrnam(optarg))) {
1249 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1262 domain_name = optarg; /* BB fix this - currently ignored */
1266 strlcpy(parsed_info->password, optarg, sizeof(parsed_info->password));
1267 parsed_info->got_password = 1;
1270 rc = get_password_from_file(0, NULL, parsed_info);
1280 fprintf(stderr, "unknown mount option %c\n",c);
1281 rc = mount_cifs_usage(stderr);
1286 if(argc < 3 || argv[optind] == NULL || argv[optind + 1] == NULL) {
1287 rc = mount_cifs_usage(stderr);
1291 dev_name = argv[optind];
1292 share_name = strndup(argv[optind], MAX_UNC_LEN);
1293 if (share_name == NULL) {
1294 fprintf(stderr, "%s: %s", thisprogram, strerror(ENOMEM));
1298 mountpoint = argv[optind + 1];
1300 /* make sure mountpoint is legit */
1301 rc = chdir(mountpoint);
1303 fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
1309 rc = check_mountpoint(thisprogram, mountpoint);
1313 /* sanity check for unprivileged mounts */
1315 rc = check_fstab(thisprogram, mountpoint, dev_name,
1320 /* enable any default user mount flags */
1321 parsed_info->flags |= CIFS_SETUID_FLAGS;
1324 rc = get_pw_from_env(parsed_info);
1328 options = calloc(options_size, 1);
1330 fprintf(stderr, "Unable to allocate memory.\n");
1336 rc = parse_options(orgoptions, parsed_info);
1342 #if !CIFS_LEGACY_SETUID_CHECK
1343 if (!(parsed_info->flags & (MS_USERS|MS_USER))) {
1344 fprintf(stderr, "%s: permission denied\n", thisprogram);
1348 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1351 fprintf(stderr, "%s: not installed setuid - \"user\" "
1352 "CIFS mounts not supported.",
1359 parsed_info->flags &= ~(MS_USERS|MS_USER);
1361 addrhead = addr = parse_server(&share_name);
1362 if((addrhead == NULL) && (got_ip == 0)) {
1363 fprintf(stderr, "No ip address specified and hostname not found\n");
1368 /* BB save off path and pop after mount returns? */
1369 resolved_path = (char *)malloc(PATH_MAX+1);
1370 if (!resolved_path) {
1371 fprintf(stderr, "Unable to allocate memory.\n");
1376 /* Note that if we can not canonicalize the name, we get
1377 another chance to see if it is valid when we chdir to it */
1378 if(!realpath(".", resolved_path)) {
1379 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1380 mountpoint, strerror(errno));
1385 mountpoint = resolved_path;
1388 /* Note that the password will not be retrieved from the
1389 USER env variable (ie user%password form) as there is
1390 already a PASSWD environment varaible */
1392 user_name = strdup(getenv("USER"));
1393 if (user_name == NULL)
1394 user_name = getusername();
1398 if(!parsed_info->got_password) {
1399 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1400 no good replacement yet. */
1402 fprintf(stderr, "Password not entered, exiting\n");
1406 strlcpy(parsed_info->password, tmp_pass, sizeof(parsed_info->password));
1407 parsed_info->got_password = 1;
1411 fprintf(stderr, "No server share name specified\n");
1418 strlcat(options, ",", options_size);
1420 strlcat(options, "unc=", options_size);
1421 strlcat(options, share_name, options_size);
1423 /* scan backwards and reverse direction of slash */
1424 temp = strrchr(options, '/');
1425 if(temp > options + 6)
1428 /* check for syntax like user=domain\user */
1430 domain_name = check_for_domain(&user_name);
1431 strlcat(options,",user=",options_size);
1432 strlcat(options,user_name,options_size);
1436 /* extra length accounted for in option string above */
1437 strlcat(options,",domain=",options_size);
1438 strlcat(options,domain_name,options_size);
1442 strlcat(options,",ver=",options_size);
1443 strlcat(options,OPTIONS_VERSION,options_size);
1445 if (*parsed_info->options) {
1446 strlcat(options, ",", options_size);
1447 strlcat(options, parsed_info->options, options_size);
1451 strlcat(options,",prefixpath=",options_size);
1452 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1455 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1456 replace_char(dev_name, '\\', '/', strlen(share_name));
1458 if (!got_ip && addr) {
1459 strlcat(options, ",ip=", options_size);
1460 current_len = strnlen(options, options_size);
1461 optionstail = options + current_len;
1462 switch (addr->ai_addr->sa_family) {
1464 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1465 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1466 options_size - current_len);
1469 addr4 = (struct sockaddr_in *) addr->ai_addr;
1470 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1471 options_size - current_len);
1477 /* if the address looks bogus, try the next one */
1479 addr = addr->ai_next;
1487 if (addr && addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1488 strlcat(options, "%", options_size);
1489 current_len = strnlen(options, options_size);
1490 optionstail = options + current_len;
1491 snprintf(optionstail, options_size - current_len, "%u",
1492 addr6->sin6_scope_id);
1496 fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1498 if (parsed_info->got_password) {
1500 * Commas have to be doubled, or else they will
1501 * look like the parameter separator
1504 replace_commas(parsed_info->password);
1505 strlcat(options, ",pass=", options_size);
1506 strlcat(options, parsed_info->password, options_size);
1508 fprintf(stderr, ",pass=********");
1512 fprintf(stderr, "\n");
1514 rc = check_mtab(thisprogram, dev_name, mountpoint);
1518 if (!fakemnt && mount(dev_name, ".", cifs_fstype, parsed_info->flags, options)) {
1523 addr = addr->ai_next;
1529 fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1534 if (uppercase_string(dev_name) &&
1535 uppercase_string(share_name) &&
1536 uppercase_string(prefixpath)) {
1537 fprintf(stderr, "retrying with upper case share name\n");
1542 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1543 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1551 atexit(unlock_mtab);
1554 fprintf(stderr, "cannot lock mtab");
1557 pmntfile = setmntent(MOUNTED, "a+");
1559 fprintf(stderr, "could not update mount table\n");
1564 mountent.mnt_fsname = dev_name;
1565 mountent.mnt_dir = mountpoint;
1566 mountent.mnt_type = (char *)(void *)cifs_fstype;
1567 mountent.mnt_opts = (char *)malloc(220);
1568 if(mountent.mnt_opts) {
1569 char * mount_user = getusername();
1570 memset(mountent.mnt_opts,0,200);
1571 if(parsed_info->flags & MS_RDONLY)
1572 strlcat(mountent.mnt_opts,"ro",220);
1574 strlcat(mountent.mnt_opts,"rw",220);
1575 if(parsed_info->flags & MS_MANDLOCK)
1576 strlcat(mountent.mnt_opts,",mand",220);
1577 if(parsed_info->flags & MS_NOEXEC)
1578 strlcat(mountent.mnt_opts,",noexec",220);
1579 if(parsed_info->flags & MS_NOSUID)
1580 strlcat(mountent.mnt_opts,",nosuid",220);
1581 if(parsed_info->flags & MS_NODEV)
1582 strlcat(mountent.mnt_opts,",nodev",220);
1583 if(parsed_info->flags & MS_SYNCHRONOUS)
1584 strlcat(mountent.mnt_opts,",sync",220);
1587 strlcat(mountent.mnt_opts,
1589 strlcat(mountent.mnt_opts,
1594 mountent.mnt_freq = 0;
1595 mountent.mnt_passno = 0;
1596 rc = addmntent(pmntfile,&mountent);
1597 endmntent(pmntfile);
1599 SAFE_FREE(mountent.mnt_opts);
1604 freeaddrinfo(addrhead);
1605 memset(parsed_info->password, 0, sizeof(parsed_info->password));
1606 SAFE_FREE(parsed_info);
1608 SAFE_FREE(orgoptions);
1609 SAFE_FREE(resolved_path);
1610 SAFE_FREE(share_name);