-/*
- Mount helper utility for Linux CIFS VFS (virtual filesystem) client
- Copyright (C) 2003,2008 Steve French (sfrench@us.ibm.com)
- Copyright (C) 2008 Jeremy Allison (jra@samba.org)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/*
+ * Mount helper utility for Linux CIFS VFS (virtual filesystem) client
+ * Copyright (C) 2003,2010 Steve French (sfrench@us.ibm.com)
+ * Copyright (C) 2008 Jeremy Allison (jra@samba.org)
+ * Copyright (C) 2010 Jeff Layton (jlayton@samba.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <mntent.h>
#include <fcntl.h>
#include <limits.h>
-#include <fstab.h>
+#include <paths.h>
+#include <libgen.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <stdbool.h>
+#ifdef HAVE_SYS_FSUID_H
+#include <sys/fsuid.h>
+#endif /* HAVE_SYS_FSUID_H */
+#ifdef HAVE_LIBCAP_NG
+#include <cap-ng.h>
+#else /* HAVE_LIBCAP_NG */
+#ifdef HAVE_PRCTL
+#include <sys/prctl.h>
+#endif /* HAVE_PRCTL */
+#ifdef HAVE_LIBCAP
+#include <sys/capability.h>
+#endif /* HAVE_LIBCAP */
+#endif /* HAVE_LIBCAP_NG */
#include "mount.h"
#include "util.h"
+#include "resolve_host.h"
#ifndef MS_MOVE
#define MS_MOVE 8192
#define MAX_UNC_LEN 1024
+/* I believe that the kernel limits options data to a page */
+#define MAX_OPTIONS_LEN 4096
+
+/* max length of mtab options */
+#define MTAB_OPTIONS_LEN 220
+
+/*
+ * Maximum length of "share" portion of a UNC. I have no idea if this is at
+ * all valid. According to MSDN, the typical max length of any component is
+ * 255, so use that here.
+ */
+#define MAX_SHARE_LEN 256
+
+/* max length of username (somewhat made up here) */
+#define MAX_USERNAME_SIZE 32
+
#ifndef SAFE_FREE
-#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x = NULL; } } while (0)
#endif
#define MOUNT_PASSWD_SIZE 128
-#define DOMAIN_SIZE 64
-
-/* currently maximum length of IPv6 address string */
-#define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
+#define MAX_DOMAIN_SIZE 64
/*
* mount.cifs has been the subject of many "security" bugs that have arisen
- * because of users and distributions installing it as a setuid root program.
- * mount.cifs has not been audited for security. Thus, we strongly recommend
- * that it not be installed setuid root. To make that abundantly clear,
- * mount.cifs now check whether it's running setuid root and exit with an
- * error if it is. If you wish to disable this check, then set the following
- * #define to 1, but please realize that you do so at your own peril.
- */
-#define CIFS_DISABLE_SETUID_CHECK 0
-
-/*
- * By default, mount.cifs follows the conventions set forth by /bin/mount
- * for user mounts. That is, it requires that the mount be listed in
- * /etc/fstab with the "user" option when run as an unprivileged user and
- * mount.cifs is setuid root.
- *
- * Older versions of mount.cifs however were "looser" in this regard. When
- * made setuid root, a user could run mount.cifs directly and mount any share
- * on a directory owned by that user.
- *
- * The legacy behavior is now disabled by default. To reenable it, set the
- * following #define to true.
+ * because of users and distributions installing it as a setuid root program
+ * before it had been audited for security holes. The default behavior is
+ * now to allow mount.cifs to be run as a setuid root program. Some admins
+ * may want to disable this fully, so this switch remains in place.
*/
-#define CIFS_LEGACY_SETUID_CHECK 0
+#define CIFS_DISABLE_SETUID_CAPABILITY 0
/*
* When an unprivileged user runs a setuid mount.cifs, we set certain mount
*/
#define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
-const char *thisprogram;
-int verboseflag = 0;
-int fakemnt = 0;
-static int got_password = 0;
-static int got_user = 0;
-static int got_domain = 0;
-static int got_ip = 0;
-static int got_unc = 0;
-static int got_uid = 0;
-static int got_gid = 0;
-static char * user_name = NULL;
-static char * mountpassword = NULL;
-char * domain_name = NULL;
-char * prefixpath = NULL;
-const char *cifs_fstype = "cifs";
-
/*
- * If an unprivileged user is doing the mounting then we need to ensure
- * that the entry is in /etc/fstab.
+ * Values for parsing a credentials file.
*/
-static int
-check_mountpoint(const char *progname, char *mountpoint)
-{
- int err;
- struct stat statbuf;
-
- /* does mountpoint exist and is it a directory? */
- err = stat(".", &statbuf);
- if (err) {
- fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
- mountpoint, strerror(errno));
- return EX_USAGE;
- }
+#define CRED_UNPARSEABLE 0
+#define CRED_USER 1
+#define CRED_PASS 2
+#define CRED_DOM 4
- if (!S_ISDIR(statbuf.st_mode)) {
- fprintf(stderr, "%s: %s is not a directory!", progname,
- mountpoint);
- return EX_USAGE;
- }
+/*
+ * Values for parsing command line options.
+ */
+#define OPT_ERROR -1
+#define OPT_IGNORE 0
+#define OPT_USERS 1
+#define OPT_USER 2
+#define OPT_USER_XATTR 3
+#define OPT_PASS 4
+#define OPT_SEC 5
+#define OPT_IP 6
+#define OPT_UNC 7
+#define OPT_CRED 8
+#define OPT_UID 9
+#define OPT_GID 10
+#define OPT_FMASK 11
+#define OPT_FILE_MODE 12
+#define OPT_DMASK 13
+#define OPT_DIR_MODE 14
+#define OPT_DOM 15
+#define OPT_NO_SUID 16
+#define OPT_SUID 17
+#define OPT_NO_DEV 18
+#define OPT_DEV 19
+#define OPT_NO_LOCK 20
+#define OPT_NO_EXEC 21
+#define OPT_EXEC 22
+#define OPT_GUEST 23
+#define OPT_RO 24
+#define OPT_RW 25
+#define OPT_REMOUNT 26
+#define OPT_MAND 27
+#define OPT_NOMAND 28
+#define OPT_CRUID 29
+#define OPT_BKUPUID 30
+#define OPT_BKUPGID 31
+#define OPT_NOFAIL 32
+
+#define MNT_TMP_FILE "/.mtab.cifs.XXXXXX"
+
+/* struct for holding parsed mount info for use by privleged process */
+struct parsed_mount_info {
+ unsigned long flags;
+ char host[NI_MAXHOST + 1];
+ char share[MAX_SHARE_LEN + 1];
+ char prefix[PATH_MAX + 1];
+ char options[MAX_OPTIONS_LEN];
+ char domain[MAX_DOMAIN_SIZE + 1];
+ char username[MAX_USERNAME_SIZE + 1];
+ char password[MOUNT_PASSWD_SIZE + 1];
+ char addrlist[MAX_ADDR_LIST_LEN];
+ unsigned int got_user:1;
+ unsigned int got_password:1;
+ unsigned int fakemnt:1;
+ unsigned int nomtab:1;
+ unsigned int verboseflag:1;
+ unsigned int nofail:1;
+};
-#if CIFS_LEGACY_SETUID_CHECK
- /* do extra checks on mountpoint for legacy setuid behavior */
- if (!getuid() || geteuid())
- return 0;
+const char *thisprogram;
+const char *cifs_fstype = "cifs";
- if (statbuf.st_uid != getuid()) {
- fprintf(stderr, "%s: %s is not owned by user\n", progname,
- mountpoint);
- return EX_USAGE;
- }
+static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info);
- if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
- fprintf(stderr, "%s: invalid permissions on %s\n", progname,
- mountpoint);
+static int check_setuid(void)
+{
+ if (geteuid()) {
+ fprintf(stderr, "This program is not installed setuid root - "
+ " \"user\" CIFS mounts not supported.\n");
return EX_USAGE;
}
-#endif /* CIFS_LEGACY_SETUID_CHECK */
-
- return 0;
-}
-#if CIFS_DISABLE_SETUID_CHECK
-static int
-check_setuid(void)
-{
- return 0;
-}
-#else /* CIFS_DISABLE_SETUID_CHECK */
-static int
-check_setuid(void)
-{
+#if CIFS_DISABLE_SETUID_CAPABILITY
if (getuid() && !geteuid()) {
printf("This mount.cifs program has been built with the "
- "ability to run as a setuid root program disabled.\n"
- "mount.cifs has not been well audited for security "
- "holes. Therefore the Samba team does not recommend "
- "installing it as a setuid root program.\n");
- return 1;
+ "ability to run as a setuid root program disabled.\n");
+ return EX_USAGE;
}
+#endif /* CIFS_DISABLE_SETUID_CAPABILITY */
return 0;
}
-#endif /* CIFS_DISABLE_SETUID_CHECK */
-#if CIFS_LEGACY_SETUID_CHECK
static int
-check_fstab(const char *progname, char *mountpoint, char *devname,
- char **options)
-{
- return 0;
-}
-#else /* CIFS_LEGACY_SETUID_CHECK */
-static int
-check_fstab(const char *progname, char *mountpoint, char *devname,
+check_fstab(const char *progname, const char *mountpoint, const char *devname,
char **options)
{
FILE *fstab;
struct mntent *mnt;
/* make sure this mount is listed in /etc/fstab */
- fstab = setmntent(_PATH_FSTAB, "r");
+ fstab = setmntent(_PATH_MNTTAB, "r");
if (!fstab) {
- fprintf(stderr, "Couldn't open %s for reading!\n",
- _PATH_FSTAB);
+ fprintf(stderr, "Couldn't open %s for reading!\n", _PATH_MNTTAB);
return EX_FILEIO;
}
- while((mnt = getmntent(fstab))) {
+ while ((mnt = getmntent(fstab))) {
if (!strcmp(mountpoint, mnt->mnt_dir))
break;
}
if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
fprintf(stderr, "%s: permission denied: no match for "
- "%s found in %s\n", progname, mountpoint,
- _PATH_FSTAB);
+ "%s found in %s\n", progname, mountpoint, _PATH_MNTTAB);
return EX_USAGE;
}
*options = strdup(mnt->mnt_opts);
return 0;
}
-#endif /* CIFS_LEGACY_SETUID_CHECK */
/* BB finish BB
- cifs_umount
- open nofollow - avoid symlink exposure?
- get owner of dir see if matches self or if root
- call system(umount argv) etc.
-
-BB end finish BB */
-
-static char * check_for_domain(char **);
+ cifs_umount
+ open nofollow - avoid symlink exposure?
+ get owner of dir see if matches self or if root
+ call system(umount argv) etc.
+BB end finish BB */
-static void mount_cifs_usage(FILE *stream)
+static int mount_usage(FILE * stream)
{
- fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
+ fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n",
+ thisprogram);
fprintf(stream, "\nMount the remote target, specified as a UNC name,");
fprintf(stream, " to a local directory.\n\nOptions:\n");
fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
fprintf(stream, "\nLess commonly used options:");
- fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
- fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
- fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
- fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
- fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
- fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
- fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
+ fprintf(stream,
+ "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
+ fprintf(stream,
+ "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
+ fprintf(stream,
+ "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
+ fprintf(stream,
+ "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign,fsc");
+ fprintf(stream,
+ "\n\nOptions not needed for servers supporting CIFS Unix extensions");
+ fprintf(stream,
+ "\n\t(e.g. unneeded for mounts to most Samba versions):");
+ fprintf(stream,
+ "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
fprintf(stream, "\n\nRarely used options:");
- fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
- fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
- fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
- fprintf(stream, "\n\nOptions are described in more detail in the manual page");
+ fprintf(stream,
+ "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
+ fprintf(stream,
+ "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
+ fprintf(stream,
+ "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
+ fprintf(stream,
+ "\n\nOptions are described in more detail in the manual page");
fprintf(stream, "\n\tman 8 mount.cifs\n");
fprintf(stream, "\nTo display the version number of the mount helper:");
- fprintf(stream, "\n\t%s -V\n",thisprogram);
-
- SAFE_FREE(mountpassword);
+ fprintf(stream, "\n\t%s -V\n", thisprogram);
if (stream == stderr)
- exit(EX_USAGE);
- exit(0);
+ return EX_USAGE;
+ return 0;
+}
+
+/*
+ * CIFS has to "escape" commas in the password field so that they don't
+ * end up getting confused for option delimiters. Copy password into pw
+ * field, turning any commas into double commas.
+ */
+static int set_password(struct parsed_mount_info *parsed_info, const char *src)
+{
+ char *dst = parsed_info->password;
+ unsigned int i = 0, j = 0;
+
+ while (src[i]) {
+ if (src[i] == ',')
+ dst[j++] = ',';
+ dst[j++] = src[i++];
+ if (j > sizeof(parsed_info->password)) {
+ fprintf(stderr, "Converted password too long!\n");
+ return EX_USAGE;
+ }
+ }
+ dst[j] = '\0';
+ parsed_info->got_password = 1;
+ return 0;
}
-/* caller frees username if necessary */
-static char * getusername(void) {
- char *username = NULL;
- struct passwd *password = getpwuid(getuid());
+/*
+ * Parse a username string into parsed_mount_info fields. The format is:
+ *
+ * DOMAIN\username%password
+ *
+ * ...obviously the only required component is "username". The source string
+ * is modified in the process, but it should remain unchanged at the end.
+ *
+ * NOTE: the above syntax does not allow for usernames that have slashes in
+ * them, as some krb5 usernames do. Support for the above syntax will be
+ * removed in a later version of cifs-utils. Users should use separate options
+ * instead of overloading this info into the username.
+ */
+static int parse_username(char *rawuser, struct parsed_mount_info *parsed_info)
+{
+ char *user, *password, slash;
+ int rc = 0;
+ bool warn = false;
+ /* everything after first % sign is a password */
+ password = strchr(rawuser, '%');
if (password) {
- username = password->pw_name;
+ warn = true;
+ rc = set_password(parsed_info, password + 1);
+ if (rc)
+ return rc;
+ *password = '\0';
+ }
+
+ /* everything after first '/' or '\' is a username */
+ user = strchr(rawuser, '/');
+ if (!user)
+ user = strchr(rawuser, '\\');
+
+ /* everything before that slash is a domain */
+ if (user) {
+ warn = true;
+ slash = *user;
+ *user = '\0';
+ strlcpy(parsed_info->domain, rawuser,
+ sizeof(parsed_info->domain));
+ *(user++) = slash;
+ } else {
+ user = rawuser;
+ }
+
+ strlcpy(parsed_info->username, user, sizeof(parsed_info->username));
+ parsed_info->got_user = 1;
+ if (password)
+ *password = '%';
+
+ if (warn)
+ fprintf(stderr, "WARNING: The DOMAIN/username%%password syntax "
+ "for usernames is deprecated and will be "
+ "removed in version 5.9 of cifs-utils.\n");
+
+ return 0;
+}
+
+#ifdef HAVE_LIBCAP_NG
+static int
+drop_capabilities(int parent)
+{
+ capng_setpid(getpid());
+ capng_clear(CAPNG_SELECT_BOTH);
+ if (parent) {
+ if (capng_updatev(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH, CAP_DAC_OVERRIDE, -1)) {
+ fprintf(stderr, "Unable to update capability set.\n");
+ return EX_SYSERR;
+ }
+ if (capng_update(CAPNG_ADD, CAPNG_PERMITTED|CAPNG_EFFECTIVE, CAP_SYS_ADMIN)) {
+ fprintf(stderr, "Unable to update capability set.\n");
+ return EX_SYSERR;
+ }
+ } else {
+ if (capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH)) {
+ fprintf(stderr, "Unable to update capability set.\n");
+ return EX_SYSERR;
+ }
+ }
+ if (capng_apply(CAPNG_SELECT_BOTH)) {
+ fprintf(stderr, "Unable to apply new capability set.\n");
+ return EX_SYSERR;
}
- return username;
+ return 0;
}
-static int open_cred_file(char * file_name)
+static int
+toggle_dac_capability(int writable, int enable)
{
- char * line_buf;
- char * temp_val;
- FILE * fs;
- int i, length;
+ unsigned int capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
- i = access(file_name, R_OK);
+ if (capng_update(enable ? CAPNG_ADD : CAPNG_DROP, CAPNG_EFFECTIVE, capability)) {
+ fprintf(stderr, "Unable to update capability set.\n");
+ return EX_SYSERR;
+ }
+ if (capng_apply(CAPNG_SELECT_CAPS)) {
+ fprintf(stderr, "Unable to apply new capability set.\n");
+ return EX_SYSERR;
+ }
+ return 0;
+}
+#else /* HAVE_LIBCAP_NG */
+#ifdef HAVE_LIBCAP
+#ifdef HAVE_PRCTL
+static int
+prune_bounding_set(void)
+{
+ int i, rc = 0;
+ static int bounding_set_cleared;
+
+ if (bounding_set_cleared)
+ return 0;
+
+ for (i = 0; i <= CAP_LAST_CAP && rc == 0; ++i)
+ rc = prctl(PR_CAPBSET_DROP, i);
+
+ if (rc != 0) {
+ fprintf(stderr, "Unable to clear capability bounding set: %d\n", rc);
+ return EX_SYSERR;
+ }
+
+ ++bounding_set_cleared;
+ return 0;
+}
+#else /* HAVE_PRCTL */
+static int
+prune_bounding_set(void)
+{
+ return 0;
+}
+#endif /* HAVE_PRCTL */
+static int
+drop_capabilities(int parent)
+{
+ int rc, ncaps;
+ cap_t caps;
+ cap_value_t cap_list[3];
+
+ rc = prune_bounding_set();
+ if (rc)
+ return rc;
+
+ caps = cap_get_proc();
+ if (caps == NULL) {
+ fprintf(stderr, "Unable to get current capability set: %s\n",
+ strerror(errno));
+ return EX_SYSERR;
+ }
+
+ if (cap_clear(caps) == -1) {
+ fprintf(stderr, "Unable to clear capability set: %s\n",
+ strerror(errno));
+ rc = EX_SYSERR;
+ goto free_caps;
+ }
+
+ if (parent || getuid() == 0) {
+ ncaps = 1;
+ cap_list[0] = CAP_DAC_READ_SEARCH;
+ if (parent) {
+ cap_list[1] = CAP_DAC_OVERRIDE;
+ cap_list[2] = CAP_SYS_ADMIN;
+ ncaps += 2;
+ }
+ if (cap_set_flag(caps, CAP_PERMITTED, ncaps, cap_list, CAP_SET) == -1) {
+ fprintf(stderr, "Unable to set permitted capabilities: %s\n",
+ strerror(errno));
+ rc = EX_SYSERR;
+ goto free_caps;
+ }
+ if (parent) {
+ cap_list[0] = CAP_SYS_ADMIN;
+ if (cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET) == -1) {
+ fprintf(stderr, "Unable to set effective capabilities: %s\n",
+ strerror(errno));
+ rc = EX_SYSERR;
+ goto free_caps;
+ }
+ }
+ }
+
+ if (cap_set_proc(caps) != 0) {
+ fprintf(stderr, "Unable to set current process capabilities: %s\n",
+ strerror(errno));
+ rc = EX_SYSERR;
+ }
+free_caps:
+ cap_free(caps);
+ return rc;
+}
+
+static int
+toggle_dac_capability(int writable, int enable)
+{
+ int rc = 0;
+ cap_t caps;
+ cap_value_t capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
+
+ caps = cap_get_proc();
+ if (caps == NULL) {
+ fprintf(stderr, "Unable to get current capability set: %s\n",
+ strerror(errno));
+ return EX_SYSERR;
+ }
+
+ if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &capability,
+ enable ? CAP_SET : CAP_CLEAR) == -1) {
+ fprintf(stderr, "Unable to %s effective capabilities: %s\n",
+ enable ? "set" : "clear", strerror(errno));
+ rc = EX_SYSERR;
+ goto free_caps;
+ }
+
+ if (cap_set_proc(caps) != 0) {
+ fprintf(stderr, "Unable to set current process capabilities: %s\n",
+ strerror(errno));
+ rc = EX_SYSERR;
+ }
+free_caps:
+ cap_free(caps);
+ return rc;
+}
+#else /* HAVE_LIBCAP */
+static int
+drop_capabilities(int parent __attribute((unused)))
+{
+ return 0;
+}
+
+static int
+toggle_dac_capability(int writable __attribute((unused)), int enable __attribute((unused)))
+{
+ return 0;
+}
+#endif /* HAVE_LIBCAP */
+#endif /* HAVE_LIBCAP_NG */
+
+static void null_terminate_endl(char *source)
+{
+ char *newline = strchr(source, '\n');
+ if (newline)
+ *newline = '\0';
+}
+
+/*
+ * Parse a line from the credentials file. Changes target to first
+ * character after '=' on 'line' and returns the value type of the line
+ * Returns CRED_UNPARSEABLE on failure or if either parameter is NULL.
+ */
+static int parse_cred_line(char *line, char **target)
+{
+ if (line == NULL || target == NULL)
+ goto parsing_err;
+
+ /* position target at first char of value */
+ *target = strchr(line, '=');
+ if (!*target)
+ goto parsing_err;
+ *target += 1;
+
+ /* tell the caller which value target points to */
+ if (strncasecmp("user", line, 4) == 0)
+ return CRED_USER;
+ if (strncasecmp("pass", line, 4) == 0)
+ return CRED_PASS;
+ if (strncasecmp("dom", line, 3) == 0)
+ return CRED_DOM;
+
+parsing_err:
+ return CRED_UNPARSEABLE;
+}
+
+static int open_cred_file(char *file_name,
+ struct parsed_mount_info *parsed_info,
+ char **saved_username)
+{
+ char *line_buf = NULL;
+ char *temp_val = NULL;
+ FILE *fs = NULL;
+ int i;
+ const int line_buf_size = 4096;
+ const int min_non_white = 10;
+
+ i = toggle_dac_capability(0, 1);
if (i)
- return i;
+ goto return_i;
- fs = fopen(file_name,"r");
- if(fs == NULL)
- return errno;
- line_buf = (char *)malloc(4096);
- if(line_buf == NULL) {
- fclose(fs);
- return ENOMEM;
+ i = access(file_name, R_OK);
+ if (i) {
+ toggle_dac_capability(0, 0);
+ i = errno;
+ goto return_i;
+ }
+
+ fs = fopen(file_name, "r");
+ if (fs == NULL) {
+ toggle_dac_capability(0, 0);
+ i = errno;
+ goto return_i;
}
- while(fgets(line_buf,4096,fs)) {
- /* parse line from credential file */
+ i = toggle_dac_capability(0, 0);
+ if (i)
+ goto return_i;
+
+ line_buf = (char *)malloc(line_buf_size);
+ if (line_buf == NULL) {
+ i = EX_SYSERR;
+ goto return_i;
+ }
+ /* parse line from credentials file */
+ while (fgets(line_buf, line_buf_size, fs)) {
/* eat leading white space */
- for(i=0;i<4086;i++) {
- if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
+ for (i = 0; i < line_buf_size - min_non_white + 1; i++) {
+ if ((line_buf[i] != ' ') && (line_buf[i] != '\t'))
break;
- /* if whitespace - skip past it */
}
- if (strncasecmp("username",line_buf+i,8) == 0) {
- temp_val = strchr(line_buf + i,'=');
- if(temp_val) {
- /* go past equals sign */
- temp_val++;
- for(length = 0;length<4087;length++) {
- if ((temp_val[length] == '\n')
- || (temp_val[length] == '\0')) {
- temp_val[length] = '\0';
- break;
- }
- }
- if(length > 4086) {
- fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
- memset(line_buf,0,4096);
- exit(EX_USAGE);
- } else {
- got_user = 1;
- user_name = (char *)calloc(1 + length,1);
- /* BB adding free of user_name string before exit,
- not really necessary but would be cleaner */
- strlcpy(user_name,temp_val, length+1);
- }
- }
- } else if (strncasecmp("password",line_buf+i,8) == 0) {
- temp_val = strchr(line_buf+i,'=');
- if(temp_val) {
- /* go past equals sign */
- temp_val++;
- for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
- if ((temp_val[length] == '\n')
- || (temp_val[length] == '\0')) {
- temp_val[length] = '\0';
- break;
- }
- }
- if(length > MOUNT_PASSWD_SIZE) {
- fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
- memset(line_buf,0, 4096);
- exit(EX_USAGE);
- } else {
- if(mountpassword == NULL) {
- mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
- } else
- memset(mountpassword,0,MOUNT_PASSWD_SIZE);
- if(mountpassword) {
- strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
- got_password = 1;
- }
- }
+ null_terminate_endl(line_buf);
+
+ /* parse next token */
+ switch (parse_cred_line(line_buf + i, &temp_val)) {
+ case CRED_USER:
+ *saved_username = strdup(temp_val);
+ if (!*saved_username) {
+ i = EX_SYSERR;
+ goto return_i;
}
- } else if (strncasecmp("domain",line_buf+i,6) == 0) {
- temp_val = strchr(line_buf+i,'=');
- if(temp_val) {
- /* go past equals sign */
- temp_val++;
- if(verboseflag)
- fprintf(stderr, "\nDomain %s\n",temp_val);
- for(length = 0;length<DOMAIN_SIZE+1;length++) {
- if ((temp_val[length] == '\n')
- || (temp_val[length] == '\0')) {
- temp_val[length] = '\0';
- break;
- }
- }
- if(length > DOMAIN_SIZE) {
- fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
- exit(EX_USAGE);
- } else {
- if(domain_name == NULL) {
- domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
- } else
- memset(domain_name,0,DOMAIN_SIZE);
- if(domain_name) {
- strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
- got_domain = 1;
- }
- }
- }
- }
-
- }
- fclose(fs);
+ break;
+ case CRED_PASS:
+ i = set_password(parsed_info, temp_val);
+ if (i)
+ goto return_i;
+ break;
+ case CRED_DOM:
+ if (parsed_info->verboseflag)
+ fprintf(stderr, "domain=%s\n",
+ temp_val);
+ strlcpy(parsed_info->domain, temp_val,
+ sizeof(parsed_info->domain));
+ break;
+ case CRED_UNPARSEABLE:
+ if (parsed_info->verboseflag)
+ fprintf(stderr, "Credential formatted "
+ "incorrectly: %s\n",
+ temp_val ? temp_val : "(null)");
+ break;
+ }
+ }
+ i = 0;
+return_i:
+ if (fs != NULL)
+ fclose(fs);
+
+ /* make sure passwords are scrubbed from memory */
+ if (line_buf != NULL)
+ memset(line_buf, 0, line_buf_size);
SAFE_FREE(line_buf);
- return 0;
+ return i;
}
-static int get_password_from_file(int file_descript, char * filename)
+static int
+get_password_from_file(int file_descript, char *filename,
+ struct parsed_mount_info *parsed_info)
{
int rc = 0;
- int i;
- char c;
-
- if(mountpassword == NULL)
- mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
- else
- memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
+ char buf[sizeof(parsed_info->password) + 1];
- if (mountpassword == NULL) {
- fprintf(stderr, "malloc failed\n");
- exit(EX_SYSERR);
- }
+ if (filename != NULL) {
+ rc = toggle_dac_capability(0, 1);
+ if (rc)
+ return rc;
- if(filename != NULL) {
rc = access(filename, R_OK);
if (rc) {
- fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
- filename, strerror(errno));
- exit(EX_SYSERR);
+ fprintf(stderr,
+ "mount.cifs failed: access check of %s failed: %s\n",
+ filename, strerror(errno));
+ toggle_dac_capability(0, 0);
+ return EX_SYSERR;
}
+
file_descript = open(filename, O_RDONLY);
- if(file_descript < 0) {
- fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
- strerror(errno),filename);
- exit(EX_SYSERR);
+ if (file_descript < 0) {
+ fprintf(stderr,
+ "mount.cifs failed. %s attempting to open password file %s\n",
+ strerror(errno), filename);
+ toggle_dac_capability(0, 0);
+ return EX_SYSERR;
}
- }
- /* else file already open and fd provided */
-
- for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
- rc = read(file_descript,&c,1);
- if(rc < 0) {
- fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
- if(filename != NULL)
- close(file_descript);
- exit(EX_SYSERR);
- } else if(rc == 0) {
- if(mountpassword[0] == 0) {
- if(verboseflag)
- fprintf(stderr, "\nWarning: null password used since cifs password file empty");
- }
- break;
- } else /* read valid character */ {
- if((c == 0) || (c == '\n')) {
- mountpassword[i] = '\0';
- break;
- } else
- mountpassword[i] = c;
+
+ rc = toggle_dac_capability(0, 0);
+ if (rc) {
+ rc = EX_SYSERR;
+ goto get_pw_exit;
}
}
- if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
- fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
- MOUNT_PASSWD_SIZE);
- }
- got_password = 1;
- if(filename != NULL) {
- close(file_descript);
+
+ memset(buf, 0, sizeof(buf));
+ rc = read(file_descript, buf, sizeof(buf) - 1);
+ if (rc < 0) {
+ fprintf(stderr,
+ "mount.cifs failed. Error %s reading password file\n",
+ strerror(errno));
+ rc = EX_SYSERR;
+ goto get_pw_exit;
}
+ rc = set_password(parsed_info, buf);
+
+get_pw_exit:
+ if (filename != NULL)
+ close(file_descript);
return rc;
}
-static int parse_options(char ** optionsp, unsigned long * filesys_flags)
+/*
+ * Returns OPT_ERROR on unparsable token.
+ */
+static int parse_opt_token(const char *token)
+{
+ if (token == NULL)
+ return OPT_ERROR;
+
+ if (strncmp(token, "users", 5) == 0)
+ return OPT_USERS;
+ if (strncmp(token, "user_xattr", 10) == 0)
+ return OPT_USER_XATTR;
+ if (strncmp(token, "user", 4) == 0)
+ return OPT_USER;
+ if (strncmp(token, "pass", 4) == 0)
+ return OPT_PASS;
+ if (strncmp(token, "sec", 3) == 0)
+ return OPT_SEC;
+ if (strncmp(token, "ip", 2) == 0)
+ return OPT_IP;
+ if (strncmp(token, "unc", 3) == 0 ||
+ strncmp(token, "target", 6) == 0 ||
+ strncmp(token, "path", 4) == 0)
+ return OPT_UNC;
+ if (strncmp(token, "dom", 3) == 0 || strncmp(token, "workg", 5) == 0)
+ return OPT_DOM;
+ if (strncmp(token, "cred", 4) == 0)
+ return OPT_CRED;
+ if (strncmp(token, "uid", 3) == 0)
+ return OPT_UID;
+ if (strncmp(token, "cruid", 5) == 0)
+ return OPT_CRUID;
+ if (strncmp(token, "gid", 3) == 0)
+ return OPT_GID;
+ if (strncmp(token, "fmask", 5) == 0)
+ return OPT_FMASK;
+ if (strncmp(token, "file_mode", 9) == 0)
+ return OPT_FILE_MODE;
+ if (strncmp(token, "dmask", 5) == 0)
+ return OPT_DMASK;
+ if (strncmp(token, "dir_mode", 4) == 0 || strncmp(token, "dirm", 4) == 0)
+ return OPT_DIR_MODE;
+ if (strncmp(token, "nosuid", 6) == 0)
+ return OPT_NO_SUID;
+ if (strncmp(token, "suid", 4) == 0)
+ return OPT_SUID;
+ if (strncmp(token, "nodev", 5) == 0)
+ return OPT_NO_DEV;
+ if (strncmp(token, "nobrl", 5) == 0 || strncmp(token, "nolock", 6) == 0)
+ return OPT_NO_LOCK;
+ if (strncmp(token, "mand", 4) == 0)
+ return OPT_MAND;
+ if (strncmp(token, "nomand", 6) == 0)
+ return OPT_NOMAND;
+ if (strncmp(token, "dev", 3) == 0)
+ return OPT_DEV;
+ if (strncmp(token, "noexec", 6) == 0)
+ return OPT_NO_EXEC;
+ if (strncmp(token, "exec", 4) == 0)
+ return OPT_EXEC;
+ if (strncmp(token, "guest", 5) == 0)
+ return OPT_GUEST;
+ if (strncmp(token, "ro", 2) == 0)
+ return OPT_RO;
+ if (strncmp(token, "rw", 2) == 0 && strlen(token) == 2)
+ return OPT_RW;
+ if (strncmp(token, "remount", 7) == 0)
+ return OPT_REMOUNT;
+ if (strncmp(token, "_netdev", 7) == 0)
+ return OPT_IGNORE;
+ if (strncmp(token, "backupuid", 9) == 0)
+ return OPT_BKUPUID;
+ if (strncmp(token, "backupgid", 9) == 0)
+ return OPT_BKUPGID;
+ if (strncmp(token, "nofail", 6) == 0)
+ return OPT_NOFAIL;
+
+ return OPT_ERROR;
+}
+
+static int
+parse_options(const char *data, struct parsed_mount_info *parsed_info)
{
- const char * data;
- char * percent_char = NULL;
- char * value = NULL;
- char * next_keyword = NULL;
- char * out = NULL;
+ char *value = NULL;
+ char *equals = NULL;
+ char *next_keyword = NULL;
+ char *out = parsed_info->options;
+ unsigned long *filesys_flags = &parsed_info->flags;
int out_len = 0;
int word_len;
int rc = 0;
- char user[32];
- char group[32];
+ int got_bkupuid = 0;
+ int got_bkupgid = 0;
+ int got_uid = 0;
+ int got_cruid = 0;
+ int got_gid = 0;
+ uid_t uid, cruid = 0, bkupuid = 0;
+ gid_t gid, bkupgid = 0;
+ char *ep;
+ struct passwd *pw;
+ struct group *gr;
+ char *saved_username = NULL;
+ bool krb5_auth = false;
+ /*
+ * max 32-bit uint in decimal is 4294967295 which is 10 chars wide
+ * +1 for NULL, and +1 for good measure
+ */
+ char txtbuf[12];
- if (!optionsp || !*optionsp)
- return 1;
- data = *optionsp;
+ /* make sure we're starting from beginning */
+ out[0] = '\0';
/* BB fixme check for separator override BB */
-
- if (getuid()) {
+ uid = getuid();
+ if (uid != 0)
got_uid = 1;
- snprintf(user,sizeof(user),"%u",getuid());
+
+ gid = getgid();
+ if (gid != 0)
got_gid = 1;
- snprintf(group,sizeof(group),"%u",getgid());
- }
-/* while ((data = strsep(&options, ",")) != NULL) { */
- while(data != NULL) {
- /* check if ends with trailing comma */
- if(*data == 0)
- break;
+ if (!data)
+ return EX_USAGE;
- /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
- /* data = next keyword */
- /* value = next value ie stuff after equal sign */
+ /*
+ * format is keyword,keyword2=value2,keyword3=value3...
+ * data = next keyword
+ * value = next value ie stuff after equal sign
+ */
+ while (data && *data) {
+ next_keyword = strchr(data, ','); /* BB handle sep= */
- next_keyword = strchr(data,','); /* BB handle sep= */
-
/* temporarily null terminate end of keyword=value pair */
- if(next_keyword)
+ if (next_keyword)
*next_keyword++ = 0;
- /* temporarily null terminate keyword to make keyword and value distinct */
- if ((value = strchr(data, '=')) != NULL) {
- *value = '\0';
- value++;
+ /* temporarily null terminate keyword if there's a value */
+ value = NULL;
+ if ((equals = strchr(data, '=')) != NULL) {
+ *equals = '\0';
+ value = equals + 1;
}
- if (strncmp(data, "users",5) == 0) {
- if(!value || !*value) {
+ switch(parse_opt_token(data)) {
+ case OPT_USERS:
+ if (!value || !*value) {
*filesys_flags |= MS_USERS;
goto nocopy;
}
- } else if (strncmp(data, "user_xattr",10) == 0) {
- /* do nothing - need to skip so not parsed as user name */
- } else if (strncmp(data, "user", 4) == 0) {
+ break;
+ case OPT_USER:
if (!value || !*value) {
- if(data[4] == '\0') {
+ if (data[4] == '\0') {
*filesys_flags |= MS_USER;
goto nocopy;
} else {
- fprintf(stderr, "username specified with no parameter\n");
- SAFE_FREE(out);
- return 1; /* needs_arg; */
+ fprintf(stderr,
+ "username specified with no parameter\n");
+ return EX_USAGE;
}
} else {
- if (strnlen(value, 260) < 260) {
- got_user=1;
- percent_char = strchr(value,'%');
- if(percent_char) {
- *percent_char = ',';
- if(mountpassword == NULL)
- mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
- if(mountpassword) {
- if(got_password)
- fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
- got_password = 1;
- percent_char++;
- strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
- /* remove password from username */
- while(*percent_char != 0) {
- *percent_char = ',';
- percent_char++;
- }
- }
- }
- /* this is only case in which the user
- name buf is not malloc - so we have to
- check for domain name embedded within
- the user name here since the later
- call to check_for_domain will not be
- invoked */
- domain_name = check_for_domain(&value);
- } else {
+ /* domain/username%password + NULL term. */
+ const size_t max = MAX_DOMAIN_SIZE +
+ MAX_USERNAME_SIZE +
+ MOUNT_PASSWD_SIZE + 2 + 1;
+ if (strnlen(value, max) >= max) {
fprintf(stderr, "username too long\n");
- SAFE_FREE(out);
- return 1;
+ return EX_USAGE;
}
+ saved_username = strdup(value);
+ if (!saved_username) {
+ fprintf(stderr, "Unable to allocate memory!\n");
+ return EX_SYSERR;
+ }
+ goto nocopy;
+ }
+
+ case OPT_PASS:
+ if (parsed_info->got_password) {
+ fprintf(stderr,
+ "password specified twice, ignoring second\n");
+ goto nocopy;
}
- } else if (strncmp(data, "pass", 4) == 0) {
if (!value || !*value) {
- if(got_password) {
- fprintf(stderr, "\npassword specified twice, ignoring second\n");
- } else
- got_password = 1;
- } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
- if (got_password) {
- fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
- } else {
- mountpassword = strndup(value, MOUNT_PASSWD_SIZE);
- if (!mountpassword) {
- fprintf(stderr, "mount.cifs error: %s", strerror(ENOMEM));
- SAFE_FREE(out);
- return 1;
- }
- got_password = 1;
- }
- } else {
- fprintf(stderr, "password too long\n");
- SAFE_FREE(out);
- return 1;
+ parsed_info->got_password = 1;
+ goto nocopy;
}
+ rc = set_password(parsed_info, value);
+ if (rc)
+ return rc;
goto nocopy;
- } else if (strncmp(data, "sec", 3) == 0) {
+
+ case OPT_SEC:
if (value) {
- if (!strncmp(value, "none", 4) ||
- !strncmp(value, "krb5", 4))
- got_password = 1;
+ if (!strncmp(value, "none", 4)) {
+ parsed_info->got_password = 1;
+ } else if (!strncmp(value, "krb5", 4)) {
+ parsed_info->got_password = 1;
+ krb5_auth = true;
+ }
}
- } else if (strncmp(data, "ip", 2) == 0) {
+ break;
+
+ case OPT_IP:
if (!value || !*value) {
- fprintf(stderr, "target ip address argument missing");
- } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
- if(verboseflag)
- fprintf(stderr, "ip address %s override specified\n",value);
- got_ip = 1;
+ fprintf(stderr,
+ "target ip address argument missing\n");
+ } else if (strnlen(value, MAX_ADDRESS_LEN) <=
+ MAX_ADDRESS_LEN) {
+ strcpy(parsed_info->addrlist, value);
+ if (parsed_info->verboseflag)
+ fprintf(stderr,
+ "ip address %s override specified\n",
+ value);
+ goto nocopy;
} else {
fprintf(stderr, "ip address too long\n");
- SAFE_FREE(out);
- return 1;
+ return EX_USAGE;
+
}
- } else if ((strncmp(data, "unc", 3) == 0)
- || (strncmp(data, "target", 6) == 0)
- || (strncmp(data, "path", 4) == 0)) {
+ break;
+
+ /* unc || target || path */
+ case OPT_UNC:
if (!value || !*value) {
- fprintf(stderr, "invalid path to network resource\n");
- SAFE_FREE(out);
- return 1; /* needs_arg; */
- } else if(strnlen(value,5) < 5) {
- fprintf(stderr, "UNC name too short");
+ fprintf(stderr,
+ "invalid path to network resource\n");
+ return EX_USAGE;
}
+ rc = parse_unc(value, parsed_info);
+ if (rc)
+ return rc;
+ break;
- if (strnlen(value, 300) < 300) {
- got_unc = 1;
- if (strncmp(value, "//", 2) == 0) {
- if(got_unc)
- fprintf(stderr, "unc name specified twice, ignoring second\n");
- else
- got_unc = 1;
- } else if (strncmp(value, "\\\\", 2) != 0) {
- fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
- SAFE_FREE(out);
- return 1;
- } else {
- if(got_unc)
- fprintf(stderr, "unc name specified twice, ignoring second\n");
- else
- got_unc = 1;
- }
- } else {
- fprintf(stderr, "CIFS: UNC name too long\n");
- SAFE_FREE(out);
- return 1;
- }
- } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
- || (strncmp(data, "workg", 5) == 0)) {
- /* note this allows for synonyms of "domain"
- such as "DOM" and "dom" and "workgroup"
- and "WORKGRP" etc. */
+ /* dom || workgroup */
+ case OPT_DOM:
if (!value || !*value) {
fprintf(stderr, "CIFS: invalid domain name\n");
- SAFE_FREE(out);
- return 1; /* needs_arg; */
+ return EX_USAGE;
}
- if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
- got_domain = 1;
- } else {
+ if (strnlen(value, sizeof(parsed_info->domain)) >=
+ sizeof(parsed_info->domain)) {
fprintf(stderr, "domain name too long\n");
- SAFE_FREE(out);
- return 1;
+ return EX_USAGE;
}
- } else if (strncmp(data, "cred", 4) == 0) {
- if (value && *value) {
- rc = open_cred_file(value);
- if(rc) {
- fprintf(stderr, "error %d (%s) opening credential file %s\n",
- rc, strerror(rc), value);
- SAFE_FREE(out);
- return 1;
- }
- } else {
- fprintf(stderr, "invalid credential file name specified\n");
- SAFE_FREE(out);
- return 1;
+ strlcpy(parsed_info->domain, value,
+ sizeof(parsed_info->domain));
+ goto nocopy;
+
+ case OPT_CRED:
+ if (!value || !*value) {
+ fprintf(stderr,
+ "invalid credential file name specified\n");
+ return EX_USAGE;
}
- } else if (strncmp(data, "uid", 3) == 0) {
- if (value && *value) {
- got_uid = 1;
- if (!isdigit(*value)) {
- struct passwd *pw;
-
- if (!(pw = getpwnam(value))) {
- fprintf(stderr, "bad user name \"%s\"\n", value);
- exit(EX_USAGE);
- }
- snprintf(user, sizeof(user), "%u", pw->pw_uid);
- } else {
- strlcpy(user,value,sizeof(user));
- }
+ rc = open_cred_file(value, parsed_info, &saved_username);
+ if (rc) {
+ fprintf(stderr,
+ "error %d (%s) opening credential file %s\n",
+ rc, strerror(rc), value);
+ return rc;
}
goto nocopy;
- } else if (strncmp(data, "gid", 3) == 0) {
- if (value && *value) {
- got_gid = 1;
- if (!isdigit(*value)) {
- struct group *gr;
-
- if (!(gr = getgrnam(value))) {
- fprintf(stderr, "bad group name \"%s\"\n", value);
- exit(EX_USAGE);
- }
- snprintf(group, sizeof(group), "%u", gr->gr_gid);
- } else {
- strlcpy(group,value,sizeof(group));
- }
+
+ case OPT_UID:
+ if (!value || !*value)
+ goto nocopy;
+
+ got_uid = 1;
+ errno = 0;
+ uid = strtoul(value, &ep, 10);
+ if (errno == 0 && *ep == '\0')
+ goto nocopy;
+
+ pw = getpwnam(value);
+ if (pw == NULL) {
+ fprintf(stderr, "bad user name \"%s\"\n", value);
+ return EX_USAGE;
}
+
+ uid = pw->pw_uid;
goto nocopy;
- /* fmask and dmask synonyms for people used to smbfs syntax */
- } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
- if (!value || !*value) {
- fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
- SAFE_FREE(out);
- return 1;
- }
- if (value[0] != '0') {
- fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
+ case OPT_CRUID:
+ if (!value || !*value)
+ goto nocopy;
+
+ got_cruid = 1;
+ errno = 0;
+ cruid = strtoul(value, &ep, 10);
+ if (errno == 0 && *ep == '\0')
+ goto nocopy;
+
+ pw = getpwnam(value);
+ if (pw == NULL) {
+ fprintf(stderr, "bad user name \"%s\"\n", value);
+ return EX_USAGE;
}
+ cruid = pw->pw_uid;
+ goto nocopy;
+
+ case OPT_GID:
+ if (!value || !*value)
+ goto nocopy;
- if (strcmp (data, "fmask") == 0) {
- fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
- data = "file_mode"; /* BB fix this */
+ got_gid = 1;
+ errno = 0;
+ gid = strtoul(value, &ep, 10);
+ if (errno == 0 && *ep == '\0')
+ goto nocopy;
+
+ gr = getgrnam(value);
+ if (gr == NULL) {
+ fprintf(stderr, "bad group name \"%s\"\n", value);
+ return EX_USAGE;
}
- } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
+
+ gid = gr->gr_gid;
+ goto nocopy;
+
+ /* fmask fall through to file_mode */
+ case OPT_FMASK:
+ fprintf(stderr,
+ "WARNING: CIFS mount option 'fmask' is\
+ deprecated. Use 'file_mode' instead.\n");
+ data = "file_mode"; /* BB fix this */
+ case OPT_FILE_MODE:
if (!value || !*value) {
- fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
- SAFE_FREE(out);
- return 1;
+ fprintf(stderr,
+ "Option '%s' requires a numerical argument\n",
+ data);
+ return EX_USAGE;
}
- if (value[0] != '0') {
- fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
- }
+ if (value[0] != '0')
+ fprintf(stderr,
+ "WARNING: '%s' not expressed in octal.\n",
+ data);
+ break;
- if (strcmp (data, "dmask") == 0) {
- fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
- data = "dir_mode";
+ /* dmask falls through to dir_mode */
+ case OPT_DMASK:
+ fprintf(stderr,
+ "WARNING: CIFS mount option 'dmask' is\
+ deprecated. Use 'dir_mode' instead.\n");
+ data = "dir_mode";
+ case OPT_DIR_MODE:
+ if (!value || !*value) {
+ fprintf(stderr,
+ "Option '%s' requires a numerical argument\n",
+ data);
+ return EX_USAGE;
}
- /* the following eight mount options should be
- stripped out from what is passed into the kernel
- since these eight options are best passed as the
- mount flags rather than redundantly to the kernel
- and could generate spurious warnings depending on the
- level of the corresponding cifs vfs kernel code */
- } else if (strncmp(data, "nosuid", 6) == 0) {
+
+ if (value[0] != '0')
+ fprintf(stderr,
+ "WARNING: '%s' not expressed in octal.\n",
+ data);
+ break;
+
+ /* the following mount options should be
+ stripped out from what is passed into the kernel
+ since these options are best passed as the
+ mount flags rather than redundantly to the kernel
+ and could generate spurious warnings depending on the
+ level of the corresponding cifs vfs kernel code */
+ case OPT_NO_SUID:
*filesys_flags |= MS_NOSUID;
- } else if (strncmp(data, "suid", 4) == 0) {
+ break;
+ case OPT_SUID:
*filesys_flags &= ~MS_NOSUID;
- } else if (strncmp(data, "nodev", 5) == 0) {
+ break;
+ case OPT_NO_DEV:
*filesys_flags |= MS_NODEV;
- } else if ((strncmp(data, "nobrl", 5) == 0) ||
- (strncmp(data, "nolock", 6) == 0)) {
+ break;
+ /* nolock || nobrl */
+ case OPT_NO_LOCK:
*filesys_flags &= ~MS_MANDLOCK;
- } else if (strncmp(data, "dev", 3) == 0) {
+ break;
+ case OPT_MAND:
+ *filesys_flags |= MS_MANDLOCK;
+ goto nocopy;
+ case OPT_NOMAND:
+ *filesys_flags &= ~MS_MANDLOCK;
+ goto nocopy;
+ case OPT_DEV:
*filesys_flags &= ~MS_NODEV;
- } else if (strncmp(data, "noexec", 6) == 0) {
+ break;
+ case OPT_NO_EXEC:
*filesys_flags |= MS_NOEXEC;
- } else if (strncmp(data, "exec", 4) == 0) {
+ break;
+ case OPT_EXEC:
*filesys_flags &= ~MS_NOEXEC;
- } else if (strncmp(data, "guest", 5) == 0) {
- user_name = (char *)calloc(1, 1);
- got_user = 1;
- got_password = 1;
- } else if (strncmp(data, "ro", 2) == 0) {
+ break;
+ case OPT_GUEST:
+ parsed_info->got_user = 1;
+ parsed_info->got_password = 1;
+ break;
+ case OPT_RO:
*filesys_flags |= MS_RDONLY;
goto nocopy;
- } else if (strncmp(data, "rw", 2) == 0) {
+ case OPT_RW:
*filesys_flags &= ~MS_RDONLY;
goto nocopy;
- } else if (strncmp(data, "remount", 7) == 0) {
- *filesys_flags |= MS_REMOUNT;
- } /* else if (strnicmp(data, "port", 4) == 0) {
- if (value && *value) {
- vol->port =
- simple_strtoul(value, &value, 0);
- }
- } else if (strnicmp(data, "rsize", 5) == 0) {
- if (value && *value) {
- vol->rsize =
- simple_strtoul(value, &value, 0);
+ case OPT_REMOUNT:
+ *filesys_flags |= MS_REMOUNT;
+ break;
+ case OPT_IGNORE:
+ goto nocopy;
+ case OPT_BKUPUID:
+ if (!value || !*value)
+ goto nocopy;
+
+ got_bkupuid = 1;
+ errno = 0;
+ bkupuid = strtoul(value, &ep, 10);
+ if (errno == 0 && *ep == '\0')
+ goto nocopy;
+
+ pw = getpwnam(value);
+ if (pw == NULL) {
+ fprintf(stderr,
+ "bad user name \"%s\"\n", value);
+ return EX_USAGE;
}
- } else if (strnicmp(data, "wsize", 5) == 0) {
- if (value && *value) {
- vol->wsize =
- simple_strtoul(value, &value, 0);
+
+ bkupuid = pw->pw_uid;
+ goto nocopy;
+ case OPT_BKUPGID:
+ if (!value || !*value)
+ goto nocopy;
+
+ got_bkupgid = 1;
+ errno = 0;
+ bkupgid = strtoul(value, &ep, 10);
+ if (errno == 0 && *ep == '\0')
+ goto nocopy;
+
+ gr = getgrnam(value);
+ if (gr == NULL) {
+ fprintf(stderr,
+ "bad group name \"%s\"\n", value);
+ return EX_USAGE;
}
- } else if (strnicmp(data, "version", 3) == 0) {
- } else {
- fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
- } */ /* nothing to do on those four mount options above.
- Just pass to kernel and ignore them here */
- /* Copy (possibly modified) option to out */
+ bkupgid = gr->gr_gid;
+ goto nocopy;
+ case OPT_NOFAIL:
+ parsed_info->nofail = 1;
+ goto nocopy;
+ }
+
+ /* check size before copying option to buffer */
word_len = strlen(data);
if (value)
word_len += 1 + strlen(value);
- out = (char *)realloc(out, out_len + word_len + 2);
- if (out == NULL) {
- perror("malloc");
- exit(EX_SYSERR);
+ /* need 2 extra bytes for comma and null byte */
+ if (out_len + word_len + 2 > MAX_OPTIONS_LEN) {
+ fprintf(stderr, "Options string too long\n");
+ return EX_USAGE;
}
- if (out_len) {
- strlcat(out, ",", out_len + word_len + 2);
- out_len++;
- }
+ /* put back equals sign, if any */
+ if (equals)
+ *equals = '=';
- if (value)
- snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
- else
- snprintf(out + out_len, word_len + 1, "%s", data);
- out_len = strlen(out);
+ /* go ahead and copy */
+ if (out_len)
+ strlcat(out, ",", MAX_OPTIONS_LEN);
+ strlcat(out, data, MAX_OPTIONS_LEN);
+ out_len = strlen(out);
nocopy:
data = next_keyword;
}
+ if (saved_username) {
+ if (krb5_auth) {
+ strlcpy(parsed_info->username, saved_username,
+ sizeof(parsed_info->username));
+ parsed_info->got_user = 1;
+ } else {
+ rc = parse_username(saved_username, parsed_info);
+ free(saved_username);
+ if (rc) {
+ fprintf(stderr, "Unable to parse username!\n");
+ return rc;
+ }
+ }
+ }
+
+
/* special-case the uid and gid */
if (got_uid) {
- word_len = strlen(user);
+ word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", uid);
- out = (char *)realloc(out, out_len + word_len + 6);
- if (out == NULL) {
- perror("malloc");
- exit(EX_SYSERR);
+ /* comma + "uid=" + terminating NULL == 6 */
+ if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
+ fprintf(stderr, "Options string too long\n");
+ return EX_USAGE;
}
if (out_len) {
- strlcat(out, ",", out_len + word_len + 6);
+ strlcat(out, ",", MAX_OPTIONS_LEN);
out_len++;
}
- snprintf(out + out_len, word_len + 5, "uid=%s", user);
+ snprintf(out + out_len, word_len + 5, "uid=%s", txtbuf);
out_len = strlen(out);
}
- if (got_gid) {
- word_len = strlen(group);
+ if (got_cruid) {
+ word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", cruid);
- out = (char *)realloc(out, out_len + 1 + word_len + 6);
- if (out == NULL) {
- perror("malloc");
- exit(EX_SYSERR);
+ /* comma + "cruid=" + terminating NULL == 6 */
+ if (out_len + word_len + 8 > MAX_OPTIONS_LEN) {
+ fprintf(stderr, "Options string too long\n");
+ return EX_USAGE;
}
if (out_len) {
- strlcat(out, ",", out_len + word_len + 6);
+ strlcat(out, ",", MAX_OPTIONS_LEN);
out_len++;
}
- snprintf(out + out_len, word_len + 5, "gid=%s", group);
+ snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf);
out_len = strlen(out);
}
+ if (got_gid) {
+ word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", gid);
- SAFE_FREE(*optionsp);
- *optionsp = out;
- return 0;
-}
-
-/* replace all (one or more) commas with double commas */
-static void check_for_comma(char ** ppasswrd)
-{
- char *new_pass_buf;
- char *pass;
- int i,j;
- int number_of_commas = 0;
- int len;
-
- if(ppasswrd == NULL)
- return;
- else
- (pass = *ppasswrd);
-
- len = strlen(pass);
-
- for(i=0;i<len;i++) {
- if(pass[i] == ',')
- number_of_commas++;
- }
-
- if(number_of_commas == 0)
- return;
- if(number_of_commas > MOUNT_PASSWD_SIZE) {
- /* would otherwise overflow the mount options buffer */
- fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
- return;
- }
-
- new_pass_buf = (char *)malloc(len+number_of_commas+1);
- if(new_pass_buf == NULL)
- return;
-
- for(i=0,j=0;i<len;i++,j++) {
- new_pass_buf[j] = pass[i];
- if(pass[i] == ',') {
- j++;
- new_pass_buf[j] = pass[i];
+ /* comma + "gid=" + terminating NULL == 6 */
+ if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
+ fprintf(stderr, "Options string too long\n");
+ return EX_USAGE;
}
- }
- new_pass_buf[len+number_of_commas] = 0;
- SAFE_FREE(*ppasswrd);
- *ppasswrd = new_pass_buf;
-
- return;
-}
-
-/* Usernames can not have backslash in them and we use
- [BB check if usernames can have forward slash in them BB]
- backslash as domain\user separator character
-*/
-static char * check_for_domain(char **ppuser)
-{
- char * original_string;
- char * usernm;
- char * domainnm;
- int original_len;
- int len;
- int i;
-
- if(ppuser == NULL)
- return NULL;
-
- original_string = *ppuser;
-
- if (original_string == NULL)
- return NULL;
-
- original_len = strlen(original_string);
-
- usernm = strchr(*ppuser,'/');
- if (usernm == NULL) {
- usernm = strchr(*ppuser,'\\');
- if (usernm == NULL)
- return NULL;
- }
-
- if(got_domain) {
- fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
- return NULL;
- }
-
- usernm[0] = 0;
- domainnm = *ppuser;
- if (domainnm[0] != 0) {
- got_domain = 1;
- } else {
- fprintf(stderr, "null domain\n");
+ if (out_len) {
+ strlcat(out, ",", MAX_OPTIONS_LEN);
+ out_len++;
+ }
+ snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf);
}
- len = strlen(domainnm);
- /* reset domainm to new buffer, and copy
- domain name into it */
- domainnm = (char *)malloc(len+1);
- if(domainnm == NULL)
- return NULL;
-
- strlcpy(domainnm,*ppuser,len+1);
-
-/* move_string(*ppuser, usernm+1) */
- len = strlen(usernm+1);
+ if (got_bkupuid) {
+ word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupuid);
- if(len >= original_len) {
- /* should not happen */
- return domainnm;
- }
+ /* comma + "backupuid=" + terminating NULL == 12 */
+ if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
+ fprintf(stderr, "Options string too long\n");
+ return EX_USAGE;
+ }
- for(i=0;i<original_len;i++) {
- if(i<len)
- original_string[i] = usernm[i+1];
- else /* stuff with commas to remove last parm */
- original_string[i] = ',';
+ if (out_len) {
+ strlcat(out, ",", MAX_OPTIONS_LEN);
+ out_len++;
+ }
+ snprintf(out + out_len, word_len + 11, "backupuid=%s", txtbuf);
+ out_len = strlen(out);
}
+ if (got_bkupgid) {
+ word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupgid);
- /* BB add check for more than one slash?
- strchr(*ppuser,'/');
- strchr(*ppuser,'\\')
- */
-
- return domainnm;
-}
+ /* comma + "backkupgid=" + terminating NULL == 12 */
+ if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
+ fprintf(stderr, "Options string too long\n");
+ return EX_USAGE;
+ }
-/* replace all occurances of "from" in a string with "to" */
-static void replace_char(char *string, char from, char to, int maxlen)
-{
- char *lastchar = string + maxlen;
- while (string) {
- string = strchr(string, from);
- if (string) {
- *string = to;
- if (string >= lastchar)
- return;
+ if (out_len) {
+ strlcat(out, ",", MAX_OPTIONS_LEN);
+ out_len++;
}
+ snprintf(out + out_len, word_len + 11, "backupgid=%s", txtbuf);
}
+
+ return 0;
}
-/* Note that caller frees the returned buffer if necessary */
-static struct addrinfo *
-parse_server(char **punc_name)
+static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info)
{
- char *unc_name = *punc_name;
int length = strnlen(unc_name, MAX_UNC_LEN);
- char *share;
- struct addrinfo *addrlist;
- int rc;
+ const char *host, *share, *prepath;
+ size_t hostlen, sharelen, prepathlen;
- if(length > (MAX_UNC_LEN - 1)) {
+ if (length > (MAX_UNC_LEN - 1)) {
fprintf(stderr, "mount error: UNC name too long\n");
- return NULL;
+ return EX_USAGE;
}
- if(length < 3) {
+ if (length < 3) {
fprintf(stderr, "mount error: UNC name too short\n");
- return NULL;
+ return EX_USAGE;
}
if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
(strncasecmp("smb://", unc_name, 6) == 0)) {
- fprintf(stderr, "Mounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
- return NULL;
+ fprintf(stderr,
+ "Mounting cifs URL not implemented yet. Attempt to mount %s\n",
+ unc_name);
+ return EX_USAGE;
}
- if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
- /* check for nfs syntax ie server:share */
- share = strchr(unc_name,':');
- if(!share) {
- fprintf(stderr, "mount error: improperly formatted UNC name.");
- fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
- return NULL;
+ /* Set up "host" and "share" pointers based on UNC format. */
+ /* TODO: Remove support for NFS syntax as of cifs-utils-6.0. */
+ if (strncmp(unc_name, "//", 2) && strncmp(unc_name, "\\\\", 2)) {
+ /*
+ * check for nfs syntax (server:/share/prepath)
+ *
+ * FIXME: IPv6 addresses?
+ */
+ host = unc_name;
+ share = strchr(host, ':');
+ if (!share) {
+ fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
+ return EX_USAGE;
}
-
- *punc_name = (char *)malloc(length + 3);
- if(*punc_name == NULL) {
- *punc_name = unc_name;
- return NULL;
+ hostlen = share - host;
+ share++;
+ if (*share == '/')
+ ++share;
+ fprintf(stderr, "WARNING: using NFS syntax for mounting CIFS "
+ "shares is deprecated and will be removed in cifs-utils"
+ "-6.0. Please migrate to UNC syntax.\n");
+ } else {
+ host = unc_name + 2;
+ hostlen = strcspn(host, "/\\");
+ if (!hostlen) {
+ fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
+ return EX_USAGE;
}
-
- *share = '/';
- strlcpy((*punc_name)+2, unc_name, length + 1);
- SAFE_FREE(unc_name);
- unc_name = *punc_name;
- unc_name[length+2] = 0;
+ share = host + hostlen + 1;
}
- unc_name[0] = '/';
- unc_name[1] = '/';
- unc_name += 2;
-
- /*
- * allow for either delimiter between host and sharename
- * If there's not one, then the UNC is malformed
- */
- if (!(share = strpbrk(unc_name, "/\\"))) {
- fprintf(stderr, "mount error: Malformed UNC\n");
- return NULL;
+ if (hostlen + 1 > sizeof(parsed_info->host)) {
+ fprintf(stderr, "mount.cifs: host portion of UNC too long\n");
+ return EX_USAGE;
}
- *share = 0; /* temporarily terminate the string */
- share += 1;
- if(got_ip == 0) {
- rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
- if (rc != 0) {
- fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
- unc_name, gai_strerror(rc));
- addrlist = NULL;
- }
+ sharelen = strcspn(share, "/\\");
+ if (sharelen + 1 > sizeof(parsed_info->share)) {
+ fprintf(stderr, "mount.cifs: share portion of UNC too long\n");
+ return EX_USAGE;
}
- *(share - 1) = '/'; /* put delimiter back */
- /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
- if ((prefixpath = strpbrk(share, "/\\"))) {
- *prefixpath = 0; /* permanently terminate the string */
- if (!strlen(++prefixpath))
- prefixpath = NULL; /* this needs to be done explicitly */
- }
- if(got_ip) {
- if(verboseflag)
- fprintf(stderr, "ip address specified explicitly\n");
- return NULL;
+ prepath = share + sharelen;
+ if (*prepath != '\0')
+ prepath++;
+
+ prepathlen = strlen(prepath);
+
+ if (prepathlen + 1 > sizeof(parsed_info->prefix)) {
+ fprintf(stderr, "mount.cifs: UNC prefixpath too long\n");
+ return EX_USAGE;
}
- /* BB should we pass an alternate version of the share name as Unicode */
- return addrlist;
+ /* copy pieces into their resepective buffers */
+ memcpy(parsed_info->host, host, hostlen);
+ memcpy(parsed_info->share, share, sharelen);
+ memcpy(parsed_info->prefix, prepath, prepathlen);
+
+ return 0;
+}
+
+static int get_pw_from_env(struct parsed_mount_info *parsed_info)
+{
+ int rc = 0;
+
+ if (getenv("PASSWD"))
+ rc = set_password(parsed_info, getenv("PASSWD"));
+ else if (getenv("PASSWD_FD"))
+ rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL,
+ parsed_info);
+ else if (getenv("PASSWD_FILE"))
+ rc = get_password_from_file(0, getenv("PASSWD_FILE"),
+ parsed_info);
+
+ return rc;
}
static struct option longopts[] = {
- { "all", 0, NULL, 'a' },
- { "help",0, NULL, 'h' },
- { "move",0, NULL, 'm' },
- { "bind",0, NULL, 'b' },
- { "read-only", 0, NULL, 'r' },
- { "ro", 0, NULL, 'r' },
- { "verbose", 0, NULL, 'v' },
- { "version", 0, NULL, 'V' },
- { "read-write", 0, NULL, 'w' },
- { "rw", 0, NULL, 'w' },
- { "options", 1, NULL, 'o' },
- { "type", 1, NULL, 't' },
- { "rsize",1, NULL, 'R' },
- { "wsize",1, NULL, 'W' },
- { "uid", 1, NULL, '1'},
- { "gid", 1, NULL, '2'},
- { "user",1,NULL,'u'},
- { "username",1,NULL,'u'},
- { "dom",1,NULL,'d'},
- { "domain",1,NULL,'d'},
- { "password",1,NULL,'p'},
- { "pass",1,NULL,'p'},
- { "credentials",1,NULL,'c'},
- { "port",1,NULL,'P'},
- /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
- { NULL, 0, NULL, 0 }
+ {"all", 0, NULL, 'a'},
+ {"help", 0, NULL, 'h'},
+ {"move", 0, NULL, 'm'},
+ {"bind", 0, NULL, 'b'},
+ {"read-only", 0, NULL, 'r'},
+ {"ro", 0, NULL, 'r'},
+ {"verbose", 0, NULL, 'v'},
+ {"version", 0, NULL, 'V'},
+ {"read-write", 0, NULL, 'w'},
+ {"rw", 0, NULL, 'w'},
+ {"options", 1, NULL, 'o'},
+ {"type", 1, NULL, 't'},
+ {"uid", 1, NULL, '1'},
+ {"gid", 1, NULL, '2'},
+ {"user", 1, NULL, 'u'},
+ {"username", 1, NULL, 'u'},
+ {"dom", 1, NULL, 'd'},
+ {"domain", 1, NULL, 'd'},
+ {"password", 1, NULL, 'p'},
+ {"pass", 1, NULL, 'p'},
+ {"credentials", 1, NULL, 'c'},
+ {"port", 1, NULL, 'P'},
+ {"sloppy", 0, NULL, 's'},
+ {NULL, 0, NULL, 0}
};
/* convert a string to uppercase. return false if the string
* wasn't ASCII. Return success on a NULL ptr */
-static int
-uppercase_string(char *string)
+static int uppercase_string(char *string)
{
if (!string)
return 1;
while (*string) {
/* check for unicode */
- if ((unsigned char) string[0] & 0x80)
+ if ((unsigned char)string[0] & 0x80)
return 0;
- *string = toupper((unsigned char) *string);
+ *string = toupper((unsigned char)*string);
string++;
}
*/
static int check_newline(const char *progname, const char *name)
{
- const char *s;
- for (s = "\n"; *s; s++) {
- if (strchr(name, *s)) {
- fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
- progname, *s);
- return EX_USAGE;
- }
- }
- return 0;
+ const char *s;
+ for (s = "\n"; *s; s++) {
+ if (strchr(name, *s)) {
+ fprintf(stderr,
+ "%s: illegal character 0x%02x in mount entry\n",
+ progname, *s);
+ return EX_USAGE;
+ }
+ }
+ return 0;
}
static int check_mtab(const char *progname, const char *devname,
- const char *dir)
+ const char *dir)
{
- if (check_newline(progname, devname) == -1 ||
- check_newline(progname, dir) == -1)
+ if (check_newline(progname, devname) || check_newline(progname, dir))
return EX_USAGE;
return 0;
}
-
-int main(int argc, char ** argv)
+static int
+add_mtab(char *devname, char *mountpoint, unsigned long flags, const char *fstype)
{
- int c;
- unsigned long flags = MS_MANDLOCK;
- char * orgoptions = NULL;
- char * share_name = NULL;
- const char * ipaddr = NULL;
- char * uuid = NULL;
- char * mountpoint = NULL;
- char * options = NULL;
- char * optionstail;
- char * resolved_path = NULL;
- char * temp;
- char * dev_name = NULL;
- int rc = 0;
- int rsize = 0;
- int wsize = 0;
- int nomtab = 0;
- int uid = 0;
- int gid = 0;
- int optlen = 0;
- int orgoptlen = 0;
- size_t options_size = 0;
- size_t current_len;
- int retry = 0; /* set when we have to retry mount with uppercase */
- struct addrinfo *addrhead = NULL, *addr;
+ int rc = 0, tmprc, fd;
+ uid_t uid;
+ char *mount_user = NULL;
struct mntent mountent;
- struct sockaddr_in *addr4 = NULL;
- struct sockaddr_in6 *addr6 = NULL;
- FILE * pmntfile;
+ struct stat statbuf;
+ FILE *pmntfile;
+ sigset_t mask, oldmask;
- if (check_setuid())
- return EX_USAGE;
+ uid = getuid();
+ if (uid != 0)
+ mount_user = getusername(uid);
- /* setlocale(LC_ALL, "");
- bindtextdomain(PACKAGE, LOCALEDIR);
- textdomain(PACKAGE); */
+ /*
+ * Set the real uid to the effective uid. This prevents unprivileged
+ * users from sending signals to this process, though ^c on controlling
+ * terminal should still work.
+ */
+ rc = setreuid(geteuid(), -1);
+ if (rc != 0) {
+ fprintf(stderr, "Unable to set real uid to effective uid: %s\n",
+ strerror(errno));
+ return EX_FILEIO;
+ }
- if(argc && argv)
- thisprogram = argv[0];
- else
- mount_cifs_usage(stderr);
+ rc = sigfillset(&mask);
+ if (rc) {
+ fprintf(stderr, "Unable to set filled signal mask\n");
+ return EX_FILEIO;
+ }
- if(thisprogram == NULL)
- thisprogram = "mount.cifs";
+ rc = sigprocmask(SIG_SETMASK, &mask, &oldmask);
+ if (rc) {
+ fprintf(stderr, "Unable to make process ignore signals\n");
+ return EX_FILEIO;
+ }
- /* add sharename in opts string as unc= parm */
- while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
- longopts, NULL)) != -1) {
- switch (c) {
-/* No code to do the following options yet */
-/* case 'l':
- list_with_volumelabel = 1;
- break;
- case 'L':
- volumelabel = optarg;
- break; */
-/* case 'a':
- ++mount_all;
- break; */
+ rc = toggle_dac_capability(1, 1);
+ if (rc)
+ return EX_FILEIO;
- case '?':
- case 'h': /* help */
- mount_cifs_usage(stdout);
- case 'n':
- ++nomtab;
- break;
- case 'b':
-#ifdef MS_BIND
- flags |= MS_BIND;
-#else
- fprintf(stderr,
- "option 'b' (MS_BIND) not supported\n");
-#endif
- break;
- case 'm':
-#ifdef MS_MOVE
- flags |= MS_MOVE;
-#else
- fprintf(stderr,
- "option 'm' (MS_MOVE) not supported\n");
-#endif
- break;
- case 'o':
- orgoptions = strdup(optarg);
- break;
- case 'r': /* mount readonly */
- flags |= MS_RDONLY;
- break;
- case 'U':
- uuid = optarg;
- break;
- case 'v':
- ++verboseflag;
- break;
- case 'V':
- print_cifs_mount_version();
- exit (0);
- case 'w':
- flags &= ~MS_RDONLY;
- break;
- case 'R':
- rsize = atoi(optarg) ;
- break;
- case 'W':
- wsize = atoi(optarg);
- break;
- case '1':
- if (isdigit(*optarg)) {
- char *ep;
-
- uid = strtoul(optarg, &ep, 10);
- if (*ep) {
- fprintf(stderr, "bad uid value \"%s\"\n", optarg);
- exit(EX_USAGE);
- }
- } else {
- struct passwd *pw;
+ atexit(unlock_mtab);
+ rc = lock_mtab();
+ if (rc) {
+ fprintf(stderr, "cannot lock mtab");
+ rc = EX_FILEIO;
+ goto add_mtab_exit;
+ }
- if (!(pw = getpwnam(optarg))) {
- fprintf(stderr, "bad user name \"%s\"\n", optarg);
- exit(EX_USAGE);
- }
- uid = pw->pw_uid;
- endpwent();
- }
- break;
- case '2':
- if (isdigit(*optarg)) {
- char *ep;
-
- gid = strtoul(optarg, &ep, 10);
- if (*ep) {
- fprintf(stderr, "bad gid value \"%s\"\n", optarg);
- exit(EX_USAGE);
- }
- } else {
- struct group *gr;
+ pmntfile = setmntent(MOUNTED, "a+");
+ if (!pmntfile) {
+ fprintf(stderr, "could not update mount table\n");
+ unlock_mtab();
+ rc = EX_FILEIO;
+ goto add_mtab_exit;
+ }
- if (!(gr = getgrnam(optarg))) {
- fprintf(stderr, "bad user name \"%s\"\n", optarg);
- exit(EX_USAGE);
- }
- gid = gr->gr_gid;
- endpwent();
- }
- break;
- case 'u':
- got_user = 1;
- user_name = optarg;
- break;
- case 'd':
- domain_name = optarg; /* BB fix this - currently ignored */
- got_domain = 1;
- break;
- case 'p':
- if(mountpassword == NULL)
- mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
- if(mountpassword) {
- got_password = 1;
- strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
- }
- break;
- case 'S':
- get_password_from_file(0 /* stdin */,NULL);
- break;
- case 't':
- break;
- case 'f':
- ++fakemnt;
- break;
- default:
- fprintf(stderr, "unknown mount option %c\n",c);
- mount_cifs_usage(stderr);
+ fd = fileno(pmntfile);
+ if (fd < 0) {
+ fprintf(stderr, "mntent does not appear to be valid\n");
+ unlock_mtab();
+ rc = EX_FILEIO;
+ goto add_mtab_exit;
+ }
+
+ rc = fstat(fd, &statbuf);
+ if (rc != 0) {
+ fprintf(stderr, "unable to fstat open mtab\n");
+ endmntent(pmntfile);
+ unlock_mtab();
+ rc = EX_FILEIO;
+ goto add_mtab_exit;
+ }
+
+ mountent.mnt_fsname = devname;
+ mountent.mnt_dir = mountpoint;
+ mountent.mnt_type = (char *)(void *)fstype;
+ mountent.mnt_opts = (char *)calloc(MTAB_OPTIONS_LEN, 1);
+ if (mountent.mnt_opts) {
+ if (flags & MS_RDONLY)
+ strlcat(mountent.mnt_opts, "ro", MTAB_OPTIONS_LEN);
+ else
+ strlcat(mountent.mnt_opts, "rw", MTAB_OPTIONS_LEN);
+
+ if (flags & MS_MANDLOCK)
+ strlcat(mountent.mnt_opts, ",mand", MTAB_OPTIONS_LEN);
+ if (flags & MS_NOEXEC)
+ strlcat(mountent.mnt_opts, ",noexec", MTAB_OPTIONS_LEN);
+ if (flags & MS_NOSUID)
+ strlcat(mountent.mnt_opts, ",nosuid", MTAB_OPTIONS_LEN);
+ if (flags & MS_NODEV)
+ strlcat(mountent.mnt_opts, ",nodev", MTAB_OPTIONS_LEN);
+ if (flags & MS_SYNCHRONOUS)
+ strlcat(mountent.mnt_opts, ",sync", MTAB_OPTIONS_LEN);
+ if (mount_user) {
+ strlcat(mountent.mnt_opts, ",user=", MTAB_OPTIONS_LEN);
+ strlcat(mountent.mnt_opts, mount_user,
+ MTAB_OPTIONS_LEN);
}
}
+ mountent.mnt_freq = 0;
+ mountent.mnt_passno = 0;
+ rc = addmntent(pmntfile, &mountent);
+ if (rc) {
+ int ignore __attribute__((unused));
+
+ fprintf(stderr, "unable to add mount entry to mtab\n");
+ ignore = ftruncate(fd, statbuf.st_size);
+ rc = EX_FILEIO;
+ }
+ tmprc = my_endmntent(pmntfile, statbuf.st_size);
+ if (tmprc) {
+ fprintf(stderr, "error %d detected on close of mtab\n", tmprc);
+ rc = EX_FILEIO;
+ }
+ unlock_mtab();
+ SAFE_FREE(mountent.mnt_opts);
+add_mtab_exit:
+ toggle_dac_capability(1, 0);
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
- if(argc < 3 || argv[optind] == NULL || argv[optind + 1] == NULL)
- mount_cifs_usage(stderr);
+ return rc;
+}
- dev_name = argv[optind];
- share_name = strndup(argv[optind], MAX_UNC_LEN);
- if (share_name == NULL) {
- fprintf(stderr, "%s: %s", thisprogram, strerror(ENOMEM));
- exit(EX_SYSERR);
+static int
+del_mtab(char *mountpoint)
+{
+ int tmprc, rc = 0;
+ FILE *mnttmp, *mntmtab;
+ struct mntent *mountent;
+ char *mtabfile, *mtabdir, *mtabtmpfile;
+
+ mtabfile = strdup(MOUNTED);
+ mtabdir = dirname(mtabfile);
+ mtabdir = realloc(mtabdir, strlen(mtabdir) + strlen(MNT_TMP_FILE) + 2);
+ if (!mtabdir) {
+ fprintf(stderr, "del_mtab: cannot determine current mtab path");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
}
- mountpoint = argv[optind + 1];
- /* make sure mountpoint is legit */
- rc = chdir(mountpoint);
+ mtabtmpfile = strcat(mtabdir, MNT_TMP_FILE);
+ if (!mtabtmpfile) {
+ fprintf(stderr, "del_mtab: cannot allocate memory to tmp file");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ atexit(unlock_mtab);
+ rc = lock_mtab();
if (rc) {
- fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
+ fprintf(stderr, "del_mtab: cannot lock mtab");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ mtabtmpfile = mktemp(mtabtmpfile);
+ if (!mtabtmpfile) {
+ fprintf(stderr, "del_mtab: cannot setup tmp file destination");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ mntmtab = setmntent(MOUNTED, "r");
+ if (!mntmtab) {
+ fprintf(stderr, "del_mtab: could not update mount table\n");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ mnttmp = setmntent(mtabtmpfile, "w");
+ if (!mnttmp) {
+ fprintf(stderr, "del_mtab: could not update mount table\n");
+ endmntent(mntmtab);
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ while ((mountent = getmntent(mntmtab)) != NULL) {
+ if (!strcmp(mountent->mnt_dir, mountpoint))
+ continue;
+ rc = addmntent(mnttmp, mountent);
+ if (rc) {
+ fprintf(stderr, "del_mtab: unable to add mount entry to mtab\n");
+ rc = EX_FILEIO;
+ goto del_mtab_error;
+ }
+ }
+
+ endmntent(mntmtab);
+
+ tmprc = my_endmntent(mnttmp, 0);
+ if (tmprc) {
+ fprintf(stderr, "del_mtab: error %d detected on close of tmp file\n", tmprc);
+ rc = EX_FILEIO;
+ goto del_mtab_error;
+ }
+
+ if (rename(mtabtmpfile, MOUNTED)) {
+ fprintf(stderr, "del_mtab: error %d when renaming mtab in place\n", errno);
+ rc = EX_FILEIO;
+ goto del_mtab_error;
+ }
+
+del_mtab_exit:
+ unlock_mtab();
+ free(mtabdir);
+ return rc;
+
+del_mtab_error:
+ if (unlink(mtabtmpfile))
+ fprintf(stderr, "del_mtab: failed to delete tmp file - %s\n",
strerror(errno));
- rc = EX_USAGE;
- goto mount_exit;
+ goto del_mtab_exit;
+}
+
+/* have the child drop root privileges */
+static int
+drop_child_privs(void)
+{
+ int rc;
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+
+ if (gid) {
+ rc = setgid(gid);
+ if (rc) {
+ fprintf(stderr, "Unable set group identity: %s\n",
+ strerror(errno));
+ return EX_SYSERR;
+ }
+ }
+ if (uid) {
+ rc = setuid(uid);
+ if (rc) {
+ fprintf(stderr, "Unable set user identity: %s\n",
+ strerror(errno));
+ return EX_SYSERR;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * If systemd is running and /bin/systemd-ask-password --
+ * is available, then use that else fallback on getpass(..)
+ *
+ * Returns: @input or NULL on error
+ */
+static char*
+get_password(const char *prompt, char *input, int capacity)
+{
+#ifdef ENABLE_SYSTEMD
+ int is_systemd_running;
+ struct stat a, b;
+
+ /* We simply test whether the systemd cgroup hierarchy is
+ * mounted */
+ is_systemd_running = (lstat("/sys/fs/cgroup", &a) == 0)
+ && (lstat("/sys/fs/cgroup/systemd", &b) == 0)
+ && (a.st_dev != b.st_dev);
+
+ if (is_systemd_running) {
+ char *cmd, *ret;
+ FILE *ask_pass_fp = NULL;
+
+ cmd = ret = NULL;
+ if (asprintf(&cmd, "/bin/systemd-ask-password \"%s\"", prompt) >= 0) {
+ ask_pass_fp = popen (cmd, "re");
+ free (cmd);
+ }
+
+ if (ask_pass_fp) {
+ ret = fgets(input, capacity, ask_pass_fp);
+ pclose(ask_pass_fp);
+ }
+
+ if (ret) {
+ int len = strlen(input);
+ if (input[len - 1] == '\n')
+ input[len - 1] = '\0';
+ return input;
+ }
}
+#endif
- rc = check_mountpoint(thisprogram, mountpoint);
+ /*
+ * Falling back to getpass(..)
+ * getpass is obsolete, but there's apparently nothing that replaces it
+ */
+ char *tmp_pass = getpass(prompt);
+ if (!tmp_pass)
+ return NULL;
+
+ strncpy(input, tmp_pass, capacity - 1);
+ input[capacity - 1] = '\0';
+
+ /* zero-out the static buffer */
+ memset(tmp_pass, 0, strlen(tmp_pass));
+
+ return input;
+}
+
+static int
+assemble_mountinfo(struct parsed_mount_info *parsed_info,
+ const char *thisprogram, const char *mountpoint,
+ const char *orig_dev, char *orgoptions)
+{
+ int rc;
+
+ rc = drop_capabilities(0);
if (rc)
- goto mount_exit;
+ goto assemble_exit;
+
+ rc = drop_child_privs();
+ if (rc)
+ goto assemble_exit;
- /* sanity check for unprivileged mounts */
if (getuid()) {
- rc = check_fstab(thisprogram, mountpoint, dev_name,
+ rc = check_fstab(thisprogram, mountpoint, orig_dev,
&orgoptions);
if (rc)
- goto mount_exit;
+ goto assemble_exit;
/* enable any default user mount flags */
- flags |= CIFS_SETUID_FLAGS;
+ parsed_info->flags |= CIFS_SETUID_FLAGS;
}
- if (getenv("PASSWD")) {
- if(mountpassword == NULL)
- mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
- if(mountpassword) {
- strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
- got_password = 1;
- }
- } else if (getenv("PASSWD_FD")) {
- get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
- } else if (getenv("PASSWD_FILE")) {
- get_password_from_file(0, getenv("PASSWD_FILE"));
- }
+ rc = get_pw_from_env(parsed_info);
+ if (rc)
+ goto assemble_exit;
- if (orgoptions && parse_options(&orgoptions, &flags)) {
- rc = EX_USAGE;
- goto mount_exit;
+ if (orgoptions) {
+ rc = parse_options(orgoptions, parsed_info);
+ if (rc)
+ goto assemble_exit;
}
if (getuid()) {
-#if !CIFS_LEGACY_SETUID_CHECK
- if (!(flags & (MS_USERS|MS_USER))) {
+ if (!(parsed_info->flags & (MS_USERS | MS_USER))) {
fprintf(stderr, "%s: permission denied\n", thisprogram);
rc = EX_USAGE;
- goto mount_exit;
+ goto assemble_exit;
}
-#endif /* !CIFS_LEGACY_SETUID_CHECK */
-
- if (geteuid()) {
- fprintf(stderr, "%s: not installed setuid - \"user\" "
- "CIFS mounts not supported.",
- thisprogram);
- rc = EX_FAIL;
- goto mount_exit;
+ }
+
+ parsed_info->flags &= ~(MS_USERS | MS_USER);
+
+ rc = parse_unc(orig_dev, parsed_info);
+ if (rc)
+ goto assemble_exit;
+
+ if (parsed_info->addrlist[0] == '\0')
+ rc = resolve_host(parsed_info->host, parsed_info->addrlist);
+
+ switch (rc) {
+ case EX_USAGE:
+ fprintf(stderr, "mount error: could not resolve address for "
+ "%s: %s\n", parsed_info->host,
+ rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
+ goto assemble_exit;
+
+ case EX_SYSERR:
+ fprintf(stderr, "mount error: problem parsing address "
+ "list: %s\n", strerror(errno));
+ goto assemble_exit;
+ }
+
+ if (!parsed_info->got_user) {
+ /*
+ * Note that the password will not be retrieved from the
+ * USER env variable (ie user%password form) as there is
+ * already a PASSWD environment varaible
+ */
+ if (getenv("USER"))
+ strlcpy(parsed_info->username, getenv("USER"),
+ sizeof(parsed_info->username));
+ else
+ strlcpy(parsed_info->username, getusername(getuid()),
+ sizeof(parsed_info->username));
+ parsed_info->got_user = 1;
+ }
+
+ if (!parsed_info->got_password) {
+ char tmp_pass[MOUNT_PASSWD_SIZE + 1];
+ char *prompt = NULL;
+
+ if(asprintf(&prompt, "Password for %s@%s: ", parsed_info->username, orig_dev) < 0)
+ prompt = NULL;
+
+ if (get_password(prompt ? prompt : "Password: ", tmp_pass, MOUNT_PASSWD_SIZE + 1)) {
+ rc = set_password(parsed_info, tmp_pass);
+ } else {
+ fprintf(stderr, "Error reading password, exiting\n");
+ rc = EX_SYSERR;
}
+
+ free(prompt);
+ if (rc)
+ goto assemble_exit;
}
- flags &= ~(MS_USERS|MS_USER);
+ /* copy in user= string */
+ if (parsed_info->got_user) {
+ if (*parsed_info->options)
+ strlcat(parsed_info->options, ",",
+ sizeof(parsed_info->options));
+ strlcat(parsed_info->options, "user=",
+ sizeof(parsed_info->options));
+ strlcat(parsed_info->options, parsed_info->username,
+ sizeof(parsed_info->options));
+ }
- addrhead = addr = parse_server(&share_name);
- if((addrhead == NULL) && (got_ip == 0)) {
- fprintf(stderr, "No ip address specified and hostname not found\n");
- rc = EX_USAGE;
- goto mount_exit;
+ if (*parsed_info->domain) {
+ if (*parsed_info->options)
+ strlcat(parsed_info->options, ",",
+ sizeof(parsed_info->options));
+ strlcat(parsed_info->options, ",domain=",
+ sizeof(parsed_info->options));
+ strlcat(parsed_info->options, parsed_info->domain,
+ sizeof(parsed_info->options));
}
-
- /* BB save off path and pop after mount returns? */
- resolved_path = (char *)malloc(PATH_MAX+1);
- if (!resolved_path) {
- fprintf(stderr, "Unable to allocate memory.\n");
- rc = EX_SYSERR;
- goto mount_exit;
+
+assemble_exit:
+ return rc;
+}
+
+/*
+ * chdir() into the mountpoint and determine "realpath". We assume here that
+ * "mountpoint" is a statically allocated string and does not need to be freed.
+ */
+static int
+acquire_mountpoint(char **mountpointp)
+{
+ int rc, dacrc;
+ uid_t realuid, oldfsuid;
+ gid_t oldfsgid;
+ char *mountpoint;
+
+ /*
+ * Acquire the necessary privileges to chdir to the mountpoint. If
+ * the real uid is root, then we reacquire CAP_DAC_READ_SEARCH. If
+ * it's not, then we change the fsuid to the real uid to ensure that
+ * the mounting user actually has access to the mountpoint.
+ *
+ * The mount(8) manpage does not state that users must be able to
+ * chdir into the mountpoint in order to mount onto it, but if we
+ * allow that, then an unprivileged user could use this program to
+ * "probe" into directories to which he does not have access.
+ */
+ realuid = getuid();
+ if (realuid == 0) {
+ dacrc = toggle_dac_capability(0, 1);
+ if (dacrc)
+ return dacrc;
+ } else {
+ oldfsuid = setfsuid(realuid);
+ oldfsgid = setfsgid(getgid());
}
- /* Note that if we can not canonicalize the name, we get
- another chance to see if it is valid when we chdir to it */
- if(!realpath(".", resolved_path)) {
+ rc = chdir(*mountpointp);
+ if (rc) {
+ fprintf(stderr, "Couldn't chdir to %s: %s\n", *mountpointp,
+ strerror(errno));
+ rc = EX_USAGE;
+ goto restore_privs;
+ }
+
+ mountpoint = realpath(".", NULL);
+ if (!mountpoint) {
fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
- mountpoint, strerror(errno));
+ *mountpointp, strerror(errno));
rc = EX_SYSERR;
- goto mount_exit;
}
- mountpoint = resolved_path;
-
- if(got_user == 0) {
- /* Note that the password will not be retrieved from the
- USER env variable (ie user%password form) as there is
- already a PASSWD environment varaible */
- if (getenv("USER"))
- user_name = strdup(getenv("USER"));
- if (user_name == NULL)
- user_name = getusername();
- got_user = 1;
- }
-
- if(got_password == 0) {
- char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
- no good replacement yet. */
- mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
- if (!tmp_pass || !mountpassword) {
- fprintf(stderr, "Password not entered, exiting\n");
- exit(EX_USAGE);
- }
- strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
- got_password = 1;
- }
- /* FIXME launch daemon (handles dfs name resolution and credential change)
- remember to clear parms and overwrite password field before launching */
- if(orgoptions) {
- optlen = strlen(orgoptions);
- orgoptlen = optlen;
- } else
- optlen = 0;
- if(share_name)
- optlen += strlen(share_name) + 4;
- else {
- fprintf(stderr, "No server share name specified\n");
- fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
- exit(EX_USAGE);
- }
- if(user_name)
- optlen += strlen(user_name) + 6;
- optlen += MAX_ADDRESS_LEN + 4;
- if(mountpassword)
- optlen += strlen(mountpassword) + 6;
-mount_retry:
- SAFE_FREE(options);
- options_size = optlen + 10 + DOMAIN_SIZE;
- options = (char *)malloc(options_size /* space for commas in password */ + 8 /* space for domain= , domain name itself was counted as part of the length username string above */);
-
- if(options == NULL) {
- fprintf(stderr, "Could not allocate memory for mount options\n");
- exit(EX_SYSERR);
- }
-
- strlcpy(options, "unc=", options_size);
- strlcat(options,share_name,options_size);
- /* scan backwards and reverse direction of slash */
- temp = strrchr(options, '/');
- if(temp > options + 6)
- *temp = '\\';
- if(user_name) {
- /* check for syntax like user=domain\user */
- if(got_domain == 0)
- domain_name = check_for_domain(&user_name);
- strlcat(options,",user=",options_size);
- strlcat(options,user_name,options_size);
- }
- if(retry == 0) {
- if(domain_name) {
- /* extra length accounted for in option string above */
- strlcat(options,",domain=",options_size);
- strlcat(options,domain_name,options_size);
- }
+ *mountpointp = mountpoint;
+restore_privs:
+ if (realuid == 0) {
+ dacrc = toggle_dac_capability(0, 0);
+ if (dacrc)
+ rc = rc ? rc : dacrc;
+ } else {
+ uid_t __attribute__((unused)) uignore = setfsuid(oldfsuid);
+ gid_t __attribute__((unused)) gignore = setfsgid(oldfsgid);
}
- strlcat(options,",ver=",options_size);
- strlcat(options,VERSION,options_size);
+ return rc;
+}
- if(orgoptions) {
- strlcat(options,",",options_size);
- strlcat(options,orgoptions,options_size);
- }
- if(prefixpath) {
- strlcat(options,",prefixpath=",options_size);
- strlcat(options,prefixpath,options_size); /* no need to cat the / */
+int main(int argc, char **argv)
+{
+ int c;
+ char *orgoptions = NULL;
+ char *mountpoint = NULL;
+ char *options = NULL;
+ char *orig_dev = NULL;
+ char *currentaddress, *nextaddress;
+ int rc = 0;
+ int already_uppercased = 0;
+ int sloppy = 0;
+ size_t options_size = MAX_OPTIONS_LEN;
+ struct parsed_mount_info *parsed_info = NULL;
+ pid_t pid;
+
+ rc = check_setuid();
+ if (rc)
+ return rc;
+
+ rc = drop_capabilities(1);
+ if (rc)
+ return EX_SYSERR;
+
+ /* setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE); */
+
+ if (!argc || !argv) {
+ rc = mount_usage(stderr);
+ goto mount_exit;
}
- /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
- replace_char(dev_name, '\\', '/', strlen(share_name));
+ thisprogram = basename(argv[0]);
+ if (thisprogram == NULL)
+ thisprogram = "mount.cifs";
+
+ /* allocate parsed_info as shared anonymous memory range */
+ parsed_info = mmap(0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (parsed_info == (struct parsed_mount_info *) -1) {
+ parsed_info = NULL;
+ fprintf(stderr, "Unable to allocate memory: %s\n",
+ strerror(errno));
+ return EX_SYSERR;
+ }
- if (!got_ip && addr) {
- strlcat(options, ",ip=", options_size);
- current_len = strnlen(options, options_size);
- optionstail = options + current_len;
- switch (addr->ai_addr->sa_family) {
- case AF_INET6:
- addr6 = (struct sockaddr_in6 *) addr->ai_addr;
- ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
- options_size - current_len);
+ /* add sharename in opts string as unc= parm */
+ while ((c = getopt_long(argc, argv, "?fhno:rsvVw",
+ longopts, NULL)) != -1) {
+ switch (c) {
+ case '?':
+ case 'h': /* help */
+ rc = mount_usage(stdout);
+ goto mount_exit;
+ case 'n':
+ ++parsed_info->nomtab;
+ break;
+ case 'o':
+ orgoptions = strndup(optarg, MAX_OPTIONS_LEN);
+ if (!orgoptions) {
+ rc = EX_SYSERR;
+ goto mount_exit;
+ }
break;
- case AF_INET:
- addr4 = (struct sockaddr_in *) addr->ai_addr;
- ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
- options_size - current_len);
+ case 'r': /* mount readonly */
+ parsed_info->flags |= MS_RDONLY;
+ break;
+ case 'v':
+ ++parsed_info->verboseflag;
+ break;
+ case 'V':
+ print_cifs_mount_version();
+ exit(0);
+ case 'w':
+ parsed_info->flags &= ~MS_RDONLY;
+ break;
+ case 'f':
+ ++parsed_info->fakemnt;
+ break;
+ case 's':
+ ++sloppy;
break;
default:
- ipaddr = NULL;
+ fprintf(stderr, "unknown command-line option: %c\n", c);
+ rc = mount_usage(stderr);
+ goto mount_exit;
}
+ }
- /* if the address looks bogus, try the next one */
- if (!ipaddr) {
- addr = addr->ai_next;
- if (addr)
- goto mount_retry;
+ if (argc < 3 || argv[optind] == NULL || argv[optind + 1] == NULL) {
+ rc = mount_usage(stderr);
+ goto mount_exit;
+ }
+
+ orig_dev = argv[optind];
+ mountpoint = argv[optind + 1];
+
+ /* chdir into mountpoint as soon as possible */
+ rc = acquire_mountpoint(&mountpoint);
+ if (rc)
+ return rc;
+
+ /*
+ * mount.cifs does privilege separation. Most of the code to handle
+ * assembling the mount info is done in a child process that drops
+ * privileges. The info is assembled in parsed_info which is a
+ * shared, mmaped memory segment. The parent waits for the child to
+ * exit and checks the return code. If it's anything but "0", then
+ * the process exits without attempting anything further.
+ */
+ pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
+ rc = EX_SYSERR;
+ goto mount_exit;
+ } else if (!pid) {
+ /* child */
+ rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
+ orig_dev, orgoptions);
+ return rc;
+ } else {
+ /* parent */
+ pid = wait(&rc);
+ if (!WIFEXITED(rc)) {
+ fprintf(stderr, "Child process terminated abnormally.\n");
rc = EX_SYSERR;
goto mount_exit;
}
+ rc = WEXITSTATUS(rc);
+ if (rc)
+ goto mount_exit;
}
- if (addr && addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
- strlcat(options, "%", options_size);
- current_len = strnlen(options, options_size);
- optionstail = options + current_len;
- snprintf(optionstail, options_size - current_len, "%u",
- addr6->sin6_scope_id);
+ options = calloc(options_size, 1);
+ if (!options) {
+ fprintf(stderr, "Unable to allocate memory.\n");
+ rc = EX_SYSERR;
+ goto mount_exit;
+ }
+
+ currentaddress = parsed_info->addrlist;
+ nextaddress = strchr(currentaddress, ',');
+ if (nextaddress)
+ *nextaddress++ = '\0';
+
+mount_retry:
+ if (!currentaddress) {
+ fprintf(stderr, "Unable to find suitable address.\n");
+ rc = parsed_info->nofail ? 0 : EX_FAIL;
+ goto mount_exit;
}
+ strlcpy(options, "ip=", options_size);
+ strlcat(options, currentaddress, options_size);
- if(verboseflag)
- fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
+ strlcat(options, ",unc=\\\\", options_size);
+ strlcat(options, parsed_info->host, options_size);
+ strlcat(options, "\\", options_size);
+ strlcat(options, parsed_info->share, options_size);
- if (mountpassword) {
+ if (*parsed_info->options) {
+ strlcat(options, ",", options_size);
+ strlcat(options, parsed_info->options, options_size);
+ }
+
+ if (*parsed_info->prefix) {
+ strlcat(options, ",prefixpath=", options_size);
+ strlcat(options, parsed_info->prefix, options_size);
+ }
+
+ if (sloppy)
+ strlcat(options, ",sloppy", options_size);
+
+ if (parsed_info->verboseflag)
+ fprintf(stderr, "%s kernel mount options: %s",
+ thisprogram, options);
+
+ if (parsed_info->got_password) {
/*
* Commas have to be doubled, or else they will
* look like the parameter separator
*/
- if(retry == 0)
- check_for_comma(&mountpassword);
- strlcat(options,",pass=",options_size);
- strlcat(options,mountpassword,options_size);
- if (verboseflag)
+ strlcat(options, ",pass=", options_size);
+ strlcat(options, parsed_info->password, options_size);
+ if (parsed_info->verboseflag)
fprintf(stderr, ",pass=********");
}
- if (verboseflag)
+ if (parsed_info->verboseflag)
fprintf(stderr, "\n");
- rc = check_mtab(thisprogram, dev_name, mountpoint);
+ rc = check_mtab(thisprogram, orig_dev, mountpoint);
if (rc)
goto mount_exit;
- if (!fakemnt && mount(dev_name, ".", cifs_fstype, flags, options)) {
+ if (!parsed_info->fakemnt) {
+ toggle_dac_capability(0, 1);
+ rc = mount(orig_dev, ".", cifs_fstype, parsed_info->flags, options);
+ toggle_dac_capability(0, 0);
+ if (rc == 0)
+ goto do_mtab;
+
switch (errno) {
case ECONNREFUSED:
case EHOSTUNREACH:
- if (addr) {
- addr = addr->ai_next;
- if (addr)
- goto mount_retry;
+ currentaddress = nextaddress;
+ if (currentaddress) {
+ nextaddress = strchr(currentaddress, ',');
+ if (nextaddress)
+ *nextaddress++ = '\0';
}
- break;
+ goto mount_retry;
case ENODEV:
- fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
+ fprintf(stderr,
+ "mount error: %s filesystem not supported by the system\n", cifs_fstype);
break;
case ENXIO:
- if(retry == 0) {
- retry = 1;
- if (uppercase_string(dev_name) &&
- uppercase_string(share_name) &&
- uppercase_string(prefixpath)) {
- fprintf(stderr, "retrying with upper case share name\n");
- goto mount_retry;
- }
+ if (!already_uppercased &&
+ uppercase_string(parsed_info->host) &&
+ uppercase_string(parsed_info->share) &&
+ uppercase_string(parsed_info->prefix)) {
+ fprintf(stderr,
+ "Retrying with upper case share name\n");
+ already_uppercased = 1;
+ goto mount_retry;
}
}
- fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
- fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
- "mount.cifs)\n");
+ fprintf(stderr, "mount error(%d): %s\n", errno,
+ strerror(errno));
+ fprintf(stderr,
+ "Refer to the %s(8) manual page (e.g. man "
+ "%s)\n", thisprogram, thisprogram);
rc = EX_FAIL;
goto mount_exit;
}
- if (nomtab)
- goto mount_exit;
- atexit(unlock_mtab);
- rc = lock_mtab();
- if (rc) {
- fprintf(stderr, "cannot lock mtab");
- goto mount_exit;
- }
- pmntfile = setmntent(MOUNTED, "a+");
- if (!pmntfile) {
- fprintf(stderr, "could not update mount table\n");
- unlock_mtab();
- rc = EX_FILEIO;
- goto mount_exit;
- }
- mountent.mnt_fsname = dev_name;
- mountent.mnt_dir = mountpoint;
- mountent.mnt_type = (char *)(void *)cifs_fstype;
- mountent.mnt_opts = (char *)malloc(220);
- if(mountent.mnt_opts) {
- char * mount_user = getusername();
- memset(mountent.mnt_opts,0,200);
- if(flags & MS_RDONLY)
- strlcat(mountent.mnt_opts,"ro",220);
- else
- strlcat(mountent.mnt_opts,"rw",220);
- if(flags & MS_MANDLOCK)
- strlcat(mountent.mnt_opts,",mand",220);
- if(flags & MS_NOEXEC)
- strlcat(mountent.mnt_opts,",noexec",220);
- if(flags & MS_NOSUID)
- strlcat(mountent.mnt_opts,",nosuid",220);
- if(flags & MS_NODEV)
- strlcat(mountent.mnt_opts,",nodev",220);
- if(flags & MS_SYNCHRONOUS)
- strlcat(mountent.mnt_opts,",sync",220);
- if(mount_user) {
- if(getuid() != 0) {
- strlcat(mountent.mnt_opts,
- ",user=", 220);
- strlcat(mountent.mnt_opts,
- mount_user, 220);
- }
+do_mtab:
+ if (!parsed_info->nomtab && !mtab_unusable()) {
+ if (parsed_info->flags & MS_REMOUNT) {
+ rc = del_mtab(mountpoint);
+ if (rc)
+ goto mount_exit;
}
+
+ rc = add_mtab(orig_dev, mountpoint, parsed_info->flags, cifs_fstype);
}
- mountent.mnt_freq = 0;
- mountent.mnt_passno = 0;
- rc = addmntent(pmntfile,&mountent);
- endmntent(pmntfile);
- unlock_mtab();
- SAFE_FREE(mountent.mnt_opts);
- if (rc)
- rc = EX_FILEIO;
+
mount_exit:
- if(mountpassword) {
- int len = strlen(mountpassword);
- memset(mountpassword,0,len);
- SAFE_FREE(mountpassword);
+ if (parsed_info) {
+ memset(parsed_info->password, 0, sizeof(parsed_info->password));
+ munmap(parsed_info, sizeof(*parsed_info));
}
-
- if (addrhead)
- freeaddrinfo(addrhead);
SAFE_FREE(options);
SAFE_FREE(orgoptions);
- SAFE_FREE(resolved_path);
- SAFE_FREE(share_name);
- exit(rc);
+ return rc;
}