Home | History | Annotate | Download | only in sec_gss
      1 /*
      2  * CDDL HEADER START
      3  *
      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.
      7  *
      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.
     12  *
     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]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
     28  *
     29  * $Header:
     30  * /afs/gza.com/product/secure/rel-eng/src/1.1/rpc/RCS/auth_gssapi.c,v
     31  * 1.14 1995/03/22 22:07:55 jik Exp $
     32  */
     33 
     34 #include  <sys/systm.h>
     35 #include  <sys/types.h>
     36 #include  <gssapi/gssapi.h>
     37 #include  <rpc/rpc.h>
     38 #include  <rpc/rpcsec_defs.h>
     39 #include  <sys/debug.h>
     40 #include  <sys/cmn_err.h>
     41 #include  <sys/ddi.h>
     42 
     43 static	void	rpc_gss_nextverf();
     44 static	bool_t	rpc_gss_marshall();
     45 static	bool_t	rpc_gss_validate();
     46 static	bool_t	rpc_gss_refresh();
     47 static	void	rpc_gss_destroy();
     48 #if 0
     49 static	void	rpc_gss_destroy_pvt();
     50 #endif
     51 static	void	rpc_gss_free_pvt();
     52 static	int	rpc_gss_seccreate_pvt();
     53 static  bool_t	rpc_gss_wrap();
     54 static  bool_t	rpc_gss_unwrap();
     55 static	bool_t	validate_seqwin();
     56 
     57 
     58 #ifdef	DEBUG
     59 #include <sys/promif.h>
     60 #endif
     61 
     62 static struct auth_ops rpc_gss_ops = {
     63 	rpc_gss_nextverf,
     64 	rpc_gss_marshall,
     65 	rpc_gss_validate,
     66 	rpc_gss_refresh,
     67 	rpc_gss_destroy,
     68 	rpc_gss_wrap,
     69 	rpc_gss_unwrap,
     70 };
     71 
     72 /*
     73  * Private data for RPCSEC_GSS.
     74  */
     75 typedef struct _rpc_gss_data {
     76 	bool_t		established;	/* TRUE when established */
     77 	CLIENT		*clnt;		/* associated client handle */
     78 	int		version;	/* RPCSEC version */
     79 	gss_ctx_id_t	context;	/* GSS context id */
     80 	gss_buffer_desc	ctx_handle;	/* RPCSEC GSS context handle */
     81 	uint_t		seq_num;	/* last sequence number rcvd */
     82 	gss_cred_id_t	my_cred;	/* caller's GSS credentials */
     83 	OM_uint32	qop;		/* requested QOP */
     84 	rpc_gss_service_t	service;	/* requested service */
     85 	uint_t		gss_proc;	/* GSS control procedure */
     86 	gss_name_t	target_name;	/* target server */
     87 	int		req_flags;	/* GSS request bits */
     88 	gss_OID		mech_type;	/* GSS mechanism */
     89 	OM_uint32	time_req;	/* requested cred lifetime */
     90 	bool_t		invalid;	/* can't use this any more */
     91 	OM_uint32	seq_window;	/* server sequence window */
     92 	struct opaque_auth *verifier;	/* rpc reply verifier saved for */
     93 					/* validating the sequence window */
     94 	gss_channel_bindings_t	icb;
     95 } rpc_gss_data;
     96 #define	AUTH_PRIVATE(auth)	((rpc_gss_data *)auth->ah_private)
     97 
     98 #define	INTERRUPT_OK	1	/* allow interrupt */
     99 
    100 /*
    101  *  RPCSEC_GSS auth cache definitions.
    102  */
    103 
    104 /* The table size must be a power of two. */
    105 #define	GSSAUTH_TABLESIZE 16
    106 #define	HASH(keynum, uid_num) \
    107 	((((intptr_t)(keynum)) ^ ((int)uid_num)) & (GSSAUTH_TABLESIZE - 1))
    108 
    109 /*
    110  * gss auth cache entry.
    111  */
    112 typedef struct ga_cache_entry {
    113 	void	*cache_key;
    114 	uid_t	uid;
    115 	zoneid_t zoneid;
    116 	bool_t	in_use;
    117 	time_t	ref_time; /* the time referenced previously */
    118 	time_t	ctx_expired_time; /* when the context will be expired */
    119 	AUTH	*auth;
    120 	struct ga_cache_entry *next;
    121 } *ga_cache_list;
    122 
    123 struct ga_cache_entry	*ga_cache_table[GSSAUTH_TABLESIZE];
    124 static krwlock_t	ga_cache_table_lock;
    125 static struct kmem_cache *ga_cache_handle;
    126 static void gssauth_cache_reclaim(void *);
    127 
    128 static void gssauth_zone_fini(zoneid_t, void *);
    129 static zone_key_t	gssauth_zone_key;
    130 
    131 int ga_cache_hit;
    132 int ga_cache_miss;
    133 int ga_cache_reclaim;
    134 
    135 #define	NOT_DEAD(ptr)	ASSERT((((intptr_t)(ptr)) != 0xdeadbeef))
    136 
    137 void
    138 gssauth_init(void)
    139 {
    140 	/*
    141 	 * Initialize gss auth cache table lock
    142 	 */
    143 	rw_init(&ga_cache_table_lock, NULL, RW_DEFAULT, NULL);
    144 
    145 	/*
    146 	 * Allocate gss auth cache handle
    147 	 */
    148 	ga_cache_handle = kmem_cache_create("ga_cache_handle",
    149 	    sizeof (struct ga_cache_entry), 0, NULL, NULL,
    150 	    gssauth_cache_reclaim, NULL, NULL, 0);
    151 	zone_key_create(&gssauth_zone_key, NULL, NULL, gssauth_zone_fini);
    152 }
    153 
    154 /*
    155  * Destroy the structures previously initialized in gssauth_init()
    156  * This routine is called by _init() if mod_install() failed.
    157  */
    158 void
    159 gssauth_fini(void)
    160 {
    161 	(void) zone_key_delete(gssauth_zone_key);
    162 	kmem_cache_destroy(ga_cache_handle);
    163 	rw_destroy(&ga_cache_table_lock);
    164 }
    165 
    166 /*
    167  * This is a cleanup routine to release cached entries when a zone is being
    168  * destroyed.  The code is also used when kmem calls us to free up memory, at
    169  * which point ``zoneid'' will be ALL_ZONES.  We don't honor the cache timeout
    170  * when the zone is going away, since the zoneid (and all associated cached
    171  * entries) are invalid.
    172  */
    173 time_t rpc_gss_cache_time = 60 * 60;
    174 
    175 /* ARGSUSED */
    176 static void
    177 gssauth_zone_fini(zoneid_t zoneid, void *unused)
    178 {
    179 	struct ga_cache_entry *p, *prev, *next;
    180 	int i;
    181 	time_t now;
    182 
    183 	rw_enter(&ga_cache_table_lock, RW_WRITER);
    184 
    185 	for (i = 0; i < GSSAUTH_TABLESIZE; i++) {
    186 		prev = NULL;
    187 		for (p = ga_cache_table[i]; p; p = next) {
    188 			NOT_DEAD(p->next);
    189 			next = p->next;
    190 			NOT_DEAD(next);
    191 			if (zoneid == ALL_ZONES) {	/* kmem callback */
    192 				/*
    193 				 * Free entries that have not been
    194 				 * used for rpc_gss_cache_time seconds.
    195 				 */
    196 				now = gethrestime_sec();
    197 				if ((p->ref_time + rpc_gss_cache_time >
    198 				    now) || p->in_use) {
    199 					if ((p->ref_time + rpc_gss_cache_time <=
    200 					    now) && p->in_use) {
    201 						RPCGSS_LOG0(2, "gssauth_cache_"
    202 						    "reclaim: in_use\n");
    203 					}
    204 					prev = p;
    205 					continue;
    206 				}
    207 			} else {
    208 				if (p->zoneid != zoneid) {
    209 					prev = p;
    210 					continue;
    211 				}
    212 				ASSERT(!p->in_use);
    213 			}
    214 
    215 			RPCGSS_LOG(2, "gssauth_cache_reclaim: destroy auth "
    216 			    "%p\n", (void *)p->auth);
    217 			rpc_gss_destroy(p->auth);
    218 			kmem_cache_free(ga_cache_handle, (void *)p);
    219 			if (prev == NULL) {
    220 				ga_cache_table[i] = next;
    221 			} else {
    222 				NOT_DEAD(prev->next);
    223 				prev->next = next;
    224 			}
    225 		}
    226 	}
    227 
    228 	rw_exit(&ga_cache_table_lock);
    229 
    230 }
    231 
    232 /*
    233  * Called by the kernel memory allocator when
    234  * memory is low. Free unused cache entries.
    235  * If that's not enough, the VM system will
    236  * call again for some more.
    237  */
    238 /*ARGSUSED*/
    239 static void
    240 gssauth_cache_reclaim(void *cdrarg)
    241 {
    242 	gssauth_zone_fini(ALL_ZONES, NULL);
    243 }
    244 
    245 #define	NOT_NULL(ptr)	ASSERT(ptr)
    246 #define	IS_ALIGNED(ptr)	ASSERT((((intptr_t)(ptr)) & 3) == 0)
    247 
    248 /*
    249  *  Get the client gss security service handle.
    250  *  If it is in the cache table, get it, otherwise, create
    251  *  a new one by calling rpc_gss_seccreate().
    252  */
    253 int
    254 rpc_gss_secget(CLIENT *clnt,
    255 	char	*principal,
    256 	rpc_gss_OID	mechanism,
    257 	rpc_gss_service_t service_type,
    258 	uint_t	qop,
    259 	rpc_gss_options_req_t *options_req,
    260 	rpc_gss_options_ret_t *options_ret,
    261 	void *cache_key,
    262 	cred_t *cr,
    263 	AUTH **retauth)
    264 {
    265 	struct ga_cache_entry **head, *current, *new, *prev;
    266 	AUTH *auth = NULL;
    267 	rpc_gss_data	*ap;
    268 	rpc_gss_options_ret_t opt_ret;
    269 	int status = 0;
    270 	uid_t uid = crgetuid(cr);
    271 	zoneid_t zoneid = getzoneid();
    272 
    273 	if (retauth == NULL)
    274 		return (EINVAL);
    275 	*retauth = NULL;
    276 
    277 	NOT_NULL(cr);
    278 	IS_ALIGNED(cr);
    279 #ifdef DEBUG
    280 if (HASH(cache_key, uid) < 0) {
    281 	prom_printf("cache_key %p, cr %p\n", cache_key, (void *)cr);
    282 }
    283 #endif
    284 
    285 	/*
    286 	 *  Get a valid gss auth handle from the cache table.
    287 	 *  If auth in cache is invalid and not in use, destroy it.
    288 	 */
    289 	prev = NULL;
    290 	rw_enter(&ga_cache_table_lock, RW_WRITER);
    291 
    292 	ASSERT(HASH(cache_key, uid) >= 0);
    293 	head = &ga_cache_table[HASH(cache_key, uid)];
    294 	NOT_NULL(head);
    295 	IS_ALIGNED(head);
    296 
    297 	for (current = *head; current; current = current->next) {
    298 		NOT_NULL(current);
    299 		IS_ALIGNED(current);
    300 		if ((cache_key == current->cache_key) &&
    301 			(uid == current->uid) && (zoneid == current->zoneid) &&
    302 			!current->in_use) {
    303 			current->in_use = TRUE;
    304 			current->ref_time = gethrestime_sec();
    305 			ap = AUTH_PRIVATE(current->auth);
    306 			ap->clnt = clnt;
    307 			ga_cache_hit++;
    308 			if (ap->invalid ||
    309 			    ((current->ctx_expired_time != GSS_C_INDEFINITE) &&
    310 			    (gethrestime_sec() >=
    311 			    current->ctx_expired_time))) {
    312 			    RPCGSS_LOG0(1, "NOTICE: rpc_gss_secget: time to "
    313 					"refresh the auth\n");
    314 			    if (prev == NULL) {
    315 				*head = current->next;
    316 			    } else {
    317 				prev->next = current->next;
    318 			    }
    319 			    rpc_gss_destroy(current->auth);
    320 			    kmem_cache_free(ga_cache_handle, (void *) current);
    321 			    auth = NULL;
    322 			} else {
    323 			    auth = current->auth;
    324 			}
    325 			break;
    326 		} else {
    327 			prev = current;
    328 		}
    329 	}
    330 	rw_exit(&ga_cache_table_lock);
    331 
    332 	/*
    333 	 *  If no valid gss auth handle can be found in the cache, create
    334 	 *  a new one.
    335 	 */
    336 	if (!auth) {
    337 		ga_cache_miss++;
    338 		if (options_ret == NULL)
    339 			options_ret = &opt_ret;
    340 
    341 		status = rpc_gss_seccreate(clnt, principal, mechanism,
    342 			service_type, qop, options_req, options_ret, cr, &auth);
    343 		if (status == 0) {
    344 			RPCGSS_LOG(2, "rpc_gss_secget: new auth %p\n",
    345 					(void *)auth);
    346 			new = kmem_cache_alloc(ga_cache_handle, KM_NOSLEEP);
    347 			IS_ALIGNED(new);
    348 			NOT_DEAD(new);
    349 			if (new) {
    350 				new->cache_key = cache_key;
    351 				new->uid = uid;
    352 				new->zoneid = zoneid;
    353 				new->in_use = TRUE;
    354 				new->ref_time = gethrestime_sec();
    355 				if (options_ret->time_ret != GSS_C_INDEFINITE) {
    356 				    new->ctx_expired_time = new->ref_time +
    357 					options_ret->time_ret;
    358 				} else {
    359 				    new->ctx_expired_time = GSS_C_INDEFINITE;
    360 				}
    361 				new->auth = auth;
    362 				rw_enter(&ga_cache_table_lock, RW_WRITER);
    363 				NOT_DEAD(*head);
    364 				NOT_DEAD(new->next);
    365 				new->next = *head;
    366 				*head = new;
    367 				rw_exit(&ga_cache_table_lock);
    368 			}
    369 			/* done with opt_ret */
    370 			if (options_ret == &opt_ret) {
    371 			    kgss_free_oid((gss_OID) opt_ret.actual_mechanism);
    372 			}
    373 		}
    374 	}
    375 
    376 	*retauth = auth;
    377 	return (status);
    378 }
    379 
    380 
    381 
    382 /*
    383  *  rpc_gss_secfree will destroy a rpcsec_gss context only if
    384  *  the auth handle is not in the cache table.
    385  */
    386 void
    387 rpc_gss_secfree(AUTH *auth)
    388 {
    389 	struct ga_cache_entry *next, *cur;
    390 	int i;
    391 
    392 	/*
    393 	 *  Check the cache table to find the auth.
    394 	 *  Marked it unused.
    395 	 */
    396 	rw_enter(&ga_cache_table_lock, RW_WRITER);
    397 	for (i = 0; i < GSSAUTH_TABLESIZE; i++) {
    398 		for (cur = ga_cache_table[i]; cur; cur = next) {
    399 			NOT_DEAD(cur);
    400 			next = cur->next;
    401 			NOT_DEAD(next);
    402 			if (cur->auth == auth) {
    403 				ASSERT(cur->in_use == TRUE);
    404 				cur->in_use = FALSE;
    405 				rw_exit(&ga_cache_table_lock);
    406 				return;
    407 			}
    408 		}
    409 	}
    410 	rw_exit(&ga_cache_table_lock);
    411 	RPCGSS_LOG(2, "rpc_gss_secfree: destroy auth %p\n", (void *)auth);
    412 	rpc_gss_destroy(auth);
    413 }
    414 
    415 
    416 /*
    417  *  Create a gss security service context.
    418  */
    419 int
    420 rpc_gss_seccreate(CLIENT *clnt,
    421 	char			*principal,	/* target service@server */
    422 	rpc_gss_OID		mechanism,	/* security mechanism */
    423 	rpc_gss_service_t	service_type,	/* security service */
    424 	uint_t			qop,		/* requested QOP */
    425 	rpc_gss_options_req_t	*options_req,	/* requested options */
    426 	rpc_gss_options_ret_t	*options_ret,	/* returned options */
    427 	cred_t			*cr,		/* client's unix cred */
    428 	AUTH			**retauth)	/* auth handle */
    429 {
    430 	OM_uint32		gssstat;
    431 	OM_uint32		minor_stat;
    432 	gss_name_t		target_name;
    433 	int			ret_flags;
    434 	OM_uint32		time_rec;
    435 	gss_buffer_desc		input_name;
    436 	AUTH			*auth = NULL;
    437 	rpc_gss_data		*ap = NULL;
    438 	int			error;
    439 
    440 	/*
    441 	 * convert name to GSS internal type
    442 	 */
    443 	input_name.value = principal;
    444 	input_name.length = strlen(principal);
    445 
    446 	gssstat = gss_import_name(&minor_stat, &input_name,
    447 	    (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &target_name);
    448 
    449 	if (gssstat != GSS_S_COMPLETE) {
    450 		RPCGSS_LOG0(1,
    451 		    "rpc_gss_seccreate: unable to import gss name\n");
    452 		return (ENOMEM);
    453 	}
    454 
    455 	/*
    456 	 * Create AUTH handle.  Save the necessary interface information
    457 	 * so that the client can refresh the handle later if needed.
    458 	 */
    459 	if ((auth = (AUTH *) kmem_alloc(sizeof (*auth), KM_SLEEP)) != NULL)
    460 		ap = (rpc_gss_data *) kmem_alloc(sizeof (*ap), KM_SLEEP);
    461 	if (auth == NULL || ap == NULL) {
    462 		RPCGSS_LOG0(1, "rpc_gss_seccreate: out of memory\n");
    463 		if (auth != NULL)
    464 			kmem_free((char *)auth, sizeof (*auth));
    465 		(void) gss_release_name(&minor_stat, &target_name);
    466 		return (ENOMEM);
    467 	}
    468 
    469 	bzero((char *)ap, sizeof (*ap));
    470 	ap->clnt = clnt;
    471 	ap->version = RPCSEC_GSS_VERSION;
    472 	if (options_req != NULL) {
    473 		ap->my_cred = options_req->my_cred;
    474 		ap->req_flags = options_req->req_flags;
    475 		ap->time_req = options_req->time_req;
    476 		ap->icb = options_req->input_channel_bindings;
    477 	} else {
    478 		ap->my_cred = GSS_C_NO_CREDENTIAL;
    479 		ap->req_flags = GSS_C_MUTUAL_FLAG;
    480 		ap->time_req = 0;
    481 		ap->icb = GSS_C_NO_CHANNEL_BINDINGS;
    482 	}
    483 	if ((ap->service = service_type) == rpc_gss_svc_default)
    484 		ap->service = rpc_gss_svc_integrity;
    485 	ap->qop = qop;
    486 	ap->target_name = target_name;
    487 
    488 	/*
    489 	 * Now invoke the real interface that sets up the context from
    490 	 * the information stashed away in the private data.
    491 	 */
    492 	if (error = rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth, ap,
    493 	    mechanism, &ap->mech_type, &ret_flags, &time_rec, cr, 0)) {
    494 		if (ap->target_name) {
    495 			(void) gss_release_name(&minor_stat, &ap->target_name);
    496 		}
    497 		kmem_free((char *)ap, sizeof (*ap));
    498 		kmem_free((char *)auth, sizeof (*auth));
    499 		RPCGSS_LOG(1, "rpc_gss_seccreate: init context failed"
    500 		    " errno=%d\n", error);
    501 		return (error);
    502 	}
    503 
    504 	/*
    505 	 * Make sure that the requested service is supported.  In all
    506 	 * cases, integrity service must be available.
    507 	 */
    508 	if ((ap->service == rpc_gss_svc_privacy &&
    509 	    !(ret_flags & GSS_C_CONF_FLAG)) ||
    510 	    !(ret_flags & GSS_C_INTEG_FLAG)) {
    511 		rpc_gss_destroy(auth);
    512 		RPCGSS_LOG0(1, "rpc_gss_seccreate: service not supported\n");
    513 		return (EPROTONOSUPPORT);
    514 	}
    515 
    516 	/*
    517 	 * return option values if requested
    518 	 */
    519 	if (options_ret != NULL) {
    520 		options_ret->major_status = gssstat;
    521 		options_ret->minor_status = minor_stat;
    522 		options_ret->rpcsec_version = ap->version;
    523 		options_ret->ret_flags = ret_flags;
    524 		options_ret->time_ret = time_rec;
    525 		options_ret->gss_context = ap->context;
    526 		/*
    527 		 *  Caller's responsibility to free this.
    528 		 */
    529 		NOT_NULL(ap->mech_type);
    530 		__rpc_gss_dup_oid(ap->mech_type,
    531 		    (gss_OID *)&options_ret->actual_mechanism);
    532 	}
    533 
    534 	*retauth = auth;
    535 	return (0);
    536 }
    537 
    538 /*
    539  * Private interface to create a context.  This is the interface
    540  * that's invoked when the context has to be refreshed.
    541  */
    542 static int
    543 rpc_gss_seccreate_pvt(gssstat, minor_stat, auth, ap, desired_mech_type,
    544 			actual_mech_type, ret_flags, time_rec, cr, isrefresh)
    545 	OM_uint32		*gssstat;
    546 	OM_uint32		*minor_stat;
    547 	AUTH			*auth;
    548 	rpc_gss_data		*ap;
    549 	gss_OID			desired_mech_type;
    550 	gss_OID			*actual_mech_type;
    551 	int			*ret_flags;
    552 	OM_uint32		*time_rec;
    553 	cred_t			*cr;
    554 	int			isrefresh;
    555 {
    556 	CLIENT			*clnt = ap->clnt;
    557 	AUTH			*save_auth;
    558 	enum clnt_stat		callstat;
    559 	rpc_gss_init_arg	call_arg;
    560 	rpc_gss_init_res	call_res;
    561 	gss_buffer_desc		*input_token_p, input_token, process_token;
    562 	int 			free_results = 0;
    563 	k_sigset_t		smask;
    564 	int			error = 0;
    565 
    566 	/*
    567 	 * (re)initialize AUTH handle and private data.
    568 	 */
    569 	bzero((char *)auth, sizeof (*auth));
    570 	auth->ah_ops = &rpc_gss_ops;
    571 	auth->ah_private = (caddr_t)ap;
    572 	auth->ah_cred.oa_flavor = RPCSEC_GSS;
    573 
    574 	ap->established = FALSE;
    575 	ap->ctx_handle.length = 0;
    576 	ap->ctx_handle.value = NULL;
    577 	ap->context = NULL;
    578 	ap->seq_num = 0;
    579 	ap->gss_proc = RPCSEC_GSS_INIT;
    580 
    581 	/*
    582 	 * should not change clnt->cl_auth at this time, so save
    583 	 * old handle
    584 	 */
    585 	save_auth = clnt->cl_auth;
    586 	clnt->cl_auth = auth;
    587 
    588 	/*
    589 	 * set state for starting context setup
    590 	 */
    591 	bzero((char *)&call_arg, sizeof (call_arg));
    592 	input_token_p = GSS_C_NO_BUFFER;
    593 
    594 next_token:
    595 	*gssstat = kgss_init_sec_context(minor_stat,
    596 					ap->my_cred,
    597 					&ap->context,
    598 					ap->target_name,
    599 					desired_mech_type,
    600 					ap->req_flags,
    601 					ap->time_req,
    602 					NULL,
    603 					input_token_p,
    604 					actual_mech_type,
    605 					&call_arg,
    606 					ret_flags,
    607 					time_rec,
    608 					crgetuid(cr));
    609 
    610 	if (input_token_p != GSS_C_NO_BUFFER) {
    611 		OM_uint32 minor_stat2;
    612 
    613 		(void) gss_release_buffer(&minor_stat2, input_token_p);
    614 		input_token_p = GSS_C_NO_BUFFER;
    615 	}
    616 
    617 	if (*gssstat != GSS_S_COMPLETE && *gssstat != GSS_S_CONTINUE_NEEDED) {
    618 		rpc_gss_display_status(*gssstat, *minor_stat,
    619 			desired_mech_type, crgetuid(cr),
    620 			"rpcsec_gss_secreate_pvt:gss_init_sec_context");
    621 		error = EACCES;
    622 		goto cleanup;
    623 	}
    624 
    625 	/*
    626 	 * if we got a token, pass it on
    627 	 */
    628 	if (call_arg.length != 0) {
    629 		struct timeval timeout = {30, 0};
    630 		int	 rpcsec_retry = isrefresh ?
    631 			RPCSEC_GSS_REFRESH_ATTEMPTS : 1;
    632 		uint32_t oldxid;
    633 		uint32_t zeroxid = 0;
    634 
    635 		bzero((char *)&call_res, sizeof (call_res));
    636 
    637 		(void) CLNT_CONTROL(clnt, CLGET_XID, (char *)&oldxid);
    638 		(void) CLNT_CONTROL(clnt, CLSET_XID, (char *)&zeroxid);
    639 
    640 
    641 		while (rpcsec_retry > 0) {
    642 			struct rpc_err rpcerr;
    643 
    644 			sigintr(&smask, INTERRUPT_OK);
    645 
    646 			callstat = clnt_call(clnt, NULLPROC,
    647 				__xdr_rpc_gss_init_arg, (caddr_t)&call_arg,
    648 				__xdr_rpc_gss_init_res, (caddr_t)&call_res,
    649 				timeout);
    650 
    651 			sigunintr(&smask);
    652 
    653 			if (callstat == RPC_SUCCESS) {
    654 				error = 0;
    655 				if (isrefresh &&
    656 				    call_res.gss_major == GSS_S_FAILURE) {
    657 
    658 					clock_t one_sec = drv_usectohz(1000000);
    659 
    660 					rpcsec_retry--;
    661 
    662 					/*
    663 					 * Pause a little and try again.
    664 					 */
    665 
    666 					if (clnt->cl_nosignal == TRUE) {
    667 						delay(one_sec);
    668 					} else {
    669 						if (delay_sig(one_sec)) {
    670 							error = EINTR;
    671 							break;
    672 						}
    673 					}
    674 					continue;
    675 				}
    676 				break;
    677 			}
    678 
    679 			if (callstat == RPC_TIMEDOUT) {
    680 				error = ETIMEDOUT;
    681 				break;
    682 			}
    683 
    684 			if (callstat == RPC_XPRTFAILED) {
    685 				error = ECONNRESET;
    686 				break;
    687 			}
    688 
    689 			if (callstat == RPC_INTR) {
    690 				error = EINTR;
    691 				break;
    692 			}
    693 
    694 			if (callstat == RPC_INPROGRESS) {
    695 				continue;
    696 			}
    697 
    698 			clnt_geterr(clnt, &rpcerr);
    699 			error = rpcerr.re_errno;
    700 			break;
    701 		}
    702 
    703 		(void) CLNT_CONTROL(clnt, CLSET_XID, (char *)&oldxid);
    704 
    705 		(void) gss_release_buffer(minor_stat, &call_arg);
    706 
    707 		if (callstat != RPC_SUCCESS) {
    708 			RPCGSS_LOG(1,
    709 			    "rpc_gss_seccreate_pvt: clnt_call failed %d\n",
    710 			    callstat);
    711 			goto cleanup;
    712 		}
    713 
    714 		/*
    715 		 * we have results - note that these need to be freed
    716 		 */
    717 		free_results = 1;
    718 
    719 		if ((call_res.gss_major != GSS_S_COMPLETE) &&
    720 		    (call_res.gss_major != GSS_S_CONTINUE_NEEDED)) {
    721 			RPCGSS_LOG1(1, "rpc_gss_seccreate_pvt: "
    722 				"call_res gss_major %x, gss_minor %x\n",
    723 				call_res.gss_major, call_res.gss_minor);
    724 			error = EACCES;
    725 			goto cleanup;
    726 		}
    727 
    728 		ap->gss_proc = RPCSEC_GSS_CONTINUE_INIT;
    729 
    730 		/*
    731 		 * check for ctx_handle
    732 		 */
    733 		if (ap->ctx_handle.length == 0) {
    734 			if (call_res.ctx_handle.length == 0) {
    735 				RPCGSS_LOG0(1, "rpc_gss_seccreate_pvt: zero "
    736 					"length handle in response\n");
    737 				error = EACCES;
    738 				goto cleanup;
    739 			}
    740 			GSS_DUP_BUFFER(ap->ctx_handle,
    741 					call_res.ctx_handle);
    742 		} else if (!GSS_BUFFERS_EQUAL(ap->ctx_handle,
    743 						call_res.ctx_handle)) {
    744 			RPCGSS_LOG0(1,
    745 			"rpc_gss_seccreate_pvt: ctx_handle not the same\n");
    746 			error = EACCES;
    747 			goto cleanup;
    748 		}
    749 
    750 		/*
    751 		 * check for token
    752 		 */
    753 		if (call_res.token.length != 0) {
    754 			if (*gssstat == GSS_S_COMPLETE) {
    755 				RPCGSS_LOG0(1, "rpc_gss_seccreate_pvt: non "
    756 					"zero length token in response, but "
    757 					"gsstat == GSS_S_COMPLETE\n");
    758 				error = EACCES;
    759 				goto cleanup;
    760 			}
    761 			GSS_DUP_BUFFER(input_token, call_res.token);
    762 			input_token_p = &input_token;
    763 
    764 		} else if (*gssstat != GSS_S_COMPLETE) {
    765 			RPCGSS_LOG0(1, "rpc_gss_seccreate_pvt:zero length "
    766 				"token in response, but "
    767 				"gsstat != GSS_S_COMPLETE\n");
    768 			error = EACCES;
    769 			goto cleanup;
    770 		}
    771 
    772 		/* save the sequence window value; validate later */
    773 		ap->seq_window = call_res.seq_window;
    774 		xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res);
    775 		free_results = 0;
    776 	}
    777 
    778 	/*
    779 	 * results were okay.. continue if necessary
    780 	 */
    781 	if (*gssstat == GSS_S_CONTINUE_NEEDED) {
    782 		goto next_token;
    783 	}
    784 
    785 	/*
    786 	 * Context is established. Now use kgss_export_sec_context and
    787 	 * kgss_import_sec_context to transfer the context from the user
    788 	 * land to kernel if the mechanism specific kernel module is
    789 	 * available.
    790 	 */
    791 	*gssstat  = kgss_export_sec_context(minor_stat, ap->context,
    792 						&process_token);
    793 	if (*gssstat == GSS_S_NAME_NOT_MN) {
    794 		RPCGSS_LOG(2, "rpc_gss_seccreate_pvt: export_sec_context "
    795 			"Kernel Module unavailable  gssstat = 0x%x\n",
    796 			*gssstat);
    797 		goto done;
    798 	} else if (*gssstat != GSS_S_COMPLETE) {
    799 		(void) rpc_gss_display_status(*gssstat, *minor_stat,
    800 			isrefresh ? GSS_C_NULL_OID : *actual_mech_type,
    801 					crgetuid(cr),
    802 			"rpcsec_gss_secreate_pvt:gss_export_sec_context");
    803 		(void) kgss_delete_sec_context(minor_stat,
    804 					&ap->context, NULL);
    805 		error = EACCES;
    806 		goto cleanup;
    807 	} else if (process_token.length == 0) {
    808 		RPCGSS_LOG0(1, "rpc_gss_seccreate_pvt:zero length "
    809 				"token in response for export_sec_context, but "
    810 				"gsstat == GSS_S_COMPLETE\n");
    811 		(void) kgss_delete_sec_context(minor_stat,
    812 					&ap->context, NULL);
    813 		error = EACCES;
    814 		goto cleanup;
    815 	} else
    816 		*gssstat = kgss_import_sec_context(minor_stat, &process_token,
    817 							ap->context);
    818 
    819 	if (*gssstat == GSS_S_COMPLETE) {
    820 		(void) gss_release_buffer(minor_stat, &process_token);
    821 	} else {
    822 		rpc_gss_display_status(*gssstat, *minor_stat,
    823 			desired_mech_type, crgetuid(cr),
    824 			"rpcsec_gss_secreate_pvt:gss_import_sec_context");
    825 		(void) kgss_delete_sec_context(minor_stat,
    826 					&ap->context, NULL);
    827 		(void) gss_release_buffer(minor_stat, &process_token);
    828 		error = EACCES;
    829 		goto cleanup;
    830 	}
    831 
    832 done:
    833 	/*
    834 	 * Validate the sequence window - RFC 2203 section 5.2.3.1
    835 	 */
    836 	if (!validate_seqwin(ap)) {
    837 		error = EACCES;
    838 		goto cleanup;
    839 	}
    840 
    841 	/*
    842 	 * Done!  Security context creation is successful.
    843 	 * Ready for exchanging data.
    844 	 */
    845 	ap->established = TRUE;
    846 	ap->seq_num = 1;
    847 	ap->gss_proc = RPCSEC_GSS_DATA;
    848 	ap->invalid = FALSE;
    849 
    850 	clnt->cl_auth = save_auth;	/* restore cl_auth */
    851 
    852 	return (0);
    853 
    854 cleanup:
    855 	if (free_results)
    856 		xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res);
    857 	clnt->cl_auth = save_auth;	/* restore cl_auth */
    858 
    859 	/*
    860 	 * If need to retry for AUTH_REFRESH, do not cleanup the
    861 	 * auth private data.
    862 	 */
    863 	if (isrefresh && (error == ETIMEDOUT || error == ECONNRESET)) {
    864 		return (error);
    865 	}
    866 
    867 	if (ap->context != NULL) {
    868 		rpc_gss_free_pvt(auth);
    869 	}
    870 
    871 	return (error? error : EACCES);
    872 }
    873 
    874 /*
    875  * Marshall credentials.
    876  */
    877 static bool_t
    878 marshall_creds(ap, xdrs, cred_buf_len)
    879 	rpc_gss_data		*ap;
    880 	XDR			*xdrs;
    881 	uint_t			cred_buf_len;
    882 {
    883 	rpc_gss_creds		ag_creds;
    884 	char			*cred_buf;
    885 	struct opaque_auth	creds;
    886 	XDR			cred_xdrs;
    887 
    888 	ag_creds.version = ap->version;
    889 	ag_creds.gss_proc = ap->gss_proc;
    890 	ag_creds.seq_num = ap->seq_num;
    891 	ag_creds.service = ap->service;
    892 
    893 	/*
    894 	 * If context has not been set up yet, use NULL handle.
    895 	 */
    896 	if (ap->ctx_handle.length > 0)
    897 		ag_creds.ctx_handle = ap->ctx_handle;
    898 	else {
    899 		ag_creds.ctx_handle.length = 0;
    900 		ag_creds.ctx_handle.value = NULL;
    901 	}
    902 
    903 	cred_buf = kmem_alloc(cred_buf_len, KM_SLEEP);
    904 	xdrmem_create(&cred_xdrs, (caddr_t)cred_buf, cred_buf_len,
    905 								XDR_ENCODE);
    906 	if (!__xdr_rpc_gss_creds(&cred_xdrs, &ag_creds)) {
    907 		kmem_free(cred_buf, MAX_AUTH_BYTES);
    908 		XDR_DESTROY(&cred_xdrs);
    909 		return (FALSE);
    910 	}
    911 
    912 	creds.oa_flavor = RPCSEC_GSS;
    913 	creds.oa_base = cred_buf;
    914 	creds.oa_length = xdr_getpos(&cred_xdrs);
    915 	XDR_DESTROY(&cred_xdrs);
    916 
    917 	if (!xdr_opaque_auth(xdrs, &creds)) {
    918 		kmem_free(cred_buf, cred_buf_len);
    919 		return (FALSE);
    920 	}
    921 
    922 	kmem_free(cred_buf, cred_buf_len);
    923 	return (TRUE);
    924 }
    925 
    926 /*
    927  * Marshall verifier.  The verifier is the checksum of the RPC header
    928  * up to and including the credential field.  The XDR handle that's
    929  * passed in has the header up to and including the credential field
    930  * encoded.  A pointer to the transmit buffer is also passed in.
    931  */
    932 static bool_t
    933 marshall_verf(ap, xdrs, buf)
    934 	rpc_gss_data		*ap;
    935 	XDR			*xdrs;	/* send XDR */
    936 	char			*buf;	/* pointer of send buffer */
    937 {
    938 	struct opaque_auth	verf;
    939 	OM_uint32		major, minor;
    940 	gss_buffer_desc		in_buf, out_buf;
    941 	bool_t			ret = FALSE;
    942 
    943 	/*
    944 	 * If context is not established yet, use NULL verifier.
    945 	 */
    946 	if (!ap->established) {
    947 		verf.oa_flavor = AUTH_NONE;
    948 		verf.oa_base = NULL;
    949 		verf.oa_length = 0;
    950 		return (xdr_opaque_auth(xdrs, &verf));
    951 	}
    952 
    953 	verf.oa_flavor = RPCSEC_GSS;
    954 	in_buf.length = xdr_getpos(xdrs);
    955 	in_buf.value = buf;
    956 	if ((major = kgss_sign(&minor, ap->context, ap->qop, &in_buf,
    957 				&out_buf)) != GSS_S_COMPLETE) {
    958 		if (major == GSS_S_CONTEXT_EXPIRED) {
    959 			ap->invalid = TRUE;
    960 		}
    961 		RPCGSS_LOG1(1,
    962 		    "marshall_verf: kgss_sign failed GSS Major %x Minor %x\n",
    963 		    major, minor);
    964 		return (FALSE);
    965 	}
    966 	verf.oa_base = out_buf.value;
    967 	verf.oa_length = out_buf.length;
    968 	ret = xdr_opaque_auth(xdrs, &verf);
    969 	(void) gss_release_buffer(&minor, &out_buf);
    970 
    971 	return (ret);
    972 }
    973 
    974 /*
    975  * Validate sequence window upon a successful RPCSEC_GSS INIT session.
    976  * The sequence window sent back by the server should be verifiable by
    977  * the verifier which is a checksum of the sequence window.
    978  */
    979 static bool_t
    980 validate_seqwin(rpc_gss_data *ap)
    981 {
    982 	uint_t			seq_win_net;
    983 	OM_uint32		major = 0, minor = 0;
    984 	gss_buffer_desc		msg_buf, tok_buf;
    985 	int			qop_state = 0;
    986 
    987 	ASSERT(ap->verifier);
    988 	ASSERT(ap->context);
    989 	seq_win_net = (uint_t)htonl(ap->seq_window);
    990 	msg_buf.length = sizeof (seq_win_net);
    991 	msg_buf.value = (char *)&seq_win_net;
    992 	tok_buf.length = ap->verifier->oa_length;
    993 	tok_buf.value = ap->verifier->oa_base;
    994 	major = kgss_verify(&minor, ap->context, &msg_buf, &tok_buf,
    995 	    &qop_state);
    996 
    997 	if (major != GSS_S_COMPLETE) {
    998 		RPCGSS_LOG1(1,
    999 		    "validate_seqwin: kgss_verify failed GSS Major "
   1000 		    "%x Minor %x\n", major, minor);
   1001 		RPCGSS_LOG1(1, "seq_window %d, verf len %d ", ap->seq_window,
   1002 		    ap->verifier->oa_length);
   1003 		return (FALSE);
   1004 	}
   1005 	return (TRUE);
   1006 }
   1007 
   1008 /*
   1009  * Validate RPC response verifier from server.  The response verifier
   1010  * is the checksum of the request sequence number.
   1011  */
   1012 static bool_t
   1013 rpc_gss_validate(auth, verf)
   1014 	AUTH			*auth;
   1015 	struct opaque_auth	*verf;
   1016 {
   1017 	rpc_gss_data		*ap = AUTH_PRIVATE(auth);
   1018 	uint_t			seq_num_net;
   1019 	OM_uint32		major, minor;
   1020 	gss_buffer_desc		msg_buf, tok_buf;
   1021 	int			qop_state;
   1022 
   1023 	/*
   1024 	 * If context is not established yet, save the verifier for
   1025 	 * validating the sequence window later at the end of context
   1026 	 * creation session.
   1027 	 */
   1028 	if (!ap->established) {
   1029 	    if (ap->verifier == NULL) {
   1030 		ap->verifier = kmem_zalloc(sizeof (struct opaque_auth),
   1031 						KM_SLEEP);
   1032 		if (verf->oa_length > 0)
   1033 		    ap->verifier->oa_base = kmem_zalloc(verf->oa_length,
   1034 						KM_SLEEP);
   1035 	    } else {
   1036 		if (ap->verifier->oa_length > 0)
   1037 		    kmem_free(ap->verifier->oa_base, ap->verifier->oa_length);
   1038 		if (verf->oa_length > 0)
   1039 		    ap->verifier->oa_base = kmem_zalloc(verf->oa_length,
   1040 						KM_SLEEP);
   1041 	    }
   1042 	    ap->verifier->oa_length = verf->oa_length;
   1043 	    bcopy(verf->oa_base, ap->verifier->oa_base, verf->oa_length);
   1044 	    return (TRUE);
   1045 	}
   1046 
   1047 	seq_num_net = (uint_t)htonl(ap->seq_num);
   1048 	msg_buf.length = sizeof (seq_num_net);
   1049 	msg_buf.value = (char *)&seq_num_net;
   1050 	tok_buf.length = verf->oa_length;
   1051 	tok_buf.value = verf->oa_base;
   1052 	major = kgss_verify(&minor, ap->context, &msg_buf, &tok_buf,
   1053 				&qop_state);
   1054 	if (major != GSS_S_COMPLETE) {
   1055 		RPCGSS_LOG1(1,
   1056 		"rpc_gss_validate: kgss_verify failed GSS Major %x Minor %x\n",
   1057 		major, minor);
   1058 		return (FALSE);
   1059 	}
   1060 	return (TRUE);
   1061 }
   1062 
   1063 /*
   1064  * Refresh client context.  This is necessary sometimes because the
   1065  * server will ocassionally destroy contexts based on LRU method, or
   1066  * because of expired credentials.
   1067  */
   1068 static bool_t
   1069 rpc_gss_refresh(auth, msg, cr)
   1070 	AUTH		*auth;
   1071 	struct rpc_msg	*msg;
   1072 	cred_t		*cr;
   1073 {
   1074 	rpc_gss_data	*ap = AUTH_PRIVATE(auth);
   1075 	gss_ctx_id_t	ctx_sav = NULL;
   1076 	gss_buffer_desc	ctx_hdle_sav = {0, NULL};
   1077 	uint_t		sn_sav, proc_sav;
   1078 	bool_t		est_sav;
   1079 	OM_uint32	gssstat, minor_stat;
   1080 	int error;
   1081 
   1082 	/*
   1083 	 * The context needs to be recreated only when the error status
   1084 	 * returned from the server is one of the following:
   1085 	 *	RPCSEC_GSS_NOCRED and RPCSEC_GSS_FAILED
   1086 	 * The existing context should not be destroyed unless the above
   1087 	 * error status codes are received or if the context has not
   1088 	 * been set up.
   1089 	 */
   1090 
   1091 	if (msg->rjcted_rply.rj_why == RPCSEC_GSS_NOCRED ||
   1092 			msg->rjcted_rply.rj_why == RPCSEC_GSS_FAILED ||
   1093 							!ap->established) {
   1094 		/*
   1095 		 * Destroy the context if necessary.  Use the same memory
   1096 		 * for the new context since we've already passed a pointer
   1097 		 * to it to the user.
   1098 		 */
   1099 		if (ap->context != NULL) {
   1100 			ctx_sav = ap->context;
   1101 			ap->context = NULL;
   1102 		}
   1103 		if (ap->ctx_handle.length != 0) {
   1104 			ctx_hdle_sav.length = ap->ctx_handle.length;
   1105 			ctx_hdle_sav.value = ap->ctx_handle.value;
   1106 			ap->ctx_handle.length = 0;
   1107 			ap->ctx_handle.value = NULL;
   1108 		}
   1109 
   1110 		/*
   1111 		 * If the context was not already established, don't try to
   1112 		 * recreate it.
   1113 		 */
   1114 		if (!ap->established) {
   1115 			ap->invalid = TRUE;
   1116 			RPCGSS_LOG0(1,
   1117 			"rpc_gss_refresh: context was not established\n");
   1118 			error = EINVAL;
   1119 			goto out;
   1120 		}
   1121 
   1122 		est_sav = ap->established;
   1123 		sn_sav = ap->seq_num;
   1124 		proc_sav = ap->gss_proc;
   1125 
   1126 		/*
   1127 		 * Recreate context.
   1128 		 */
   1129 		error = rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth,
   1130 				ap, ap->mech_type, (gss_OID *)NULL, (int *)NULL,
   1131 				(OM_uint32 *)NULL, cr, 1);
   1132 
   1133 		switch (error) {
   1134 		case 0:
   1135 			RPCGSS_LOG(1,
   1136 			"rpc_gss_refresh: auth %p refreshed\n", (void *)auth);
   1137 			goto out;
   1138 
   1139 		case ETIMEDOUT:
   1140 		case ECONNRESET:
   1141 			RPCGSS_LOG0(1, "rpc_gss_refresh: try again\n");
   1142 
   1143 			if (ap->context != NULL) {
   1144 			    (void) kgss_delete_sec_context(&minor_stat,
   1145 					&ap->context, NULL);
   1146 			}
   1147 			if (ap->ctx_handle.length != 0) {
   1148 			    (void) gss_release_buffer(&minor_stat,
   1149 					&ap->ctx_handle);
   1150 			}
   1151 
   1152 			/*
   1153 			 * Restore the original value for the caller to
   1154 			 * try again later.
   1155 			 */
   1156 			ap->context = ctx_sav;
   1157 			ap->ctx_handle.length = ctx_hdle_sav.length;
   1158 			ap->ctx_handle.value = ctx_hdle_sav.value;
   1159 			ap->established = est_sav;
   1160 			ap->seq_num = sn_sav;
   1161 			ap->gss_proc = proc_sav;
   1162 
   1163 			return (FALSE);
   1164 
   1165 		default:
   1166 			ap->invalid = TRUE;
   1167 			RPCGSS_LOG(1, "rpc_gss_refresh: can't refresh this "
   1168 				"auth, error=%d\n", error);
   1169 			goto out;
   1170 		}
   1171 	}
   1172 	RPCGSS_LOG0(1, "rpc_gss_refresh: don't refresh");
   1173 	return (FALSE);
   1174 
   1175 out:
   1176 	if (ctx_sav != NULL) {
   1177 		(void) kgss_delete_sec_context(&minor_stat,
   1178 				&ctx_sav, NULL);
   1179 	}
   1180 	if (ctx_hdle_sav.length != 0) {
   1181 		(void) gss_release_buffer(&minor_stat, &ctx_hdle_sav);
   1182 	}
   1183 
   1184 	return (error == 0);
   1185 }
   1186 
   1187 /*
   1188  * Destroy a context.
   1189  */
   1190 static void
   1191 rpc_gss_destroy(auth)
   1192 	AUTH		*auth;
   1193 {
   1194 	rpc_gss_data	*ap = AUTH_PRIVATE(auth);
   1195 
   1196 	/*
   1197 	 *  XXX Currently, we do not ping the server (rpc_gss_destroy_pvt)
   1198 	 *  to destroy the context in the server cache.
   1199 	 *  We assume there is a good LRU/aging mechanism for the
   1200 	 *  context cache on the server side.
   1201 	 */
   1202 	rpc_gss_free_pvt(auth);
   1203 	kmem_free((char *)ap, sizeof (*ap));
   1204 	kmem_free(auth, sizeof (*auth));
   1205 }
   1206 
   1207 /*
   1208  * Private interface to free memory allocated in the rpcsec_gss private
   1209  * data structure (rpc_gss_data).
   1210  */
   1211 static void
   1212 rpc_gss_free_pvt(auth)
   1213 	AUTH		*auth;
   1214 {
   1215 	OM_uint32	minor_stat;
   1216 	rpc_gss_data	*ap = AUTH_PRIVATE(auth);
   1217 
   1218 	if (ap->ctx_handle.length != 0) {
   1219 		(void) gss_release_buffer(&minor_stat, &ap->ctx_handle);
   1220 		ap->ctx_handle.length = 0;
   1221 		ap->ctx_handle.value = NULL;
   1222 	}
   1223 
   1224 	/*
   1225 	 * Destroy local GSS context.
   1226 	 */
   1227 	if (ap->context != NULL) {
   1228 		(void) kgss_delete_sec_context(&minor_stat, &ap->context, NULL);
   1229 		ap->context = NULL;
   1230 	}
   1231 
   1232 	/*
   1233 	 * Looks like we need to release default credentials if we use it.
   1234 	 * Non-default creds need to be released by user.
   1235 	 */
   1236 	if (ap->my_cred == GSS_C_NO_CREDENTIAL)
   1237 		(void) kgss_release_cred(&minor_stat, &ap->my_cred,
   1238 					crgetuid(CRED()));
   1239 
   1240 	/*
   1241 	 * Release any internal name structures.
   1242 	 */
   1243 	if (ap->target_name != NULL) {
   1244 		(void) gss_release_name(&minor_stat, &ap->target_name);
   1245 		ap->target_name = NULL;
   1246 	}
   1247 
   1248 	/*
   1249 	 * Free mech_type oid structure.
   1250 	 */
   1251 	if (ap->mech_type != NULL) {
   1252 		kgss_free_oid(ap->mech_type);
   1253 		ap->mech_type = NULL;
   1254 	}
   1255 
   1256 	/*
   1257 	 * Free the verifier saved for sequence window checking.
   1258 	 */
   1259 	if (ap->verifier != NULL) {
   1260 	    if (ap->verifier->oa_length > 0) {
   1261 		kmem_free(ap->verifier->oa_base, ap->verifier->oa_length);
   1262 	    }
   1263 	    kmem_free(ap->verifier, sizeof (struct opaque_auth));
   1264 	    ap->verifier = NULL;
   1265 	}
   1266 }
   1267 
   1268 #if 0
   1269 /*
   1270  * XXX this function is not used right now.
   1271  * There is a client handle issue needs to be resolved.
   1272  *
   1273  * This is a private interface which will destroy a context
   1274  * without freeing up the memory used by it.  We need to do this when
   1275  * a refresh fails, for example, so the user will still have a handle.
   1276  */
   1277 static void
   1278 rpc_gss_destroy_pvt(auth)
   1279 	AUTH		*auth;
   1280 {
   1281 	struct timeval	timeout;
   1282 	rpc_gss_data	*ap = AUTH_PRIVATE(auth);
   1283 
   1284 	/*
   1285 	 * If we have a server context id, inform server that we are
   1286 	 * destroying the context.
   1287 	 */
   1288 	if (ap->ctx_handle.length != 0) {
   1289 		uint32_t oldxid;
   1290 		uint32_t zeroxid = 0;
   1291 
   1292 		ap->gss_proc = RPCSEC_GSS_DESTROY;
   1293 		timeout.tv_sec = 10;
   1294 		timeout.tv_usec = 0;
   1295 		(void) CLNT_CONTROL(ap->clnt, CLGET_XID, (char *)&oldxid);
   1296 		(void) CLNT_CONTROL(ap->clnt, CLSET_XID, (char *)&zeroxid);
   1297 		(void) clnt_call(ap->clnt, NULLPROC, xdr_void, NULL,
   1298 						xdr_void, NULL, timeout);
   1299 		(void) CLNT_CONTROL(ap->clnt, CLSET_XID, (char *)&oldxid);
   1300 	}
   1301 
   1302 	rpc_gss_free_pvt(auth);
   1303 }
   1304 #endif
   1305 
   1306 /*
   1307  * Wrap client side data.  The encoded header is passed in through
   1308  * buf and buflen.  The header is up to but not including the
   1309  * credential field.
   1310  */
   1311 bool_t
   1312 rpc_gss_wrap(auth, buf, buflen, out_xdrs, xdr_func, xdr_ptr)
   1313 	AUTH			*auth;
   1314 	char			*buf;		/* encoded header */
   1315 /* has been changed to u_int in the user land */
   1316 	uint_t			buflen;		/* encoded header length */
   1317 	XDR			*out_xdrs;
   1318 	xdrproc_t		xdr_func;
   1319 	caddr_t			xdr_ptr;
   1320 {
   1321 	rpc_gss_data		*ap = AUTH_PRIVATE(auth);
   1322 	XDR			xdrs;
   1323 	char			*tmp_buf;
   1324 	uint_t			xdr_buf_len, cred_buf_len;
   1325 
   1326 /*
   1327  *  Here is how MAX_SIGNED_LEN is estimated.
   1328  *  Signing a 48 bytes buffer using des_cbc_md5 would end up with
   1329  *  a buffer length 33 (padded data + 16 bytes of seq_num/checksum).
   1330  *  Current known max seq_num/checksum size is 24 bytes.
   1331  *  88 is derived from RNDUP(33+(24-16)) * 2.
   1332  */
   1333 #define	MAX_SIGNED_LEN	88
   1334 
   1335 	/*
   1336 	 * Reject an invalid context.
   1337 	 */
   1338 	if (ap->invalid) {
   1339 		RPCGSS_LOG0(1, "rpc_gss_wrap: reject an invalid context\n");
   1340 		return (FALSE);
   1341 	}
   1342 
   1343 	/*
   1344 	 * If context is established, bump up sequence number.
   1345 	 */
   1346 	if (ap->established)
   1347 		ap->seq_num++;
   1348 
   1349 	/*
   1350 	 * Create the header in a temporary XDR context and buffer
   1351 	 * before putting it out.
   1352 	 */
   1353 	cred_buf_len = RNDUP(sizeof (ap->version) + sizeof (ap->gss_proc) +
   1354 			sizeof (ap->seq_num) + sizeof (ap->service) +
   1355 			sizeof (ap->ctx_handle) + ap->ctx_handle.length);
   1356 
   1357 	xdr_buf_len = buflen + cred_buf_len + sizeof (struct opaque_auth) +
   1358 			MAX_SIGNED_LEN;
   1359 	tmp_buf = kmem_alloc(xdr_buf_len, KM_SLEEP);
   1360 	xdrmem_create(&xdrs, tmp_buf, xdr_buf_len, XDR_ENCODE);
   1361 	if (!XDR_PUTBYTES(&xdrs, buf, buflen)) {
   1362 		kmem_free(tmp_buf, xdr_buf_len);
   1363 		RPCGSS_LOG0(1, "rpc_gss_wrap: xdr putbytes failed\n");
   1364 		return (FALSE);
   1365 	}
   1366 
   1367 	/*
   1368 	 * create cred field
   1369 	 */
   1370 	if (!marshall_creds(ap, &xdrs, cred_buf_len)) {
   1371 		kmem_free(tmp_buf, xdr_buf_len);
   1372 		RPCGSS_LOG0(1, "rpc_gss_wrap: marshall_creds failed\n");
   1373 		return (FALSE);
   1374 	}
   1375 
   1376 	/*
   1377 	 * create verifier
   1378 	 */
   1379 	if (!marshall_verf(ap, &xdrs, tmp_buf)) {
   1380 		kmem_free(tmp_buf, xdr_buf_len);
   1381 		RPCGSS_LOG0(1, "rpc_gss_wrap: marshall_verf failed\n");
   1382 		return (FALSE);
   1383 	}
   1384 
   1385 	/*
   1386 	 * write out header and destroy temp structures
   1387 	 */
   1388 	if (!XDR_PUTBYTES(out_xdrs, tmp_buf, XDR_GETPOS(&xdrs))) {
   1389 		kmem_free(tmp_buf, xdr_buf_len);
   1390 		RPCGSS_LOG0(1, "rpc_gss_wrap: write out header failed\n");
   1391 		return (FALSE);
   1392 	}
   1393 	XDR_DESTROY(&xdrs);
   1394 	kmem_free(tmp_buf, xdr_buf_len);
   1395 
   1396 	/*
   1397 	 * If context is not established, or if neither integrity
   1398 	 * nor privacy is used, just XDR encode data.
   1399 	 */
   1400 	if (!ap->established || ap->service == rpc_gss_svc_none) {
   1401 		return ((*xdr_func)(out_xdrs, xdr_ptr));
   1402 	}
   1403 
   1404 	return (__rpc_gss_wrap_data(ap->service, ap->qop, ap->context,
   1405 				ap->seq_num, out_xdrs, xdr_func, xdr_ptr));
   1406 }
   1407 
   1408 /*
   1409  * Unwrap received data.
   1410  */
   1411 bool_t
   1412 rpc_gss_unwrap(auth, in_xdrs, xdr_func, xdr_ptr)
   1413 	AUTH			*auth;
   1414 	XDR			*in_xdrs;
   1415 	bool_t			(*xdr_func)();
   1416 	caddr_t			xdr_ptr;
   1417 {
   1418 	rpc_gss_data		*ap = AUTH_PRIVATE(auth);
   1419 
   1420 	/*
   1421 	 * If context is not established, of if neither integrity
   1422 	 * nor privacy is used, just XDR encode data.
   1423 	 */
   1424 	if (!ap->established || ap->service == rpc_gss_svc_none)
   1425 		return ((*xdr_func)(in_xdrs, xdr_ptr));
   1426 
   1427 	return (__rpc_gss_unwrap_data(ap->service,
   1428 				ap->context,
   1429 				ap->seq_num,
   1430 				ap->qop,
   1431 				in_xdrs, xdr_func, xdr_ptr));
   1432 }
   1433 
   1434 /*
   1435  *  Revoke an GSSAPI based security credentials
   1436  *  from the cache table.
   1437  */
   1438 int
   1439 rpc_gss_revauth(uid_t uid, rpc_gss_OID mech)
   1440 {
   1441 	struct ga_cache_entry *next, *prev, *cur;
   1442 	rpc_gss_data *ap;
   1443 	zoneid_t zoneid = getzoneid();
   1444 	int i;
   1445 
   1446 	/*
   1447 	 *  Check the cache table against the uid and the
   1448 	 *  mechanism type.
   1449 	 */
   1450 	rw_enter(&ga_cache_table_lock, RW_WRITER);
   1451 	for (i = 0; i < GSSAUTH_TABLESIZE; i++) {
   1452 		prev = NULL;
   1453 		for (cur = ga_cache_table[i]; cur; cur = next) {
   1454 			NOT_DEAD(cur);
   1455 			next = cur->next;
   1456 			NOT_DEAD(next);
   1457 			ap = AUTH_PRIVATE(cur->auth);
   1458 			if (__rpc_gss_oids_equal(ap->mech_type,
   1459 			    (gss_OID) mech) && (cur->uid == uid) &&
   1460 			    (cur->zoneid == zoneid)) {
   1461 				if (cur->in_use) {
   1462 					RPCGSS_LOG(2, "rpc_gss_revauth:invalid "
   1463 					    "auth %p\n", (void *)cur->auth);
   1464 					ap->invalid = TRUE;
   1465 				} else {
   1466 					RPCGSS_LOG(2, "rpc_gss_revauth:destroy "
   1467 					    "auth %p\n", (void *)cur->auth);
   1468 					rpc_gss_destroy(cur->auth);
   1469 					kmem_cache_free(ga_cache_handle,
   1470 					    (void *)cur);
   1471 				}
   1472 				if (prev == NULL) {
   1473 					ga_cache_table[i] = next;
   1474 				} else {
   1475 					prev->next = next;
   1476 					NOT_DEAD(prev->next);
   1477 				}
   1478 			} else {
   1479 				prev = cur;
   1480 			}
   1481 		}
   1482 	}
   1483 	rw_exit(&ga_cache_table_lock);
   1484 
   1485 	return (0);
   1486 }
   1487 
   1488 
   1489 /*
   1490  *  Delete all the entries indexed by the cache_key.
   1491  *
   1492  *  For example, the cache_key used for NFS is the address of the
   1493  *  security entry for each mount point.  When the file system is unmounted,
   1494  *  all the cache entries indexed by this key should be deleted.
   1495  */
   1496 void
   1497 rpc_gss_secpurge(void *cache_key)
   1498 {
   1499 	struct ga_cache_entry *next, *prev, *cur;
   1500 	int i;
   1501 
   1502 	/*
   1503 	 *  Check the cache table against the cache_key.
   1504 	 */
   1505 	rw_enter(&ga_cache_table_lock, RW_WRITER);
   1506 	for (i = 0; i < GSSAUTH_TABLESIZE; i++) {
   1507 		prev = NULL;
   1508 		for (cur = ga_cache_table[i]; cur; cur = next) {
   1509 			NOT_DEAD(cur);
   1510 			next = cur->next;
   1511 			NOT_DEAD(next);
   1512 			if (cache_key == cur->cache_key) {
   1513 				RPCGSS_LOG(2, "rpc_gss_secpurge: destroy auth "
   1514 				    "%p\n", (void *)cur->auth);
   1515 				if (cur->in_use == FALSE)
   1516 					rpc_gss_destroy(cur->auth);
   1517 				kmem_cache_free(ga_cache_handle, (void *)cur);
   1518 				if (prev == NULL) {
   1519 					ga_cache_table[i] = next;
   1520 				} else {
   1521 					NOT_DEAD(prev->next);
   1522 					prev->next = next;
   1523 				}
   1524 			} else {
   1525 				prev = cur;
   1526 			}
   1527 		}
   1528 	}
   1529 	rw_exit(&ga_cache_table_lock);
   1530 }
   1531 
   1532 /*
   1533  * Function: rpc_gss_nextverf.  Not used.
   1534  */
   1535 static void
   1536 rpc_gss_nextverf()
   1537 {
   1538 }
   1539 
   1540 /*
   1541  * Function: rpc_gss_marshall - no op routine.
   1542  *		rpc_gss_wrap() is doing the marshalling.
   1543  */
   1544 /*ARGSUSED*/
   1545 static bool_t
   1546 rpc_gss_marshall(auth, xdrs)
   1547 	AUTH		*auth;
   1548 	XDR		*xdrs;
   1549 {
   1550 	return (TRUE);
   1551 }
   1552 
   1553 /*
   1554  * Set service defaults.
   1555  * Not supported yet.
   1556  */
   1557 /* ARGSUSED */
   1558 bool_t
   1559 rpc_gss_set_defaults(auth, service, qop)
   1560 	AUTH			*auth;
   1561 	rpc_gss_service_t	service;
   1562 	uint_t			qop;
   1563 {
   1564 	return (FALSE);
   1565 }
   1566 
   1567 /* ARGSUSED */
   1568 int
   1569 rpc_gss_max_data_length(AUTH *rpcgss_handle, int max_tp_unit_len)
   1570 {
   1571 	return (0);
   1572 }
   1573 
   1574 rpc_gss_service_t
   1575 rpc_gss_get_service_type(AUTH *auth)
   1576 {
   1577 	rpc_gss_data		*ap = AUTH_PRIVATE(auth);
   1578 
   1579 	return (ap->service);
   1580 }
   1581