#include "mount.h"
#define MOUNT_CIFS_VERSION_MAJOR "1"
-#define MOUNT_CIFS_VERSION_MINOR "13"
+#define MOUNT_CIFS_VERSION_MINOR "14"
#ifndef MOUNT_CIFS_VENDOR_SUFFIX
#ifdef _SAMBA_BUILD_
#define MAX_UNC_LEN 1024
-#define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
-
#ifndef SAFE_FREE
#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
#endif
/* currently maximum length of IPv6 address string */
#define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
+/*
+ * 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
static char * mountpassword = NULL;
char * domain_name = NULL;
char * prefixpath = NULL;
+const char *cifs_fstype = "cifs";
/* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
* don't link to libreplace so need them here. */
struct stat statbuf;
/* does mountpoint exist and is it a directory? */
- err = stat(mountpoint, &statbuf);
+ err = stat(".", &statbuf);
if (err) {
fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
mountpoint, strerror(errno));
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 (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;
+ }
+
+ return 0;
+}
+#endif /* CIFS_DISABLE_SETUID_CHECK */
+
#if CIFS_LEGACY_SETUID_CHECK
static int
check_fstab(const char *progname, char *mountpoint, char *devname,
char * temp_val;
FILE * fs;
int i, length;
+
+ i = access(file_name, R_OK);
+ if (i)
+ return i;
+
fs = fopen(file_name,"r");
if(fs == NULL)
return errno;
}
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);
+ }
file_descript = open(filename, O_RDONLY);
if(file_descript < 0) {
fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
return 1;
data = *optionsp;
- if(verboseflag)
- fprintf(stderr, "parsing options: %s\n", data);
-
/* BB fixme check for separator override BB */
if (getuid()) {
} else
got_password = 1;
} else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
- if(got_password)
+ if (got_password) {
fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
- got_password = 1;
+ } 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;
}
+ goto nocopy;
} else if (strncmp(data, "sec", 3) == 0) {
if (value) {
if (!strncmp(value, "none", 4) ||
MOUNT_CIFS_VENDOR_SUFFIX);
}
+/*
+ * This function borrowed from fuse-utils...
+ *
+ * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
+ * newlines embedded within the text fields. To make sure no one corrupts
+ * the mtab, fail the mount if there are embedded newlines.
+ */
+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;
+}
+
+static int check_mtab(const char *progname, const char *devname,
+ const char *dir)
+{
+ if (check_newline(progname, devname) == -1 ||
+ check_newline(progname, dir) == -1)
+ return EX_USAGE;
+ return 0;
+}
+
+
int main(int argc, char ** argv)
{
int c;
struct sockaddr_in6 *addr6;
FILE * pmntfile;
+ if (check_setuid())
+ return EX_USAGE;
+
/* setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE); */
}
/* make sure mountpoint is legit */
+ rc = chdir(mountpoint);
+ if (rc) {
+ fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
+ strerror(errno));
+ rc = EX_USAGE;
+ goto mount_exit;
+ }
+
rc = check_mountpoint(thisprogram, mountpoint);
if (rc)
goto mount_exit;
/* BB save off path and pop after mount returns? */
resolved_path = (char *)malloc(PATH_MAX+1);
- if(resolved_path) {
- /* 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(mountpoint, resolved_path)) {
- mountpoint = resolved_path;
- }
+ if (!resolved_path) {
+ fprintf(stderr, "Unable to allocate memory.\n");
+ rc = EX_SYSERR;
+ goto mount_exit;
}
+
+ /* 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)) {
+ fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
+ mountpoint, 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
strlcat(options,domain_name,options_size);
}
}
- if(mountpassword) {
- /* Commas have to be doubled, or else they will
- look like the parameter separator */
-/* if(sep is not set)*/
- if(retry == 0)
- check_for_comma(&mountpassword);
- strlcat(options,",pass=",options_size);
- strlcat(options,mountpassword,options_size);
- }
strlcat(options,",ver=",options_size);
strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
strlcat(options,",prefixpath=",options_size);
strlcat(options,prefixpath,options_size); /* no need to cat the / */
}
- if(verboseflag)
- fprintf(stderr, "\nmount.cifs kernel mount options %s \n",options);
/* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
replace_char(dev_name, '\\', '/', strlen(share_name));
addr6->sin6_scope_id);
}
- if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
+ if(verboseflag)
+ fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
+
+ if (mountpassword) {
+ /*
+ * 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)
+ fprintf(stderr, ",pass=********");
+ }
+
+ if (verboseflag)
+ fprintf(stderr, "\n");
+
+ rc = check_mtab(thisprogram, dev_name, mountpoint);
+ if (rc)
+ goto mount_exit;
+
+ if (!fakemnt && mount(dev_name, ".", cifs_fstype, flags, options)) {
switch (errno) {
case ECONNREFUSED:
case EHOSTUNREACH:
}
mountent.mnt_fsname = dev_name;
mountent.mnt_dir = mountpoint;
- mountent.mnt_type = CONST_DISCARD(char *,"cifs");
+ mountent.mnt_type = (char *)(void *)cifs_fstype;
mountent.mnt_opts = (char *)malloc(220);
if(mountent.mnt_opts) {
char * mount_user = getusername();