]> rtime.felk.cvut.cz Git - linux-imx.git/blobdiff - fs/nfs/super.c
Merge branch 'labeled-nfs' into linux-next
[linux-imx.git] / fs / nfs / super.c
index 1bb071dca9ab1349deecd44e19ec7e0531b737ea..71fdc0dfa0d28d9e293cbb58729d63395af7cc99 100644 (file)
@@ -269,7 +269,7 @@ static match_table_t nfs_local_lock_tokens = {
 
 enum {
        Opt_vers_2, Opt_vers_3, Opt_vers_4, Opt_vers_4_0,
-       Opt_vers_4_1,
+       Opt_vers_4_1, Opt_vers_4_2,
 
        Opt_vers_err
 };
@@ -280,6 +280,7 @@ static match_table_t nfs_vers_tokens = {
        { Opt_vers_4, "4" },
        { Opt_vers_4_0, "4.0" },
        { Opt_vers_4_1, "4.1" },
+       { Opt_vers_4_2, "4.2" },
 
        { Opt_vers_err, NULL }
 };
@@ -832,6 +833,7 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root)
                seq_printf(m, "\n\tnfsv4:\t");
                seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
                seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
+               seq_printf(m, ",bm2=0x%x", nfss->attr_bitmask[2]);
                seq_printf(m, ",acl=0x%x", nfss->acl_bitmask);
                show_sessions(m, nfss);
                show_pnfs(m, nfss);
@@ -1097,6 +1099,10 @@ static int nfs_parse_version_string(char *string,
                mnt->version = 4;
                mnt->minorversion = 1;
                break;
+       case Opt_vers_4_2:
+               mnt->version = 4;
+               mnt->minorversion = 2;
+               break;
        default:
                return 0;
        }
@@ -1608,57 +1614,35 @@ out_security_failure:
 }
 
 /*
- * Select a security flavor for this mount.  The selected flavor
- * is planted in args->auth_flavors[0].
+ * Ensure that the specified authtype in args->auth_flavors[0] is supported by
+ * the server. Returns 0 if it's ok, and -EACCES if not.
  */
-static void nfs_select_flavor(struct nfs_parsed_mount_data *args,
-                             struct nfs_mount_request *request)
+static int nfs_verify_authflavor(struct nfs_parsed_mount_data *args,
+                       rpc_authflavor_t *server_authlist, unsigned int count)
 {
-       unsigned int i, count = *(request->auth_flav_len);
-       rpc_authflavor_t flavor;
-
-       if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR)
-               goto out;
+       unsigned int i;
 
        /*
-        * The NFSv2 MNT operation does not return a flavor list.
-        */
-       if (args->mount_server.version != NFS_MNT3_VERSION)
-               goto out_default;
-
-       /*
-        * Certain releases of Linux's mountd return an empty
-        * flavor list in some cases.
-        */
-       if (count == 0)
-               goto out_default;
-
-       /*
-        * RFC 2623, section 2.7 suggests we SHOULD prefer the
-        * flavor listed first.  However, some servers list
-        * AUTH_NULL first.  Avoid ever choosing AUTH_NULL.
+        * If the sec= mount option is used, the specified flavor or AUTH_NULL
+        * must be in the list returned by the server.
+        *
+        * AUTH_NULL has a special meaning when it's in the server list - it
+        * means that the server will ignore the rpc creds, so any flavor
+        * can be used.
         */
        for (i = 0; i < count; i++) {
-               struct rpcsec_gss_info info;
-
-               flavor = request->auth_flavs[i];
-               switch (flavor) {
-               case RPC_AUTH_UNIX:
-                       goto out_set;
-               case RPC_AUTH_NULL:
-                       continue;
-               default:
-                       if (rpcauth_get_gssinfo(flavor, &info) == 0)
-                               goto out_set;
-               }
+               if (args->auth_flavors[0] == server_authlist[i] ||
+                   server_authlist[i] == RPC_AUTH_NULL)
+                       goto out;
        }
 
-out_default:
-       flavor = RPC_AUTH_UNIX;
-out_set:
-       args->auth_flavors[0] = flavor;
+       dfprintk(MOUNT, "NFS: auth flavor %u not supported by server\n",
+               args->auth_flavors[0]);
+       return -EACCES;
+
 out:
-       dfprintk(MOUNT, "NFS: using auth flavor %d\n", args->auth_flavors[0]);
+       dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]);
+       return 0;
 }
 
 /*
@@ -1666,10 +1650,10 @@ out:
  * corresponding to the provided path.
  */
 static int nfs_request_mount(struct nfs_parsed_mount_data *args,
-                            struct nfs_fh *root_fh)
+                            struct nfs_fh *root_fh,
+                            rpc_authflavor_t *server_authlist,
+                            unsigned int *server_authlist_len)
 {
-       rpc_authflavor_t server_authlist[NFS_MAX_SECFLAVORS];
-       unsigned int server_authlist_len = ARRAY_SIZE(server_authlist);
        struct nfs_mount_request request = {
                .sap            = (struct sockaddr *)
                                                &args->mount_server.address,
@@ -1677,7 +1661,7 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
                .protocol       = args->mount_server.protocol,
                .fh             = root_fh,
                .noresvport     = args->flags & NFS_MOUNT_NORESVPORT,
-               .auth_flav_len  = &server_authlist_len,
+               .auth_flav_len  = server_authlist_len,
                .auth_flavs     = server_authlist,
                .net            = args->net,
        };
@@ -1721,25 +1705,92 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
                return status;
        }
 
