mount.cifs: take extra care that mountpoint isn't changed during mount
[samba.git] / client / mount.cifs.c
index 1b94486a1acae462b320ac317e9e5da1909e99d2..f30418bfc268f7a994d00395b63640039df497d7 100644 (file)
 #include <mntent.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <fstab.h>
 #include "mount.h"
 
 #define MOUNT_CIFS_VERSION_MAJOR "1"
-#define MOUNT_CIFS_VERSION_MINOR "12"
+#define MOUNT_CIFS_VERSION_MINOR "13"
 
 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
  #ifdef _SAMBA_BUILD_
 #define MS_BIND 4096
 #endif
 
-#define MAX_UNC_LEN 1024
+/* private flags - clear these before passing to kernel */
+#define MS_USERS       0x40000000
+#define MS_USER                0x80000000
 
-#define CONST_DISCARD(type, ptr)      ((type) ((void *) (ptr)))
+#define MAX_UNC_LEN 1024
 
 #ifndef SAFE_FREE
 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
 /* currently maximum length of IPv6 address string */
 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
 
+/*
+ * 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.
+ */
+#define CIFS_LEGACY_SETUID_CHECK 0
+
+/*
+ * When an unprivileged user runs a setuid mount.cifs, we set certain mount
+ * flags by default. These defaults can be changed here.
+ */
+#define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
+
 const char *thisprogram;
 int verboseflag = 0;
 int fakemnt = 0;
@@ -97,6 +121,7 @@ static char * user_name = NULL;
 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. */
@@ -142,6 +167,99 @@ static size_t strlcat(char *d, const char *s, size_t bufsize)
 }
 #endif
 
+/*
+ * If an unprivileged user is doing the mounting then we need to ensure
+ * that the entry is in /etc/fstab.
+ */
+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;
+       }
+
+       if (!S_ISDIR(statbuf.st_mode)) {
+               fprintf(stderr, "%s: %s is not a directory!", progname,
+                               mountpoint);
+               return EX_USAGE;
+       }
+
+#if CIFS_LEGACY_SETUID_CHECK
+       /* do extra checks on mountpoint for legacy setuid behavior */
+       if (!getuid() || geteuid())
+               return 0;
+
+       if (statbuf.st_uid != getuid()) {
+               fprintf(stderr, "%s: %s is not owned by user\n", progname,
+                       mountpoint);
+               return EX_USAGE;
+       }
+
+       if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
+               fprintf(stderr, "%s: invalid permissions on %s\n", progname,
+                       mountpoint);
+               return EX_USAGE;
+       }
+#endif /* CIFS_LEGACY_SETUID_CHECK */
+
+       return 0;
+}
+
+#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,
+           char **options)
+{
+       FILE *fstab;
+       struct mntent *mnt;
+
+       /* make sure this mount is listed in /etc/fstab */
+       fstab = setmntent(_PATH_FSTAB, "r");
+       if (!fstab) {
+               fprintf(stderr, "Couldn't open %s for reading!\n",
+                               _PATH_FSTAB);
+               return EX_FILEIO;
+       }
+
+       while((mnt = getmntent(fstab))) {
+               if (!strcmp(mountpoint, mnt->mnt_dir))
+                       break;
+       }
+       endmntent(fstab);
+
+       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);
+               return EX_USAGE;
+       }
+
+       /*
+        * 'mount' munges the options from fstab before passing them
+        * to us. It is non-trivial to test that we have the correct
+        * set of options. We don't want to trust what the user
+        * gave us, so just take whatever is in /etc/fstab.
+        */
+       free(*options);
+       *options = strdup(mnt->mnt_opts);
+       return 0;
+}
+#endif /* CIFS_LEGACY_SETUID_CHECK */
+
 /* BB finish BB
 
         cifs_umount
@@ -154,31 +272,34 @@ BB end finish BB */
 static char * check_for_domain(char **);
 
 
