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]
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.
44 #include <gssapi/gssapi.h>
45 #include <gssapi/gssapi_ext.h>
47 #include <rpc/rpcsec_defs.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
63 #define SEQ_MAX 0x80000000
66 /* cache retransmit data */
67 typedef struct _retrans_entry
{
69 rpc_gss_init_res result
;
70 struct _retrans_entry
*next
, *prev
;
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
;
81 gss_name_t client_name
;
82 gss_cred_id_t server_creds
;
85 uint_t seq_bits
[SEQ_ARR_SIZE
];
88 bool_t done_docallback
;
90 rpc_gss_rawcred_t raw_cred
;
91 rpc_gss_ucred_t u_cred
;
99 retrans_entry
*retrans_data
;
103 * Data structures used for LRU based context management.
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
;
135 mutex_t refresh_mutex
;
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
;
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,
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
= {
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
;
212 svc_rpc_gss_data
*cl
;
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
);
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
);
229 mutex_unlock(&ctx_mutex
);
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
)
244 mutex_unlock(&ctx_mutex
);
249 * Set server parameters.
252 __rpc_gss_set_server_parms(init_cred_lifetime
, max_cred_lifetime
, cache_size
)
253 int init_cred_lifetime
;
254 int max_cred_lifetime
;
258 * Ignore parameters unless greater than zero.
260 mutex_lock(&ctx_mutex
);
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.
274 shift_bits(arr
, arrlen
, nbits
)
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
++) {
289 for (j
= 0; j
< arrlen
; j
++) {
290 lo
= arr
[j
] & SEQ_LO_BIT
;
293 arr
[j
] |= SEQ_HI_BIT
;
298 for (j
= 0; j
< arrlen
; j
++)
304 * Check that the received sequence number seq_num is valid.
307 check_seq(cl
, seq_num
, kill_context
)
308 svc_rpc_gss_data
*cl
;
310 bool_t
*kill_context
;
316 * If it exceeds the maximum, kill context.
318 if (seq_num
>= SEQ_MAX
) {
319 *kill_context
= TRUE
;
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
;
336 * If it is outside the sequence window, return failure.
338 i
= cl
->seq_num
- seq_num
;
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;
349 if (cl
->seq_bits
[i
] & bit
)
351 cl
->seq_bits
[i
] |= bit
;
356 * Convert a name in gss exported type to rpc_gss_principal_t type.
359 __rpc_gss_make_principal(principal
, name
)
360 rpc_gss_principal_t
*principal
;
361 gss_buffer_desc
*name
;
366 plen
= RNDUP(name
->length
) + sizeof (int);
367 (*principal
) = (rpc_gss_principal_t
)malloc(plen
);
368 if ((*principal
) == NULL
)
370 bzero((caddr_t
)(*principal
), plen
);
371 (*principal
)->len
= RNDUP(name
->length
);
372 s
= (*principal
)->name
;
373 memcpy(s
, name
->value
, name
->length
);
378 * Convert a name in internal form to the exported type.
381 set_client_principal(g_name
, r_name
)
383 rpc_gss_principal_t
*r_name
;
385 gss_buffer_desc name
;
386 OM_uint32 major
, minor
;
389 major
= gss_export_name(&minor
, g_name
, &name
);
390 if (major
!= GSS_S_COMPLETE
)
392 ret
= __rpc_gss_make_principal(r_name
, &name
);
393 (void) gss_release_buffer(&minor
, &name
);
398 * Set server callback.
401 __rpc_gss_set_callback(cb
)
402 rpc_gss_callback_t
*cb
;
406 if (cb
->callback
== NULL
)
408 if ((cbl
= (cblist_t
*)malloc(sizeof (*cbl
))) == NULL
)
411 mutex_lock(&cb_mutex
);
414 mutex_unlock(&cb_mutex
);
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.
425 do_callback(req
, client_data
)
427 svc_rpc_gss_data
*client_data
;
430 bool_t ret
= TRUE
, found
= FALSE
;
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
)
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
);
445 client_data
->locked
= lock
.locked
;
446 client_data
->deleg
= GSS_C_NO_CREDENTIAL
;
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
);
461 * Return caller credentials.
464 __rpc_gss_getcred(req
, rcred
, ucred
, cookie
)
466 rpc_gss_rawcred_t
**rcred
;
467 rpc_gss_ucred_t
**ucred
;
471 svc_rpc_gss_data
*client_data
;
472 svc_rpc_gss_parms_t
*gss_parms
;
478 svcauth
= __svc_get_svcauth(req
->rq_xprt
);
480 client_data
= (svc_rpc_gss_data
*)svcauth
->svc_ah_private
;
481 gss_parms
= &svcauth
->svc_gss_parms
;
483 mutex_lock(&client_data
->clm
);
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
;
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"));
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
,
511 if (status
== GSS_S_COMPLETE
) {
512 client_data
->u_cred_set
= TRUE
;
513 client_data
->u_cred
.gidlen
=
517 client_data
->time_secs_set
=
519 *ucred
= &client_data
->u_cred
;
527 * check if they have expired.
529 gettimeofday(&now
, NULL
);
530 if ((now
.tv_sec
- client_data
->time_secs_set
)
533 status
= gss_get_group_info(
534 client_data
->u_cred
.uid
,
535 &client_data
->u_cred
.gid
,
536 &client_data
->u_cred
.gidlist
,
538 if (status
== GSS_S_COMPLETE
) {
539 client_data
->u_cred
.gidlen
=
543 client_data
->time_secs_set
= now
.tv_sec
;
544 *ucred
= &client_data
->u_cred
;
546 client_data
->u_cred_set
= FALSE
;
551 *ucred
= &client_data
->u_cred
;
555 *cookie
= client_data
->cookie
;
557 mutex_unlock(&client_data
->clm
);
563 * Server side authentication for RPCSEC_GSS.
567 __svcrpcsec_gss(rqst
, msg
, no_dispatch
)
568 struct svc_req
*rqst
;
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
;
581 svc_creds_list_t
*sc
;
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
)) {
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) {
626 if ((client_data
= create_client()) == NULL
) {
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
:
651 if (creds
.ctx_handle
.length
== 0) {
655 if ((client_data
= get_client(&creds
.ctx_handle
)) == NULL
) {
656 ret
= RPCSEC_GSS_NOCRED
;
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
;
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
;
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
;
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
;
728 gssstat
= GSS_S_FAILURE
;
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
)
740 mutex_lock(&sc
->refresh_mutex
);
741 gssstat
= gss_accept_sec_context(&minor_stat
,
742 &client_data
->context
,
745 GSS_C_NO_CHANNEL_BINDINGS
,
746 &client_data
->client_name
,
753 if (gssstat
== GSS_S_CREDENTIALS_EXPIRED
) {
754 if (rpc_gss_refresh_svc_cred(sc
)) {
755 gssstat
= gss_accept_sec_context(
757 &client_data
->context
,
760 GSS_C_NO_CHANNEL_BINDINGS
,
761 &client_data
->client_name
,
767 mutex_unlock(&sc
->refresh_mutex
);
770 mutex_unlock(&sc
->refresh_mutex
);
771 gssstat
= GSS_S_NO_CRED
;
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
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
=
797 mutex_unlock(&sc
->refresh_mutex
);
799 if ((client_data
->raw_cred
.mechanism
800 = __rpc_gss_oid_to_mech(mech_type
))
802 gssstat
= GSS_S_FAILURE
;
803 (void) gss_release_buffer(&minor_stat
,
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
,
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
822 mutex_lock(&sc
->refresh_mutex
);
823 client_data
->server_creds
= sc
->cred
;
824 mutex_unlock(&sc
->refresh_mutex
);
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
,
851 client_data
->stale
= TRUE
;
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
,
869 ret
= RPCSEC_GSS_FAILED
;
870 client_data
->stale
= TRUE
;
871 (void) gss_release_buffer(&minor_stat
,
877 svc_sendreply(rqst
->rq_xprt
, __xdr_rpc_gss_init_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
);
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
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);
903 client_data
->expiration
=
905 } else if (max_lifetime
== GSS_C_INDEFINITE
||
906 max_lifetime
> time_rec
)
907 client_data
->expiration
= time_rec
+ time(0);
909 client_data
->expiration
= max_lifetime
+
911 client_data
->established
= TRUE
;
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
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
);
944 /* fall thru to default */
947 syslog(LOG_ERR
, "_svcrpcsec_gss: non-data request "
948 "on an established context");
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
;
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
)) {
983 client_data
->stale
= TRUE
;
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
) {
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
;
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.
1013 *no_dispatch
= TRUE
;
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
;
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
;
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
) {
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
;
1063 if (creds
.ctx_handle
.length
!= 0)
1064 xdr_free(__xdr_rpc_gss_creds
, (caddr_t
)&creds
);
1065 mutex_unlock(&client_data
->clm
);
1068 mutex_unlock(&client_data
->clm
);
1073 if (creds
.ctx_handle
.length
!= 0)
1074 xdr_free(__xdr_rpc_gss_creds
, (caddr_t
)&creds
);
1079 * Check verifier. The verifier is the checksum of the RPC header
1080 * upto and including the credentials field.
1083 check_verf(msg
, context
, qop_state
)
1084 struct rpc_msg
*msg
;
1085 gss_ctx_id_t context
;
1090 struct opaque_auth
*oa
;
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
)
1105 /* 8 XDR units from the IXDR macro calls. */
1106 if (sizeof (hdr
) < (8 * BYTES_PER_XDR_UNIT
+
1107 RNDUP(oa
->oa_length
)))
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
);
1122 buf
+= len
/ sizeof (int);
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
,
1135 if (gssstat
!= GSS_S_COMPLETE
)
1141 * Set response verifier. This is the checksum of the given number.
1142 * (e.g. sequence number or sequence window)
1145 set_response_verf(rqst
, msg
, cl
, num
)
1146 struct svc_req
*rqst
;
1147 struct rpc_msg
*msg
;
1148 svc_rpc_gss_data
*cl
;
1152 gss_buffer_desc in_buf
, out_buf
;
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
)
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
,
1166 (void) gss_release_buffer(&minor
, &out_buf
);
1171 * Create client context.
1173 static svc_rpc_gss_data
*
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
)
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
1209 if (num_gss_contexts
>= max_gss_contexts
) {
1210 mutex_unlock(&ctx_mutex
);
1211 free((char *)client_data
);
1217 * The client context handle is a 32-bit key (unsigned int).
1218 * The key is incremented until there is no duplicate for it.
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
) {
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
);
1241 * Insert client context into hash list and LRU list.
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
;
1260 lru_last
= client_data
;
1261 lru_first
= client_data
;
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
);
1281 mutex_unlock(&cl
->clm
);
1282 mutex_unlock(&ctx_mutex
);
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
;
1292 lru_last
= cl
->lru_prev
;
1293 cl
->lru_prev
= NULL
;
1294 cl
->lru_next
= lru_first
;
1295 lru_first
->lru_prev
= cl
;
1299 mutex_unlock(&ctx_mutex
);
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
*
1311 int index
= (key
& HASHMASK
);
1312 svc_rpc_gss_data
*cl
;
1314 for (cl
= clients
[index
]; cl
!= NULL
; cl
= cl
->next
) {
1322 * Destroy a client context.
1325 destroy_client(client_data
)
1326 svc_rpc_gss_data
*client_data
;
1329 int index
= (client_data
->key
& HASHMASK
);
1332 * remove from hash list
1334 if (client_data
->prev
== NULL
)
1335 clients
[index
] = client_data
->next
;
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
;
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
;
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
,
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
);
1377 * Check for expired client contexts.
1382 svc_rpc_gss_data
*cl
, *next
;
1385 for (index
= 0; index
< HASHMOD
; index
++) {
1386 cl
= clients
[index
];
1389 mutex_lock(&cl
->clm
);
1390 if ((cl
->expiration
!= GSS_C_INDEFINITE
&&
1391 cl
->expiration
<= time(0)) || cl
->stale
) {
1393 if (cl
->ref_cnt
== 0) {
1394 mutex_unlock(&cl
->clm
);
1397 mutex_unlock(&cl
->clm
);
1399 mutex_unlock(&cl
->clm
);
1403 last_swept
= time(0);
1407 * Drop the least recently used client context, if possible.
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
);
1422 * find service credentials
1423 * return cred if found,
1428 find_svc_cred(char *service_name
, uint_t program
, uint_t version
) {
1430 svc_creds_list_t
*sc
;
1432 if (!svc_creds_list
)
1435 for (sc
= svc_creds_list
; sc
!= NULL
; sc
= sc
->next
) {
1436 if (program
!= sc
->program
|| version
!= sc
->version
)
1439 if (strcmp(service_name
, sc
->server_name
) != 0)
1447 * Set the server principal name.
1450 __rpc_gss_set_svc_name(server_name
, mech
, req_time
, program
, version
)
1458 svc_creds_list_t
*svc_cred
;
1460 gss_OID_set_desc oid_set_desc
;
1461 gss_OID_set oid_set
;
1463 OM_uint32 major
, minor
;
1464 gss_buffer_desc name_buf
;
1466 if (!__rpc_gss_mech_to_oid(mech
, &mechanism
)) {
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
) {
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
,
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
);
1497 } else if (major
== GSS_S_DUPLICATE_ELEMENT
) {
1498 rw_unlock(&cred_lock
);
1500 } else if (major
== GSS_S_CREDENTIALS_EXPIRED
) {
1501 if (rpc_gss_refresh_svc_cred(svc_cred
)) {
1502 rw_unlock(&cred_lock
);
1505 rw_unlock(&cred_lock
);
1509 rw_unlock(&cred_lock
);
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
);
1519 oid_set_desc
.count
= 1;
1520 oid_set_desc
.elements
= mechanism
;
1521 major
= gss_acquire_cred(&minor
, name
, req_time
,
1525 &oid_set
, &ret_time
);
1527 if (major
!= GSS_S_COMPLETE
) {
1528 (void) gss_release_name(&minor
, &name
);
1530 rw_unlock(&cred_lock
);
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
);
1546 mutex_init(&svc_cred
->refresh_mutex
, USYNC_THREAD
, NULL
);
1548 svc_cred
->next
= svc_creds_list
;
1549 svc_creds_list
= svc_cred
;
1551 rw_unlock(&cred_lock
);
1557 * Refresh server credentials.
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
;
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
,
1572 if (major
!= GSS_S_COMPLETE
) {
1575 gss_release_oid_set(&minor
, &svc_cred
->oid_set
);
1576 svc_cred
->oid_set
= oid_set
;
1581 * Encrypt the serialized arguments from xdr_func applied to xdr_ptr
1582 * and write the result to xdrs.
1585 svc_rpc_gss_wrap(auth
, out_xdrs
, xdr_func
, xdr_ptr
)
1588 bool_t (*xdr_func
)();
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
,
1606 out_xdrs
, xdr_func
, xdr_ptr
));
1610 * Decrypt the serialized arguments and XDR decode them.
1613 svc_rpc_gss_unwrap(auth
, in_xdrs
, xdr_func
, xdr_ptr
)
1616 bool_t (*xdr_func
)();
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
,
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
;
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)
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
;
1663 rpc_gss_init_res
*result
;
1665 retrans_entry
*rdata
;
1667 if (client
->retrans_data
&& client
->retrans_data
->xid
== xid
)
1670 rdata
= (retrans_entry
*) malloc(sizeof (*rdata
));
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
)
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
;