Commit 62346217 authored by Benjamin Coddington's avatar Benjamin Coddington Committed by Chuck Lever
Browse files

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: default avatarBenjamin Coddington <bcodding@hammerspace.com>
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 116b6b7a
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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
+3 −2
Original line number Diff line number Diff line
@@ -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,
	},
	{
+1 −0
Original line number Diff line number Diff line
@@ -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 */
+37 −1
Original line number Diff line number Diff line
@@ -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);
+22 −0
Original line number Diff line number Diff line
@@ -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
Loading