-static void mount_cifs_usage(void)
+static void mount_cifs_usage(FILE *stream)
 {
-       printf("\nUsage:  %s <remotetarget> <dir> -o <options>\n", thisprogram);
-       printf("\nMount the remote target, specified as a UNC name,");
-       printf(" to a local directory.\n\nOptions:\n");
-       printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
-       printf("\nLess commonly used options:");
-       printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
-       printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
-       printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
-       printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
-       printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
-       printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
-       printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
-       printf("\n\nRarely used options:");
-       printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
-       printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
-       printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
-       printf("\n\tin6_addr");
-       printf("\n\nOptions are described in more detail in the manual page");
-       printf("\n\tman 8 mount.cifs\n");
-       printf("\nTo display the version number of the mount helper:");
-       printf("\n\t%s -V\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\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\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);
+
+       if (stream == stderr)
+               exit(EX_USAGE);
+       exit(0);
 }
 
 /* caller frees username if necessary */
@@ -198,6 +319,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;
@@ -229,7 +355,7 @@ static int open_cred_file(char * file_name)
                                        }
                                }
                                if(length > 4086) {
-                                       printf("mount.cifs failed due to malformed username in credentials file");
+                                       fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
                                        memset(line_buf,0,4096);
                                        exit(EX_USAGE);
                                } else {
@@ -253,7 +379,7 @@ static int open_cred_file(char * file_name)
                                        }
                                }
                                if(length > MOUNT_PASSWD_SIZE) {
-                                       printf("mount.cifs failed: password in credentials file too long\n");
+                                       fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
                                        memset(line_buf,0, 4096);
                                        exit(EX_USAGE);
                                } else {
@@ -273,7 +399,7 @@ static int open_cred_file(char * file_name)
                                 /* go past equals sign */
                                 temp_val++;
                                if(verboseflag)
-                                       printf("\nDomain %s\n",temp_val);
+                                       fprintf(stderr, "\nDomain %s\n",temp_val);
                                 for(length = 0;length<DOMAIN_SIZE+1;length++) {
                                        if ((temp_val[length] == '\n')
                                            || (temp_val[length] == '\0')) {
@@ -282,7 +408,7 @@ static int open_cred_file(char * file_name)
                                        }
                                 }
                                 if(length > DOMAIN_SIZE) {
-                                        printf("mount.cifs failed: domain in credentials file too long\n");
+                                        fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
                                         exit(EX_USAGE);
                                 } else {
                                         if(domain_name == NULL) {
@@ -315,14 +441,20 @@ static int get_password_from_file(int file_descript, char * filename)
                memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
 
        if (mountpassword == NULL) {
-               printf("malloc failed\n");
+               fprintf(stderr, "malloc failed\n");
                exit(EX_SYSERR);
        }
 
        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) {
-                       printf("mount.cifs failed. %s attempting to open password file %s\n",
+                       fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
                                   strerror(errno),filename);
                        exit(EX_SYSERR);
                }
@@ -332,14 +464,14 @@ static int get_password_from_file(int file_descript, char * filename)
        for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
                rc = read(file_descript,&c,1);
                if(rc < 0) {
-                       printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
+                       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)
-                                       printf("\nWarning: null password used since cifs password file empty");
+                                       fprintf(stderr, "\nWarning: null password used since cifs password file empty");
                        }
                        break;
                } else /* read valid character */ {
@@ -351,7 +483,7 @@ static int get_password_from_file(int file_descript, char * filename)
                }
        }
        if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
-               printf("\nWarning: password longer than %d characters specified in cifs password file",
+               fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
                        MOUNT_PASSWD_SIZE);
        }
        got_password = 1;
@@ -362,7 +494,7 @@ static int get_password_from_file(int file_descript, char * filename)
        return rc;
 }
 
