This adds the ability to pass a non-init user namespace to rpcauth_create, via rpc_auth_create_args. If the specific authentication mechanism does not support non-init user namespaces, then it will return an error. Currently, the only two authentication mechanisms that support non-init user namespaces are auth_null, and auth_unix. auth_unix will send the UID / GID from the user namespace for authentication. Signed-off-by: Sargun Dhillon <sargun@xxxxxxxxx> --- fs/nfs/nfs4proc.c | 3 +- include/linux/sunrpc/auth.h | 9 ++-- net/sunrpc/auth.c | 17 +++----- net/sunrpc/auth_generic.c | 1 + net/sunrpc/auth_gss/auth_gss.c | 10 +++-- net/sunrpc/auth_null.c | 3 +- net/sunrpc/auth_unix.c | 97 ++++++++++++++++++++++++++++-------------- net/sunrpc/clnt.c | 5 ++- 8 files changed, 89 insertions(+), 56 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6dd146885da9..ab92ac8d48a8 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3657,7 +3657,8 @@ static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandl struct nfs_fsinfo *info, rpc_authflavor_t flavor) { struct rpc_auth_create_args auth_args = { - .pseudoflavor = flavor, + .pseudoflavor = flavor, + .user_ns = &init_user_ns, }; struct rpc_auth *auth; diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index d9af474a857d..7f320be28efc 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -111,6 +111,7 @@ struct rpc_auth { struct rpc_auth_create_args { rpc_authflavor_t pseudoflavor; + struct user_namespace *user_ns; const char *target_name; }; @@ -125,7 +126,9 @@ struct rpc_authops { struct module *owner; rpc_authflavor_t au_flavor; /* flavor (RPC_AUTH_*) */ char * au_name; - struct rpc_auth * (*create)(struct rpc_auth_create_args *, struct rpc_clnt *); + bool user_ns; /* supports user namespaces */ + struct rpc_auth * (*create)(const struct rpc_auth_create_args *, + struct rpc_clnt *); void (*destroy)(struct rpc_auth *); int (*hash_cred)(struct auth_cred *, unsigned int); @@ -161,12 +164,10 @@ struct rpc_credops { extern const struct rpc_authops authunix_ops; extern const struct rpc_authops authnull_ops; -int __init rpc_init_authunix(void); int __init rpc_init_generic_auth(void); int __init rpcauth_init_module(void); void rpcauth_remove_module(void); void rpc_destroy_generic_auth(void); -void rpc_destroy_authunix(void); struct rpc_cred * rpc_lookup_cred(void); struct rpc_cred * rpc_lookup_cred_nonblock(void); @@ -174,7 +175,7 @@ struct rpc_cred * rpc_lookup_generic_cred(struct auth_cred *, int, gfp_t); struct rpc_cred * rpc_lookup_machine_cred(const char *service_name); int rpcauth_register(const struct rpc_authops *); int rpcauth_unregister(const struct rpc_authops *); -struct rpc_auth * rpcauth_create(struct rpc_auth_create_args *, +struct rpc_auth * rpcauth_create(const struct rpc_auth_create_args *, struct rpc_clnt *); void rpcauth_release(struct rpc_auth *); rpc_authflavor_t rpcauth_get_pseudoflavor(rpc_authflavor_t, diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index d2623b9f23d6..9cf1076375d5 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -253,7 +253,7 @@ rpcauth_list_flavors(rpc_authflavor_t *array, int size) EXPORT_SYMBOL_GPL(rpcauth_list_flavors); struct rpc_auth * -rpcauth_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) +rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) { struct rpc_auth *auth; const struct rpc_authops *ops; @@ -272,7 +272,8 @@ rpcauth_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) goto out; } spin_unlock(&rpc_authflavor_lock); - auth = ops->create(args, clnt); + if (args->user_ns == &init_user_ns || ops->user_ns) + auth = ops->create(args, clnt); module_put(ops->owner); if (IS_ERR(auth)) return auth; @@ -870,27 +871,21 @@ int __init rpcauth_init_module(void) { int err; - err = rpc_init_authunix(); - if (err < 0) - goto out1; err = rpc_init_generic_auth(); if (err < 0) - goto out2; + goto out1; err = register_shrinker(&rpc_cred_shrinker); if (err < 0) - goto out3; + goto out2; return 0; -out3: - rpc_destroy_generic_auth(); out2: - rpc_destroy_authunix(); + rpc_destroy_generic_auth(); out1: return err; } void rpcauth_remove_module(void) { - rpc_destroy_authunix(); rpc_destroy_generic_auth(); unregister_shrinker(&rpc_cred_shrinker); } diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index f1df9837f1ac..2ce9dc8a843b 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -270,6 +270,7 @@ static const struct rpc_authops generic_auth_ops = { .lookup_cred = generic_lookup_cred, .crcreate = generic_create_cred, .key_timeout = generic_key_timeout, + .user_ns = false, }; static struct rpc_auth generic_auth = { diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index be8f103d22fd..34ec2770c71c 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -985,7 +985,7 @@ static void gss_pipe_free(struct gss_pipe *p) * parameters based on the input flavor (which must be a pseudoflavor) */ static struct gss_auth * -gss_create_new(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) +gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) { rpc_authflavor_t flavor = args->pseudoflavor; struct gss_auth *gss_auth; @@ -1132,7 +1132,7 @@ gss_destroy(struct rpc_auth *auth) * (which is guaranteed to last as long as any of its descendants). */ static struct gss_auth * -gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args, +gss_auth_find_or_add_hashed(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt, struct gss_auth *new) { @@ -1169,7 +1169,8 @@ gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args, } static struct gss_auth * -gss_create_hashed(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) +gss_create_hashed(const struct rpc_auth_create_args *args, + struct rpc_clnt *clnt) { struct gss_auth *gss_auth; struct gss_auth *new; @@ -1188,7 +1189,7 @@ gss_create_hashed(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) } static struct rpc_auth * -gss_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) +gss_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) { struct gss_auth *gss_auth; struct rpc_xprt_switch *xps = rcu_access_pointer(clnt->cl_xpi.xpi_xpswitch); @@ -2005,6 +2006,7 @@ static const struct rpc_authops authgss_ops = { .list_pseudoflavors = gss_mech_list_pseudoflavors, .info2flavor = gss_mech_info2flavor, .flavor2info = gss_mech_flavor2info, + .user_ns = false, }; static const struct rpc_credops gss_credops = { diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index 75d72e109a04..a2743bfc79f9 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -19,7 +19,7 @@ static struct rpc_auth null_auth; static struct rpc_cred null_cred; static struct rpc_auth * -nul_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) +nul_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) { atomic_inc(&null_auth.au_count); return &null_auth; @@ -110,6 +110,7 @@ const struct rpc_authops authnull_ops = { .create = nul_create, .destroy = nul_destroy, .lookup_cred = nul_lookup_cred, + .user_ns = true, }; static diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index dafd6b870ba3..9935e878aac0 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -15,10 +15,16 @@ #include <linux/sunrpc/auth.h> #include <linux/user_namespace.h> +struct unix_auth { + struct rpc_auth rpc_auth; + struct user_namespace *user_ns; +}; + struct unx_cred { struct rpc_cred uc_base; kgid_t uc_gid; kgid_t uc_gids[UNX_NGROUPS]; + struct user_namespace *user_ns; }; #define uc_uid uc_base.cr_uid @@ -26,31 +32,71 @@ struct unx_cred { # define RPCDBG_FACILITY RPCDBG_AUTH #endif -static struct rpc_auth unix_auth; static const struct rpc_credops unix_credops; static struct rpc_auth * -unx_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) +unx_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) { + struct unix_auth *unix_auth; + struct rpc_auth *auth; + int err = -ENOMEM; + dprintk("RPC: creating UNIX authenticator for client %p\n", clnt); - atomic_inc(&unix_auth.au_count); - return &unix_auth; + if (!try_module_get(THIS_MODULE)) + return ERR_PTR(-EINVAL); + unix_auth = kmalloc(sizeof(*unix_auth), GFP_KERNEL); + if (!unix_auth) + goto error; + + unix_auth->user_ns = get_user_ns(args->user_ns); + auth = &unix_auth->rpc_auth; + + auth->au_cslack = UNX_CALLSLACK; + auth->au_rslack = NUL_REPLYSLACK, + auth->au_flags = RPCAUTH_AUTH_NO_CRKEY_TIMEOUT, + auth->au_ops = &authunix_ops, + auth->au_flavor = RPC_AUTH_UNIX; + atomic_set(&unix_auth->rpc_auth.au_count, 1); + + err = rpcauth_init_credcache(auth); + if (err) + goto error_free_auth; + + return auth; + +error_free_auth: + put_user_ns(unix_auth->user_ns); + kfree(unix_auth); +error: + module_put(THIS_MODULE); + return ERR_PTR(err); } static void unx_destroy(struct rpc_auth *auth) { + struct unix_auth *unix_auth; + + unix_auth = container_of(auth, struct unix_auth, rpc_auth); dprintk("RPC: destroying UNIX authenticator %p\n", auth); - rpcauth_clear_credcache(auth->au_credcache); + rpcauth_destroy_credcache(auth); + put_user_ns(unix_auth->user_ns); + kfree(unix_auth); + module_put(THIS_MODULE); } static int unx_hash_cred(struct auth_cred *acred, unsigned int hashbits) { - return hash_64(from_kgid(&init_user_ns, acred->gid) | - ((u64)from_kuid(&init_user_ns, acred->uid) << - (sizeof(gid_t) * 8)), hashbits); + /* + * No need to convert this based on the user namespace, because + * the cred cache is only scoped to the unix_auth instances + */ + uid_t uid = __kuid_val(acred->uid); + gid_t gid = __kgid_val(acred->gid); + + return hash_64(gid | ((u64)uid << (sizeof(gid_t) * 8)), hashbits); } /* @@ -65,19 +111,22 @@ unx_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) static struct rpc_cred * unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp) { + struct unix_auth *unix_auth; struct unx_cred *cred; unsigned int groups = 0; unsigned int i; + unix_auth = container_of(auth, struct unix_auth, rpc_auth); dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", - from_kuid(&init_user_ns, acred->uid), - from_kgid(&init_user_ns, acred->gid)); + from_kuid(unix_auth->user_ns, acred->uid), + from_kgid(unix_auth->user_ns, acred->gid)); if (!(cred = kmalloc(sizeof(*cred), gfp))) return ERR_PTR(-ENOMEM); rpcauth_init_cred(&cred->uc_base, acred, auth, &unix_credops); cred->uc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE; + cred->user_ns = get_user_ns(unix_auth->user_ns); if (acred->group_info != NULL) groups = acred->group_info->ngroups; @@ -97,6 +146,7 @@ static void unx_free_cred(struct unx_cred *unx_cred) { dprintk("RPC: unx_free_cred %p\n", unx_cred); + put_user_ns(unx_cred->user_ns); kfree(unx_cred); } @@ -162,11 +212,11 @@ unx_marshal(struct rpc_task *task, __be32 *p) */ p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen); - *p++ = htonl((u32) from_kuid(&init_user_ns, cred->uc_uid)); - *p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gid)); + *p++ = htonl((u32)from_kuid(cred->user_ns, cred->uc_uid)); + *p++ = htonl((u32)from_kgid(cred->user_ns, cred->uc_gid)); hold = p++; for (i = 0; i < UNX_NGROUPS && gid_valid(cred->uc_gids[i]); i++) - *p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gids[i])); + *p++ = htonl((u32)from_kgid(cred->user_ns, cred->uc_gids[i])); *hold = htonl(p - hold - 1); /* gid array length */ *base = htonl((p - base - 1) << 2); /* cred length */ @@ -211,16 +261,6 @@ unx_validate(struct rpc_task *task, __be32 *p) return p; } -int __init rpc_init_authunix(void) -{ - return rpcauth_init_credcache(&unix_auth); -} - -void rpc_destroy_authunix(void) -{ - rpcauth_destroy_credcache(&unix_auth); -} - const struct rpc_authops authunix_ops = { .owner = THIS_MODULE, .au_flavor = RPC_AUTH_UNIX, @@ -230,16 +270,7 @@ const struct rpc_authops authunix_ops = { .hash_cred = unx_hash_cred, .lookup_cred = unx_lookup_cred, .crcreate = unx_create_cred, -}; - -static -struct rpc_auth unix_auth = { - .au_cslack = UNX_CALLSLACK, - .au_rslack = NUL_REPLYSLACK, - .au_flags = RPCAUTH_AUTH_NO_CRKEY_TIMEOUT, - .au_ops = &authunix_ops, - .au_flavor = RPC_AUTH_UNIX, - .au_count = ATOMIC_INIT(0), + .user_ns = false, }; static diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index d839c33ae7d9..33d4c18060e4 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -294,8 +294,9 @@ static int rpc_client_register(struct rpc_clnt *clnt, const char *client_name) { struct rpc_auth_create_args auth_args = { - .pseudoflavor = pseudoflavor, - .target_name = client_name, + .pseudoflavor = pseudoflavor, + .target_name = client_name, + .user_ns = &init_user_ns, }; struct rpc_auth *auth; struct net *net = rpc_net_ns(clnt); -- 2.14.1