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