-static int parse_options(char ** optionsp, int * filesys_flags)
+static int parse_options(char ** optionsp, unsigned long * filesys_flags)
 {
        const char * data;
        char * percent_char = NULL;
@@ -379,9 +511,6 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                return 1;
        data = *optionsp;
 
-       if(verboseflag)
-               printf("parsing options: %s\n", data);
-
        /* BB fixme check for separator override BB */
 
        if (getuid()) {
@@ -415,6 +544,7 @@ static int parse_options(char ** optionsp, int * filesys_flags)
 
                if (strncmp(data, "users",5) == 0) {
                        if(!value || !*value) {
+                               *filesys_flags |= MS_USERS;
                                goto nocopy;
                        }
                } else if (strncmp(data, "user_xattr",10) == 0) {
@@ -423,13 +553,10 @@ static int parse_options(char ** optionsp, int * filesys_flags)
 
                        if (!value || !*value) {
                                if(data[4] == '\0') {
-                                       if(verboseflag)
-                                               printf("\nskipping empty user mount parameter\n");
-                                       /* remove the parm since it would otherwise be confusing
-                                       to the kernel code which would think it was a real username */
+                                       *filesys_flags |= MS_USER;
                                        goto nocopy;
                                } else {
-                                       printf("username specified with no parameter\n");
+                                       fprintf(stderr, "username specified with no parameter\n");
                                        SAFE_FREE(out);
                                        return 1;       /* needs_arg; */
                                }
@@ -443,7 +570,7 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                                                        mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
                                                if(mountpassword) {
                                                        if(got_password)
-                                                               printf("\nmount.cifs warning - password specified twice\n");
+                                                               fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
                                                        got_password = 1;
                                                        percent_char++;
                                                        strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
@@ -462,7 +589,7 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                                        invoked */
                                        domain_name = check_for_domain(&value);
                                } else {
-                                       printf("username too long\n");
+                                       fprintf(stderr, "username too long\n");
                                        SAFE_FREE(out);
                                        return 1;
                                }
@@ -470,18 +597,27 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                } else if (strncmp(data, "pass", 4) == 0) {
                        if (!value || !*value) {
                                if(got_password) {
-                                       printf("\npassword specified twice, ignoring second\n");
+                                       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)
-                                       printf("\nmount.cifs warning - password specified twice\n");
-                               got_password = 1;
+                               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 {
-                               printf("password too long\n");
+                               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) ||
@@ -490,13 +626,13 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                        }
                } else if (strncmp(data, "ip", 2) == 0) {
                        if (!value || !*value) {
-                               printf("target ip address argument missing");
+                               fprintf(stderr, "target ip address argument missing");
                        } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
                                if(verboseflag)
-                                       printf("ip address %s override specified\n",value);
+                                       fprintf(stderr, "ip address %s override specified\n",value);
                                got_ip = 1;
                        } else {
-                               printf("ip address too long\n");
+                               fprintf(stderr, "ip address too long\n");
                                SAFE_FREE(out);
                                return 1;
                        }
@@ -504,32 +640,32 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                   || (strncmp(data, "target", 6) == 0)
                   || (strncmp(data, "path", 4) == 0)) {
                        if (!value || !*value) {
-                               printf("invalid path to network resource\n");
+                               fprintf(stderr, "invalid path to network resource\n");
                                SAFE_FREE(out);
                                return 1;  /* needs_arg; */
                        } else if(strnlen(value,5) < 5) {
-                               printf("UNC name too short");
+                               fprintf(stderr, "UNC name too short");
                        }
 
                        if (strnlen(value, 300) < 300) {
                                got_unc = 1;
                                if (strncmp(value, "//", 2) == 0) {
                                        if(got_unc)
-                                               printf("unc name specified twice, ignoring second\n");
+                                               fprintf(stderr, "unc name specified twice, ignoring second\n");
                                        else
                                                got_unc = 1;
                                } else if (strncmp(value, "\\\\", 2) != 0) {                       
-                                       printf("UNC Path does not begin with // or \\\\ \n");
+                                       fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
                                        SAFE_FREE(out);
                                        return 1;
                                } else {
                                        if(got_unc)
-                                               printf("unc name specified twice, ignoring second\n");
+                                               fprintf(stderr, "unc name specified twice, ignoring second\n");
                                        else
                                                got_unc = 1;
                                }
                        } else {
-                               printf("CIFS: UNC name too long\n");
+                               fprintf(stderr, "CIFS: UNC name too long\n");
                                SAFE_FREE(out);
                                return 1;
                        }
@@ -539,14 +675,14 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                           such as "DOM" and "dom" and "workgroup"
                           and "WORKGRP" etc. */
                        if (!value || !*value) {
-                               printf("CIFS: invalid domain name\n");
+                               fprintf(stderr, "CIFS: invalid domain name\n");
                                SAFE_FREE(out);
                                return 1;       /* needs_arg; */
                        }
                        if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
                                got_domain = 1;
                        } else {
-                               printf("domain name too long\n");
+                               fprintf(stderr, "domain name too long\n");
                                SAFE_FREE(out);
                                return 1;
                        }
@@ -554,13 +690,13 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                        if (value && *value) {
                                rc = open_cred_file(value);
                                if(rc) {
-                                       printf("error %d (%s) opening credential file %s\n",
+                                       fprintf(stderr, "error %d (%s) opening credential file %s\n",
                                                rc, strerror(rc), value);
                                        SAFE_FREE(out);
                                        return 1;
                                }
                        } else {
-                               printf("invalid credential file name specified\n");
+                               fprintf(stderr, "invalid credential file name specified\n");
                                SAFE_FREE(out);
                                return 1;
                        }
@@ -571,7 +707,7 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                                        struct passwd *pw;
 
                                        if (!(pw = getpwnam(value))) {
-                                               printf("bad user name \"%s\"\n", value);
+                                               fprintf(stderr, "bad user name \"%s\"\n", value);
                                                exit(EX_USAGE);
                                        }
                                        snprintf(user, sizeof(user), "%u", pw->pw_uid);
@@ -587,7 +723,7 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                                        struct group *gr;
 
                                        if (!(gr = getgrnam(value))) {
-                                               printf("bad group name \"%s\"\n", value);
+                                               fprintf(stderr, "bad group name \"%s\"\n", value);
                                                exit(EX_USAGE);
                                        }
                                        snprintf(group, sizeof(group), "%u", gr->gr_gid);
@@ -599,32 +735,32 @@ static int parse_options(char ** optionsp, int * filesys_flags)
        /* fmask and dmask synonyms for people used to smbfs syntax */
                } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
                        if (!value || !*value) {
-                               printf ("Option '%s' requires a numerical argument\n", data);
+                               fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
                                SAFE_FREE(out);
                                return 1;
                        }
 
                        if (value[0] != '0') {
-                               printf ("WARNING: '%s' not expressed in octal.\n", data);
+                               fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
                        }
 
                        if (strcmp (data, "fmask") == 0) {
-                               printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
+                               fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
                                data = "file_mode"; /* BB fix this */
                        }
                } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
                        if (!value || !*value) {
-                               printf ("Option '%s' requires a numerical argument\n", data);
+                               fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
                                SAFE_FREE(out);
                                return 1;
                        }
 
                        if (value[0] != '0') {
-                               printf ("WARNING: '%s' not expressed in octal.\n", data);
+                               fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
                        }
 
                        if (strcmp (data, "dmask") == 0) {
-                               printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
+                               fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
                                data = "dir_mode";
                        }
                        /* the following eight mount options should be
@@ -654,8 +790,10 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                        got_password = 1;
                } else if (strncmp(data, "ro", 2) == 0) {
                        *filesys_flags |= MS_RDONLY;
+                       goto nocopy;
                } else if (strncmp(data, "rw", 2) == 0) {
                        *filesys_flags &= ~MS_RDONLY;
+                       goto nocopy;
                 } else if (strncmp(data, "remount", 7) == 0) {
                         *filesys_flags |= MS_REMOUNT;
                } /* else if (strnicmp(data, "port", 4) == 0) {
@@ -675,7 +813,7 @@ static int parse_options(char ** optionsp, int * filesys_flags)
                        }
                } else if (strnicmp(data, "version", 3) == 0) {
                } else {
-                       printf("CIFS: Unknown mount option %s\n",data);
+                       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 */
 
@@ -769,7 +907,7 @@ static void check_for_comma(char ** ppasswrd)
                return;
        if(number_of_commas > MOUNT_PASSWD_SIZE) {
                /* would otherwise overflow the mount options buffer */
-               printf("\nInvalid password. Password contains too many commas.\n");
+               fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
                return;
        }
 
@@ -823,7 +961,7 @@ static char * check_for_domain(char **ppuser)
        }
 
        if(got_domain) {
-               printf("Domain name specified twice. Username probably malformed\n");
+               fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
                return NULL;
        }
 
