A future patch will enable NFSD to sign filehandles by appending a Message Authentication Code(MAC). To do this, NFSD requires a secret 128-bit key that can persist across reboots. A persisted key allows the server to accept filehandles after a restart. Enable NFSD to be configured with this key the netlink interface. Link: https://lore.kernel.org/linux-nfs/8C67F451-980D-4739-B044-F8562B2A8B74@hammerspace.com Signed-off-by: Benjamin Coddington Reviewed-by: Jeff Layton --- Documentation/netlink/specs/nfsd.yaml | 6 ++++ fs/nfsd/netlink.c | 5 ++-- fs/nfsd/netns.h | 1 + fs/nfsd/nfsctl.c | 41 ++++++++++++++++++++++++++- fs/nfsd/trace.h | 22 ++++++++++++++ include/uapi/linux/nfsd_netlink.h | 1 + 6 files changed, 73 insertions(+), 3 deletions(-) diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml index f87b5a05e5e9..8ab43c8253b2 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -81,6 +81,11 @@ attribute-sets: - name: min-threads type: u32 + - + name: fh-key + type: binary + checks: + exact-len: 16 - name: version attributes: @@ -163,6 +168,7 @@ operations: - leasetime - scope - min-threads + - fh-key - name: threads-get doc: get the maximum number of running threads diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index 887525964451..4e08c1a6b394 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -24,12 +24,13 @@ const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1] = { }; /* NFSD_CMD_THREADS_SET - do */ -static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_MIN_THREADS + 1] = { +static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_FH_KEY + 1] = { [NFSD_A_SERVER_THREADS] = { .type = NLA_U32, }, [NFSD_A_SERVER_GRACETIME] = { .type = NLA_U32, }, [NFSD_A_SERVER_LEASETIME] = { .type = NLA_U32, }, [NFSD_A_SERVER_SCOPE] = { .type = NLA_NUL_STRING, }, [NFSD_A_SERVER_MIN_THREADS] = { .type = NLA_U32, }, + [NFSD_A_SERVER_FH_KEY] = NLA_POLICY_EXACT_LEN(16), }; /* NFSD_CMD_VERSION_SET - do */ @@ -58,7 +59,7 @@ static const struct genl_split_ops nfsd_nl_ops[] = { .cmd = NFSD_CMD_THREADS_SET, .doit = nfsd_nl_threads_set_doit, .policy = nfsd_threads_set_nl_policy, - .maxattr = NFSD_A_SERVER_MIN_THREADS, + .maxattr = NFSD_A_SERVER_MAX, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 9fa600602658..0071cc25fbc2 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -224,6 +224,7 @@ struct nfsd_net { spinlock_t local_clients_lock; struct list_head local_clients; #endif + siphash_key_t *fh_key; }; /* Simple check to find out if a given net was properly initialized */ diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index a58eb1adac0f..bdf7311041d6 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -1571,6 +1571,35 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb, return ret; } +/** + * nfsd_nl_fh_key_set - helper to copy fh_key from userspace + * @attr: nlattr NFSD_A_SERVER_FH_KEY + * @nn: nfsd_net + * + * Callers should hold nfsd_mutex, returns 0 on success or negative errno. + * Callers must ensure the server is shut down (sv_nrthreads == 0), + * userspace documentation asserts the key may only be set when the server + * is not running. + */ +static int nfsd_nl_fh_key_set(const struct nlattr *attr, struct nfsd_net *nn) +{ + siphash_key_t *fh_key = nn->fh_key; + + if (nla_len(attr) != sizeof(siphash_key_t)) + return -EINVAL; + + if (!fh_key) { + fh_key = kmalloc(sizeof(siphash_key_t), GFP_KERNEL); + if (!fh_key) + return -ENOMEM; + nn->fh_key = fh_key; + } + + fh_key->key[0] = get_unaligned_le64(nla_data(attr)); + fh_key->key[1] = get_unaligned_le64(nla_data(attr) + 8); + return 0; +} + /** * nfsd_nl_threads_set_doit - set the number of running threads * @skb: reply buffer @@ -1612,7 +1641,8 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NFSD_A_SERVER_GRACETIME] || info->attrs[NFSD_A_SERVER_LEASETIME] || - info->attrs[NFSD_A_SERVER_SCOPE]) { + info->attrs[NFSD_A_SERVER_SCOPE] || + info->attrs[NFSD_A_SERVER_FH_KEY]) { ret = -EBUSY; if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads) goto out_unlock; @@ -1641,6 +1671,14 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info) attr = info->attrs[NFSD_A_SERVER_SCOPE]; if (attr) scope = nla_data(attr); + + attr = info->attrs[NFSD_A_SERVER_FH_KEY]; + if (attr) { + ret = nfsd_nl_fh_key_set(attr, nn); + trace_nfsd_ctl_fh_key_set((const char *)nn->fh_key, ret); + if (ret) + goto out_unlock; + } } attr = info->attrs[NFSD_A_SERVER_MIN_THREADS]; @@ -2240,6 +2278,7 @@ static __net_exit void nfsd_net_exit(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); + kfree_sensitive(nn->fh_key); nfsd_proc_stat_shutdown(net); percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM); nfsd_idmap_shutdown(net); diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index d1d0b0dd0545..185a998996a0 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -2240,6 +2240,28 @@ TRACE_EVENT(nfsd_end_grace, ) ); +TRACE_EVENT(nfsd_ctl_fh_key_set, + TP_PROTO( + const char *key, + int result + ), + TP_ARGS(key, result), + TP_STRUCT__entry( + __field(u32, key_hash) + __field(int, result) + ), + TP_fast_assign( + if (key) + __entry->key_hash = ~crc32_le(0xFFFFFFFF, key, 16); + else + __entry->key_hash = 0; + __entry->result = result; + ), + TP_printk("key=0x%08x result=%d", + __entry->key_hash, __entry->result + ) +); + DECLARE_EVENT_CLASS(nfsd_copy_class, TP_PROTO( const struct nfsd4_copy *copy diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h index e9efbc9e63d8..97c7447f4d14 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -36,6 +36,7 @@ enum { NFSD_A_SERVER_LEASETIME, NFSD_A_SERVER_SCOPE, NFSD_A_SERVER_MIN_THREADS, + NFSD_A_SERVER_FH_KEY, __NFSD_A_SERVER_MAX, NFSD_A_SERVER_MAX = (__NFSD_A_SERVER_MAX - 1) -- 2.50.1