NFSD: Add a key for signing filehandles

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 via the netlink interface.

Link: https://lore.kernel.org/linux-nfs/cover.1772022373.git.bcodding@hammerspace.com
Signed-off-by: Benjamin Coddington <bcodding@hammerspace.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
Benjamin Coddington 2026-02-25 07:51:36 -05:00 committed by Chuck Lever
parent 116b6b7acd
commit 62346217fd
6 changed files with 70 additions and 3 deletions

View File

@ -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

View File

@ -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_FH_KEY,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{

View File

@ -227,6 +227,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 */

View File

@ -1581,6 +1581,32 @@ 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 (!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
@ -1622,7 +1648,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;
@ -1651,6 +1678,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];
@ -2237,6 +2272,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);

View File

@ -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

View File

@ -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)