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 cr->cr_prison = &prison0;
482 prison_hold(cr->cr_prison);
483 *crp = crhold(cr);
484
485 return (TRUE);
486 }
487
488 int
489 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
490 {
491 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
492 struct svc_rpc_gss_client *client = cc->cc_client;
493 int want_conf;
494 OM_uint32 max;
495 OM_uint32 maj_stat, min_stat;
496 int result;
497
498 switch (client->cl_rawcred.service) {
499 case rpc_gss_svc_none:
500 return (max_tp_unit_len);
501 break;
502
503 case rpc_gss_svc_default:
504 case rpc_gss_svc_integrity:
505 want_conf = FALSE;
506 break;
507
508 case rpc_gss_svc_privacy:
509 want_conf = TRUE;
510 break;
511
512 default:
513 return (0);
514 }
515
516 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
517 client->cl_qop, max_tp_unit_len, &max);
518
519 if (maj_stat == GSS_S_COMPLETE) {
520 result = (int) max;
521 if (result < 0)
522 result = 0;
523 return (result);
524 } else {
525 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
526 maj_stat, min_stat);
527 return (0);
528 }
529 }
530
531 static struct svc_rpc_gss_client *
532 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
533 {
534 struct svc_rpc_gss_client *client;
535 struct svc_rpc_gss_client_list *list;
536 struct timeval boottime;
537 unsigned long hostid;
538
539 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
540
541 getcredhostid(curthread->td_ucred, &hostid);
542 getboottime(&boottime);
543 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
544 return (NULL);
545
546 list = &svc_rpc_gss_client_hash[id->ci_id % svc_rpc_gss_client_hash_size];
547 sx_xlock(&svc_rpc_gss_lock);
548 TAILQ_FOREACH(client, list, cl_link) {
549 if (client->cl_id.ci_id == id->ci_id) {
550 /*
551 * Move this client to the front of the LRU
552 * list.
553 */
554 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
555 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
556 cl_alllink);
557 refcount_acquire(&client->cl_refs);
558 break;
559 }
560 }
561 sx_xunlock(&svc_rpc_gss_lock);
562
563 return (client);
564 }
565
566 static struct svc_rpc_gss_client *
567 svc_rpc_gss_create_client(void)
568 {
569 struct svc_rpc_gss_client *client;
570 struct svc_rpc_gss_client_list *list;
571 struct timeval boottime;
572 unsigned long hostid;
573
574 rpc_gss_log_debug("in svc_rpc_gss_create_client()");
575
576 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
577 memset(client, 0, sizeof(struct svc_rpc_gss_client));
578
579 /*
580 * Set the initial value of cl_refs to two. One for the caller
581 * and the other to hold onto the client structure until it expires.
582 */
583 refcount_init(&client->cl_refs, 2);
584 sx_init(&client->cl_lock, "GSS-client");
585 getcredhostid(curthread->td_ucred, &hostid);
586 client->cl_id.ci_hostid = hostid;
587 getboottime(&boottime);
588 client->cl_id.ci_boottime = boottime.tv_sec;
589 client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
590
591 /*
592 * Start the client off with a short expiration time. We will
593 * try to get a saner value from the client creds later.
594 */
595 client->cl_state = CLIENT_NEW;
596 client->cl_locked = FALSE;
597 client->cl_expiration = time_uptime + 5*60;
598
599 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
600 sx_xlock(&svc_rpc_gss_lock);
601 TAILQ_INSERT_HEAD(list, client, cl_link);
602 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
603 svc_rpc_gss_client_count++;
604 sx_xunlock(&svc_rpc_gss_lock);
605 return (client);
606 }
607
608 static void
609 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
610 {
611 OM_uint32 min_stat;
612
613 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
614
615 if (client->cl_ctx)
616 gss_delete_sec_context(&min_stat,
617 &client->cl_ctx, GSS_C_NO_BUFFER);
618
619 if (client->cl_cname)
620 gss_release_name(&min_stat, &client->cl_cname);
621
622 if (client->cl_rawcred.client_principal)
623 mem_free(client->cl_rawcred.client_principal,
624 sizeof(*client->cl_rawcred.client_principal)
625 + client->cl_rawcred.client_principal->len);
626
627 if (client->cl_cred)
628 crfree(client->cl_cred);
629
630 sx_destroy(&client->cl_lock);
631 mem_free(client, sizeof(*client));
632 }
633
634 /*
635 * Drop a reference to a client and free it if that was the last reference.
636 */
637 static void
638 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
639 {
640
641 if (!refcount_release(&client->cl_refs))
642 return;
643 svc_rpc_gss_destroy_client(client);
644 }
645
646 /*
647 * Remove a client from our global lists.
648 * Must be called with svc_rpc_gss_lock held.
649 */
650 static void
651 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
652 {
653 struct svc_rpc_gss_client_list *list;
654
655 sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
656 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
657 TAILQ_REMOVE(list, client, cl_link);
658 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
659 svc_rpc_gss_client_count--;
660 }
661
662 /*
663 * Remove a client from our global lists and free it if we can.
664 */
665 static void
666 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
667 {
668 struct svc_rpc_gss_client_list *list;
669 struct svc_rpc_gss_client *tclient;
670
671 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
672 sx_xlock(&svc_rpc_gss_lock);
673 TAILQ_FOREACH(tclient, list, cl_link) {
674 /*
675 * Make sure this client has not already been removed
676 * from the lists by svc_rpc_gss_forget_client() or
677 * svc_rpc_gss_forget_client_locked().
678 */
679 if (client == tclient) {
680 svc_rpc_gss_forget_client_locked(client);
681 sx_xunlock(&svc_rpc_gss_lock);
682 svc_rpc_gss_release_client(client);
683 return;
684 }
685 }
686 sx_xunlock(&svc_rpc_gss_lock);
687 }
688
689 static void
690 svc_rpc_gss_timeout_clients(void)
691 {
692 struct svc_rpc_gss_client *client;
693 time_t now = time_uptime;
694
695 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
696
697 /*
698 * First enforce the max client limit. We keep
699 * svc_rpc_gss_clients in LRU order.
700 */
701 sx_xlock(&svc_rpc_gss_lock);
702 client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
703 while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
704 svc_rpc_gss_forget_client_locked(client);
705 sx_xunlock(&svc_rpc_gss_lock);
706 svc_rpc_gss_release_client(client);
707 sx_xlock(&svc_rpc_gss_lock);
708 client = TAILQ_LAST(&svc_rpc_gss_clients,
709 svc_rpc_gss_client_list);
710 }
711 again:
712 TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
713 if (client->cl_state == CLIENT_STALE
714 || now > client->cl_expiration) {
715 svc_rpc_gss_forget_client_locked(client);
716 sx_xunlock(&svc_rpc_gss_lock);
717 rpc_gss_log_debug("expiring client %p", client);
718 svc_rpc_gss_release_client(client);
719 sx_xlock(&svc_rpc_gss_lock);
720 goto again;
721 }
722 }
723 sx_xunlock(&svc_rpc_gss_lock);
724 }
725
726 #ifdef DEBUG
727 /*
728 * OID<->string routines. These are uuuuugly.
729 */
730 static OM_uint32
731 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
732 {
733 char numstr[128];
734 unsigned long number;
735 int numshift;
736 size_t string_length;
737 size_t i;
738 unsigned char *cp;
739 char *bp;
740
741 /* Decoded according to krb5/gssapi_krb5.c */
742
743 /* First determine the size of the string */
744 string_length = 0;
745 number = 0;
746 numshift = 0;
747 cp = (unsigned char *) oid->elements;
748 number = (unsigned long) cp[0];
749 sprintf(numstr, "%ld ", number/40);
750 string_length += strlen(numstr);
751 sprintf(numstr, "%ld ", number%40);
752 string_length += strlen(numstr);
753 for (i=1; i<oid->length; i++) {
754 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
755 number = (number << 7) | (cp[i] & 0x7f);
756 numshift += 7;
757 }
758 else {
759 *minor_status = 0;
760 return(GSS_S_FAILURE);
761 }
762 if ((cp[i] & 0x80) == 0) {
763 sprintf(numstr, "%ld ", number);
764 string_length += strlen(numstr);
765 number = 0;
766 numshift = 0;
767 }
768 }
769 /*
770 * If we get here, we've calculated the length of "n n n ... n ". Add 4
771 * here for "{ " and "}\0".
772 */
773 string_length += 4;
774 if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
775 strcpy(bp, "{ ");
776 number = (unsigned long) cp[0];
777 sprintf(numstr, "%ld ", number/40);
778 strcat(bp, numstr);
779 sprintf(numstr, "%ld ", number%40);
780 strcat(bp, numstr);
781 number = 0;
782 cp = (unsigned char *) oid->elements;
783 for (i=1; i<oid->length; i++) {
784 number = (number << 7) | (cp[i] & 0x7f);
785 if ((cp[i] & 0x80) == 0) {
786 sprintf(numstr, "%ld ", number);
787 strcat(bp, numstr);
788 number = 0;
789 }
790 }
791 strcat(bp, "}");
792 oid_str->length = strlen(bp)+1;
793 oid_str->value = (void *) bp;
794 *minor_status = 0;
795 return(GSS_S_COMPLETE);
796 }
797 *minor_status = 0;
798 return(GSS_S_FAILURE);
799 }
800 #endif
801
802 static void
803 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
804 const gss_name_t name)
805 {
806 OM_uint32 maj_stat, min_stat;
807 rpc_gss_ucred_t *uc = &client->cl_ucred;
808 int numgroups;
809
810 uc->uid = 65534;
811 uc->gid = 65534;
812 uc->gidlist = client->cl_gid_storage;
813
814 numgroups = NGROUPS;
815 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
816 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
817 if (GSS_ERROR(maj_stat))
818 uc->gidlen = 0;
819 else
820 uc->gidlen = numgroups;
821 }
822
823 static void
824 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
825 {
826 static gss_OID_desc krb5_mech_oid =
827 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
828
829 /*
830 * Attempt to translate mech type and service into a
831 * 'pseudo flavor'. Hardwire in krb5 support for now.
832 */
833 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
834 switch (client->cl_rawcred.service) {
835 case rpc_gss_svc_default:
836 case rpc_gss_svc_none:
837 client->cl_rpcflavor = RPCSEC_GSS_KRB5;
838 break;
839 case rpc_gss_svc_integrity:
840 client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
841 break;
842 case rpc_gss_svc_privacy:
843 client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
844 break;
845 }
846 } else {
847 client->cl_rpcflavor = RPCSEC_GSS;
848 }
849 }
850
851 static bool_t
852 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
853 struct svc_req *rqst,
854 struct rpc_gss_init_res *gr,
855 struct rpc_gss_cred *gc)
856 {
857 gss_buffer_desc recv_tok;
858 gss_OID mech;
859 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
860 OM_uint32 cred_lifetime;
861 struct svc_rpc_gss_svc_name *sname;
862
863 rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
864
865 /* Deserialize arguments. */
866 memset(&recv_tok, 0, sizeof(recv_tok));
867
868 if (!svc_getargs(rqst,
869 (xdrproc_t) xdr_gss_buffer_desc,
870 (caddr_t) &recv_tok)) {
871 client->cl_state = CLIENT_STALE;
872 return (FALSE);
873 }
874
875 /*
876 * First time round, try all the server names we have until
877 * one matches. Afterwards, stick with that one.
878 */
879 sx_xlock(&svc_rpc_gss_lock);
880 if (!client->cl_sname) {
881 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
882 if (sname->sn_program == rqst->rq_prog
883 && sname->sn_version == rqst->rq_vers) {
884 retry:
885 gr->gr_major = gss_accept_sec_context(
886 &gr->gr_minor,
887 &client->cl_ctx,
888 sname->sn_cred,
889 &recv_tok,
890 GSS_C_NO_CHANNEL_BINDINGS,
891 &client->cl_cname,
892 &mech,
893 &gr->gr_token,
894 &ret_flags,
895 &cred_lifetime,
896 &client->cl_creds);
897 if (gr->gr_major ==
898 GSS_S_CREDENTIALS_EXPIRED) {
899 /*
900 * Either our creds really did
901 * expire or gssd was
902 * restarted.
903 */
904 if (rpc_gss_acquire_svc_cred(sname))
905 goto retry;
906 }
907 client->cl_sname = sname;
908 break;
909 }
910 }
911 if (!sname) {
912 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
913 (char *) &recv_tok);
914 sx_xunlock(&svc_rpc_gss_lock);
915 return (FALSE);
916 }
917 } else {
918 gr->gr_major = gss_accept_sec_context(
919 &gr->gr_minor,
920 &client->cl_ctx,
921 client->cl_sname->sn_cred,
922 &recv_tok,
923 GSS_C_NO_CHANNEL_BINDINGS,
924 &client->cl_cname,
925 &mech,
926 &gr->gr_token,
927 &ret_flags,
928 &cred_lifetime,
929 NULL);
930 }
931 sx_xunlock(&svc_rpc_gss_lock);
932
933 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
934
935 /*
936 * If we get an error from gss_accept_sec_context, send the
937 * reply anyway so that the client gets a chance to see what
938 * is wrong.
939 */
940 if (gr->gr_major != GSS_S_COMPLETE &&
941 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
942 rpc_gss_log_status("accept_sec_context", client->cl_mech,
943 gr->gr_major, gr->gr_minor);
944 client->cl_state = CLIENT_STALE;
945 return (TRUE);
946 }
947
948 gr->gr_handle.value = &client->cl_id;
949 gr->gr_handle.length = sizeof(client->cl_id);
950 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
951
952 /* Save client info. */
953 client->cl_mech = mech;
954 client->cl_qop = GSS_C_QOP_DEFAULT;
955 client->cl_done_callback = FALSE;
956
957 if (gr->gr_major == GSS_S_COMPLETE) {
958 gss_buffer_desc export_name;
959
960 /*
961 * Change client expiration time to be near when the
962 * client creds expire (or 24 hours if we can't figure
963 * that out).
964 */
965 if (cred_lifetime == GSS_C_INDEFINITE)
966 cred_lifetime = 24*60*60;
967
968 /*
969 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set.
970 */
971 if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime >
972 svc_rpc_gss_lifetime_max)
973 cred_lifetime = svc_rpc_gss_lifetime_max;
974
975 client->cl_expiration = time_uptime + cred_lifetime;
976
977 /*
978 * Fill in cred details in the rawcred structure.
979 */
980 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
981 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
982 maj_stat = gss_export_name(&min_stat, client->cl_cname,
983 &export_name);
984 if (maj_stat != GSS_S_COMPLETE) {
985 rpc_gss_log_status("gss_export_name", client->cl_mech,
986 maj_stat, min_stat);
987 return (FALSE);
988 }
989 client->cl_rawcred.client_principal =
990 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
991 + export_name.length);
992 client->cl_rawcred.client_principal->len = export_name.length;
993 memcpy(client->cl_rawcred.client_principal->name,
994 export_name.value, export_name.length);
995 gss_release_buffer(&min_stat, &export_name);
996 client->cl_rawcred.svc_principal =
997 client->cl_sname->sn_principal;
998 client->cl_rawcred.service = gc->gc_svc;
999
1000 /*
1001 * Use gss_pname_to_uid to map to unix creds. For
1002 * kerberos5, this uses krb5_aname_to_localname.
1003 */
1004 svc_rpc_gss_build_ucred(client, client->cl_cname);
1005 svc_rpc_gss_set_flavor(client);
1006 gss_release_name(&min_stat, &client->cl_cname);
1007
1008 #ifdef DEBUG
1009 {
1010 gss_buffer_desc mechname;
1011
1012 gss_oid_to_str(&min_stat, mech, &mechname);
1013
1014 rpc_gss_log_debug("accepted context for %s with "
1015 "<mech %.*s, qop %d, svc %d>",
1016 client->cl_rawcred.client_principal->name,
1017 mechname.length, (char *)mechname.value,
1018 client->cl_qop, client->cl_rawcred.service);
1019
1020 gss_release_buffer(&min_stat, &mechname);
1021 }
1022 #endif /* DEBUG */
1023 }
1024 return (TRUE);
1025 }
1026
1027 static bool_t
1028 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1029 gss_qop_t *qop, rpc_gss_proc_t gcproc)
1030 {
1031 struct opaque_auth *oa;
1032 gss_buffer_desc rpcbuf, checksum;
1033 OM_uint32 maj_stat, min_stat;
1034 gss_qop_t qop_state;
1035 int32_t rpchdr[128 / sizeof(int32_t)];
1036 int32_t *buf;
1037
1038 rpc_gss_log_debug("in svc_rpc_gss_validate()");
1039
1040 memset(rpchdr, 0, sizeof(rpchdr));
1041
1042 /* Reconstruct RPC header for signing (from xdr_callmsg). */
1043 buf = rpchdr;
1044 IXDR_PUT_LONG(buf, msg->rm_xid);
1045 IXDR_PUT_ENUM(buf, msg->rm_direction);
1046 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1047 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1048 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1049 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1050 oa = &msg->rm_call.cb_cred;
1051 IXDR_PUT_ENUM(buf, oa->oa_flavor);
1052 IXDR_PUT_LONG(buf, oa->oa_length);
1053 if (oa->oa_length) {
1054 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1055 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1056 }
1057 rpcbuf.value = rpchdr;
1058 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1059
1060 checksum.value = msg->rm_call.cb_verf.oa_base;
1061 checksum.length = msg->rm_call.cb_verf.oa_length;
1062
1063 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1064 &qop_state);
1065
1066 if (maj_stat != GSS_S_COMPLETE) {
1067 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1068 maj_stat, min_stat);
1069 /*
1070 * A bug in some versions of the Linux client generates a
1071 * Destroy operation with a bogus encrypted checksum. Deleting
1072 * the credential handle for that case causes the mount to fail.
1073 * Since the checksum is bogus (gss_verify_mic() failed), it
1074 * doesn't make sense to destroy the handle and not doing so
1075 * fixes the Linux mount.
1076 */
1077 if (gcproc != RPCSEC_GSS_DESTROY)
1078 client->cl_state = CLIENT_STALE;
1079 return (FALSE);
1080 }
1081
1082 *qop = qop_state;
1083 return (TRUE);
1084 }
1085
1086 static bool_t
1087 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1088 struct svc_req *rqst, u_int seq)
1089 {
1090 gss_buffer_desc signbuf;
1091 gss_buffer_desc mic;
1092 OM_uint32 maj_stat, min_stat;
1093 uint32_t nseq;
1094
1095 rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1096
1097 nseq = htonl(seq);
1098 signbuf.value = &nseq;
1099 signbuf.length = sizeof(nseq);
1100
1101 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1102 &signbuf, &mic);
1103
1104 if (maj_stat != GSS_S_COMPLETE) {
1105 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1106 client->cl_state = CLIENT_STALE;
1107 return (FALSE);
1108 }
1109
1110 KASSERT(mic.length <= MAX_AUTH_BYTES,
1111 ("MIC too large for RPCSEC_GSS"));
1112
1113 rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1114 rqst->rq_verf.oa_length = mic.length;
1115 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1116
1117 gss_release_buffer(&min_stat, &mic);
1118
1119 return (TRUE);
1120 }
1121
1122 static bool_t
1123 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1124 {
1125 struct svc_rpc_gss_callback *scb;
1126 rpc_gss_lock_t lock;
1127 void *cookie;
1128 bool_t cb_res;
1129 bool_t result;
1130
1131 /*
1132 * See if we have a callback for this guy.
1133 */
1134 result = TRUE;
1135 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1136 if (scb->cb_callback.program == rqst->rq_prog
1137 && scb->cb_callback.version == rqst->rq_vers) {
1138 /*
1139 * This one matches. Call the callback and see
1140 * if it wants to veto or something.
1141 */
1142 lock.locked = FALSE;
1143 lock.raw_cred = &client->cl_rawcred;
1144 cb_res = scb->cb_callback.callback(rqst,
1145 client->cl_creds,
1146 client->cl_ctx,
1147 &lock,
1148 &cookie);
1149
1150 if (!cb_res) {
1151 client->cl_state = CLIENT_STALE;
1152 result = FALSE;
1153 break;
1154 }
1155
1156 /*
1157 * The callback accepted the connection - it
1158 * is responsible for freeing client->cl_creds
1159 * now.
1160 */
1161 client->cl_creds = GSS_C_NO_CREDENTIAL;
1162 client->cl_locked = lock.locked;
1163 client->cl_cookie = cookie;
1164 return (TRUE);
1165 }
1166 }
1167
1168 /*
1169 * Either no callback exists for this program/version or one
1170 * of the callbacks rejected the connection. We just need to
1171 * clean up the delegated client creds, if any.
1172 */
1173 if (client->cl_creds) {
1174 OM_uint32 min_ver;
1175 gss_release_cred(&min_ver, &client->cl_creds);
1176 }
1177 return (result);
1178 }
1179
1180 static bool_t
1181 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1182 {
1183 u_int32_t offset;
1184 int word, bit;
1185 bool_t result;
1186
1187 sx_xlock(&client->cl_lock);
1188 if (seq <= client->cl_seqlast) {
1189 /*
1190 * The request sequence number is less than
1191 * the largest we have seen so far. If it is
1192 * outside the window or if we have seen a
1193 * request with this sequence before, silently
1194 * discard it.
1195 */
1196 offset = client->cl_seqlast - seq;
1197 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1198 result = FALSE;
1199 goto out;
1200 }
1201 word = offset / 32;
1202 bit = offset % 32;
1203 if (client->cl_seqmask[word] & (1 << bit)) {
1204 result = FALSE;
1205 goto out;
1206 }
1207 }
1208
1209 result = TRUE;
1210 out:
1211 sx_xunlock(&client->cl_lock);
1212 return (result);
1213 }
1214
1215 static void
1216 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1217 {
1218 int offset, i, word, bit;
1219 uint32_t carry, newcarry;
1220
1221 sx_xlock(&client->cl_lock);
1222 if (seq > client->cl_seqlast) {
1223 /*
1224 * This request has a sequence number greater
1225 * than any we have seen so far. Advance the
1226 * seq window and set bit zero of the window
1227 * (which corresponds to the new sequence
1228 * number)
1229 */
1230 offset = seq - client->cl_seqlast;
1231 while (offset > 32) {
1232 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1233 i > 0; i--) {
1234 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1235 }
1236 client->cl_seqmask[0] = 0;
1237 offset -= 32;
1238 }
1239 carry = 0;
1240 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1241 newcarry = client->cl_seqmask[i] >> (32 - offset);
1242 client->cl_seqmask[i] =
1243 (client->cl_seqmask[i] << offset) | carry;
1244 carry = newcarry;
1245 }
1246 client->cl_seqmask[0] |= 1;
1247 client->cl_seqlast = seq;
1248 } else {
1249 offset = client->cl_seqlast - seq;
1250 word = offset / 32;
1251 bit = offset % 32;
1252 client->cl_seqmask[word] |= (1 << bit);
1253 }
1254 sx_xunlock(&client->cl_lock);
1255 }
1256
1257 enum auth_stat
1258 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1259
1260 {
1261 OM_uint32 min_stat;
1262 XDR xdrs;
1263 struct svc_rpc_gss_cookedcred *cc;
1264 struct svc_rpc_gss_client *client;
1265 struct rpc_gss_cred gc;
1266 struct rpc_gss_init_res gr;
1267 gss_qop_t qop;
1268 int call_stat;
1269 enum auth_stat result;
1270
1271 rpc_gss_log_debug("in svc_rpc_gss()");
1272
1273 /* Garbage collect old clients. */
1274 svc_rpc_gss_timeout_clients();
1275
1276 /* Initialize reply. */
1277 rqst->rq_verf = _null_auth;
1278
1279 /* Deserialize client credentials. */
1280 if (rqst->rq_cred.oa_length <= 0)
1281 return (AUTH_BADCRED);
1282
1283 memset(&gc, 0, sizeof(gc));
1284
1285 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1286 rqst->rq_cred.oa_length, XDR_DECODE);
1287
1288 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1289 XDR_DESTROY(&xdrs);
1290 return (AUTH_BADCRED);
1291 }
1292 XDR_DESTROY(&xdrs);
1293
1294 client = NULL;
1295
1296 /* Check version. */
1297 if (gc.gc_version != RPCSEC_GSS_VERSION) {
1298 result = AUTH_BADCRED;
1299 goto out;
1300 }
1301
1302 /* Check the proc and find the client (or create it) */
1303 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1304 if (gc.gc_handle.length != 0) {
1305 result = AUTH_BADCRED;
1306 goto out;
1307 }
1308 client = svc_rpc_gss_create_client();
1309 } else {
1310 struct svc_rpc_gss_clientid *p;
1311 if (gc.gc_handle.length != sizeof(*p)) {
1312 result = AUTH_BADCRED;
1313 goto out;
1314 }
1315 p = gc.gc_handle.value;
1316 client = svc_rpc_gss_find_client(p);
1317 if (!client) {
1318 /*
1319 * Can't find the client - we may have
1320 * destroyed it - tell the other side to
1321 * re-authenticate.
1322 */
1323 result = RPCSEC_GSS_CREDPROBLEM;
1324 goto out;
1325 }
1326 }
1327 cc = rqst->rq_clntcred;
1328 cc->cc_client = client;
1329 cc->cc_service = gc.gc_svc;
1330 cc->cc_seq = gc.gc_seq;
1331
1332 /*
1333 * The service and sequence number must be ignored for
1334 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1335 */
1336 if (gc.gc_proc != RPCSEC_GSS_INIT
1337 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1338 /*
1339 * Check for sequence number overflow.
1340 */
1341 if (gc.gc_seq >= MAXSEQ) {
1342 result = RPCSEC_GSS_CTXPROBLEM;
1343 goto out;
1344 }
1345
1346 /*
1347 * Check for valid service.
1348 */
1349 if (gc.gc_svc != rpc_gss_svc_none &&
1350 gc.gc_svc != rpc_gss_svc_integrity &&
1351 gc.gc_svc != rpc_gss_svc_privacy) {
1352 result = AUTH_BADCRED;
1353 goto out;
1354 }
1355 }
1356
1357 /* Handle RPCSEC_GSS control procedure. */
1358 switch (gc.gc_proc) {
1359
1360 case RPCSEC_GSS_INIT:
1361 case RPCSEC_GSS_CONTINUE_INIT:
1362 if (rqst->rq_proc != NULLPROC) {
1363 result = AUTH_REJECTEDCRED;
1364 break;
1365 }
1366
1367 memset(&gr, 0, sizeof(gr));
1368 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1369 result = AUTH_REJECTEDCRED;
1370 break;
1371 }
1372
1373 if (gr.gr_major == GSS_S_COMPLETE) {
1374 /*
1375 * We borrow the space for the call verf to
1376 * pack our reply verf.
1377 */
1378 rqst->rq_verf = msg->rm_call.cb_verf;
1379 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1380 result = AUTH_REJECTEDCRED;
1381 break;
1382 }
1383 } else {
1384 rqst->rq_verf = _null_auth;
1385 }
1386
1387 call_stat = svc_sendreply(rqst,
1388 (xdrproc_t) xdr_rpc_gss_init_res,
1389 (caddr_t) &gr);
1390
1391 gss_release_buffer(&min_stat, &gr.gr_token);
1392
1393 if (!call_stat) {
1394 result = AUTH_FAILED;
1395 break;
1396 }
1397
1398 if (gr.gr_major == GSS_S_COMPLETE)
1399 client->cl_state = CLIENT_ESTABLISHED;
1400
1401 result = RPCSEC_GSS_NODISPATCH;
1402 break;
1403
1404 case RPCSEC_GSS_DATA:
1405 case RPCSEC_GSS_DESTROY:
1406 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1407 result = RPCSEC_GSS_NODISPATCH;
1408 break;
1409 }
1410
1411 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1412 result = RPCSEC_GSS_CREDPROBLEM;
1413 break;
1414 }
1415
1416 /*
1417 * We borrow the space for the call verf to pack our
1418 * reply verf.
1419 */
1420 rqst->rq_verf = msg->rm_call.cb_verf;
1421 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1422 result = RPCSEC_GSS_CTXPROBLEM;
1423 break;
1424 }
1425
1426 svc_rpc_gss_update_seq(client, gc.gc_seq);
1427
1428 /*
1429 * Change the SVCAUTH ops on the request to point at
1430 * our own code so that we can unwrap the arguments
1431 * and wrap the result. The caller will re-set this on
1432 * every request to point to a set of null wrap/unwrap
1433 * methods. Acquire an extra reference to the client
1434 * which will be released by svc_rpc_gss_release()
1435 * after the request has finished processing.
1436 */
1437 refcount_acquire(&client->cl_refs);
1438 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1439 rqst->rq_auth.svc_ah_private = cc;
1440
1441 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1442 /*
1443 * We might be ready to do a callback to the server to
1444 * see if it wants to accept/reject the connection.
1445 */
1446 sx_xlock(&client->cl_lock);
1447 if (!client->cl_done_callback) {
1448 client->cl_done_callback = TRUE;
1449 client->cl_qop = qop;
1450 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1451 client->cl_rawcred.mechanism, qop);
1452 if (!svc_rpc_gss_callback(client, rqst)) {
1453 result = AUTH_REJECTEDCRED;
1454 sx_xunlock(&client->cl_lock);
1455 break;
1456 }
1457 }
1458 sx_xunlock(&client->cl_lock);
1459
1460 /*
1461 * If the server has locked this client to a
1462 * particular service+qop pair, enforce that
1463 * restriction now.
1464 */
1465 if (client->cl_locked) {
1466 if (client->cl_rawcred.service != gc.gc_svc) {
1467 result = AUTH_FAILED;
1468 break;
1469 } else if (client->cl_qop != qop) {
1470 result = AUTH_BADVERF;
1471 break;
1472 }
1473 }
1474
1475 /*
1476 * If the qop changed, look up the new qop
1477 * name for rawcred.
1478 */
1479 if (client->cl_qop != qop) {
1480 client->cl_qop = qop;
1481 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1482 client->cl_rawcred.mechanism, qop);
1483 }
1484
1485 /*
1486 * Make sure we use the right service value
1487 * for unwrap/wrap.
1488 */
1489 if (client->cl_rawcred.service != gc.gc_svc) {
1490 client->cl_rawcred.service = gc.gc_svc;
1491 svc_rpc_gss_set_flavor(client);
1492 }
1493
1494 result = AUTH_OK;
1495 } else {
1496 if (rqst->rq_proc != NULLPROC) {
1497 result = AUTH_REJECTEDCRED;
1498 break;
1499 }
1500
1501 call_stat = svc_sendreply(rqst,
1502 (xdrproc_t) xdr_void, (caddr_t) NULL);
1503
1504 if (!call_stat) {
1505 result = AUTH_FAILED;
1506 break;
1507 }
1508
1509 svc_rpc_gss_forget_client(client);
1510
1511 result = RPCSEC_GSS_NODISPATCH;
1512 break;
1513 }
1514 break;
1515
1516 default:
1517 result = AUTH_BADCRED;
1518 break;
1519 }
1520 out:
1521 if (client)
1522 svc_rpc_gss_release_client(client);
1523
1524 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1525 return (result);
1526 }
1527
1528 static bool_t
1529 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1530 {
1531 struct svc_rpc_gss_cookedcred *cc;
1532 struct svc_rpc_gss_client *client;
1533
1534 rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1535
1536 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1537 client = cc->cc_client;
1538 if (client->cl_state != CLIENT_ESTABLISHED
1539 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1540 return (TRUE);
1541 }
1542
1543 return (xdr_rpc_gss_wrap_data(mp,
1544 client->cl_ctx, client->cl_qop,
1545 cc->cc_service, cc->cc_seq));
1546 }
1547
1548 static bool_t
1549 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1550 {
1551 struct svc_rpc_gss_cookedcred *cc;
1552 struct svc_rpc_gss_client *client;
1553
1554 rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1555
1556 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1557 client = cc->cc_client;
1558 if (client->cl_state != CLIENT_ESTABLISHED
1559 || cc->cc_service == rpc_gss_svc_none) {
1560 return (TRUE);
1561 }
1562
1563 return (xdr_rpc_gss_unwrap_data(mp,
1564 client->cl_ctx, client->cl_qop,
1565 cc->cc_service, cc->cc_seq));
1566 }
1567
1568 static void
1569 svc_rpc_gss_release(SVCAUTH *auth)
1570 {
1571 struct svc_rpc_gss_cookedcred *cc;
1572 struct svc_rpc_gss_client *client;
1573
1574 rpc_gss_log_debug("in svc_rpc_gss_release()");
1575
1576 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1577 client = cc->cc_client;
1578 svc_rpc_gss_release_client(client);
1579 }
Cache object: b75b9a8e83d02d4344c6152e84a1344c
|