Commit 87268f7a4f1f ("nfs: create a kernel keyring") allocates one .nfs keyring at module load, and nothing in the NFS client reads it. One module-wide keyring also cannot isolate x.509 credentials between network namespaces. Each tlshd instance services the handshake socket of one network namespace, so a credential provisioned for that namespace's mounts has to be reachable by that tlshd and by no other. Allocate one .nfs keyring per network namespace in nfs_net_init() and release it in nfs_net_exit(). tlshd finds a keyring by name through /proc/keys, which is not namespace scoped, so each handshake request has to carry the keyring serial number instead. Allocate the keyring under a kernel credential rather than that of the task creating the namespace, so an LSM labels every namespace's keyring the same way. The keyring grants its owner every permission and everyone else none. Make the owner of the network namespace's user namespace the keyring's owner, so that in a container with its own user namespace the container's root can provision the keyring and its tlshd can link it. Signed-off-by: Chuck Lever --- fs/nfs/inode.c | 83 ++++++++++++++++++++++++++++++++-------------------------- fs/nfs/netns.h | 2 ++ 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 832923be43a9..2b494fa5ecc0 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -2641,11 +2641,52 @@ static int nfsiod_start(void) unsigned int nfs_net_id; EXPORT_SYMBOL_GPL(nfs_net_id); +#ifdef CONFIG_KEYS +static int nfs_init_keyring(struct net *net) +{ + struct nfs_net *nn = net_generic(net, nfs_net_id); + struct cred *cred; + struct key *keyring; + + cred = prepare_kernel_cred(&init_task); + if (!cred) + return -ENOMEM; + keyring = keyring_alloc(".nfs", net->user_ns->owner, + net->user_ns->group, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + (KEY_USR_ALL & ~KEY_USR_SETATTR), + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); + put_cred(cred); + if (IS_ERR(keyring)) + return PTR_ERR(keyring); + nn->nfs_keyring = keyring; + return 0; +} + +static void nfs_exit_keyring(struct nfs_net *nn) +{ + key_put(nn->nfs_keyring); +} +#else +static inline int nfs_init_keyring(struct net *net) +{ + return 0; +} + +static inline void nfs_exit_keyring(struct nfs_net *nn) +{ +} +#endif /* CONFIG_KEYS */ + static int nfs_net_init(struct net *net) { struct nfs_net *nn = net_generic(net, nfs_net_id); int err; + err = nfs_init_keyring(net); + if (err) + return err; + nfs_clients_init(net); if (!rpc_proc_register(net, &nn->rpcstats)) { @@ -2663,14 +2704,18 @@ static int nfs_net_init(struct net *net) rpc_proc_unregister(net, "nfs"); err_proc_rpc: nfs_clients_exit(net); + nfs_exit_keyring(nn); return err; } static void nfs_net_exit(struct net *net) { + struct nfs_net *nn = net_generic(net, nfs_net_id); + rpc_proc_unregister(net, "nfs"); nfs_fs_proc_net_exit(net); nfs_clients_exit(net); + nfs_exit_keyring(nn); } static struct pernet_operations nfs_net_ops = { @@ -2680,35 +2725,6 @@ static struct pernet_operations nfs_net_ops = { .size = sizeof(struct nfs_net), }; -#ifdef CONFIG_KEYS -static struct key *nfs_keyring; - -static int __init nfs_init_keyring(void) -{ - nfs_keyring = keyring_alloc(".nfs", - GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, - current_cred(), - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - (KEY_USR_ALL & ~KEY_USR_SETATTR), - KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); - return PTR_ERR_OR_ZERO(nfs_keyring); -} - -static void nfs_exit_keyring(void) -{ - key_put(nfs_keyring); -} -#else -static inline int nfs_init_keyring(void) -{ - return 0; -} - -static inline void nfs_exit_keyring(void) -{ -} -#endif /* CONFIG_KEYS */ - /* * Initialize NFS */ @@ -2716,13 +2732,9 @@ static int __init init_nfs_fs(void) { int err; - err = nfs_init_keyring(); - if (err) - return err; - err = nfs_sysfs_init(); if (err < 0) - goto err_keyring; + return err; err = register_pernet_subsys(&nfs_net_ops); if (err < 0) @@ -2779,8 +2791,6 @@ static int __init init_nfs_fs(void) unregister_pernet_subsys(&nfs_net_ops); err_sysfs: nfs_sysfs_exit(); -err_keyring: - nfs_exit_keyring(); return err; } @@ -2796,7 +2806,6 @@ static void __exit exit_nfs_fs(void) nfs_fs_proc_exit(); nfsiod_stop(); nfs_sysfs_exit(); - nfs_exit_keyring(); } /* Not quite true; I just maintain it */ diff --git a/fs/nfs/netns.h b/fs/nfs/netns.h index 36658579100d..da0854510404 100644 --- a/fs/nfs/netns.h +++ b/fs/nfs/netns.h @@ -16,6 +16,7 @@ struct bl_dev_msg { uint32_t major, minor; }; +struct key; struct nfs_netns_client; struct nfs_net { @@ -36,6 +37,7 @@ struct nfs_net { #endif /* CONFIG_NFS_V4 */ struct nfs_netns_client *nfs_client; spinlock_t nfs_client_lock; + struct key *nfs_keyring; ktime_t boot_time; struct rpc_stat rpcstats; #ifdef CONFIG_PROC_FS -- 2.55.0