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