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;
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)
1034 uid = strtoul(value, &ep, 10);
1035 if (errno != EINVAL && *ep == '\0')
1038 pw = getpwnam(value);
1040 fprintf(stderr, "bad user name \"%s\"\n", value);
1048 if (!value || !*value)
1052 cruid = strtoul(value, &ep, 10);
1053 if (errno != EINVAL && *ep == '\0')
1056 pw = getpwnam(value);
1058 fprintf(stderr, "bad user name \"%s\"\n", value);
1065 if (!value || !*value)
1069 gid = strtoul(value, &ep, 10);
1070 if (errno != EINVAL && *ep == '\0')
1073 gr = getgrnam(value);
1075 fprintf(stderr, "bad group name \"%s\"\n", value);
1082 /* fmask fall through to file_mode */
1085 "WARNING: CIFS mount option 'fmask' is\
1086 deprecated. Use 'file_mode' instead.\n");
1087 data = "file_mode"; /* BB fix this */
1089 if (!value || !*value) {
1091 "Option '%s' requires a numerical argument\n",
1096 if (value[0] != '0')
1098 "WARNING: '%s' not expressed in octal.\n",
1102 /* dmask falls through to dir_mode */
1105 "WARNING: CIFS mount option 'dmask' is\
1106 deprecated. Use 'dir_mode' instead.\n");
1109 if (!value || !*value) {
1111 "Option '%s' requires a numerical argument\n",
1116 if (value[0] != '0')
1118 "WARNING: '%s' not expressed in octal.\n",
1122 /* the following mount options should be
1123 stripped out from what is passed into the kernel
1124 since these options are best passed as the
1125 mount flags rather than redundantly to the kernel
1126 and could generate spurious warnings depending on the
1127 level of the corresponding cifs vfs kernel code */
1129 *filesys_flags |= MS_NOSUID;
1132 *filesys_flags &= ~MS_NOSUID;
1135 *filesys_flags |= MS_NODEV;
1137 /* nolock || nobrl */
1139 *filesys_flags &= ~MS_MANDLOCK;
1142 *filesys_flags |= MS_MANDLOCK;
1145 *filesys_flags &= ~MS_MANDLOCK;
1148 *filesys_flags &= ~MS_NODEV;
1151 *filesys_flags |= MS_NOEXEC;
1154 *filesys_flags &= ~MS_NOEXEC;
1157 parsed_info->got_user = 1;
1158 parsed_info->got_password = 1;
1161 *filesys_flags |= MS_RDONLY;
1164 *filesys_flags &= ~MS_RDONLY;
1167 *filesys_flags |= MS_REMOUNT;
1173 /* check size before copying option to buffer */
1174 word_len = strlen(data);
1176 word_len += 1 + strlen(value);
1178 /* need 2 extra bytes for comma and null byte */
1179 if (out_len + word_len + 2 > MAX_OPTIONS_LEN) {
1180 fprintf(stderr, "Options string too long\n");
1184 /* put back equals sign, if any */
1188 /* go ahead and copy */
1190 strlcat(out, ",", MAX_OPTIONS_LEN);
1192 strlcat(out, data, MAX_OPTIONS_LEN);
1193 out_len = strlen(out);
1195 data = next_keyword;
1198 /* special-case the uid and gid */
1200 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", uid);
1202 /* comma + "uid=" + terminating NULL == 6 */
1203 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1204 fprintf(stderr, "Options string too long\n");
1209 strlcat(out, ",", MAX_OPTIONS_LEN);
1212 snprintf(out + out_len, word_len + 5, "uid=%s", txtbuf);
1213 out_len = strlen(out);
1216 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", cruid);
1218 /* comma + "cruid=" + terminating NULL == 6 */
1219 if (out_len + word_len + 8 > MAX_OPTIONS_LEN) {
1220 fprintf(stderr, "Options string too long\n");
1225 strlcat(out, ",", MAX_OPTIONS_LEN);
1228 snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf);
1229 out_len = strlen(out);
1232 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", gid);
1234 /* comma + "gid=" + terminating NULL == 6 */
1235 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1236 fprintf(stderr, "Options string too long\n");
1241 strlcat(out, ",", MAX_OPTIONS_LEN);
1244 snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf);
1249 static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info)
1251 int length = strnlen(unc_name, MAX_UNC_LEN);
1252 const char *host, *share, *prepath;
1253 size_t hostlen, sharelen, prepathlen;
1255 if (length > (MAX_UNC_LEN - 1)) {
1256 fprintf(stderr, "mount error: UNC name too long\n");
1261 fprintf(stderr, "mount error: UNC name too short\n");
1265 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1266 (strncasecmp("smb://", unc_name, 6) == 0)) {
1268 "Mounting cifs URL not implemented yet. Attempt to mount %s\n",
1273 /* Set up "host" and "share" pointers based on UNC format. */
1274 if (strncmp(unc_name, "//", 2) && strncmp(unc_name, "\\\\", 2)) {
1276 * check for nfs syntax (server:/share/prepath)
1278 * FIXME: IPv6 addresses?
1281 share = strchr(host, ':');
1283 fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1286 hostlen = share - host;
1291 host = unc_name + 2;
1292 hostlen = strcspn(host, "/\\");
1294 fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1297 share = host + hostlen + 1;
1300 if (hostlen + 1 > sizeof(parsed_info->host)) {
1301 fprintf(stderr, "mount.cifs: host portion of UNC too long\n");
1305 sharelen = strcspn(share, "/\\");
1306 if (sharelen + 1 > sizeof(parsed_info->share)) {
1307 fprintf(stderr, "mount.cifs: share portion of UNC too long\n");
1311 prepath = share + sharelen;
1312 if (*prepath != '\0')
1315 prepathlen = strlen(prepath);
1317 if (prepathlen + 1 > sizeof(parsed_info->prefix)) {
1318 fprintf(stderr, "mount.cifs: UNC prefixpath too long\n");
1322 /* copy pieces into their resepective buffers */
1323 memcpy(parsed_info->host, host, hostlen);
1324 memcpy(parsed_info->share, share, sharelen);
1325 memcpy(parsed_info->prefix, prepath, prepathlen);
1330 static int get_pw_from_env(struct parsed_mount_info *parsed_info)
1334 if (getenv("PASSWD"))
1335 rc = set_password(parsed_info, getenv("PASSWD"));
1336 else if (getenv("PASSWD_FD"))
1337 rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL,
1339 else if (getenv("PASSWD_FILE"))
1340 rc = get_password_from_file(0, getenv("PASSWD_FILE"),
1346 static struct option longopts[] = {
1347 {"all", 0, NULL, 'a'},
1348 {"help", 0, NULL, 'h'},
1349 {"move", 0, NULL, 'm'},
1350 {"bind", 0, NULL, 'b'},
1351 {"read-only", 0, NULL, 'r'},
1352 {"ro", 0, NULL, 'r'},
1353 {"verbose", 0, NULL, 'v'},
1354 {"version", 0, NULL, 'V'},
1355 {"read-write", 0, NULL, 'w'},
1356 {"rw", 0, NULL, 'w'},
1357 {"options", 1, NULL, 'o'},
1358 {"type", 1, NULL, 't'},
1359 {"uid", 1, NULL, '1'},
1360 {"gid", 1, NULL, '2'},
1361 {"user", 1, NULL, 'u'},
1362 {"username", 1, NULL, 'u'},
1363 {"dom", 1, NULL, 'd'},
1364 {"domain", 1, NULL, 'd'},
1365 {"password", 1, NULL, 'p'},
1366 {"pass", 1, NULL, 'p'},
1367 {"credentials", 1, NULL, 'c'},
1368 {"port", 1, NULL, 'P'},
1372 /* convert a string to uppercase. return false if the string
1373 * wasn't ASCII. Return success on a NULL ptr */
1374 static int uppercase_string(char *string)
1380 /* check for unicode */
1381 if ((unsigned char)string[0] & 0x80)
1383 *string = toupper((unsigned char)*string);
1390 static void print_cifs_mount_version(void)
1392 printf("mount.cifs version: %s\n", VERSION);
1396 * This function borrowed from fuse-utils...
1398 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1399 * newlines embedded within the text fields. To make sure no one corrupts
1400 * the mtab, fail the mount if there are embedded newlines.
1402 static int check_newline(const char *progname, const char *name)
1405 for (s = "\n"; *s; s++) {
1406 if (strchr(name, *s)) {
1408 "%s: illegal character 0x%02x in mount entry\n",
1416 static int check_mtab(const char *progname, const char *devname,
1419 if (check_newline(progname, devname) == -1 ||
1420 check_newline(progname, dir) == -1)
1426 add_mtab(char *devname, char *mountpoint, unsigned long flags, const char *fstype)
1430 char *mount_user = NULL;
1431 struct mntent mountent;
1433 sigset_t mask, oldmask;
1437 mount_user = getusername(uid);
1440 * Set the real uid to the effective uid. This prevents unprivileged
1441 * users from sending signals to this process, though ^c on controlling
1442 * terminal should still work.
1444 rc = setreuid(geteuid(), -1);
1446 fprintf(stderr, "Unable to set real uid to effective uid: %s\n",
1451 rc = sigfillset(&mask);
1453 fprintf(stderr, "Unable to set filled signal mask\n");
1457 rc = sigprocmask(SIG_SETMASK, &mask, &oldmask);
1459 fprintf(stderr, "Unable to make process ignore signals\n");
1463 rc = toggle_dac_capability(1, 1);
1467 atexit(unlock_mtab);
1470 fprintf(stderr, "cannot lock mtab");
1475 pmntfile = setmntent(MOUNTED, "a+");
1477 fprintf(stderr, "could not update mount table\n");
1483 mountent.mnt_fsname = devname;
1484 mountent.mnt_dir = mountpoint;
1485 mountent.mnt_type = (char *)(void *)fstype;
1486 mountent.mnt_opts = (char *)calloc(MTAB_OPTIONS_LEN, 1);
1487 if (mountent.mnt_opts) {
1488 if (flags & MS_RDONLY)
1489 strlcat(mountent.mnt_opts, "ro", MTAB_OPTIONS_LEN);
1491 strlcat(mountent.mnt_opts, "rw", MTAB_OPTIONS_LEN);
1493 if (flags & MS_MANDLOCK)
1494 strlcat(mountent.mnt_opts, ",mand", MTAB_OPTIONS_LEN);
1495 if (flags & MS_NOEXEC)
1496 strlcat(mountent.mnt_opts, ",noexec", MTAB_OPTIONS_LEN);
1497 if (flags & MS_NOSUID)
1498 strlcat(mountent.mnt_opts, ",nosuid", MTAB_OPTIONS_LEN);
1499 if (flags & MS_NODEV)
1500 strlcat(mountent.mnt_opts, ",nodev", MTAB_OPTIONS_LEN);
1501 if (flags & MS_SYNCHRONOUS)
1502 strlcat(mountent.mnt_opts, ",sync", MTAB_OPTIONS_LEN);
1504 strlcat(mountent.mnt_opts, ",user=", MTAB_OPTIONS_LEN);
1505 strlcat(mountent.mnt_opts, mount_user,
1509 mountent.mnt_freq = 0;
1510 mountent.mnt_passno = 0;
1511 rc = addmntent(pmntfile, &mountent);
1513 fprintf(stderr, "unable to add mount entry to mtab\n");
1516 endmntent(pmntfile);
1518 SAFE_FREE(mountent.mnt_opts);
1520 toggle_dac_capability(1, 0);
1521 sigprocmask(SIG_SETMASK, &oldmask, NULL);
1526 /* have the child drop root privileges */
1528 drop_child_privs(void)
1531 uid_t uid = getuid();
1532 gid_t gid = getgid();
1537 fprintf(stderr, "Unable set group identity: %s\n",
1545 fprintf(stderr, "Unable set user identity: %s\n",
1555 assemble_mountinfo(struct parsed_mount_info *parsed_info,
1556 const char *thisprogram, const char *mountpoint,
1557 const char *orig_dev, char *orgoptions)
1561 rc = drop_capabilities(0);
1565 rc = drop_child_privs();
1570 rc = check_fstab(thisprogram, mountpoint, orig_dev,
1575 /* enable any default user mount flags */
1576 parsed_info->flags |= CIFS_SETUID_FLAGS;
1579 rc = get_pw_from_env(parsed_info);
1584 rc = parse_options(orgoptions, parsed_info);
1590 if (!(parsed_info->flags & (MS_USERS | MS_USER))) {
1591 fprintf(stderr, "%s: permission denied\n", thisprogram);
1597 parsed_info->flags &= ~(MS_USERS | MS_USER);
1599 rc = parse_unc(orig_dev, parsed_info);
1603 if (parsed_info->addrlist[0] == '\0')
1604 rc = resolve_host(parsed_info->host, parsed_info->addrlist);
1608 fprintf(stderr, "mount error: could not resolve address for "
1609 "%s: %s\n", parsed_info->host,
1610 rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
1614 fprintf(stderr, "mount error: problem parsing address "
1615 "list: %s\n", strerror(errno));
1619 if (!parsed_info->got_user) {
1621 * Note that the password will not be retrieved from the
1622 * USER env variable (ie user%password form) as there is
1623 * already a PASSWD environment varaible
1626 strlcpy(parsed_info->username, getenv("USER"),
1627 sizeof(parsed_info->username));
1629 strlcpy(parsed_info->username, getusername(getuid()),
1630 sizeof(parsed_info->username));
1631 parsed_info->got_user = 1;
1634 if (!parsed_info->got_password) {
1635 /* getpass is obsolete, but there's apparently nothing that replaces it */
1636 char *tmp_pass = getpass("Password: ");
1638 fprintf(stderr, "Error reading password, exiting\n");
1642 rc = set_password(parsed_info, tmp_pass);
1647 /* copy in ver= string. It's not really needed, but what the hell */
1648 strlcat(parsed_info->options, ",ver=", sizeof(parsed_info->options));
1649 strlcat(parsed_info->options, OPTIONS_VERSION, sizeof(parsed_info->options));
1651 /* copy in user= string */
1652 if (parsed_info->got_user) {
1653 strlcat(parsed_info->options, ",user=",
1654 sizeof(parsed_info->options));
1655 strlcat(parsed_info->options, parsed_info->username,
1656 sizeof(parsed_info->options));
1659 if (*parsed_info->domain) {
1660 strlcat(parsed_info->options, ",domain=",
1661 sizeof(parsed_info->options));
1662 strlcat(parsed_info->options, parsed_info->domain,
1663 sizeof(parsed_info->options));
1670 int main(int argc, char **argv)
1673 char *orgoptions = NULL;
1674 char *mountpoint = NULL;
1675 char *options = NULL;
1676 char *dev_name = NULL, *orig_dev = NULL;
1677 char *currentaddress, *nextaddress;
1679 int already_uppercased = 0;
1680 size_t options_size = MAX_OPTIONS_LEN;
1682 struct parsed_mount_info *parsed_info = NULL;
1686 rc = check_setuid();
1690 rc = drop_capabilities(1);
1694 /* setlocale(LC_ALL, "");
1695 bindtextdomain(PACKAGE, LOCALEDIR);
1696 textdomain(PACKAGE); */
1698 if (!argc || !argv) {
1699 rc = mount_usage(stderr);
1703 thisprogram = basename(argv[0]);
1704 if (thisprogram == NULL)
1705 thisprogram = "mount.cifs";
1707 /* allocate parsed_info as shared anonymous memory range */
1708 parsed_info = mmap(0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
1709 MAP_ANONYMOUS | MAP_SHARED, -1, 0);
1710 if (parsed_info == (struct parsed_mount_info *) -1) {
1712 fprintf(stderr, "Unable to allocate memory: %s\n",
1717 /* add sharename in opts string as unc= parm */
1718 while ((c = getopt_long(argc, argv, "?fhno:rvVw",
1719 longopts, NULL)) != -1) {
1722 case 'h': /* help */
1723 rc = mount_usage(stdout);
1726 ++parsed_info->nomtab;
1729 orgoptions = strndup(optarg, MAX_OPTIONS_LEN);
1735 case 'r': /* mount readonly */
1736 parsed_info->flags |= MS_RDONLY;
1739 ++parsed_info->verboseflag;
1742 print_cifs_mount_version();
1745 parsed_info->flags &= ~MS_RDONLY;
1748 ++parsed_info->fakemnt;
1751 fprintf(stderr, "unknown command-line option: %c\n", c);
1752 rc = mount_usage(stderr);
1757 if (argc < 3 || argv[optind] == NULL || argv[optind + 1] == NULL) {
1758 rc = mount_usage(stderr);
1762 orig_dev = argv[optind];
1763 mountpoint = argv[optind + 1];
1765 /* chdir into mountpoint as soon as possible */
1766 rc = toggle_dac_capability(0, 1);
1769 rc = chdir(mountpoint);
1771 fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
1777 mountpoint = realpath(".", NULL);
1779 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1780 mountpoint, strerror(errno));
1784 rc = toggle_dac_capability(0, 0);
1789 * mount.cifs does privilege separation. Most of the code to handle
1790 * assembling the mount info is done in a child process that drops
1791 * privileges. The info is assembled in parsed_info which is a
1792 * shared, mmaped memory segment. The parent waits for the child to
1793 * exit and checks the return code. If it's anything but "0", then
1794 * the process exits without attempting anything further.
1798 fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
1803 rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
1804 orig_dev, orgoptions);
1809 if (!WIFEXITED(rc)) {
1810 fprintf(stderr, "Child process terminated abnormally.\n");
1814 rc = WEXITSTATUS(rc);
1819 options = calloc(options_size, 1);
1821 fprintf(stderr, "Unable to allocate memory.\n");
1826 /* lengths of different strings + slashes + trailing \0 */
1827 dev_len = strnlen(parsed_info->host, sizeof(parsed_info->host)) +
1828 strnlen(parsed_info->share, sizeof(parsed_info->share)) +
1829 strnlen(parsed_info->prefix, sizeof(parsed_info->prefix)) +
1831 dev_name = calloc(dev_len, 1);
1837 /* rebuild device name with forward slashes */
1838 strlcpy(dev_name, "//", dev_len);
1839 strlcat(dev_name, parsed_info->host, dev_len);
1840 strlcat(dev_name, "/", dev_len);
1841 strlcat(dev_name, parsed_info->share, dev_len);
1842 strlcat(dev_name, "/", dev_len);
1843 strlcat(dev_name, parsed_info->prefix, dev_len);
1845 currentaddress = parsed_info->addrlist;
1846 nextaddress = strchr(currentaddress, ',');
1848 *nextaddress++ = '\0';
1851 if (!currentaddress) {
1852 fprintf(stderr, "Unable to find suitable address.\n");
1856 strlcpy(options, "ip=", options_size);
1857 strlcat(options, currentaddress, options_size);
1859 strlcat(options, ",unc=\\\\", options_size);
1860 strlcat(options, parsed_info->host, options_size);
1861 strlcat(options, "\\", options_size);
1862 strlcat(options, parsed_info->share, options_size);
1864 if (*parsed_info->options) {
1865 strlcat(options, ",", options_size);
1866 strlcat(options, parsed_info->options, options_size);
1869 if (*parsed_info->prefix) {
1870 strlcat(options, ",prefixpath=", options_size);
1871 strlcat(options, parsed_info->prefix, options_size);
1874 if (parsed_info->verboseflag)
1875 fprintf(stderr, "%s kernel mount options: %s",
1876 thisprogram, options);
1878 if (parsed_info->got_password) {
1880 * Commas have to be doubled, or else they will
1881 * look like the parameter separator
1883 strlcat(options, ",pass=", options_size);
1884 strlcat(options, parsed_info->password, options_size);
1885 if (parsed_info->verboseflag)
1886 fprintf(stderr, ",pass=********");
1889 if (parsed_info->verboseflag)
1890 fprintf(stderr, "\n");
1892 rc = check_mtab(thisprogram, dev_name, mountpoint);
1896 if (strcmp(thisprogram, "mount.smb2") == 0)
1897 fstype = smb2_fstype;
1899 fstype = cifs_fstype;
1901 if (!parsed_info->fakemnt
1902 && mount(dev_name, ".", fstype, parsed_info->flags, options)) {
1906 currentaddress = nextaddress;
1907 if (currentaddress) {
1908 nextaddress = strchr(currentaddress, ',');
1910 *nextaddress++ = '\0';
1915 "mount error: %s filesystem not supported by the system\n", fstype);
1918 if (!already_uppercased &&
1919 uppercase_string(parsed_info->host) &&
1920 uppercase_string(parsed_info->share) &&
1921 uppercase_string(parsed_info->prefix)) {
1923 "Retrying with upper case share name\n");
1924 already_uppercased = 1;
1928 fprintf(stderr, "mount error(%d): %s\n", errno,
1931 "Refer to the %s(8) manual page (e.g. man "
1932 "%s)\n", thisprogram, thisprogram);
1937 if (!parsed_info->nomtab && !mtab_unusable())
1938 rc = add_mtab(orig_dev, mountpoint, parsed_info->flags, fstype);
1942 memset(parsed_info->password, 0, sizeof(parsed_info->password));
1943 munmap(parsed_info, sizeof(*parsed_info));
1945 SAFE_FREE(dev_name);
1947 SAFE_FREE(orgoptions);