2 * Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 * Copyright (C) 2003,2010 Steve French (sfrench@us.ibm.com)
4 * Copyright (C) 2008 Jeremy Allison (jra@samba.org)
5 * Copyright (C) 2010 Jeff Layton (jlayton@samba.org)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #endif /* HAVE_CONFIG_H */
31 #include <sys/types.h>
32 #include <sys/mount.h>
34 #include <sys/utsname.h>
35 #include <sys/socket.h>
36 #include <arpa/inet.h>
49 #else /* HAVE_LIBCAP_NG */
51 #include <sys/prctl.h>
52 #endif /* HAVE_PRCTL */
54 #include <sys/capability.h>
55 #endif /* HAVE_LIBCAP */
56 #endif /* HAVE_LIBCAP_NG */
59 #include "resolve_host.h"
69 /* private flags - clear these before passing to kernel */
70 #define MS_USERS 0x40000000
71 #define MS_USER 0x80000000
73 #define MAX_UNC_LEN 1024
75 /* I believe that the kernel limits options data to a page */
76 #define MAX_OPTIONS_LEN 4096
78 /* max length of mtab options */
79 #define MTAB_OPTIONS_LEN 220
82 * Maximum length of "share" portion of a UNC. I have no idea if this is at
83 * all valid. According to MSDN, the typical max length of any component is
84 * 255, so use that here.
86 #define MAX_SHARE_LEN 256
88 /* max length of username (somewhat made up here) */
89 #define MAX_USERNAME_SIZE 32
92 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x = NULL; } } while (0)
95 #define MOUNT_PASSWD_SIZE 128
96 #define MAX_DOMAIN_SIZE 64
99 * value of the ver= option that gets passed to the kernel. Used to indicate
100 * behavioral changes introduced in the mount helper.
102 #define OPTIONS_VERSION "1"
105 * mount.cifs has been the subject of many "security" bugs that have arisen
106 * because of users and distributions installing it as a setuid root program
107 * before it had been audited for security holes. The default behavior is
108 * now to allow mount.cifs to be run as a setuid root program. Some admins
109 * may want to disable this fully, so this switch remains in place.
111 #define CIFS_DISABLE_SETUID_CAPABILITY 0
114 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
115 * flags by default. These defaults can be changed here.
117 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
120 * Values for parsing a credentials file.
122 #define CRED_UNPARSEABLE 0
128 * Values for parsing command line options.
134 #define OPT_USER_XATTR 3
143 #define OPT_FILE_MODE 12
145 #define OPT_DIR_MODE 14
147 #define OPT_NO_SUID 16
149 #define OPT_NO_DEV 18
151 #define OPT_NO_LOCK 20
152 #define OPT_NO_EXEC 21
157 #define OPT_REMOUNT 26
159 #define OPT_NOMAND 28
163 /* struct for holding parsed mount info for use by privleged process */
164 struct parsed_mount_info {
166 char host[NI_MAXHOST + 1];
167 char share[MAX_SHARE_LEN + 1];
168 char prefix[PATH_MAX + 1];
169 char options[MAX_OPTIONS_LEN];
170 char domain[MAX_DOMAIN_SIZE + 1];
171 char username[MAX_USERNAME_SIZE + 1];
172 char password[MOUNT_PASSWD_SIZE + 1];
173 char addrlist[MAX_ADDR_LIST_LEN];
174 unsigned int got_user:1;
175 unsigned int got_password:1;
176 unsigned int fakemnt:1;
177 unsigned int nomtab:1;
178 unsigned int verboseflag:1;
181 const char *thisprogram;
182 const char *cifs_fstype = "cifs";
183 const char *smb2_fstype = "smb2";
185 static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info);
187 static int check_setuid(void)
190 fprintf(stderr, "This program is not installed setuid root - "
191 " \"user\" CIFS mounts not supported.\n");
195 #if CIFS_DISABLE_SETUID_CAPABILITY
196 if (getuid() && !geteuid()) {
197 printf("This mount.cifs program has been built with the "
198 "ability to run as a setuid root program disabled.\n");
201 #endif /* CIFS_DISABLE_SETUID_CAPABILITY */
207 check_fstab(const char *progname, const char *mountpoint, const char *devname,
213 /* make sure this mount is listed in /etc/fstab */
214 fstab = setmntent(_PATH_MNTTAB, "r");
216 fprintf(stderr, "Couldn't open %s for reading!\n", _PATH_MNTTAB);
220 while ((mnt = getmntent(fstab))) {
221 if (!strcmp(mountpoint, mnt->mnt_dir))
226 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
227 fprintf(stderr, "%s: permission denied: no match for "
228 "%s found in %s\n", progname, mountpoint, _PATH_MNTTAB);
233 * 'mount' munges the options from fstab before passing them
234 * to us. It is non-trivial to test that we have the correct
235 * set of options. We don't want to trust what the user
236 * gave us, so just take whatever is in /etc/fstab.
239 *options = strdup(mnt->mnt_opts);
246 open nofollow - avoid symlink exposure?
247 get owner of dir see if matches self or if root
248 call system(umount argv) etc.
252 static int mount_cifs_usage(FILE * stream)
254 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n",
256 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
257 fprintf(stream, " to a local directory.\n\nOptions:\n");
258 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
259 fprintf(stream, "\nLess commonly used options:");
261 "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
263 "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
265 "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
267 "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign,fsc");
269 "\n\nOptions not needed for servers supporting CIFS Unix extensions");
271 "\n\t(e.g. unneeded for mounts to most Samba versions):");
273 "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
274 fprintf(stream, "\n\nRarely used options:");
276 "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
278 "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
280 "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
282 "\n\nOptions are described in more detail in the manual page");
283 fprintf(stream, "\n\tman 8 mount.cifs\n");
284 fprintf(stream, "\nTo display the version number of the mount helper:");
285 fprintf(stream, "\n\t%s -V\n", thisprogram);
287 if (stream == stderr)
292 static int mount_smb2_usage(FILE *stream)
294 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n",
296 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
297 fprintf(stream, " to a local directory.\n\nOptions:\n");
298 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
299 fprintf(stream, "\nLess commonly used options:");
301 "\n\tcredentials=<filename>,guest,perm,noperm,rw,ro,");
303 "\n\tsep=<char>,iocharset=<codepage>,exec,noexec");
305 "\n\tnolock,directio,sec=<authentication mechanism>,sign");
307 "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>");
308 fprintf(stream, "\n\nRarely used options:");
310 "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
312 "\n\tdev,nodev,hard,soft,intr,");
314 "\n\tnointr,ignorecase,noacl,prefixpath=<path>,nobrl");
316 "\n\nOptions are described in more detail in the manual page");
317 fprintf(stream, "\n\tman 8 mount.smb2\n");
318 fprintf(stream, "\nTo display the version number of the mount helper:");
319 fprintf(stream, "\n\tmount.smb2 -V\n");
321 if (stream == stderr)
326 static int mount_usage(FILE *stream)
330 if (strcmp(thisprogram, "mount.smb2") == 0)
331 rc = mount_smb2_usage(stream);
333 rc = mount_cifs_usage(stream);
339 * CIFS has to "escape" commas in the password field so that they don't
340 * end up getting confused for option delimiters. Copy password into pw
341 * field, turning any commas into double commas.
343 static int set_password(struct parsed_mount_info *parsed_info, const char *src)
345 char *dst = parsed_info->password;
346 unsigned int i = 0, j = 0;
352 if (j > sizeof(parsed_info->password)) {
353 fprintf(stderr, "Converted password too long!\n");
358 parsed_info->got_password = 1;
362 /* caller frees username if necessary */
363 static char *getusername(uid_t uid)
365 char *username = NULL;
366 struct passwd *password = getpwuid(uid);
369 username = password->pw_name;
374 * Parse a username string into parsed_mount_info fields. The format is:
376 * DOMAIN\username%password
378 * ...obviously the only required component is "username". The source string
379 * is modified in the process, but it should remain unchanged at the end.
381 static int parse_username(char *rawuser, struct parsed_mount_info *parsed_info)
383 char *user, *password, slash;
386 /* everything after first % sign is a password */
387 password = strchr(rawuser, '%');
389 rc = set_password(parsed_info, password + 1);
395 /* everything after first '/' or '\' is a username */
396 user = strchr(rawuser, '/');
398 user = strchr(rawuser, '\\');
400 /* everything before that slash is a domain */
404 strlcpy(parsed_info->domain, rawuser,
405 sizeof(parsed_info->domain));
411 strlcpy(parsed_info->username, user, sizeof(parsed_info->username));
412 parsed_info->got_user = 1;
419 #ifdef HAVE_LIBCAP_NG
421 drop_capabilities(int parent)
423 capng_setpid(getpid());
424 capng_clear(CAPNG_SELECT_BOTH);
426 if (capng_updatev(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH, CAP_DAC_OVERRIDE, -1)) {
427 fprintf(stderr, "Unable to update capability set.\n");
430 if (capng_update(CAPNG_ADD, CAPNG_PERMITTED|CAPNG_EFFECTIVE, CAP_SYS_ADMIN)) {
431 fprintf(stderr, "Unable to update capability set.\n");
435 if (capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH)) {
436 fprintf(stderr, "Unable to update capability set.\n");
440 if (capng_apply(CAPNG_SELECT_BOTH)) {
441 fprintf(stderr, "Unable to apply new capability set.\n");
448 toggle_dac_capability(int writable, int enable)
450 unsigned int capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
452 if (capng_update(enable ? CAPNG_ADD : CAPNG_DROP, CAPNG_EFFECTIVE, capability)) {
453 fprintf(stderr, "Unable to update capability set.\n");
456 if (capng_apply(CAPNG_SELECT_CAPS)) {
457 fprintf(stderr, "Unable to apply new capability set.\n");
462 #else /* HAVE_LIBCAP_NG */
466 prune_bounding_set(void)
469 static int bounding_set_cleared;
471 if (bounding_set_cleared)
474 for (i = 0; i <= CAP_LAST_CAP && rc == 0; ++i)
475 rc = prctl(PR_CAPBSET_DROP, i);
478 fprintf(stderr, "Unable to clear capability bounding set: %d\n", rc);
482 ++bounding_set_cleared;
485 #else /* HAVE_PRCTL */
487 prune_bounding_set(void)
491 #endif /* HAVE_PRCTL */
493 drop_capabilities(int parent)
497 cap_value_t cap_list[3];
499 rc = prune_bounding_set();
503 caps = cap_get_proc();
505 fprintf(stderr, "Unable to get current capability set: %s\n",
510 if (cap_clear(caps) == -1) {
511 fprintf(stderr, "Unable to clear capability set: %s\n",
517 if (parent || getuid() == 0) {
519 cap_list[0] = CAP_DAC_READ_SEARCH;
521 cap_list[1] = CAP_DAC_OVERRIDE;
522 cap_list[2] = CAP_SYS_ADMIN;
525 if (cap_set_flag(caps, CAP_PERMITTED, ncaps, cap_list, CAP_SET) == -1) {
526 fprintf(stderr, "Unable to set permitted capabilities: %s\n",
532 cap_list[0] = CAP_SYS_ADMIN;
533 if (cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET) == -1) {
534 fprintf(stderr, "Unable to set effective capabilities: %s\n",
542 if (cap_set_proc(caps) != 0) {
543 fprintf(stderr, "Unable to set current process capabilities: %s\n",
553 toggle_dac_capability(int writable, int enable)
557 cap_value_t capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
562 caps = cap_get_proc();
564 fprintf(stderr, "Unable to get current capability set: %s\n",
569 if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &capability,
570 enable ? CAP_SET : CAP_CLEAR) == -1) {
571 fprintf(stderr, "Unable to %s effective capabilities: %s\n",
572 enable ? "set" : "clear", strerror(errno));
577 if (cap_set_proc(caps) != 0) {
578 fprintf(stderr, "Unable to set current process capabilities: %s\n",
586 #else /* HAVE_LIBCAP */
588 drop_capabilities(int parent)
594 toggle_dac_capability(int writable, int enable)
598 #endif /* HAVE_LIBCAP */
599 #endif /* HAVE_LIBCAP_NG */
601 static void null_terminate_endl(char *source)
603 char *newline = strchr(source, '\n');
609 * Parse a line from the credentials file. Changes target to first
610 * character after '=' on 'line' and returns the value type of the line
611 * Returns CRED_UNPARSEABLE on failure or if either parameter is NULL.
613 static int parse_cred_line(char *line, char **target)
615 if (line == NULL || target == NULL)
618 /* position target at first char of value */
619 *target = strchr(line, '=');
624 /* tell the caller which value target points to */
625 if (strncasecmp("user", line, 4) == 0)
627 if (strncasecmp("pass", line, 4) == 0)
629 if (strncasecmp("dom", line, 3) == 0)
633 return CRED_UNPARSEABLE;
636 static int open_cred_file(char *file_name,
637 struct parsed_mount_info *parsed_info)
639 char *line_buf = NULL;
640 char *temp_val = NULL;
643 const int line_buf_size = 4096;
644 const int min_non_white = 10;
646 i = toggle_dac_capability(0, 1);
650 i = access(file_name, R_OK);
652 toggle_dac_capability(0, 0);
656 fs = fopen(file_name, "r");
658 toggle_dac_capability(0, 0);
663 i = toggle_dac_capability(0, 0);
667 line_buf = (char *)malloc(line_buf_size);
668 if (line_buf == NULL) {
673 /* parse line from credentials file */
674 while (fgets(line_buf, line_buf_size, fs)) {
675 /* eat leading white space */
676 for (i = 0; i < line_buf_size - min_non_white + 1; i++) {
677 if ((line_buf[i] != ' ') && (line_buf[i] != '\t'))
680 null_terminate_endl(line_buf);
682 /* parse next token */
683 switch (parse_cred_line(line_buf + i, &temp_val)) {
685 i = parse_username(temp_val, parsed_info);
690 i = set_password(parsed_info, temp_val);
695 if (parsed_info->verboseflag)
696 fprintf(stderr, "domain=%s\n",
698 strlcpy(parsed_info->domain, temp_val,
699 sizeof(parsed_info->domain));
701 case CRED_UNPARSEABLE:
702 if (parsed_info->verboseflag)
703 fprintf(stderr, "Credential formatted "
705 temp_val ? temp_val : "(null)");
714 /* make sure passwords are scrubbed from memory */
715 if (line_buf != NULL)
716 memset(line_buf, 0, line_buf_size);
722 get_password_from_file(int file_descript, char *filename,
723 struct parsed_mount_info *parsed_info)
726 char buf[sizeof(parsed_info->password) + 1];
728 if (filename != NULL) {
729 rc = toggle_dac_capability(0, 1);
733 rc = access(filename, R_OK);
736 "mount.cifs failed: access check of %s failed: %s\n",
737 filename, strerror(errno));
738 toggle_dac_capability(0, 0);
742 file_descript = open(filename, O_RDONLY);
743 if (file_descript < 0) {
745 "mount.cifs failed. %s attempting to open password file %s\n",
746 strerror(errno), filename);
747 toggle_dac_capability(0, 0);
751 rc = toggle_dac_capability(0, 0);
758 memset(buf, 0, sizeof(buf));
759 rc = read(file_descript, buf, sizeof(buf) - 1);
762 "mount.cifs failed. Error %s reading password file\n",
768 rc = set_password(parsed_info, buf);
771 if (filename != NULL)
772 close(file_descript);
777 * Returns OPT_ERROR on unparsable token.
779 static int parse_opt_token(const char *token)
784 if (strncmp(token, "users", 5) == 0)
786 if (strncmp(token, "user_xattr", 10) == 0)
787 return OPT_USER_XATTR;
788 if (strncmp(token, "user", 4) == 0)
790 if (strncmp(token, "pass", 4) == 0)
792 if (strncmp(token, "sec", 3) == 0)
794 if (strncmp(token, "ip", 2) == 0)
796 if (strncmp(token, "unc", 3) == 0 ||
797 strncmp(token, "target", 6) == 0 ||
798 strncmp(token, "path", 4) == 0)
800 if (strncmp(token, "dom", 3) == 0 || strncmp(token, "workg", 5) == 0)
802 if (strncmp(token, "cred", 4) == 0)
804 if (strncmp(token, "uid", 3) == 0)
806 if (strncmp(token, "cruid", 5) == 0)
808 if (strncmp(token, "gid", 3) == 0)
810 if (strncmp(token, "fmask", 5) == 0)
812 if (strncmp(token, "file_mode", 9) == 0)
813 return OPT_FILE_MODE;
814 if (strncmp(token, "dmask", 5) == 0)
816 if (strncmp(token, "dir_mode", 4) == 0 || strncmp(token, "dirm", 4) == 0)
818 if (strncmp(token, "nosuid", 6) == 0)
820 if (strncmp(token, "suid", 4) == 0)
822 if (strncmp(token, "nodev", 5) == 0)
824 if (strncmp(token, "nobrl", 5) == 0 || strncmp(token, "nolock", 6) == 0)
826 if (strncmp(token, "mand", 4) == 0)
828 if (strncmp(token, "nomand", 6) == 0)
830 if (strncmp(token, "dev", 3) == 0)
832 if (strncmp(token, "noexec", 6) == 0)
834 if (strncmp(token, "exec", 4) == 0)
836 if (strncmp(token, "guest", 5) == 0)
838 if (strncmp(token, "ro", 2) == 0)
840 if (strncmp(token, "rw", 2) == 0)
842 if (strncmp(token, "remount", 7) == 0)
844 if (strncmp(token, "_netdev", 7) == 0)
851 parse_options(const char *data, struct parsed_mount_info *parsed_info)
855 char *next_keyword = NULL;
856 char *out = parsed_info->options;
857 unsigned long *filesys_flags = &parsed_info->flags;
864 uid_t uid, cruid = 0;
870 * max 32-bit uint in decimal is 4294967295 which is 10 chars wide
871 * +1 for NULL, and +1 for good measure
875 /* make sure we're starting from beginning */
878 /* BB fixme check for separator override BB */
891 * format is keyword,keyword2=value2,keyword3=value3...
892 * data = next keyword
893 * value = next value ie stuff after equal sign
895 while (data && *data) {
896 next_keyword = strchr(data, ','); /* BB handle sep= */
898 /* temporarily null terminate end of keyword=value pair */
902 /* temporarily null terminate keyword if there's a value */
904 if ((equals = strchr(data, '=')) != NULL) {
909 switch(parse_opt_token(data)) {
911 if (!value || !*value) {
912 *filesys_flags |= MS_USERS;
918 if (!value || !*value) {
919 if (data[4] == '\0') {
920 *filesys_flags |= MS_USER;
924 "username specified with no parameter\n");
928 /* domain/username%password */
929 const int max = MAX_DOMAIN_SIZE +
931 MOUNT_PASSWD_SIZE + 2;
932 if (strnlen(value, max + 1) >= max + 1) {
933 fprintf(stderr, "username too long\n");
936 rc = parse_username(value, parsed_info);
939 "problem parsing username\n");
946 if (parsed_info->got_password) {
948 "password specified twice, ignoring second\n");
951 if (!value || !*value) {
952 parsed_info->got_password = 1;
955 rc = set_password(parsed_info, value);
962 if (!strncmp(value, "none", 4) ||
963 !strncmp(value, "krb5", 4))
964 parsed_info->got_password = 1;
969 if (!value || !*value) {
971 "target ip address argument missing\n");
972 } else if (strnlen(value, MAX_ADDRESS_LEN) <=
974 strcpy(parsed_info->addrlist, value);
975 if (parsed_info->verboseflag)
977 "ip address %s override specified\n",
981 fprintf(stderr, "ip address too long\n");
987 /* unc || target || path */
989 if (!value || !*value) {
991 "invalid path to network resource\n");
994 rc = parse_unc(value, parsed_info);
999 /* dom || workgroup */
1001 if (!value || !*value) {
1002 fprintf(stderr, "CIFS: invalid domain name\n");
1005 if (strnlen(value, sizeof(parsed_info->domain)) >=
1006 sizeof(parsed_info->domain)) {
1007 fprintf(stderr, "domain name too long\n");
1010 strlcpy(parsed_info->domain, value,
1011 sizeof(parsed_info->domain));
1015 if (!value || !*value) {
1017 "invalid credential file name specified\n");
1020 rc = open_cred_file(value, parsed_info);
1023 "error %d (%s) opening credential file %s\n",
1024 rc, strerror(rc), value);
1030 if (!value || !*value)
1035 uid = strtoul(value, &ep, 10);
1039 pw = getpwnam(value);
1041 fprintf(stderr, "bad user name \"%s\"\n", value);
1049 if (!value || !*value)
1054 cruid = strtoul(value, &ep, 10);
1058 pw = getpwnam(value);
1060 fprintf(stderr, "bad user name \"%s\"\n", value);
1067 if (!value || !*value)
1072 gid = strtoul(value, &ep, 10);
1076 gr = getgrnam(value);
1078 fprintf(stderr, "bad group name \"%s\"\n", value);
1085 /* fmask fall through to file_mode */
1088 "WARNING: CIFS mount option 'fmask' is\
1089 deprecated. Use 'file_mode' instead.\n");
1090 data = "file_mode"; /* BB fix this */
1092 if (!value || !*value) {
1094 "Option '%s' requires a numerical argument\n",
1099 if (value[0] != '0')
1101 "WARNING: '%s' not expressed in octal.\n",
1105 /* dmask falls through to dir_mode */
1108 "WARNING: CIFS mount option 'dmask' is\
1109 deprecated. Use 'dir_mode' instead.\n");
1112 if (!value || !*value) {
1114 "Option '%s' requires a numerical argument\n",
1119 if (value[0] != '0')
1121 "WARNING: '%s' not expressed in octal.\n",
1125 /* the following mount options should be
1126 stripped out from what is passed into the kernel
1127 since these options are best passed as the
1128 mount flags rather than redundantly to the kernel
1129 and could generate spurious warnings depending on the
1130 level of the corresponding cifs vfs kernel code */
1132 *filesys_flags |= MS_NOSUID;
1135 *filesys_flags &= ~MS_NOSUID;
1138 *filesys_flags |= MS_NODEV;
1140 /* nolock || nobrl */
1142 *filesys_flags &= ~MS_MANDLOCK;
1145 *filesys_flags |= MS_MANDLOCK;
1148 *filesys_flags &= ~MS_MANDLOCK;
1151 *filesys_flags &= ~MS_NODEV;
1154 *filesys_flags |= MS_NOEXEC;
1157 *filesys_flags &= ~MS_NOEXEC;
1160 parsed_info->got_user = 1;
1161 parsed_info->got_password = 1;
1164 *filesys_flags |= MS_RDONLY;
1167 *filesys_flags &= ~MS_RDONLY;
1170 *filesys_flags |= MS_REMOUNT;
1176 /* check size before copying option to buffer */
1177 word_len = strlen(data);
1179 word_len += 1 + strlen(value);
1181 /* need 2 extra bytes for comma and null byte */
1182 if (out_len + word_len + 2 > MAX_OPTIONS_LEN) {
1183 fprintf(stderr, "Options string too long\n");
1187 /* put back equals sign, if any */
1191 /* go ahead and copy */
1193 strlcat(out, ",", MAX_OPTIONS_LEN);
1195 strlcat(out, data, MAX_OPTIONS_LEN);
1196 out_len = strlen(out);
1198 data = next_keyword;
1201 /* special-case the uid and gid */
1203 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", uid);
1205 /* comma + "uid=" + terminating NULL == 6 */
1206 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1207 fprintf(stderr, "Options string too long\n");
1212 strlcat(out, ",", MAX_OPTIONS_LEN);
1215 snprintf(out + out_len, word_len + 5, "uid=%s", txtbuf);
1216 out_len = strlen(out);
1219 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", cruid);
1221 /* comma + "cruid=" + terminating NULL == 6 */
1222 if (out_len + word_len + 8 > MAX_OPTIONS_LEN) {
1223 fprintf(stderr, "Options string too long\n");
1228 strlcat(out, ",", MAX_OPTIONS_LEN);
1231 snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf);
1232 out_len = strlen(out);
1235 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", gid);
1237 /* comma + "gid=" + terminating NULL == 6 */
1238 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1239 fprintf(stderr, "Options string too long\n");
1244 strlcat(out, ",", MAX_OPTIONS_LEN);
1247 snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf);
1252 static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info)
1254 int length = strnlen(unc_name, MAX_UNC_LEN);
1255 const char *host, *share, *prepath;
1256 size_t hostlen, sharelen, prepathlen;
1258 if (length > (MAX_UNC_LEN - 1)) {
1259 fprintf(stderr, "mount error: UNC name too long\n");
1264 fprintf(stderr, "mount error: UNC name too short\n");
1268 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1269 (strncasecmp("smb://", unc_name, 6) == 0)) {
1271 "Mounting cifs URL not implemented yet. Attempt to mount %s\n",
1276 /* Set up "host" and "share" pointers based on UNC format. */
1277 if (strncmp(unc_name, "//", 2) && strncmp(unc_name, "\\\\", 2)) {
1279 * check for nfs syntax (server:/share/prepath)
1281 * FIXME: IPv6 addresses?
1284 share = strchr(host, ':');
1286 fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1289 hostlen = share - host;
1294 host = unc_name + 2;
1295 hostlen = strcspn(host, "/\\");
1297 fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1300 share = host + hostlen + 1;
1303 if (hostlen + 1 > sizeof(parsed_info->host)) {
1304 fprintf(stderr, "mount.cifs: host portion of UNC too long\n");
1308 sharelen = strcspn(share, "/\\");
1309 if (sharelen + 1 > sizeof(parsed_info->share)) {
1310 fprintf(stderr, "mount.cifs: share portion of UNC too long\n");
1314 prepath = share + sharelen;
1315 if (*prepath != '\0')
1318 prepathlen = strlen(prepath);
1320 if (prepathlen + 1 > sizeof(parsed_info->prefix)) {
1321 fprintf(stderr, "mount.cifs: UNC prefixpath too long\n");
1325 /* copy pieces into their resepective buffers */
1326 memcpy(parsed_info->host, host, hostlen);
1327 memcpy(parsed_info->share, share, sharelen);
1328 memcpy(parsed_info->prefix, prepath, prepathlen);
1333 static int get_pw_from_env(struct parsed_mount_info *parsed_info)
1337 if (getenv("PASSWD"))
1338 rc = set_password(parsed_info, getenv("PASSWD"));
1339 else if (getenv("PASSWD_FD"))
1340 rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL,
1342 else if (getenv("PASSWD_FILE"))
1343 rc = get_password_from_file(0, getenv("PASSWD_FILE"),
1349 static struct option longopts[] = {
1350 {"all", 0, NULL, 'a'},
1351 {"help", 0, NULL, 'h'},
1352 {"move", 0, NULL, 'm'},
1353 {"bind", 0, NULL, 'b'},
1354 {"read-only", 0, NULL, 'r'},
1355 {"ro", 0, NULL, 'r'},
1356 {"verbose", 0, NULL, 'v'},
1357 {"version", 0, NULL, 'V'},
1358 {"read-write", 0, NULL, 'w'},
1359 {"rw", 0, NULL, 'w'},
1360 {"options", 1, NULL, 'o'},
1361 {"type", 1, NULL, 't'},
1362 {"uid", 1, NULL, '1'},
1363 {"gid", 1, NULL, '2'},
1364 {"user", 1, NULL, 'u'},
1365 {"username", 1, NULL, 'u'},
1366 {"dom", 1, NULL, 'd'},
1367 {"domain", 1, NULL, 'd'},
1368 {"password", 1, NULL, 'p'},
1369 {"pass", 1, NULL, 'p'},
1370 {"credentials", 1, NULL, 'c'},
1371 {"port", 1, NULL, 'P'},
1375 /* convert a string to uppercase. return false if the string
1376 * wasn't ASCII. Return success on a NULL ptr */
1377 static int uppercase_string(char *string)
1383 /* check for unicode */
1384 if ((unsigned char)string[0] & 0x80)
1386 *string = toupper((unsigned char)*string);
1393 static void print_cifs_mount_version(void)
1395 printf("mount.cifs version: %s\n", VERSION);
1399 * This function borrowed from fuse-utils...
1401 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1402 * newlines embedded within the text fields. To make sure no one corrupts
1403 * the mtab, fail the mount if there are embedded newlines.
1405 static int check_newline(const char *progname, const char *name)
1408 for (s = "\n"; *s; s++) {
1409 if (strchr(name, *s)) {
1411 "%s: illegal character 0x%02x in mount entry\n",
1419 static int check_mtab(const char *progname, const char *devname,
1422 if (check_newline(progname, devname) || check_newline(progname, dir))
1428 add_mtab(char *devname, char *mountpoint, unsigned long flags, const char *fstype)
1430 int rc = 0, tmprc, fd;
1432 char *mount_user = NULL;
1433 struct mntent mountent;
1434 struct stat statbuf;
1436 sigset_t mask, oldmask;
1440 mount_user = getusername(uid);
1443 * Set the real uid to the effective uid. This prevents unprivileged
1444 * users from sending signals to this process, though ^c on controlling
1445 * terminal should still work.
1447 rc = setreuid(geteuid(), -1);
1449 fprintf(stderr, "Unable to set real uid to effective uid: %s\n",
1454 rc = sigfillset(&mask);
1456 fprintf(stderr, "Unable to set filled signal mask\n");
1460 rc = sigprocmask(SIG_SETMASK, &mask, &oldmask);
1462 fprintf(stderr, "Unable to make process ignore signals\n");
1466 rc = toggle_dac_capability(1, 1);
1470 atexit(unlock_mtab);
1473 fprintf(stderr, "cannot lock mtab");
1478 pmntfile = setmntent(MOUNTED, "a+");
1480 fprintf(stderr, "could not update mount table\n");
1486 fd = fileno(pmntfile);
1488 fprintf(stderr, "mntent does not appear to be valid\n");
1494 rc = fstat(fd, &statbuf);
1496 fprintf(stderr, "unable to fstat open mtab\n");
1497 endmntent(pmntfile);
1503 mountent.mnt_fsname = devname;
1504 mountent.mnt_dir = mountpoint;
1505 mountent.mnt_type = (char *)(void *)fstype;
1506 mountent.mnt_opts = (char *)calloc(MTAB_OPTIONS_LEN, 1);
1507 if (mountent.mnt_opts) {
1508 if (flags & MS_RDONLY)
1509 strlcat(mountent.mnt_opts, "ro", MTAB_OPTIONS_LEN);
1511 strlcat(mountent.mnt_opts, "rw", MTAB_OPTIONS_LEN);
1513 if (flags & MS_MANDLOCK)
1514 strlcat(mountent.mnt_opts, ",mand", MTAB_OPTIONS_LEN);
1515 if (flags & MS_NOEXEC)
1516 strlcat(mountent.mnt_opts, ",noexec", MTAB_OPTIONS_LEN);
1517 if (flags & MS_NOSUID)
1518 strlcat(mountent.mnt_opts, ",nosuid", MTAB_OPTIONS_LEN);
1519 if (flags & MS_NODEV)
1520 strlcat(mountent.mnt_opts, ",nodev", MTAB_OPTIONS_LEN);
1521 if (flags & MS_SYNCHRONOUS)
1522 strlcat(mountent.mnt_opts, ",sync", MTAB_OPTIONS_LEN);
1524 strlcat(mountent.mnt_opts, ",user=", MTAB_OPTIONS_LEN);
1525 strlcat(mountent.mnt_opts, mount_user,
1529 mountent.mnt_freq = 0;
1530 mountent.mnt_passno = 0;
1531 rc = addmntent(pmntfile, &mountent);
1533 fprintf(stderr, "unable to add mount entry to mtab\n");
1534 ftruncate(fd, statbuf.st_size);
1537 tmprc = my_endmntent(pmntfile, statbuf.st_size);
1539 fprintf(stderr, "error %d detected on close of mtab\n", tmprc);
1543 SAFE_FREE(mountent.mnt_opts);
1545 toggle_dac_capability(1, 0);
1546 sigprocmask(SIG_SETMASK, &oldmask, NULL);
1551 /* have the child drop root privileges */
1553 drop_child_privs(void)
1556 uid_t uid = getuid();
1557 gid_t gid = getgid();
1562 fprintf(stderr, "Unable set group identity: %s\n",
1570 fprintf(stderr, "Unable set user identity: %s\n",
1580 assemble_mountinfo(struct parsed_mount_info *parsed_info,
1581 const char *thisprogram, const char *mountpoint,
1582 const char *orig_dev, char *orgoptions)
1586 rc = drop_capabilities(0);
1590 rc = drop_child_privs();
1595 rc = check_fstab(thisprogram, mountpoint, orig_dev,
1600 /* enable any default user mount flags */
1601 parsed_info->flags |= CIFS_SETUID_FLAGS;
1604 rc = get_pw_from_env(parsed_info);
1609 rc = parse_options(orgoptions, parsed_info);
1615 if (!(parsed_info->flags & (MS_USERS | MS_USER))) {
1616 fprintf(stderr, "%s: permission denied\n", thisprogram);
1622 parsed_info->flags &= ~(MS_USERS | MS_USER);
1624 rc = parse_unc(orig_dev, parsed_info);
1628 if (parsed_info->addrlist[0] == '\0')
1629 rc = resolve_host(parsed_info->host, parsed_info->addrlist);
1633 fprintf(stderr, "mount error: could not resolve address for "
1634 "%s: %s\n", parsed_info->host,
1635 rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
1639 fprintf(stderr, "mount error: problem parsing address "
1640 "list: %s\n", strerror(errno));
1644 if (!parsed_info->got_user) {
1646 * Note that the password will not be retrieved from the
1647 * USER env variable (ie user%password form) as there is
1648 * already a PASSWD environment varaible
1651 strlcpy(parsed_info->username, getenv("USER"),
1652 sizeof(parsed_info->username));
1654 strlcpy(parsed_info->username, getusername(getuid()),
1655 sizeof(parsed_info->username));
1656 parsed_info->got_user = 1;
1659 if (!parsed_info->got_password) {
1660 /* getpass is obsolete, but there's apparently nothing that replaces it */
1661 char *tmp_pass = getpass("Password: ");
1663 fprintf(stderr, "Error reading password, exiting\n");
1667 rc = set_password(parsed_info, tmp_pass);
1672 /* copy in ver= string. It's not really needed, but what the hell */
1673 strlcat(parsed_info->options, ",ver=", sizeof(parsed_info->options));
1674 strlcat(parsed_info->options, OPTIONS_VERSION, sizeof(parsed_info->options));
1676 /* copy in user= string */
1677 if (parsed_info->got_user) {
1678 strlcat(parsed_info->options, ",user=",
1679 sizeof(parsed_info->options));
1680 strlcat(parsed_info->options, parsed_info->username,
1681 sizeof(parsed_info->options));
1684 if (*parsed_info->domain) {
1685 strlcat(parsed_info->options, ",domain=",
1686 sizeof(parsed_info->options));
1687 strlcat(parsed_info->options, parsed_info->domain,
1688 sizeof(parsed_info->options));
1695 int main(int argc, char **argv)
1698 char *orgoptions = NULL;
1699 char *mountpoint = NULL;
1700 char *options = NULL;
1701 char *orig_dev = NULL;
1702 char *currentaddress, *nextaddress;
1704 int already_uppercased = 0;
1705 size_t options_size = MAX_OPTIONS_LEN;
1706 struct parsed_mount_info *parsed_info = NULL;
1710 rc = check_setuid();
1714 rc = drop_capabilities(1);
1718 /* setlocale(LC_ALL, "");
1719 bindtextdomain(PACKAGE, LOCALEDIR);
1720 textdomain(PACKAGE); */
1722 if (!argc || !argv) {
1723 rc = mount_usage(stderr);
1727 thisprogram = basename(argv[0]);
1728 if (thisprogram == NULL)
1729 thisprogram = "mount.cifs";
1731 /* allocate parsed_info as shared anonymous memory range */
1732 parsed_info = mmap(0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
1733 MAP_ANONYMOUS | MAP_SHARED, -1, 0);
1734 if (parsed_info == (struct parsed_mount_info *) -1) {
1736 fprintf(stderr, "Unable to allocate memory: %s\n",
1741 /* add sharename in opts string as unc= parm */
1742 while ((c = getopt_long(argc, argv, "?fhno:rvVw",
1743 longopts, NULL)) != -1) {
1746 case 'h': /* help */
1747 rc = mount_usage(stdout);
1750 ++parsed_info->nomtab;
1753 orgoptions = strndup(optarg, MAX_OPTIONS_LEN);
1759 case 'r': /* mount readonly */
1760 parsed_info->flags |= MS_RDONLY;
1763 ++parsed_info->verboseflag;
1766 print_cifs_mount_version();
1769 parsed_info->flags &= ~MS_RDONLY;
1772 ++parsed_info->fakemnt;
1775 fprintf(stderr, "unknown command-line option: %c\n", c);
1776 rc = mount_usage(stderr);
1781 if (argc < 3 || argv[optind] == NULL || argv[optind + 1] == NULL) {
1782 rc = mount_usage(stderr);
1786 orig_dev = argv[optind];
1787 mountpoint = argv[optind + 1];
1789 /* chdir into mountpoint as soon as possible */
1790 rc = toggle_dac_capability(0, 1);
1793 rc = chdir(mountpoint);
1795 fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
1801 mountpoint = realpath(".", NULL);
1803 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1804 mountpoint, strerror(errno));
1808 rc = toggle_dac_capability(0, 0);
1813 * mount.cifs does privilege separation. Most of the code to handle
1814 * assembling the mount info is done in a child process that drops
1815 * privileges. The info is assembled in parsed_info which is a
1816 * shared, mmaped memory segment. The parent waits for the child to
1817 * exit and checks the return code. If it's anything but "0", then
1818 * the process exits without attempting anything further.
1822 fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
1827 rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
1828 orig_dev, orgoptions);
1833 if (!WIFEXITED(rc)) {
1834 fprintf(stderr, "Child process terminated abnormally.\n");
1838 rc = WEXITSTATUS(rc);
1843 options = calloc(options_size, 1);
1845 fprintf(stderr, "Unable to allocate memory.\n");
1850 currentaddress = parsed_info->addrlist;
1851 nextaddress = strchr(currentaddress, ',');
1853 *nextaddress++ = '\0';
1856 if (!currentaddress) {
1857 fprintf(stderr, "Unable to find suitable address.\n");
1861 strlcpy(options, "ip=", options_size);
1862 strlcat(options, currentaddress, options_size);
1864 strlcat(options, ",unc=\\\\", options_size);
1865 strlcat(options, parsed_info->host, options_size);
1866 strlcat(options, "\\", options_size);
1867 strlcat(options, parsed_info->share, options_size);
1869 if (*parsed_info->options) {
1870 strlcat(options, ",", options_size);
1871 strlcat(options, parsed_info->options, options_size);
1874 if (*parsed_info->prefix) {
1875 strlcat(options, ",prefixpath=", options_size);
1876 strlcat(options, parsed_info->prefix, options_size);
1879 if (parsed_info->verboseflag)
1880 fprintf(stderr, "%s kernel mount options: %s",
1881 thisprogram, options);
1883 if (parsed_info->got_password) {
1885 * Commas have to be doubled, or else they will
1886 * look like the parameter separator
1888 strlcat(options, ",pass=", options_size);
1889 strlcat(options, parsed_info->password, options_size);
1890 if (parsed_info->verboseflag)
1891 fprintf(stderr, ",pass=********");
1894 if (parsed_info->verboseflag)
1895 fprintf(stderr, "\n");
1897 rc = check_mtab(thisprogram, orig_dev, mountpoint);
1901 if (strcmp(thisprogram, "mount.smb2") == 0)
1902 fstype = smb2_fstype;
1904 fstype = cifs_fstype;
1906 if (!parsed_info->fakemnt) {
1907 toggle_dac_capability(0, 1);
1908 rc = mount(orig_dev, ".", fstype, parsed_info->flags, options);
1909 toggle_dac_capability(0, 0);
1916 currentaddress = nextaddress;
1917 if (currentaddress) {
1918 nextaddress = strchr(currentaddress, ',');
1920 *nextaddress++ = '\0';
1925 "mount error: %s filesystem not supported by the system\n", fstype);
1928 if (!already_uppercased &&
1929 uppercase_string(parsed_info->host) &&
1930 uppercase_string(parsed_info->share) &&
1931 uppercase_string(parsed_info->prefix)) {
1933 "Retrying with upper case share name\n");
1934 already_uppercased = 1;
1938 fprintf(stderr, "mount error(%d): %s\n", errno,
1941 "Refer to the %s(8) manual page (e.g. man "
1942 "%s)\n", thisprogram, thisprogram);
1948 if (!parsed_info->nomtab && !mtab_unusable())
1949 rc = add_mtab(orig_dev, mountpoint, parsed_info->flags, fstype);
1953 memset(parsed_info->password, 0, sizeof(parsed_info->password));
1954 munmap(parsed_info, sizeof(*parsed_info));
1957 SAFE_FREE(orgoptions);