From patchwork Thu Jul 19 17:42:48 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sargun Dhillon X-Patchwork-Id: 10534963 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 0C469600D0 for ; Thu, 19 Jul 2018 17:42:56 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5641229A9D for ; Thu, 19 Jul 2018 17:42:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4A03229B60; Thu, 19 Jul 2018 17:42:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 33FF329A9D for ; Thu, 19 Jul 2018 17:42:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732029AbeGSS1D (ORCPT ); Thu, 19 Jul 2018 14:27:03 -0400 Received: from mail-it0-f65.google.com ([209.85.214.65]:40531 "EHLO mail-it0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732003AbeGSS1D (ORCPT ); Thu, 19 Jul 2018 14:27:03 -0400 Received: by mail-it0-f65.google.com with SMTP id 188-v6so11030828ita.5 for ; Thu, 19 Jul 2018 10:42:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sargun.me; s=google; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=l9o54JRbz7fTAS5MfEJ5z+BLYSU1oCYcMfCagW2kDIc=; b=guDlj9eud2lhcggYeC+h65xaaWXbWbaBCWMxc3CK+qQ6O4n+DnjIwH81I0Kg7qdEeW z+mOpM0Dukn0rohY+PGNhxlcGeobacpemII0X2k7mAH5aS7B+GPdE2LkZTbJ/0QNaH3o Q8/K34ZBOq3y7qFRYg44yn0xeyQ+Ss17THBeU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=l9o54JRbz7fTAS5MfEJ5z+BLYSU1oCYcMfCagW2kDIc=; b=nDWqZkaMP5i7HmnOnJL2aVxRfPE7WbRBAwA+XVT7NRhq8lWGGqmFuD9nlVTQ6Xwrzx ayKyc71ORQKKP0QhUesStAa4tbe3h91Kw+FO6HtJVZaDC3aD/CRH1LFAfSspeARW8vNH buMl84SKeFPdtTpntumU3Ilg3OKSOoiOyTjlX7GiI68RYRN4DReQkf62URkQaWTCYHqk IPcsxx4xZgAl+xe75r1/4wiHN4YFstH8KSQEt1pcTmMXxWNqtdVaNMvtfhr7wSWyd8IN q/JvAGoBmi7pBrgQioiy+5hha7VhYqg/FHICVucRzIoCW5gF/rh/lTeJZJWdSViI5o7c LZDA== X-Gm-Message-State: AOUpUlESRJs12HPP9z5oX50VuG6RSqXKQSpityIAaYxblCC/vNeGZ+TH Eb7aICnpDM6hhMx5fmI2akGmJZd17KA= X-Google-Smtp-Source: AAOMgpd3OH98jlmWZz+mRfh0HNieUimjOPyBtHVY/I1PDP7LCv/vG69p6Ds0FJyhk3mvjwQwi9OqlA== X-Received: by 2002:a24:41e9:: with SMTP id b102-v6mr6130484itd.19.1532022170503; Thu, 19 Jul 2018 10:42:50 -0700 (PDT) Received: from ircssh-2.c.rugged-nimbus-611.internal (80.60.198.104.bc.googleusercontent.com. [104.198.60.80]) by smtp.gmail.com with ESMTPSA id w191-v6sm31066ita.33.2018.07.19.10.42.50 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 19 Jul 2018 10:42:50 -0700 (PDT) Date: Thu, 19 Jul 2018 17:42:48 +0000 From: Sargun Dhillon To: linux-fsdevel@vger.kernel.org Cc: Anna.Schumaker@Netapp.com, kinglongmee@gmail.com, ebiederm@xmission.com Subject: [PATCH] net/sunrpc: Add user namespace support Message-ID: <20180719174246.GA19824@ircssh-2.c.rugged-nimbus-611.internal> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 #include +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);