1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * linux/net/sunrpc/auth_unix.c |
4 | * |
5 | * UNIX-style authentication; no AUTH_SHORT support |
6 | * |
7 | * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> |
8 | */ |
9 | |
10 | #include <linux/slab.h> |
11 | #include <linux/types.h> |
12 | #include <linux/sched.h> |
13 | #include <linux/module.h> |
14 | #include <linux/mempool.h> |
15 | #include <linux/sunrpc/clnt.h> |
16 | #include <linux/sunrpc/auth.h> |
17 | #include <linux/user_namespace.h> |
18 | |
19 | |
20 | #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) |
21 | # define RPCDBG_FACILITY RPCDBG_AUTH |
22 | #endif |
23 | |
24 | static struct rpc_auth unix_auth; |
25 | static const struct rpc_credops unix_credops; |
26 | static mempool_t *unix_pool; |
27 | |
28 | static struct rpc_auth * |
29 | unx_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) |
30 | { |
31 | refcount_inc(r: &unix_auth.au_count); |
32 | return &unix_auth; |
33 | } |
34 | |
35 | static void |
36 | unx_destroy(struct rpc_auth *auth) |
37 | { |
38 | } |
39 | |
40 | /* |
41 | * Lookup AUTH_UNIX creds for current process |
42 | */ |
43 | static struct rpc_cred *unx_lookup_cred(struct rpc_auth *auth, |
44 | struct auth_cred *acred, int flags) |
45 | { |
46 | struct rpc_cred *ret; |
47 | |
48 | ret = kmalloc(size: sizeof(*ret), flags: rpc_task_gfp_mask()); |
49 | if (!ret) { |
50 | if (!(flags & RPCAUTH_LOOKUP_ASYNC)) |
51 | return ERR_PTR(error: -ENOMEM); |
52 | ret = mempool_alloc(pool: unix_pool, GFP_NOWAIT); |
53 | if (!ret) |
54 | return ERR_PTR(error: -ENOMEM); |
55 | } |
56 | rpcauth_init_cred(ret, acred, auth, &unix_credops); |
57 | ret->cr_flags = 1UL << RPCAUTH_CRED_UPTODATE; |
58 | return ret; |
59 | } |
60 | |
61 | static void |
62 | unx_free_cred_callback(struct rcu_head *head) |
63 | { |
64 | struct rpc_cred *rpc_cred = container_of(head, struct rpc_cred, cr_rcu); |
65 | |
66 | put_cred(cred: rpc_cred->cr_cred); |
67 | mempool_free(element: rpc_cred, pool: unix_pool); |
68 | } |
69 | |
70 | static void |
71 | unx_destroy_cred(struct rpc_cred *cred) |
72 | { |
73 | call_rcu(head: &cred->cr_rcu, func: unx_free_cred_callback); |
74 | } |
75 | |
76 | /* |
77 | * Match credentials against current the auth_cred. |
78 | */ |
79 | static int |
80 | unx_match(struct auth_cred *acred, struct rpc_cred *cred, int flags) |
81 | { |
82 | unsigned int groups = 0; |
83 | unsigned int i; |
84 | |
85 | if (cred->cr_cred == acred->cred) |
86 | return 1; |
87 | |
88 | if (!uid_eq(left: cred->cr_cred->fsuid, right: acred->cred->fsuid) || !gid_eq(left: cred->cr_cred->fsgid, right: acred->cred->fsgid)) |
89 | return 0; |
90 | |
91 | if (acred->cred->group_info != NULL) |
92 | groups = acred->cred->group_info->ngroups; |
93 | if (groups > UNX_NGROUPS) |
94 | groups = UNX_NGROUPS; |
95 | if (cred->cr_cred->group_info == NULL) |
96 | return groups == 0; |
97 | if (groups != cred->cr_cred->group_info->ngroups) |
98 | return 0; |
99 | |
100 | for (i = 0; i < groups ; i++) |
101 | if (!gid_eq(left: cred->cr_cred->group_info->gid[i], right: acred->cred->group_info->gid[i])) |
102 | return 0; |
103 | return 1; |
104 | } |
105 | |
106 | /* |
107 | * Marshal credentials. |
108 | * Maybe we should keep a cached credential for performance reasons. |
109 | */ |
110 | static int |
111 | unx_marshal(struct rpc_task *task, struct xdr_stream *xdr) |
112 | { |
113 | struct rpc_clnt *clnt = task->tk_client; |
114 | struct rpc_cred *cred = task->tk_rqstp->rq_cred; |
115 | __be32 *p, *cred_len, *gidarr_len; |
116 | int i; |
117 | struct group_info *gi = cred->cr_cred->group_info; |
118 | struct user_namespace *userns = clnt->cl_cred ? |
119 | clnt->cl_cred->user_ns : &init_user_ns; |
120 | |
121 | /* Credential */ |
122 | |
123 | p = xdr_reserve_space(xdr, nbytes: 3 * sizeof(*p)); |
124 | if (!p) |
125 | goto marshal_failed; |
126 | *p++ = rpc_auth_unix; |
127 | cred_len = p++; |
128 | *p++ = xdr_zero; /* stamp */ |
129 | if (xdr_stream_encode_opaque(xdr, ptr: clnt->cl_nodename, |
130 | len: clnt->cl_nodelen) < 0) |
131 | goto marshal_failed; |
132 | p = xdr_reserve_space(xdr, nbytes: 3 * sizeof(*p)); |
133 | if (!p) |
134 | goto marshal_failed; |
135 | *p++ = cpu_to_be32(from_kuid_munged(userns, cred->cr_cred->fsuid)); |
136 | *p++ = cpu_to_be32(from_kgid_munged(userns, cred->cr_cred->fsgid)); |
137 | |
138 | gidarr_len = p++; |
139 | if (gi) |
140 | for (i = 0; i < UNX_NGROUPS && i < gi->ngroups; i++) |
141 | *p++ = cpu_to_be32(from_kgid_munged(userns, gi->gid[i])); |
142 | *gidarr_len = cpu_to_be32(p - gidarr_len - 1); |
143 | *cred_len = cpu_to_be32((p - cred_len - 1) << 2); |
144 | p = xdr_reserve_space(xdr, nbytes: (p - gidarr_len - 1) << 2); |
145 | if (!p) |
146 | goto marshal_failed; |
147 | |
148 | /* Verifier */ |
149 | |
150 | p = xdr_reserve_space(xdr, nbytes: 2 * sizeof(*p)); |
151 | if (!p) |
152 | goto marshal_failed; |
153 | *p++ = rpc_auth_null; |
154 | *p = xdr_zero; |
155 | |
156 | return 0; |
157 | |
158 | marshal_failed: |
159 | return -EMSGSIZE; |
160 | } |
161 | |
162 | /* |
163 | * Refresh credentials. This is a no-op for AUTH_UNIX |
164 | */ |
165 | static int |
166 | unx_refresh(struct rpc_task *task) |
167 | { |
168 | set_bit(RPCAUTH_CRED_UPTODATE, addr: &task->tk_rqstp->rq_cred->cr_flags); |
169 | return 0; |
170 | } |
171 | |
172 | static int |
173 | unx_validate(struct rpc_task *task, struct xdr_stream *xdr) |
174 | { |
175 | struct rpc_auth *auth = task->tk_rqstp->rq_cred->cr_auth; |
176 | __be32 *p; |
177 | u32 size; |
178 | |
179 | p = xdr_inline_decode(xdr, nbytes: 2 * sizeof(*p)); |
180 | if (!p) |
181 | return -EIO; |
182 | switch (*p++) { |
183 | case rpc_auth_null: |
184 | case rpc_auth_unix: |
185 | case rpc_auth_short: |
186 | break; |
187 | default: |
188 | return -EIO; |
189 | } |
190 | size = be32_to_cpup(p); |
191 | if (size > RPC_MAX_AUTH_SIZE) |
192 | return -EIO; |
193 | p = xdr_inline_decode(xdr, nbytes: size); |
194 | if (!p) |
195 | return -EIO; |
196 | |
197 | auth->au_verfsize = XDR_QUADLEN(size) + 2; |
198 | auth->au_rslack = XDR_QUADLEN(size) + 2; |
199 | auth->au_ralign = XDR_QUADLEN(size) + 2; |
200 | return 0; |
201 | } |
202 | |
203 | int __init rpc_init_authunix(void) |
204 | { |
205 | unix_pool = mempool_create_kmalloc_pool(min_nr: 16, size: sizeof(struct rpc_cred)); |
206 | return unix_pool ? 0 : -ENOMEM; |
207 | } |
208 | |
209 | void rpc_destroy_authunix(void) |
210 | { |
211 | mempool_destroy(pool: unix_pool); |
212 | } |
213 | |
214 | const struct rpc_authops authunix_ops = { |
215 | .owner = THIS_MODULE, |
216 | .au_flavor = RPC_AUTH_UNIX, |
217 | .au_name = "UNIX" , |
218 | .create = unx_create, |
219 | .destroy = unx_destroy, |
220 | .lookup_cred = unx_lookup_cred, |
221 | }; |
222 | |
223 | static |
224 | struct rpc_auth unix_auth = { |
225 | .au_cslack = UNX_CALLSLACK, |
226 | .au_rslack = NUL_REPLYSLACK, |
227 | .au_verfsize = NUL_REPLYSLACK, |
228 | .au_ops = &authunix_ops, |
229 | .au_flavor = RPC_AUTH_UNIX, |
230 | .au_count = REFCOUNT_INIT(1), |
231 | }; |
232 | |
233 | static |
234 | const struct rpc_credops unix_credops = { |
235 | .cr_name = "AUTH_UNIX" , |
236 | .crdestroy = unx_destroy_cred, |
237 | .crmatch = unx_match, |
238 | .crmarshal = unx_marshal, |
239 | .crwrap_req = rpcauth_wrap_req_encode, |
240 | .crrefresh = unx_refresh, |
241 | .crvalidate = unx_validate, |
242 | .crunwrap_resp = rpcauth_unwrap_resp_decode, |
243 | }; |
244 | |