1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2008 Doug Rabson
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28 /*
29 auth_gss.c
30
31 RPCSEC_GSS client routines.
32
33 Copyright (c) 2000 The Regents of the University of Michigan.
34 All rights reserved.
35
36 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
37 All rights reserved, all wrongs reversed.
38
39 Redistribution and use in source and binary forms, with or without
40 modification, are permitted provided that the following conditions
41 are met:
42
43 1. Redistributions of source code must retain the above copyright
44 notice, this list of conditions and the following disclaimer.
45 2. Redistributions in binary form must reproduce the above copyright
46 notice, this list of conditions and the following disclaimer in the
47 documentation and/or other materials provided with the distribution.
48 3. Neither the name of the University nor the names of its
49 contributors may be used to endorse or promote products derived
50 from this software without specific prior written permission.
51
52 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
53 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
59 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63
64 $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
65 */
66
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/hash.h>
70 #include <sys/jail.h>
71 #include <sys/kernel.h>
72 #include <sys/kobj.h>
73 #include <sys/lock.h>
74 #include <sys/malloc.h>
75 #include <sys/mbuf.h>
76 #include <sys/mutex.h>
77 #include <sys/proc.h>
78 #include <sys/refcount.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 <kgssapi/krb5/kcrypto.h>
86
87 #include "rpcsec_gss_int.h"
88
89 static void rpc_gss_nextverf(AUTH*);
90 static bool_t rpc_gss_marshal(AUTH *, uint32_t, XDR *, struct mbuf *);
91 static bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
92 static bool_t rpc_gss_refresh(AUTH *, void *);
93 static bool_t rpc_gss_validate(AUTH *, uint32_t, struct opaque_auth *,
94 struct mbuf **);
95 static void rpc_gss_destroy(AUTH *);
96 static void rpc_gss_destroy_context(AUTH *, bool_t);
97
98 static const struct auth_ops rpc_gss_ops = {
99 .ah_nextverf = rpc_gss_nextverf,
100 .ah_marshal = rpc_gss_marshal,
101 .ah_validate = rpc_gss_validate,
102 .ah_refresh = rpc_gss_refresh,
103 .ah_destroy = rpc_gss_destroy,
104 };
105
106 enum rpcsec_gss_state {
107 RPCSEC_GSS_START,
108 RPCSEC_GSS_CONTEXT,
109 RPCSEC_GSS_ESTABLISHED,
110 RPCSEC_GSS_DESTROYING
111 };
112
113 struct rpc_pending_request {
114 uint32_t pr_xid; /* XID of rpc */
115 uint32_t pr_seq; /* matching GSS seq */
116 LIST_ENTRY(rpc_pending_request) pr_link;
117 };
118 LIST_HEAD(rpc_pending_request_list, rpc_pending_request);
119
120 struct rpc_gss_data {
121 volatile u_int gd_refs; /* number of current users */
122 struct mtx gd_lock;
123 uint32_t gd_hash;
124 AUTH *gd_auth; /* link back to AUTH */
125 struct ucred *gd_ucred; /* matching local cred */
126 char *gd_principal; /* server principal name */
127 char *gd_clntprincipal; /* client principal name */
128 rpc_gss_options_req_t gd_options; /* GSS context options */
129 enum rpcsec_gss_state gd_state; /* connection state */
130 gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE
131 * NULL RPC verifier to
132 * process at end of
133 * context negotiation */
134 CLIENT *gd_clnt; /* client handle */
135 gss_OID gd_mech; /* mechanism to use */
136 gss_qop_t gd_qop; /* quality of protection */
137 gss_ctx_id_t gd_ctx; /* context id */
138 struct rpc_gss_cred gd_cred; /* client credentials */
139 uint32_t gd_seq; /* next sequence number */
140 u_int gd_win; /* sequence window */
141 struct rpc_pending_request_list gd_reqs;
142 TAILQ_ENTRY(rpc_gss_data) gd_link;
143 TAILQ_ENTRY(rpc_gss_data) gd_alllink;
144 };
145 TAILQ_HEAD(rpc_gss_data_list, rpc_gss_data);
146
147 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
148
149 static struct timeval AUTH_TIMEOUT = { 25, 0 };
150
151 #define RPC_GSS_HASH_SIZE 11
152 #define RPC_GSS_MAX 256
153
154 VNET_DEFINE_STATIC(struct rpc_gss_data_list *, rpc_gss_cache);
155 VNET_DEFINE_STATIC(struct rpc_gss_data_list, rpc_gss_all);
156 VNET_DEFINE_STATIC(struct sx, rpc_gss_lock);
157 VNET_DEFINE_STATIC(int, rpc_gss_count);
158
159 static AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *,
160 const char *, gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *,
161 rpc_gss_options_ret_t *);
162
163 static void
rpc_gss_hashinit(void * dummy __unused)164 rpc_gss_hashinit(void *dummy __unused)
165 {
166 int i;
167
168 VNET(rpc_gss_cache) = mem_alloc(sizeof(struct rpc_gss_data_list) *
169 RPC_GSS_HASH_SIZE);
170 for (i = 0; i < RPC_GSS_HASH_SIZE; i++)
171 TAILQ_INIT(&VNET(rpc_gss_cache)[i]);
172 TAILQ_INIT(&VNET(rpc_gss_all));
173 sx_init(&VNET(rpc_gss_lock), "rpc_gss_lock");
174 }
175 VNET_SYSINIT(rpc_gss_hashinit, SI_SUB_VNET_DONE, SI_ORDER_ANY,
176 rpc_gss_hashinit, NULL);
177
178 static void
rpc_gss_hashinit_cleanup(void * dummy __unused)179 rpc_gss_hashinit_cleanup(void *dummy __unused)
180 {
181
182 rpc_gss_secpurge(NULL);
183 mem_free(VNET(rpc_gss_cache), sizeof(struct rpc_gss_data_list) *
184 RPC_GSS_HASH_SIZE);
185 sx_destroy(&VNET(rpc_gss_lock));
186 }
187 VNET_SYSUNINIT(rpc_gss_hashinit_cleanup, SI_SUB_VNET_DONE, SI_ORDER_ANY,
188 rpc_gss_hashinit_cleanup, NULL);
189
190 static uint32_t
rpc_gss_hash(const char * principal,gss_OID mech,struct ucred * cred,rpc_gss_service_t service)191 rpc_gss_hash(const char *principal, gss_OID mech,
192 struct ucred *cred, rpc_gss_service_t service)
193 {
194 uint32_t h;
195
196 h = HASHSTEP(HASHINIT, cred->cr_uid);
197 h = hash32_str(principal, h);
198 h = hash32_buf(mech->elements, mech->length, h);
199 h = HASHSTEP(h, (int) service);
200
201 return (h % RPC_GSS_HASH_SIZE);
202 }
203
204 /*
205 * Simplified interface to create a security association for the
206 * current thread's * ucred.
207 */
208 AUTH *
rpc_gss_secfind(CLIENT * clnt,struct ucred * cred,const char * principal,gss_OID mech_oid,rpc_gss_service_t service)209 rpc_gss_secfind(CLIENT *clnt, struct ucred *cred, const char *principal,
210 gss_OID mech_oid, rpc_gss_service_t service)
211 {
212 uint32_t h, th;
213 AUTH *auth;
214 struct rpc_gss_data *gd, *tgd;
215 rpc_gss_options_ret_t options;
216
217 CURVNET_ASSERT_SET();
218 if (VNET(rpc_gss_count) > RPC_GSS_MAX) {
219 while (VNET(rpc_gss_count) > RPC_GSS_MAX) {
220 sx_xlock(&VNET(rpc_gss_lock));
221 tgd = TAILQ_FIRST(&VNET(rpc_gss_all));
222 th = tgd->gd_hash;
223 TAILQ_REMOVE(&VNET(rpc_gss_cache)[th], tgd, gd_link);
224 TAILQ_REMOVE(&VNET(rpc_gss_all), tgd, gd_alllink);
225 VNET(rpc_gss_count)--;
226 sx_xunlock(&VNET(rpc_gss_lock));
227 AUTH_DESTROY(tgd->gd_auth);
228 }
229 }
230
231 /*
232 * See if we already have an AUTH which matches.
233 */
234 h = rpc_gss_hash(principal, mech_oid, cred, service);
235
236 again:
237 sx_slock(&VNET(rpc_gss_lock));
238 TAILQ_FOREACH(gd, &VNET(rpc_gss_cache)[h], gd_link) {
239 if (gd->gd_ucred->cr_uid == cred->cr_uid
240 && !strcmp(gd->gd_principal, principal)
241 && gd->gd_mech == mech_oid
242 && gd->gd_cred.gc_svc == service) {
243 refcount_acquire(&gd->gd_refs);
244 if (sx_try_upgrade(&VNET(rpc_gss_lock))) {
245 /*
246 * Keep rpc_gss_all LRU sorted.
247 */
248 TAILQ_REMOVE(&VNET(rpc_gss_all), gd,
249 gd_alllink);
250 TAILQ_INSERT_TAIL(&VNET(rpc_gss_all), gd,
251 gd_alllink);
252 sx_xunlock(&VNET(rpc_gss_lock));
253 } else {
254 sx_sunlock(&VNET(rpc_gss_lock));
255 }
256
257 /*
258 * If the state != ESTABLISHED, try and initialize
259 * the authenticator again. This will happen if the
260 * user's credentials have expired. It may succeed now,
261 * if they have done a kinit or similar.
262 */
263 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
264 memset(&options, 0, sizeof (options));
265 (void) rpc_gss_init(gd->gd_auth, &options);
266 }
267 return (gd->gd_auth);
268 }
269 }
270 sx_sunlock(&VNET(rpc_gss_lock));
271
272 /*
273 * We missed in the cache - create a new association.
274 */
275 auth = rpc_gss_seccreate_int(clnt, cred, NULL, principal, mech_oid,
276 service, GSS_C_QOP_DEFAULT, NULL, NULL);
277 if (!auth)
278 return (NULL);
279
280 gd = AUTH_PRIVATE(auth);
281 gd->gd_hash = h;
282
283 sx_xlock(&VNET(rpc_gss_lock));
284 TAILQ_FOREACH(tgd, &VNET(rpc_gss_cache)[h], gd_link) {
285 if (tgd->gd_ucred->cr_uid == cred->cr_uid
286 && !strcmp(tgd->gd_principal, principal)
287 && tgd->gd_mech == mech_oid
288 && tgd->gd_cred.gc_svc == service) {
289 /*
290 * We lost a race to create the AUTH that
291 * matches this cred.
292 */
293 sx_xunlock(&VNET(rpc_gss_lock));
294 AUTH_DESTROY(auth);
295 goto again;
296 }
297 }
298
299 VNET(rpc_gss_count)++;
300 TAILQ_INSERT_TAIL(&VNET(rpc_gss_cache)[h], gd, gd_link);
301 TAILQ_INSERT_TAIL(&VNET(rpc_gss_all), gd, gd_alllink);
302 refcount_acquire(&gd->gd_refs); /* one for the cache, one for user */
303 sx_xunlock(&VNET(rpc_gss_lock));
304
305 return (auth);
306 }
307
308 void
rpc_gss_secpurge(CLIENT * clnt)309 rpc_gss_secpurge(CLIENT *clnt)
310 {
311 uint32_t h;
312 struct rpc_gss_data *gd, *tgd;
313
314 CURVNET_ASSERT_SET();
315 TAILQ_FOREACH_SAFE(gd, &VNET(rpc_gss_all), gd_alllink, tgd) {
316 if (clnt == NULL || gd->gd_clnt == clnt) {
317 sx_xlock(&VNET(rpc_gss_lock));
318 h = gd->gd_hash;
319 TAILQ_REMOVE(&VNET(rpc_gss_cache)[h], gd, gd_link);
320 TAILQ_REMOVE(&VNET(rpc_gss_all), gd, gd_alllink);
321 VNET(rpc_gss_count)--;
322 sx_xunlock(&VNET(rpc_gss_lock));
323 AUTH_DESTROY(gd->gd_auth);
324 }
325 }
326 }
327
328 AUTH *
rpc_gss_seccreate(CLIENT * clnt,struct ucred * cred,const char * clnt_principal,const char * principal,const char * mechanism,rpc_gss_service_t service,const char * qop,rpc_gss_options_req_t * options_req,rpc_gss_options_ret_t * options_ret)329 rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *clnt_principal,
330 const char *principal, const char *mechanism, rpc_gss_service_t service,
331 const char *qop, rpc_gss_options_req_t *options_req,
332 rpc_gss_options_ret_t *options_ret)
333 {
334 gss_OID oid;
335 u_int qop_num;
336
337 /*
338 * Bail out now if we don't know this mechanism.
339 */
340 if (!rpc_gss_mech_to_oid(mechanism, &oid))
341 return (NULL);
342
343 if (qop) {
344 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
345 return (NULL);
346 } else {
347 qop_num = GSS_C_QOP_DEFAULT;
348 }
349
350 return (rpc_gss_seccreate_int(clnt, cred, clnt_principal, principal,
351 oid, service, qop_num, options_req, options_ret));
352 }
353
354 void
rpc_gss_refresh_auth(AUTH * auth)355 rpc_gss_refresh_auth(AUTH *auth)
356 {
357 struct rpc_gss_data *gd;
358 rpc_gss_options_ret_t options;
359
360 gd = AUTH_PRIVATE(auth);
361 /*
362 * If the state != ESTABLISHED, try and initialize
363 * the authenticator again. This will happen if the
364 * user's credentials have expired. It may succeed now,
365 * if they have done a kinit or similar.
366 */
367 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
368 memset(&options, 0, sizeof (options));
369 (void) rpc_gss_init(auth, &options);
370 }
371 }
372
373 static AUTH *
rpc_gss_seccreate_int(CLIENT * clnt,struct ucred * cred,const char * clnt_principal,const char * principal,gss_OID mech_oid,rpc_gss_service_t service,u_int qop_num,rpc_gss_options_req_t * options_req,rpc_gss_options_ret_t * options_ret)374 rpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred,
375 const char *clnt_principal, const char *principal, gss_OID mech_oid,
376 rpc_gss_service_t service, u_int qop_num,
377 rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
378 {
379 AUTH *auth;
380 rpc_gss_options_ret_t options;
381 struct rpc_gss_data *gd;
382
383 /*
384 * If the caller doesn't want the options, point at local
385 * storage to simplify the code below.
386 */
387 if (!options_ret)
388 options_ret = &options;
389
390 /*
391 * Default service is integrity.
392 */
393 if (service == rpc_gss_svc_default)
394 service = rpc_gss_svc_integrity;
395
396 memset(options_ret, 0, sizeof(*options_ret));
397
398 rpc_gss_log_debug("in rpc_gss_seccreate()");
399
400 memset(&rpc_createerr, 0, sizeof(rpc_createerr));
401
402 auth = mem_alloc(sizeof(*auth));
403 if (auth == NULL) {
404 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
405 rpc_createerr.cf_error.re_errno = ENOMEM;
406 return (NULL);
407 }
408 gd = mem_alloc(sizeof(*gd));
409 if (gd == NULL) {
410 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
411 rpc_createerr.cf_error.re_errno = ENOMEM;
412 mem_free(auth, sizeof(*auth));
413 return (NULL);
414 }
415
416 auth->ah_ops = &rpc_gss_ops;
417 auth->ah_private = (caddr_t) gd;
418 auth->ah_cred.oa_flavor = RPCSEC_GSS;
419
420 refcount_init(&gd->gd_refs, 1);
421 mtx_init(&gd->gd_lock, "gd->gd_lock", NULL, MTX_DEF);
422 gd->gd_auth = auth;
423 gd->gd_ucred = crdup(cred);
424 gd->gd_principal = strdup(principal, M_RPC);
425 if (clnt_principal != NULL)
426 gd->gd_clntprincipal = strdup(clnt_principal, M_RPC);
427 else
428 gd->gd_clntprincipal = NULL;
429
430
431 if (options_req) {
432 gd->gd_options = *options_req;
433 } else {
434 gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
435 gd->gd_options.time_req = 0;
436 gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
437 gd->gd_options.input_channel_bindings = NULL;
438 }
439 CLNT_ACQUIRE(clnt);
440 gd->gd_clnt = clnt;
441 gd->gd_ctx = GSS_C_NO_CONTEXT;
442 gd->gd_mech = mech_oid;
443 gd->gd_qop = qop_num;
444
445 gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
446 gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
447 gd->gd_cred.gc_seq = 0;
448 gd->gd_cred.gc_svc = service;
449 LIST_INIT(&gd->gd_reqs);
450
451 if (!rpc_gss_init(auth, options_ret)) {
452 goto bad;
453 }
454
455 return (auth);
456
457 bad:
458 AUTH_DESTROY(auth);
459 return (NULL);
460 }
461
462 bool_t
rpc_gss_set_defaults(AUTH * auth,rpc_gss_service_t service,const char * qop)463 rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
464 {
465 struct rpc_gss_data *gd;
466 u_int qop_num;
467 const char *mechanism;
468
469 gd = AUTH_PRIVATE(auth);
470 if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
471 return (FALSE);
472 }
473
474 if (qop) {
475 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
476 return (FALSE);
477 }
478 } else {
479 qop_num = GSS_C_QOP_DEFAULT;
480 }
481
482 gd->gd_cred.gc_svc = service;
483 gd->gd_qop = qop_num;
484 return (TRUE);
485 }
486
487 static void
rpc_gss_purge_xid(struct rpc_gss_data * gd,uint32_t xid)488 rpc_gss_purge_xid(struct rpc_gss_data *gd, uint32_t xid)
489 {
490 struct rpc_pending_request *pr, *npr;
491 struct rpc_pending_request_list reqs;
492
493 LIST_INIT(&reqs);
494 mtx_lock(&gd->gd_lock);
495 LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
496 if (pr->pr_xid == xid) {
497 LIST_REMOVE(pr, pr_link);
498 LIST_INSERT_HEAD(&reqs, pr, pr_link);
499 }
500 }
501
502 mtx_unlock(&gd->gd_lock);
503
504 LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
505 mem_free(pr, sizeof(*pr));
506 }
507 }
508
509 static uint32_t
rpc_gss_alloc_seq(struct rpc_gss_data * gd)510 rpc_gss_alloc_seq(struct rpc_gss_data *gd)
511 {
512 uint32_t seq;
513
514 mtx_lock(&gd->gd_lock);
515 seq = gd->gd_seq;
516 gd->gd_seq++;
517 mtx_unlock(&gd->gd_lock);
518
519 return (seq);
520 }
521
522 static void
rpc_gss_nextverf(__unused AUTH * auth)523 rpc_gss_nextverf(__unused AUTH *auth)
524 {
525
526 /* not used */
527 }
528
529 static bool_t
rpc_gss_marshal(AUTH * auth,uint32_t xid,XDR * xdrs,struct mbuf * args)530 rpc_gss_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
531 {
532 struct rpc_gss_data *gd;
533 struct rpc_pending_request *pr;
534 uint32_t seq;
535 XDR tmpxdrs;
536 struct rpc_gss_cred gsscred;
537 char credbuf[MAX_AUTH_BYTES];
538 struct opaque_auth creds, verf;
539 gss_buffer_desc rpcbuf, checksum;
540 OM_uint32 maj_stat, min_stat;
541 bool_t xdr_stat;
542
543 rpc_gss_log_debug("in rpc_gss_marshal()");
544
545 gd = AUTH_PRIVATE(auth);
546
547 gsscred = gd->gd_cred;
548 seq = rpc_gss_alloc_seq(gd);
549 gsscred.gc_seq = seq;
550
551 xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
552 if (!xdr_rpc_gss_cred(&tmpxdrs, &gsscred)) {
553 XDR_DESTROY(&tmpxdrs);
554 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
555 return (FALSE);
556 }
557 creds.oa_flavor = RPCSEC_GSS;
558 creds.oa_base = credbuf;
559 creds.oa_length = XDR_GETPOS(&tmpxdrs);
560 XDR_DESTROY(&tmpxdrs);
561
562 xdr_opaque_auth(xdrs, &creds);
563
564 if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
565 gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
566 if (!xdr_opaque_auth(xdrs, &_null_auth)) {
567 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
568 return (FALSE);
569 }
570 return (xdr_putmbuf(xdrs, args));
571 } else {
572 /*
573 * Keep track of this XID + seq pair so that we can do
574 * the matching gss_verify_mic in AUTH_VALIDATE.
575 */
576 pr = mem_alloc(sizeof(struct rpc_pending_request));
577 mtx_lock(&gd->gd_lock);
578 pr->pr_xid = xid;
579 pr->pr_seq = seq;
580 LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
581 mtx_unlock(&gd->gd_lock);
582
583 /*
584 * Checksum serialized RPC header, up to and including
585 * credential. For the in-kernel environment, we
586 * assume that our XDR stream is on a contiguous
587 * memory buffer (e.g. an mbuf).
588 */
589 rpcbuf.length = XDR_GETPOS(xdrs);
590 XDR_SETPOS(xdrs, 0);
591 rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
592
593 maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
594 &rpcbuf, &checksum);
595
596 if (maj_stat != GSS_S_COMPLETE) {
597 rpc_gss_log_status("gss_get_mic", gd->gd_mech,
598 maj_stat, min_stat);
599 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
600 rpc_gss_destroy_context(auth, TRUE);
601 }
602 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
603 return (FALSE);
604 }
605
606 verf.oa_flavor = RPCSEC_GSS;
607 verf.oa_base = checksum.value;
608 verf.oa_length = checksum.length;
609
610 xdr_stat = xdr_opaque_auth(xdrs, &verf);
611 gss_release_buffer(&min_stat, &checksum);
612 if (!xdr_stat) {
613 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
614 return (FALSE);
615 }
616 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
617 gd->gd_cred.gc_svc == rpc_gss_svc_none) {
618 return (xdr_putmbuf(xdrs, args));
619 } else {
620 if (!xdr_rpc_gss_wrap_data(&args,
621 gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
622 seq))
623 return (FALSE);
624 return (xdr_putmbuf(xdrs, args));
625 }
626 }
627
628 return (TRUE);
629 }
630
631 static bool_t
rpc_gss_validate(AUTH * auth,uint32_t xid,struct opaque_auth * verf,struct mbuf ** resultsp)632 rpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
633 struct mbuf **resultsp)
634 {
635 struct rpc_gss_data *gd;
636 struct rpc_pending_request *pr, *npr;
637 struct rpc_pending_request_list reqs;
638 gss_qop_t qop_state;
639 uint32_t num, seq;
640 gss_buffer_desc signbuf, checksum;
641 OM_uint32 maj_stat, min_stat;
642
643 rpc_gss_log_debug("in rpc_gss_validate()");
644
645 gd = AUTH_PRIVATE(auth);
646
647 /*
648 * The client will call us with a NULL verf when it gives up
649 * on an XID.
650 */
651 if (!verf) {
652 rpc_gss_purge_xid(gd, xid);
653 return (TRUE);
654 }
655
656 if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
657 /*
658 * Save the on the wire verifier to validate last INIT
659 * phase packet after decode if the major status is
660 * GSS_S_COMPLETE.
661 */
662 if (gd->gd_verf.value)
663 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
664 (char *) &gd->gd_verf);
665 gd->gd_verf.value = mem_alloc(verf->oa_length);
666 if (gd->gd_verf.value == NULL) {
667 printf("gss_validate: out of memory\n");
668 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
669 m_freem(*resultsp);
670 *resultsp = NULL;
671 return (FALSE);
672 }
673 memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
674 gd->gd_verf.length = verf->oa_length;
675
676 return (TRUE);
677 }
678
679 /*
680 * We need to check the verifier against all the requests
681 * we've send for this XID - for unreliable protocols, we
682 * retransmit with the same XID but different sequence
683 * number. We temporarily take this set of requests out of the
684 * list so that we can work through the list without having to
685 * hold the lock.
686 */
687 mtx_lock(&gd->gd_lock);
688 LIST_INIT(&reqs);
689 LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
690 if (pr->pr_xid == xid) {
691 LIST_REMOVE(pr, pr_link);
692 LIST_INSERT_HEAD(&reqs, pr, pr_link);
693 }
694 }
695 mtx_unlock(&gd->gd_lock);
696 LIST_FOREACH(pr, &reqs, pr_link) {
697 if (pr->pr_xid == xid) {
698 seq = pr->pr_seq;
699 num = htonl(seq);
700 signbuf.value = #
701 signbuf.length = sizeof(num);
702
703 checksum.value = verf->oa_base;
704 checksum.length = verf->oa_length;
705
706 maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
707 &signbuf, &checksum, &qop_state);
708 if (maj_stat != GSS_S_COMPLETE
709 || qop_state != gd->gd_qop) {
710 continue;
711 }
712 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
713 rpc_gss_destroy_context(auth, TRUE);
714 break;
715 }
716 //rpc_gss_purge_reqs(gd, seq);
717 LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr)
718 mem_free(pr, sizeof(*pr));
719
720 if (gd->gd_cred.gc_svc == rpc_gss_svc_none) {
721 return (TRUE);
722 } else {
723 if (!xdr_rpc_gss_unwrap_data(resultsp,
724 gd->gd_ctx, gd->gd_qop,
725 gd->gd_cred.gc_svc, seq)) {
726 return (FALSE);
727 }
728 }
729 return (TRUE);
730 }
731 }
732
733 /*
734 * We didn't match - put back any entries for this XID so that
735 * a future call to validate can retry.
736 */
737 mtx_lock(&gd->gd_lock);
738 LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
739 LIST_REMOVE(pr, pr_link);
740 LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
741 }
742 mtx_unlock(&gd->gd_lock);
743
744 /*
745 * Nothing matches - give up.
746 */
747 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
748 m_freem(*resultsp);
749 *resultsp = NULL;
750 return (FALSE);
751 }
752
753 static bool_t
rpc_gss_init(AUTH * auth,rpc_gss_options_ret_t * options_ret)754 rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
755 {
756 struct thread *td = curthread;
757 struct ucred *crsave;
758 struct rpc_gss_data *gd;
759 struct rpc_gss_init_res gr;
760 gss_buffer_desc principal_desc;
761 gss_buffer_desc *recv_tokenp, recv_token, send_token;
762 gss_name_t name;
763 OM_uint32 maj_stat, min_stat, call_stat;
764 const char *mech;
765 struct rpc_callextra ext;
766 gss_OID mech_oid;
767 gss_OID_set mechlist;
768 static enum krb_imp my_krb_imp = KRBIMP_UNKNOWN;
769
770 CURVNET_ASSERT_SET();
771 rpc_gss_log_debug("in rpc_gss_refresh()");
772
773 gd = AUTH_PRIVATE(auth);
774
775 mtx_lock(&gd->gd_lock);
776 /*
777 * If the context isn't in START state, someone else is
778 * refreshing - we wait till they are done. If they fail, they
779 * will put the state back to START and we can try (most
780 * likely to also fail).
781 */
782 while (gd->gd_state != RPCSEC_GSS_START
783 && gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
784 msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
785 }
786 if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) {
787 mtx_unlock(&gd->gd_lock);
788 return (TRUE);
789 }
790 gd->gd_state = RPCSEC_GSS_CONTEXT;
791 mtx_unlock(&gd->gd_lock);
792
793 gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
794 gd->gd_cred.gc_seq = 0;
795
796 /*
797 * For KerberosV, if there is a client principal name, that implies
798 * that this is a host based initiator credential in the default
799 * keytab file. For this case, it is necessary to do a
800 * gss_acquire_cred(). When this is done, the gssd daemon will
801 * do the equivalent of "kinit -k" to put a TGT for the name in
802 * the credential cache file for the gssd daemon.
803 */
804 if (gd->gd_clntprincipal != NULL &&
805 rpc_gss_mech_to_oid("kerberosv5", &mech_oid) &&
806 gd->gd_mech == mech_oid) {
807 /* Get rid of any old credential. */
808 if (gd->gd_options.my_cred != GSS_C_NO_CREDENTIAL) {
809 gss_release_cred(&min_stat, &gd->gd_options.my_cred);
810 gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
811 }
812
813 /*
814 * The mechanism must be set to KerberosV for acquisition
815 * of credentials to work reliably.
816 */
817 maj_stat = gss_create_empty_oid_set(&min_stat, &mechlist);
818 if (maj_stat != GSS_S_COMPLETE) {
819 options_ret->major_status = maj_stat;
820 options_ret->minor_status = min_stat;
821 goto out;
822 }
823 maj_stat = gss_add_oid_set_member(&min_stat, gd->gd_mech,
824 &mechlist);
825 if (maj_stat != GSS_S_COMPLETE) {
826 options_ret->major_status = maj_stat;
827 options_ret->minor_status = min_stat;
828 gss_release_oid_set(&min_stat, &mechlist);
829 goto out;
830 }
831
832 principal_desc.value = (void *)gd->gd_clntprincipal;
833 principal_desc.length = strlen(gd->gd_clntprincipal);
834 maj_stat = gss_import_name(&min_stat, &principal_desc,
835 GSS_C_NT_HOSTBASED_SERVICE, &name);
836 if (maj_stat != GSS_S_COMPLETE) {
837 options_ret->major_status = maj_stat;
838 options_ret->minor_status = min_stat;
839 gss_release_oid_set(&min_stat, &mechlist);
840 goto out;
841 }
842 /* Acquire the credentials. */
843 maj_stat = gss_acquire_cred(&min_stat, name, 0,
844 mechlist, GSS_C_INITIATE,
845 &gd->gd_options.my_cred, NULL, NULL);
846 gss_release_name(&min_stat, &name);
847 gss_release_oid_set(&min_stat, &mechlist);
848 if (maj_stat != GSS_S_COMPLETE) {
849 options_ret->major_status = maj_stat;
850 options_ret->minor_status = min_stat;
851 goto out;
852 }
853 }
854
855 principal_desc.value = (void *)gd->gd_principal;
856 principal_desc.length = strlen(gd->gd_principal);
857 maj_stat = gss_import_name(&min_stat, &principal_desc,
858 GSS_C_NT_HOSTBASED_SERVICE, &name);
859 if (maj_stat != GSS_S_COMPLETE) {
860 options_ret->major_status = maj_stat;
861 options_ret->minor_status = min_stat;
862 goto out;
863 }
864
865 if (my_krb_imp == KRBIMP_UNKNOWN) {
866 maj_stat = gss_supports_lucid(&min_stat, NULL);
867 if (maj_stat == GSS_S_COMPLETE)
868 my_krb_imp = KRBIMP_MIT;
869 else
870 my_krb_imp = KRBIMP_HEIMDALV1;
871 }
872
873 /* GSS context establishment loop. */
874 memset(&recv_token, 0, sizeof(recv_token));
875 memset(&gr, 0, sizeof(gr));
876 memset(options_ret, 0, sizeof(*options_ret));
877 options_ret->major_status = GSS_S_FAILURE;
878 recv_tokenp = GSS_C_NO_BUFFER;
879
880 for (;;) {
881 crsave = td->td_ucred;
882 td->td_ucred = gd->gd_ucred;
883 if (my_krb_imp == KRBIMP_MIT)
884 maj_stat = gss_init_sec_context_lucid_v1(&min_stat,
885 gd->gd_options.my_cred,
886 &gd->gd_ctx,
887 name,
888 gd->gd_mech,
889 gd->gd_options.req_flags,
890 gd->gd_options.time_req,
891 gd->gd_options.input_channel_bindings,
892 recv_tokenp,
893 &gd->gd_mech, /* used mech */
894 &send_token,
895 &options_ret->ret_flags,
896 &options_ret->time_req);
897 else
898 maj_stat = gss_init_sec_context(&min_stat,
899 gd->gd_options.my_cred,
900 &gd->gd_ctx,
901 name,
902 gd->gd_mech,
903 gd->gd_options.req_flags,
904 gd->gd_options.time_req,
905 gd->gd_options.input_channel_bindings,
906 recv_tokenp,
907 &gd->gd_mech, /* used mech */
908 &send_token,
909 &options_ret->ret_flags,
910 &options_ret->time_req);
911 td->td_ucred = crsave;
912
913 /*
914 * Free the token which we got from the server (if
915 * any). Remember that this was allocated by XDR, not
916 * GSS-API.
917 */
918 if (recv_tokenp != GSS_C_NO_BUFFER) {
919 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
920 (char *) &recv_token);
921 recv_tokenp = GSS_C_NO_BUFFER;
922 }
923 if (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
924 strlcpy(options_ret->actual_mechanism,
925 mech,
926 sizeof(options_ret->actual_mechanism));
927 }
928 if (maj_stat != GSS_S_COMPLETE &&
929 maj_stat != GSS_S_CONTINUE_NEEDED) {
930 rpc_gss_log_status("gss_init_sec_context", gd->gd_mech,
931 maj_stat, min_stat);
932 options_ret->major_status = maj_stat;
933 options_ret->minor_status = min_stat;
934 break;
935 }
936 if (send_token.length != 0) {
937 memset(&gr, 0, sizeof(gr));
938
939 bzero(&ext, sizeof(ext));
940 ext.rc_auth = auth;
941 call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
942 (xdrproc_t)xdr_gss_buffer_desc,
943 &send_token,
944 (xdrproc_t)xdr_rpc_gss_init_res,
945 (caddr_t)&gr, AUTH_TIMEOUT);
946
947 gss_release_buffer(&min_stat, &send_token);
948
949 if (call_stat != RPC_SUCCESS)
950 break;
951
952 if (gr.gr_major != GSS_S_COMPLETE &&
953 gr.gr_major != GSS_S_CONTINUE_NEEDED) {
954 rpc_gss_log_status("server reply", gd->gd_mech,
955 gr.gr_major, gr.gr_minor);
956 options_ret->major_status = gr.gr_major;
957 options_ret->minor_status = gr.gr_minor;
958 break;
959 }
960
961 /*
962 * Save the server's gr_handle value, freeing
963 * what we have already (remember that this
964 * was allocated by XDR, not GSS-API).
965 */
966 if (gr.gr_handle.length != 0) {
967 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
968 (char *) &gd->gd_cred.gc_handle);
969 gd->gd_cred.gc_handle = gr.gr_handle;
970 }
971
972 /*
973 * Save the server's token as well.
974 */
975 if (gr.gr_token.length != 0) {
976 recv_token = gr.gr_token;
977 recv_tokenp = &recv_token;
978 }
979
980 /*
981 * Since we have copied out all the bits of gr
982 * which XDR allocated for us, we don't need
983 * to free it.
984 */
985 gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
986 }
987
988 if (maj_stat == GSS_S_COMPLETE) {
989 gss_buffer_desc bufin;
990 u_int seq, qop_state = 0;
991
992 /*
993 * gss header verifier,
994 * usually checked in gss_validate
995 */
996 seq = htonl(gr.gr_win);
997 bufin.value = (unsigned char *)&seq;
998 bufin.length = sizeof(seq);
999
1000 maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
1001 &bufin, &gd->gd_verf, &qop_state);
1002
1003 if (maj_stat != GSS_S_COMPLETE ||
1004 qop_state != gd->gd_qop) {
1005 rpc_gss_log_status("gss_verify_mic", gd->gd_mech,
1006 maj_stat, min_stat);
1007 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
1008 rpc_gss_destroy_context(auth, TRUE);
1009 }
1010 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
1011 EPERM);
1012 options_ret->major_status = maj_stat;
1013 options_ret->minor_status = min_stat;
1014 break;
1015 }
1016
1017 options_ret->major_status = GSS_S_COMPLETE;
1018 options_ret->minor_status = 0;
1019 options_ret->rpcsec_version = gd->gd_cred.gc_version;
1020 options_ret->gss_context = gd->gd_ctx;
1021
1022 gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
1023 gd->gd_seq = 1;
1024 gd->gd_win = gr.gr_win;
1025 break;
1026 }
1027 }
1028
1029 gss_release_name(&min_stat, &name);
1030 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1031 (char *) &gd->gd_verf);
1032
1033 out:
1034 /* End context negotiation loop. */
1035 if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
1036 rpc_createerr.cf_stat = RPC_AUTHERROR;
1037 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
1038 if (gd->gd_ctx) {
1039 gss_delete_sec_context(&min_stat, &gd->gd_ctx,
1040 GSS_C_NO_BUFFER);
1041 }
1042 mtx_lock(&gd->gd_lock);
1043 gd->gd_state = RPCSEC_GSS_START;
1044 wakeup(gd);
1045 mtx_unlock(&gd->gd_lock);
1046 return (FALSE);
1047 }
1048
1049 mtx_lock(&gd->gd_lock);
1050 gd->gd_state = RPCSEC_GSS_ESTABLISHED;
1051 wakeup(gd);
1052 mtx_unlock(&gd->gd_lock);
1053
1054 return (TRUE);
1055 }
1056
1057 static bool_t
rpc_gss_refresh(AUTH * auth,void * msg)1058 rpc_gss_refresh(AUTH *auth, void *msg)
1059 {
1060 struct rpc_msg *reply = (struct rpc_msg *) msg;
1061 rpc_gss_options_ret_t options;
1062 struct rpc_gss_data *gd;
1063
1064 gd = AUTH_PRIVATE(auth);
1065
1066 /*
1067 * If the context is in DESTROYING state, then just return, since
1068 * there is no point in refreshing the credentials.
1069 */
1070 mtx_lock(&gd->gd_lock);
1071 if (gd->gd_state == RPCSEC_GSS_DESTROYING) {
1072 mtx_unlock(&gd->gd_lock);
1073 return (FALSE);
1074 }
1075 mtx_unlock(&gd->gd_lock);
1076
1077 /*
1078 * If the error was RPCSEC_GSS_CREDPROBLEM of
1079 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
1080 * other errors are fatal.
1081 */
1082 if (reply->rm_reply.rp_stat == MSG_DENIED
1083 && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
1084 && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
1085 || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
1086 rpc_gss_destroy_context(auth, FALSE);
1087 memset(&options, 0, sizeof(options));
1088 return (rpc_gss_init(auth, &options));
1089 }
1090
1091 return (FALSE);
1092 }
1093
1094 static void
rpc_gss_destroy_context(AUTH * auth,bool_t send_destroy)1095 rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
1096 {
1097 struct rpc_gss_data *gd;
1098 struct rpc_pending_request *pr;
1099 OM_uint32 min_stat;
1100 struct rpc_callextra ext;
1101
1102 rpc_gss_log_debug("in rpc_gss_destroy_context()");
1103
1104 gd = AUTH_PRIVATE(auth);
1105
1106 mtx_lock(&gd->gd_lock);
1107 /*
1108 * If the context isn't in ESTABISHED state, someone else is
1109 * destroying/refreshing - we wait till they are done.
1110 */
1111 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
1112 while (gd->gd_state != RPCSEC_GSS_START
1113 && gd->gd_state != RPCSEC_GSS_ESTABLISHED)
1114 msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
1115 mtx_unlock(&gd->gd_lock);
1116 return;
1117 }
1118 gd->gd_state = RPCSEC_GSS_DESTROYING;
1119 mtx_unlock(&gd->gd_lock);
1120
1121 if (send_destroy) {
1122 gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
1123 bzero(&ext, sizeof(ext));
1124 ext.rc_auth = auth;
1125 CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
1126 (xdrproc_t)xdr_void, NULL,
1127 (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
1128 }
1129
1130 while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) {
1131 LIST_REMOVE(pr, pr_link);
1132 mem_free(pr, sizeof(*pr));
1133 }
1134
1135 /*
1136 * Free the context token. Remember that this was
1137 * allocated by XDR, not GSS-API.
1138 */
1139 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1140 (char *) &gd->gd_cred.gc_handle);
1141 gd->gd_cred.gc_handle.length = 0;
1142
1143 if (gd->gd_ctx != GSS_C_NO_CONTEXT)
1144 gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
1145
1146 mtx_lock(&gd->gd_lock);
1147 gd->gd_state = RPCSEC_GSS_START;
1148 wakeup(gd);
1149 mtx_unlock(&gd->gd_lock);
1150 }
1151
1152 static void
rpc_gss_destroy(AUTH * auth)1153 rpc_gss_destroy(AUTH *auth)
1154 {
1155 struct rpc_gss_data *gd;
1156
1157 rpc_gss_log_debug("in rpc_gss_destroy()");
1158
1159 gd = AUTH_PRIVATE(auth);
1160
1161 if (!refcount_release(&gd->gd_refs))
1162 return;
1163
1164 rpc_gss_destroy_context(auth, TRUE);
1165
1166 CLNT_RELEASE(gd->gd_clnt);
1167 crfree(gd->gd_ucred);
1168 free(gd->gd_principal, M_RPC);
1169 if (gd->gd_clntprincipal != NULL)
1170 free(gd->gd_clntprincipal, M_RPC);
1171 if (gd->gd_verf.value)
1172 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1173 (char *) &gd->gd_verf);
1174 mtx_destroy(&gd->gd_lock);
1175
1176 mem_free(gd, sizeof(*gd));
1177 mem_free(auth, sizeof(*auth));
1178 }
1179
1180 int
rpc_gss_max_data_length(AUTH * auth,int max_tp_unit_len)1181 rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
1182 {
1183 struct rpc_gss_data *gd;
1184 int want_conf;
1185 OM_uint32 max;
1186 OM_uint32 maj_stat, min_stat;
1187 int result;
1188
1189 gd = AUTH_PRIVATE(auth);
1190
1191 switch (gd->gd_cred.gc_svc) {
1192 case rpc_gss_svc_none:
1193 return (max_tp_unit_len);
1194 break;
1195
1196 case rpc_gss_svc_default:
1197 case rpc_gss_svc_integrity:
1198 want_conf = FALSE;
1199 break;
1200
1201 case rpc_gss_svc_privacy:
1202 want_conf = TRUE;
1203 break;
1204
1205 default:
1206 return (0);
1207 }
1208
1209 maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
1210 gd->gd_qop, max_tp_unit_len, &max);
1211
1212 if (maj_stat == GSS_S_COMPLETE) {
1213 result = (int) max;
1214 if (result < 0)
1215 result = 0;
1216 return (result);
1217 } else {
1218 rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech,
1219 maj_stat, min_stat);
1220 return (0);
1221 }
1222 }
1223