@@ -832,7 +970,7 @@ static char * check_for_domain(char **ppuser)
        if (domainnm[0] != 0) {
                got_domain = 1;
        } else {
-               printf("null domain\n");
+               fprintf(stderr, "null domain\n");
        }
        len = strlen(domainnm);
        /* reset domainm to new buffer, and copy
@@ -891,18 +1029,18 @@ parse_server(char ** punc_name)
        int rc;
 
        if(length > (MAX_UNC_LEN - 1)) {
-               printf("mount error: UNC name too long");
+               fprintf(stderr, "mount error: UNC name too long");
                return NULL;
        }
        if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
            (strncasecmp("smb://", unc_name, 6) == 0)) {
-               printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
+               fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
                return NULL;
        }
 
        if(length < 3) {
                /* BB add code to find DFS root here */
-               printf("\nMounting the DFS root for domain not implemented yet\n");
+               fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
                return NULL;
        } else {
                if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
@@ -923,8 +1061,8 @@ parse_server(char ** punc_name)
                                unc_name[length+2] = 0;
                                goto continue_unc_parsing;
                        } else {
-                               printf("mount error: improperly formatted UNC name.");
-                               printf(" %s does not begin with \\\\ or //\n",unc_name);
+                               fprintf(stderr, "mount error: improperly formatted UNC name.");
+                               fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
                                return NULL;
                        }
                } else {
@@ -940,7 +1078,7 @@ continue_unc_parsing:
                                if(got_ip == 0) {
                                        rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
                                        if (rc != 0) {
-                                               printf("mount error: could not resolve address for %s: %s\n",
+                                               fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
                                                        unc_name, gai_strerror(rc));
                                                addrlist = NULL;
                                        }
@@ -955,7 +1093,7 @@ continue_unc_parsing:
                                }
                                if(got_ip) {
                                        if(verboseflag)
-                                               printf("ip address specified explicitly\n");
+                                               fprintf(stderr, "ip address specified explicitly\n");
                                        return NULL;
                                }
                                /* BB should we pass an alternate version of the share name as Unicode */
@@ -963,7 +1101,7 @@ continue_unc_parsing:
                                return addrlist; 
                        } else {
                                /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
-                               printf("Mounting the DFS root for a particular server not implemented yet\n");
+                               fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
                                return NULL;
                        }
                }
