1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 * Copyright (c) 1990 The Regents of the University of California.
4 *
5 * Copyright (c) 2008 Doug Rabson
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29 /*
30 svc_rpcsec_gss.c
31
32 Copyright (c) 2000 The Regents of the University of Michigan.
33 All rights reserved.
34
35 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
36 All rights reserved, all wrongs reversed.
37
38 Redistribution and use in source and binary forms, with or without
39 modification, are permitted provided that the following conditions
40 are met:
41
42 1. Redistributions of source code must retain the above copyright
43 notice, this list of conditions and the following disclaimer.
44 2. Redistributions in binary form must reproduce the above copyright
45 notice, this list of conditions and the following disclaimer in the
46 documentation and/or other materials provided with the distribution.
47 3. Neither the name of the University nor the names of its
48 contributors may be used to endorse or promote products derived
49 from this software without specific prior written permission.
50
51 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
52 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
54 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
58 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
59 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
60 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
61 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62
63 $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
64 */
65
66 #include <sys/param.h>
67 #include <sys/systm.h>
68 #include <sys/jail.h>
69 #include <sys/kernel.h>
70 #include <sys/kobj.h>
71 #include <sys/lock.h>
72 #include <sys/malloc.h>
73 #include <sys/mbuf.h>
74 #include <sys/mutex.h>
75 #include <sys/proc.h>
76 #include <sys/sx.h>
77 #include <sys/ucred.h>
78
79 #include <rpc/rpc.h>
80 #include <rpc/rpcsec_gss.h>
81
82 #include "rpcsec_gss_int.h"
83
84 static bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
85 static bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
86 static void svc_rpc_gss_release(SVCAUTH *);
87 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
88 static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
89
90 static const struct svc_auth_ops svc_auth_gss_ops = {
91 .svc_ah_wrap = svc_rpc_gss_wrap,
92 .svc_ah_unwrap = svc_rpc_gss_unwrap,
93 .svc_ah_release = svc_rpc_gss_release,
94 };
95
96 struct sx svc_rpc_gss_lock;
97
98 struct svc_rpc_gss_callback {
99 SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
100 rpc_gss_callback_t cb_callback;
101 };
102 SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback);
103 KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_callback_list,
104 svc_rpc_gss_callbacks) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
105
106 struct svc_rpc_gss_svc_name {
107 SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
108 char *sn_principal;
109 gss_OID sn_mech;
110 u_int sn_req_time;
111 gss_cred_id_t sn_cred;
112 u_int sn_program;
113 u_int sn_version;
114 };
115 SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name);
116 KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_svc_name_list,
117 svc_rpc_gss_svc_names) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
118
119 enum svc_rpc_gss_client_state {
120 CLIENT_NEW, /* still authenticating */
121 CLIENT_ESTABLISHED, /* context established */
122 CLIENT_STALE /* garbage to collect */
123 };
124
125 #define SVC_RPC_GSS_SEQWINDOW 128
126
127 struct svc_rpc_gss_clientid {
128 unsigned long ci_hostid;
129 uint32_t ci_boottime;
130 uint32_t ci_id;
131 };
132
133 struct svc_rpc_gss_client {
134 TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
135 TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
136 volatile u_int cl_refs;
137 struct sx cl_lock;
138 struct svc_rpc_gss_clientid cl_id;
139 time_t cl_expiration; /* when to gc */
140 enum svc_rpc_gss_client_state cl_state; /* client state */
141 bool_t cl_locked; /* fixed service+qop */
142 gss_ctx_id_t cl_ctx; /* context id */
143 gss_cred_id_t cl_creds; /* delegated creds */
144 gss_name_t cl_cname; /* client name */
145 struct svc_rpc_gss_svc_name *cl_sname; /* server name used */
146 rpc_gss_rawcred_t cl_rawcred; /* raw credentials */
147 rpc_gss_ucred_t cl_ucred; /* unix-style credentials */
148 struct ucred *cl_cred; /* kernel-style credentials */
149 int cl_rpcflavor; /* RPC pseudo sec flavor */
150 bool_t cl_done_callback; /* TRUE after call */
151 void *cl_cookie; /* user cookie from callback */
152 gid_t cl_gid_storage[NGROUPS];
153 gss_OID cl_mech; /* mechanism */
154 gss_qop_t cl_qop; /* quality of protection */
155 uint32_t cl_seqlast; /* sequence window origin */
156 uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
157 };
158 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
159
160 /*
161 * This structure holds enough information to unwrap arguments or wrap
162 * results for a given request. We use the rq_clntcred area for this
163 * (which is a per-request buffer).
164 */
165 struct svc_rpc_gss_cookedcred {
166 struct svc_rpc_gss_client *cc_client;
167 rpc_gss_service_t cc_service;
168 uint32_t cc_seq;
169 };
170
171 #define CLIENT_HASH_SIZE 256
172 #define CLIENT_MAX 1024
173 u_int svc_rpc_gss_client_max = CLIENT_MAX;
174 u_int svc_rpc_gss_client_hash_size = CLIENT_HASH_SIZE;
175
176 SYSCTL_DECL(_kern_rpc);
177 SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
178 "GSS");
179
180 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW,
181 &svc_rpc_gss_client_max, 0,
182 "Max number of rpc-gss clients");
183
184 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_hash, CTLFLAG_RDTUN,
185 &svc_rpc_gss_client_hash_size, 0,
186 "Size of rpc-gss client hash table");
187
188 static u_int svc_rpc_gss_lifetime_max = 0;
189 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, lifetime_max, CTLFLAG_RW,
190 &svc_rpc_gss_lifetime_max, 0,
191 "Maximum lifetime (seconds) of rpc-gss clients");
192
193 static u_int svc_rpc_gss_client_count;
194 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
195 &svc_rpc_gss_client_count, 0,
196 "Number of rpc-gss clients");
197
198 KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list *, svc_rpc_gss_client_hash);
199 KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list, svc_rpc_gss_clients);
200 KGSS_VNET_DEFINE_STATIC(uint32_t, svc_rpc_gss_next_clientid) = 1;
201
202 static void
svc_rpc_gss_init(void * unused __unused)203 svc_rpc_gss_init(void *unused __unused)
204 {
205
206 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
207 sx_init(&svc_rpc_gss_lock, "gsslock");
208 }
209 SYSINIT(svc_rpc_gss_init, SI_SUB_VFS, SI_ORDER_ANY,
210 svc_rpc_gss_init, NULL);
211
212 static void
svc_rpc_gss_cleanup(void * unused __unused)213 svc_rpc_gss_cleanup(void *unused __unused)
214 {
215
216 sx_destroy(&svc_rpc_gss_lock);
217 }
218 SYSUNINIT(svc_rpc_gss_cleanup, SI_SUB_VFS, SI_ORDER_ANY,
219 svc_rpc_gss_cleanup, NULL);
220
221 static void
svc_rpc_gss_vnetinit(void * unused __unused)222 svc_rpc_gss_vnetinit(void *unused __unused)
223 {
224 int i;
225
226 KGSS_VNET(svc_rpc_gss_client_hash) = mem_alloc(
227 sizeof(struct svc_rpc_gss_client_list) *
228 svc_rpc_gss_client_hash_size);
229 for (i = 0; i < svc_rpc_gss_client_hash_size; i++)
230 TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_client_hash)[i]);
231 TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_clients));
232 }
233 VNET_SYSINIT(svc_rpc_gss_vnetinit, SI_SUB_VNET_DONE, SI_ORDER_ANY,
234 svc_rpc_gss_vnetinit, NULL);
235
236 static void
svc_rpc_gss_vnet_cleanup(void * unused __unused)237 svc_rpc_gss_vnet_cleanup(void *unused __unused)
238 {
239
240 mem_free(KGSS_VNET(svc_rpc_gss_client_hash),
241 sizeof(struct svc_rpc_gss_client_list) *
242 svc_rpc_gss_client_hash_size);
243 }
244 VNET_SYSUNINIT(svc_rpc_gss_vnet_cleanup, SI_SUB_VNET_DONE, SI_ORDER_ANY,
245 svc_rpc_gss_vnet_cleanup, NULL);
246
247 bool_t
rpc_gss_set_callback(rpc_gss_callback_t * cb)248 rpc_gss_set_callback(rpc_gss_callback_t *cb)
249 {
250 struct svc_rpc_gss_callback *scb;
251
252 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
253 if (!scb) {
254 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
255 return (FALSE);
256 }
257 scb->cb_callback = *cb;
258 sx_xlock(&svc_rpc_gss_lock);
259 SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_callbacks), scb, cb_link);
260 sx_xunlock(&svc_rpc_gss_lock);
261
262 return (TRUE);
263 }
264
265 void
rpc_gss_clear_callback(rpc_gss_callback_t * cb)266 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
267 {
268 struct svc_rpc_gss_callback *scb;
269
270 sx_xlock(&svc_rpc_gss_lock);
271 SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
272 if (scb->cb_callback.program == cb->program
273 && scb->cb_callback.version == cb->version
274 && scb->cb_callback.callback == cb->callback) {
275 SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_callbacks), scb,
276 svc_rpc_gss_callback, cb_link);
277 sx_xunlock(&svc_rpc_gss_lock);
278 mem_free(scb, sizeof(*scb));
279 return;
280 }
281 }
282 sx_xunlock(&svc_rpc_gss_lock);
283 }
284
285 static bool_t
rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name * sname)286 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
287 {
288 OM_uint32 maj_stat, min_stat;
289 gss_buffer_desc namebuf;
290 gss_name_t name;
291 gss_OID_set_desc oid_set;
292
293 oid_set.count = 1;
294 oid_set.elements = sname->sn_mech;
295
296 namebuf.value = (void *) sname->sn_principal;
297 namebuf.length = strlen(sname->sn_principal);
298
299 maj_stat = gss_import_name(&min_stat, &namebuf,
300 GSS_C_NT_HOSTBASED_SERVICE, &name);
301 if (maj_stat != GSS_S_COMPLETE)
302 return (FALSE);
303
304 if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
305 gss_release_cred(&min_stat, &sname->sn_cred);
306
307 maj_stat = gss_acquire_cred(&min_stat, name,
308 sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
309 NULL, NULL);
310 if (maj_stat != GSS_S_COMPLETE) {
311 gss_release_name(&min_stat, &name);
312 return (FALSE);
313 }
314 gss_release_name(&min_stat, &name);
315
316 return (TRUE);
317 }
318
319 bool_t
rpc_gss_set_svc_name(const char * principal,const char * mechanism,u_int req_time,u_int program,u_int version)320 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
321 u_int req_time, u_int program, u_int version)
322 {
323 struct svc_rpc_gss_svc_name *sname;
324 gss_OID mech_oid;
325
326 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
327 return (FALSE);
328
329 sname = mem_alloc(sizeof(*sname));
330 if (!sname)
331 return (FALSE);
332 sname->sn_principal = strdup(principal, M_RPC);
333 sname->sn_mech = mech_oid;
334 sname->sn_req_time = req_time;
335 sname->sn_cred = GSS_C_NO_CREDENTIAL;
336 sname->sn_program = program;
337 sname->sn_version = version;
338
339 if (!rpc_gss_acquire_svc_cred(sname)) {
340 free(sname->sn_principal, M_RPC);
341 mem_free(sname, sizeof(*sname));
342 return (FALSE);
343 }
344
345 sx_xlock(&svc_rpc_gss_lock);
346 SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_svc_names), sname, sn_link);
347 sx_xunlock(&svc_rpc_gss_lock);
348
349 return (TRUE);
350 }
351
352 void
rpc_gss_clear_svc_name(u_int program,u_int version)353 rpc_gss_clear_svc_name(u_int program, u_int version)
354 {
355 OM_uint32 min_stat;
356 struct svc_rpc_gss_svc_name *sname;
357
358 sx_xlock(&svc_rpc_gss_lock);
359 SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names), sn_link) {
360 if (sname->sn_program == program
361 && sname->sn_version == version) {
362 SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_svc_names), sname,
363 svc_rpc_gss_svc_name, sn_link);
364 sx_xunlock(&svc_rpc_gss_lock);
365 gss_release_cred(&min_stat, &sname->sn_cred);
366 free(sname->sn_principal, M_RPC);
367 mem_free(sname, sizeof(*sname));
368 return;
369 }
370 }
371 sx_xunlock(&svc_rpc_gss_lock);
372 }
373
374 bool_t
rpc_gss_get_principal_name(rpc_gss_principal_t * principal,const char * mech,const char * name,const char * node,const char * domain)375 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
376 const char *mech, const char *name, const char *node, const char *domain)
377 {
378 OM_uint32 maj_stat, min_stat;
379 gss_OID mech_oid;
380 size_t namelen;
381 gss_buffer_desc buf;
382 gss_name_t gss_name, gss_mech_name;
383 rpc_gss_principal_t result;
384
385 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
386 return (FALSE);
387
388 /*
389 * Construct a gss_buffer containing the full name formatted
390 * as "name/node@domain" where node and domain are optional.
391 */
392 namelen = strlen(name) + 1;
393 if (node) {
394 namelen += strlen(node) + 1;
395 }
396 if (domain) {
397 namelen += strlen(domain) + 1;
398 }
399
400 buf.value = mem_alloc(namelen);
401 buf.length = namelen;
402 strcpy((char *) buf.value, name);
403 if (node) {
404 strcat((char *) buf.value, "/");
405 strcat((char *) buf.value, node);
406 }
407 if (domain) {
408 strcat((char *) buf.value, "@");
409 strcat((char *) buf.value, domain);
410 }
411
412 /*
413 * Convert that to a gss_name_t and then convert that to a
414 * mechanism name in the selected mechanism.
415 */
416 maj_stat = gss_import_name(&min_stat, &buf,
417 GSS_C_NT_USER_NAME, &gss_name);
418 mem_free(buf.value, buf.length);
419 if (maj_stat != GSS_S_COMPLETE) {
420 rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
421 return (FALSE);
422 }
423 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
424 &gss_mech_name);
425 if (maj_stat != GSS_S_COMPLETE) {
426 rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
427 min_stat);
428 gss_release_name(&min_stat, &gss_name);
429 return (FALSE);
430 }
431 gss_release_name(&min_stat, &gss_name);
432
433 /*
434 * Export the mechanism name and use that to construct the
435 * rpc_gss_principal_t result.
436 */
437 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
438 if (maj_stat != GSS_S_COMPLETE) {
439 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
440 gss_release_name(&min_stat, &gss_mech_name);
441 return (FALSE);
442 }
443 gss_release_name(&min_stat, &gss_mech_name);
444
445 result = mem_alloc(sizeof(int) + buf.length);
446 if (!result) {
447 gss_release_buffer(&min_stat, &buf);
448 return (FALSE);
449 }
450 result->len = buf.length;
451 memcpy(result->name, buf.value, buf.length);
452 gss_release_buffer(&min_stat, &buf);
453
454 *principal = result;
455 return (TRUE);
456 }
457
458 /*
459 * Note that the ip_addr and srv_principal pointers can point to the same
460 * buffer, so long as ip_addr is at least strlen(srv_name) + 1 > srv_principal.
461 */
462 bool_t
rpc_gss_ip_to_srv_principal(char * ip_addr,const char * srv_name,char * srv_principal)463 rpc_gss_ip_to_srv_principal(char *ip_addr, const char *srv_name,
464 char *srv_principal)
465 {
466 OM_uint32 maj_stat, min_stat;
467 size_t len;
468
469 /*
470 * First fill in the service name and '@'.
471 */
472 len = strlen(srv_name);
473 if (len > NI_MAXSERV)
474 return (FALSE);
475 memcpy(srv_principal, srv_name, len);
476 srv_principal[len] = '@';
477
478 /*
479 * Do reverse DNS to get the DNS name for the ip_addr.
480 */
481 maj_stat = gss_ip_to_dns(&min_stat, ip_addr, &srv_principal[len + 1]);
482 if (maj_stat != GSS_S_COMPLETE) {
483 rpc_gss_log_status("gss_ip_to_dns", NULL, maj_stat, min_stat);
484 return (FALSE);
485 }
486 return (TRUE);
487 }
488
489 bool_t
rpc_gss_getcred(struct svc_req * req,rpc_gss_rawcred_t ** rcred,rpc_gss_ucred_t ** ucred,void ** cookie)490 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
491 rpc_gss_ucred_t **ucred, void **cookie)
492 {
493 struct svc_rpc_gss_cookedcred *cc;
494 struct svc_rpc_gss_client *client;
495
496 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
497 return (FALSE);
498
499 cc = req->rq_clntcred;
500 client = cc->cc_client;
501 if (rcred)
502 *rcred = &client->cl_rawcred;
503 if (ucred)
504 *ucred = &client->cl_ucred;
505 if (cookie)
506 *cookie = client->cl_cookie;
507 return (TRUE);
508 }
509
510 /*
511 * This simpler interface is used by svc_getcred to copy the cred data
512 * into a kernel cred structure.
513 */
514 static int
rpc_gss_svc_getcred(struct svc_req * req,struct ucred ** crp,int * flavorp)515 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
516 {
517 struct ucred *cr;
518 struct svc_rpc_gss_cookedcred *cc;
519 struct svc_rpc_gss_client *client;
520 rpc_gss_ucred_t *uc;
521
522 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
523 return (FALSE);
524
525 cc = req->rq_clntcred;
526 client = cc->cc_client;
527
528 if (flavorp)
529 *flavorp = client->cl_rpcflavor;
530
531 if (client->cl_cred) {
532 *crp = crhold(client->cl_cred);
533 return (TRUE);
534 }
535
536 uc = &client->cl_ucred;
537 cr = client->cl_cred = crget();
538 cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
539 cr->cr_rgid = cr->cr_svgid = uc->gid;
540 crsetgroups_and_egid(cr, uc->gidlen, uc->gidlist, uc->gid);
541 cr->cr_prison = curthread->td_ucred->cr_prison;
542 prison_hold(cr->cr_prison);
543 *crp = crhold(cr);
544
545 return (TRUE);
546 }
547
548 int
rpc_gss_svc_max_data_length(struct svc_req * req,int max_tp_unit_len)549 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
550 {
551 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
552 struct svc_rpc_gss_client *client = cc->cc_client;
553 int want_conf;
554 OM_uint32 max;
555 OM_uint32 maj_stat, min_stat;
556 int result;
557
558 switch (client->cl_rawcred.service) {
559 case rpc_gss_svc_none:
560 return (max_tp_unit_len);
561 break;
562
563 case rpc_gss_svc_default:
564 case rpc_gss_svc_integrity:
565 want_conf = FALSE;
566 break;
567
568 case rpc_gss_svc_privacy:
569 want_conf = TRUE;
570 break;
571
572 default:
573 return (0);
574 }
575
576 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
577 client->cl_qop, max_tp_unit_len, &max);
578
579 if (maj_stat == GSS_S_COMPLETE) {
580 result = (int) max;
581 if (result < 0)
582 result = 0;
583 return (result);
584 } else {
585 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
586 maj_stat, min_stat);
587 return (0);
588 }
589 }
590
591 static struct svc_rpc_gss_client *
svc_rpc_gss_find_client(struct svc_rpc_gss_clientid * id)592 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
593 {
594 struct svc_rpc_gss_client *client;
595 struct svc_rpc_gss_client_list *list;
596 struct timeval boottime;
597 unsigned long hostid;
598
599 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
600
601 getcredhostid(curthread->td_ucred, &hostid);
602 getboottime(&boottime);
603 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
604 return (NULL);
605
606 list = &KGSS_VNET(svc_rpc_gss_client_hash)
607 [id->ci_id % svc_rpc_gss_client_hash_size];
608 sx_xlock(&svc_rpc_gss_lock);
609 TAILQ_FOREACH(client, list, cl_link) {
610 if (client->cl_id.ci_id == id->ci_id) {
611 /*
612 * Move this client to the front of the LRU
613 * list.
614 */
615 TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client,
616 cl_alllink);
617 TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients),
618 client, cl_alllink);
619 refcount_acquire(&client->cl_refs);
620 break;
621 }
622 }
623 sx_xunlock(&svc_rpc_gss_lock);
624
625 return (client);
626 }
627
628 static struct svc_rpc_gss_client *
svc_rpc_gss_create_client(void)629 svc_rpc_gss_create_client(void)
630 {
631 struct svc_rpc_gss_client *client;
632 struct svc_rpc_gss_client_list *list;
633 struct timeval boottime;
634 unsigned long hostid;
635
636 rpc_gss_log_debug("in svc_rpc_gss_create_client()");
637
638 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
639 memset(client, 0, sizeof(struct svc_rpc_gss_client));
640
641 /*
642 * Set the initial value of cl_refs to two. One for the caller
643 * and the other to hold onto the client structure until it expires.
644 */
645 refcount_init(&client->cl_refs, 2);
646 sx_init(&client->cl_lock, "GSS-client");
647 getcredhostid(curthread->td_ucred, &hostid);
648 client->cl_id.ci_hostid = hostid;
649 getboottime(&boottime);
650 client->cl_id.ci_boottime = boottime.tv_sec;
651 client->cl_id.ci_id = KGSS_VNET(svc_rpc_gss_next_clientid)++;
652
653 /*
654 * Start the client off with a short expiration time. We will
655 * try to get a saner value from the client creds later.
656 */
657 client->cl_state = CLIENT_NEW;
658 client->cl_locked = FALSE;
659 client->cl_expiration = time_uptime + 5*60;
660
661 list = &KGSS_VNET(svc_rpc_gss_client_hash)
662 [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
663 sx_xlock(&svc_rpc_gss_lock);
664 TAILQ_INSERT_HEAD(list, client, cl_link);
665 TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink);
666 svc_rpc_gss_client_count++;
667 sx_xunlock(&svc_rpc_gss_lock);
668 return (client);
669 }
670
671 static void
svc_rpc_gss_destroy_client(struct svc_rpc_gss_client * client)672 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
673 {
674 OM_uint32 min_stat;
675
676 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
677
678 if (client->cl_ctx)
679 gss_delete_sec_context(&min_stat,
680 &client->cl_ctx, GSS_C_NO_BUFFER);
681
682 if (client->cl_cname)
683 gss_release_name(&min_stat, &client->cl_cname);
684
685 if (client->cl_rawcred.client_principal)
686 mem_free(client->cl_rawcred.client_principal,
687 sizeof(*client->cl_rawcred.client_principal)
688 + client->cl_rawcred.client_principal->len);
689
690 if (client->cl_cred)
691 crfree(client->cl_cred);
692
693 sx_destroy(&client->cl_lock);
694 mem_free(client, sizeof(*client));
695 }
696
697 /*
698 * Drop a reference to a client and free it if that was the last reference.
699 */
700 static void
svc_rpc_gss_release_client(struct svc_rpc_gss_client * client)701 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
702 {
703
704 if (!refcount_release(&client->cl_refs))
705 return;
706 svc_rpc_gss_destroy_client(client);
707 }
708
709 /*
710 * Remove a client from our global lists.
711 * Must be called with svc_rpc_gss_lock held.
712 */
713 static void
svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client * client)714 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
715 {
716 struct svc_rpc_gss_client_list *list;
717
718 sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
719 list = &KGSS_VNET(svc_rpc_gss_client_hash)
720 [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
721 TAILQ_REMOVE(list, client, cl_link);
722 TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink);
723 svc_rpc_gss_client_count--;
724 }
725
726 /*
727 * Remove a client from our global lists and free it if we can.
728 */
729 static void
svc_rpc_gss_forget_client(struct svc_rpc_gss_client * client)730 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
731 {
732 struct svc_rpc_gss_client_list *list;
733 struct svc_rpc_gss_client *tclient;
734
735 list = &KGSS_VNET(svc_rpc_gss_client_hash)
736 [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
737 sx_xlock(&svc_rpc_gss_lock);
738 TAILQ_FOREACH(tclient, list, cl_link) {
739 /*
740 * Make sure this client has not already been removed
741 * from the lists by svc_rpc_gss_forget_client() or
742 * svc_rpc_gss_forget_client_locked().
743 */
744 if (client == tclient) {
745 svc_rpc_gss_forget_client_locked(client);
746 sx_xunlock(&svc_rpc_gss_lock);
747 svc_rpc_gss_release_client(client);
748 return;
749 }
750 }
751 sx_xunlock(&svc_rpc_gss_lock);
752 }
753
754 static void
svc_rpc_gss_timeout_clients(void)755 svc_rpc_gss_timeout_clients(void)
756 {
757 struct svc_rpc_gss_client *client;
758 time_t now = time_uptime;
759
760 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
761
762 /*
763 * First enforce the max client limit. We keep
764 * svc_rpc_gss_clients in LRU order.
765 */
766 sx_xlock(&svc_rpc_gss_lock);
767 client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients),
768 svc_rpc_gss_client_list);
769 while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
770 svc_rpc_gss_forget_client_locked(client);
771 sx_xunlock(&svc_rpc_gss_lock);
772 svc_rpc_gss_release_client(client);
773 sx_xlock(&svc_rpc_gss_lock);
774 client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients),
775 svc_rpc_gss_client_list);
776 }
777 again:
778 TAILQ_FOREACH(client, &KGSS_VNET(svc_rpc_gss_clients), cl_alllink) {
779 if (client->cl_state == CLIENT_STALE
780 || now > client->cl_expiration) {
781 svc_rpc_gss_forget_client_locked(client);
782 sx_xunlock(&svc_rpc_gss_lock);
783 rpc_gss_log_debug("expiring client %p", client);
784 svc_rpc_gss_release_client(client);
785 sx_xlock(&svc_rpc_gss_lock);
786 goto again;
787 }
788 }
789 sx_xunlock(&svc_rpc_gss_lock);
790 }
791
792 #ifdef DEBUG
793 /*
794 * OID<->string routines. These are uuuuugly.
795 */
796 static OM_uint32
gss_oid_to_str(OM_uint32 * minor_status,gss_OID oid,gss_buffer_t oid_str)797 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
798 {
799 char numstr[128];
800 unsigned long number;
801 int numshift;
802 size_t string_length;
803 size_t i;
804 unsigned char *cp;
805 char *bp;
806
807 /* Decoded according to krb5/gssapi_krb5.c */
808
809 /* First determine the size of the string */
810 string_length = 0;
811 number = 0;
812 numshift = 0;
813 cp = (unsigned char *) oid->elements;
814 number = (unsigned long) cp[0];
815 sprintf(numstr, "%ld ", number/40);
816 string_length += strlen(numstr);
817 sprintf(numstr, "%ld ", number%40);
818 string_length += strlen(numstr);
819 for (i=1; i<oid->length; i++) {
820 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
821 number = (number << 7) | (cp[i] & 0x7f);
822 numshift += 7;
823 }
824 else {
825 *minor_status = 0;
826 return(GSS_S_FAILURE);
827 }
828 if ((cp[i] & 0x80) == 0) {
829 sprintf(numstr, "%ld ", number);
830 string_length += strlen(numstr);
831 number = 0;
832 numshift = 0;
833 }
834 }
835 /*
836 * If we get here, we've calculated the length of "n n n ... n ". Add 4
837 * here for "{ " and "}\0".
838 */
839 string_length += 4;
840 if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
841 strcpy(bp, "{ ");
842 number = (unsigned long) cp[0];
843 sprintf(numstr, "%ld ", number/40);
844 strcat(bp, numstr);
845 sprintf(numstr, "%ld ", number%40);
846 strcat(bp, numstr);
847 number = 0;
848 cp = (unsigned char *) oid->elements;
849 for (i=1; i<oid->length; i++) {
850 number = (number << 7) | (cp[i] & 0x7f);
851 if ((cp[i] & 0x80) == 0) {
852 sprintf(numstr, "%ld ", number);
853 strcat(bp, numstr);
854 number = 0;
855 }
856 }
857 strcat(bp, "}");
858 oid_str->length = strlen(bp)+1;
859 oid_str->value = (void *) bp;
860 *minor_status = 0;
861 return(GSS_S_COMPLETE);
862 }
863 *minor_status = 0;
864 return(GSS_S_FAILURE);
865 }
866 #endif
867
868 static void
svc_rpc_gss_build_ucred(struct svc_rpc_gss_client * client,const gss_name_t name)869 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
870 const gss_name_t name)
871 {
872 OM_uint32 maj_stat, min_stat;
873 rpc_gss_ucred_t *uc = &client->cl_ucred;
874 int numgroups;
875
876 uc->uid = 65534;
877 uc->gid = 65534;
878 uc->gidlist = client->cl_gid_storage;
879
880 numgroups = NGROUPS;
881 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
882 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
883 if (GSS_ERROR(maj_stat))
884 uc->gidlen = 0;
885 else
886 uc->gidlen = numgroups;
887 }
888
889 static void
svc_rpc_gss_set_flavor(struct svc_rpc_gss_client * client)890 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
891 {
892 static gss_OID_desc krb5_mech_oid =
893 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
894
895 /*
896 * Attempt to translate mech type and service into a
897 * 'pseudo flavor'. Hardwire in krb5 support for now.
898 */
899 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
900 switch (client->cl_rawcred.service) {
901 case rpc_gss_svc_default:
902 case rpc_gss_svc_none:
903 client->cl_rpcflavor = RPCSEC_GSS_KRB5;
904 break;
905 case rpc_gss_svc_integrity:
906 client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
907 break;
908 case rpc_gss_svc_privacy:
909 client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
910 break;
911 }
912 } else {
913 client->cl_rpcflavor = RPCSEC_GSS;
914 }
915 }
916
917 static bool_t
svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client * client,struct svc_req * rqst,struct rpc_gss_init_res * gr,struct rpc_gss_cred * gc)918 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
919 struct svc_req *rqst,
920 struct rpc_gss_init_res *gr,
921 struct rpc_gss_cred *gc)
922 {
923 gss_buffer_desc recv_tok;
924 gss_OID mech;
925 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
926 OM_uint32 cred_lifetime;
927 struct svc_rpc_gss_svc_name *sname;
928 gss_buffer_desc export_name;
929 rpc_gss_ucred_t *uc = &client->cl_ucred;
930 int numgroups;
931 static enum krb_imp my_krb_imp = KRBIMP_UNKNOWN;
932
933 rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
934
935 if (my_krb_imp == KRBIMP_UNKNOWN) {
936 maj_stat = gss_supports_lucid(&min_stat, NULL);
937 if (maj_stat == GSS_S_COMPLETE)
938 my_krb_imp = KRBIMP_MIT;
939 else
940 my_krb_imp = KRBIMP_HEIMDALV1;
941 min_stat = 0;
942 }
943
944 if (my_krb_imp == KRBIMP_MIT) {
945 uc->uid = 65534;
946 uc->gid = 65534;
947 uc->gidlist = client->cl_gid_storage;
948 numgroups = NGROUPS;
949 }
950
951 /* Deserialize arguments. */
952 memset(&recv_tok, 0, sizeof(recv_tok));
953
954 if (!svc_getargs(rqst,
955 (xdrproc_t) xdr_gss_buffer_desc,
956 (caddr_t) &recv_tok)) {
957 client->cl_state = CLIENT_STALE;
958 return (FALSE);
959 }
960
961 /*
962 * First time round, try all the server names we have until
963 * one matches. Afterwards, stick with that one.
964 */
965 sx_xlock(&svc_rpc_gss_lock);
966 if (!client->cl_sname) {
967 SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names),
968 sn_link) {
969 if (sname->sn_program == rqst->rq_prog
970 && sname->sn_version == rqst->rq_vers) {
971 retry:
972 if (my_krb_imp == KRBIMP_MIT)
973 gr->gr_major =
974 gss_accept_sec_context_lucid_v1(
975 &gr->gr_minor,
976 &client->cl_ctx,
977 sname->sn_cred,
978 &recv_tok,
979 GSS_C_NO_CHANNEL_BINDINGS,
980 &client->cl_cname,
981 &mech,
982 &gr->gr_token,
983 &ret_flags,
984 &cred_lifetime,
985 &client->cl_creds,
986 &export_name,
987 &uc->uid,
988 &uc->gid,
989 &numgroups,
990 &uc->gidlist[0]);
991 else
992 gr->gr_major = gss_accept_sec_context(
993 &gr->gr_minor,
994 &client->cl_ctx,
995 sname->sn_cred,
996 &recv_tok,
997 GSS_C_NO_CHANNEL_BINDINGS,
998 &client->cl_cname,
999 &mech,
1000 &gr->gr_token,
1001 &ret_flags,
1002 &cred_lifetime,
1003 &client->cl_creds);
1004 if (gr->gr_major ==
1005 GSS_S_CREDENTIALS_EXPIRED) {
1006 /*
1007 * Either our creds really did
1008 * expire or gssd was
1009 * restarted.
1010 */
1011 if (rpc_gss_acquire_svc_cred(sname))
1012 goto retry;
1013 }
1014 client->cl_sname = sname;
1015 break;
1016 }
1017 }
1018 if (!sname) {
1019 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1020 (char *) &recv_tok);
1021 sx_xunlock(&svc_rpc_gss_lock);
1022 return (FALSE);
1023 }
1024 } else {
1025 if (my_krb_imp == KRBIMP_MIT)
1026 gr->gr_major = gss_accept_sec_context_lucid_v1(
1027 &gr->gr_minor,
1028 &client->cl_ctx,
1029 client->cl_sname->sn_cred,
1030 &recv_tok,
1031 GSS_C_NO_CHANNEL_BINDINGS,
1032 &client->cl_cname,
1033 &mech,
1034 &gr->gr_token,
1035 &ret_flags,
1036 &cred_lifetime,
1037 NULL,
1038 &export_name,
1039 &uc->uid,
1040 &uc->gid,
1041 &numgroups,
1042 &uc->gidlist[0]);
1043 else
1044 gr->gr_major = gss_accept_sec_context(
1045 &gr->gr_minor,
1046 &client->cl_ctx,
1047 client->cl_sname->sn_cred,
1048 &recv_tok,
1049 GSS_C_NO_CHANNEL_BINDINGS,
1050 &client->cl_cname,
1051 &mech,
1052 &gr->gr_token,
1053 &ret_flags,
1054 &cred_lifetime,
1055 NULL);
1056 }
1057 sx_xunlock(&svc_rpc_gss_lock);
1058
1059 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
1060
1061 /*
1062 * If we get an error from gss_accept_sec_context, send the
1063 * reply anyway so that the client gets a chance to see what
1064 * is wrong.
1065 */
1066 if (gr->gr_major != GSS_S_COMPLETE &&
1067 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
1068 rpc_gss_log_status("accept_sec_context", client->cl_mech,
1069 gr->gr_major, gr->gr_minor);
1070 client->cl_state = CLIENT_STALE;
1071 if (my_krb_imp == KRBIMP_MIT)
1072 uc->gidlen = 0;
1073 return (TRUE);
1074 }
1075 if (my_krb_imp == KRBIMP_MIT)
1076 uc->gidlen = numgroups;
1077
1078 gr->gr_handle.value = &client->cl_id;
1079 gr->gr_handle.length = sizeof(client->cl_id);
1080 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
1081
1082 /* Save client info. */
1083 client->cl_mech = mech;
1084 client->cl_qop = GSS_C_QOP_DEFAULT;
1085 client->cl_done_callback = FALSE;
1086
1087 if (gr->gr_major == GSS_S_COMPLETE) {
1088 /*
1089 * Change client expiration time to be near when the
1090 * client creds expire (or 24 hours if we can't figure
1091 * that out).
1092 */
1093 if (cred_lifetime == GSS_C_INDEFINITE)
1094 cred_lifetime = 24*60*60;
1095
1096 /*
1097 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set.
1098 */
1099 if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime >
1100 svc_rpc_gss_lifetime_max)
1101 cred_lifetime = svc_rpc_gss_lifetime_max;
1102
1103 client->cl_expiration = time_uptime + cred_lifetime;
1104
1105 /*
1106 * Fill in cred details in the rawcred structure.
1107 */
1108 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
1109 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
1110 maj_stat = GSS_S_COMPLETE;
1111 if (my_krb_imp != KRBIMP_MIT)
1112 maj_stat = gss_export_name(&min_stat, client->cl_cname,
1113 &export_name);
1114 if (maj_stat != GSS_S_COMPLETE) {
1115 rpc_gss_log_status("gss_export_name", client->cl_mech,
1116 maj_stat, min_stat);
1117 return (FALSE);
1118 }
1119 client->cl_rawcred.client_principal =
1120 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
1121 + export_name.length);
1122 client->cl_rawcred.client_principal->len = export_name.length;
1123 memcpy(client->cl_rawcred.client_principal->name,
1124 export_name.value, export_name.length);
1125 gss_release_buffer(&min_stat, &export_name);
1126 client->cl_rawcred.svc_principal =
1127 client->cl_sname->sn_principal;
1128 client->cl_rawcred.service = gc->gc_svc;
1129
1130 /*
1131 * Use gss_pname_to_uid to map to unix creds. For
1132 * kerberos5, this uses krb5_aname_to_localname.
1133 */
1134 if (my_krb_imp != KRBIMP_MIT)
1135 svc_rpc_gss_build_ucred(client, client->cl_cname);
1136 svc_rpc_gss_set_flavor(client);
1137 gss_release_name(&min_stat, &client->cl_cname);
1138
1139 #ifdef DEBUG
1140 {
1141 gss_buffer_desc mechname;
1142
1143 gss_oid_to_str(&min_stat, mech, &mechname);
1144
1145 rpc_gss_log_debug("accepted context for %s with "
1146 "<mech %.*s, qop %d, svc %d>",
1147 client->cl_rawcred.client_principal->name,
1148 mechname.length, (char *)mechname.value,
1149 client->cl_qop, client->cl_rawcred.service);
1150
1151 gss_release_buffer(&min_stat, &mechname);
1152 }
1153 #endif /* DEBUG */
1154 }
1155 return (TRUE);
1156 }
1157
1158 static bool_t
svc_rpc_gss_validate(struct svc_rpc_gss_client * client,struct rpc_msg * msg,gss_qop_t * qop,rpc_gss_proc_t gcproc)1159 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1160 gss_qop_t *qop, rpc_gss_proc_t gcproc)
1161 {
1162 struct opaque_auth *oa;
1163 gss_buffer_desc rpcbuf, checksum;
1164 OM_uint32 maj_stat, min_stat;
1165 gss_qop_t qop_state;
1166 int32_t rpchdr[128 / sizeof(int32_t)];
1167 int32_t *buf;
1168
1169 rpc_gss_log_debug("in svc_rpc_gss_validate()");
1170
1171 memset(rpchdr, 0, sizeof(rpchdr));
1172
1173 /* Reconstruct RPC header for signing (from xdr_callmsg). */
1174 buf = rpchdr;
1175 IXDR_PUT_LONG(buf, msg->rm_xid);
1176 IXDR_PUT_ENUM(buf, msg->rm_direction);
1177 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1178 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1179 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1180 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1181 oa = &msg->rm_call.cb_cred;
1182 IXDR_PUT_ENUM(buf, oa->oa_flavor);
1183 IXDR_PUT_LONG(buf, oa->oa_length);
1184 if (oa->oa_length) {
1185 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1186 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1187 }
1188 rpcbuf.value = rpchdr;
1189 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1190
1191 checksum.value = msg->rm_call.cb_verf.oa_base;
1192 checksum.length = msg->rm_call.cb_verf.oa_length;
1193
1194 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1195 &qop_state);
1196
1197 if (maj_stat != GSS_S_COMPLETE) {
1198 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1199 maj_stat, min_stat);
1200 /*
1201 * A bug in some versions of the Linux client generates a
1202 * Destroy operation with a bogus encrypted checksum. Deleting
1203 * the credential handle for that case causes the mount to fail.
1204 * Since the checksum is bogus (gss_verify_mic() failed), it
1205 * doesn't make sense to destroy the handle and not doing so
1206 * fixes the Linux mount.
1207 */
1208 if (gcproc != RPCSEC_GSS_DESTROY)
1209 client->cl_state = CLIENT_STALE;
1210 return (FALSE);
1211 }
1212
1213 *qop = qop_state;
1214 return (TRUE);
1215 }
1216
1217 static bool_t
svc_rpc_gss_nextverf(struct svc_rpc_gss_client * client,struct svc_req * rqst,u_int seq)1218 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1219 struct svc_req *rqst, u_int seq)
1220 {
1221 gss_buffer_desc signbuf;
1222 gss_buffer_desc mic;
1223 OM_uint32 maj_stat, min_stat;
1224 uint32_t nseq;
1225
1226 rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1227
1228 nseq = htonl(seq);
1229 signbuf.value = &nseq;
1230 signbuf.length = sizeof(nseq);
1231
1232 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1233 &signbuf, &mic);
1234
1235 if (maj_stat != GSS_S_COMPLETE) {
1236 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1237 client->cl_state = CLIENT_STALE;
1238 return (FALSE);
1239 }
1240
1241 KASSERT(mic.length <= MAX_AUTH_BYTES,
1242 ("MIC too large for RPCSEC_GSS"));
1243
1244 rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1245 rqst->rq_verf.oa_length = mic.length;
1246 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1247
1248 gss_release_buffer(&min_stat, &mic);
1249
1250 return (TRUE);
1251 }
1252
1253 static bool_t
svc_rpc_gss_callback(struct svc_rpc_gss_client * client,struct svc_req * rqst)1254 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1255 {
1256 struct svc_rpc_gss_callback *scb;
1257 rpc_gss_lock_t lock;
1258 void *cookie;
1259 bool_t cb_res;
1260 bool_t result;
1261
1262 /*
1263 * See if we have a callback for this guy.
1264 */
1265 result = TRUE;
1266 SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
1267 if (scb->cb_callback.program == rqst->rq_prog
1268 && scb->cb_callback.version == rqst->rq_vers) {
1269 /*
1270 * This one matches. Call the callback and see
1271 * if it wants to veto or something.
1272 */
1273 lock.locked = FALSE;
1274 lock.raw_cred = &client->cl_rawcred;
1275 cb_res = scb->cb_callback.callback(rqst,
1276 client->cl_creds,
1277 client->cl_ctx,
1278 &lock,
1279 &cookie);
1280
1281 if (!cb_res) {
1282 client->cl_state = CLIENT_STALE;
1283 result = FALSE;
1284 break;
1285 }
1286
1287 /*
1288 * The callback accepted the connection - it
1289 * is responsible for freeing client->cl_creds
1290 * now.
1291 */
1292 client->cl_creds = GSS_C_NO_CREDENTIAL;
1293 client->cl_locked = lock.locked;
1294 client->cl_cookie = cookie;
1295 return (TRUE);
1296 }
1297 }
1298
1299 /*
1300 * Either no callback exists for this program/version or one
1301 * of the callbacks rejected the connection. We just need to
1302 * clean up the delegated client creds, if any.
1303 */
1304 if (client->cl_creds) {
1305 OM_uint32 min_ver;
1306 gss_release_cred(&min_ver, &client->cl_creds);
1307 }
1308 return (result);
1309 }
1310
1311 static bool_t
svc_rpc_gss_check_replay(struct svc_rpc_gss_client * client,uint32_t seq)1312 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1313 {
1314 uint32_t offset;
1315 int word, bit;
1316 bool_t result;
1317
1318 sx_xlock(&client->cl_lock);
1319 if (seq <= client->cl_seqlast) {
1320 /*
1321 * The request sequence number is less than
1322 * the largest we have seen so far. If it is
1323 * outside the window or if we have seen a
1324 * request with this sequence before, silently
1325 * discard it.
1326 */
1327 offset = client->cl_seqlast - seq;
1328 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1329 result = FALSE;
1330 goto out;
1331 }
1332 word = offset / 32;
1333 bit = offset % 32;
1334 if (client->cl_seqmask[word] & (1 << bit)) {
1335 result = FALSE;
1336 goto out;
1337 }
1338 }
1339
1340 result = TRUE;
1341 out:
1342 sx_xunlock(&client->cl_lock);
1343 return (result);
1344 }
1345
1346 static void
svc_rpc_gss_update_seq(struct svc_rpc_gss_client * client,uint32_t seq)1347 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1348 {
1349 int offset, i, word, bit;
1350 uint32_t carry, newcarry;
1351
1352 sx_xlock(&client->cl_lock);
1353 if (seq > client->cl_seqlast) {
1354 /*
1355 * This request has a sequence number greater
1356 * than any we have seen so far. Advance the
1357 * seq window and set bit zero of the window
1358 * (which corresponds to the new sequence
1359 * number)
1360 */
1361 offset = seq - client->cl_seqlast;
1362 while (offset > 32) {
1363 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1364 i > 0; i--) {
1365 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1366 }
1367 client->cl_seqmask[0] = 0;
1368 offset -= 32;
1369 }
1370 carry = 0;
1371 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1372 newcarry = client->cl_seqmask[i] >> (32 - offset);
1373 client->cl_seqmask[i] =
1374 (client->cl_seqmask[i] << offset) | carry;
1375 carry = newcarry;
1376 }
1377 client->cl_seqmask[0] |= 1;
1378 client->cl_seqlast = seq;
1379 } else {
1380 offset = client->cl_seqlast - seq;
1381 word = offset / 32;
1382 bit = offset % 32;
1383 client->cl_seqmask[word] |= (1 << bit);
1384 }
1385 sx_xunlock(&client->cl_lock);
1386 }
1387
1388 enum auth_stat
svc_rpc_gss(struct svc_req * rqst,struct rpc_msg * msg)1389 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1390
1391 {
1392 OM_uint32 min_stat;
1393 XDR xdrs;
1394 struct svc_rpc_gss_cookedcred *cc;
1395 struct svc_rpc_gss_client *client;
1396 struct rpc_gss_cred gc;
1397 struct rpc_gss_init_res gr;
1398 gss_qop_t qop;
1399 int call_stat;
1400 enum auth_stat result;
1401
1402 KGSS_CURVNET_SET_QUIET(KGSS_TD_TO_VNET(curthread));
1403 rpc_gss_log_debug("in svc_rpc_gss()");
1404
1405 /* Garbage collect old clients. */
1406 svc_rpc_gss_timeout_clients();
1407
1408 /* Initialize reply. */
1409 rqst->rq_verf = _null_auth;
1410
1411 /* Deserialize client credentials. */
1412 if (rqst->rq_cred.oa_length <= 0) {
1413 KGSS_CURVNET_RESTORE();
1414 return (AUTH_BADCRED);
1415 }
1416
1417 memset(&gc, 0, sizeof(gc));
1418
1419 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1420 rqst->rq_cred.oa_length, XDR_DECODE);
1421
1422 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1423 XDR_DESTROY(&xdrs);
1424 KGSS_CURVNET_RESTORE();
1425 return (AUTH_BADCRED);
1426 }
1427 XDR_DESTROY(&xdrs);
1428
1429 client = NULL;
1430
1431 /* Check version. */
1432 if (gc.gc_version != RPCSEC_GSS_VERSION) {
1433 result = AUTH_BADCRED;
1434 goto out;
1435 }
1436
1437 /* Check the proc and find the client (or create it) */
1438 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1439 if (gc.gc_handle.length != 0) {
1440 result = AUTH_BADCRED;
1441 goto out;
1442 }
1443 client = svc_rpc_gss_create_client();
1444 } else {
1445 struct svc_rpc_gss_clientid *p;
1446 if (gc.gc_handle.length != sizeof(*p)) {
1447 result = AUTH_BADCRED;
1448 goto out;
1449 }
1450 p = gc.gc_handle.value;
1451 client = svc_rpc_gss_find_client(p);
1452 if (!client) {
1453 /*
1454 * Can't find the client - we may have
1455 * destroyed it - tell the other side to
1456 * re-authenticate.
1457 */
1458 result = RPCSEC_GSS_CREDPROBLEM;
1459 goto out;
1460 }
1461 }
1462 cc = rqst->rq_clntcred;
1463 cc->cc_client = client;
1464 cc->cc_service = gc.gc_svc;
1465 cc->cc_seq = gc.gc_seq;
1466
1467 /*
1468 * The service and sequence number must be ignored for
1469 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1470 */
1471 if (gc.gc_proc != RPCSEC_GSS_INIT
1472 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1473 /*
1474 * Check for sequence number overflow.
1475 */
1476 if (gc.gc_seq >= MAXSEQ) {
1477 result = RPCSEC_GSS_CTXPROBLEM;
1478 goto out;
1479 }
1480
1481 /*
1482 * Check for valid service.
1483 */
1484 if (gc.gc_svc != rpc_gss_svc_none &&
1485 gc.gc_svc != rpc_gss_svc_integrity &&
1486 gc.gc_svc != rpc_gss_svc_privacy) {
1487 result = AUTH_BADCRED;
1488 goto out;
1489 }
1490 }
1491
1492 /* Handle RPCSEC_GSS control procedure. */
1493 switch (gc.gc_proc) {
1494
1495 case RPCSEC_GSS_INIT:
1496 case RPCSEC_GSS_CONTINUE_INIT:
1497 if (rqst->rq_proc != NULLPROC) {
1498 result = AUTH_REJECTEDCRED;
1499 break;
1500 }
1501
1502 memset(&gr, 0, sizeof(gr));
1503 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1504 result = AUTH_REJECTEDCRED;
1505 break;
1506 }
1507
1508 if (gr.gr_major == GSS_S_COMPLETE) {
1509 /*
1510 * We borrow the space for the call verf to
1511 * pack our reply verf.
1512 */
1513 rqst->rq_verf = msg->rm_call.cb_verf;
1514 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1515 result = AUTH_REJECTEDCRED;
1516 break;
1517 }
1518 } else {
1519 rqst->rq_verf = _null_auth;
1520 }
1521
1522 call_stat = svc_sendreply(rqst,
1523 (xdrproc_t) xdr_rpc_gss_init_res,
1524 (caddr_t) &gr);
1525
1526 gss_release_buffer(&min_stat, &gr.gr_token);
1527
1528 if (!call_stat) {
1529 result = AUTH_FAILED;
1530 break;
1531 }
1532
1533 if (gr.gr_major == GSS_S_COMPLETE)
1534 client->cl_state = CLIENT_ESTABLISHED;
1535
1536 result = RPCSEC_GSS_NODISPATCH;
1537 break;
1538
1539 case RPCSEC_GSS_DATA:
1540 case RPCSEC_GSS_DESTROY:
1541 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1542 result = RPCSEC_GSS_NODISPATCH;
1543 break;
1544 }
1545
1546 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1547 result = RPCSEC_GSS_CREDPROBLEM;
1548 break;
1549 }
1550
1551 /*
1552 * We borrow the space for the call verf to pack our
1553 * reply verf.
1554 */
1555 rqst->rq_verf = msg->rm_call.cb_verf;
1556 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1557 result = RPCSEC_GSS_CTXPROBLEM;
1558 break;
1559 }
1560
1561 svc_rpc_gss_update_seq(client, gc.gc_seq);
1562
1563 /*
1564 * Change the SVCAUTH ops on the request to point at
1565 * our own code so that we can unwrap the arguments
1566 * and wrap the result. The caller will re-set this on
1567 * every request to point to a set of null wrap/unwrap
1568 * methods. Acquire an extra reference to the client
1569 * which will be released by svc_rpc_gss_release()
1570 * after the request has finished processing.
1571 */
1572 refcount_acquire(&client->cl_refs);
1573 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1574 rqst->rq_auth.svc_ah_private = cc;
1575
1576 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1577 /*
1578 * We might be ready to do a callback to the server to
1579 * see if it wants to accept/reject the connection.
1580 */
1581 sx_xlock(&client->cl_lock);
1582 if (!client->cl_done_callback) {
1583 client->cl_done_callback = TRUE;
1584 client->cl_qop = qop;
1585 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1586 client->cl_rawcred.mechanism, qop);
1587 if (!svc_rpc_gss_callback(client, rqst)) {
1588 result = AUTH_REJECTEDCRED;
1589 sx_xunlock(&client->cl_lock);
1590 break;
1591 }
1592 }
1593 sx_xunlock(&client->cl_lock);
1594
1595 /*
1596 * If the server has locked this client to a
1597 * particular service+qop pair, enforce that
1598 * restriction now.
1599 */
1600 if (client->cl_locked) {
1601 if (client->cl_rawcred.service != gc.gc_svc) {
1602 result = AUTH_FAILED;
1603 break;
1604 } else if (client->cl_qop != qop) {
1605 result = AUTH_BADVERF;
1606 break;
1607 }
1608 }
1609
1610 /*
1611 * If the qop changed, look up the new qop
1612 * name for rawcred.
1613 */
1614 if (client->cl_qop != qop) {
1615 client->cl_qop = qop;
1616 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1617 client->cl_rawcred.mechanism, qop);
1618 }
1619
1620 /*
1621 * Make sure we use the right service value
1622 * for unwrap/wrap.
1623 */
1624 if (client->cl_rawcred.service != gc.gc_svc) {
1625 client->cl_rawcred.service = gc.gc_svc;
1626 svc_rpc_gss_set_flavor(client);
1627 }
1628
1629 result = AUTH_OK;
1630 } else {
1631 if (rqst->rq_proc != NULLPROC) {
1632 result = AUTH_REJECTEDCRED;
1633 break;
1634 }
1635
1636 call_stat = svc_sendreply(rqst,
1637 (xdrproc_t) xdr_void, (caddr_t) NULL);
1638
1639 if (!call_stat) {
1640 result = AUTH_FAILED;
1641 break;
1642 }
1643
1644 svc_rpc_gss_forget_client(client);
1645
1646 result = RPCSEC_GSS_NODISPATCH;
1647 break;
1648 }
1649 break;
1650
1651 default:
1652 result = AUTH_BADCRED;
1653 break;
1654 }
1655 out:
1656 if (client)
1657 svc_rpc_gss_release_client(client);
1658
1659 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1660 KGSS_CURVNET_RESTORE();
1661 return (result);
1662 }
1663
1664 static bool_t
svc_rpc_gss_wrap(SVCAUTH * auth,struct mbuf ** mp)1665 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1666 {
1667 struct svc_rpc_gss_cookedcred *cc;
1668 struct svc_rpc_gss_client *client;
1669
1670 rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1671
1672 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1673 client = cc->cc_client;
1674 if (client->cl_state != CLIENT_ESTABLISHED
1675 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1676 return (TRUE);
1677 }
1678
1679 return (xdr_rpc_gss_wrap_data(mp,
1680 client->cl_ctx, client->cl_qop,
1681 cc->cc_service, cc->cc_seq));
1682 }
1683
1684 static bool_t
svc_rpc_gss_unwrap(SVCAUTH * auth,struct mbuf ** mp)1685 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1686 {
1687 struct svc_rpc_gss_cookedcred *cc;
1688 struct svc_rpc_gss_client *client;
1689
1690 rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1691
1692 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1693 client = cc->cc_client;
1694 if (client->cl_state != CLIENT_ESTABLISHED
1695 || cc->cc_service == rpc_gss_svc_none) {
1696 return (TRUE);
1697 }
1698
1699 return (xdr_rpc_gss_unwrap_data(mp,
1700 client->cl_ctx, client->cl_qop,
1701 cc->cc_service, cc->cc_seq));
1702 }
1703
1704 static void
svc_rpc_gss_release(SVCAUTH * auth)1705 svc_rpc_gss_release(SVCAUTH *auth)
1706 {
1707 struct svc_rpc_gss_cookedcred *cc;
1708 struct svc_rpc_gss_client *client;
1709
1710 rpc_gss_log_debug("in svc_rpc_gss_release()");
1711
1712 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1713 client = cc->cc_client;
1714 svc_rpc_gss_release_client(client);
1715 }
1716