xref: /illumos-gate/usr/src/uts/common/rpc/sec_gss/rpcsec_gss.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
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