-       nfs_select_flavor(args, &request);
        return 0;
 }
 
-struct dentry *nfs_try_mount(int flags, const char *dev_name,
-                            struct nfs_mount_info *mount_info,
-                            struct nfs_subversion *nfs_mod)
+static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_info,
+                                       struct nfs_subversion *nfs_mod)
 {
        int status;
-       struct nfs_server *server;
+       unsigned int i;
+       bool tried_auth_unix = false;
+       bool auth_null_in_list = false;
+       struct nfs_server *server = ERR_PTR(-EACCES);
+       struct nfs_parsed_mount_data *args = mount_info->parsed;
+       rpc_authflavor_t authlist[NFS_MAX_SECFLAVORS];
+       unsigned int authlist_len = ARRAY_SIZE(authlist);
+
+       status = nfs_request_mount(args, mount_info->mntfh, authlist,
+                                       &authlist_len);
+       if (status)
+               return ERR_PTR(status);
 
-       if (mount_info->parsed->need_mount) {
-               status = nfs_request_mount(mount_info->parsed, mount_info->mntfh);
+       /*
+        * Was a sec= authflavor specified in the options? First, verify
+        * whether the server supports it, and then just try to use it if so.
+        */
+       if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR) {
+               status = nfs_verify_authflavor(args, authlist, authlist_len);
+               dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]);
                if (status)
                        return ERR_PTR(status);
+               return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
        }
 
-       /* Get a volume representation */
-       server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+       /*
+        * No sec= option was provided. RFC 2623, section 2.7 suggests we
+        * SHOULD prefer the flavor listed first. However, some servers list
+        * AUTH_NULL first. Avoid ever choosing AUTH_NULL.
+        */
+       for (i = 0; i < authlist_len; ++i) {
+               rpc_authflavor_t flavor;
+               struct rpcsec_gss_info info;
+
+               flavor = authlist[i];
+               switch (flavor) {
+               case RPC_AUTH_UNIX:
+                       tried_auth_unix = true;
+                       break;
+               case RPC_AUTH_NULL:
+                       auth_null_in_list = true;
+                       continue;
+               default:
+                       if (rpcauth_get_gssinfo(flavor, &info) != 0)
+                               continue;
+                       /* Fallthrough */
+               }
+               dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", flavor);
+               args->auth_flavors[0] = flavor;
+               server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+               if (!IS_ERR(server))
+                       return server;
+       }
+
+       /*
+        * Nothing we tried so far worked. At this point, give up if we've
+        * already tried AUTH_UNIX or if the server's list doesn't contain
+        * AUTH_NULL
+        */
+       if (tried_auth_unix || !auth_null_in_list)
+               return server;
+
+       /* Last chance! Try AUTH_UNIX */
+       dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", RPC_AUTH_UNIX);
+       args->auth_flavors[0] = RPC_AUTH_UNIX;
+       return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+}
+
+struct dentry *nfs_try_mount(int flags, const char *dev_name,
+                            struct nfs_mount_info *mount_info,
+                            struct nfs_subversion *nfs_mod)
+{
+       struct nfs_server *server;
+
+       if (mount_info->parsed->need_mount)
+               server = nfs_try_mount_request(mount_info, nfs_mod);
+       else
+               server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+
        if (IS_ERR(server))
                return ERR_CAST(server);
 
@@ -1908,6 +1959,7 @@ static int nfs23_validate_mount_data(void *options,
                args->namlen            = data->namlen;
                args->bsize             = data->bsize;
 
+               args->auth_flavors[0] = RPC_AUTH_UNIX;
                if (data->flags & NFS_MOUNT_SECFLAVOUR)
                        args->auth_flavors[0] = data->pseudoflavor;
                if (!args->nfs_server.hostname)
@@ -2377,7 +2429,21 @@ static int nfs_bdi_register(struct nfs_server *server)
 int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
                        struct nfs_mount_info *mount_info)
 {
-       return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts);
+       int error;
+       unsigned long kflags = 0, kflags_out = 0;
+       if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
+               kflags |= SECURITY_LSM_NATIVE_LABELS;
+
+       error = security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts,
+                                               kflags, &kflags_out);
+       if (error)
+               goto err;
+
+       if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
+               !(kflags_out & SECURITY_LSM_NATIVE_LABELS))
+               NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
+err:
+       return error;
 }
 EXPORT_SYMBOL_GPL(nfs_set_sb_security);
 
@@ -2603,6 +2669,7 @@ static int nfs4_validate_mount_data(void *options,
                        goto out_no_address;
                args->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port);
 
+               args->auth_flavors[0] = RPC_AUTH_UNIX;
                if (data->auth_flavourlen) {
                        if (data->auth_flavourlen > 1)
                                goto out_inval_auth;