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 * Maximum length of "share" portion of a UNC. I have no idea if this is at
88 * all valid. According to MSDN, the typical max length of any component is
89 * 255, so use that here.
91 #define MAX_SHARE_LEN 256
93 /* max length of username (somewhat made up here) */
94 #define MAX_USERNAME_SIZE 32
97 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x = NULL; } } while (0)
100 #define MOUNT_PASSWD_SIZE 128
101 #define MAX_DOMAIN_SIZE 64
104 * mount.cifs has been the subject of many "security" bugs that have arisen
105 * because of users and distributions installing it as a setuid root program
106 * before it had been audited for security holes. The default behavior is
107 * now to allow mount.cifs to be run as a setuid root program. Some admins
108 * may want to disable this fully, so this switch remains in place.
110 #define CIFS_DISABLE_SETUID_CAPABILITY 0
113 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
114 * flags by default. These defaults can be changed here.
116 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
119 * Values for parsing a credentials file.
121 #define CRED_UNPARSEABLE 0
127 * Values for parsing command line options.
133 #define OPT_USER_XATTR 3
142 #define OPT_FILE_MODE 12
144 #define OPT_DIR_MODE 14
146 #define OPT_NO_SUID 16
148 #define OPT_NO_DEV 18
150 #define OPT_NO_LOCK 20
151 #define OPT_NO_EXEC 21
156 #define OPT_REMOUNT 26
158 #define OPT_NOMAND 28
160 #define OPT_BKUPUID 30
161 #define OPT_BKUPGID 31
162 #define OPT_NOFAIL 32
164 #define MNT_TMP_FILE "/.mtab.cifs.XXXXXX"
166 /* struct for holding parsed mount info for use by privleged process */
167 struct parsed_mount_info {
169 char host[NI_MAXHOST + 1];
170 char share[MAX_SHARE_LEN + 1];
171 char prefix[PATH_MAX + 1];
172 char options[MAX_OPTIONS_LEN];
173 char domain[MAX_DOMAIN_SIZE + 1];
174 char username[MAX_USERNAME_SIZE + 1];
175 char password[MOUNT_PASSWD_SIZE + 1];
176 char addrlist[MAX_ADDR_LIST_LEN];
177 unsigned int got_user:1;
178 unsigned int got_password:1;
179 unsigned int fakemnt:1;
180 unsigned int nomtab:1;
181 unsigned int verboseflag:1;
182 unsigned int nofail:1;
185 const char *thisprogram;
186 const char *cifs_fstype = "cifs";
188 static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info);
190 static int check_setuid(void)
193 fprintf(stderr, "This program is not installed setuid root - "
194 " \"user\" CIFS mounts not supported.\n");
198 #if CIFS_DISABLE_SETUID_CAPABILITY
199 if (getuid() && !geteuid()) {
200 printf("This mount.cifs program has been built with the "
201 "ability to run as a setuid root program disabled.\n");
204 #endif /* CIFS_DISABLE_SETUID_CAPABILITY */
210 check_fstab(const char *progname, const char *mountpoint, const char *devname,
216 /* make sure this mount is listed in /etc/fstab */
217 fstab = setmntent(_PATH_MNTTAB, "r");
219 fprintf(stderr, "Couldn't open %s for reading!\n", _PATH_MNTTAB);
223 while ((mnt = getmntent(fstab))) {
224 if (!strcmp(mountpoint, mnt->mnt_dir))
229 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
230 fprintf(stderr, "%s: permission denied: no match for "
231 "%s found in %s\n", progname, mountpoint, _PATH_MNTTAB);
236 * 'mount' munges the options from fstab before passing them
237 * to us. It is non-trivial to test that we have the correct
238 * set of options. We don't want to trust what the user
239 * gave us, so just take whatever is in /etc/fstab.
242 *options = strdup(mnt->mnt_opts);
249 open nofollow - avoid symlink exposure?
250 get owner of dir see if matches self or if root
251 call system(umount argv) etc.
255 static int mount_usage(FILE * stream)
257 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n",
259 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
260 fprintf(stream, " to a local directory.\n\nOptions:\n");
261 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
262 fprintf(stream, "\nLess commonly used options:");
264 "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
266 "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
268 "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
270 "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign,fsc");
272 "\n\nOptions not needed for servers supporting CIFS Unix extensions");
274 "\n\t(e.g. unneeded for mounts to most Samba versions):");
276 "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
277 fprintf(stream, "\n\nRarely used options:");
279 "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
281 "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
283 "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
285 "\n\nOptions are described in more detail in the manual page");
286 fprintf(stream, "\n\tman 8 mount.cifs\n");
287 fprintf(stream, "\nTo display the version number of the mount helper:");
288 fprintf(stream, "\n\t%s -V\n", thisprogram);
290 if (stream == stderr)
296 * CIFS has to "escape" commas in the password field so that they don't
297 * end up getting confused for option delimiters. Copy password into pw
298 * field, turning any commas into double commas.
300 static int set_password(struct parsed_mount_info *parsed_info, const char *src)
302 char *dst = parsed_info->password;
303 unsigned int i = 0, j = 0;
309 if (j > sizeof(parsed_info->password)) {
310 fprintf(stderr, "Converted password too long!\n");
315 parsed_info->got_password = 1;
320 * Parse a username string into parsed_mount_info fields. The format is:
322 * DOMAIN\username%password
324 * ...obviously the only required component is "username". The source string
325 * is modified in the process, but it should remain unchanged at the end.
327 * NOTE: the above syntax does not allow for usernames that have slashes in
328 * them, as some krb5 usernames do. Support for the above syntax will be
329 * removed in a later version of cifs-utils. Users should use separate options
330 * instead of overloading this info into the username.
332 static int parse_username(char *rawuser, struct parsed_mount_info *parsed_info)
334 char *user, *password, slash;
338 /* everything after first % sign is a password */
339 password = strchr(rawuser, '%');
342 rc = set_password(parsed_info, password + 1);
348 /* everything after first '/' or '\' is a username */
349 user = strchr(rawuser, '/');
351 user = strchr(rawuser, '\\');
353 /* everything before that slash is a domain */
358 strlcpy(parsed_info->domain, rawuser,
359 sizeof(parsed_info->domain));
365 strlcpy(parsed_info->username, user, sizeof(parsed_info->username));
366 parsed_info->got_user = 1;
371 fprintf(stderr, "WARNING: The DOMAIN/username%%password syntax "
372 "for usernames is deprecated and will be "
373 "removed in version 5.9 of cifs-utils.\n");
378 #ifdef HAVE_LIBCAP_NG
380 drop_capabilities(int parent)
382 capng_setpid(getpid());
383 capng_clear(CAPNG_SELECT_BOTH);
385 if (capng_updatev(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH, CAP_DAC_OVERRIDE, -1)) {
386 fprintf(stderr, "Unable to update capability set.\n");
389 if (capng_update(CAPNG_ADD, CAPNG_PERMITTED|CAPNG_EFFECTIVE, CAP_SYS_ADMIN)) {
390 fprintf(stderr, "Unable to update capability set.\n");
394 if (capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH)) {
395 fprintf(stderr, "Unable to update capability set.\n");
399 if (capng_apply(CAPNG_SELECT_BOTH)) {
400 fprintf(stderr, "Unable to apply new capability set.\n");
407 toggle_dac_capability(int writable, int enable)
409 unsigned int capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
411 if (capng_update(enable ? CAPNG_ADD : CAPNG_DROP, CAPNG_EFFECTIVE, capability)) {
412 fprintf(stderr, "Unable to update capability set.\n");
415 if (capng_apply(CAPNG_SELECT_CAPS)) {
416 fprintf(stderr, "Unable to apply new capability set.\n");
421 #else /* HAVE_LIBCAP_NG */
425 prune_bounding_set(void)
428 static int bounding_set_cleared;
430 if (bounding_set_cleared)
433 for (i = 0; i <= CAP_LAST_CAP && rc == 0; ++i)
434 rc = prctl(PR_CAPBSET_DROP, i);
437 fprintf(stderr, "Unable to clear capability bounding set: %d\n", rc);
441 ++bounding_set_cleared;
444 #else /* HAVE_PRCTL */
446 prune_bounding_set(void)
450 #endif /* HAVE_PRCTL */
452 drop_capabilities(int parent)
456 cap_value_t cap_list[3];
458 rc = prune_bounding_set();
462 caps = cap_get_proc();
464 fprintf(stderr, "Unable to get current capability set: %s\n",
469 if (cap_clear(caps) == -1) {
470 fprintf(stderr, "Unable to clear capability set: %s\n",
476 if (parent || getuid() == 0) {
478 cap_list[0] = CAP_DAC_READ_SEARCH;
480 cap_list[1] = CAP_DAC_OVERRIDE;
481 cap_list[2] = CAP_SYS_ADMIN;
484 if (cap_set_flag(caps, CAP_PERMITTED, ncaps, cap_list, CAP_SET) == -1) {
485 fprintf(stderr, "Unable to set permitted capabilities: %s\n",
491 cap_list[0] = CAP_SYS_ADMIN;
492 if (cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET) == -1) {
493 fprintf(stderr, "Unable to set effective capabilities: %s\n",
501 if (cap_set_proc(caps) != 0) {
502 fprintf(stderr, "Unable to set current process capabilities: %s\n",
512 toggle_dac_capability(int writable, int enable)
516 cap_value_t capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
518 caps = cap_get_proc();
520 fprintf(stderr, "Unable to get current capability set: %s\n",
525 if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &capability,
526 enable ? CAP_SET : CAP_CLEAR) == -1) {
527 fprintf(stderr, "Unable to %s effective capabilities: %s\n",
528 enable ? "set" : "clear", strerror(errno));
533 if (cap_set_proc(caps) != 0) {
534 fprintf(stderr, "Unable to set current process capabilities: %s\n",
542 #else /* HAVE_LIBCAP */
544 drop_capabilities(int parent __attribute((unused)))
550 toggle_dac_capability(int writable __attribute((unused)), int enable __attribute((unused)))
554 #endif /* HAVE_LIBCAP */
555 #endif /* HAVE_LIBCAP_NG */
557 static void null_terminate_endl(char *source)
559 char *newline = strchr(source, '\n');
565 * Parse a line from the credentials file. Changes target to first
566 * character after '=' on 'line' and returns the value type of the line
567 * Returns CRED_UNPARSEABLE on failure or if either parameter is NULL.
569 static int parse_cred_line(char *line, char **target)
571 if (line == NULL || target == NULL)
574 /* position target at first char of value */
575 *target = strchr(line, '=');
580 /* tell the caller which value target points to */
581 if (strncasecmp("user", line, 4) == 0)
583 if (strncasecmp("pass", line, 4) == 0)
585 if (strncasecmp("dom", line, 3) == 0)
589 return CRED_UNPARSEABLE;
592 static int open_cred_file(char *file_name,
593 struct parsed_mount_info *parsed_info,
594 char **saved_username)
596 char *line_buf = NULL;
597 char *temp_val = NULL;
600 const int line_buf_size = 4096;
601 const int min_non_white = 10;
603 i = toggle_dac_capability(0, 1);
607 i = access(file_name, R_OK);
609 toggle_dac_capability(0, 0);
614 fs = fopen(file_name, "r");
616 toggle_dac_capability(0, 0);
621 i = toggle_dac_capability(0, 0);
625 line_buf = (char *)malloc(line_buf_size);
626 if (line_buf == NULL) {
631 /* parse line from credentials file */
632 while (fgets(line_buf, line_buf_size, fs)) {
633 /* eat leading white space */
634 for (i = 0; i < line_buf_size - min_non_white + 1; i++) {
635 if ((line_buf[i] != ' ') && (line_buf[i] != '\t'))
638 null_terminate_endl(line_buf);
640 /* parse next token */
641 switch (parse_cred_line(line_buf + i, &temp_val)) {
643 *saved_username = strdup(temp_val);
644 if (!*saved_username) {
650 i = set_password(parsed_info, temp_val);
655 if (parsed_info->verboseflag)
656 fprintf(stderr, "domain=%s\n",
658 strlcpy(parsed_info->domain, temp_val,
659 sizeof(parsed_info->domain));
661 case CRED_UNPARSEABLE:
662 if (parsed_info->verboseflag)
663 fprintf(stderr, "Credential formatted "
665 temp_val ? temp_val : "(null)");
674 /* make sure passwords are scrubbed from memory */
675 if (line_buf != NULL)
676 memset(line_buf, 0, line_buf_size);
682 get_password_from_file(int file_descript, char *filename,
683 struct parsed_mount_info *parsed_info)
686 char buf[sizeof(parsed_info->password) + 1];
688 if (filename != NULL) {
689 rc = toggle_dac_capability(0, 1);
693 rc = access(filename, R_OK);
696 "mount.cifs failed: access check of %s failed: %s\n",
697 filename, strerror(errno));
698 toggle_dac_capability(0, 0);
702 file_descript = open(filename, O_RDONLY);
703 if (file_descript < 0) {
705 "mount.cifs failed. %s attempting to open password file %s\n",
706 strerror(errno), filename);
707 toggle_dac_capability(0, 0);
711 rc = toggle_dac_capability(0, 0);
718 memset(buf, 0, sizeof(buf));
719 rc = read(file_descript, buf, sizeof(buf) - 1);
722 "mount.cifs failed. Error %s reading password file\n",
728 rc = set_password(parsed_info, buf);
731 if (filename != NULL)
732 close(file_descript);
737 * Returns OPT_ERROR on unparsable token.
739 static int parse_opt_token(const char *token)
744 if (strncmp(token, "users", 5) == 0)
746 if (strncmp(token, "user_xattr", 10) == 0)
747 return OPT_USER_XATTR;
748 if (strncmp(token, "user", 4) == 0)
750 if (strncmp(token, "pass", 4) == 0)
752 if (strncmp(token, "sec", 3) == 0)
754 if (strncmp(token, "ip", 2) == 0)
756 if (strncmp(token, "unc", 3) == 0 ||
757 strncmp(token, "target", 6) == 0 ||
758 strncmp(token, "path", 4) == 0)
760 if (strncmp(token, "dom", 3) == 0 || strncmp(token, "workg", 5) == 0)
762 if (strncmp(token, "cred", 4) == 0)
764 if (strncmp(token, "uid", 3) == 0)
766 if (strncmp(token, "cruid", 5) == 0)
768 if (strncmp(token, "gid", 3) == 0)
770 if (strncmp(token, "fmask", 5) == 0)
772 if (strncmp(token, "file_mode", 9) == 0)
773 return OPT_FILE_MODE;
774 if (strncmp(token, "dmask", 5) == 0)
776 if (strncmp(token, "dir_mode", 4) == 0 || strncmp(token, "dirm", 4) == 0)
778 if (strncmp(token, "nosuid", 6) == 0)
780 if (strncmp(token, "suid", 4) == 0)
782 if (strncmp(token, "nodev", 5) == 0)
784 if (strncmp(token, "nobrl", 5) == 0 || strncmp(token, "nolock", 6) == 0)
786 if (strncmp(token, "mand", 4) == 0)
788 if (strncmp(token, "nomand", 6) == 0)
790 if (strncmp(token, "dev", 3) == 0)
792 if (strncmp(token, "noexec", 6) == 0)
794 if (strncmp(token, "exec", 4) == 0)
796 if (strncmp(token, "guest", 5) == 0)
798 if (strncmp(token, "ro", 2) == 0)
800 if (strncmp(token, "rw", 2) == 0 && strlen(token) == 2)
802 if (strncmp(token, "remount", 7) == 0)
804 if (strncmp(token, "_netdev", 7) == 0)
806 if (strncmp(token, "backupuid", 9) == 0)
808 if (strncmp(token, "backupgid", 9) == 0)
810 if (strncmp(token, "nofail", 6) == 0)
817 parse_options(const char *data, struct parsed_mount_info *parsed_info)
821 char *next_keyword = NULL;
822 char *out = parsed_info->options;
823 unsigned long *filesys_flags = &parsed_info->flags;
832 uid_t uid, cruid = 0, bkupuid = 0;
833 gid_t gid, bkupgid = 0;
837 char *saved_username = NULL;
838 bool krb5_auth = false;
840 * max 32-bit uint in decimal is 4294967295 which is 10 chars wide
841 * +1 for NULL, and +1 for good measure
845 /* make sure we're starting from beginning */
848 /* BB fixme check for separator override BB */
861 * format is keyword,keyword2=value2,keyword3=value3...
862 * data = next keyword
863 * value = next value ie stuff after equal sign
865 while (data && *data) {
866 next_keyword = strchr(data, ','); /* BB handle sep= */
868 /* temporarily null terminate end of keyword=value pair */
872 /* temporarily null terminate keyword if there's a value */
874 if ((equals = strchr(data, '=')) != NULL) {
879 switch(parse_opt_token(data)) {
881 if (!value || !*value) {
882 *filesys_flags |= MS_USERS;
888 if (!value || !*value) {
889 if (data[4] == '\0') {
890 *filesys_flags |= MS_USER;
894 "username specified with no parameter\n");
898 /* domain/username%password + NULL term. */
899 const size_t max = MAX_DOMAIN_SIZE +
901 MOUNT_PASSWD_SIZE + 2 + 1;
902 if (strnlen(value, max) >= max) {
903 fprintf(stderr, "username too long\n");
906 saved_username = strdup(value);
907 if (!saved_username) {
908 fprintf(stderr, "Unable to allocate memory!\n");
915 if (parsed_info->got_password) {
917 "password specified twice, ignoring second\n");
920 if (!value || !*value) {
921 parsed_info->got_password = 1;
924 rc = set_password(parsed_info, value);
931 if (!strncmp(value, "none", 4)) {
932 parsed_info->got_password = 1;
933 } else if (!strncmp(value, "krb5", 4)) {
934 parsed_info->got_password = 1;
941 if (!value || !*value) {
943 "target ip address argument missing\n");
944 } else if (strnlen(value, MAX_ADDRESS_LEN) <=
946 strcpy(parsed_info->addrlist, value);
947 if (parsed_info->verboseflag)
949 "ip address %s override specified\n",
953 fprintf(stderr, "ip address too long\n");
959 /* unc || target || path */
961 if (!value || !*value) {
963 "invalid path to network resource\n");
966 rc = parse_unc(value, parsed_info);
971 /* dom || workgroup */
973 if (!value || !*value) {
974 fprintf(stderr, "CIFS: invalid domain name\n");
977 if (strnlen(value, sizeof(parsed_info->domain)) >=
978 sizeof(parsed_info->domain)) {
979 fprintf(stderr, "domain name too long\n");
982 strlcpy(parsed_info->domain, value,
983 sizeof(parsed_info->domain));
987 if (!value || !*value) {
989 "invalid credential file name specified\n");
992 rc = open_cred_file(value, parsed_info, &saved_username);
995 "error %d (%s) opening credential file %s\n",
996 rc, strerror(rc), value);
1002 if (!value || !*value)
1007 uid = strtoul(value, &ep, 10);
1008 if (errno == 0 && *ep == '\0')
1011 pw = getpwnam(value);
1013 fprintf(stderr, "bad user name \"%s\"\n", value);
1021 if (!value || !*value)
1026 cruid = strtoul(value, &ep, 10);
1027 if (errno == 0 && *ep == '\0')
1030 pw = getpwnam(value);
1032 fprintf(stderr, "bad user name \"%s\"\n", value);
1039 if (!value || !*value)
1044 gid = strtoul(value, &ep, 10);
1045 if (errno == 0 && *ep == '\0')
1048 gr = getgrnam(value);
1050 fprintf(stderr, "bad group name \"%s\"\n", value);
1057 /* fmask fall through to file_mode */
1060 "WARNING: CIFS mount option 'fmask' is\
1061 deprecated. Use 'file_mode' instead.\n");
1062 data = "file_mode"; /* BB fix this */
1064 if (!value || !*value) {
1066 "Option '%s' requires a numerical argument\n",
1071 if (value[0] != '0')
1073 "WARNING: '%s' not expressed in octal.\n",
1077 /* dmask falls through to dir_mode */
1080 "WARNING: CIFS mount option 'dmask' is\
1081 deprecated. Use 'dir_mode' instead.\n");
1084 if (!value || !*value) {
1086 "Option '%s' requires a numerical argument\n",
1091 if (value[0] != '0')
1093 "WARNING: '%s' not expressed in octal.\n",
1097 /* the following mount options should be
1098 stripped out from what is passed into the kernel
1099 since these options are best passed as the
1100 mount flags rather than redundantly to the kernel
1101 and could generate spurious warnings depending on the
1102 level of the corresponding cifs vfs kernel code */
1104 *filesys_flags |= MS_NOSUID;
1107 *filesys_flags &= ~MS_NOSUID;
1110 *filesys_flags |= MS_NODEV;
1112 /* nolock || nobrl */
1114 *filesys_flags &= ~MS_MANDLOCK;
1117 *filesys_flags |= MS_MANDLOCK;
1120 *filesys_flags &= ~MS_MANDLOCK;
1123 *filesys_flags &= ~MS_NODEV;
1126 *filesys_flags |= MS_NOEXEC;
1129 *filesys_flags &= ~MS_NOEXEC;
1132 parsed_info->got_user = 1;
1133 parsed_info->got_password = 1;
1136 *filesys_flags |= MS_RDONLY;
1139 *filesys_flags &= ~MS_RDONLY;
1142 *filesys_flags |= MS_REMOUNT;
1147 if (!value || !*value)
1152 bkupuid = strtoul(value, &ep, 10);
1153 if (errno == 0 && *ep == '\0')
1156 pw = getpwnam(value);
1159 "bad user name \"%s\"\n", value);
1163 bkupuid = pw->pw_uid;
1166 if (!value || !*value)
1171 bkupgid = strtoul(value, &ep, 10);
1172 if (errno == 0 && *ep == '\0')
1175 gr = getgrnam(value);
1178 "bad group name \"%s\"\n", value);
1182 bkupgid = gr->gr_gid;
1185 parsed_info->nofail = 1;
1189 /* check size before copying option to buffer */
1190 word_len = strlen(data);
1192 word_len += 1 + strlen(value);
1194 /* need 2 extra bytes for comma and null byte */
1195 if (out_len + word_len + 2 > MAX_OPTIONS_LEN) {
1196 fprintf(stderr, "Options string too long\n");
1200 /* put back equals sign, if any */
1204 /* go ahead and copy */
1206 strlcat(out, ",", MAX_OPTIONS_LEN);
1208 strlcat(out, data, MAX_OPTIONS_LEN);
1209 out_len = strlen(out);
1211 data = next_keyword;
1214 if (saved_username) {
1216 strlcpy(parsed_info->username, saved_username,
1217 sizeof(parsed_info->username));
1218 parsed_info->got_user = 1;
1220 rc = parse_username(saved_username, parsed_info);
1221 free(saved_username);
1223 fprintf(stderr, "Unable to parse username!\n");
1230 /* special-case the uid and gid */
1232 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", uid);
1234 /* comma + "uid=" + 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, "uid=%s", txtbuf);
1245 out_len = strlen(out);
1248 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", cruid);
1250 /* comma + "cruid=" + terminating NULL == 6 */
1251 if (out_len + word_len + 8 > MAX_OPTIONS_LEN) {
1252 fprintf(stderr, "Options string too long\n");
1257 strlcat(out, ",", MAX_OPTIONS_LEN);
1260 snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf);
1261 out_len = strlen(out);
1264 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", gid);
1266 /* comma + "gid=" + terminating NULL == 6 */
1267 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1268 fprintf(stderr, "Options string too long\n");
1273 strlcat(out, ",", MAX_OPTIONS_LEN);
1276 snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf);
1279 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupuid);
1281 /* comma + "backupuid=" + terminating NULL == 12 */
1282 if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
1283 fprintf(stderr, "Options string too long\n");
1288 strlcat(out, ",", MAX_OPTIONS_LEN);
1291 snprintf(out + out_len, word_len + 11, "backupuid=%s", txtbuf);
1292 out_len = strlen(out);
1295 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupgid);
1297 /* comma + "backkupgid=" + terminating NULL == 12 */
1298 if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
1299 fprintf(stderr, "Options string too long\n");
1304 strlcat(out, ",", MAX_OPTIONS_LEN);
1307 snprintf(out + out_len, word_len + 11, "backupgid=%s", txtbuf);
1313 static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info)
1315 int length = strnlen(unc_name, MAX_UNC_LEN);
1316 const char *host, *share, *prepath;
1317 size_t hostlen, sharelen, prepathlen;
1319 if (length > (MAX_UNC_LEN - 1)) {
1320 fprintf(stderr, "mount error: UNC name too long\n");
1325 fprintf(stderr, "mount error: UNC name too short\n");
1329 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1330 (strncasecmp("smb://", unc_name, 6) == 0)) {
1332 "Mounting cifs URL not implemented yet. Attempt to mount %s\n",
1337 /* Set up "host" and "share" pointers based on UNC format. */
1338 /* TODO: Remove support for NFS syntax as of cifs-utils-6.0. */
1339 if (strncmp(unc_name, "//", 2) && strncmp(unc_name, "\\\\", 2)) {
1341 * check for nfs syntax (server:/share/prepath)
1343 * FIXME: IPv6 addresses?
1346 share = strchr(host, ':');
1348 fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1351 hostlen = share - host;
1355 fprintf(stderr, "WARNING: using NFS syntax for mounting CIFS "
1356 "shares is deprecated and will be removed in cifs-utils"
1357 "-6.0. Please migrate to UNC syntax.\n");
1359 host = unc_name + 2;
1360 hostlen = strcspn(host, "/\\");
1362 fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1365 share = host + hostlen + 1;
1368 if (hostlen + 1 > sizeof(parsed_info->host)) {
1369 fprintf(stderr, "mount.cifs: host portion of UNC too long\n");
1373 sharelen = strcspn(share, "/\\");
1374 if (sharelen + 1 > sizeof(parsed_info->share)) {
1375 fprintf(stderr, "mount.cifs: share portion of UNC too long\n");
1379 prepath = share + sharelen;
1380 if (*prepath != '\0')
1383 prepathlen = strlen(prepath);
1385 if (prepathlen + 1 > sizeof(parsed_info->prefix)) {
1386 fprintf(stderr, "mount.cifs: UNC prefixpath too long\n");
1390 /* copy pieces into their resepective buffers */
1391 memcpy(parsed_info->host, host, hostlen);
1392 memcpy(parsed_info->share, share, sharelen);
1393 memcpy(parsed_info->prefix, prepath, prepathlen);
1398 static int get_pw_from_env(struct parsed_mount_info *parsed_info)
1402 if (getenv("PASSWD"))
1403 rc = set_password(parsed_info, getenv("PASSWD"));
1404 else if (getenv("PASSWD_FD"))
1405 rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL,
1407 else if (getenv("PASSWD_FILE"))
1408 rc = get_password_from_file(0, getenv("PASSWD_FILE"),
1414 static struct option longopts[] = {
1415 {"all", 0, NULL, 'a'},
1416 {"help", 0, NULL, 'h'},
1417 {"move", 0, NULL, 'm'},
1418 {"bind", 0, NULL, 'b'},
1419 {"read-only", 0, NULL, 'r'},
1420 {"ro", 0, NULL, 'r'},
1421 {"verbose", 0, NULL, 'v'},
1422 {"version", 0, NULL, 'V'},
1423 {"read-write", 0, NULL, 'w'},
1424 {"rw", 0, NULL, 'w'},
1425 {"options", 1, NULL, 'o'},
1426 {"type", 1, NULL, 't'},
1427 {"uid", 1, NULL, '1'},
1428 {"gid", 1, NULL, '2'},
1429 {"user", 1, NULL, 'u'},
1430 {"username", 1, NULL, 'u'},
1431 {"dom", 1, NULL, 'd'},
1432 {"domain", 1, NULL, 'd'},
1433 {"password", 1, NULL, 'p'},
1434 {"pass", 1, NULL, 'p'},
1435 {"credentials", 1, NULL, 'c'},
1436 {"port", 1, NULL, 'P'},
1437 {"sloppy", 0, NULL, 's'},
1441 /* convert a string to uppercase. return false if the string
1442 * wasn't ASCII. Return success on a NULL ptr */
1443 static int uppercase_string(char *string)
1449 /* check for unicode */
1450 if ((unsigned char)string[0] & 0x80)
1452 *string = toupper((unsigned char)*string);
1459 static void print_cifs_mount_version(void)
1461 printf("mount.cifs version: %s\n", VERSION);
1465 * This function borrowed from fuse-utils...
1467 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1468 * newlines embedded within the text fields. To make sure no one corrupts
1469 * the mtab, fail the mount if there are embedded newlines.
1471 static int check_newline(const char *progname, const char *name)
1474 for (s = "\n"; *s; s++) {
1475 if (strchr(name, *s)) {
1477 "%s: illegal character 0x%02x in mount entry\n",
1485 static int check_mtab(const char *progname, const char *devname,
1488 if (check_newline(progname, devname) || check_newline(progname, dir))
1494 add_mtab(char *devname, char *mountpoint, unsigned long flags, const char *fstype)
1496 int rc = 0, tmprc, fd;
1498 char *mount_user = NULL;
1499 struct mntent mountent;
1500 struct stat statbuf;
1502 sigset_t mask, oldmask;
1506 mount_user = getusername(uid);
1509 * Set the real uid to the effective uid. This prevents unprivileged
1510 * users from sending signals to this process, though ^c on controlling
1511 * terminal should still work.
1513 rc = setreuid(geteuid(), -1);
1515 fprintf(stderr, "Unable to set real uid to effective uid: %s\n",
1520 rc = sigfillset(&mask);
1522 fprintf(stderr, "Unable to set filled signal mask\n");
1526 rc = sigprocmask(SIG_SETMASK, &mask, &oldmask);
1528 fprintf(stderr, "Unable to make process ignore signals\n");
1532 rc = toggle_dac_capability(1, 1);
1536 atexit(unlock_mtab);
1539 fprintf(stderr, "cannot lock mtab");
1544 pmntfile = setmntent(MOUNTED, "a+");
1546 fprintf(stderr, "could not update mount table\n");
1552 fd = fileno(pmntfile);
1554 fprintf(stderr, "mntent does not appear to be valid\n");
1560 rc = fstat(fd, &statbuf);
1562 fprintf(stderr, "unable to fstat open mtab\n");
1563 endmntent(pmntfile);
1569 mountent.mnt_fsname = devname;
1570 mountent.mnt_dir = mountpoint;
1571 mountent.mnt_type = (char *)(void *)fstype;
1572 mountent.mnt_opts = (char *)calloc(MTAB_OPTIONS_LEN, 1);
1573 if (mountent.mnt_opts) {
1574 if (flags & MS_RDONLY)
1575 strlcat(mountent.mnt_opts, "ro", MTAB_OPTIONS_LEN);
1577 strlcat(mountent.mnt_opts, "rw", MTAB_OPTIONS_LEN);
1579 if (flags & MS_MANDLOCK)
1580 strlcat(mountent.mnt_opts, ",mand", MTAB_OPTIONS_LEN);
1581 if (flags & MS_NOEXEC)
1582 strlcat(mountent.mnt_opts, ",noexec", MTAB_OPTIONS_LEN);
1583 if (flags & MS_NOSUID)
1584 strlcat(mountent.mnt_opts, ",nosuid", MTAB_OPTIONS_LEN);
1585 if (flags & MS_NODEV)
1586 strlcat(mountent.mnt_opts, ",nodev", MTAB_OPTIONS_LEN);
1587 if (flags & MS_SYNCHRONOUS)
1588 strlcat(mountent.mnt_opts, ",sync", MTAB_OPTIONS_LEN);
1590 strlcat(mountent.mnt_opts, ",user=", MTAB_OPTIONS_LEN);
1591 strlcat(mountent.mnt_opts, mount_user,
1595 mountent.mnt_freq = 0;
1596 mountent.mnt_passno = 0;
1597 rc = addmntent(pmntfile, &mountent);
1599 int ignore __attribute__((unused));
1601 fprintf(stderr, "unable to add mount entry to mtab\n");
1602 ignore = ftruncate(fd, statbuf.st_size);
1605 tmprc = my_endmntent(pmntfile, statbuf.st_size);
1607 fprintf(stderr, "error %d detected on close of mtab\n", tmprc);
1611 SAFE_FREE(mountent.mnt_opts);
1613 toggle_dac_capability(1, 0);
1614 sigprocmask(SIG_SETMASK, &oldmask, NULL);
1620 del_mtab(char *mountpoint)
1623 FILE *mnttmp, *mntmtab;
1624 struct mntent *mountent;
1625 char *mtabfile, *mtabdir, *mtabtmpfile;
1627 mtabfile = strdup(MOUNTED);
1628 mtabdir = dirname(mtabfile);
1629 mtabdir = realloc(mtabdir, strlen(mtabdir) + strlen(MNT_TMP_FILE) + 2);
1631 fprintf(stderr, "del_mtab: cannot determine current mtab path");
1636 mtabtmpfile = strcat(mtabdir, MNT_TMP_FILE);
1638 fprintf(stderr, "del_mtab: cannot allocate memory to tmp file");
1643 atexit(unlock_mtab);
1646 fprintf(stderr, "del_mtab: cannot lock mtab");
1651 mtabtmpfile = mktemp(mtabtmpfile);
1653 fprintf(stderr, "del_mtab: cannot setup tmp file destination");
1658 mntmtab = setmntent(MOUNTED, "r");
1660 fprintf(stderr, "del_mtab: could not update mount table\n");
1665 mnttmp = setmntent(mtabtmpfile, "w");
1667 fprintf(stderr, "del_mtab: could not update mount table\n");
1673 while ((mountent = getmntent(mntmtab)) != NULL) {
1674 if (!strcmp(mountent->mnt_dir, mountpoint))
1676 rc = addmntent(mnttmp, mountent);
1678 fprintf(stderr, "del_mtab: unable to add mount entry to mtab\n");
1680 goto del_mtab_error;
1686 tmprc = my_endmntent(mnttmp, 0);
1688 fprintf(stderr, "del_mtab: error %d detected on close of tmp file\n", tmprc);
1690 goto del_mtab_error;
1693 if (rename(mtabtmpfile, MOUNTED)) {
1694 fprintf(stderr, "del_mtab: error %d when renaming mtab in place\n", errno);
1696 goto del_mtab_error;
1705 if (unlink(mtabtmpfile))
1706 fprintf(stderr, "del_mtab: failed to delete tmp file - %s\n",
1711 /* have the child drop root privileges */
1713 drop_child_privs(void)
1716 uid_t uid = getuid();
1717 gid_t gid = getgid();
1722 fprintf(stderr, "Unable set group identity: %s\n",
1730 fprintf(stderr, "Unable set user identity: %s\n",
1740 * If systemd is running and /bin/systemd-ask-password --
1741 * is available, then use that else fallback on getpass(..)
1743 * Returns: @input or NULL on error
1746 get_password(const char *prompt, char *input, int capacity)
1748 #ifdef ENABLE_SYSTEMD
1749 int is_systemd_running;
1752 /* We simply test whether the systemd cgroup hierarchy is
1754 is_systemd_running = (lstat("/sys/fs/cgroup", &a) == 0)
1755 && (lstat("/sys/fs/cgroup/systemd", &b) == 0)
1756 && (a.st_dev != b.st_dev);
1758 if (is_systemd_running) {
1760 FILE *ask_pass_fp = NULL;
1763 if (asprintf(&cmd, "/bin/systemd-ask-password \"%s\"", prompt) >= 0) {
1764 ask_pass_fp = popen (cmd, "re");
1769 ret = fgets(input, capacity, ask_pass_fp);
1770 pclose(ask_pass_fp);
1774 int len = strlen(input);
1775 if (input[len - 1] == '\n')
1776 input[len - 1] = '\0';
1783 * Falling back to getpass(..)
1784 * getpass is obsolete, but there's apparently nothing that replaces it
1786 char *tmp_pass = getpass(prompt);
1790 strncpy(input, tmp_pass, capacity - 1);
1791 input[capacity - 1] = '\0';
1793 /* zero-out the static buffer */
1794 memset(tmp_pass, 0, strlen(tmp_pass));
1800 assemble_mountinfo(struct parsed_mount_info *parsed_info,
1801 const char *thisprogram, const char *mountpoint,
1802 const char *orig_dev, char *orgoptions)
1806 rc = drop_capabilities(0);
1810 rc = drop_child_privs();
1815 rc = check_fstab(thisprogram, mountpoint, orig_dev,
1820 /* enable any default user mount flags */
1821 parsed_info->flags |= CIFS_SETUID_FLAGS;
1824 rc = get_pw_from_env(parsed_info);
1829 rc = parse_options(orgoptions, parsed_info);
1835 if (!(parsed_info->flags & (MS_USERS | MS_USER))) {
1836 fprintf(stderr, "%s: permission denied\n", thisprogram);
1842 parsed_info->flags &= ~(MS_USERS | MS_USER);
1844 rc = parse_unc(orig_dev, parsed_info);
1848 if (parsed_info->addrlist[0] == '\0')
1849 rc = resolve_host(parsed_info->host, parsed_info->addrlist);
1853 fprintf(stderr, "mount error: could not resolve address for "
1854 "%s: %s\n", parsed_info->host,
1855 rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
1859 fprintf(stderr, "mount error: problem parsing address "
1860 "list: %s\n", strerror(errno));
1864 if (!parsed_info->got_user) {
1866 * Note that the password will not be retrieved from the
1867 * USER env variable (ie user%password form) as there is
1868 * already a PASSWD environment varaible
1871 strlcpy(parsed_info->username, getenv("USER"),
1872 sizeof(parsed_info->username));
1874 strlcpy(parsed_info->username, getusername(getuid()),
1875 sizeof(parsed_info->username));
1876 parsed_info->got_user = 1;
1879 if (!parsed_info->got_password) {
1880 char tmp_pass[MOUNT_PASSWD_SIZE + 1];
1881 char *prompt = NULL;
1883 if(asprintf(&prompt, "Password for %s@%s: ", parsed_info->username, orig_dev) < 0)
1886 if (get_password(prompt ? prompt : "Password: ", tmp_pass, MOUNT_PASSWD_SIZE + 1)) {
1887 rc = set_password(parsed_info, tmp_pass);
1889 fprintf(stderr, "Error reading password, exiting\n");
1898 /* copy in user= string */
1899 if (parsed_info->got_user) {
1900 if (*parsed_info->options)
1901 strlcat(parsed_info->options, ",",
1902 sizeof(parsed_info->options));
1903 strlcat(parsed_info->options, "user=",
1904 sizeof(parsed_info->options));
1905 strlcat(parsed_info->options, parsed_info->username,
1906 sizeof(parsed_info->options));
1909 if (*parsed_info->domain) {
1910 if (*parsed_info->options)
1911 strlcat(parsed_info->options, ",",
1912 sizeof(parsed_info->options));
1913 strlcat(parsed_info->options, ",domain=",
1914 sizeof(parsed_info->options));
1915 strlcat(parsed_info->options, parsed_info->domain,
1916 sizeof(parsed_info->options));
1924 * chdir() into the mountpoint and determine "realpath". We assume here that
1925 * "mountpoint" is a statically allocated string and does not need to be freed.
1928 acquire_mountpoint(char **mountpointp)
1931 uid_t realuid, oldfsuid;
1936 * Acquire the necessary privileges to chdir to the mountpoint. If
1937 * the real uid is root, then we reacquire CAP_DAC_READ_SEARCH. If
1938 * it's not, then we change the fsuid to the real uid to ensure that
1939 * the mounting user actually has access to the mountpoint.
1941 * The mount(8) manpage does not state that users must be able to
1942 * chdir into the mountpoint in order to mount onto it, but if we
1943 * allow that, then an unprivileged user could use this program to
1944 * "probe" into directories to which he does not have access.
1948 dacrc = toggle_dac_capability(0, 1);
1952 oldfsuid = setfsuid(realuid);
1953 oldfsgid = setfsgid(getgid());
1956 rc = chdir(*mountpointp);
1958 fprintf(stderr, "Couldn't chdir to %s: %s\n", *mountpointp,
1964 mountpoint = realpath(".", NULL);
1966 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1967 *mountpointp, strerror(errno));
1971 *mountpointp = mountpoint;
1974 dacrc = toggle_dac_capability(0, 0);
1976 rc = rc ? rc : dacrc;
1978 uid_t __attribute__((unused)) uignore = setfsuid(oldfsuid);
1979 gid_t __attribute__((unused)) gignore = setfsgid(oldfsgid);
1985 int main(int argc, char **argv)
1988 char *orgoptions = NULL;
1989 char *mountpoint = NULL;
1990 char *options = NULL;
1991 char *orig_dev = NULL;
1992 char *currentaddress, *nextaddress;
1994 int already_uppercased = 0;
1996 size_t options_size = MAX_OPTIONS_LEN;
1997 struct parsed_mount_info *parsed_info = NULL;
2000 rc = check_setuid();
2004 rc = drop_capabilities(1);
2008 /* setlocale(LC_ALL, "");
2009 bindtextdomain(PACKAGE, LOCALEDIR);
2010 textdomain(PACKAGE); */
2012 if (!argc || !argv) {
2013 rc = mount_usage(stderr);
2017 thisprogram = basename(argv[0]);
2018 if (thisprogram == NULL)
2019 thisprogram = "mount.cifs";
2021 /* allocate parsed_info as shared anonymous memory range */
2022 parsed_info = mmap(0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
2023 MAP_ANONYMOUS | MAP_SHARED, -1, 0);
2024 if (parsed_info == (struct parsed_mount_info *) -1) {
2026 fprintf(stderr, "Unable to allocate memory: %s\n",
2031 /* add sharename in opts string as unc= parm */
2032 while ((c = getopt_long(argc, argv, "?fhno:rsvVw",
2033 longopts, NULL)) != -1) {
2036 case 'h': /* help */
2037 rc = mount_usage(stdout);
2040 ++parsed_info->nomtab;
2043 orgoptions = strndup(optarg, MAX_OPTIONS_LEN);
2049 case 'r': /* mount readonly */
2050 parsed_info->flags |= MS_RDONLY;
2053 ++parsed_info->verboseflag;
2056 print_cifs_mount_version();
2059 parsed_info->flags &= ~MS_RDONLY;
2062 ++parsed_info->fakemnt;
2068 fprintf(stderr, "unknown command-line option: %c\n", c);
2069 rc = mount_usage(stderr);
2074 if (argc < 3 || argv[optind] == NULL || argv[optind + 1] == NULL) {
2075 rc = mount_usage(stderr);
2079 orig_dev = argv[optind];
2080 mountpoint = argv[optind + 1];
2082 /* chdir into mountpoint as soon as possible */
2083 rc = acquire_mountpoint(&mountpoint);
2088 * mount.cifs does privilege separation. Most of the code to handle
2089 * assembling the mount info is done in a child process that drops
2090 * privileges. The info is assembled in parsed_info which is a
2091 * shared, mmaped memory segment. The parent waits for the child to
2092 * exit and checks the return code. If it's anything but "0", then
2093 * the process exits without attempting anything further.
2097 fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
2102 rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
2103 orig_dev, orgoptions);
2108 if (!WIFEXITED(rc)) {
2109 fprintf(stderr, "Child process terminated abnormally.\n");
2113 rc = WEXITSTATUS(rc);
2118 options = calloc(options_size, 1);
2120 fprintf(stderr, "Unable to allocate memory.\n");
2125 currentaddress = parsed_info->addrlist;
2126 nextaddress = strchr(currentaddress, ',');
2128 *nextaddress++ = '\0';
2131 if (!currentaddress) {
2132 fprintf(stderr, "Unable to find suitable address.\n");
2133 rc = parsed_info->nofail ? 0 : EX_FAIL;
2136 strlcpy(options, "ip=", options_size);
2137 strlcat(options, currentaddress, options_size);
2139 strlcat(options, ",unc=\\\\", options_size);
2140 strlcat(options, parsed_info->host, options_size);
2141 strlcat(options, "\\", options_size);
2142 strlcat(options, parsed_info->share, options_size);
2144 if (*parsed_info->options) {
2145 strlcat(options, ",", options_size);
2146 strlcat(options, parsed_info->options, options_size);
2149 if (*parsed_info->prefix) {
2150 strlcat(options, ",prefixpath=", options_size);
2151 strlcat(options, parsed_info->prefix, options_size);
2155 strlcat(options, ",sloppy", options_size);
2157 if (parsed_info->verboseflag)
2158 fprintf(stderr, "%s kernel mount options: %s",
2159 thisprogram, options);
2161 if (parsed_info->got_password) {
2163 * Commas have to be doubled, or else they will
2164 * look like the parameter separator
2166 strlcat(options, ",pass=", options_size);
2167 strlcat(options, parsed_info->password, options_size);
2168 if (parsed_info->verboseflag)
2169 fprintf(stderr, ",pass=********");
2172 if (parsed_info->verboseflag)
2173 fprintf(stderr, "\n");
2175 rc = check_mtab(thisprogram, orig_dev, mountpoint);
2179 if (!parsed_info->fakemnt) {
2180 toggle_dac_capability(0, 1);
2181 rc = mount(orig_dev, ".", cifs_fstype, parsed_info->flags, options);
2182 toggle_dac_capability(0, 0);
2189 currentaddress = nextaddress;
2190 if (currentaddress) {
2191 nextaddress = strchr(currentaddress, ',');
2193 *nextaddress++ = '\0';
2198 "mount error: %s filesystem not supported by the system\n", cifs_fstype);
2201 if (!already_uppercased &&
2202 uppercase_string(parsed_info->host) &&
2203 uppercase_string(parsed_info->share) &&
2204 uppercase_string(parsed_info->prefix)) {
2206 "Retrying with upper case share name\n");
2207 already_uppercased = 1;
2211 fprintf(stderr, "mount error(%d): %s\n", errno,
2214 "Refer to the %s(8) manual page (e.g. man "
2215 "%s)\n", thisprogram, thisprogram);
2221 if (!parsed_info->nomtab && !mtab_unusable()) {
2222 if (parsed_info->flags & MS_REMOUNT) {
2223 rc = del_mtab(mountpoint);
2228 rc = add_mtab(orig_dev, mountpoint, parsed_info->flags, cifs_fstype);
2233 memset(parsed_info->password, 0, sizeof(parsed_info->password));
2234 munmap(parsed_info, sizeof(*parsed_info));
2237 SAFE_FREE(orgoptions);