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 #ifdef HAVE_SYS_FSUID_H
50 #include <sys/fsuid.h>
51 #endif /* HAVE_SYS_FSUID_H */
54 #else /* HAVE_LIBCAP_NG */
56 #include <sys/prctl.h>
57 #endif /* HAVE_PRCTL */
59 #include <sys/capability.h>
60 #endif /* HAVE_LIBCAP */
61 #endif /* HAVE_LIBCAP_NG */
64 #include "resolve_host.h"
74 /* private flags - clear these before passing to kernel */
75 #define MS_USERS 0x40000000
76 #define MS_USER 0x80000000
78 #define MAX_UNC_LEN 1024
80 /* I believe that the kernel limits options data to a page */
81 #define MAX_OPTIONS_LEN 4096
83 /* max length of mtab options */
84 #define MTAB_OPTIONS_LEN 220
87 * Max share name, username, password and domain sizes match the kernel's
88 * allowances for these string sizes which in turn match Microsoft's
92 /* Max length of the share name portion of a UNC. Share names over 80
93 * characters cannot be accessed via commandline in Windows 2000/XP. */
94 #define MAX_SHARE_LEN 256
96 /* Max user name length. */
97 #define MAX_USERNAME_SIZE 256
99 /* Max domain size. */
100 #define MAX_DOMAIN_SIZE 256
102 /* Max password size. */
103 #define MOUNT_PASSWD_SIZE 512
108 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x = NULL; } } while (0)
113 * mount.cifs has been the subject of many "security" bugs that have arisen
114 * because of users and distributions installing it as a setuid root program
115 * before it had been audited for security holes. The default behavior is
116 * now to allow mount.cifs to be run as a setuid root program. Some admins
117 * may want to disable this fully, so this switch remains in place.
119 #define CIFS_DISABLE_SETUID_CAPABILITY 0
122 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
123 * flags by default. These defaults can be changed here.
125 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
128 * Values for parsing a credentials file.
130 #define CRED_UNPARSEABLE 0
136 * Values for parsing command line options.
142 #define OPT_USER_XATTR 3
151 #define OPT_FILE_MODE 12
153 #define OPT_DIR_MODE 14
155 #define OPT_NO_SUID 16
157 #define OPT_NO_DEV 18
159 #define OPT_NO_LOCK 20
160 #define OPT_NO_EXEC 21
165 #define OPT_REMOUNT 26
167 #define OPT_NOMAND 28
169 #define OPT_BKUPUID 30
170 #define OPT_BKUPGID 31
171 #define OPT_NOFAIL 32
173 #define MNT_TMP_FILE "/.mtab.cifs.XXXXXX"
175 /* struct for holding parsed mount info for use by privleged process */
176 struct parsed_mount_info {
178 char host[NI_MAXHOST + 1];
179 char share[MAX_SHARE_LEN + 1];
180 char prefix[PATH_MAX + 1];
181 char options[MAX_OPTIONS_LEN];
182 char domain[MAX_DOMAIN_SIZE + 1];
183 char username[MAX_USERNAME_SIZE + 1];
184 char password[MOUNT_PASSWD_SIZE + 1];
185 char addrlist[MAX_ADDR_LIST_LEN];
186 unsigned int got_user:1;
187 unsigned int got_password:1;
188 unsigned int fakemnt:1;
189 unsigned int nomtab:1;
190 unsigned int verboseflag:1;
191 unsigned int nofail:1;
194 static const char *thisprogram;
195 static const char *cifs_fstype = "cifs";
197 static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info);
199 static int check_setuid(void)
202 fprintf(stderr, "This program is not installed setuid root - "
203 " \"user\" CIFS mounts not supported.\n");
207 #if CIFS_DISABLE_SETUID_CAPABILITY
208 if (getuid() && !geteuid()) {
209 printf("This mount.cifs program has been built with the "
210 "ability to run as a setuid root program disabled.\n");
213 #endif /* CIFS_DISABLE_SETUID_CAPABILITY */
219 check_fstab(const char *progname, const char *mountpoint, const char *devname,
225 /* make sure this mount is listed in /etc/fstab */
226 fstab = setmntent(_PATH_MNTTAB, "r");
228 fprintf(stderr, "Couldn't open %s for reading!\n", _PATH_MNTTAB);
232 while ((mnt = getmntent(fstab))) {
233 if (!strcmp(mountpoint, mnt->mnt_dir))
238 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
239 fprintf(stderr, "%s: permission denied: no match for "
240 "%s found in %s\n", progname, mountpoint, _PATH_MNTTAB);
245 * 'mount' munges the options from fstab before passing them
246 * to us. It is non-trivial to test that we have the correct
247 * set of options. We don't want to trust what the user
248 * gave us, so just take whatever is in /etc/fstab.
251 *options = strdup(mnt->mnt_opts);
258 open nofollow - avoid symlink exposure?
259 get owner of dir see if matches self or if root
260 call system(umount argv) etc.
264 static int mount_usage(FILE * stream)
266 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n",
268 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
269 fprintf(stream, " to a local directory.\n\nOptions:\n");
270 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
271 fprintf(stream, "\nLess commonly used options:");
273 "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
275 "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
277 "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
279 "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign,fsc");
281 "\n\nOptions not needed for servers supporting CIFS Unix extensions");
283 "\n\t(e.g. unneeded for mounts to most Samba versions):");
285 "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
286 fprintf(stream, "\n\nRarely used options:");
288 "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
290 "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
292 "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
294 "\n\nOptions are described in more detail in the manual page");
295 fprintf(stream, "\n\tman 8 mount.cifs\n");
296 fprintf(stream, "\nTo display the version number of the mount helper:");
297 fprintf(stream, "\n\t%s -V\n", thisprogram);
299 if (stream == stderr)
305 * CIFS has to "escape" commas in the password field so that they don't
306 * end up getting confused for option delimiters. Copy password into pw
307 * field, turning any commas into double commas.
309 static int set_password(struct parsed_mount_info *parsed_info, const char *src)
311 char *dst = parsed_info->password;
312 unsigned int i = 0, j = 0;
318 if (j > sizeof(parsed_info->password)) {
319 fprintf(stderr, "Converted password too long!\n");
324 parsed_info->got_password = 1;
328 #ifdef HAVE_LIBCAP_NG
330 drop_capabilities(int parent)
332 capng_setpid(getpid());
333 capng_clear(CAPNG_SELECT_BOTH);
335 if (capng_updatev(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH, CAP_DAC_OVERRIDE, -1)) {
336 fprintf(stderr, "Unable to update capability set.\n");
339 if (capng_update(CAPNG_ADD, CAPNG_PERMITTED|CAPNG_EFFECTIVE, CAP_SYS_ADMIN)) {
340 fprintf(stderr, "Unable to update capability set.\n");
344 if (capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH)) {
345 fprintf(stderr, "Unable to update capability set.\n");
349 if (capng_apply(CAPNG_SELECT_BOTH)) {
350 fprintf(stderr, "Unable to apply new capability set.\n");
357 toggle_dac_capability(int writable, int enable)
359 unsigned int capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
361 if (capng_update(enable ? CAPNG_ADD : CAPNG_DROP, CAPNG_EFFECTIVE, capability)) {
362 fprintf(stderr, "Unable to update capability set.\n");
365 if (capng_apply(CAPNG_SELECT_CAPS)) {
366 fprintf(stderr, "Unable to apply new capability set.\n");
371 #else /* HAVE_LIBCAP_NG */
375 prune_bounding_set(void)
378 static int bounding_set_cleared;
380 if (bounding_set_cleared)
383 for (i = 0; i <= CAP_LAST_CAP && rc == 0; ++i)
384 rc = prctl(PR_CAPBSET_DROP, i);
387 fprintf(stderr, "Unable to clear capability bounding set: %d\n", rc);
391 ++bounding_set_cleared;
394 #else /* HAVE_PRCTL */
396 prune_bounding_set(void)
400 #endif /* HAVE_PRCTL */
402 drop_capabilities(int parent)
406 cap_value_t cap_list[3];
408 rc = prune_bounding_set();
412 caps = cap_get_proc();
414 fprintf(stderr, "Unable to get current capability set: %s\n",
419 if (cap_clear(caps) == -1) {
420 fprintf(stderr, "Unable to clear capability set: %s\n",
426 if (parent || getuid() == 0) {
428 cap_list[0] = CAP_DAC_READ_SEARCH;
430 cap_list[1] = CAP_DAC_OVERRIDE;
431 cap_list[2] = CAP_SYS_ADMIN;
434 if (cap_set_flag(caps, CAP_PERMITTED, ncaps, cap_list, CAP_SET) == -1) {
435 fprintf(stderr, "Unable to set permitted capabilities: %s\n",
441 cap_list[0] = CAP_SYS_ADMIN;
442 if (cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET) == -1) {
443 fprintf(stderr, "Unable to set effective capabilities: %s\n",
451 if (cap_set_proc(caps) != 0) {
452 fprintf(stderr, "Unable to set current process capabilities: %s\n",
462 toggle_dac_capability(int writable, int enable)
466 cap_value_t capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
468 caps = cap_get_proc();
470 fprintf(stderr, "Unable to get current capability set: %s\n",
475 if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &capability,
476 enable ? CAP_SET : CAP_CLEAR) == -1) {
477 fprintf(stderr, "Unable to %s effective capabilities: %s\n",
478 enable ? "set" : "clear", strerror(errno));
483 if (cap_set_proc(caps) != 0) {
484 fprintf(stderr, "Unable to set current process capabilities: %s\n",
492 #else /* HAVE_LIBCAP */
494 drop_capabilities(int parent __attribute((unused)))
500 toggle_dac_capability(int writable __attribute((unused)), int enable __attribute((unused)))
504 #endif /* HAVE_LIBCAP */
505 #endif /* HAVE_LIBCAP_NG */
507 static void null_terminate_endl(char *source)
509 char *newline = strchr(source, '\n');
515 * Parse a line from the credentials file. Changes target to first
516 * character after '=' on 'line' and returns the value type of the line
517 * Returns CRED_UNPARSEABLE on failure or if either parameter is NULL.
519 static int parse_cred_line(char *line, char **target)
521 if (line == NULL || target == NULL)
524 /* position target at first char of value */
525 *target = strchr(line, '=');
530 /* tell the caller which value target points to */
531 if (strncasecmp("user", line, 4) == 0)
533 if (strncasecmp("pass", line, 4) == 0)
535 if (strncasecmp("dom", line, 3) == 0)
539 return CRED_UNPARSEABLE;
542 static int open_cred_file(char *file_name,
543 struct parsed_mount_info *parsed_info)
545 char *line_buf = NULL;
546 char *temp_val = NULL;
549 const int line_buf_size = 4096;
550 const int min_non_white = 10;
552 i = toggle_dac_capability(0, 1);
556 i = access(file_name, R_OK);
558 toggle_dac_capability(0, 0);
563 fs = fopen(file_name, "r");
565 toggle_dac_capability(0, 0);
570 i = toggle_dac_capability(0, 0);
574 line_buf = (char *)malloc(line_buf_size);
575 if (line_buf == NULL) {
580 /* parse line from credentials file */
581 while (fgets(line_buf, line_buf_size, fs)) {
582 /* eat leading white space */
583 for (i = 0; i < line_buf_size - min_non_white + 1; i++) {
584 if ((line_buf[i] != ' ') && (line_buf[i] != '\t'))
587 null_terminate_endl(line_buf);
589 /* parse next token */
590 switch (parse_cred_line(line_buf + i, &temp_val)) {
592 strlcpy(parsed_info->username, temp_val,
593 sizeof(parsed_info->username));
594 parsed_info->got_user = 1;
597 i = set_password(parsed_info, temp_val);
602 if (parsed_info->verboseflag)
603 fprintf(stderr, "domain=%s\n",
605 strlcpy(parsed_info->domain, temp_val,
606 sizeof(parsed_info->domain));
608 case CRED_UNPARSEABLE:
609 if (parsed_info->verboseflag)
610 fprintf(stderr, "Credential formatted "
612 temp_val ? temp_val : "(null)");
621 /* make sure passwords are scrubbed from memory */
622 if (line_buf != NULL)
623 memset(line_buf, 0, line_buf_size);
629 get_password_from_file(int file_descript, char *filename,
630 struct parsed_mount_info *parsed_info)
633 char buf[sizeof(parsed_info->password) + 1];
635 if (filename != NULL) {
636 rc = toggle_dac_capability(0, 1);
640 rc = access(filename, R_OK);
643 "mount.cifs failed: access check of %s failed: %s\n",
644 filename, strerror(errno));
645 toggle_dac_capability(0, 0);
649 file_descript = open(filename, O_RDONLY);
650 if (file_descript < 0) {
652 "mount.cifs failed. %s attempting to open password file %s\n",
653 strerror(errno), filename);
654 toggle_dac_capability(0, 0);
658 rc = toggle_dac_capability(0, 0);
665 memset(buf, 0, sizeof(buf));
666 rc = read(file_descript, buf, sizeof(buf) - 1);
669 "mount.cifs failed. Error %s reading password file\n",
675 rc = set_password(parsed_info, buf);
678 if (filename != NULL)
679 close(file_descript);
684 * Returns OPT_ERROR on unparsable token.
686 static int parse_opt_token(const char *token)
691 if (strncmp(token, "users", 5) == 0)
693 if (strncmp(token, "user_xattr", 10) == 0)
694 return OPT_USER_XATTR;
695 if (strncmp(token, "user", 4) == 0)
697 if (strncmp(token, "pass", 4) == 0)
699 if (strncmp(token, "sec", 3) == 0)
701 if (strncmp(token, "ip", 2) == 0)
703 if (strncmp(token, "unc", 3) == 0 ||
704 strncmp(token, "target", 6) == 0 ||
705 strncmp(token, "path", 4) == 0)
707 if (strncmp(token, "dom", 3) == 0 || strncmp(token, "workg", 5) == 0)
709 if (strncmp(token, "cred", 4) == 0)
711 if (strncmp(token, "uid", 3) == 0)
713 if (strncmp(token, "cruid", 5) == 0)
715 if (strncmp(token, "gid", 3) == 0)
717 if (strncmp(token, "fmask", 5) == 0)
719 if (strncmp(token, "file_mode", 9) == 0)
720 return OPT_FILE_MODE;
721 if (strncmp(token, "dmask", 5) == 0)
723 if (strncmp(token, "dir_mode", 4) == 0 || strncmp(token, "dirm", 4) == 0)
725 if (strncmp(token, "nosuid", 6) == 0)
727 if (strncmp(token, "suid", 4) == 0)
729 if (strncmp(token, "nodev", 5) == 0)
731 if (strncmp(token, "nobrl", 5) == 0 || strncmp(token, "nolock", 6) == 0)
733 if (strncmp(token, "mand", 4) == 0)
735 if (strncmp(token, "nomand", 6) == 0)
737 if (strncmp(token, "dev", 3) == 0)
739 if (strncmp(token, "noexec", 6) == 0)
741 if (strncmp(token, "exec", 4) == 0)
743 if (strncmp(token, "guest", 5) == 0)
745 if (strncmp(token, "ro", 2) == 0)
747 if (strncmp(token, "rw", 2) == 0 && strlen(token) == 2)
749 if (strncmp(token, "remount", 7) == 0)
751 if (strncmp(token, "_netdev", 7) == 0)
753 if (strncmp(token, "backupuid", 9) == 0)
755 if (strncmp(token, "backupgid", 9) == 0)
757 if (strncmp(token, "nofail", 6) == 0)
764 parse_options(const char *data, struct parsed_mount_info *parsed_info)
768 char *next_keyword = NULL;
769 char *out = parsed_info->options;
770 unsigned long *filesys_flags = &parsed_info->flags;
779 uid_t uid, cruid = 0, bkupuid = 0;
780 gid_t gid, bkupgid = 0;
785 * max 32-bit uint in decimal is 4294967295 which is 10 chars wide
786 * +1 for NULL, and +1 for good measure
790 /* make sure we're starting from beginning */
793 /* BB fixme check for separator override BB */
806 * format is keyword,keyword2=value2,keyword3=value3...
807 * data = next keyword
808 * value = next value ie stuff after equal sign
810 while (data && *data) {
811 next_keyword = strchr(data, ','); /* BB handle sep= */
813 /* temporarily null terminate end of keyword=value pair */
817 /* temporarily null terminate keyword if there's a value */
819 if ((equals = strchr(data, '=')) != NULL) {
824 switch(parse_opt_token(data)) {
826 if (!value || !*value) {
827 *filesys_flags |= MS_USERS;
833 if (!value || !*value) {
834 if (data[4] == '\0') {
835 *filesys_flags |= MS_USER;
839 "username specified with no parameter\n");
843 strlcpy(parsed_info->username, value,
844 sizeof(parsed_info->username));
845 parsed_info->got_user = 1;
850 if (parsed_info->got_password) {
852 "password specified twice, ignoring second\n");
855 if (!value || !*value) {
856 parsed_info->got_password = 1;
859 rc = set_password(parsed_info, value);
866 if (!strncmp(value, "none", 4) ||
867 !strncmp(value, "krb5", 4))
868 parsed_info->got_password = 1;
873 if (!value || !*value) {
875 "target ip address argument missing\n");
876 } else if (strnlen(value, MAX_ADDRESS_LEN) <=
878 strcpy(parsed_info->addrlist, value);
879 if (parsed_info->verboseflag)
881 "ip address %s override specified\n",
885 fprintf(stderr, "ip address too long\n");
891 /* unc || target || path */
893 if (!value || !*value) {
895 "invalid path to network resource\n");
898 rc = parse_unc(value, parsed_info);
903 /* dom || workgroup */
905 if (!value || !*value) {
906 fprintf(stderr, "CIFS: invalid domain name\n");
909 if (strnlen(value, sizeof(parsed_info->domain)) >=
910 sizeof(parsed_info->domain)) {
911 fprintf(stderr, "domain name too long\n");
914 strlcpy(parsed_info->domain, value,
915 sizeof(parsed_info->domain));
919 if (!value || !*value) {
921 "invalid credential file name specified\n");
924 rc = open_cred_file(value, parsed_info);
927 "error %d (%s) opening credential file %s\n",
928 rc, strerror(rc), value);
934 if (!value || !*value)
938 pw = getpwnam(value);
945 uid = strtoul(value, &ep, 10);
946 if (errno == 0 && *ep == '\0')
949 fprintf(stderr, "bad option uid=\"%s\"\n", value);
952 if (!value || !*value)
956 pw = getpwnam(value);
963 cruid = strtoul(value, &ep, 10);
964 if (errno == 0 && *ep == '\0')
967 fprintf(stderr, "bad option: cruid=\"%s\"\n", value);
970 if (!value || !*value)
974 gr = getgrnam(value);
981 gid = strtoul(value, &ep, 10);
982 if (errno == 0 && *ep == '\0')
985 fprintf(stderr, "bad option: gid=\"%s\"\n", value);
987 /* fmask falls through to file_mode */
990 "WARNING: CIFS mount option 'fmask' is\
991 deprecated. Use 'file_mode' instead.\n");
992 data = "file_mode"; /* BB fix this */
994 if (!value || !*value) {
996 "Option '%s' requires a numerical argument\n",
1001 if (value[0] != '0')
1003 "WARNING: '%s' not expressed in octal.\n",
1007 /* dmask falls through to dir_mode */
1010 "WARNING: CIFS mount option 'dmask' is\
1011 deprecated. Use 'dir_mode' instead.\n");
1014 if (!value || !*value) {
1016 "Option '%s' requires a numerical argument\n",
1021 if (value[0] != '0')
1023 "WARNING: '%s' not expressed in octal.\n",
1027 *filesys_flags |= MS_NOSUID;
1030 *filesys_flags &= ~MS_NOSUID;
1033 *filesys_flags |= MS_NODEV;
1036 *filesys_flags &= ~MS_MANDLOCK;
1039 *filesys_flags |= MS_MANDLOCK;
1042 *filesys_flags &= ~MS_MANDLOCK;
1045 *filesys_flags &= ~MS_NODEV;
1048 *filesys_flags |= MS_NOEXEC;
1051 *filesys_flags &= ~MS_NOEXEC;
1054 parsed_info->got_user = 1;
1055 parsed_info->got_password = 1;
1058 *filesys_flags |= MS_RDONLY;
1061 *filesys_flags &= ~MS_RDONLY;
1064 *filesys_flags |= MS_REMOUNT;
1069 if (!value || !*value)
1074 bkupuid = strtoul(value, &ep, 10);
1075 if (errno == 0 && *ep == '\0')
1078 pw = getpwnam(value);
1081 "bad user name \"%s\"\n", value);
1085 bkupuid = pw->pw_uid;
1088 if (!value || !*value)
1093 bkupgid = strtoul(value, &ep, 10);
1094 if (errno == 0 && *ep == '\0')
1097 gr = getgrnam(value);
1100 "bad group name \"%s\"\n", value);
1104 bkupgid = gr->gr_gid;
1107 parsed_info->nofail = 1;
1111 /* check size before copying option to buffer */
1112 word_len = strlen(data);
1114 word_len += 1 + strlen(value);
1116 /* need 2 extra bytes for comma and null byte */
1117 if (out_len + word_len + 2 > MAX_OPTIONS_LEN) {
1118 fprintf(stderr, "Options string too long\n");
1122 /* put back equals sign, if any */
1126 /* go ahead and copy */
1128 strlcat(out, ",", MAX_OPTIONS_LEN);
1130 strlcat(out, data, MAX_OPTIONS_LEN);
1131 out_len = strlen(out);
1133 data = next_keyword;
1137 /* special-case the uid and gid */
1139 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", uid);
1141 /* comma + "uid=" + terminating NULL == 6 */
1142 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1143 fprintf(stderr, "Options string too long\n");
1148 strlcat(out, ",", MAX_OPTIONS_LEN);
1151 snprintf(out + out_len, word_len + 5, "uid=%s", txtbuf);
1152 out_len = strlen(out);
1155 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", cruid);
1157 /* comma + "cruid=" + terminating NULL == 8 */
1158 if (out_len + word_len + 8 > MAX_OPTIONS_LEN) {
1159 fprintf(stderr, "Options string too long\n");
1164 strlcat(out, ",", MAX_OPTIONS_LEN);
1167 snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf);
1168 out_len = strlen(out);
1171 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", gid);
1173 /* comma + "gid=" + terminating NULL == 6 */
1174 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1175 fprintf(stderr, "Options string too long\n");
1180 strlcat(out, ",", MAX_OPTIONS_LEN);
1183 snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf);
1186 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupuid);
1188 /* comma + "backupuid=" + terminating NULL == 12 */
1189 if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
1190 fprintf(stderr, "Options string too long\n");
1195 strlcat(out, ",", MAX_OPTIONS_LEN);
1198 snprintf(out + out_len, word_len + 11, "backupuid=%s", txtbuf);
1199 out_len = strlen(out);
1202 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupgid);
1204 /* comma + "backkupgid=" + terminating NULL == 12 */
1205 if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
1206 fprintf(stderr, "Options string too long\n");
1211 strlcat(out, ",", MAX_OPTIONS_LEN);
1214 snprintf(out + out_len, word_len + 11, "backupgid=%s", txtbuf);
1220 static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info)
1222 int length = strnlen(unc_name, MAX_UNC_LEN);
1223 const char *host, *share, *prepath;
1224 size_t hostlen, sharelen, prepathlen;
1226 if (length > (MAX_UNC_LEN - 1)) {
1227 fprintf(stderr, "mount error: UNC name too long\n");
1232 fprintf(stderr, "mount error: UNC name too short\n");
1236 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1237 (strncasecmp("smb://", unc_name, 6) == 0)) {
1239 "Mounting cifs URL not implemented yet. Attempt to mount %s\n",
1244 if (strncmp(unc_name, "//", 2) && strncmp(unc_name, "\\\\", 2)) {
1245 fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1249 host = unc_name + 2;
1250 hostlen = strcspn(host, "/\\");
1252 fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1255 share = host + hostlen + 1;
1257 if (hostlen + 1 > sizeof(parsed_info->host)) {
1258 fprintf(stderr, "mount.cifs: host portion of UNC too long\n");
1262 sharelen = strcspn(share, "/\\");
1263 if (sharelen + 1 > sizeof(parsed_info->share)) {
1264 fprintf(stderr, "mount.cifs: share portion of UNC too long\n");
1268 prepath = share + sharelen;
1269 if (*prepath != '\0')
1272 prepathlen = strlen(prepath);
1274 if (prepathlen + 1 > sizeof(parsed_info->prefix)) {
1275 fprintf(stderr, "mount.cifs: UNC prefixpath too long\n");
1279 /* copy pieces into their resepective buffers */
1280 memcpy(parsed_info->host, host, hostlen);
1281 memcpy(parsed_info->share, share, sharelen);
1282 memcpy(parsed_info->prefix, prepath, prepathlen);
1287 static int get_pw_from_env(struct parsed_mount_info *parsed_info)
1291 if (getenv("PASSWD"))
1292 rc = set_password(parsed_info, getenv("PASSWD"));
1293 else if (getenv("PASSWD_FD"))
1294 rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL,
1296 else if (getenv("PASSWD_FILE"))
1297 rc = get_password_from_file(0, getenv("PASSWD_FILE"),
1303 static struct option longopts[] = {
1304 {"all", 0, NULL, 'a'},
1305 {"help", 0, NULL, 'h'},
1306 {"move", 0, NULL, 'm'},
1307 {"bind", 0, NULL, 'b'},
1308 {"read-only", 0, NULL, 'r'},
1309 {"ro", 0, NULL, 'r'},
1310 {"verbose", 0, NULL, 'v'},
1311 {"version", 0, NULL, 'V'},
1312 {"read-write", 0, NULL, 'w'},
1313 {"rw", 0, NULL, 'w'},
1314 {"options", 1, NULL, 'o'},
1315 {"type", 1, NULL, 't'},
1316 {"uid", 1, NULL, '1'},
1317 {"gid", 1, NULL, '2'},
1318 {"user", 1, NULL, 'u'},
1319 {"username", 1, NULL, 'u'},
1320 {"dom", 1, NULL, 'd'},
1321 {"domain", 1, NULL, 'd'},
1322 {"password", 1, NULL, 'p'},
1323 {"pass", 1, NULL, 'p'},
1324 {"credentials", 1, NULL, 'c'},
1325 {"port", 1, NULL, 'P'},
1326 {"sloppy", 0, NULL, 's'},
1330 /* convert a string to uppercase. return false if the string
1331 * wasn't ASCII. Return success on a NULL ptr */
1332 static int uppercase_string(char *string)
1338 /* check for unicode */
1339 if ((unsigned char)string[0] & 0x80)
1341 *string = toupper((unsigned char)*string);
1348 static void print_cifs_mount_version(void)
1350 printf("mount.cifs version: %s\n", VERSION);
1354 * This function borrowed from fuse-utils...
1356 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1357 * newlines embedded within the text fields. To make sure no one corrupts
1358 * the mtab, fail the mount if there are embedded newlines.
1360 static int check_newline(const char *progname, const char *name)
1363 for (s = "\n"; *s; s++) {
1364 if (strchr(name, *s)) {
1366 "%s: illegal character 0x%02x in mount entry\n",
1374 static int check_mtab(const char *progname, const char *devname,
1377 if (check_newline(progname, devname) || check_newline(progname, dir))
1383 add_mtab(char *devname, char *mountpoint, unsigned long flags, const char *fstype)
1385 int rc = 0, tmprc, fd;
1387 char *mount_user = NULL;
1388 struct mntent mountent;
1389 struct stat statbuf;
1391 sigset_t mask, oldmask;
1395 mount_user = getusername(uid);
1398 * Set the real uid to the effective uid. This prevents unprivileged
1399 * users from sending signals to this process, though ^c on controlling
1400 * terminal should still work.
1402 rc = setreuid(geteuid(), -1);
1404 fprintf(stderr, "Unable to set real uid to effective uid: %s\n",
1409 rc = sigfillset(&mask);
1411 fprintf(stderr, "Unable to set filled signal mask\n");
1415 rc = sigprocmask(SIG_SETMASK, &mask, &oldmask);
1417 fprintf(stderr, "Unable to make process ignore signals\n");
1421 rc = toggle_dac_capability(1, 1);
1425 atexit(unlock_mtab);
1428 fprintf(stderr, "cannot lock mtab");
1433 pmntfile = setmntent(MOUNTED, "a+");
1435 fprintf(stderr, "could not update mount table\n");
1441 fd = fileno(pmntfile);
1443 fprintf(stderr, "mntent does not appear to be valid\n");
1449 rc = fstat(fd, &statbuf);
1451 fprintf(stderr, "unable to fstat open mtab\n");
1452 endmntent(pmntfile);
1458 mountent.mnt_fsname = devname;
1459 mountent.mnt_dir = mountpoint;
1460 mountent.mnt_type = (char *)(void *)fstype;
1461 mountent.mnt_opts = (char *)calloc(MTAB_OPTIONS_LEN, 1);
1462 if (mountent.mnt_opts) {
1463 if (flags & MS_RDONLY)
1464 strlcat(mountent.mnt_opts, "ro", MTAB_OPTIONS_LEN);
1466 strlcat(mountent.mnt_opts, "rw", MTAB_OPTIONS_LEN);
1468 if (flags & MS_MANDLOCK)
1469 strlcat(mountent.mnt_opts, ",mand", MTAB_OPTIONS_LEN);
1470 if (flags & MS_NOEXEC)
1471 strlcat(mountent.mnt_opts, ",noexec", MTAB_OPTIONS_LEN);
1472 if (flags & MS_NOSUID)
1473 strlcat(mountent.mnt_opts, ",nosuid", MTAB_OPTIONS_LEN);
1474 if (flags & MS_NODEV)
1475 strlcat(mountent.mnt_opts, ",nodev", MTAB_OPTIONS_LEN);
1476 if (flags & MS_SYNCHRONOUS)
1477 strlcat(mountent.mnt_opts, ",sync", MTAB_OPTIONS_LEN);
1479 strlcat(mountent.mnt_opts, ",user=", MTAB_OPTIONS_LEN);
1480 strlcat(mountent.mnt_opts, mount_user,
1484 mountent.mnt_freq = 0;
1485 mountent.mnt_passno = 0;
1486 rc = addmntent(pmntfile, &mountent);
1488 int ignore __attribute__((unused));
1490 fprintf(stderr, "unable to add mount entry to mtab\n");
1491 ignore = ftruncate(fd, statbuf.st_size);
1494 tmprc = my_endmntent(pmntfile, statbuf.st_size);
1496 fprintf(stderr, "error %d detected on close of mtab\n", tmprc);
1500 SAFE_FREE(mountent.mnt_opts);
1502 toggle_dac_capability(1, 0);
1503 sigprocmask(SIG_SETMASK, &oldmask, NULL);
1509 del_mtab(char *mountpoint)
1512 FILE *mnttmp, *mntmtab;
1513 struct mntent *mountent;
1514 char *mtabfile, *mtabdir, *mtabtmpfile;
1516 mtabfile = strdup(MOUNTED);
1517 mtabdir = dirname(mtabfile);
1518 mtabdir = realloc(mtabdir, strlen(mtabdir) + strlen(MNT_TMP_FILE) + 2);
1520 fprintf(stderr, "del_mtab: cannot determine current mtab path");
1525 mtabtmpfile = strcat(mtabdir, MNT_TMP_FILE);
1527 fprintf(stderr, "del_mtab: cannot allocate memory to tmp file");
1532 atexit(unlock_mtab);
1535 fprintf(stderr, "del_mtab: cannot lock mtab");
1540 mtabtmpfile = mktemp(mtabtmpfile);
1542 fprintf(stderr, "del_mtab: cannot setup tmp file destination");
1547 mntmtab = setmntent(MOUNTED, "r");
1549 fprintf(stderr, "del_mtab: could not update mount table\n");
1554 mnttmp = setmntent(mtabtmpfile, "w");
1556 fprintf(stderr, "del_mtab: could not update mount table\n");
1562 while ((mountent = getmntent(mntmtab)) != NULL) {
1563 if (!strcmp(mountent->mnt_dir, mountpoint))
1565 rc = addmntent(mnttmp, mountent);
1567 fprintf(stderr, "del_mtab: unable to add mount entry to mtab\n");
1569 goto del_mtab_error;
1575 tmprc = my_endmntent(mnttmp, 0);
1577 fprintf(stderr, "del_mtab: error %d detected on close of tmp file\n", tmprc);
1579 goto del_mtab_error;
1582 if (rename(mtabtmpfile, MOUNTED)) {
1583 fprintf(stderr, "del_mtab: error %d when renaming mtab in place\n", errno);
1585 goto del_mtab_error;
1594 if (unlink(mtabtmpfile))
1595 fprintf(stderr, "del_mtab: failed to delete tmp file - %s\n",
1600 /* have the child drop root privileges */
1602 drop_child_privs(void)
1605 uid_t uid = getuid();
1606 gid_t gid = getgid();
1611 fprintf(stderr, "Unable set group identity: %s\n",
1619 fprintf(stderr, "Unable set user identity: %s\n",
1629 * If systemd is running and /bin/systemd-ask-password --
1630 * is available, then use that else fallback on getpass(..)
1632 * Returns: @input or NULL on error
1635 get_password(const char *prompt, char *input, int capacity)
1637 #ifdef ENABLE_SYSTEMD
1638 int is_systemd_running;
1641 /* We simply test whether the systemd cgroup hierarchy is
1643 is_systemd_running = (lstat("/sys/fs/cgroup", &a) == 0)
1644 && (lstat("/sys/fs/cgroup/systemd", &b) == 0)
1645 && (a.st_dev != b.st_dev);
1647 if (is_systemd_running) {
1649 FILE *ask_pass_fp = NULL;
1652 if (asprintf(&cmd, "/bin/systemd-ask-password \"%s\"", prompt) >= 0) {
1653 ask_pass_fp = popen (cmd, "re");
1658 ret = fgets(input, capacity, ask_pass_fp);
1659 pclose(ask_pass_fp);
1663 int len = strlen(input);
1664 if (input[len - 1] == '\n')
1665 input[len - 1] = '\0';
1672 * Falling back to getpass(..)
1673 * getpass is obsolete, but there's apparently nothing that replaces it
1675 char *tmp_pass = getpass(prompt);
1679 strncpy(input, tmp_pass, capacity - 1);
1680 input[capacity - 1] = '\0';
1682 /* zero-out the static buffer */
1683 memset(tmp_pass, 0, strlen(tmp_pass));
1689 assemble_mountinfo(struct parsed_mount_info *parsed_info,
1690 const char *thisprogram, const char *mountpoint,
1691 const char *orig_dev, char *orgoptions)
1695 rc = drop_capabilities(0);
1699 rc = drop_child_privs();
1704 rc = check_fstab(thisprogram, mountpoint, orig_dev,
1709 /* enable any default user mount flags */
1710 parsed_info->flags |= CIFS_SETUID_FLAGS;
1713 rc = get_pw_from_env(parsed_info);
1718 rc = parse_options(orgoptions, parsed_info);
1724 if (!(parsed_info->flags & (MS_USERS | MS_USER))) {
1725 fprintf(stderr, "%s: permission denied\n", thisprogram);
1731 parsed_info->flags &= ~(MS_USERS | MS_USER);
1733 rc = parse_unc(orig_dev, parsed_info);
1737 if (parsed_info->addrlist[0] == '\0')
1738 rc = resolve_host(parsed_info->host, parsed_info->addrlist);
1742 fprintf(stderr, "mount error: could not resolve address for "
1743 "%s: %s\n", parsed_info->host,
1744 rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
1748 fprintf(stderr, "mount error: problem parsing address "
1749 "list: %s\n", strerror(errno));
1753 if (!parsed_info->got_user) {
1755 * Note that the password will not be retrieved from the
1756 * USER env variable (ie user%password form) as there is
1757 * already a PASSWD environment varaible
1760 strlcpy(parsed_info->username, getenv("USER"),
1761 sizeof(parsed_info->username));
1763 strlcpy(parsed_info->username, getusername(getuid()),
1764 sizeof(parsed_info->username));
1765 parsed_info->got_user = 1;
1768 if (!parsed_info->got_password) {
1769 char tmp_pass[MOUNT_PASSWD_SIZE + 1];
1770 char *prompt = NULL;
1772 if(asprintf(&prompt, "Password for %s@%s: ", parsed_info->username, orig_dev) < 0)
1775 if (get_password(prompt ? prompt : "Password: ", tmp_pass, MOUNT_PASSWD_SIZE + 1)) {
1776 rc = set_password(parsed_info, tmp_pass);
1778 fprintf(stderr, "Error reading password, exiting\n");
1787 /* copy in user= string */
1788 if (parsed_info->got_user) {
1789 if (*parsed_info->options)
1790 strlcat(parsed_info->options, ",",
1791 sizeof(parsed_info->options));
1792 strlcat(parsed_info->options, "user=",
1793 sizeof(parsed_info->options));
1794 strlcat(parsed_info->options, parsed_info->username,
1795 sizeof(parsed_info->options));
1798 if (*parsed_info->domain) {
1799 if (*parsed_info->options)
1800 strlcat(parsed_info->options, ",",
1801 sizeof(parsed_info->options));
1802 strlcat(parsed_info->options, ",domain=",
1803 sizeof(parsed_info->options));
1804 strlcat(parsed_info->options, parsed_info->domain,
1805 sizeof(parsed_info->options));
1813 * chdir() into the mountpoint and determine "realpath". We assume here that
1814 * "mountpoint" is a statically allocated string and does not need to be freed.
1817 acquire_mountpoint(char **mountpointp)
1820 uid_t realuid, oldfsuid;
1825 * Acquire the necessary privileges to chdir to the mountpoint. If
1826 * the real uid is root, then we reacquire CAP_DAC_READ_SEARCH. If
1827 * it's not, then we change the fsuid to the real uid to ensure that
1828 * the mounting user actually has access to the mountpoint.
1830 * The mount(8) manpage does not state that users must be able to
1831 * chdir into the mountpoint in order to mount onto it, but if we
1832 * allow that, then an unprivileged user could use this program to
1833 * "probe" into directories to which he does not have access.
1837 dacrc = toggle_dac_capability(0, 1);
1841 oldfsuid = setfsuid(realuid);
1842 oldfsgid = setfsgid(getgid());
1845 rc = chdir(*mountpointp);
1847 fprintf(stderr, "Couldn't chdir to %s: %s\n", *mountpointp,
1853 mountpoint = realpath(".", NULL);
1855 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1856 *mountpointp, strerror(errno));
1860 *mountpointp = mountpoint;
1863 dacrc = toggle_dac_capability(0, 0);
1865 rc = rc ? rc : dacrc;
1867 uid_t __attribute__((unused)) uignore = setfsuid(oldfsuid);
1868 gid_t __attribute__((unused)) gignore = setfsgid(oldfsgid);
1874 int main(int argc, char **argv)
1877 char *orgoptions = NULL;
1878 char *mountpoint = NULL;
1879 char *options = NULL;
1880 char *orig_dev = NULL;
1881 char *currentaddress, *nextaddress;
1883 int already_uppercased = 0;
1885 size_t options_size = MAX_OPTIONS_LEN;
1886 struct parsed_mount_info *parsed_info = NULL;
1889 rc = check_setuid();
1893 rc = drop_capabilities(1);
1897 /* setlocale(LC_ALL, "");
1898 bindtextdomain(PACKAGE, LOCALEDIR);
1899 textdomain(PACKAGE); */
1901 if (!argc || !argv) {
1902 rc = mount_usage(stderr);
1906 thisprogram = basename(argv[0]);
1907 if (thisprogram == NULL)
1908 thisprogram = "mount.cifs";
1910 /* allocate parsed_info as shared anonymous memory range */
1911 parsed_info = mmap((void *)0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
1912 MAP_ANONYMOUS | MAP_SHARED, -1, 0);
1913 if (parsed_info == (struct parsed_mount_info *) -1) {
1915 fprintf(stderr, "Unable to allocate memory: %s\n",
1920 /* add sharename in opts string as unc= parm */
1921 while ((c = getopt_long(argc, argv, "?fhno:rsvVw",
1922 longopts, NULL)) != -1) {
1925 case 'h': /* help */
1926 rc = mount_usage(stdout);
1929 ++parsed_info->nomtab;
1932 orgoptions = strndup(optarg, MAX_OPTIONS_LEN);
1938 case 'r': /* mount readonly */
1939 parsed_info->flags |= MS_RDONLY;
1942 ++parsed_info->verboseflag;
1945 print_cifs_mount_version();
1948 parsed_info->flags &= ~MS_RDONLY;
1951 ++parsed_info->fakemnt;
1957 fprintf(stderr, "unknown command-line option: %c\n", c);
1958 rc = mount_usage(stderr);
1963 if (argc < optind + 2) {
1964 rc = mount_usage(stderr);
1968 orig_dev = argv[optind];
1969 mountpoint = argv[optind + 1];
1971 /* chdir into mountpoint as soon as possible */
1972 rc = acquire_mountpoint(&mountpoint);
1977 * mount.cifs does privilege separation. Most of the code to handle
1978 * assembling the mount info is done in a child process that drops
1979 * privileges. The info is assembled in parsed_info which is a
1980 * shared, mmaped memory segment. The parent waits for the child to
1981 * exit and checks the return code. If it's anything but "0", then
1982 * the process exits without attempting anything further.
1986 fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
1991 rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
1992 orig_dev, orgoptions);
1997 if (!WIFEXITED(rc)) {
1998 fprintf(stderr, "Child process terminated abnormally.\n");
2002 rc = WEXITSTATUS(rc);
2007 options = calloc(options_size, 1);
2009 fprintf(stderr, "Unable to allocate memory.\n");
2014 currentaddress = parsed_info->addrlist;
2015 nextaddress = strchr(currentaddress, ',');
2017 *nextaddress++ = '\0';
2020 if (!currentaddress) {
2021 fprintf(stderr, "Unable to find suitable address.\n");
2022 rc = parsed_info->nofail ? 0 : EX_FAIL;
2025 strlcpy(options, "ip=", options_size);
2026 strlcat(options, currentaddress, options_size);
2028 strlcat(options, ",unc=\\\\", options_size);
2029 strlcat(options, parsed_info->host, options_size);
2030 strlcat(options, "\\", options_size);
2031 strlcat(options, parsed_info->share, options_size);
2033 if (*parsed_info->options) {
2034 strlcat(options, ",", options_size);
2035 strlcat(options, parsed_info->options, options_size);
2038 if (*parsed_info->prefix) {
2039 strlcat(options, ",prefixpath=", options_size);
2040 strlcat(options, parsed_info->prefix, options_size);
2044 strlcat(options, ",sloppy", options_size);
2046 if (parsed_info->verboseflag)
2047 fprintf(stderr, "%s kernel mount options: %s",
2048 thisprogram, options);
2050 if (parsed_info->got_password) {
2052 * Commas have to be doubled, or else they will
2053 * look like the parameter separator
2055 strlcat(options, ",pass=", options_size);
2056 strlcat(options, parsed_info->password, options_size);
2057 if (parsed_info->verboseflag)
2058 fprintf(stderr, ",pass=********");
2061 if (parsed_info->verboseflag)
2062 fprintf(stderr, "\n");
2064 rc = check_mtab(thisprogram, orig_dev, mountpoint);
2068 if (!parsed_info->fakemnt) {
2069 toggle_dac_capability(0, 1);
2070 rc = mount(orig_dev, ".", cifs_fstype, parsed_info->flags, options);
2071 toggle_dac_capability(0, 0);
2078 currentaddress = nextaddress;
2079 if (currentaddress) {
2080 nextaddress = strchr(currentaddress, ',');
2082 *nextaddress++ = '\0';
2087 "mount error: %s filesystem not supported by the system\n", cifs_fstype);
2090 if (!already_uppercased &&
2091 uppercase_string(parsed_info->host) &&
2092 uppercase_string(parsed_info->share) &&
2093 uppercase_string(parsed_info->prefix)) {
2095 "Retrying with upper case share name\n");
2096 already_uppercased = 1;
2100 fprintf(stderr, "mount error(%d): %s\n", errno,
2103 "Refer to the %s(8) manual page (e.g. man "
2104 "%s)\n", thisprogram, thisprogram);
2110 if (!parsed_info->nomtab && !mtab_unusable()) {
2111 if (parsed_info->flags & MS_REMOUNT) {
2112 rc = del_mtab(mountpoint);
2117 rc = add_mtab(orig_dev, mountpoint, parsed_info->flags, cifs_fstype);
2122 memset(parsed_info->password, 0, sizeof(parsed_info->password));
2123 munmap(parsed_info, sizeof(*parsed_info));
2126 SAFE_FREE(orgoptions);