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