@@ -1029,7 +1167,7 @@ static void print_cifs_mount_version(void)
 int main(int argc, char ** argv)
 {
        int c;
-       int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
+       unsigned long flags = MS_MANDLOCK;
        char * orgoptions = NULL;
        char * share_name = NULL;
        const char * ipaddr = NULL;
@@ -1052,7 +1190,6 @@ int main(int argc, char ** argv)
        size_t current_len;
        int retry = 0; /* set when we have to retry mount with uppercase */
        struct addrinfo *addrhead = NULL, *addr;
-       struct stat statbuf;
        struct utsname sysinfo;
        struct mntent mountent;
        struct sockaddr_in *addr4;
@@ -1063,12 +1200,10 @@ int main(int argc, char ** argv)
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE); */
 
-       if(argc && argv) {
+       if(argc && argv)
                thisprogram = argv[0];
-       } else {
-               mount_cifs_usage();
-               exit(EX_USAGE);
-       }
+       else
+               mount_cifs_usage(stderr);
 
        if(thisprogram == NULL)
                thisprogram = "mount.cifs";
@@ -1077,7 +1212,7 @@ int main(int argc, char ** argv)
        /* BB add workstation name and domain and pass down */
 
 /* #ifdef _GNU_SOURCE
-       printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
+       fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
 #endif */
        if(argc > 2) {
                dev_name = argv[1];
@@ -1098,20 +1233,15 @@ int main(int argc, char ** argv)
                if ((strcmp(argv[1], "-h") == 0) ||
                    (strcmp(argv[1], "-?") == 0) ||
                    (strcmp(argv[1], "--help") == 0))
-               {
-                       mount_cifs_usage();
-                       exit(0);
-               }
+                       mount_cifs_usage(stdout);
 
-               mount_cifs_usage();
-               exit(EX_USAGE);
+               mount_cifs_usage(stderr);
        } else {
-               mount_cifs_usage();
-               exit(EX_USAGE);
+               mount_cifs_usage(stderr);
        }
 
