2 * Copyright (c) 2008 Doug Rabson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 RPCSEC_GSS client routines.
31 Copyright (c) 2000 The Regents of the University of Michigan.
34 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
35 All rights reserved, all wrongs reversed.
37 Redistribution and use in source and binary forms, with or without
38 modification, are permitted provided that the following conditions
41 1. Redistributions of source code must retain the above copyright
42 notice, this list of conditions and the following disclaimer.
43 2. Redistributions in binary form must reproduce the above copyright
44 notice, this list of conditions and the following disclaimer in the
45 documentation and/or other materials provided with the distribution.
46 3. Neither the name of the University nor the names of its
47 contributors may be used to endorse or promote products derived
48 from this software without specific prior written permission.
50 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
51 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
57 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
65 #include <sys/cdefs.h>
66 __FBSDID("$FreeBSD$");
68 #include <sys/param.h>
69 #include <sys/systm.h>
71 #include <sys/kernel.h>
74 #include <sys/malloc.h>
76 #include <sys/mutex.h>
78 #include <sys/refcount.h>
80 #include <sys/ucred.h>
83 #include <rpc/rpcsec_gss.h>
85 #include "rpcsec_gss_int.h"
87 static void rpc_gss_nextverf(AUTH*);
88 static bool_t rpc_gss_marshal(AUTH *, uint32_t, XDR *, struct mbuf *);
89 static bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
90 static bool_t rpc_gss_refresh(AUTH *, void *);
91 static bool_t rpc_gss_validate(AUTH *, uint32_t, struct opaque_auth *,
93 static void rpc_gss_destroy(AUTH *);
94 static void rpc_gss_destroy_context(AUTH *, bool_t);
96 static struct auth_ops rpc_gss_ops = {
104 enum rpcsec_gss_state {
107 RPCSEC_GSS_ESTABLISHED,
108 RPCSEC_GSS_DESTROYING
111 struct rpc_pending_request {
112 uint32_t pr_xid; /* XID of rpc */
113 uint32_t pr_seq; /* matching GSS seq */
114 LIST_ENTRY(rpc_pending_request) pr_link;
116 LIST_HEAD(rpc_pending_request_list, rpc_pending_request);
118 struct rpc_gss_data {
119 volatile u_int gd_refs; /* number of current users */
122 AUTH *gd_auth; /* link back to AUTH */
123 struct ucred *gd_ucred; /* matching local cred */
124 char *gd_principal; /* server principal name */
125 rpc_gss_options_req_t gd_options; /* GSS context options */
126 enum rpcsec_gss_state gd_state; /* connection state */
127 gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE
128 * NULL RPC verfier to
130 * context negotiation */
131 CLIENT *gd_clnt; /* client handle */
132 gss_OID gd_mech; /* mechanism to use */
133 gss_qop_t gd_qop; /* quality of protection */
134 gss_ctx_id_t gd_ctx; /* context id */
135 struct rpc_gss_cred gd_cred; /* client credentials */
136 uint32_t gd_seq; /* next sequence number */
137 u_int gd_win; /* sequence window */
138 struct rpc_pending_request_list gd_reqs;
139 TAILQ_ENTRY(rpc_gss_data) gd_link;
140 TAILQ_ENTRY(rpc_gss_data) gd_alllink;
142 TAILQ_HEAD(rpc_gss_data_list, rpc_gss_data);
144 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
146 static struct timeval AUTH_TIMEOUT = { 25, 0 };
148 #define RPC_GSS_HASH_SIZE 11
149 #define RPC_GSS_MAX 256
150 static struct rpc_gss_data_list rpc_gss_cache[RPC_GSS_HASH_SIZE];
151 static struct rpc_gss_data_list rpc_gss_all;
152 static struct sx rpc_gss_lock;
153 static int rpc_gss_count;
155 static AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *,
156 gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *,
157 rpc_gss_options_ret_t *);
160 rpc_gss_hashinit(void *dummy)
164 for (i = 0; i < RPC_GSS_HASH_SIZE; i++)
165 TAILQ_INIT(&rpc_gss_cache[i]);
166 TAILQ_INIT(&rpc_gss_all);
167 sx_init(&rpc_gss_lock, "rpc_gss_lock");
169 SYSINIT(rpc_gss_hashinit, SI_SUB_KMEM, SI_ORDER_ANY, rpc_gss_hashinit, NULL);
172 rpc_gss_hash(const char *principal, gss_OID mech,
173 struct ucred *cred, rpc_gss_service_t service)
177 h = HASHSTEP(HASHINIT, cred->cr_uid);
178 h = hash32_str(principal, h);
179 h = hash32_buf(mech->elements, mech->length, h);
180 h = HASHSTEP(h, (int) service);
182 return (h % RPC_GSS_HASH_SIZE);
186 * Simplified interface to create a security association for the
187 * current thread's * ucred.
190 rpc_gss_secfind(CLIENT *clnt, struct ucred *cred, const char *principal,
191 gss_OID mech_oid, rpc_gss_service_t service)
195 struct rpc_gss_data *gd, *tgd;
196 rpc_gss_options_ret_t options;
198 if (rpc_gss_count > RPC_GSS_MAX) {
199 while (rpc_gss_count > RPC_GSS_MAX) {
200 sx_xlock(&rpc_gss_lock);
201 tgd = TAILQ_FIRST(&rpc_gss_all);
203 TAILQ_REMOVE(&rpc_gss_cache[th], tgd, gd_link);
204 TAILQ_REMOVE(&rpc_gss_all, tgd, gd_alllink);
206 sx_xunlock(&rpc_gss_lock);
207 AUTH_DESTROY(tgd->gd_auth);
212 * See if we already have an AUTH which matches.
214 h = rpc_gss_hash(principal, mech_oid, cred, service);
217 sx_slock(&rpc_gss_lock);
218 TAILQ_FOREACH(gd, &rpc_gss_cache[h], gd_link) {
219 if (gd->gd_ucred->cr_uid == cred->cr_uid
220 && !strcmp(gd->gd_principal, principal)
221 && gd->gd_mech == mech_oid
222 && gd->gd_cred.gc_svc == service) {
223 refcount_acquire(&gd->gd_refs);
224 if (sx_try_upgrade(&rpc_gss_lock)) {
226 * Keep rpc_gss_all LRU sorted.
228 TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
229 TAILQ_INSERT_TAIL(&rpc_gss_all, gd,
231 sx_xunlock(&rpc_gss_lock);
233 sx_sunlock(&rpc_gss_lock);
237 * If the state != ESTABLISHED, try and initialize
238 * the authenticator again. This will happen if the
239 * user's credentials have expired. It may succeed now,
240 * if they have done a kinit or similar.
242 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
243 memset(&options, 0, sizeof (options));
244 (void) rpc_gss_init(gd->gd_auth, &options);
246 return (gd->gd_auth);
249 sx_sunlock(&rpc_gss_lock);
252 * We missed in the cache - create a new association.
254 auth = rpc_gss_seccreate_int(clnt, cred, principal, mech_oid, service,
255 GSS_C_QOP_DEFAULT, NULL, NULL);
259 gd = AUTH_PRIVATE(auth);
262 sx_xlock(&rpc_gss_lock);
263 TAILQ_FOREACH(tgd, &rpc_gss_cache[h], gd_link) {
264 if (tgd->gd_ucred->cr_uid == cred->cr_uid
265 && !strcmp(tgd->gd_principal, principal)
266 && tgd->gd_mech == mech_oid
267 && tgd->gd_cred.gc_svc == service) {
269 * We lost a race to create the AUTH that
272 sx_xunlock(&rpc_gss_lock);
279 TAILQ_INSERT_TAIL(&rpc_gss_cache[h], gd, gd_link);
280 TAILQ_INSERT_TAIL(&rpc_gss_all, gd, gd_alllink);
281 refcount_acquire(&gd->gd_refs); /* one for the cache, one for user */
282 sx_xunlock(&rpc_gss_lock);
288 rpc_gss_secpurge(CLIENT *clnt)
291 struct rpc_gss_data *gd, *tgd;
293 TAILQ_FOREACH_SAFE(gd, &rpc_gss_all, gd_alllink, tgd) {
294 if (gd->gd_clnt == clnt) {
295 sx_xlock(&rpc_gss_lock);
297 TAILQ_REMOVE(&rpc_gss_cache[h], gd, gd_link);
298 TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
300 sx_xunlock(&rpc_gss_lock);
301 AUTH_DESTROY(gd->gd_auth);
307 rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *principal,
308 const char *mechanism, rpc_gss_service_t service, const char *qop,
309 rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
315 * Bail out now if we don't know this mechanism.
317 if (!rpc_gss_mech_to_oid(mechanism, &oid))
321 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
324 qop_num = GSS_C_QOP_DEFAULT;
327 return (rpc_gss_seccreate_int(clnt, cred, principal, oid, service,
328 qop_num, options_req, options_ret));
332 rpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred, const char *principal,
333 gss_OID mech_oid, rpc_gss_service_t service, u_int qop_num,
334 rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
337 rpc_gss_options_ret_t options;
338 struct rpc_gss_data *gd;
341 * If the caller doesn't want the options, point at local
342 * storage to simplify the code below.
345 options_ret = &options;
348 * Default service is integrity.
350 if (service == rpc_gss_svc_default)
351 service = rpc_gss_svc_integrity;
353 memset(options_ret, 0, sizeof(*options_ret));
355 rpc_gss_log_debug("in rpc_gss_seccreate()");
357 memset(&rpc_createerr, 0, sizeof(rpc_createerr));
359 auth = mem_alloc(sizeof(*auth));
361 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
362 rpc_createerr.cf_error.re_errno = ENOMEM;
365 gd = mem_alloc(sizeof(*gd));
367 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
368 rpc_createerr.cf_error.re_errno = ENOMEM;
369 mem_free(auth, sizeof(*auth));
373 auth->ah_ops = &rpc_gss_ops;
374 auth->ah_private = (caddr_t) gd;
375 auth->ah_cred.oa_flavor = RPCSEC_GSS;
377 refcount_init(&gd->gd_refs, 1);
378 mtx_init(&gd->gd_lock, "gd->gd_lock", NULL, MTX_DEF);
380 gd->gd_ucred = crdup(cred);
381 gd->gd_principal = strdup(principal, M_RPC);
385 gd->gd_options = *options_req;
387 gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
388 gd->gd_options.time_req = 0;
389 gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
390 gd->gd_options.input_channel_bindings = NULL;
394 gd->gd_ctx = GSS_C_NO_CONTEXT;
395 gd->gd_mech = mech_oid;
396 gd->gd_qop = qop_num;
398 gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
399 gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
400 gd->gd_cred.gc_seq = 0;
401 gd->gd_cred.gc_svc = service;
402 LIST_INIT(&gd->gd_reqs);
404 if (!rpc_gss_init(auth, options_ret)) {
416 rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
418 struct rpc_gss_data *gd;
420 const char *mechanism;
422 gd = AUTH_PRIVATE(auth);
423 if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
428 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
432 qop_num = GSS_C_QOP_DEFAULT;
435 gd->gd_cred.gc_svc = service;
436 gd->gd_qop = qop_num;
441 rpc_gss_purge_xid(struct rpc_gss_data *gd, uint32_t xid)
443 struct rpc_pending_request *pr, *npr;
444 struct rpc_pending_request_list reqs;
447 mtx_lock(&gd->gd_lock);
448 LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
449 if (pr->pr_xid == xid) {
450 LIST_REMOVE(pr, pr_link);
451 LIST_INSERT_HEAD(&reqs, pr, pr_link);
455 mtx_unlock(&gd->gd_lock);
457 LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
458 mem_free(pr, sizeof(*pr));
463 rpc_gss_alloc_seq(struct rpc_gss_data *gd)
467 mtx_lock(&gd->gd_lock);
470 mtx_unlock(&gd->gd_lock);
476 rpc_gss_nextverf(__unused AUTH *auth)
483 rpc_gss_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
485 struct rpc_gss_data *gd;
486 struct rpc_pending_request *pr;
489 struct rpc_gss_cred gsscred;
490 char credbuf[MAX_AUTH_BYTES];
491 struct opaque_auth creds, verf;
492 gss_buffer_desc rpcbuf, checksum;
493 OM_uint32 maj_stat, min_stat;
496 rpc_gss_log_debug("in rpc_gss_marshal()");
498 gd = AUTH_PRIVATE(auth);
500 gsscred = gd->gd_cred;
501 seq = rpc_gss_alloc_seq(gd);
502 gsscred.gc_seq = seq;
504 xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
505 if (!xdr_rpc_gss_cred(&tmpxdrs, &gsscred)) {
506 XDR_DESTROY(&tmpxdrs);
507 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
510 creds.oa_flavor = RPCSEC_GSS;
511 creds.oa_base = credbuf;
512 creds.oa_length = XDR_GETPOS(&tmpxdrs);
513 XDR_DESTROY(&tmpxdrs);
515 xdr_opaque_auth(xdrs, &creds);
517 if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
518 gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
519 if (!xdr_opaque_auth(xdrs, &_null_auth)) {
520 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
523 xdrmbuf_append(xdrs, args);
527 * Keep track of this XID + seq pair so that we can do
528 * the matching gss_verify_mic in AUTH_VALIDATE.
530 pr = mem_alloc(sizeof(struct rpc_pending_request));
531 mtx_lock(&gd->gd_lock);
534 LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
535 mtx_unlock(&gd->gd_lock);
538 * Checksum serialized RPC header, up to and including
539 * credential. For the in-kernel environment, we
540 * assume that our XDR stream is on a contiguous
541 * memory buffer (e.g. an mbuf).
543 rpcbuf.length = XDR_GETPOS(xdrs);
545 rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
547 maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
550 if (maj_stat != GSS_S_COMPLETE) {
551 rpc_gss_log_status("gss_get_mic", gd->gd_mech,
553 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
554 rpc_gss_destroy_context(auth, TRUE);
556 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
560 verf.oa_flavor = RPCSEC_GSS;
561 verf.oa_base = checksum.value;
562 verf.oa_length = checksum.length;
564 xdr_stat = xdr_opaque_auth(xdrs, &verf);
565 gss_release_buffer(&min_stat, &checksum);
567 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
570 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
571 gd->gd_cred.gc_svc == rpc_gss_svc_none) {
572 xdrmbuf_append(xdrs, args);
575 if (!xdr_rpc_gss_wrap_data(&args,
576 gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
579 xdrmbuf_append(xdrs, args);
588 rpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
589 struct mbuf **resultsp)
591 struct rpc_gss_data *gd;
592 struct rpc_pending_request *pr, *npr;
593 struct rpc_pending_request_list reqs;
596 gss_buffer_desc signbuf, checksum;
597 OM_uint32 maj_stat, min_stat;
599 rpc_gss_log_debug("in rpc_gss_validate()");
601 gd = AUTH_PRIVATE(auth);
604 * The client will call us with a NULL verf when it gives up
608 rpc_gss_purge_xid(gd, xid);
612 if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
614 * Save the on the wire verifier to validate last INIT
615 * phase packet after decode if the major status is
618 if (gd->gd_verf.value)
619 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
620 (char *) &gd->gd_verf);
621 gd->gd_verf.value = mem_alloc(verf->oa_length);
622 if (gd->gd_verf.value == NULL) {
623 printf("gss_validate: out of memory\n");
624 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
629 memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
630 gd->gd_verf.length = verf->oa_length;
636 * We need to check the verifier against all the requests
637 * we've send for this XID - for unreliable protocols, we
638 * retransmit with the same XID but different sequence
639 * number. We temporarily take this set of requests out of the
640 * list so that we can work through the list without having to
643 mtx_lock(&gd->gd_lock);
645 LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
646 if (pr->pr_xid == xid) {
647 LIST_REMOVE(pr, pr_link);
648 LIST_INSERT_HEAD(&reqs, pr, pr_link);
651 mtx_unlock(&gd->gd_lock);
652 LIST_FOREACH(pr, &reqs, pr_link) {
653 if (pr->pr_xid == xid) {
656 signbuf.value = #
657 signbuf.length = sizeof(num);
659 checksum.value = verf->oa_base;
660 checksum.length = verf->oa_length;
662 maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
663 &signbuf, &checksum, &qop_state);
664 if (maj_stat != GSS_S_COMPLETE
665 || qop_state != gd->gd_qop) {
668 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
669 rpc_gss_destroy_context(auth, TRUE);
672 //rpc_gss_purge_reqs(gd, seq);
673 LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr)
674 mem_free(pr, sizeof(*pr));
676 if (gd->gd_cred.gc_svc == rpc_gss_svc_none) {
679 if (!xdr_rpc_gss_unwrap_data(resultsp,
680 gd->gd_ctx, gd->gd_qop,
681 gd->gd_cred.gc_svc, seq)) {
690 * We didn't match - put back any entries for this XID so that
691 * a future call to validate can retry.
693 mtx_lock(&gd->gd_lock);
694 LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
695 LIST_REMOVE(pr, pr_link);
696 LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
698 mtx_unlock(&gd->gd_lock);
701 * Nothing matches - give up.
703 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
710 rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
712 struct thread *td = curthread;
713 struct ucred *crsave;
714 struct rpc_gss_data *gd;
715 struct rpc_gss_init_res gr;
716 gss_buffer_desc principal_desc;
717 gss_buffer_desc *recv_tokenp, recv_token, send_token;
719 OM_uint32 maj_stat, min_stat, call_stat;
721 struct rpc_callextra ext;
723 rpc_gss_log_debug("in rpc_gss_refresh()");
725 gd = AUTH_PRIVATE(auth);
727 mtx_lock(&gd->gd_lock);
729 * If the context isn't in START state, someone else is
730 * refreshing - we wait till they are done. If they fail, they
731 * will put the state back to START and we can try (most
732 * likely to also fail).
734 while (gd->gd_state != RPCSEC_GSS_START
735 && gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
736 msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
738 if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) {
739 mtx_unlock(&gd->gd_lock);
742 gd->gd_state = RPCSEC_GSS_CONTEXT;
743 mtx_unlock(&gd->gd_lock);
745 gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
746 gd->gd_cred.gc_seq = 0;
748 principal_desc.value = (void *)gd->gd_principal;
749 principal_desc.length = strlen(gd->gd_principal);
750 maj_stat = gss_import_name(&min_stat, &principal_desc,
751 GSS_C_NT_HOSTBASED_SERVICE, &name);
752 if (maj_stat != GSS_S_COMPLETE) {
753 options_ret->major_status = maj_stat;
754 options_ret->minor_status = min_stat;
758 /* GSS context establishment loop. */
759 memset(&recv_token, 0, sizeof(recv_token));
760 memset(&gr, 0, sizeof(gr));
761 memset(options_ret, 0, sizeof(*options_ret));
762 options_ret->major_status = GSS_S_FAILURE;
763 recv_tokenp = GSS_C_NO_BUFFER;
766 crsave = td->td_ucred;
767 td->td_ucred = gd->gd_ucred;
768 maj_stat = gss_init_sec_context(&min_stat,
769 gd->gd_options.my_cred,
773 gd->gd_options.req_flags,
774 gd->gd_options.time_req,
775 gd->gd_options.input_channel_bindings,
777 &gd->gd_mech, /* used mech */
779 &options_ret->ret_flags,
780 &options_ret->time_req);
781 td->td_ucred = crsave;
784 * Free the token which we got from the server (if
785 * any). Remember that this was allocated by XDR, not
788 if (recv_tokenp != GSS_C_NO_BUFFER) {
789 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
790 (char *) &recv_token);
791 recv_tokenp = GSS_C_NO_BUFFER;
793 if (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
794 strlcpy(options_ret->actual_mechanism,
796 sizeof(options_ret->actual_mechanism));
798 if (maj_stat != GSS_S_COMPLETE &&
799 maj_stat != GSS_S_CONTINUE_NEEDED) {
800 rpc_gss_log_status("gss_init_sec_context", gd->gd_mech,
802 options_ret->major_status = maj_stat;
803 options_ret->minor_status = min_stat;
806 if (send_token.length != 0) {
807 memset(&gr, 0, sizeof(gr));
809 bzero(&ext, sizeof(ext));
811 call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
812 (xdrproc_t)xdr_gss_buffer_desc,
814 (xdrproc_t)xdr_rpc_gss_init_res,
815 (caddr_t)&gr, AUTH_TIMEOUT);
817 gss_release_buffer(&min_stat, &send_token);
819 if (call_stat != RPC_SUCCESS)
822 if (gr.gr_major != GSS_S_COMPLETE &&
823 gr.gr_major != GSS_S_CONTINUE_NEEDED) {
824 rpc_gss_log_status("server reply", gd->gd_mech,
825 gr.gr_major, gr.gr_minor);
826 options_ret->major_status = gr.gr_major;
827 options_ret->minor_status = gr.gr_minor;
832 * Save the server's gr_handle value, freeing
833 * what we have already (remember that this
834 * was allocated by XDR, not GSS-API).
836 if (gr.gr_handle.length != 0) {
837 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
838 (char *) &gd->gd_cred.gc_handle);
839 gd->gd_cred.gc_handle = gr.gr_handle;
843 * Save the server's token as well.
845 if (gr.gr_token.length != 0) {
846 recv_token = gr.gr_token;
847 recv_tokenp = &recv_token;
851 * Since we have copied out all the bits of gr
852 * which XDR allocated for us, we don't need
855 gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
858 if (maj_stat == GSS_S_COMPLETE) {
859 gss_buffer_desc bufin;
860 u_int seq, qop_state = 0;
863 * gss header verifier,
864 * usually checked in gss_validate
866 seq = htonl(gr.gr_win);
867 bufin.value = (unsigned char *)&seq;
868 bufin.length = sizeof(seq);
870 maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
871 &bufin, &gd->gd_verf, &qop_state);
873 if (maj_stat != GSS_S_COMPLETE ||
874 qop_state != gd->gd_qop) {
875 rpc_gss_log_status("gss_verify_mic", gd->gd_mech,
877 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
878 rpc_gss_destroy_context(auth, TRUE);
880 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
882 options_ret->major_status = maj_stat;
883 options_ret->minor_status = min_stat;
887 options_ret->major_status = GSS_S_COMPLETE;
888 options_ret->minor_status = 0;
889 options_ret->rpcsec_version = gd->gd_cred.gc_version;
890 options_ret->gss_context = gd->gd_ctx;
892 gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
894 gd->gd_win = gr.gr_win;
899 gss_release_name(&min_stat, &name);
900 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
901 (char *) &gd->gd_verf);
904 /* End context negotiation loop. */
905 if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
906 rpc_createerr.cf_stat = RPC_AUTHERROR;
907 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
909 gss_delete_sec_context(&min_stat, &gd->gd_ctx,
912 mtx_lock(&gd->gd_lock);
913 gd->gd_state = RPCSEC_GSS_START;
915 mtx_unlock(&gd->gd_lock);
919 mtx_lock(&gd->gd_lock);
920 gd->gd_state = RPCSEC_GSS_ESTABLISHED;
922 mtx_unlock(&gd->gd_lock);
928 rpc_gss_refresh(AUTH *auth, void *msg)
930 struct rpc_msg *reply = (struct rpc_msg *) msg;
931 rpc_gss_options_ret_t options;
932 struct rpc_gss_data *gd;
934 gd = AUTH_PRIVATE(auth);
937 * If the context is in DESTROYING state, then just return, since
938 * there is no point in refreshing the credentials.
940 mtx_lock(&gd->gd_lock);
941 if (gd->gd_state == RPCSEC_GSS_DESTROYING) {
942 mtx_unlock(&gd->gd_lock);
945 mtx_unlock(&gd->gd_lock);
948 * If the error was RPCSEC_GSS_CREDPROBLEM of
949 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
950 * other errors are fatal.
952 if (reply->rm_reply.rp_stat == MSG_DENIED
953 && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
954 && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
955 || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
956 rpc_gss_destroy_context(auth, FALSE);
957 memset(&options, 0, sizeof(options));
958 return (rpc_gss_init(auth, &options));
965 rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
967 struct rpc_gss_data *gd;
968 struct rpc_pending_request *pr;
970 struct rpc_callextra ext;
972 rpc_gss_log_debug("in rpc_gss_destroy_context()");
974 gd = AUTH_PRIVATE(auth);
976 mtx_lock(&gd->gd_lock);
978 * If the context isn't in ESTABISHED state, someone else is
979 * destroying/refreshing - we wait till they are done.
981 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
982 while (gd->gd_state != RPCSEC_GSS_START
983 && gd->gd_state != RPCSEC_GSS_ESTABLISHED)
984 msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
985 mtx_unlock(&gd->gd_lock);
988 gd->gd_state = RPCSEC_GSS_DESTROYING;
989 mtx_unlock(&gd->gd_lock);
992 gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
993 bzero(&ext, sizeof(ext));
995 CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
996 (xdrproc_t)xdr_void, NULL,
997 (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
1000 while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) {
1001 LIST_REMOVE(pr, pr_link);
1002 mem_free(pr, sizeof(*pr));
1006 * Free the context token. Remember that this was
1007 * allocated by XDR, not GSS-API.
1009 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1010 (char *) &gd->gd_cred.gc_handle);
1011 gd->gd_cred.gc_handle.length = 0;
1013 if (gd->gd_ctx != GSS_C_NO_CONTEXT)
1014 gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
1016 mtx_lock(&gd->gd_lock);
1017 gd->gd_state = RPCSEC_GSS_START;
1019 mtx_unlock(&gd->gd_lock);
1023 rpc_gss_destroy(AUTH *auth)
1025 struct rpc_gss_data *gd;
1027 rpc_gss_log_debug("in rpc_gss_destroy()");
1029 gd = AUTH_PRIVATE(auth);
1031 if (!refcount_release(&gd->gd_refs))
1034 rpc_gss_destroy_context(auth, TRUE);
1036 CLNT_RELEASE(gd->gd_clnt);
1037 crfree(gd->gd_ucred);
1038 free(gd->gd_principal, M_RPC);
1039 if (gd->gd_verf.value)
1040 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1041 (char *) &gd->gd_verf);
1042 mtx_destroy(&gd->gd_lock);
1044 mem_free(gd, sizeof(*gd));
1045 mem_free(auth, sizeof(*auth));
1049 rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
1051 struct rpc_gss_data *gd;
1054 OM_uint32 maj_stat, min_stat;
1057 gd = AUTH_PRIVATE(auth);
1059 switch (gd->gd_cred.gc_svc) {
1060 case rpc_gss_svc_none:
1061 return (max_tp_unit_len);
1064 case rpc_gss_svc_default:
1065 case rpc_gss_svc_integrity:
1069 case rpc_gss_svc_privacy:
1077 maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
1078 gd->gd_qop, max_tp_unit_len, &max);
1080 if (maj_stat == GSS_S_COMPLETE) {
1086 rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech,
1087 maj_stat, min_stat);