Skip to content

Commit 0850e86

Browse files
jtlaytonchucklever
authored andcommitted
sunrpc: add netlink upcall for the auth.unix.gid cache
Add netlink-based cache upcall support for the unix_gid (auth.unix.gid) cache, using the sunrpc generic netlink family. Add unix-gid attribute-set (seqno, uid, gids multi-attr, negative, expiry), unix-gid-reqs wrapper, and unix-gid-get-reqs / unix-gid-set-reqs operations to the sunrpc_cache YAML spec and generated headers. Implement sunrpc_nl_unix_gid_get_reqs_dumpit() which snapshots pending unix_gid cache requests and sends each entry's seqno and uid over netlink. Implement sunrpc_nl_unix_gid_set_reqs_doit() which parses unix_gid cache responses from userspace (uid, expiry, gids as u32 multi-attr or negative flag) and updates the cache via unix_gid_lookup() / sunrpc_cache_update(). Wire up unix_gid_notify() callback in unix_gid_cache_template so cache misses trigger SUNRPC_CMD_CACHE_NOTIFY multicast events with SUNRPC_CACHE_TYPE_UNIX_GID. Signed-off-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
1 parent 712bdbb commit 0850e86

5 files changed

Lines changed: 336 additions & 0 deletions

File tree

Documentation/netlink/specs/sunrpc_cache.yaml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,33 @@ attribute-sets:
4949
type: nest
5050
nested-attributes: ip-map
5151
multi-attr: true
52+
-
53+
name: unix-gid
54+
attributes:
55+
-
56+
name: seqno
57+
type: u64
58+
-
59+
name: uid
60+
type: u32
61+
-
62+
name: gids
63+
type: u32
64+
multi-attr: true
65+
-
66+
name: negative
67+
type: flag
68+
-
69+
name: expiry
70+
type: u64
71+
-
72+
name: unix-gid-reqs
73+
attributes:
74+
-
75+
name: requests
76+
type: nest
77+
nested-attributes: unix-gid
78+
multi-attr: true
5279

5380
operations:
5481
list:
@@ -78,6 +105,24 @@ operations:
78105
request:
79106
attributes:
80107
- requests
108+
-
109+
name: unix-gid-get-reqs
110+
doc: Dump all pending unix_gid requests
111+
attribute-set: unix-gid-reqs
112+
flags: [admin-perm]
113+
dump:
114+
request:
115+
attributes:
116+
- requests
117+
-
118+
name: unix-gid-set-reqs
119+
doc: Respond to one or more unix_gid requests
120+
attribute-set: unix-gid-reqs
121+
flags: [admin-perm]
122+
do:
123+
request:
124+
attributes:
125+
- requests
81126

82127
mcast-groups:
83128
list:

include/uapi/linux/sunrpc_netlink.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,30 @@ enum {
4141
SUNRPC_A_IP_MAP_REQS_MAX = (__SUNRPC_A_IP_MAP_REQS_MAX - 1)
4242
};
4343

