9162 libpp: this statement may fall through
[unleashed.git] / usr / src / lib / rpcsec_gss / svc_rpcsec_gss.c
blobffc29240261b1c4e22bac3fe7f957f5a367c6434
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.
28 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
30 * $Id: svc_auth_gssapi.c,v 1.19 1994/10/27 12:38:51 jik Exp $
34 * Server side handling of RPCSEC_GSS flavor.
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <strings.h>
40 #include <sys/stat.h>
41 #include <sys/time.h>
42 #include <gssapi/gssapi.h>
43 #include <gssapi/gssapi_ext.h>
44 #include <rpc/rpc.h>
45 #include <rpc/rpcsec_defs.h>
46 #include <sys/file.h>
47 #include <fcntl.h>
48 #include <pwd.h>
49 #include <stdio.h>
50 #include <syslog.h>
53 * Sequence window definitions.
55 #define SEQ_ARR_SIZE 4
56 #define SEQ_WIN (SEQ_ARR_SIZE*32)
57 #define SEQ_HI_BIT 0x80000000
58 #define SEQ_LO_BIT 1
59 #define DIV_BY_32 5
60 #define SEQ_MASK 0x1f
61 #define SEQ_MAX 0x80000000
64 /* cache retransmit data */
65 typedef struct _retrans_entry {
66 uint32_t xid;
67 rpc_gss_init_res result;
68 struct _retrans_entry *next, *prev;
69 } retrans_entry;
72 * Server side RPCSEC_GSS context information.
74 typedef struct _svc_rpc_gss_data {
75 struct _svc_rpc_gss_data *next, *prev;
76 struct _svc_rpc_gss_data *lru_next, *lru_prev;
77 bool_t established;
78 gss_ctx_id_t context;
79 gss_name_t client_name;
80 gss_cred_id_t server_creds;
81 uint_t expiration;
82 uint_t seq_num;
83 uint_t seq_bits[SEQ_ARR_SIZE];
84 uint_t key;
85 OM_uint32 qop;
86 bool_t done_docallback;
87 bool_t locked;
88 rpc_gss_rawcred_t raw_cred;
89 rpc_gss_ucred_t u_cred;
90 bool_t u_cred_set;
91 void *cookie;
92 gss_cred_id_t deleg;
93 mutex_t clm;
94 int ref_cnt;
95 bool_t stale;
96 time_t time_secs_set;
97 retrans_entry *retrans_data;
98 } svc_rpc_gss_data;
101 * Data structures used for LRU based context management.
103 #define HASHMOD 256
104 #define HASHMASK 255
106 static svc_rpc_gss_data *clients[HASHMOD];
107 static svc_rpc_gss_data *lru_first, *lru_last;
108 static int num_gss_contexts = 0;
109 static int max_gss_contexts = 128;
110 static int sweep_interval = 10;
111 static int last_swept = 0;
112 static uint_t max_lifetime = GSS_C_INDEFINITE;
113 static int init_lifetime = 0;
114 static uint_t gid_timeout = 43200; /* 43200 secs = 12 hours */
117 * lock used with context/lru variables
119 static mutex_t ctx_mutex = DEFAULTMUTEX;
122 * server credential management data and structures
124 typedef struct svc_creds_list_s {
125 struct svc_creds_list_s *next;
126 gss_cred_id_t cred;
127 gss_name_t name;
128 rpcprog_t program;
129 rpcvers_t version;
130 gss_OID_set oid_set;
131 OM_uint32 req_time;
132 char *server_name;
133 mutex_t refresh_mutex;
134 } svc_creds_list_t;
137 static svc_creds_list_t *svc_creds_list;
138 static int svc_creds_count = 0;
141 * lock used with server credential variables list
143 * server cred list locking guidelines:
144 * - Writer's lock holder has exclusive access to the list
145 * - Reader's lock holder(s) must also lock (refresh_mutex) each node
146 * before accessing that node's elements (ie. cred)
148 static rwlock_t cred_lock = DEFAULTRWLOCK;
151 * server callback list
153 typedef struct cblist_s {
154 struct cblist_s *next;
155 rpc_gss_callback_t cb;
156 } cblist_t;
158 cblist_t *cblist = NULL;
161 * lock used with callback variables
163 static mutex_t cb_mutex = DEFAULTMUTEX;
166 * forward declarations
168 static bool_t svc_rpc_gss_wrap();
169 static bool_t svc_rpc_gss_unwrap();
170 static svc_rpc_gss_data *create_client();
171 static svc_rpc_gss_data *get_client();
172 static svc_rpc_gss_data *find_client();
173 static void destroy_client();
174 static void sweep_clients();
175 static void drop_lru_client();
176 static void insert_client();
177 static bool_t check_verf();
178 static bool_t rpc_gss_refresh_svc_cred();
179 static bool_t set_response_verf();
180 static void retrans_add(svc_rpc_gss_data *, uint32_t,
181 rpc_gss_init_res *);
182 static void retrans_del(struct _svc_rpc_gss_data *);
186 * server side wrap/unwrap routines
188 struct svc_auth_ops svc_rpc_gss_ops = {
189 svc_rpc_gss_wrap,
190 svc_rpc_gss_unwrap,
194 * Fetch server side authentication structure.
196 extern SVCAUTH *__svc_get_svcauth();
199 * Cleanup routine for destroying context, called after service
200 * procedure is executed, for MT safeness.
202 extern void *__svc_set_proc_cleanup_cb();
203 static void (*old_cleanup_cb)() = NULL;
204 static bool_t cleanup_cb_set = FALSE;
206 static void
207 ctx_cleanup(SVCXPRT *xprt)
209 svc_rpc_gss_data *cl;
210 SVCAUTH *svcauth;
212 if (old_cleanup_cb != NULL)
213 (*old_cleanup_cb)(xprt);
216 * First check if current context needs to be cleaned up.
218 svcauth = __svc_get_svcauth(xprt);
219 /*LINTED*/
220 if ((cl = (svc_rpc_gss_data *)svcauth->svc_ah_private) != NULL) {
221 mutex_lock(&cl->clm);
222 if (--cl->ref_cnt == 0 && cl->stale) {
223 mutex_unlock(&cl->clm);
224 mutex_lock(&ctx_mutex);
225 destroy_client(cl);
226 mutex_unlock(&ctx_mutex);
227 } else
228 mutex_unlock(&cl->clm);
232 * Check for other expired contexts.
234 if ((time(0) - last_swept) > sweep_interval) {
235 mutex_lock(&ctx_mutex);
237 * Check again, in case some other thread got in.
239 if ((time(0) - last_swept) > sweep_interval)
240 sweep_clients();
241 mutex_unlock(&ctx_mutex);
246 * Set server parameters.
248 void
249 __rpc_gss_set_server_parms(int init_cred_lifetime, int max_cred_lifetime,
250 int cache_size)
253 * Ignore parameters unless greater than zero.
255 mutex_lock(&ctx_mutex);
256 if (cache_size > 0)
257 max_gss_contexts = cache_size;
258 if (max_cred_lifetime > 0)
259 max_lifetime = (uint_t)max_cred_lifetime;
260 if (init_cred_lifetime > 0)
261 init_lifetime = init_cred_lifetime;
262 mutex_unlock(&ctx_mutex);
266 * Shift the array arr of length arrlen right by nbits bits.
268 static void
269 shift_bits(uint_t *arr, int arrlen, int nbits)
271 int i, j;
272 uint_t lo, hi;
275 * If the number of bits to be shifted exceeds SEQ_WIN, just
276 * zero out the array.
278 if (nbits < SEQ_WIN) {
279 for (i = 0; i < nbits; i++) {
280 hi = 0;
281 for (j = 0; j < arrlen; j++) {
282 lo = arr[j] & SEQ_LO_BIT;
283 arr[j] >>= 1;
284 if (hi)
285 arr[j] |= SEQ_HI_BIT;
286 hi = lo;
289 } else {
290 for (j = 0; j < arrlen; j++)
291 arr[j] = 0;
296 * Check that the received sequence number seq_num is valid.
298 static bool_t
299 check_seq(svc_rpc_gss_data *cl, uint_t seq_num, bool_t *kill_context)
301 int i, j;
302 uint_t bit;
305 * If it exceeds the maximum, kill context.
307 if (seq_num >= SEQ_MAX) {
308 *kill_context = TRUE;
309 return (FALSE);
313 * If greater than the last seen sequence number, just shift
314 * the sequence window so that it starts at the new sequence
315 * number and extends downwards by SEQ_WIN.
317 if (seq_num > cl->seq_num) {
318 shift_bits(cl->seq_bits, SEQ_ARR_SIZE, seq_num - cl->seq_num);
319 cl->seq_bits[0] |= SEQ_HI_BIT;
320 cl->seq_num = seq_num;
321 return (TRUE);
325 * If it is outside the sequence window, return failure.
327 i = cl->seq_num - seq_num;
328 if (i >= SEQ_WIN)
329 return (FALSE);
332 * If within sequence window, set the bit corresponding to it
333 * if not already seen; if already seen, return failure.
335 j = SEQ_MASK - (i & SEQ_MASK);
336 bit = j > 0 ? (1 << j) : 1;
337 i >>= DIV_BY_32;
338 if (cl->seq_bits[i] & bit)
339 return (FALSE);
340 cl->seq_bits[i] |= bit;
341 return (TRUE);
345 * Convert a name in gss exported type to rpc_gss_principal_t type.
347 static bool_t
348 __rpc_gss_make_principal(rpc_gss_principal_t *principal, gss_buffer_desc *name)
350 int plen;
351 char *s;
353 plen = RNDUP(name->length) + sizeof (int);
354 (*principal) = (rpc_gss_principal_t)malloc(plen);
355 if ((*principal) == NULL)
356 return (FALSE);
357 bzero((caddr_t)(*principal), plen);
358 (*principal)->len = RNDUP(name->length);
359 s = (*principal)->name;
360 memcpy(s, name->value, name->length);
361 return (TRUE);
365 * Convert a name in internal form to the exported type.
367 static bool_t
368 set_client_principal(gss_name_t g_name, rpc_gss_principal_t *r_name)
370 gss_buffer_desc name;
371 OM_uint32 major, minor;
372 bool_t ret = FALSE;
374 major = gss_export_name(&minor, g_name, &name);
375 if (major != GSS_S_COMPLETE)
376 return (FALSE);
377 ret = __rpc_gss_make_principal(r_name, &name);
378 (void) gss_release_buffer(&minor, &name);
379 return (ret);
383 * Set server callback.
385 bool_t
386 __rpc_gss_set_callback(rpc_gss_callback_t *cb)
388 cblist_t *cbl;
390 if (cb->callback == NULL)
391 return (FALSE);
392 if ((cbl = (cblist_t *)malloc(sizeof (*cbl))) == NULL)
393 return (FALSE);
394 cbl->cb = *cb;
395 mutex_lock(&cb_mutex);
396 cbl->next = cblist;
397 cblist = cbl;
398 mutex_unlock(&cb_mutex);
399 return (TRUE);
403 * Locate callback (if specified) and call server. Release any
404 * delegated credentials unless passed to server and the server
405 * accepts the context. If a callback is not specified, accept
406 * the incoming context.
408 static bool_t
409 do_callback(struct svc_req *req, svc_rpc_gss_data *client_data)
411 cblist_t *cbl;
412 bool_t ret = TRUE, found = FALSE;
413 rpc_gss_lock_t lock;
414 OM_uint32 minor;
416 mutex_lock(&cb_mutex);
417 for (cbl = cblist; cbl != NULL; cbl = cbl->next) {
418 if (req->rq_prog != cbl->cb.program ||
419 req->rq_vers != cbl->cb.version)
420 continue;
421 found = TRUE;
422 lock.locked = FALSE;
423 lock.raw_cred = &client_data->raw_cred;
424 ret = (*cbl->cb.callback)(req, client_data->deleg,
425 client_data->context, &lock, &client_data->cookie);
426 if (ret) {
427 client_data->locked = lock.locked;
428 client_data->deleg = GSS_C_NO_CREDENTIAL;
430 break;
432 if (!found) {
433 if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
434 (void) gss_release_cred(&minor, &client_data->deleg);
435 client_data->deleg = GSS_C_NO_CREDENTIAL;
438 mutex_unlock(&cb_mutex);
439 return (ret);
443 * Return caller credentials.
445 bool_t
446 __rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
447 rpc_gss_ucred_t **ucred, void **cookie)
449 SVCAUTH *svcauth;
450 svc_rpc_gss_data *client_data;
451 svc_rpc_gss_parms_t *gss_parms;
452 gss_OID oid;
453 OM_uint32 status;
454 int len = 0;
455 struct timeval now;
457 svcauth = __svc_get_svcauth(req->rq_xprt);
458 /*LINTED*/
459 client_data = (svc_rpc_gss_data *)svcauth->svc_ah_private;
460 gss_parms = &svcauth->svc_gss_parms;
462 mutex_lock(&client_data->clm);
464 if (rcred != NULL) {
465 svcauth->raw_cred = client_data->raw_cred;
466 svcauth->raw_cred.service = gss_parms->service;
467 svcauth->raw_cred.qop = __rpc_gss_num_to_qop(
468 svcauth->raw_cred.mechanism, gss_parms->qop_rcvd);
469 *rcred = &svcauth->raw_cred;
471 if (ucred != NULL) {
472 if (!client_data->u_cred_set) {
474 * Double check making sure ucred is not set
475 * after acquiring the lock.
477 if (!client_data->u_cred_set) {
478 if (!__rpc_gss_mech_to_oid(
479 (*rcred)->mechanism, &oid)) {
480 fprintf(stderr, dgettext(TEXT_DOMAIN,
481 "mech_to_oid failed in "
482 "getcred.\n"));
483 *ucred = NULL;
484 } else {
485 status = gsscred_name_to_unix_cred(
486 client_data->client_name, oid,
487 &client_data->u_cred.uid,
488 &client_data->u_cred.gid,
489 &client_data->u_cred.gidlist,
490 &len);
491 if (status == GSS_S_COMPLETE) {
492 client_data->u_cred_set = TRUE;
493 client_data->u_cred.gidlen =
494 (short)len;
495 gettimeofday(&now, NULL);
496 client_data->time_secs_set =
497 now.tv_sec;
498 *ucred = &client_data->u_cred;
499 } else
500 *ucred = NULL;
503 } else {
505 * gid's already set;
506 * check if they have expired.
508 gettimeofday(&now, NULL);
509 if ((now.tv_sec - client_data->time_secs_set)
510 > gid_timeout) {
511 /* Refresh gid's */
512 status = gss_get_group_info(
513 client_data->u_cred.uid,
514 &client_data->u_cred.gid,
515 &client_data->u_cred.gidlist,
516 &len);
517 if (status == GSS_S_COMPLETE) {
518 client_data->u_cred.gidlen =
519 (short)len;
520 gettimeofday(&now, NULL);
521 client_data->time_secs_set = now.tv_sec;
522 *ucred = &client_data->u_cred;
523 } else {
524 client_data->u_cred_set = FALSE;
525 *ucred = NULL;
528 else
529 *ucred = &client_data->u_cred;
532 if (cookie != NULL)
533 *cookie = client_data->cookie;
535 mutex_unlock(&client_data->clm);
537 return (TRUE);
541 * Server side authentication for RPCSEC_GSS.
544 enum auth_stat
545 __svcrpcsec_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t *no_dispatch)
547 XDR xdrs;
548 rpc_gss_creds creds;
549 rpc_gss_init_arg call_arg;
550 rpc_gss_init_res call_res, *retrans_result;
551 gss_buffer_desc output_token;
552 OM_uint32 gssstat, minor_stat, time_rec, ret_flags;
553 struct opaque_auth *cred;
554 svc_rpc_gss_data *client_data;
555 int ret;
556 svc_creds_list_t *sc;
557 SVCAUTH *svcauth;
558 svc_rpc_gss_parms_t *gss_parms;
559 gss_OID mech_type = GSS_C_NULL_OID;
562 * Initialize response verifier to NULL verifier. If
563 * necessary, this will be changed later.
565 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NONE;
566 rqst->rq_xprt->xp_verf.oa_base = NULL;
567 rqst->rq_xprt->xp_verf.oa_length = 0;
569 * Need to null out results to start with.
571 memset((char *)&call_res, 0, sizeof (call_res));
574 * Pull out and check credential and verifier.
576 cred = &msg->rm_call.cb_cred;
577 if (cred->oa_length == 0) {
578 return (AUTH_BADCRED);
581 xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE);
583 memset((char *)&creds, 0, sizeof (creds));
584 if (!__xdr_rpc_gss_creds(&xdrs, &creds)) {
585 XDR_DESTROY(&xdrs);
586 ret = AUTH_BADCRED;
587 goto error;
589 XDR_DESTROY(&xdrs);
592 * If this is a control message and proc is GSSAPI_INIT, then
593 * create a client handle for this client. Otherwise, look up
594 * the existing handle.
596 if (creds.gss_proc == RPCSEC_GSS_INIT) {
597 if (creds.ctx_handle.length != 0) {
598 ret = AUTH_BADCRED;
599 goto error;
601 if ((client_data = create_client()) == NULL) {
602 ret = AUTH_FAILED;
603 goto error;
605 } else {
607 * Only verify values for service parameter when proc
608 * not RPCSEC_GSS_INIT or RPCSEC_GSS_CONTINUE_INIT.
609 * RFC2203 says contents for sequence and service args
610 * are undefined for creation procs.
612 * Note: only need to check for *CONTINUE_INIT here because
613 * if() clause already checked for RPCSEC_GSS_INIT
615 if (creds.gss_proc != RPCSEC_GSS_CONTINUE_INIT) {
616 switch (creds.service) {
617 case rpc_gss_svc_none:
618 case rpc_gss_svc_integrity:
619 case rpc_gss_svc_privacy:
620 break;
621 default:
622 ret = AUTH_BADCRED;
623 goto error;
626 if (creds.ctx_handle.length == 0) {
627 ret = AUTH_BADCRED;
628 goto error;
630 if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
631 ret = RPCSEC_GSS_NOCRED;
632 goto error;
637 * lock the client data until it's safe; if it's already stale,
638 * no more processing is possible
640 mutex_lock(&client_data->clm);
641 if (client_data->stale) {
642 ret = RPCSEC_GSS_NOCRED;
643 goto error2;
647 * Any response we send will use ctx_handle, so set it now;
648 * also set seq_window since this won't change.
650 call_res.ctx_handle.length = sizeof (client_data->key);
651 call_res.ctx_handle.value = (char *)&client_data->key;
652 call_res.seq_window = SEQ_WIN;
655 * Set the appropriate wrap/unwrap routine for RPCSEC_GSS.
657 svcauth = __svc_get_svcauth(rqst->rq_xprt);
658 svcauth->svc_ah_ops = svc_rpc_gss_ops;
659 svcauth->svc_ah_private = (caddr_t)client_data;
662 * Keep copy of parameters we'll need for response, for the
663 * sake of reentrancy (we don't want to look in the context
664 * data because when we are sending a response, another
665 * request may have come in.
667 gss_parms = &svcauth->svc_gss_parms;
668 gss_parms->established = client_data->established;
669 gss_parms->service = creds.service;
670 gss_parms->qop_rcvd = (uint_t)client_data->qop;
671 gss_parms->context = (void *)client_data->context;
672 gss_parms->seq_num = creds.seq_num;
674 if (!client_data->established) {
675 if (creds.gss_proc == RPCSEC_GSS_DATA) {
676 ret = RPCSEC_GSS_FAILED;
677 client_data->stale = TRUE;
678 goto error2;
682 * If the context is not established, then only GSSAPI_INIT
683 * and _CONTINUE requests are valid.
685 if (creds.gss_proc != RPCSEC_GSS_INIT && creds.gss_proc !=
686 RPCSEC_GSS_CONTINUE_INIT) {
687 ret = RPCSEC_GSS_FAILED;
688 client_data->stale = TRUE;
689 goto error2;
693 * call is for us, deserialize arguments
695 memset(&call_arg, 0, sizeof (call_arg));
696 if (!svc_getargs(rqst->rq_xprt, __xdr_rpc_gss_init_arg,
697 (caddr_t)&call_arg)) {
698 ret = RPCSEC_GSS_FAILED;
699 client_data->stale = TRUE;
700 goto error2;
703 gssstat = GSS_S_FAILURE;
704 minor_stat = 0;
705 rw_rdlock(&cred_lock);
707 * set next sc to point to the server cred
708 * if the client_data contains server_creds
710 for (sc = svc_creds_list; sc != NULL; sc = sc->next) {
711 if (rqst->rq_prog != sc->program ||
712 rqst->rq_vers != sc->version)
713 continue;
715 mutex_lock(&sc->refresh_mutex);
716 gssstat = gss_accept_sec_context(&minor_stat,
717 &client_data->context,
718 sc->cred,
719 &call_arg,
720 GSS_C_NO_CHANNEL_BINDINGS,
721 &client_data->client_name,
722 &mech_type,
723 &output_token,
724 &ret_flags,
725 &time_rec,
726 NULL);
728 if (gssstat == GSS_S_CREDENTIALS_EXPIRED) {
729 if (rpc_gss_refresh_svc_cred(sc)) {
730 gssstat = gss_accept_sec_context(
731 &minor_stat,
732 &client_data->context,
733 sc->cred,
734 &call_arg,
735 GSS_C_NO_CHANNEL_BINDINGS,
736 &client_data->client_name,
737 &mech_type,
738 &output_token,
739 &ret_flags,
740 &time_rec,
741 NULL);
742 mutex_unlock(&sc->refresh_mutex);
744 } else {
745 mutex_unlock(&sc->refresh_mutex);
746 gssstat = GSS_S_NO_CRED;
747 break;
750 } else
751 mutex_unlock(&sc->refresh_mutex);
753 if (gssstat == GSS_S_COMPLETE) {
755 * Server_creds was right - set it. Also
756 * set the raw and unix credentials at this
757 * point. This saves a lot of computation
758 * later when credentials are retrieved.
761 * XXX server_creds will prob be stale
762 * after rpc_gss_refresh_svc_cred(), but
763 * it appears not to ever be referenced
764 * anyways.
766 mutex_lock(&sc->refresh_mutex);
767 client_data->server_creds = sc->cred;
768 client_data->raw_cred.version = creds.version;
769 client_data->raw_cred.service = creds.service;
770 client_data->raw_cred.svc_principal =
771 sc->server_name;
772 mutex_unlock(&sc->refresh_mutex);
774 if ((client_data->raw_cred.mechanism
775 = __rpc_gss_oid_to_mech(mech_type))
776 == NULL) {
777 gssstat = GSS_S_FAILURE;
778 (void) gss_release_buffer(&minor_stat,
779 &output_token);
780 } else if (!set_client_principal(client_data->
781 client_name, &client_data->
782 raw_cred.client_principal)) {
783 gssstat = GSS_S_FAILURE;
784 (void) gss_release_buffer(&minor_stat,
785 &output_token);
787 break;
790 if (gssstat == GSS_S_CONTINUE_NEEDED) {
792 * XXX server_creds will prob be stale
793 * after rpc_gss_refresh_svc_cred(), but
794 * it appears not to ever be referenced
795 * anyways.
797 mutex_lock(&sc->refresh_mutex);
798 client_data->server_creds = sc->cred;
799 mutex_unlock(&sc->refresh_mutex);
800 break;
804 rw_unlock(&cred_lock);
806 call_res.gss_major = gssstat;
807 call_res.gss_minor = minor_stat;
809 xdr_free(__xdr_rpc_gss_init_arg, (caddr_t)&call_arg);
811 if (gssstat != GSS_S_COMPLETE &&
812 gssstat != GSS_S_CONTINUE_NEEDED) {
814 * We have a failure - send response and delete
815 * the context. Don't dispatch. Set ctx_handle
816 * to NULL and seq_window to 0.
818 call_res.ctx_handle.length = 0;
819 call_res.ctx_handle.value = NULL;
820 call_res.seq_window = 0;
822 svc_sendreply(rqst->rq_xprt, __xdr_rpc_gss_init_res,
823 (caddr_t)&call_res);
824 *no_dispatch = TRUE;
825 ret = AUTH_OK;
826 client_data->stale = TRUE;
827 goto error2;
831 * This step succeeded. Send a response, along with
832 * a token if there's one. Don't dispatch.
834 if (output_token.length != 0) {
835 GSS_COPY_BUFFER(call_res.token, output_token);
839 * set response verifier: checksum of SEQ_WIN
841 if (gssstat == GSS_S_COMPLETE) {
842 if (!set_response_verf(rqst, msg, client_data,
843 (uint_t)SEQ_WIN)) {
844 ret = RPCSEC_GSS_FAILED;
845 client_data->stale = TRUE;
846 (void) gss_release_buffer(&minor_stat,
847 &output_token);
848 goto error2;
852 svc_sendreply(rqst->rq_xprt, __xdr_rpc_gss_init_res,
853 (caddr_t)&call_res);
855 * Cache last response in case it is lost and the client
856 * retries on an established context.
858 (void) retrans_add(client_data, msg->rm_xid, &call_res);
859 *no_dispatch = TRUE;
860 (void) gss_release_buffer(&minor_stat, &output_token);
863 * If appropriate, set established to TRUE *after* sending
864 * response (otherwise, the client will receive the final
865 * token encrypted)
867 if (gssstat == GSS_S_COMPLETE) {
869 * Context is established. Set expiry time for
870 * context (the minimum of time_rec and max_lifetime).
872 client_data->seq_num = 1;
873 if (time_rec == GSS_C_INDEFINITE) {
874 if (max_lifetime != GSS_C_INDEFINITE) {
875 client_data->expiration =
876 max_lifetime + time(0);
877 } else {
878 client_data->expiration =
879 GSS_C_INDEFINITE;
881 } else if (max_lifetime == GSS_C_INDEFINITE ||
882 max_lifetime > time_rec) {
883 client_data->expiration = time_rec + time(0);
884 } else {
885 client_data->expiration = max_lifetime +
886 time(0);
888 client_data->established = TRUE;
891 } else {
892 if ((creds.gss_proc != RPCSEC_GSS_DATA) &&
893 (creds.gss_proc != RPCSEC_GSS_DESTROY)) {
895 switch (creds.gss_proc) {
897 case RPCSEC_GSS_CONTINUE_INIT:
899 * This is an established context. Continue to
900 * satisfy retried continue init requests out of
901 * the retransmit cache. Throw away any that
902 * don't have a matching xid or the cach is
903 * empty. Delete the retransmit cache once the
904 * client sends a data request.
906 if (client_data->retrans_data &&
907 (client_data->retrans_data->xid ==
908 msg->rm_xid)) {
910 retrans_result =
911 &client_data->retrans_data->result;
912 if (set_response_verf(rqst, msg,
913 client_data, (uint_t)
914 retrans_result->seq_window)) {
916 gss_parms->established = FALSE;
917 svc_sendreply(rqst->rq_xprt,
918 __xdr_rpc_gss_init_res,
919 (caddr_t)retrans_result);
920 *no_dispatch = TRUE;
921 goto success;
924 /* FALLTHROUGH */
926 default:
927 syslog(LOG_ERR, "_svcrpcsec_gss: non-data "
928 "request on an established context");
929 ret = AUTH_FAILED;
930 goto error2;
935 * Once the context is established and there is no more
936 * retransmission of last continue init request, it is safe
937 * to delete the retransmit cache entry.
939 if (client_data->retrans_data)
940 retrans_del(client_data);
943 * Context is already established. Check verifier, and
944 * note parameters we will need for response in gss_parms.
946 if (!check_verf(msg, client_data->context,
947 &gss_parms->qop_rcvd)) {
948 ret = RPCSEC_GSS_NOCRED;
949 goto error2;
952 * Check and invoke callback if necessary.
954 if (!client_data->done_docallback) {
955 client_data->done_docallback = TRUE;
956 client_data->qop = gss_parms->qop_rcvd;
957 client_data->raw_cred.qop = __rpc_gss_num_to_qop(
958 client_data->raw_cred.mechanism,
959 gss_parms->qop_rcvd);
960 client_data->raw_cred.service = creds.service;
961 if (!do_callback(rqst, client_data)) {
962 ret = AUTH_FAILED;
963 client_data->stale = TRUE;
964 goto error2;
969 * If the context was locked, make sure that the client
970 * has not changed QOP.
972 if (client_data->locked &&
973 gss_parms->qop_rcvd != client_data->qop) {
974 ret = AUTH_BADVERF;
975 goto error2;
979 * Validate sequence number.
981 if (!check_seq(client_data, creds.seq_num,
982 &client_data->stale)) {
983 if (client_data->stale)
984 ret = RPCSEC_GSS_FAILED;
985 else {
987 * Operational error, drop packet silently.
988 * The client will recover after timing out,
989 * assuming this is a client error and not
990 * a relpay attack. Don't dispatch.
992 ret = AUTH_OK;
993 *no_dispatch = TRUE;
995 goto error2;
999 * set response verifier
1001 if (!set_response_verf(rqst, msg, client_data, creds.seq_num)) {
1002 ret = RPCSEC_GSS_FAILED;
1003 client_data->stale = TRUE;
1004 goto error2;
1008 * If this is a control message RPCSEC_GSS_DESTROY, process
1009 * the call; otherwise, return AUTH_OK so it will be
1010 * dispatched to the application server.
1012 if (creds.gss_proc == RPCSEC_GSS_DESTROY) {
1013 svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
1014 *no_dispatch = TRUE;
1015 client_data->stale = TRUE;
1017 } else {
1019 * This should be an RPCSEC_GSS_DATA request.
1020 * If context is locked, make sure that the client
1021 * has not changed the security service.
1023 if (client_data->locked &&
1024 client_data->raw_cred.service != creds.service) {
1025 ret = AUTH_FAILED;
1026 goto error2;
1030 * Set client credentials to raw credential
1031 * structure in context. This is okay, since
1032 * this will not change during the lifetime of
1033 * the context (so it's MT safe).
1035 rqst->rq_clntcred = (char *)&client_data->raw_cred;
1039 success:
1041 * Success.
1043 if (creds.ctx_handle.length != 0)
1044 xdr_free(__xdr_rpc_gss_creds, (caddr_t)&creds);
1045 mutex_unlock(&client_data->clm);
1046 return (AUTH_OK);
1047 error2:
1048 mutex_unlock(&client_data->clm);
1049 error:
1051 * Failure.
1053 if (creds.ctx_handle.length != 0)
1054 xdr_free(__xdr_rpc_gss_creds, (caddr_t)&creds);
1055 return (ret);
1059 * Check verifier. The verifier is the checksum of the RPC header
1060 * upto and including the credentials field.
1062 static bool_t
1063 check_verf(struct rpc_msg *msg, gss_ctx_id_t context, int *qop_state)
1065 int *buf, *tmp;
1066 int hdr[32];
1067 struct opaque_auth *oa;
1068 int len;
1069 gss_buffer_desc msg_buf;
1070 gss_buffer_desc tok_buf;
1071 OM_uint32 gssstat, minor_stat;
1074 * We have to reconstruct the RPC header from the previously
1075 * parsed information, since we haven't kept the header intact.
1078 oa = &msg->rm_call.cb_cred;
1079 if (oa->oa_length > MAX_AUTH_BYTES)
1080 return (FALSE);
1082 /* 8 XDR units from the IXDR macro calls. */
1083 if (sizeof (hdr) < (8 * BYTES_PER_XDR_UNIT + RNDUP(oa->oa_length)))
1084 return (FALSE);
1085 buf = hdr;
1087 IXDR_PUT_U_INT32(buf, msg->rm_xid);
1088 IXDR_PUT_ENUM(buf, msg->rm_direction);
1089 IXDR_PUT_U_INT32(buf, msg->rm_call.cb_rpcvers);
1090 IXDR_PUT_U_INT32(buf, msg->rm_call.cb_prog);
1091 IXDR_PUT_U_INT32(buf, msg->rm_call.cb_vers);
1092 IXDR_PUT_U_INT32(buf, msg->rm_call.cb_proc);
1093 IXDR_PUT_ENUM(buf, oa->oa_flavor);
1094 IXDR_PUT_U_INT32(buf, oa->oa_length);
1095 if (oa->oa_length) {
1096 len = RNDUP(oa->oa_length);
1097 tmp = buf;
1098 buf += len / sizeof (int);
1099 *(buf - 1) = 0;
1100 (void) memcpy((caddr_t)tmp, oa->oa_base, oa->oa_length);
1102 len = ((char *)buf) - (char *)hdr;
1103 msg_buf.length = len;
1104 msg_buf.value = (char *)hdr;
1105 oa = &msg->rm_call.cb_verf;
1106 tok_buf.length = oa->oa_length;
1107 tok_buf.value = oa->oa_base;
1109 gssstat = gss_verify(&minor_stat, context, &msg_buf, &tok_buf,
1110 qop_state);
1111 if (gssstat != GSS_S_COMPLETE)
1112 return (FALSE);
1113 return (TRUE);
1117 * Set response verifier. This is the checksum of the given number.
1118 * (e.g. sequence number or sequence window)
1120 static bool_t
1121 set_response_verf(struct svc_req *rqst, struct rpc_msg *msg,
1122 svc_rpc_gss_data *cl, uint_t num)
1124 OM_uint32 minor;
1125 gss_buffer_desc in_buf, out_buf;
1126 uint_t num_net;
1128 num_net = (uint_t)htonl(num);
1129 in_buf.length = sizeof (num);
1130 in_buf.value = (char *)&num_net;
1131 if (gss_sign(&minor, cl->context, cl->qop, &in_buf,
1132 &out_buf) != GSS_S_COMPLETE)
1133 return (FALSE);
1134 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
1135 rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
1136 rqst->rq_xprt->xp_verf.oa_length = out_buf.length;
1137 memcpy(rqst->rq_xprt->xp_verf.oa_base, out_buf.value,
1138 out_buf.length);
1139 (void) gss_release_buffer(&minor, &out_buf);
1140 return (TRUE);
1144 * Create client context.
1146 static svc_rpc_gss_data *
1147 create_client(void)
1149 svc_rpc_gss_data *client_data;
1150 static uint_t key = 1;
1152 client_data = (svc_rpc_gss_data *) malloc(sizeof (*client_data));
1153 if (client_data == NULL)
1154 return (NULL);
1155 memset((char *)client_data, 0, sizeof (*client_data));
1158 * set up client data structure
1160 client_data->established = FALSE;
1161 client_data->locked = FALSE;
1162 client_data->u_cred_set = FALSE;
1163 client_data->context = GSS_C_NO_CONTEXT;
1164 client_data->expiration = init_lifetime + time(0);
1165 client_data->ref_cnt = 1;
1166 client_data->qop = GSS_C_QOP_DEFAULT;
1167 client_data->done_docallback = FALSE;
1168 client_data->stale = FALSE;
1169 client_data->time_secs_set = 0;
1170 client_data->retrans_data = NULL;
1171 mutex_init(&client_data->clm, USYNC_THREAD, NULL);
1173 * Check totals. If we've hit the limit, we destroy a context
1174 * based on LRU method.
1176 mutex_lock(&ctx_mutex);
1177 if (num_gss_contexts >= max_gss_contexts) {
1179 * now try on LRU basis
1181 drop_lru_client();
1182 if (num_gss_contexts >= max_gss_contexts) {
1183 mutex_unlock(&ctx_mutex);
1184 free((char *)client_data);
1185 return (NULL);
1190 * The client context handle is a 32-bit key (unsigned int).
1191 * The key is incremented until there is no duplicate for it.
1193 for (;;) {
1194 client_data->key = key++;
1195 if (find_client(client_data->key) == NULL) {
1196 insert_client(client_data);
1198 * Set cleanup callback if we haven't.
1200 if (!cleanup_cb_set) {
1201 old_cleanup_cb =
1202 (void (*)()) __svc_set_proc_cleanup_cb(
1203 (void *)ctx_cleanup);
1204 cleanup_cb_set = TRUE;
1206 mutex_unlock(&ctx_mutex);
1207 return (client_data);
1210 /*NOTREACHED*/
1214 * Insert client context into hash list and LRU list.
1216 static void
1217 insert_client(svc_rpc_gss_data *client_data)
1219 svc_rpc_gss_data *cl;
1220 int index = (client_data->key & HASHMASK);
1222 client_data->prev = NULL;
1223 cl = clients[index];
1224 if ((client_data->next = cl) != NULL)
1225 cl->prev = client_data;
1226 clients[index] = client_data;
1228 client_data->lru_prev = NULL;
1229 if ((client_data->lru_next = lru_first) != NULL)
1230 lru_first->lru_prev = client_data;
1231 else
1232 lru_last = client_data;
1233 lru_first = client_data;
1235 num_gss_contexts++;
1239 * Fetch a client, given the client context handle. Move it to the
1240 * top of the LRU list since this is the most recently used context.
1242 static svc_rpc_gss_data *
1243 get_client(gss_buffer_t ctx_handle)
1245 uint_t key = *(uint_t *)ctx_handle->value;
1246 svc_rpc_gss_data *cl;
1248 mutex_lock(&ctx_mutex);
1249 if ((cl = find_client(key)) != NULL) {
1250 mutex_lock(&cl->clm);
1251 if (cl->stale) {
1252 mutex_unlock(&cl->clm);
1253 mutex_unlock(&ctx_mutex);
1254 return (NULL);
1256 cl->ref_cnt++;
1257 mutex_unlock(&cl->clm);
1258 if (cl != lru_first) {
1259 cl->lru_prev->lru_next = cl->lru_next;
1260 if (cl->lru_next != NULL)
1261 cl->lru_next->lru_prev = cl->lru_prev;
1262 else
1263 lru_last = cl->lru_prev;
1264 cl->lru_prev = NULL;
1265 cl->lru_next = lru_first;
1266 lru_first->lru_prev = cl;
1267 lru_first = cl;
1270 mutex_unlock(&ctx_mutex);
1271 return (cl);
1275 * Given the client context handle, find the context corresponding to it.
1276 * Don't change its LRU state since it may not be used.
1278 static svc_rpc_gss_data *
1279 find_client(uint_t key)
1281 int index = (key & HASHMASK);
1282 svc_rpc_gss_data *cl;
1284 for (cl = clients[index]; cl != NULL; cl = cl->next) {
1285 if (cl->key == key)
1286 break;
1288 return (cl);
1292 * Destroy a client context.
1294 static void
1295 destroy_client(svc_rpc_gss_data *client_data)
1297 OM_uint32 minor;
1298 int index = (client_data->key & HASHMASK);
1301 * remove from hash list
1303 if (client_data->prev == NULL)
1304 clients[index] = client_data->next;
1305 else
1306 client_data->prev->next = client_data->next;
1307 if (client_data->next != NULL)
1308 client_data->next->prev = client_data->prev;
1311 * remove from LRU list
1313 if (client_data->lru_prev == NULL)
1314 lru_first = client_data->lru_next;
1315 else
1316 client_data->lru_prev->lru_next = client_data->lru_next;
1317 if (client_data->lru_next != NULL)
1318 client_data->lru_next->lru_prev = client_data->lru_prev;
1319 else
1320 lru_last = client_data->lru_prev;
1323 * If there is a GSS context, clean up GSS state.
1325 if (client_data->context != GSS_C_NO_CONTEXT) {
1326 (void) gss_delete_sec_context(&minor, &client_data->context,
1327 NULL);
1328 if (client_data->client_name) {
1329 (void) gss_release_name(&minor,
1330 &client_data->client_name);
1332 free(client_data->raw_cred.client_principal);
1333 free(client_data->u_cred.gidlist);
1334 if (client_data->deleg != GSS_C_NO_CREDENTIAL)
1335 (void) gss_release_cred(&minor, &client_data->deleg);
1338 if (client_data->retrans_data != NULL)
1339 retrans_del(client_data);
1341 free(client_data);
1342 num_gss_contexts--;
1346 * Check for expired client contexts.
1348 static void
1349 sweep_clients(void)
1351 svc_rpc_gss_data *cl, *next;
1352 int index;
1354 for (index = 0; index < HASHMOD; index++) {
1355 cl = clients[index];
1356 while (cl) {
1357 next = cl->next;
1358 mutex_lock(&cl->clm);
1359 if ((cl->expiration != GSS_C_INDEFINITE &&
1360 cl->expiration <= time(0)) || cl->stale) {
1361 cl->stale = TRUE;
1362 if (cl->ref_cnt == 0) {
1363 mutex_unlock(&cl->clm);
1364 destroy_client(cl);
1365 } else
1366 mutex_unlock(&cl->clm);
1367 } else
1368 mutex_unlock(&cl->clm);
1369 cl = next;
1372 last_swept = time(0);
1376 * Drop the least recently used client context, if possible.
1378 static void
1379 drop_lru_client(void)
1381 mutex_lock(&lru_last->clm);
1382 lru_last->stale = TRUE;
1383 mutex_unlock(&lru_last->clm);
1384 if (lru_last->ref_cnt == 0)
1385 destroy_client(lru_last);
1386 else
1387 sweep_clients();
1391 * find service credentials
1392 * return cred if found,
1393 * other wise, NULL
1395 svc_creds_list_t *
1396 find_svc_cred(char *service_name, uint_t program, uint_t version)
1399 svc_creds_list_t *sc;
1401 if (!svc_creds_list)
1402 return (NULL);
1404 for (sc = svc_creds_list; sc != NULL; sc = sc->next) {
1405 if (program != sc->program || version != sc->version)
1406 continue;
1408 if (strcmp(service_name, sc->server_name) != 0)
1409 continue;
1410 return (sc);
1412 return (NULL);
1416 * Set the server principal name.
1418 bool_t
1419 __rpc_gss_set_svc_name(char *server_name, char *mech, OM_uint32 req_time,
1420 uint_t program, uint_t version)
1422 gss_name_t name;
1423 svc_creds_list_t *svc_cred;
1424 gss_OID mechanism;
1425 gss_OID_set_desc oid_set_desc;
1426 gss_OID_set oid_set;
1427 OM_uint32 ret_time;
1428 OM_uint32 major, minor;
1429 gss_buffer_desc name_buf;
1431 if (!__rpc_gss_mech_to_oid(mech, &mechanism)) {
1432 return (FALSE);
1435 name_buf.value = server_name;
1436 name_buf.length = strlen(server_name);
1437 major = gss_import_name(&minor, &name_buf,
1438 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &name);
1439 if (major != GSS_S_COMPLETE) {
1440 return (FALSE);
1443 /* Check if there is already an entry in the svc_creds_list. */
1444 rw_wrlock(&cred_lock);
1445 if (svc_cred = find_svc_cred(server_name, program, version)) {
1447 major = gss_add_cred(&minor, svc_cred->cred, name,
1448 mechanism, GSS_C_ACCEPT, 0, req_time, NULL,
1449 &oid_set, NULL, &ret_time);
1450 (void) gss_release_name(&minor, &name);
1451 if (major == GSS_S_COMPLETE) {
1453 * Successfully added the mech to the cred handle
1454 * free the existing oid_set in svc_cred
1456 gss_release_oid_set(&minor, &svc_cred->oid_set);
1457 svc_cred->oid_set = oid_set;
1458 rw_unlock(&cred_lock);
1459 return (TRUE);
1460 } else if (major == GSS_S_DUPLICATE_ELEMENT) {
1461 rw_unlock(&cred_lock);
1462 return (TRUE);
1463 } else if (major == GSS_S_CREDENTIALS_EXPIRED) {
1464 if (rpc_gss_refresh_svc_cred(svc_cred)) {
1465 rw_unlock(&cred_lock);
1466 return (TRUE);
1467 } else {
1468 rw_unlock(&cred_lock);
1469 return (FALSE);
1471 } else {
1472 rw_unlock(&cred_lock);
1473 return (FALSE);
1475 } else {
1476 svc_cred = (svc_creds_list_t *)malloc(sizeof (*svc_cred));
1477 if (svc_cred == NULL) {
1478 (void) gss_release_name(&minor, &name);
1479 rw_unlock(&cred_lock);
1480 return (FALSE);
1482 oid_set_desc.count = 1;
1483 oid_set_desc.elements = mechanism;
1484 major = gss_acquire_cred(&minor, name, req_time, &oid_set_desc,
1485 GSS_C_ACCEPT, &svc_cred->cred, &oid_set, &ret_time);
1487 if (major != GSS_S_COMPLETE) {
1488 (void) gss_release_name(&minor, &name);
1489 free(svc_cred);
1490 rw_unlock(&cred_lock);
1491 return (FALSE);
1494 svc_cred->name = name;
1495 svc_cred->program = program;
1496 svc_cred->version = version;
1497 svc_cred->req_time = req_time;
1498 svc_cred->oid_set = oid_set;
1499 svc_cred->server_name = strdup(server_name);
1500 if (svc_cred->server_name == NULL) {
1501 (void) gss_release_name(&minor, &name);
1502 free((char *)svc_cred);
1503 rw_unlock(&cred_lock);
1504 return (FALSE);
1506 mutex_init(&svc_cred->refresh_mutex, USYNC_THREAD, NULL);
1508 svc_cred->next = svc_creds_list;
1509 svc_creds_list = svc_cred;
1510 svc_creds_count++;
1511 rw_unlock(&cred_lock);
1513 return (TRUE);
1517 * Refresh server credentials.
1519 static bool_t
1520 rpc_gss_refresh_svc_cred(svc_creds_list_t *svc_cred)
1522 OM_uint32 major, minor;
1523 gss_OID_set oid_set;
1524 OM_uint32 ret_time;
1526 (void) gss_release_cred(&minor, &svc_cred->cred);
1527 svc_cred->cred = GSS_C_NO_CREDENTIAL;
1528 major = gss_acquire_cred(&minor, svc_cred->name, svc_cred->req_time,
1529 svc_cred->oid_set, GSS_C_ACCEPT, &svc_cred->cred, &oid_set,
1530 &ret_time);
1531 if (major != GSS_S_COMPLETE) {
1532 return (FALSE);
1534 gss_release_oid_set(&minor, &svc_cred->oid_set);
1535 svc_cred->oid_set = oid_set;
1536 return (TRUE);
1540 * Encrypt the serialized arguments from xdr_func applied to xdr_ptr
1541 * and write the result to xdrs.
1543 static bool_t
1544 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *out_xdrs, bool_t (*xdr_func)(),
1545 caddr_t xdr_ptr)
1547 svc_rpc_gss_parms_t *gss_parms = &auth->svc_gss_parms;
1550 * If context is not established, or if neither integrity nor
1551 * privacy service is used, don't wrap - just XDR encode.
1552 * Otherwise, wrap data using service and QOP parameters.
1554 if (!gss_parms->established || gss_parms->service == rpc_gss_svc_none)
1555 return ((*xdr_func)(out_xdrs, xdr_ptr));
1557 return (__rpc_gss_wrap_data(gss_parms->service,
1558 (OM_uint32)gss_parms->qop_rcvd,
1559 (gss_ctx_id_t)gss_parms->context,
1560 gss_parms->seq_num,
1561 out_xdrs, xdr_func, xdr_ptr));
1565 * Decrypt the serialized arguments and XDR decode them.
1567 static bool_t
1568 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *in_xdrs, bool_t (*xdr_func)(),
1569 caddr_t xdr_ptr)
1571 svc_rpc_gss_parms_t *gss_parms = &auth->svc_gss_parms;
1574 * If context is not established, or if neither integrity nor
1575 * privacy service is used, don't unwrap - just XDR decode.
1576 * Otherwise, unwrap data.
1578 if (!gss_parms->established || gss_parms->service == rpc_gss_svc_none)
1579 return ((*xdr_func)(in_xdrs, xdr_ptr));
1581 return (__rpc_gss_unwrap_data(gss_parms->service,
1582 (gss_ctx_id_t)gss_parms->context,
1583 gss_parms->seq_num,
1584 gss_parms->qop_rcvd,
1585 in_xdrs, xdr_func, xdr_ptr));
1589 __rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
1591 SVCAUTH *svcauth;
1592 svc_rpc_gss_parms_t *gss_parms;
1594 svcauth = __svc_get_svcauth(req->rq_xprt);
1595 gss_parms = &svcauth->svc_gss_parms;
1597 if (!gss_parms->established || max_tp_unit_len <= 0)
1598 return (0);
1600 return (__find_max_data_length(gss_parms->service,
1601 (gss_ctx_id_t)gss_parms->context,
1602 gss_parms->qop_rcvd, max_tp_unit_len));
1606 * Add retransmit entry to the context cache entry for a new xid.
1607 * If there is already an entry, delete it before adding the new one.
1609 static void retrans_add(svc_rpc_gss_data *client, uint32_t xid,
1610 rpc_gss_init_res *result)
1612 retrans_entry *rdata;
1614 if (client->retrans_data && client->retrans_data->xid == xid)
1615 return;
1617 rdata = (retrans_entry *) malloc(sizeof (*rdata));
1618 if (rdata == NULL)
1619 return;
1621 rdata->xid = xid;
1622 rdata->result = *result;
1624 if (result->token.length != 0) {
1625 GSS_DUP_BUFFER(rdata->result.token, result->token);
1628 if (client->retrans_data)
1629 retrans_del(client);
1631 client->retrans_data = rdata;
1635 * Delete the retransmit data from the context cache entry.
1637 static void retrans_del(svc_rpc_gss_data *client)
1639 retrans_entry *rdata;
1640 OM_uint32 minor_stat;
1642 if (client->retrans_data == NULL)
1643 return;
1645 rdata = client->retrans_data;
1646 if (rdata->result.token.length != 0) {
1647 (void) gss_release_buffer(&minor_stat, &rdata->result.token);
1650 free((caddr_t)rdata);
1651 client->retrans_data = NULL;