xref: /titanic_52/usr/src/lib/rpcsec_gss/rpcsec_gss.c (revision 9512fe850e98fdd448c638ca63fdd92a8a510255)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1986-1995, 1997, 2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 
30 /*
31  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
32  *
33  * $Header:
34  * /afs/gza.com/product/secure/rel-eng/src/1.1/rpc/RCS/auth_gssapi.c,v
35  * 1.14 1995/03/22 22:07:55 jik Exp $
36  */
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <strings.h>
41 #include <errno.h>
42 #include <pthread.h>
43 #include <thread.h>
44 #include <syslog.h>
45 #include <gssapi/gssapi.h>
46 #include <rpc/rpc.h>
47 #include <rpc/rpcsec_defs.h>
48 
49 static void 	rpc_gss_nextverf();
50 static bool_t 	rpc_gss_marshall();
51 static bool_t	rpc_gss_validate();
52 static bool_t	rpc_gss_refresh();
53 static void	rpc_gss_destroy();
54 static void	rpc_gss_destroy_pvt();
55 static bool_t	rpc_gss_seccreate_pvt();
56 static bool_t	validate_seqwin();
57 
58 /*
59  * Globals that should have header files but don't.
60  */
61 extern bool_t	xdr_opaque_auth(XDR *, struct opaque_auth *);
62 extern int	_thr_main(void);
63 extern int	_thr_getspecific(thread_key_t key, void **valuep);
64 typedef void	(*PFrV) (void *);
65 extern int	_thr_keycreate(thread_key_t *pkey, PFrV destructor);
66 extern int	_thr_setspecific(unsigned int key, void *value);
67 
68 
69 static struct auth_ops rpc_gss_ops = {
70 	rpc_gss_nextverf,
71 	rpc_gss_marshall,
72 	rpc_gss_validate,
73 	rpc_gss_refresh,
74 	rpc_gss_destroy
75 };
76 
77 /*
78  * Private data for RPCSEC_GSS.
79  */
80 typedef struct _rpc_gss_data {
81 	bool_t			established;	/* TRUE when established */
82 	CLIENT			*clnt;		/* associated client handle */
83 	uint_t			version;	/* RPCSEC version */
84 	gss_ctx_id_t		context;	/* GSS context id */
85 	gss_buffer_desc		ctx_handle;	/* RPCSEC context handle */
86 	uint_t			seq_num;	/* last sequence number rcvd */
87 	gss_cred_id_t		my_cred;	/* GSS credentials */
88 	OM_uint32		qop;		/* requested QOP */
89 	rpc_gss_service_t	service;	/* requested service */
90 	uint_t			gss_proc;	/* GSS control procedure */
91 	gss_name_t		target_name;	/* target server */
92 	int			req_flags;	/* GSS request bits */
93 	gss_OID			mech_type;	/* GSS mechanism */
94 	OM_uint32		time_req;	/* requested cred lifetime */
95 	bool_t			invalid;	/* can't use this any more */
96 	OM_uint32		seq_window;	/* server sequence window */
97 	struct opaque_auth	*verifier;  /* rpc reply verifier saved for */
98 					    /* validating the sequence window */
99 	gss_channel_bindings_t	icb;
100 } rpc_gss_data;
101 #define	AUTH_PRIVATE(auth) ((rpc_gss_data *)auth->ah_private)
102 
103 /*
104  * Create a context.
105  */
106 AUTH *
107 __rpc_gss_seccreate(clnt, server_name, mech, service, qop, options_req,
108 								options_ret)
109 	CLIENT			*clnt;		/* associated client handle */
110 	char			*server_name;	/* target server */
111 	char			*mech;		/* security mechanism */
112 	rpc_gss_service_t	service;	/* security service */
113 	char			*qop;		/* requested QOP */
114 	rpc_gss_options_req_t	*options_req;	/* requested options */
115 	rpc_gss_options_ret_t	*options_ret;	/* returned options */
116 {
117 	OM_uint32		gssstat;
118 	OM_uint32		minor_stat;
119 	gss_name_t		target_name;
120 	gss_OID			mech_type;
121 	OM_uint32		ret_flags;
122 	OM_uint32		time_rec;
123 	gss_buffer_desc		input_name;
124 	AUTH			*auth = NULL;
125 	rpc_gss_data		*ap = NULL;
126 	OM_uint32		qop_num;
127 	rpc_gss_error_t		error;
128 	void			__rpc_gss_get_error();
129 
130 	/*
131 	 * convert ascii strings to GSS values
132 	 */
133 	if (!__rpc_gss_qop_to_num(qop, mech, &qop_num)) {
134 		__rpc_gss_get_error(&error);
135 		return (NULL);
136 	}
137 
138 	if (!__rpc_gss_mech_to_oid(mech, &mech_type)) {
139 		__rpc_gss_get_error(&error);
140 		return (NULL);
141 	}
142 
143 	/*
144 	 * convert name to GSS internal type
145 	 */
146 	input_name.value = server_name;
147 	input_name.length = strlen(server_name);
148 	gssstat = gss_import_name(&minor_stat, &input_name,
149 				(gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
150 				&target_name);
151 	if (gssstat != GSS_S_COMPLETE) {
152 		rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
153 		rpc_gss_err.system_error = ENOMEM;
154 		return (NULL);
155 	}
156 
157 	/*
158 	 * Create AUTH handle.  Save the necessary interface information
159 	 * so that the client can refresh the handle later if needed.
160 	 */
161 	if ((auth = (AUTH *) malloc(sizeof (*auth))) != NULL)
162 		ap = (rpc_gss_data *) malloc(sizeof (*ap));
163 	if (auth == NULL || ap == NULL) {
164 		rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
165 		rpc_gss_err.system_error = ENOMEM;
166 		if (auth != NULL)
167 			free((char *)auth);
168 		(void) gss_release_name(&minor_stat, &target_name);
169 		return (NULL);
170 	}
171 
172 	memset((char *)ap, 0, sizeof (*ap));
173 	ap->clnt = clnt;
174 	ap->version = RPCSEC_GSS_VERSION;
175 	if (options_req != NULL) {
176 		ap->my_cred = options_req->my_cred;
177 		ap->req_flags = options_req->req_flags;
178 		ap->time_req = options_req->time_req;
179 		ap->icb = options_req->input_channel_bindings;
180 	} else {
181 		ap->my_cred = GSS_C_NO_CREDENTIAL;
182 		ap->req_flags = GSS_C_MUTUAL_FLAG;
183 		ap->time_req = 0;
184 		ap->icb = NULL;
185 	}
186 	if ((ap->service = service) == rpc_gss_svc_default)
187 		ap->service = rpc_gss_svc_integrity;
188 	ap->qop = qop_num;
189 	ap->target_name = target_name;
190 	ap->mech_type = mech_type;
191 
192 	/*
193 	 * Now invoke the real interface that sets up the context from
194 	 * the information stashed away in the private data.
195 	 */
196 	if (!rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth, ap,
197 				&mech_type, &ret_flags, &time_rec)) {
198 		if (ap->target_name)
199 			(void) gss_release_name(&minor_stat, &ap->target_name);
200 		free((char *)ap);
201 		free((char *)auth);
202 		return (NULL);
203 	}
204 
205 	/*
206 	 * Make sure that the requested service is supported.  In all
207 	 * cases, integrity service must be available.
208 	 */
209 	if ((ap->service == rpc_gss_svc_privacy &&
210 					!(ret_flags & GSS_C_CONF_FLAG)) ||
211 			!(ret_flags & GSS_C_INTEG_FLAG)) {
212 		rpc_gss_destroy(auth);
213 		rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
214 		rpc_gss_err.system_error = EPROTONOSUPPORT;
215 		return (NULL);
216 	}
217 
218 	/*
219 	 * return option values if requested
220 	 */
221 	if (options_ret != NULL) {
222 		char	*s;
223 
224 		options_ret->major_status = gssstat;
225 		options_ret->minor_status = minor_stat;
226 		options_ret->rpcsec_version = ap->version;
227 		options_ret->ret_flags = ret_flags;
228 		options_ret->time_ret = time_rec;
229 		options_ret->gss_context = ap->context;
230 		if ((s = __rpc_gss_oid_to_mech(mech_type)) != NULL)
231 			strcpy(options_ret->actual_mechanism, s);
232 		else
233 			options_ret->actual_mechanism[0] = '\0';
234 	}
235 	return (auth);
236 }
237 
238 /*
239  * Private interface to create a context.  This is the interface
240  * that's invoked when the context has to be refreshed.
241  */
242 static bool_t
243 rpc_gss_seccreate_pvt(gssstat, minor_stat, auth, ap, actual_mech_type,
244 						ret_flags, time_rec)
245 	OM_uint32		*gssstat;
246 	OM_uint32		*minor_stat;
247 	AUTH			*auth;
248 	rpc_gss_data		*ap;
249 	gss_OID			*actual_mech_type;
250 	OM_uint32		*ret_flags;
251 	OM_uint32		*time_rec;
252 {
253 	CLIENT			*clnt = ap->clnt;
254 	AUTH			*save_auth;
255 	enum clnt_stat		callstat;
256 	rpc_gss_init_arg	call_arg;
257 	rpc_gss_init_res	call_res;
258 	gss_buffer_desc		*input_token_p, input_token;
259 	bool_t			free_results = FALSE;
260 
261 	/*
262 	 * initialize error
263 	 */
264 	memset(&rpc_createerr, 0, sizeof (rpc_createerr));
265 
266 	/*
267 	 * (re)initialize AUTH handle and private data.
268 	 */
269 	memset((char *)auth, 0, sizeof (*auth));
270 	auth->ah_ops = &rpc_gss_ops;
271 	auth->ah_private = (caddr_t)ap;
272 	auth->ah_cred.oa_flavor = RPCSEC_GSS;
273 
274 	ap->established = FALSE;
275 	ap->ctx_handle.length = 0;
276 	ap->ctx_handle.value = NULL;
277 	ap->context = GSS_C_NO_CONTEXT;
278 	ap->seq_num = 0;
279 	ap->gss_proc = RPCSEC_GSS_INIT;
280 
281 	/*
282 	 * should not change clnt->cl_auth at this time, so save
283 	 * old handle
284 	 */
285 	save_auth = clnt->cl_auth;
286 	clnt->cl_auth = auth;
287 
288 	/*
289 	 * set state for starting context setup
290 	 */
291 	input_token_p = GSS_C_NO_BUFFER;
292 
293 next_token:
294 	*gssstat = gss_init_sec_context(minor_stat,
295 					ap->my_cred,
296 					&ap->context,
297 					ap->target_name,
298 					ap->mech_type,
299 					ap->req_flags,
300 					ap->time_req,
301 					NULL,
302 					input_token_p,
303 					actual_mech_type,
304 					&call_arg,
305 					ret_flags,
306 					time_rec);
307 
308 	if (input_token_p != GSS_C_NO_BUFFER) {
309 		OM_uint32 minor_stat2;
310 
311 		(void) gss_release_buffer(&minor_stat2, input_token_p);
312 		input_token_p = GSS_C_NO_BUFFER;
313 	}
314 
315 	if (*gssstat != GSS_S_COMPLETE && *gssstat != GSS_S_CONTINUE_NEEDED) {
316 
317 		goto cleanup;
318 	}
319 
320 	/*
321 	 * if we got a token, pass it on
322 	 */
323 	if (call_arg.length != 0) {
324 		struct timeval timeout = {30, 0};
325 
326 		memset((char *)&call_res, 0, sizeof (call_res));
327 		callstat = clnt_call(clnt, NULLPROC,
328 				__xdr_rpc_gss_init_arg, (caddr_t)&call_arg,
329 				__xdr_rpc_gss_init_res, (caddr_t)&call_res,
330 				timeout);
331 		(void) gss_release_buffer(minor_stat, &call_arg);
332 
333 		if (callstat != RPC_SUCCESS) {
334 			goto cleanup;
335 		}
336 		/*
337 		 * we have results - note that these need to be freed
338 		 */
339 		free_results = TRUE;
340 
341 		if (call_res.gss_major != GSS_S_COMPLETE &&
342 			call_res.gss_major != GSS_S_CONTINUE_NEEDED)
343 			goto cleanup;
344 
345 		ap->gss_proc = RPCSEC_GSS_CONTINUE_INIT;
346 
347 		/*
348 		 * check for ctx_handle
349 		 */
350 		if (ap->ctx_handle.length == 0) {
351 			if (call_res.ctx_handle.length == 0)
352 				goto cleanup;
353 			GSS_DUP_BUFFER(ap->ctx_handle,
354 				call_res.ctx_handle);
355 		} else if (!GSS_BUFFERS_EQUAL(ap->ctx_handle,
356 						call_res.ctx_handle))
357 			goto cleanup;
358 
359 		/*
360 		 * check for token
361 		 */
362 		if (call_res.token.length != 0) {
363 			if (*gssstat == GSS_S_COMPLETE)
364 				goto cleanup;
365 			GSS_DUP_BUFFER(input_token, call_res.token);
366 			input_token_p = &input_token;
367 
368 		} else if (*gssstat != GSS_S_COMPLETE)
369 			goto cleanup;
370 
371 		/* save the sequence window value; validate later */
372 		ap->seq_window = call_res.seq_window;
373 		xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res);
374 		free_results = FALSE;
375 	}
376 
377 	/*
378 	 * results were okay.. continue if necessary
379 	 */
380 	if (*gssstat == GSS_S_CONTINUE_NEEDED)
381 		goto next_token;
382 
383 	/*
384 	 * Validate the sequence window - RFC 2203 section 5.2.3.1
385 	 */
386 	if (!validate_seqwin(ap)) {
387 		goto cleanup;
388 	}
389 
390 	/*
391 	 * Done!  Security context creation is successful.
392 	 * Ready for exchanging data.
393 	 */
394 	ap->established = TRUE;
395 	ap->seq_num = 1;
396 	ap->gss_proc = RPCSEC_GSS_DATA;
397 	ap->invalid = FALSE;
398 
399 	clnt->cl_auth = save_auth;	/* restore cl_auth */
400 	return (TRUE);
401 
402 cleanup:
403 	if (ap->context != GSS_C_NO_CONTEXT)
404 		rpc_gss_destroy_pvt(auth);
405 	if (free_results)
406 		xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res);
407 	clnt->cl_auth = save_auth;	/* restore cl_auth */
408 
409 /*
410  *	if (rpc_createerr.cf_stat == 0)
411  *		rpc_createerr.cf_stat = RPC_AUTHERROR;
412  */
413 	if (rpc_createerr.cf_stat == 0) {
414 		rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
415 		rpc_gss_err.system_error = RPC_AUTHERROR;
416 	}
417 
418 	return (FALSE);
419 }
420 
421 /*
422  * Set service defaults.
423  */
424 bool_t
425 __rpc_gss_set_defaults(auth, service, qop)
426 	AUTH			*auth;
427 	rpc_gss_service_t	service;
428 	char			*qop;
429 {
430 	/*LINTED*/
431 	rpc_gss_data		*ap = AUTH_PRIVATE(auth);
432 	char			*mech;
433 	OM_uint32		qop_num;
434 
435 	switch (service) {
436 	case rpc_gss_svc_integrity:
437 	case rpc_gss_svc_privacy:
438 	case rpc_gss_svc_none:
439 		break;
440 	case rpc_gss_svc_default:
441 		service = rpc_gss_svc_integrity;
442 		break;
443 	default:
444 		return (FALSE);
445 	}
446 
447 	if ((mech = __rpc_gss_oid_to_mech(ap->mech_type)) == NULL)
448 		return (FALSE);
449 
450 	if (!__rpc_gss_qop_to_num(qop, mech, &qop_num))
451 		return (FALSE);
452 
453 	ap->qop = qop_num;
454 	ap->service = service;
455 	return (TRUE);
456 }
457 
458 /*
459  * Marshall credentials.
460  */
461 static bool_t
462 marshall_creds(ap, xdrs)
463 	rpc_gss_data		*ap;
464 	XDR			*xdrs;
465 {
466 	rpc_gss_creds		ag_creds;
467 	char			cred_buf[MAX_AUTH_BYTES];
468 	struct opaque_auth	creds;
469 	XDR			cred_xdrs;
470 
471 	ag_creds.version = ap->version;
472 	ag_creds.gss_proc = ap->gss_proc;
473 	ag_creds.seq_num = ap->seq_num;
474 	ag_creds.service = ap->service;
475 
476 	/*
477 	 * If context has not been set up yet, use NULL handle.
478 	 */
479 	if (ap->ctx_handle.length > 0)
480 		ag_creds.ctx_handle = ap->ctx_handle;
481 	else {
482 		ag_creds.ctx_handle.length = 0;
483 		ag_creds.ctx_handle.value = NULL;
484 	}
485 
486 	xdrmem_create(&cred_xdrs, (caddr_t)cred_buf, MAX_AUTH_BYTES,
487 								XDR_ENCODE);
488 	if (!__xdr_rpc_gss_creds(&cred_xdrs, &ag_creds)) {
489 		XDR_DESTROY(&cred_xdrs);
490 		return (FALSE);
491 	}
492 
493 	creds.oa_flavor = RPCSEC_GSS;
494 	creds.oa_base = cred_buf;
495 	creds.oa_length = xdr_getpos(&cred_xdrs);
496 	XDR_DESTROY(&cred_xdrs);
497 
498 	if (!xdr_opaque_auth(xdrs, &creds))
499 		return (FALSE);
500 
501 	return (TRUE);
502 }
503 
504 /*
505  * Marshall verifier.  The verifier is the checksum of the RPC header
506  * up to and including the credential field.  The XDR handle that's
507  * passed in has the header up to and including the credential field
508  * encoded.  A pointer to the transmit buffer is also passed in.
509  */
510 static bool_t
511 marshall_verf(ap, xdrs, buf)
512 	rpc_gss_data		*ap;
513 	XDR			*xdrs;	/* send XDR */
514 	char			*buf;	/* pointer of send buffer */
515 {
516 	struct opaque_auth	verf;
517 	OM_uint32		major, minor;
518 	gss_buffer_desc		in_buf, out_buf;
519 	bool_t			ret = FALSE;
520 
521 	/*
522 	 * If context is not established yet, use NULL verifier.
523 	 */
524 	if (!ap->established) {
525 		verf.oa_flavor = AUTH_NONE;
526 		verf.oa_base = NULL;
527 		verf.oa_length = 0;
528 		return (xdr_opaque_auth(xdrs, &verf));
529 	}
530 
531 	verf.oa_flavor = RPCSEC_GSS;
532 	in_buf.length = xdr_getpos(xdrs);
533 	in_buf.value = buf;
534 	if ((major = gss_sign(&minor, ap->context, ap->qop, &in_buf,
535 					&out_buf)) != GSS_S_COMPLETE) {
536 		if (major == GSS_S_CONTEXT_EXPIRED) {
537 			ap->invalid = TRUE;
538 		}
539 		return (FALSE);
540 	}
541 	verf.oa_base = out_buf.value;
542 	verf.oa_length = out_buf.length;
543 	ret = xdr_opaque_auth(xdrs, &verf);
544 	(void) gss_release_buffer(&minor, &out_buf);
545 
546 	return (ret);
547 }
548 
549 /*
550  * Function: rpc_gss_nextverf.  Not used.
551  */
552 static void
553 rpc_gss_nextverf()
554 {
555 }
556 
557 /*
558  * Function: rpc_gss_marshall - not used.
559  */
560 static bool_t
561 rpc_gss_marshall(auth, xdrs)
562 	AUTH		*auth;
563 	XDR		*xdrs;
564 {
565 	if (!xdr_opaque_auth(xdrs, &auth->ah_cred) ||
566 				!xdr_opaque_auth(xdrs, &auth->ah_verf))
567 		return (FALSE);
568 	return (TRUE);
569 }
570 
571 /*
572  * Validate sequence window upon a successful RPCSEC_GSS INIT session.
573  * The sequence window sent back by the server should be verifiable by
574  * the verifier which is a checksum of the sequence window.
575  */
576 static bool_t
577 validate_seqwin(rpc_gss_data *ap)
578 {
579 	uint_t			seq_win_net;
580 	OM_uint32		major = 0, minor = 0;
581 	gss_buffer_desc		msg_buf, tok_buf;
582 	int			qop_state = 0;
583 
584 	seq_win_net = (uint_t)htonl(ap->seq_window);
585 	msg_buf.length = sizeof (seq_win_net);
586 	msg_buf.value = (char *)&seq_win_net;
587 	tok_buf.length = ap->verifier->oa_length;
588 	tok_buf.value = ap->verifier->oa_base;
589 	major = gss_verify(&minor, ap->context, &msg_buf, &tok_buf, &qop_state);
590 	if (major != GSS_S_COMPLETE)
591 	    return (FALSE);
592 	return (TRUE);
593 }
594 
595 /*
596  * Validate RPC response verifier from server.  The response verifier
597  * is the checksum of the request sequence number.
598  */
599 static bool_t
600 rpc_gss_validate(auth, verf)
601 	AUTH			*auth;
602 	struct opaque_auth	*verf;
603 {
604 	/*LINTED*/
605 	rpc_gss_data		*ap = AUTH_PRIVATE(auth);
606 	uint_t			seq_num_net;
607 	OM_uint32		major, minor;
608 	gss_buffer_desc		msg_buf, tok_buf;
609 	int			qop_state;
610 
611 	/*
612 	 * If context is not established yet, save the verifier for
613 	 * validating the sequence window later at the end of context
614 	 * creation session.
615 	 */
616 	if (!ap->established) {
617 	    if (ap->verifier == NULL) {
618 		ap->verifier = malloc(sizeof (struct opaque_auth));
619 		memset(ap->verifier, 0, sizeof (struct opaque_auth));
620 		if (verf->oa_length > 0)
621 		    ap->verifier->oa_base = malloc(verf->oa_length);
622 	    } else {
623 		if (ap->verifier->oa_length > 0)
624 		    free(ap->verifier->oa_base);
625 		if (verf->oa_length > 0)
626 		    ap->verifier->oa_base = malloc(verf->oa_length);
627 	    }
628 	    ap->verifier->oa_length = verf->oa_length;
629 	    bcopy(verf->oa_base, ap->verifier->oa_base, verf->oa_length);
630 	    return (TRUE);
631 	}
632 
633 	seq_num_net = (uint_t)htonl(ap->seq_num);
634 	msg_buf.length = sizeof (seq_num_net);
635 	msg_buf.value = (char *)&seq_num_net;
636 	tok_buf.length = verf->oa_length;
637 	tok_buf.value = verf->oa_base;
638 	major = gss_verify(&minor, ap->context, &msg_buf, &tok_buf, &qop_state);
639 	if (major != GSS_S_COMPLETE)
640 		return (FALSE);
641 	return (TRUE);
642 }
643 
644 /*
645  * Refresh client context.  This is necessary sometimes because the
646  * server will ocassionally destroy contexts based on LRU method, or
647  * because of expired credentials.
648  */
649 static bool_t
650 rpc_gss_refresh(auth, msg)
651 	AUTH		*auth;
652 	struct rpc_msg	*msg;
653 {
654 	/*LINTED*/
655 	rpc_gss_data	*ap = AUTH_PRIVATE(auth);
656 	OM_uint32	gssstat, minor_stat;
657 
658 	/*
659 	 * The context needs to be recreated only when the error status
660 	 * returned from the server is one of the following:
661 	 *	RPCSEC_GSS_NOCRED and RPCSEC_GSS_FAILED
662 	 * The existing context should not be destroyed unless the above
663 	 * error status codes are received or if the context has not
664 	 * been set up.
665 	 */
666 
667 	if (msg->rjcted_rply.rj_why == RPCSEC_GSS_NOCRED ||
668 			msg->rjcted_rply.rj_why == RPCSEC_GSS_FAILED ||
669 							!ap->established) {
670 		/*
671 		 * Destroy the context if necessary.  Use the same memory
672 		 * for the new context since we've already passed a pointer
673 		 * to it to the user.
674 		 */
675 		if (ap->context != GSS_C_NO_CONTEXT) {
676 			(void) gss_delete_sec_context(&minor_stat, &ap->context,
677 								NULL);
678 			ap->context = GSS_C_NO_CONTEXT;
679 		}
680 		if (ap->ctx_handle.length != 0) {
681 			(void) gss_release_buffer(&minor_stat,
682 							&ap->ctx_handle);
683 			ap->ctx_handle.length = 0;
684 			ap->ctx_handle.value = NULL;
685 		}
686 
687 		/*
688 		 * If the context was not already established, don't try to
689 		 * recreate it.
690 		 */
691 		if (!ap->established) {
692 			ap->invalid = TRUE;
693 			return (FALSE);
694 		}
695 
696 		/*
697 		 * Recreate context.
698 		 */
699 		if (rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth, ap,
700 		    (gss_OID *)0, (OM_uint32 *)0, (OM_uint32 *)0))
701 			return (TRUE);
702 		else {
703 			ap->invalid = TRUE;
704 			return (FALSE);
705 		}
706 	}
707 	return (FALSE);
708 }
709 
710 /*
711  * Destroy a context.
712  */
713 static void
714 rpc_gss_destroy(auth)
715 	AUTH		*auth;
716 {
717 	/*LINTED*/
718 	rpc_gss_data	*ap = AUTH_PRIVATE(auth);
719 
720 	rpc_gss_destroy_pvt(auth);
721 	free((char *)ap);
722 	free(auth);
723 }
724 
725 /*
726  * Private interface to destroy a context without freeing up
727  * the memory used by it.  We need to do this when a refresh
728  * fails, for example, so the user will still have a handle.
729  */
730 static void
731 rpc_gss_destroy_pvt(auth)
732 	AUTH		*auth;
733 {
734 	struct timeval	timeout;
735 	OM_uint32	minor_stat;
736 	/*LINTED*/
737 	rpc_gss_data	*ap = AUTH_PRIVATE(auth);
738 
739 	/*
740 	 * If we have a server context id, inform server that we are
741 	 * destroying the context.
742 	 */
743 	if (ap->ctx_handle.length != 0) {
744 		ap->gss_proc = RPCSEC_GSS_DESTROY;
745 		timeout.tv_sec = 1;
746 		timeout.tv_usec = 0;
747 		(void) clnt_call(ap->clnt, NULLPROC, xdr_void, NULL,
748 						xdr_void, NULL, timeout);
749 
750 		(void) gss_release_buffer(&minor_stat, &ap->ctx_handle);
751 		ap->ctx_handle.length = 0;
752 		ap->ctx_handle.value = NULL;
753 	}
754 
755 	/*
756 	 * Destroy local GSS context.
757 	 */
758 	if (ap->context != GSS_C_NO_CONTEXT) {
759 		(void) gss_delete_sec_context(&minor_stat, &ap->context, NULL);
760 		ap->context = GSS_C_NO_CONTEXT;
761 	}
762 
763 	/*
764 	 * Looks like we need to release default credentials if we use it.
765 	 * Non-default creds need to be released by user.
766 	 */
767 	if (ap->my_cred == GSS_C_NO_CREDENTIAL)
768 		(void) gss_release_cred(&minor_stat, &ap->my_cred);
769 
770 	/*
771 	 * Release any internal name structures.
772 	 */
773 	if (ap->target_name != NULL) {
774 		(void) gss_release_name(&minor_stat, &ap->target_name);
775 		ap->target_name = NULL;
776 	}
777 
778 	/*
779 	 * Free the verifier saved for sequence window checking.
780 	 */
781 	if (ap->verifier != NULL) {
782 	    if (ap->verifier->oa_length > 0)
783 		free(ap->verifier->oa_base);
784 	    free(ap->verifier);
785 	    ap->verifier = NULL;
786 	}
787 }
788 
789 /*
790  * Wrap client side data.  The encoded header is passed in through
791  * buf and buflen.  The header is up to but not including the
792  * credential field.
793  */
794 bool_t
795 __rpc_gss_wrap(auth, buf, buflen, out_xdrs, xdr_func, xdr_ptr)
796 	AUTH			*auth;
797 	char			*buf;		/* encoded header */
798 	uint_t			buflen;		/* encoded header length */
799 	XDR			*out_xdrs;
800 	bool_t			(*xdr_func)();
801 	caddr_t			xdr_ptr;
802 {
803 	/*LINTED*/
804 	rpc_gss_data		*ap = AUTH_PRIVATE(auth);
805 	XDR			xdrs;
806 	char			tmp_buf[512];
807 
808 
809 	/*
810 	 * Reject an invalid context.
811 	 */
812 	if (ap->invalid)
813 		return (FALSE);
814 
815 	/*
816 	 * If context is established, bump up sequence number.
817 	 */
818 	if (ap->established)
819 		ap->seq_num++;
820 
821 	/*
822 	 * Create the header in a temporary XDR context and buffer
823 	 * before putting it out.
824 	 */
825 	xdrmem_create(&xdrs, tmp_buf, sizeof (tmp_buf), XDR_ENCODE);
826 	if (!XDR_PUTBYTES(&xdrs, buf, buflen))
827 		return (FALSE);
828 
829 	/*
830 	 * create cred field
831 	 */
832 	if (!marshall_creds(ap, &xdrs))
833 		return (FALSE);
834 
835 	/*
836 	 * create verifier
837 	 */
838 	if (!marshall_verf(ap, &xdrs, tmp_buf))
839 		return (FALSE);
840 
841 	/*
842 	 * write out header and destroy temp structures
843 	 */
844 	if (!XDR_PUTBYTES(out_xdrs, tmp_buf, XDR_GETPOS(&xdrs)))
845 		return (FALSE);
846 	XDR_DESTROY(&xdrs);
847 
848 	/*
849 	 * If context is not established, or if neither integrity
850 	 * nor privacy is used, just XDR encode data.
851 	 */
852 	if (!ap->established || ap->service == rpc_gss_svc_none)
853 		return ((*xdr_func)(out_xdrs, xdr_ptr));
854 
855 	return (__rpc_gss_wrap_data(ap->service, ap->qop, ap->context,
856 				ap->seq_num, out_xdrs, xdr_func, xdr_ptr));
857 }
858 
859 /*
860  * Unwrap received data.
861  */
862 bool_t
863 __rpc_gss_unwrap(auth, in_xdrs, xdr_func, xdr_ptr)
864 	AUTH			*auth;
865 	XDR			*in_xdrs;
866 	bool_t			(*xdr_func)();
867 	caddr_t			xdr_ptr;
868 {
869 	/*LINTED*/
870 	rpc_gss_data		*ap = AUTH_PRIVATE(auth);
871 
872 	/*
873 	 * If context is not established, of if neither integrity
874 	 * nor privacy is used, just XDR encode data.
875 	 */
876 	if (!ap->established || ap->service == rpc_gss_svc_none)
877 		return ((*xdr_func)(in_xdrs, xdr_ptr));
878 
879 	return (__rpc_gss_unwrap_data(ap->service,
880 				ap->context,
881 				ap->seq_num,
882 				ap->qop,
883 				in_xdrs, xdr_func, xdr_ptr));
884 }
885 
886 int
887 __rpc_gss_max_data_length(auth, max_tp_unit_len)
888 	AUTH		*auth;
889 	int		max_tp_unit_len;
890 {
891 	/*LINTED*/
892 	rpc_gss_data		*ap = AUTH_PRIVATE(auth);
893 
894 	if (!ap->established || max_tp_unit_len <= 0)
895 		return (0);
896 
897 	return (__find_max_data_length(ap->service,
898 			ap->context,
899 			ap->qop,
900 			max_tp_unit_len));
901 }
902 
903 #undef  rpc_gss_err
904 
905 rpc_gss_error_t	rpc_gss_err;
906 static mutex_t	rge_lock;		/* protects TSD key creation */
907 
908 rpc_gss_error_t *
909 __rpc_gss_err()
910 {
911 	static thread_key_t rpc_gss_err_key = 0;
912 	rpc_gss_error_t *tsd = 0;
913 
914 	if (_thr_main())
915 		return (&rpc_gss_err);
916 	if (_thr_getspecific(rpc_gss_err_key, (void **) &tsd) != 0) {
917 		mutex_lock(&rge_lock);
918 		if (_thr_keycreate(&rpc_gss_err_key, free) != 0) {
919 			mutex_unlock(&rge_lock);
920 			return (&rpc_gss_err);
921 		}
922 		mutex_unlock(&rge_lock);
923 	}
924 	if (!tsd) {
925 		tsd = (rpc_gss_error_t *)
926 			calloc(1, sizeof (rpc_gss_error_t));
927 		if (_thr_setspecific(rpc_gss_err_key, (void *) tsd) != 0) {
928 			if (tsd)
929 				free(tsd);
930 			return (&rpc_gss_err);
931 		}
932 		memset(tsd, 0, sizeof (rpc_gss_error_t));
933 		return (tsd);
934 	}
935 	return (tsd);
936 }
937 
938 void
939 __rpc_gss_get_error(error)
940 	rpc_gss_error_t	*error;
941 {
942 
943 	error->rpc_gss_error = rpc_gss_err.rpc_gss_error;
944 	error->system_error = rpc_gss_err.system_error;
945 }
946