-       /* add sharename in opts string as unc= parm */
 
+       /* add sharename in opts string as unc= parm */
        while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
                         longopts, NULL)) != -1) {
                switch (c) {
@@ -1128,8 +1258,7 @@ int main(int argc, char ** argv)
 
                case '?':
                case 'h':        /* help */
-                       mount_cifs_usage ();
-                       exit(0);
+                       mount_cifs_usage(stdout);
                case 'n':
                        ++nomtab;
                        break;
@@ -1179,14 +1308,14 @@ int main(int argc, char ** argv)
 
                                uid = strtoul(optarg, &ep, 10);
                                if (*ep) {
-                                       printf("bad uid value \"%s\"\n", optarg);
+                                       fprintf(stderr, "bad uid value \"%s\"\n", optarg);
                                        exit(EX_USAGE);
                                }
                        } else {
                                struct passwd *pw;
 
                                if (!(pw = getpwnam(optarg))) {
-                                       printf("bad user name \"%s\"\n", optarg);
+                                       fprintf(stderr, "bad user name \"%s\"\n", optarg);
                                        exit(EX_USAGE);
                                }
                                uid = pw->pw_uid;
@@ -1199,14 +1328,14 @@ int main(int argc, char ** argv)
 
                                gid = strtoul(optarg, &ep, 10);
                                if (*ep) {
-                                       printf("bad gid value \"%s\"\n", optarg);
+                                       fprintf(stderr, "bad gid value \"%s\"\n", optarg);
                                        exit(EX_USAGE);
                                }
                        } else {
                                struct group *gr;
 
                                if (!(gr = getgrnam(optarg))) {
-                                       printf("bad user name \"%s\"\n", optarg);
+                                       fprintf(stderr, "bad user name \"%s\"\n", optarg);
                                        exit(EX_USAGE);
                                }
                                gid = gr->gr_gid;
@@ -1238,15 +1367,37 @@ int main(int argc, char ** argv)
                        ++fakemnt;
                        break;
                default:
-                       printf("unknown mount option %c\n",c);
-                       mount_cifs_usage();
-                       exit(EX_USAGE);
+                       fprintf(stderr, "unknown mount option %c\n",c);
+                       mount_cifs_usage(stderr);
                }
        }
 
        if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
-               mount_cifs_usage();
-               exit(EX_USAGE);
+               mount_cifs_usage(stderr);
+       }
+
+       /* 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;
+
+       /* sanity check for unprivileged mounts */
+       if (getuid()) {
+               rc = check_fstab(thisprogram, mountpoint, dev_name,
+                                &orgoptions);
+               if (rc)
+                       goto mount_exit;
+
+               /* enable any default user mount flags */
+               flags |= CIFS_SETUID_FLAGS;
        }
 
        if (getenv("PASSWD")) {
@@ -1266,52 +1417,52 @@ int main(int argc, char ** argv)
                 rc = EX_USAGE;
                goto mount_exit;
        }
