s3: Correct documentation of case sensitive
[samba.git] / client / mount.cifs.c
index 1c04e13c8d63aa30657d79f9a55d65d959e23af7..1b472c2ea50c8dc4998355999ea0619d9b9a14b6 100644 (file)
@@ -43,7 +43,7 @@
 #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_
 /* 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
@@ -179,7 +190,7 @@ check_mountpoint(const char *progname, char *mountpoint)
        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));
@@ -213,6 +224,29 @@ check_mountpoint(const char *progname, char *mountpoint)
        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,
@@ -320,6 +354,11 @@ static int open_cred_file(char * file_name)
        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;
@@ -442,6 +481,12 @@ static int get_password_from_file(int file_descript, char * filename)
        }
 
        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",
@@ -501,9 +546,6 @@ static int parse_options(char ** optionsp, unsigned long * filesys_flags)
                return 1;
        data = *optionsp;
 
-       if(verboseflag)
-               fprintf(stderr, "parsing options: %s\n", data);
-
        /* BB fixme check for separator override BB */
 
        if (getuid()) {
@@ -594,14 +636,23 @@ static int parse_options(char ** optionsp, unsigned long * filesys_flags)
                                } 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) ||
@@ -1148,6 +1199,36 @@ static void print_cifs_mount_version(void)
                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)
+{
+    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;
@@ -1180,6 +1261,9 @@ int main(int argc, char ** argv)
        struct sockaddr_in6 *addr6;
        FILE * pmntfile;
 
+       if (check_setuid())
+               return EX_USAGE;
+
        /* setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE); */
@@ -1361,6 +1445,14 @@ int main(int argc, char ** argv)
        }
 
        /* 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;
@@ -1423,13 +1515,23 @@ int main(int argc, char ** argv)
        
        /* 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
@@ -1501,15 +1603,6 @@ mount_retry:
                        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);
@@ -1522,8 +1615,6 @@ mount_retry:
                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));
@@ -1557,7 +1648,7 @@ mount_retry:
                }
        }
 
-       if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
+       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;
@@ -1565,7 +1656,30 @@ mount_retry:
                         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", flags, options)) {
                switch (errno) {
                case ECONNREFUSED:
                case EHOSTUNREACH: