import less(1)
[unleashed/tickless.git] / usr / src / lib / rpcsec_gss / svc_rpcsec_gss.c
blobaccf1adcc61e3893d77d48667445403faece3840
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
32 * $Id: svc_auth_gssapi.c,v 1.19 1994/10/27 12:38:51 jik Exp $
36 * Server side handling of RPCSEC_GSS flavor.
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <strings.h>
42 #include <sys/stat.h>
43 #include <sys/time.h>
44 #include <gssapi/gssapi.h>
45 #include <gssapi/gssapi_ext.h>
46 #include <rpc/rpc.h>
47 #include <rpc/rpcsec_defs.h>
48 #include <sys/file.h>
49 #include <fcntl.h>
50 #include <pwd.h>
51 #include <stdio.h>
52 #include <syslog.h>
55 * Sequence window definitions.
57 #define SEQ_ARR_SIZE 4
58 #define SEQ_WIN (SEQ_ARR_SIZE*32)
59 #define SEQ_HI_BIT 0x80000000
60 #define SEQ_LO_BIT 1
61 #define DIV_BY_32 5
62 #define SEQ_MASK 0x1f
63 #define SEQ_MAX 0x80000000
66 /* cache retransmit data */
67 typedef struct _retrans_entry {
68 uint32_t xid;
69 rpc_gss_init_res result;
70 struct _retrans_entry *next, *prev;
71 } retrans_entry;
74 * Server side RPCSEC_GSS context information.
76 typedef struct _svc_rpc_gss_data {
77 struct _svc_rpc_gss_data *next, *prev;
78 struct _svc_rpc_gss_data *lru_next, *lru_prev;
79 bool_t established;
80 gss_ctx_id_t context;
81 gss_name_t client_name;
82 gss_cred_id_t server_creds;
83 uint_t expiration;
84 uint_t seq_num;
85 uint_t seq_bits[SEQ_ARR_SIZE];
86 uint_t key;
87 OM_uint32 qop;
88 bool_t done_docallback;
89 bool_t locked;
90 rpc_gss_rawcred_t raw_cred;
91 rpc_gss_ucred_t u_cred;
92 bool_t u_cred_set;
93 void *cookie;
94 gss_cred_id_t deleg;
95 mutex_t clm;
96 int ref_cnt;
97 bool_t stale;
98 time_t time_secs_set;
99 retrans_entry *retrans_data;
100 } svc_rpc_gss_data;
103 * Data structures used for LRU based context management.
105 #define HASHMOD 256
106 #define HASHMASK 255
108 static svc_rpc_gss_data *clients[HASHMOD];
109 static svc_rpc_gss_data *lru_first, *lru_last;
110 static int num_gss_contexts = 0;
111 static int max_gss_contexts = 128;
112 static int sweep_interval = 10;
113 static int last_swept = 0;
114 static uint_t max_lifetime = GSS_C_INDEFINITE;
115 static int init_lifetime = 0;
116 static uint_t gid_timeout = 43200; /* 43200 secs = 12 hours */
119 * lock used with context/lru variables
121 static mutex_t ctx_mutex = DEFAULTMUTEX;
124 * server credential management data and structures
126 typedef struct svc_creds_list_s {
127 struct svc_creds_list_s *next;
128 gss_cred_id_t cred;
129 gss_name_t name;
130 rpcprog_t program;
131 rpcvers_t version;
132 gss_OID_set oid_set;
133 OM_uint32 req_time;
134 char *server_name;
135 mutex_t refresh_mutex;
136 } svc_creds_list_t;
139 static svc_creds_list_t *svc_creds_list;
140 static int svc_creds_count = 0;
143 * lock used with server credential variables list
145 * server cred list locking guidelines:
146 * - Writer's lock holder has exclusive access to the list
147 * - Reader's lock holder(s) must also lock (refresh_mutex) each node
148 * before accessing that node's elements (ie. cred)
150 static rwlock_t cred_lock = DEFAULTRWLOCK;
153 * server callback list
155 typedef struct cblist_s {
156 struct cblist_s *next;
157 rpc_gss_callback_t cb;
158 } cblist_t;
160 cblist_t *cblist = NULL;
163 * lock used with callback variables
165 static mutex_t cb_mutex = DEFAULTMUTEX;
168 * forward declarations
170 static bool_t svc_rpc_gss_wrap();
171 static bool_t svc_rpc_gss_unwrap();
172 static svc_rpc_gss_data *create_client();
173 static svc_rpc_gss_data *get_client();
174 static svc_rpc_gss_data *find_client();
175 static void destroy_client();
176 static void sweep_clients();
177 static void drop_lru_client();
178 static void insert_client();
179 static bool_t check_verf();
180 static bool_t rpc_gss_refresh_svc_cred();
181 static bool_t set_response_verf();
182 static void retrans_add(svc_rpc_gss_data *, uint32_t,
183 rpc_gss_init_res *);
184 static void retrans_del(struct _svc_rpc_gss_data *);
188 * server side wrap/unwrap routines
190 struct svc_auth_ops svc_rpc_gss_ops = {
191 svc_rpc_gss_wrap,
192 svc_rpc_gss_unwrap,
196 * Fetch server side authentication structure.
198 extern SVCAUTH *__svc_get_svcauth();
201 * Cleanup routine for destroying context, called after service
202 * procedure is executed, for MT safeness.
204 extern void *__svc_set_proc_cleanup_cb();
205 static void (*old_cleanup_cb)() = NULL;
206 static bool_t cleanup_cb_set = FALSE;
208 static void
209 ctx_cleanup(xprt)
210 SVCXPRT *xprt;
212 svc_rpc_gss_data *cl;
213 SVCAUTH *svcauth;
215 if (old_cleanup_cb != NULL)
216 (*old_cleanup_cb)(xprt);
219 * First check if current context needs to be cleaned up.
221 svcauth = __svc_get_svcauth(xprt);
222 /*LINTED*/
223 if ((cl = (svc_rpc_gss_data *)svcauth->svc_ah_private) != NULL) {
224 mutex_lock(&cl->clm);
225 if (--cl->ref_cnt == 0 && cl->stale) {
226 mutex_unlock(&cl->clm);
227 mutex_lock(&ctx_mutex);
228 destroy_client(cl);
229 mutex_unlock(&ctx_mutex);
230 } else
231 mutex_unlock(&cl->clm);
235 * Check for other expired contexts.
237 if ((time(0) - last_swept) > sweep_interval) {
238 mutex_lock(&ctx_mutex);
240 * Check again, in case some other thread got in.
242 if ((time(0) - last_swept) > sweep_interval)
243 sweep_clients();
244 mutex_unlock(&ctx_mutex);
249 * Set server parameters.
251 void
252 __rpc_gss_set_server_parms(init_cred_lifetime, max_cred_lifetime, cache_size)
253 int init_cred_lifetime;
254 int max_cred_lifetime;
255 int cache_size;
258 * Ignore parameters unless greater than zero.
260 mutex_lock(&ctx_mutex);
261 if (cache_size > 0)
262 max_gss_contexts = cache_size;
263 if (max_cred_lifetime > 0)
264 max_lifetime = (uint_t)max_cred_lifetime;
265 if (init_cred_lifetime > 0)
266 init_lifetime = init_cred_lifetime;
267 mutex_unlock(&ctx_mutex);
271 * Shift the array arr of length arrlen right by nbits bits.
273 static void
274 shift_bits(arr, arrlen, nbits)
275 uint_t *arr;
276 int arrlen;
277 int nbits;
279 int i, j;
280 uint_t lo, hi;
283 * If the number of bits to be shifted exceeds SEQ_WIN, just
284 * zero out the array.
286 if (nbits < SEQ_WIN) {
287 for (i = 0; i < nbits; i++) {
288 hi = 0;
289 for (j = 0; j < arrlen; j++) {
290 lo = arr[j] & SEQ_LO_BIT;
291 arr[j] >>= 1;
292 if (hi)
293 arr[j] |= SEQ_HI_BIT;
294 hi = lo;
297 } else {
298 for (j = 0; j < arrlen; j++)
299 arr[j] = 0;
304 * Check that the received sequence number seq_num is valid.
306 static bool_t
307 check_seq(cl, seq_num, kill_context)
308 svc_rpc_gss_data *cl;
309 uint_t seq_num;
310 bool_t *kill_context;
312 int i, j;
313 uint_t bit;
316 * If it exceeds the maximum, kill context.
318 if (seq_num >= SEQ_MAX) {
319 *kill_context = TRUE;
320 return (FALSE);
324 * If greater than the last seen sequence number, just shift
325 * the sequence window so that it starts at the new sequence
326 * number and extends downwards by SEQ_WIN.
328 if (seq_num > cl->seq_num) {
329 shift_bits(cl->seq_bits, SEQ_ARR_SIZE, seq_num - cl->seq_num);
330 cl->seq_bits[0] |= SEQ_HI_BIT;
331 cl->seq_num = seq_num;
332 return (TRUE);
336 * If it is outside the sequence window, return failure.
338 i = cl->seq_num - seq_num;
339 if (i >= SEQ_WIN)
340 return (FALSE);
343 * If within sequence window, set the bit corresponding to it
344 * if not already seen; if already seen, return failure.
346 j = SEQ_MASK - (i & SEQ_MASK);
347 bit = j > 0 ? (1 << j) : 1;
348 i >>= DIV_BY_32;
349 if (cl->seq_bits[i] & bit)
350 return (FALSE);
351 cl->seq_bits[i] |= bit;
352 return (TRUE);
356 * Convert a name in gss exported type to rpc_gss_principal_t type.
358 static bool_t
359 __rpc_gss_make_principal(principal, name)
360 rpc_gss_principal_t *principal;
361 gss_buffer_desc *name;
363 int plen;
364 char *s;
366 plen = RNDUP(name->length) + sizeof (int);
367 (*principal) = (rpc_gss_principal_t)malloc(plen);
368 if ((*principal) == NULL)
369 return (FALSE);
370 bzero((caddr_t)(*principal), plen);
371 (*principal)->len = RNDUP(name->length);
372 s = (*principal)->name;
373 memcpy(s, name->value, name->length);
374 return (TRUE);
378 * Convert a name in internal form to the exported type.
380 static bool_t
381 set_client_principal(g_name, r_name)
382 gss_name_t g_name;
383 rpc_gss_principal_t *r_name;
385 gss_buffer_desc name;
386 OM_uint32 major, minor;
387 bool_t ret = FALSE;
389 major = gss_export_name(&minor, g_name, &name);
390 if (major != GSS_S_COMPLETE)
391 return (FALSE);
392 ret = __rpc_gss_make_principal(r_name, &name);
393 (void) gss_release_buffer(&minor, &name);
394 return (ret);
398 * Set server callback.
400 bool_t
401 __rpc_gss_set_callback(cb)
402 rpc_gss_callback_t *cb;
404 cblist_t *cbl;
406 if (cb->callback == NULL)
407 return (FALSE);
408 if ((cbl = (cblist_t *)malloc(sizeof (*cbl))) == NULL)
409 return (FALSE);
410 cbl->cb = *cb;
411 mutex_lock(&cb_mutex);
412 cbl->next = cblist;
413 cblist = cbl;
414 mutex_unlock(&cb_mutex);
415 return (TRUE);
419 * Locate callback (if specified) and call server. Release any
420 * delegated credentials unless passed to server and the server
421 * accepts the context. If a callback is not specified, accept
422 * the incoming context.
424 static bool_t
425 do_callback(req, client_data)
426 struct svc_req *req;
427 svc_rpc_gss_data *client_data;
429 cblist_t *cbl;
430 bool_t ret = TRUE, found = FALSE;
431 rpc_gss_lock_t lock;
432 OM_uint32 minor;
434 mutex_lock(&cb_mutex);
435 for (cbl = cblist; cbl != NULL; cbl = cbl->next) {
436 if (req->rq_prog != cbl->cb.program ||
437 req->rq_vers != cbl->cb.version)
438 continue;
439 found = TRUE;
440 lock.locked = FALSE;
441 lock.raw_cred = &client_data->raw_cred;
442 ret = (*cbl->cb.callback)(req, client_data->deleg,
443 client_data->context, &lock, &client_data->cookie);
444 if (ret) {
445 client_data->locked = lock.locked;
446 client_data->deleg = GSS_C_NO_CREDENTIAL;
448 break;
450 if (!found) {
451 if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
452 (void) gss_release_cred(&minor, &client_data->deleg);
453 client_data->deleg = GSS_C_NO_CREDENTIAL;
456 mutex_unlock(&cb_mutex);
457 return (ret);
461 * Return caller credentials.
463 bool_t
464 __rpc_gss_getcred(req, rcred, ucred, cookie)
465 struct svc_req *req;
466 rpc_gss_rawcred_t **rcred;
467 rpc_gss_ucred_t **ucred;
468 void **cookie;
470 SVCAUTH *svcauth;
471 svc_rpc_gss_data *client_data;
472 svc_rpc_gss_parms_t *gss_parms;
473 gss_OID oid;
474 OM_uint32 status;
475 int len = 0;
476 struct timeval now;
478 svcauth = __svc_get_svcauth(req->rq_xprt);
479 /*LINTED*/
480 client_data = (svc_rpc_gss_data *)svcauth->svc_ah_private;
481 gss_parms = &svcauth->svc_gss_parms;
483 mutex_lock(&client_data->clm);
485 if (rcred != NULL) {
486 svcauth->raw_cred = client_data->raw_cred;
487 svcauth->raw_cred.service = gss_parms->service;
488 svcauth->raw_cred.qop = __rpc_gss_num_to_qop(
489 svcauth->raw_cred.mechanism, gss_parms->qop_rcvd);
490 *rcred = &svcauth->raw_cred;
492 if (ucred != NULL) {
493 if (!client_data->u_cred_set) {
495 * Double check making sure ucred is not set
496 * after acquiring the lock.
498 if (!client_data->u_cred_set) {
499 if (!__rpc_gss_mech_to_oid(
500 (*rcred)->mechanism, &oid)) {
501 fprintf(stderr, dgettext(TEXT_DOMAIN,
502 "mech_to_oid failed in getcred.\n"));
503 *ucred = NULL;
504 } else {
505 status = gsscred_name_to_unix_cred(
506 client_data->client_name, oid,
507 &client_data->u_cred.uid,
508 &client_data->u_cred.gid,
509 &client_data->u_cred.gidlist,
510 &len);
511 if (status == GSS_S_COMPLETE) {
512 client_data->u_cred_set = TRUE;
513 client_data->u_cred.gidlen =
514 (short)len;
515 gettimeofday(&now,
516 NULL);
517 client_data->time_secs_set =
518 now.tv_sec;
519 *ucred = &client_data->u_cred;
520 } else
521 *ucred = NULL;
524 } else {
526 * gid's already set;
527 * check if they have expired.
529 gettimeofday(&now, NULL);
530 if ((now.tv_sec - client_data->time_secs_set)
531 > gid_timeout) {
532 /* Refresh gid's */
533 status = gss_get_group_info(
534 client_data->u_cred.uid,
535 &client_data->u_cred.gid,
536 &client_data->u_cred.gidlist,
537 &len);
538 if (status == GSS_S_COMPLETE) {
539 client_data->u_cred.gidlen =
540 (short)len;
541 gettimeofday(&now,
542 NULL);
543 client_data->time_secs_set = now.tv_sec;
544 *ucred = &client_data->u_cred;
545 } else {
546 client_data->u_cred_set = FALSE;
547 *ucred = NULL;
550 else
551 *ucred = &client_data->u_cred;
554 if (cookie != NULL)
555 *cookie = client_data->cookie;
557 mutex_unlock(&client_data->clm);
559 return (TRUE);
563 * Server side authentication for RPCSEC_GSS.
566 enum auth_stat
567 __svcrpcsec_gss(rqst, msg, no_dispatch)
568 struct svc_req *rqst;
569 struct rpc_msg *msg;
570 bool_t *no_dispatch;
572 XDR xdrs;
573 rpc_gss_creds creds;
574 rpc_gss_init_arg call_arg;
575 rpc_gss_init_res call_res, *retrans_result;
576 gss_buffer_desc output_token;
577 OM_uint32 gssstat, minor_stat, time_rec, ret_flags;
578 struct opaque_auth *cred;
579 svc_rpc_gss_data *client_data;
580 int ret;
581 svc_creds_list_t *sc;
582 SVCAUTH *svcauth;
583 svc_rpc_gss_parms_t *gss_parms;
584 gss_OID mech_type = GSS_C_NULL_OID;
587 * Initialize response verifier to NULL verifier. If
588 * necessary, this will be changed later.
590 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NONE;
591 rqst->rq_xprt->xp_verf.oa_base = NULL;
592 rqst->rq_xprt->xp_verf.oa_length = 0;
594 * Need to null out results to start with.
596 memset((char *)&call_res, 0, sizeof (call_res));
599 * Pull out and check credential and verifier.
601 cred = &msg->rm_call.cb_cred;
602 if (cred->oa_length == 0) {
603 return (AUTH_BADCRED);
606 xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE);
608 memset((char *)&creds, 0, sizeof (creds));
609 if (!__xdr_rpc_gss_creds(&xdrs, &creds)) {
610 XDR_DESTROY(&xdrs);
611 ret = AUTH_BADCRED;
612 goto error;
614 XDR_DESTROY(&xdrs);
617 * If this is a control message and proc is GSSAPI_INIT, then
618 * create a client handle for this client. Otherwise, look up
619 * the existing handle.
621 if (creds.gss_proc == RPCSEC_GSS_INIT) {
622 if (creds.ctx_handle.length != 0) {
623 ret = AUTH_BADCRED;
624 goto error;
626 if ((client_data = create_client()) == NULL) {
627 ret = AUTH_FAILED;
628 goto error;
630 } else {
632 * Only verify values for service parameter when proc
633 * not RPCSEC_GSS_INIT or RPCSEC_GSS_CONTINUE_INIT.
634 * RFC2203 says contents for sequence and service args
635 * are undefined for creation procs.
637 * Note: only need to check for *CONTINUE_INIT here because
638 * if() clause already checked for RPCSEC_GSS_INIT
640 if (creds.gss_proc != RPCSEC_GSS_CONTINUE_INIT) {
641 switch (creds.service) {
642 case rpc_gss_svc_none:
643 case rpc_gss_svc_integrity:
644 case rpc_gss_svc_privacy:
645 break;
646 default:
647 ret = AUTH_BADCRED;
648 goto error;
651 if (creds.ctx_handle.length == 0) {
652 ret = AUTH_BADCRED;
653 goto error;
655 if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
656 ret = RPCSEC_GSS_NOCRED;
657 goto error;
662 * lock the client data until it's safe; if it's already stale,
663 * no more processing is possible
665 mutex_lock(&client_data->clm);
666 if (client_data->stale) {
667 ret = RPCSEC_GSS_NOCRED;
668 goto error2;
672 * Any response we send will use ctx_handle, so set it now;
673 * also set seq_window since this won't change.
675 call_res.ctx_handle.length = sizeof (client_data->key);
676 call_res.ctx_handle.value = (char *)&client_data->key;
677 call_res.seq_window = SEQ_WIN;
680 * Set the appropriate wrap/unwrap routine for RPCSEC_GSS.
682 svcauth = __svc_get_svcauth(rqst->rq_xprt);
683 svcauth->svc_ah_ops = svc_rpc_gss_ops;
684 svcauth->svc_ah_private = (caddr_t)client_data;
687 * Keep copy of parameters we'll need for response, for the
688 * sake of reentrancy (we don't want to look in the context
689 * data because when we are sending a response, another
690 * request may have come in.
692 gss_parms = &svcauth->svc_gss_parms;
693 gss_parms->established = client_data->established;
694 gss_parms->service = creds.service;
695 gss_parms->qop_rcvd = (uint_t)client_data->qop;
696 gss_parms->context = (void *)client_data->context;
697 gss_parms->seq_num = creds.seq_num;
699 if (!client_data->established) {
700 if (creds.gss_proc == RPCSEC_GSS_DATA) {
701 ret = RPCSEC_GSS_FAILED;
702 client_data->stale = TRUE;
703 goto error2;
707 * If the context is not established, then only GSSAPI_INIT
708 * and _CONTINUE requests are valid.
710 if (creds.gss_proc != RPCSEC_GSS_INIT && creds.gss_proc !=
711 RPCSEC_GSS_CONTINUE_INIT) {
712 ret = RPCSEC_GSS_FAILED;
713 client_data->stale = TRUE;
714 goto error2;
718 * call is for us, deserialize arguments
720 memset(&call_arg, 0, sizeof (call_arg));
721 if (!svc_getargs(rqst->rq_xprt, __xdr_rpc_gss_init_arg,
722 (caddr_t)&call_arg)) {
723 ret = RPCSEC_GSS_FAILED;
724 client_data->stale = TRUE;
725 goto error2;
728 gssstat = GSS_S_FAILURE;
729 minor_stat = 0;
730 rw_rdlock(&cred_lock);
732 * set next sc to point to the server cred
733 * if the client_data contains server_creds
735 for (sc = svc_creds_list; sc != NULL; sc = sc->next) {
736 if (rqst->rq_prog != sc->program ||
737 rqst->rq_vers != sc->version)
738 continue;
740 mutex_lock(&sc->refresh_mutex);
741 gssstat = gss_accept_sec_context(&minor_stat,
742 &client_data->context,
743 sc->cred,
744 &call_arg,
745 GSS_C_NO_CHANNEL_BINDINGS,
746 &client_data->client_name,
747 &mech_type,
748 &output_token,
749 &ret_flags,
750 &time_rec,
751 NULL);
753 if (gssstat == GSS_S_CREDENTIALS_EXPIRED) {
754 if (rpc_gss_refresh_svc_cred(sc)) {
755 gssstat = gss_accept_sec_context(
756 &minor_stat,
757 &client_data->context,
758 sc->cred,
759 &call_arg,
760 GSS_C_NO_CHANNEL_BINDINGS,
761 &client_data->client_name,
762 &mech_type,
763 &output_token,
764 &ret_flags,
765 &time_rec,
766 NULL);
767 mutex_unlock(&sc->refresh_mutex);
769 } else {
770 mutex_unlock(&sc->refresh_mutex);
771 gssstat = GSS_S_NO_CRED;
772 break;
775 } else
776 mutex_unlock(&sc->refresh_mutex);
778 if (gssstat == GSS_S_COMPLETE) {
780 * Server_creds was right - set it. Also
781 * set the raw and unix credentials at this
782 * point. This saves a lot of computation
783 * later when credentials are retrieved.
786 * XXX server_creds will prob be stale
787 * after rpc_gss_refresh_svc_cred(), but
788 * it appears not to ever be referenced
789 * anyways.
791 mutex_lock(&sc->refresh_mutex);
792 client_data->server_creds = sc->cred;
793 client_data->raw_cred.version = creds.version;
794 client_data->raw_cred.service = creds.service;
795 client_data->raw_cred.svc_principal =
796 sc->server_name;
797 mutex_unlock(&sc->refresh_mutex);
799 if ((client_data->raw_cred.mechanism
800 = __rpc_gss_oid_to_mech(mech_type))
801 == NULL) {
802 gssstat = GSS_S_FAILURE;
803 (void) gss_release_buffer(&minor_stat,
804 &output_token);
805 } else if (!set_client_principal(client_data->
806 client_name, &client_data->
807 raw_cred.client_principal)) {
808 gssstat = GSS_S_FAILURE;
809 (void) gss_release_buffer(&minor_stat,
810 &output_token);
812 break;
815 if (gssstat == GSS_S_CONTINUE_NEEDED) {
817 * XXX server_creds will prob be stale
818 * after rpc_gss_refresh_svc_cred(), but
819 * it appears not to ever be referenced
820 * anyways.
822 mutex_lock(&sc->refresh_mutex);
823 client_data->server_creds = sc->cred;
824 mutex_unlock(&sc->refresh_mutex);
825 break;
829 rw_unlock(&cred_lock);
831 call_res.gss_major = gssstat;
832 call_res.gss_minor = minor_stat;
834 xdr_free(__xdr_rpc_gss_init_arg, (caddr_t)&call_arg);
836 if (gssstat != GSS_S_COMPLETE &&
837 gssstat != GSS_S_CONTINUE_NEEDED) {
839 * We have a failure - send response and delete
840 * the context. Don't dispatch. Set ctx_handle
841 * to NULL and seq_window to 0.
843 call_res.ctx_handle.length = 0;
844 call_res.ctx_handle.value = NULL;
845 call_res.seq_window = 0;
847 svc_sendreply(rqst->rq_xprt, __xdr_rpc_gss_init_res,
848 (caddr_t)&call_res);
849 *no_dispatch = TRUE;
850 ret = AUTH_OK;
851 client_data->stale = TRUE;
852 goto error2;
856 * This step succeeded. Send a response, along with
857 * a token if there's one. Don't dispatch.
859 if (output_token.length != 0) {
860 GSS_COPY_BUFFER(call_res.token, output_token);
864 * set response verifier: checksum of SEQ_WIN
866 if (gssstat == GSS_S_COMPLETE) {
867 if (!set_response_verf(rqst, msg, client_data,
868 (uint_t)SEQ_WIN)) {
869 ret = RPCSEC_GSS_FAILED;
870 client_data->stale = TRUE;
871 (void) gss_release_buffer(&minor_stat,
872 &output_token);
873 goto error2;
877 svc_sendreply(rqst->rq_xprt, __xdr_rpc_gss_init_res,
878 (caddr_t)&call_res);
880 * Cache last response in case it is lost and the client
881 * retries on an established context.
883 (void) retrans_add(client_data, msg->rm_xid, &call_res);
884 *no_dispatch = TRUE;
885 (void) gss_release_buffer(&minor_stat, &output_token);
888 * If appropriate, set established to TRUE *after* sending
889 * response (otherwise, the client will receive the final
890 * token encrypted)
892 if (gssstat == GSS_S_COMPLETE) {
894 * Context is established. Set expiry time for
895 * context (the minimum of time_rec and max_lifetime).
897 client_data->seq_num = 1;
898 if (time_rec == GSS_C_INDEFINITE) {
899 if (max_lifetime != GSS_C_INDEFINITE)
900 client_data->expiration =
901 max_lifetime + time(0);
902 else
903 client_data->expiration =
904 GSS_C_INDEFINITE;
905 } else if (max_lifetime == GSS_C_INDEFINITE ||
906 max_lifetime > time_rec)
907 client_data->expiration = time_rec + time(0);
908 else
909 client_data->expiration = max_lifetime +
910 time(0);
911 client_data->established = TRUE;
914 } else {
915 if ((creds.gss_proc != RPCSEC_GSS_DATA) &&
916 (creds.gss_proc != RPCSEC_GSS_DESTROY)) {
918 switch (creds.gss_proc) {
920 case RPCSEC_GSS_CONTINUE_INIT:
922 * This is an established context. Continue to
923 * satisfy retried continue init requests out of
924 * the retransmit cache. Throw away any that don't
925 * have a matching xid or the cach is empty.
926 * Delete the retransmit cache once the client sends
927 * a data request.
929 if (client_data->retrans_data &&
930 (client_data->retrans_data->xid == msg->rm_xid)) {
932 retrans_result = &client_data->retrans_data->result;
933 if (set_response_verf(rqst, msg, client_data,
934 (uint_t)retrans_result->seq_window)) {
936 gss_parms->established = FALSE;
937 svc_sendreply(rqst->rq_xprt,
938 __xdr_rpc_gss_init_res,
939 (caddr_t)retrans_result);
940 *no_dispatch = TRUE;
941 goto success;
944 /* fall thru to default */
946 default:
947 syslog(LOG_ERR, "_svcrpcsec_gss: non-data request "
948 "on an established context");
949 ret = AUTH_FAILED;
950 goto error2;
955 * Once the context is established and there is no more
956 * retransmission of last continue init request, it is safe
957 * to delete the retransmit cache entry.
959 if (client_data->retrans_data)
960 retrans_del(client_data);
963 * Context is already established. Check verifier, and
964 * note parameters we will need for response in gss_parms.
966 if (!check_verf(msg, client_data->context,
967 &gss_parms->qop_rcvd)) {
968 ret = RPCSEC_GSS_NOCRED;
969 goto error2;
972 * Check and invoke callback if necessary.
974 if (!client_data->done_docallback) {
975 client_data->done_docallback = TRUE;
976 client_data->qop = gss_parms->qop_rcvd;
977 client_data->raw_cred.qop = __rpc_gss_num_to_qop(
978 client_data->raw_cred.mechanism,
979 gss_parms->qop_rcvd);
980 client_data->raw_cred.service = creds.service;
981 if (!do_callback(rqst, client_data)) {
982 ret = AUTH_FAILED;
983 client_data->stale = TRUE;
984 goto error2;
989 * If the context was locked, make sure that the client
990 * has not changed QOP.
992 if (client_data->locked &&
993 gss_parms->qop_rcvd != client_data->qop) {
994 ret = AUTH_BADVERF;
995 goto error2;
999 * Validate sequence number.
1001 if (!check_seq(client_data, creds.seq_num,
1002 &client_data->stale)) {
1003 if (client_data->stale)
1004 ret = RPCSEC_GSS_FAILED;
1005 else {
1007 * Operational error, drop packet silently.
1008 * The client will recover after timing out,
1009 * assuming this is a client error and not
1010 * a relpay attack. Don't dispatch.
1012 ret = AUTH_OK;
1013 *no_dispatch = TRUE;
1015 goto error2;
1019 * set response verifier
1021 if (!set_response_verf(rqst, msg, client_data, creds.seq_num)) {
1022 ret = RPCSEC_GSS_FAILED;
1023 client_data->stale = TRUE;
1024 goto error2;
1028 * If this is a control message RPCSEC_GSS_DESTROY, process
1029 * the call; otherwise, return AUTH_OK so it will be
1030 * dispatched to the application server.
1032 if (creds.gss_proc == RPCSEC_GSS_DESTROY) {
1033 svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
1034 *no_dispatch = TRUE;
1035 client_data->stale = TRUE;
1037 } else {
1039 * This should be an RPCSEC_GSS_DATA request.
1040 * If context is locked, make sure that the client
1041 * has not changed the security service.
1043 if (client_data->locked &&
1044 client_data->raw_cred.service != creds.service) {
1045 ret = AUTH_FAILED;
1046 goto error2;
1050 * Set client credentials to raw credential
1051 * structure in context. This is okay, since
1052 * this will not change during the lifetime of
1053 * the context (so it's MT safe).
1055 rqst->rq_clntcred = (char *)&client_data->raw_cred;
1059 success:
1061 * Success.
1063 if (creds.ctx_handle.length != 0)
1064 xdr_free(__xdr_rpc_gss_creds, (caddr_t)&creds);
1065 mutex_unlock(&client_data->clm);
1066 return (AUTH_OK);
1067 error2:
1068 mutex_unlock(&client_data->clm);
1069 error:
1071 * Failure.
1073 if (creds.ctx_handle.length != 0)
1074 xdr_free(__xdr_rpc_gss_creds, (caddr_t)&creds);
1075 return (ret);
1079 * Check verifier. The verifier is the checksum of the RPC header
1080 * upto and including the credentials field.
1082 static bool_t
1083 check_verf(msg, context, qop_state)
1084 struct rpc_msg *msg;
1085 gss_ctx_id_t context;
1086 int *qop_state;
1088 int *buf, *tmp;
1089 int hdr[32];
1090 struct opaque_auth *oa;
1091 int len;
1092 gss_buffer_desc msg_buf;
1093 gss_buffer_desc tok_buf;
1094 OM_uint32 gssstat, minor_stat;
1097 * We have to reconstruct the RPC header from the previously
1098 * parsed information, since we haven't kept the header intact.
1101 oa = &msg->rm_call.cb_cred;
1102 if (oa->oa_length > MAX_AUTH_BYTES)
1103 return (FALSE);
1105 /* 8 XDR units from the IXDR macro calls. */
1106 if (sizeof (hdr) < (8 * BYTES_PER_XDR_UNIT +
1107 RNDUP(oa->oa_length)))
1108 return (FALSE);
1109 buf = hdr;
1111 IXDR_PUT_U_INT32(buf, msg->rm_xid);
1112 IXDR_PUT_ENUM(buf, msg->rm_direction);
1113 IXDR_PUT_U_INT32(buf, msg->rm_call.cb_rpcvers);
1114 IXDR_PUT_U_INT32(buf, msg->rm_call.cb_prog);
1115 IXDR_PUT_U_INT32(buf, msg->rm_call.cb_vers);
1116 IXDR_PUT_U_INT32(buf, msg->rm_call.cb_proc);
1117 IXDR_PUT_ENUM(buf, oa->oa_flavor);
1118 IXDR_PUT_U_INT32(buf, oa->oa_length);
1119 if (oa->oa_length) {
1120 len = RNDUP(oa->oa_length);
1121 tmp = buf;
1122 buf += len / sizeof (int);
1123 *(buf - 1) = 0;
1124 (void) memcpy((caddr_t)tmp, oa->oa_base, oa->oa_length);
1126 len = ((char *)buf) - (char *)hdr;
1127 msg_buf.length = len;
1128 msg_buf.value = (char *)hdr;
1129 oa = &msg->rm_call.cb_verf;
1130 tok_buf.length = oa->oa_length;
1131 tok_buf.value = oa->oa_base;
1133 gssstat = gss_verify(&minor_stat, context, &msg_buf, &tok_buf,
1134 qop_state);
1135 if (gssstat != GSS_S_COMPLETE)
1136 return (FALSE);
1137 return (TRUE);
1141 * Set response verifier. This is the checksum of the given number.
1142 * (e.g. sequence number or sequence window)
1144 static bool_t
1145 set_response_verf(rqst, msg, cl, num)
1146 struct svc_req *rqst;
1147 struct rpc_msg *msg;
1148 svc_rpc_gss_data *cl;
1149 uint_t num;
1151 OM_uint32 minor;
1152 gss_buffer_desc in_buf, out_buf;
1153 uint_t num_net;
1155 num_net = (uint_t)htonl(num);
1156 in_buf.length = sizeof (num);
1157 in_buf.value = (char *)&num_net;
1158 if (gss_sign(&minor, cl->context, cl->qop, &in_buf,
1159 &out_buf) != GSS_S_COMPLETE)
1160 return (FALSE);
1161 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
1162 rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
1163 rqst->rq_xprt->xp_verf.oa_length = out_buf.length;
1164 memcpy(rqst->rq_xprt->xp_verf.oa_base, out_buf.value,
1165 out_buf.length);
1166 (void) gss_release_buffer(&minor, &out_buf);
1167 return (TRUE);
1171 * Create client context.
1173 static svc_rpc_gss_data *
1174 create_client()
1176 svc_rpc_gss_data *client_data;
1177 static uint_t key = 1;
1179 client_data = (svc_rpc_gss_data *) malloc(sizeof (*client_data));
1180 if (client_data == NULL)
1181 return (NULL);
1182 memset((char *)client_data, 0, sizeof (*client_data));
1185 * set up client data structure
1187 client_data->established = FALSE;
1188 client_data->locked = FALSE;
1189 client_data->u_cred_set = FALSE;
1190 client_data->context = GSS_C_NO_CONTEXT;
1191 client_data->expiration = init_lifetime + time(0);
1192 client_data->ref_cnt = 1;
1193 client_data->qop = GSS_C_QOP_DEFAULT;
1194 client_data->done_docallback = FALSE;
1195 client_data->stale = FALSE;
1196 client_data->time_secs_set = 0;
1197 client_data->retrans_data = NULL;
1198 mutex_init(&client_data->clm, USYNC_THREAD, NULL);
1200 * Check totals. If we've hit the limit, we destroy a context
1201 * based on LRU method.
1203 mutex_lock(&ctx_mutex);
1204 if (num_gss_contexts >= max_gss_contexts) {
1206 * now try on LRU basis
1208 drop_lru_client();
1209 if (num_gss_contexts >= max_gss_contexts) {
1210 mutex_unlock(&ctx_mutex);
1211 free((char *)client_data);
1212 return (NULL);
1217 * The client context handle is a 32-bit key (unsigned int).
1218 * The key is incremented until there is no duplicate for it.
1220 for (;;) {
1221 client_data->key = key++;
1222 if (find_client(client_data->key) == NULL) {
1223 insert_client(client_data);
1225 * Set cleanup callback if we haven't.
1227 if (!cleanup_cb_set) {
1228 old_cleanup_cb =
1229 (void (*)()) __svc_set_proc_cleanup_cb(
1230 (void *)ctx_cleanup);
1231 cleanup_cb_set = TRUE;
1233 mutex_unlock(&ctx_mutex);
1234 return (client_data);
1237 /*NOTREACHED*/
1241 * Insert client context into hash list and LRU list.
1243 static void
1244 insert_client(client_data)
1245 svc_rpc_gss_data *client_data;
1247 svc_rpc_gss_data *cl;
1248 int index = (client_data->key & HASHMASK);
1250 client_data->prev = NULL;
1251 cl = clients[index];
1252 if ((client_data->next = cl) != NULL)
1253 cl->prev = client_data;
1254 clients[index] = client_data;
1256 client_data->lru_prev = NULL;
1257 if ((client_data->lru_next = lru_first) != NULL)
1258 lru_first->lru_prev = client_data;
1259 else
1260 lru_last = client_data;
1261 lru_first = client_data;
1263 num_gss_contexts++;
1267 * Fetch a client, given the client context handle. Move it to the
1268 * top of the LRU list since this is the most recently used context.
1270 static svc_rpc_gss_data *
1271 get_client(ctx_handle)
1272 gss_buffer_t ctx_handle;
1274 uint_t key = *(uint_t *)ctx_handle->value;
1275 svc_rpc_gss_data *cl;
1277 mutex_lock(&ctx_mutex);
1278 if ((cl = find_client(key)) != NULL) {
1279 mutex_lock(&cl->clm);
1280 if (cl->stale) {
1281 mutex_unlock(&cl->clm);
1282 mutex_unlock(&ctx_mutex);
1283 return (NULL);
1285 cl->ref_cnt++;
1286 mutex_unlock(&cl->clm);
1287 if (cl != lru_first) {
1288 cl->lru_prev->lru_next = cl->lru_next;
1289 if (cl->lru_next != NULL)
1290 cl->lru_next->lru_prev = cl->lru_prev;
1291 else
1292 lru_last = cl->lru_prev;
1293 cl->lru_prev = NULL;
1294 cl->lru_next = lru_first;
1295 lru_first->lru_prev = cl;
1296 lru_first = cl;
1299 mutex_unlock(&ctx_mutex);
1300 return (cl);
1304 * Given the client context handle, find the context corresponding to it.
1305 * Don't change its LRU state since it may not be used.
1307 static svc_rpc_gss_data *
1308 find_client(key)
1309 uint_t key;
1311 int index = (key & HASHMASK);
1312 svc_rpc_gss_data *cl;
1314 for (cl = clients[index]; cl != NULL; cl = cl->next) {
1315 if (cl->key == key)
1316 break;
1318 return (cl);
1322 * Destroy a client context.
1324 static void
1325 destroy_client(client_data)
1326 svc_rpc_gss_data *client_data;
1328 OM_uint32 minor;
1329 int index = (client_data->key & HASHMASK);
1332 * remove from hash list
1334 if (client_data->prev == NULL)
1335 clients[index] = client_data->next;
1336 else
1337 client_data->prev->next = client_data->next;
1338 if (client_data->next != NULL)
1339 client_data->next->prev = client_data->prev;
1342 * remove from LRU list
1344 if (client_data->lru_prev == NULL)
1345 lru_first = client_data->lru_next;
1346 else
1347 client_data->lru_prev->lru_next = client_data->lru_next;
1348 if (client_data->lru_next != NULL)
1349 client_data->lru_next->lru_prev = client_data->lru_prev;
1350 else
1351 lru_last = client_data->lru_prev;
1354 * If there is a GSS context, clean up GSS state.
1356 if (client_data->context != GSS_C_NO_CONTEXT) {
1357 (void) gss_delete_sec_context(&minor, &client_data->context,
1358 NULL);
1359 if (client_data->client_name)
1360 (void) gss_release_name(&minor, &client_data->client_name);
1361 if (client_data->raw_cred.client_principal)
1362 free((char *)client_data->raw_cred.client_principal);
1363 if (client_data->u_cred.gidlist != NULL)
1364 free((char *)client_data->u_cred.gidlist);
1365 if (client_data->deleg != GSS_C_NO_CREDENTIAL)
1366 (void) gss_release_cred(&minor, &client_data->deleg);
1369 if (client_data->retrans_data != NULL)
1370 retrans_del(client_data);
1372 free(client_data);
1373 num_gss_contexts--;
1377 * Check for expired client contexts.
1379 static void
1380 sweep_clients()
1382 svc_rpc_gss_data *cl, *next;
1383 int index;
1385 for (index = 0; index < HASHMOD; index++) {
1386 cl = clients[index];
1387 while (cl) {
1388 next = cl->next;
1389 mutex_lock(&cl->clm);
1390 if ((cl->expiration != GSS_C_INDEFINITE &&
1391 cl->expiration <= time(0)) || cl->stale) {
1392 cl->stale = TRUE;
1393 if (cl->ref_cnt == 0) {
1394 mutex_unlock(&cl->clm);
1395 destroy_client(cl);
1396 } else
1397 mutex_unlock(&cl->clm);
1398 } else
1399 mutex_unlock(&cl->clm);
1400 cl = next;
1403 last_swept = time(0);
1407 * Drop the least recently used client context, if possible.
1409 static void
1410 drop_lru_client()
1412 mutex_lock(&lru_last->clm);
1413 lru_last->stale = TRUE;
1414 mutex_unlock(&lru_last->clm);
1415 if (lru_last->ref_cnt == 0)
1416 destroy_client(lru_last);
1417 else
1418 sweep_clients();
1422 * find service credentials
1423 * return cred if found,
1424 * other wise, NULL
1427 svc_creds_list_t *
1428 find_svc_cred(char *service_name, uint_t program, uint_t version) {
1430 svc_creds_list_t *sc;
1432 if (!svc_creds_list)
1433 return (NULL);
1435 for (sc = svc_creds_list; sc != NULL; sc = sc->next) {
1436 if (program != sc->program || version != sc->version)
1437 continue;
1439 if (strcmp(service_name, sc->server_name) != 0)
1440 continue;
1441 return (sc);
1443 return (NULL);
1447 * Set the server principal name.
1449 bool_t
1450 __rpc_gss_set_svc_name(server_name, mech, req_time, program, version)
1451 char *server_name;
1452 char *mech;
1453 OM_uint32 req_time;
1454 uint_t program;
1455 uint_t version;
1457 gss_name_t name;
1458 svc_creds_list_t *svc_cred;
1459 gss_OID mechanism;
1460 gss_OID_set_desc oid_set_desc;
1461 gss_OID_set oid_set;
1462 OM_uint32 ret_time;
1463 OM_uint32 major, minor;
1464 gss_buffer_desc name_buf;
1466 if (!__rpc_gss_mech_to_oid(mech, &mechanism)) {
1467 return (FALSE);
1470 name_buf.value = server_name;
1471 name_buf.length = strlen(server_name);
1472 major = gss_import_name(&minor, &name_buf,
1473 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &name);
1474 if (major != GSS_S_COMPLETE) {
1475 return (FALSE);
1478 /* Check if there is already an entry in the svc_creds_list. */
1479 rw_wrlock(&cred_lock);
1480 if (svc_cred = find_svc_cred(server_name, program, version)) {
1482 major = gss_add_cred(&minor, svc_cred->cred, name,
1483 mechanism, GSS_C_ACCEPT,
1484 0, req_time, NULL,
1485 &oid_set, NULL,
1486 &ret_time);
1487 (void) gss_release_name(&minor, &name);
1488 if (major == GSS_S_COMPLETE) {
1490 * Successfully added the mech to the cred handle
1491 * free the existing oid_set in svc_cred
1493 gss_release_oid_set(&minor, &svc_cred->oid_set);
1494 svc_cred->oid_set = oid_set;
1495 rw_unlock(&cred_lock);
1496 return (TRUE);
1497 } else if (major == GSS_S_DUPLICATE_ELEMENT) {
1498 rw_unlock(&cred_lock);
1499 return (TRUE);
1500 } else if (major == GSS_S_CREDENTIALS_EXPIRED) {
1501 if (rpc_gss_refresh_svc_cred(svc_cred)) {
1502 rw_unlock(&cred_lock);
1503 return (TRUE);
1504 } else {
1505 rw_unlock(&cred_lock);
1506 return (FALSE);
1508 } else {
1509 rw_unlock(&cred_lock);
1510 return (FALSE);
1512 } else {
1513 svc_cred = (svc_creds_list_t *)malloc(sizeof (*svc_cred));
1514 if (svc_cred == NULL) {
1515 (void) gss_release_name(&minor, &name);
1516 rw_unlock(&cred_lock);
1517 return (FALSE);
1519 oid_set_desc.count = 1;
1520 oid_set_desc.elements = mechanism;
1521 major = gss_acquire_cred(&minor, name, req_time,
1522 &oid_set_desc,
1523 GSS_C_ACCEPT,
1524 &svc_cred->cred,
1525 &oid_set, &ret_time);
1527 if (major != GSS_S_COMPLETE) {
1528 (void) gss_release_name(&minor, &name);
1529 free(svc_cred);
1530 rw_unlock(&cred_lock);
1531 return (FALSE);
1534 svc_cred->name = name;
1535 svc_cred->program = program;
1536 svc_cred->version = version;
1537 svc_cred->req_time = req_time;
1538 svc_cred->oid_set = oid_set;
1539 svc_cred->server_name = strdup(server_name);
1540 if (svc_cred->server_name == NULL) {
1541 (void) gss_release_name(&minor, &name);
1542 free((char *)svc_cred);
1543 rw_unlock(&cred_lock);
1544 return (FALSE);
1546 mutex_init(&svc_cred->refresh_mutex, USYNC_THREAD, NULL);
1548 svc_cred->next = svc_creds_list;
1549 svc_creds_list = svc_cred;
1550 svc_creds_count++;
1551 rw_unlock(&cred_lock);
1553 return (TRUE);
1557 * Refresh server credentials.
1559 static bool_t
1560 rpc_gss_refresh_svc_cred(svc_cred)
1561 svc_creds_list_t *svc_cred;
1563 OM_uint32 major, minor;
1564 gss_OID_set oid_set;
1565 OM_uint32 ret_time;
1567 (void) gss_release_cred(&minor, &svc_cred->cred);
1568 svc_cred->cred = GSS_C_NO_CREDENTIAL;
1569 major = gss_acquire_cred(&minor, svc_cred->name, svc_cred->req_time,
1570 svc_cred->oid_set, GSS_C_ACCEPT, &svc_cred->cred, &oid_set,
1571 &ret_time);
1572 if (major != GSS_S_COMPLETE) {
1573 return (FALSE);
1575 gss_release_oid_set(&minor, &svc_cred->oid_set);
1576 svc_cred->oid_set = oid_set;
1577 return (TRUE);
1581 * Encrypt the serialized arguments from xdr_func applied to xdr_ptr
1582 * and write the result to xdrs.
1584 static bool_t
1585 svc_rpc_gss_wrap(auth, out_xdrs, xdr_func, xdr_ptr)
1586 SVCAUTH *auth;
1587 XDR *out_xdrs;
1588 bool_t (*xdr_func)();
1589 caddr_t xdr_ptr;
1591 svc_rpc_gss_parms_t *gss_parms = &auth->svc_gss_parms;
1594 * If context is not established, or if neither integrity nor
1595 * privacy service is used, don't wrap - just XDR encode.
1596 * Otherwise, wrap data using service and QOP parameters.
1598 if (!gss_parms->established ||
1599 gss_parms->service == rpc_gss_svc_none)
1600 return ((*xdr_func)(out_xdrs, xdr_ptr));
1602 return (__rpc_gss_wrap_data(gss_parms->service,
1603 (OM_uint32)gss_parms->qop_rcvd,
1604 (gss_ctx_id_t)gss_parms->context,
1605 gss_parms->seq_num,
1606 out_xdrs, xdr_func, xdr_ptr));
1610 * Decrypt the serialized arguments and XDR decode them.
1612 static bool_t
1613 svc_rpc_gss_unwrap(auth, in_xdrs, xdr_func, xdr_ptr)
1614 SVCAUTH *auth;
1615 XDR *in_xdrs;
1616 bool_t (*xdr_func)();
1617 caddr_t xdr_ptr;
1619 svc_rpc_gss_parms_t *gss_parms = &auth->svc_gss_parms;
1622 * If context is not established, or if neither integrity nor
1623 * privacy service is used, don't unwrap - just XDR decode.
1624 * Otherwise, unwrap data.
1626 if (!gss_parms->established ||
1627 gss_parms->service == rpc_gss_svc_none)
1628 return ((*xdr_func)(in_xdrs, xdr_ptr));
1630 return (__rpc_gss_unwrap_data(gss_parms->service,
1631 (gss_ctx_id_t)gss_parms->context,
1632 gss_parms->seq_num,
1633 gss_parms->qop_rcvd,
1634 in_xdrs, xdr_func, xdr_ptr));
1638 __rpc_gss_svc_max_data_length(req, max_tp_unit_len)
1639 struct svc_req *req;
1640 int max_tp_unit_len;
1642 SVCAUTH *svcauth;
1643 svc_rpc_gss_parms_t *gss_parms;
1645 svcauth = __svc_get_svcauth(req->rq_xprt);
1646 gss_parms = &svcauth->svc_gss_parms;
1648 if (!gss_parms->established || max_tp_unit_len <= 0)
1649 return (0);
1651 return (__find_max_data_length(gss_parms->service,
1652 (gss_ctx_id_t)gss_parms->context,
1653 gss_parms->qop_rcvd, max_tp_unit_len));
1657 * Add retransmit entry to the context cache entry for a new xid.
1658 * If there is already an entry, delete it before adding the new one.
1660 static void retrans_add(client, xid, result)
1661 svc_rpc_gss_data *client;
1662 uint32_t xid;
1663 rpc_gss_init_res *result;
1665 retrans_entry *rdata;
1667 if (client->retrans_data && client->retrans_data->xid == xid)
1668 return;
1670 rdata = (retrans_entry *) malloc(sizeof (*rdata));
1671 if (rdata == NULL)
1672 return;
1674 rdata->xid = xid;
1675 rdata->result = *result;
1677 if (result->token.length != 0) {
1678 GSS_DUP_BUFFER(rdata->result.token, result->token);
1681 if (client->retrans_data)
1682 retrans_del(client);
1684 client->retrans_data = rdata;
1688 * Delete the retransmit data from the context cache entry.
1690 static void retrans_del(client)
1691 svc_rpc_gss_data *client;
1693 retrans_entry *rdata;
1694 OM_uint32 minor_stat;
1696 if (client->retrans_data == NULL)
1697 return;
1699 rdata = client->retrans_data;
1700 if (rdata->result.token.length != 0) {
1701 (void) gss_release_buffer(&minor_stat, &rdata->result.token);
1704 free((caddr_t)rdata);
1705 client->retrans_data = NULL;