+
+       if (getuid()) {
+#if !CIFS_LEGACY_SETUID_CHECK
+               if (!(flags & (MS_USERS|MS_USER))) {
+                       fprintf(stderr, "%s: permission denied\n", thisprogram);
+                       rc = EX_USAGE;
+                       goto mount_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;
+               }
+       }
+
+       flags &= ~(MS_USERS|MS_USER);
+
        addrhead = addr = parse_server(&share_name);
        if((addrhead == NULL) && (got_ip == 0)) {
-               printf("No ip address specified and hostname not found\n");
+               fprintf(stderr, "No ip address specified and hostname not found\n");
                rc = EX_USAGE;
                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(chdir(mountpoint)) {
-               printf("mount error: can not change directory into mount target %s\n",mountpoint);
-               rc = EX_USAGE;
+       if (!resolved_path) {
+               fprintf(stderr, "Unable to allocate memory.\n");
+               rc = EX_SYSERR;
                goto mount_exit;
        }
 
-       if(stat (".", &statbuf)) {
-               printf("mount error: mount point %s does not exist\n",mountpoint);
-               rc = EX_USAGE;
+       /* 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;
        }
 
-       if (S_ISDIR(statbuf.st_mode) == 0) {
-               printf("mount error: mount point %s is not a directory\n",mountpoint);
-               rc = EX_USAGE;
-               goto mount_exit;
-       }
-
-       if((getuid() != 0) && (geteuid() == 0)) {
-               if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
-#ifndef CIFS_ALLOW_USR_SUID
-                       /* Do not allow user mounts to control suid flag
-                       for mount unless explicitly built that way */
-                       flags |= MS_NOSUID | MS_NODEV;
-#endif                                         
-               } else {
-                       printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n"); 
-                       exit(EX_USAGE);
-               }
-       }
+       mountpoint = resolved_path; 
 
        if(got_user == 0) {
                /* Note that the password will not be retrieved from the
@@ -1329,7 +1480,7 @@ int main(int argc, char ** argv)
                                                           no good replacement yet. */
                mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
                if (!tmp_pass || !mountpassword) {
-                       printf("Password not entered, exiting\n");
+                       fprintf(stderr, "Password not entered, exiting\n");
                        exit(EX_USAGE);
                }
                strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
@@ -1345,8 +1496,8 @@ int main(int argc, char ** argv)
        if(share_name)
                optlen += strlen(share_name) + 4;
        else {
-               printf("No server share name specified\n");
-               printf("\nMounting the DFS root for server not implemented yet\n");
+               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)
@@ -1360,7 +1511,7 @@ mount_retry:
        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) {
-               printf("Could not allocate memory for mount options\n");
+               fprintf(stderr, "Could not allocate memory for mount options\n");
                exit(EX_SYSERR);
        }
 
@@ -1384,15 +1535,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);
@@ -1405,8 +1547,6 @@ mount_retry:
                strlcat(options,",prefixpath=",options_size);
                strlcat(options,prefixpath,options_size); /* no need to cat the / */
        }
-       if(verboseflag)
-               printf("\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));
@@ -1426,6 +1566,8 @@ mount_retry:
                        ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
                                           options_size - current_len);
                        break;
+               default:
+                       ipaddr = NULL;
                }
 
                /* if the address looks bogus, try the next one */
@@ -1438,7 +1580,34 @@ mount_retry:
                }
        }
 
-       if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
+       if (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);
+       }
+
+       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");
+
+       if (!fakemnt && mount(dev_name, ".", cifs_fstype, flags, options)) {
                switch (errno) {
                case ECONNREFUSED:
                case EHOSTUNREACH:
@@ -1449,7 +1618,7 @@ mount_retry:
                        }
                        break;
                case ENODEV:
-                       printf("mount error: cifs filesystem not supported by the system\n");
+                       fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
                        break;
                case ENXIO:
                        if(retry == 0) {
@@ -1457,13 +1626,13 @@ mount_retry:
                                if (uppercase_string(dev_name) &&
                                    uppercase_string(share_name) &&
                                    uppercase_string(prefixpath)) {
-                                       printf("retrying with upper case share name\n");
+                                       fprintf(stderr, "retrying with upper case share name\n");
                                        goto mount_retry;
                                }
                        }
                }
-               printf("mount error(%d): %s\n", errno, strerror(errno));
-               printf("Refer to the mount.cifs(8) manual page (e.g. man "
+               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");
                rc = EX_FAIL;
                goto mount_exit;
@@ -1474,19 +1643,19 @@ mount_retry:
        atexit(unlock_mtab);
        rc = lock_mtab();
        if (rc) {
-               printf("cannot lock mtab");
+               fprintf(stderr, "cannot lock mtab");
                goto mount_exit;
        }
        pmntfile = setmntent(MOUNTED, "a+");
        if (!pmntfile) {
-               printf("could not update mount table\n");
+               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 = 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();