44+
enum {
45+
SUNRPC_A_UNIX_GID_SEQNO = 1,
46+
SUNRPC_A_UNIX_GID_UID,
47+
SUNRPC_A_UNIX_GID_GIDS,
48+
SUNRPC_A_UNIX_GID_NEGATIVE,
49+
SUNRPC_A_UNIX_GID_EXPIRY,
50+
51+
__SUNRPC_A_UNIX_GID_MAX,
52+
SUNRPC_A_UNIX_GID_MAX = (__SUNRPC_A_UNIX_GID_MAX - 1)
53+
};
54+
55+
enum {
56+
SUNRPC_A_UNIX_GID_REQS_REQUESTS = 1,
57+
58+
__SUNRPC_A_UNIX_GID_REQS_MAX,
59+
SUNRPC_A_UNIX_GID_REQS_MAX = (__SUNRPC_A_UNIX_GID_REQS_MAX - 1)
60+
};
61+
4462
enum {
4563
SUNRPC_CMD_CACHE_NOTIFY = 1,
4664
SUNRPC_CMD_IP_MAP_GET_REQS,
4765
SUNRPC_CMD_IP_MAP_SET_REQS,
66+
SUNRPC_CMD_UNIX_GID_GET_REQS,
67+
SUNRPC_CMD_UNIX_GID_SET_REQS,
4868

4969
__SUNRPC_CMD_MAX,
5070
SUNRPC_CMD_MAX = (__SUNRPC_CMD_MAX - 1)

net/sunrpc/netlink.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@ static const struct nla_policy sunrpc_ip_map_set_reqs_nl_policy[SUNRPC_A_IP_MAP_
3232
[SUNRPC_A_IP_MAP_REQS_REQUESTS] = NLA_POLICY_NESTED(sunrpc_ip_map_nl_policy),
3333
};
3434

35+
const struct nla_policy sunrpc_unix_gid_nl_policy[SUNRPC_A_UNIX_GID_EXPIRY + 1] = {
36+
[SUNRPC_A_UNIX_GID_SEQNO] = { .type = NLA_U64, },
37+
[SUNRPC_A_UNIX_GID_UID] = { .type = NLA_U32, },
38+
[SUNRPC_A_UNIX_GID_GIDS] = { .type = NLA_U32, },
39+
[SUNRPC_A_UNIX_GID_NEGATIVE] = { .type = NLA_FLAG, },
40+
[SUNRPC_A_UNIX_GID_EXPIRY] = { .type = NLA_U64, },
41+
};
42+
43+
/* SUNRPC_CMD_UNIX_GID_GET_REQS - dump */
44+
static const struct nla_policy sunrpc_unix_gid_get_reqs_nl_policy[SUNRPC_A_UNIX_GID_REQS_REQUESTS + 1] = {
45+
[SUNRPC_A_UNIX_GID_REQS_REQUESTS] = NLA_POLICY_NESTED(sunrpc_unix_gid_nl_policy),
46+
};
47+
48+
/* SUNRPC_CMD_UNIX_GID_SET_REQS - do */
49+
static const struct nla_policy sunrpc_unix_gid_set_reqs_nl_policy[SUNRPC_A_UNIX_GID_REQS_REQUESTS + 1] = {
50+
[SUNRPC_A_UNIX_GID_REQS_REQUESTS] = NLA_POLICY_NESTED(sunrpc_unix_gid_nl_policy),
51+
};
52+
3553
/* Ops table for sunrpc */
3654
static const struct genl_split_ops sunrpc_nl_ops[] = {
3755
{
@@ -48,6 +66,20 @@ static const struct genl_split_ops sunrpc_nl_ops[] = {
4866
.maxattr = SUNRPC_A_IP_MAP_REQS_REQUESTS,
4967
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
5068
},
69+
{
70+
.cmd = SUNRPC_CMD_UNIX_GID_GET_REQS,
71+
.dumpit = sunrpc_nl_unix_gid_get_reqs_dumpit,
72+
.policy = sunrpc_unix_gid_get_reqs_nl_policy,
73+
.maxattr = SUNRPC_A_UNIX_GID_REQS_REQUESTS,
74+
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
75+
},
76+
{
77+
.cmd = SUNRPC_CMD_UNIX_GID_SET_REQS,
78+
.doit = sunrpc_nl_unix_gid_set_reqs_doit,
79+
.policy = sunrpc_unix_gid_set_reqs_nl_policy,
80+
.maxattr = SUNRPC_A_UNIX_GID_REQS_REQUESTS,
81+
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
82+
},
5183
};
5284

5385
static const struct genl_multicast_group sunrpc_nl_mcgrps[] = {

net/sunrpc/netlink.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@
1414

1515
/* Common nested types */
1616
extern const struct nla_policy sunrpc_ip_map_nl_policy[SUNRPC_A_IP_MAP_EXPIRY + 1];
17+
extern const struct nla_policy sunrpc_unix_gid_nl_policy[SUNRPC_A_UNIX_GID_EXPIRY + 1];
1718

1819
int sunrpc_nl_ip_map_get_reqs_dumpit(struct sk_buff *skb,
1920
struct netlink_callback *cb);
2021
int sunrpc_nl_ip_map_set_reqs_doit(struct sk_buff *skb,
2122
struct genl_info *info);
23+
int sunrpc_nl_unix_gid_get_reqs_dumpit(struct sk_buff *skb,
24+
struct netlink_callback *cb);
25+
int sunrpc_nl_unix_gid_set_reqs_doit(struct sk_buff *skb,
26+
struct genl_info *info);
2227

2328
enum {
2429
SUNRPC_NLGRP_NONE,

net/sunrpc/svcauth_unix.c

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,12 +585,246 @@ static int unix_gid_show(struct seq_file *m,
585585
return 0;
586586
}
587587

588+
static int unix_gid_notify(struct cache_detail *cd, struct cache_head *h)
589+
{
590+
return sunrpc_cache_notify(cd, h, SUNRPC_CACHE_TYPE_UNIX_GID);
591+
}
592+
593+
/**
594+
* sunrpc_nl_unix_gid_get_reqs_dumpit - dump pending unix_gid requests
595+
* @skb: reply buffer
596+
* @cb: netlink metadata and command arguments
597+
*
598+
* Walk the unix_gid cache's pending request list and create a netlink
599+
* message with a nested entry for each cache_request, containing the
600+
* seqno and uid.
601+
*
602+
* Uses cb->args[0] as a seqno cursor for dump continuation across
603+
* multiple netlink messages.
604+
*
605+
* Returns the size of the reply or a negative errno.
606+
*/
607+
int sunrpc_nl_unix_gid_get_reqs_dumpit(struct sk_buff *skb,
608+
struct netlink_callback *cb)
609+
{
610+
struct sunrpc_net *sn;
611+
struct cache_detail *cd;
612+
struct cache_head **items;
613+
u64 *seqnos;
614+
int cnt, i, emitted;
615+
void *hdr;
616+
int ret;
617+
618+
sn = net_generic(sock_net(skb->sk), sunrpc_net_id);
619+
620+
cd = sn->unix_gid_cache;
621+
if (!cd)
622+
return -ENODEV;
623+
624+
cnt = sunrpc_cache_requests_count(cd);
625+
if (!cnt)
626+
return 0;
627+
628+
items = kcalloc(cnt, sizeof(*items), GFP_KERNEL);
629+
seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL);
630+
if (!items || !seqnos) {
631+
ret = -ENOMEM;
632+
goto out_alloc;
633+
}
634+
635+
cnt = sunrpc_cache_requests_snapshot(cd, items, seqnos, cnt,
636+
cb->args[0]);
637+
if (!cnt) {
638+
ret = 0;
639+
goto out_alloc;
640+
}
641+
642+
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
643+
cb->nlh->nlmsg_seq, &sunrpc_nl_family,
644+
NLM_F_MULTI, SUNRPC_CMD_UNIX_GID_GET_REQS);
645+
if (!hdr) {
646+
ret = -ENOBUFS;
647+
goto out_put;
648+
}
649+
650+
emitted = 0;
651+
for (i = 0; i < cnt; i++) {
652+
struct unix_gid *ug;
653+
struct nlattr *nest;
654+
655+
ug = container_of(items[i], struct unix_gid, h);
656+
657+
nest = nla_nest_start(skb,
658+
SUNRPC_A_UNIX_GID_REQS_REQUESTS);
659+
if (!nest)
660+
break;
661+
662+
if (nla_put_u64_64bit(skb, SUNRPC_A_UNIX_GID_SEQNO,
663+
seqnos[i], 0) ||
664+
nla_put_u32(skb, SUNRPC_A_UNIX_GID_UID,
665+
from_kuid(&init_user_ns, ug->uid))) {
666+
nla_nest_cancel(skb, nest);
667+
break;
668+
}
669+
670+
nla_nest_end(skb, nest);
671+
cb->args[0] = seqnos[i];
672+
emitted++;
673+
}
674+
675+
if (!emitted) {
676+
genlmsg_cancel(skb, hdr);
677+
ret = -EMSGSIZE;
678+
goto out_put;
679+
}
680+
681+
genlmsg_end(skb, hdr);
682+
ret = skb->len;
683+
out_put:
684+
for (i = 0; i < cnt; i++)
685+
cache_put(items[i], cd);
686+
out_alloc:
687+
kfree(seqnos);
688+
kfree(items);
689+
return ret;
690+
}
691+
692+
/**
693+
* sunrpc_nl_parse_one_unix_gid - parse one unix_gid entry from netlink
694+
* @cd: cache_detail for the unix_gid cache
695+
* @attr: nested attribute containing unix_gid fields
696+
*
697+
* Parses one unix_gid entry from a netlink message and updates the
698+
* cache. Mirrors the logic in unix_gid_parse().
699+
*
700+
* Returns 0 on success or a negative errno.
701+
*/
702+
static int sunrpc_nl_parse_one_unix_gid(struct cache_detail *cd,
703+
struct nlattr *attr)
704+
{
705+
struct nlattr *tb[SUNRPC_A_UNIX_GID_EXPIRY + 1];
706+
struct unix_gid ug, *ugp;
707+
struct timespec64 boot;
708+
struct nlattr *gid_attr;
709+
int err, rem, gids = 0;
710+
kuid_t uid;
711+
712+
err = nla_parse_nested(tb, SUNRPC_A_UNIX_GID_EXPIRY, attr,
713+
sunrpc_unix_gid_nl_policy, NULL);
714+
if (err)
715+
return err;
716+
717+
/* uid (required) */
718+
if (!tb[SUNRPC_A_UNIX_GID_UID])
719+
return -EINVAL;
720+
uid = make_kuid(current_user_ns(),
721+
nla_get_u32(tb[SUNRPC_A_UNIX_GID_UID]));
722+
ug.uid = uid;
723+
724+
/* expiry (required, wallclock seconds) */
725+
if (!tb[SUNRPC_A_UNIX_GID_EXPIRY])
726+
return -EINVAL;
727+
getboottime64(&boot);
728+
ug.h.flags = 0;
729+
ug.h.expiry_time = nla_get_u64(tb[SUNRPC_A_UNIX_GID_EXPIRY]) -
730+
boot.tv_sec;
731+
732+
if (tb[SUNRPC_A_UNIX_GID_NEGATIVE]) {
733+
ug.gi = groups_alloc(0);
734+
if (!ug.gi)
735+
return -ENOMEM;
736+
} else {
737+
/* Count gids */
738+
nla_for_each_nested_type(gid_attr, SUNRPC_A_UNIX_GID_GIDS,
739+
attr, rem)
740+
gids++;
741+
742+
if (gids > 8192)
743+
return -EINVAL;
744+
745+
ug.gi = groups_alloc(gids);
746+
if (!ug.gi)
747+
return -ENOMEM;
748+
749+
gids = 0;
750+
nla_for_each_nested_type(gid_attr, SUNRPC_A_UNIX_GID_GIDS,
751+
attr, rem) {
752+
kgid_t kgid;
753+
754+
kgid = make_kgid(current_user_ns(),
755+
nla_get_u32(gid_attr));
756+
if (!gid_valid(kgid)) {
757+
err = -EINVAL;
758+
goto out;
759+
}
760+
ug.gi->gid[gids++] = kgid;
761+
}
762+
groups_sort(ug.gi);
763+
}
764+
765+
ugp = unix_gid_lookup(cd, uid);
766+
if (ugp) {
767+
struct cache_head *ch;
768+
769+
ch = sunrpc_cache_update(cd, &ug.h, &ugp->h,
770+
unix_gid_hash(uid));
771+
if (!ch) {
772+
err = -ENOMEM;
773+
} else {
774+
err = 0;
775+
cache_put(ch, cd);
776+
}
777+
} else {
778+
err = -ENOMEM;
779+
}
780+
out:
781+
if (ug.gi)
782+
put_group_info(ug.gi);
783+
return err;
784+
}
785+
786+
/**
787+
* sunrpc_nl_unix_gid_set_reqs_doit - respond to unix_gid requests
788+
* @skb: reply buffer
789+
* @info: netlink metadata and command arguments
790+
*
791+
* Parse one or more unix_gid cache responses from userspace and
792+
* update the unix_gid cache accordingly.
793+
*
794+
* Returns 0 on success or a negative errno.
795+
*/
796+
int sunrpc_nl_unix_gid_set_reqs_doit(struct sk_buff *skb,
797+
struct genl_info *info)
798+
{
799+
struct sunrpc_net *sn;
800+
struct cache_detail *cd;
801+
const struct nlattr *attr;
802+
int rem, ret = 0;
803+
804+
sn = net_generic(genl_info_net(info), sunrpc_net_id);
805+
806+
cd = sn->unix_gid_cache;
807+
if (!cd)
808+
return -ENODEV;
809+
810+
nlmsg_for_each_attr_type(attr, SUNRPC_A_UNIX_GID_REQS_REQUESTS,
811+
info->nlhdr, GENL_HDRLEN, rem) {
812+
ret = sunrpc_nl_parse_one_unix_gid(cd,
813+
(struct nlattr *)attr);
814+
if (ret)
815+
break;
816+
}
817+
818+
return ret;
819+
}
820+
588821
static const struct cache_detail unix_gid_cache_template = {
589822
.owner = THIS_MODULE,
590823
.hash_size = GID_HASHMAX,
591824
.name = "auth.unix.gid",
592825
.cache_put = unix_gid_put,
593826
.cache_upcall = unix_gid_upcall,
827+
.cache_notify = unix_gid_notify,
594828
.cache_request = unix_gid_request,
595829
.cache_parse = unix_gid_parse,
596830
.cache_show = unix_gid_show,

0 commit comments

Comments
 (0)