1/*-
2 * Copyright (c) 2008 Doug Rabson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$FreeBSD$
27 */
28/*
29  auth_gss.c
30
31  RPCSEC_GSS client routines.
32
33  Copyright (c) 2000 The Regents of the University of Michigan.
34  All rights reserved.
35
36  Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
37  All rights reserved, all wrongs reversed.
38
39  Redistribution and use in source and binary forms, with or without
40  modification, are permitted provided that the following conditions
41  are met:
42
43  1. Redistributions of source code must retain the above copyright
44     notice, this list of conditions and the following disclaimer.
45  2. Redistributions in binary form must reproduce the above copyright
46     notice, this list of conditions and the following disclaimer in the
47     documentation and/or other materials provided with the distribution.
48  3. Neither the name of the University nor the names of its
49     contributors may be used to endorse or promote products derived
50     from this software without specific prior written permission.
51
52  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
53  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
59  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63
64  $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
65*/
66
67#include <stdio.h>
68#include <stdlib.h>
69#include <unistd.h>
70#include <string.h>
71#include <errno.h>
72#include <netinet/in.h>
73#include <rpc/rpc.h>
74#include <rpc/rpcsec_gss.h>
75#include "rpcsec_gss_int.h"
76
77static void	rpc_gss_nextverf(AUTH*);
78static bool_t	rpc_gss_marshal(AUTH *, XDR *);
79static bool_t	rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
80static bool_t	rpc_gss_refresh(AUTH *, void *);
81static bool_t	rpc_gss_validate(AUTH *, struct opaque_auth *);
82static void	rpc_gss_destroy(AUTH *);
83static void	rpc_gss_destroy_context(AUTH *, bool_t);
84
85static struct auth_ops rpc_gss_ops = {
86	rpc_gss_nextverf,
87	rpc_gss_marshal,
88	rpc_gss_validate,
89	rpc_gss_refresh,
90	rpc_gss_destroy
91};
92
93enum rpcsec_gss_state {
94	RPCSEC_GSS_START,
95	RPCSEC_GSS_CONTEXT,
96	RPCSEC_GSS_ESTABLISHED
97};
98
99struct rpc_gss_data {
100	rpc_gss_options_req_t	gd_options;	/* GSS context options */
101	enum rpcsec_gss_state	gd_state;	/* connection state */
102	gss_buffer_desc		gd_verf;	/* save GSS_S_COMPLETE
103						 * NULL RPC verfier to
104						 * process at end of
105						 * context negotiation */
106	CLIENT			*gd_clnt;	/* client handle */
107	gss_name_t		gd_name;	/* service name */
108	gss_OID			gd_mech;	/* mechanism to use */
109	gss_qop_t		gd_qop;		/* quality of protection */
110	gss_ctx_id_t		gd_ctx;		/* context id */
111	struct rpc_gss_cred	gd_cred;	/* client credentials */
112	u_int			gd_win;		/* sequence window */
113};
114
115#define	AUTH_PRIVATE(auth)	((struct rpc_gss_data *)auth->ah_private)
116
117static struct timeval AUTH_TIMEOUT = { 25, 0 };
118
119AUTH *
120rpc_gss_seccreate(CLIENT *clnt, const char *principal,
121    const char *mechanism, rpc_gss_service_t service, const char *qop,
122    rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
123{
124	AUTH			*auth, *save_auth;
125	rpc_gss_options_ret_t	options;
126	gss_OID			oid;
127	u_int			qop_num;
128	struct rpc_gss_data	*gd;
129	OM_uint32		maj_stat = 0, min_stat = 0;
130	gss_buffer_desc		principal_desc;
131
132	/*
133	 * Bail out now if we don't know this mechanism.
134	 */
135	if (!rpc_gss_mech_to_oid(mechanism, &oid))
136		return (NULL);
137
138	if (qop) {
139		if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
140			return (NULL);
141	} else {
142		qop_num = GSS_C_QOP_DEFAULT;
143	}
144
145	/*
146	 * If the caller doesn't want the options, point at local
147	 * storage to simplify the code below.
148	 */
149	if (!options_ret)
150		options_ret = &options;
151
152	/*
153	 * Default service is integrity.
154	 */
155	if (service == rpc_gss_svc_default)
156		service = rpc_gss_svc_integrity;
157
158	memset(options_ret, 0, sizeof(*options_ret));
159
160	log_debug("in rpc_gss_seccreate()");
161
162	memset(&rpc_createerr, 0, sizeof(rpc_createerr));
163
164	auth = mem_alloc(sizeof(*auth));
165	if (auth == NULL) {
166		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
167		rpc_createerr.cf_error.re_errno = ENOMEM;
168		return (NULL);
169	}
170	gd = mem_alloc(sizeof(*gd));
171	if (gd == NULL) {
172		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
173		rpc_createerr.cf_error.re_errno = ENOMEM;
174		free(auth);
175		return (NULL);
176	}
177
178	auth->ah_ops = &rpc_gss_ops;
179	auth->ah_private = (caddr_t) gd;
180	auth->ah_cred.oa_flavor = RPCSEC_GSS;
181
182	principal_desc.value = (void *)(intptr_t) principal;
183	principal_desc.length = strlen(principal);
184	maj_stat = gss_import_name(&min_stat, &principal_desc,
185	    GSS_C_NT_HOSTBASED_SERVICE, &gd->gd_name);
186	if (maj_stat != GSS_S_COMPLETE) {
187		options_ret->major_status = maj_stat;
188		options_ret->minor_status = min_stat;
189		goto bad;
190	}
191
192	if (options_req) {
193		gd->gd_options = *options_req;
194	} else {
195		gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
196		gd->gd_options.time_req = 0;
197		gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
198		gd->gd_options.input_channel_bindings = NULL;
199	}
200	gd->gd_clnt = clnt;
201	gd->gd_ctx = GSS_C_NO_CONTEXT;
202	gd->gd_mech = oid;
203	gd->gd_qop = qop_num;
204
205	gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
206	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
207	gd->gd_cred.gc_seq = 0;
208	gd->gd_cred.gc_svc = service;
209
210	save_auth = clnt->cl_auth;
211
212	clnt->cl_auth = auth;
213	if (!rpc_gss_init(auth, options_ret)) {
214		clnt->cl_auth = save_auth;
215		goto bad;
216	}
217
218	clnt->cl_auth = save_auth;
219
220	return (auth);
221
222 bad:
223	AUTH_DESTROY(auth);
224	return (NULL);
225}
226
227bool_t
228rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
229{
230	struct rpc_gss_data	*gd;
231	u_int			qop_num;
232	const char		*mechanism;
233
234	gd = AUTH_PRIVATE(auth);
235	if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
236		return (FALSE);
237	}
238
239	if (qop) {
240		if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
241			return (FALSE);
242		}
243	} else {
244		qop_num = GSS_C_QOP_DEFAULT;
245	}
246
247	gd->gd_cred.gc_svc = service;
248	gd->gd_qop = qop_num;
249	return (TRUE);
250}
251
252static void
253rpc_gss_nextverf(__unused AUTH *auth)
254{
255
256	/* not used */
257}
258
259static bool_t
260rpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs)
261{
262
263	/* not used */
264	return (FALSE);
265}
266
267static bool_t
268rpc_gss_validate(AUTH *auth, struct opaque_auth *verf)
269{
270	struct rpc_gss_data	*gd;
271	gss_qop_t		qop_state;
272	uint32_t		num;
273	gss_buffer_desc		signbuf, checksum;
274	OM_uint32		maj_stat, min_stat;
275
276	log_debug("in rpc_gss_validate()");
277
278	gd = AUTH_PRIVATE(auth);
279
280	if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
281		/*
282		 * Save the on the wire verifier to validate last INIT
283		 * phase packet after decode if the major status is
284		 * GSS_S_COMPLETE.
285		 */
286		if (gd->gd_verf.value)
287			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
288			    (char *) &gd->gd_verf);
289		gd->gd_verf.value = mem_alloc(verf->oa_length);
290		if (gd->gd_verf.value == NULL) {
291			fprintf(stderr, "gss_validate: out of memory\n");
292			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
293			return (FALSE);
294		}
295		memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
296		gd->gd_verf.length = verf->oa_length;
297		return (TRUE);
298	}
299
300	num = htonl(gd->gd_cred.gc_seq);
301	signbuf.value = &num;
302	signbuf.length = sizeof(num);
303
304	checksum.value = verf->oa_base;
305	checksum.length = verf->oa_length;
306
307	maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, &signbuf,
308	    &checksum, &qop_state);
309	if (maj_stat != GSS_S_COMPLETE || qop_state != gd->gd_qop) {
310		log_status("gss_verify_mic", gd->gd_mech, maj_stat, min_stat);
311		if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
312			rpc_gss_destroy_context(auth, TRUE);
313		}
314		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
315		return (FALSE);
316	}
317	return (TRUE);
318}
319
320static bool_t
321rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
322{
323	struct rpc_gss_data	*gd;
324	struct rpc_gss_init_res	 gr;
325	gss_buffer_desc		*recv_tokenp, recv_token, send_token;
326	OM_uint32		 maj_stat, min_stat, call_stat;
327	const char		*mech;
328
329	log_debug("in rpc_gss_refresh()");
330
331	gd = AUTH_PRIVATE(auth);
332
333	if (gd->gd_state != RPCSEC_GSS_START)
334		return (TRUE);
335
336	/* GSS context establishment loop. */
337	gd->gd_state = RPCSEC_GSS_CONTEXT;
338	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
339	gd->gd_cred.gc_seq = 0;
340
341	memset(&recv_token, 0, sizeof(recv_token));
342	memset(&gr, 0, sizeof(gr));
343	recv_tokenp = GSS_C_NO_BUFFER;
344
345	for (;;) {
346		maj_stat = gss_init_sec_context(&min_stat,
347		    gd->gd_options.my_cred,
348		    &gd->gd_ctx,
349		    gd->gd_name,
350		    gd->gd_mech,
351		    gd->gd_options.req_flags,
352		    gd->gd_options.time_req,
353		    gd->gd_options.input_channel_bindings,
354		    recv_tokenp,
355		    &gd->gd_mech,	/* used mech */
356		    &send_token,
357		    &options_ret->ret_flags,
358		    &options_ret->time_req);
359
360		/*
361		 * Free the token which we got from the server (if
362		 * any).  Remember that this was allocated by XDR, not
363		 * GSS-API.
364		 */
365		if (recv_tokenp != GSS_C_NO_BUFFER) {
366			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
367			    (char *) &recv_token);
368			recv_tokenp = GSS_C_NO_BUFFER;
369		}
370		if (maj_stat != GSS_S_COMPLETE &&
371		    maj_stat != GSS_S_CONTINUE_NEEDED) {
372			log_status("gss_init_sec_context", gd->gd_mech,
373			    maj_stat, min_stat);
374			options_ret->major_status = maj_stat;
375			options_ret->minor_status = min_stat;
376			break;
377		}
378		if (send_token.length != 0) {
379			memset(&gr, 0, sizeof(gr));
380
381			call_stat = clnt_call(gd->gd_clnt, NULLPROC,
382			    (xdrproc_t)xdr_gss_buffer_desc,
383			    &send_token,
384			    (xdrproc_t)xdr_rpc_gss_init_res,
385			    (caddr_t)&gr, AUTH_TIMEOUT);
386
387			gss_release_buffer(&min_stat, &send_token);
388
389			if (call_stat != RPC_SUCCESS)
390				break;
391
392			if (gr.gr_major != GSS_S_COMPLETE &&
393			    gr.gr_major != GSS_S_CONTINUE_NEEDED) {
394				log_status("server reply", gd->gd_mech,
395				    gr.gr_major, gr.gr_minor);
396				options_ret->major_status = gr.gr_major;
397				options_ret->minor_status = gr.gr_minor;
398				break;
399			}
400
401			/*
402			 * Save the server's gr_handle value, freeing
403			 * what we have already (remember that this
404			 * was allocated by XDR, not GSS-API).
405			 */
406			if (gr.gr_handle.length != 0) {
407				xdr_free((xdrproc_t) xdr_gss_buffer_desc,
408				    (char *) &gd->gd_cred.gc_handle);
409				gd->gd_cred.gc_handle = gr.gr_handle;
410			}
411
412			/*
413			 * Save the server's token as well.
414			 */
415			if (gr.gr_token.length != 0) {
416				recv_token = gr.gr_token;
417				recv_tokenp = &recv_token;
418			}
419
420			/*
421			 * Since we have copied out all the bits of gr
422			 * which XDR allocated for us, we don't need
423			 * to free it.
424			 */
425			gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
426		}
427
428		if (maj_stat == GSS_S_COMPLETE) {
429			gss_buffer_desc   bufin;
430			u_int seq, qop_state = 0;
431
432			/*
433			 * gss header verifier,
434			 * usually checked in gss_validate
435			 */
436			seq = htonl(gr.gr_win);
437			bufin.value = (unsigned char *)&seq;
438			bufin.length = sizeof(seq);
439
440			maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
441			    &bufin, &gd->gd_verf, &qop_state);
442
443			if (maj_stat != GSS_S_COMPLETE ||
444			    qop_state != gd->gd_qop) {
445				log_status("gss_verify_mic", gd->gd_mech,
446				    maj_stat, min_stat);
447				if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
448					rpc_gss_destroy_context(auth, TRUE);
449				}
450				_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
451				    EPERM);
452				options_ret->major_status = maj_stat;
453				options_ret->minor_status = min_stat;
454				return (FALSE);
455			}
456
457			options_ret->major_status = GSS_S_COMPLETE;
458			options_ret->minor_status = 0;
459			options_ret->rpcsec_version = gd->gd_cred.gc_version;
460			options_ret->gss_context = gd->gd_ctx;
461			if (rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
462				strlcpy(options_ret->actual_mechanism,
463				    mech,
464				    sizeof(options_ret->actual_mechanism));
465			}
466
467			gd->gd_state = RPCSEC_GSS_ESTABLISHED;
468			gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
469			gd->gd_cred.gc_seq = 0;
470			gd->gd_win = gr.gr_win;
471			break;
472		}
473	}
474	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
475	    (char *) &gd->gd_verf);
476
477	/* End context negotiation loop. */
478	if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
479		rpc_createerr.cf_stat = RPC_AUTHERROR;
480		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
481		return (FALSE);
482	}
483
484	return (TRUE);
485}
486
487static bool_t
488rpc_gss_refresh(AUTH *auth, void *msg)
489{
490	struct rpc_msg *reply = (struct rpc_msg *) msg;
491	rpc_gss_options_ret_t options;
492
493	/*
494	 * If the error was RPCSEC_GSS_CREDPROBLEM of
495	 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
496	 * other errors are fatal.
497	 */
498	if (reply->rm_reply.rp_stat == MSG_DENIED
499	    && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
500	    && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
501		|| reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
502		rpc_gss_destroy_context(auth, FALSE);
503		memset(&options, 0, sizeof(options));
504		return (rpc_gss_init(auth, &options));
505	}
506
507	return (FALSE);
508}
509
510static void
511rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
512{
513	struct rpc_gss_data	*gd;
514	OM_uint32		 min_stat;
515
516	log_debug("in rpc_gss_destroy_context()");
517
518	gd = AUTH_PRIVATE(auth);
519
520	if (gd->gd_state == RPCSEC_GSS_ESTABLISHED && send_destroy) {
521		gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
522		clnt_call(gd->gd_clnt, NULLPROC,
523		    (xdrproc_t)xdr_void, NULL,
524		    (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
525	}
526
527	/*
528	 * Free the context token. Remember that this was
529	 * allocated by XDR, not GSS-API.
530	 */
531	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
532	    (char *) &gd->gd_cred.gc_handle);
533	gd->gd_cred.gc_handle.length = 0;
534
535	if (gd->gd_ctx != GSS_C_NO_CONTEXT)
536		gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
537
538	gd->gd_state = RPCSEC_GSS_START;
539}
540
541static void
542rpc_gss_destroy(AUTH *auth)
543{
544	struct rpc_gss_data	*gd;
545	OM_uint32		 min_stat;
546
547	log_debug("in rpc_gss_destroy()");
548
549	gd = AUTH_PRIVATE(auth);
550
551	rpc_gss_destroy_context(auth, TRUE);
552
553	if (gd->gd_name != GSS_C_NO_NAME)
554		gss_release_name(&min_stat, &gd->gd_name);
555	if (gd->gd_verf.value)
556		xdr_free((xdrproc_t) xdr_gss_buffer_desc,
557		    (char *) &gd->gd_verf);
558
559	mem_free(gd, sizeof(*gd));
560	mem_free(auth, sizeof(*auth));
561}
562
563bool_t
564__rpc_gss_wrap(AUTH *auth, void *header, size_t headerlen,
565    XDR* xdrs, xdrproc_t xdr_args, void *args_ptr)
566{
567	XDR			 tmpxdrs;
568	char			 credbuf[MAX_AUTH_BYTES];
569	char			 tmpheader[MAX_AUTH_BYTES];
570	struct opaque_auth	 creds, verf;
571	struct rpc_gss_data	*gd;
572	gss_buffer_desc		 rpcbuf, checksum;
573	OM_uint32		 maj_stat, min_stat;
574	bool_t			 xdr_stat;
575
576	log_debug("in rpc_gss_wrap()");
577
578	gd = AUTH_PRIVATE(auth);
579
580	if (gd->gd_state == RPCSEC_GSS_ESTABLISHED)
581		gd->gd_cred.gc_seq++;
582
583	/*
584	 * We need to encode our creds and then put the header and
585	 * creds together in a buffer so that we can create a checksum
586	 * for the verf.
587	 */
588	xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
589	if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gd_cred)) {
590		XDR_DESTROY(&tmpxdrs);
591		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
592		return (FALSE);
593	}
594	creds.oa_flavor = RPCSEC_GSS;
595	creds.oa_base = credbuf;
596	creds.oa_length = XDR_GETPOS(&tmpxdrs);
597	XDR_DESTROY(&tmpxdrs);
598
599	xdrmem_create(&tmpxdrs, tmpheader, sizeof(tmpheader), XDR_ENCODE);
600	if (!XDR_PUTBYTES(&tmpxdrs, header, headerlen) ||
601	    !xdr_opaque_auth(&tmpxdrs, &creds)) {
602		XDR_DESTROY(&tmpxdrs);
603		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
604		return (FALSE);
605	}
606	headerlen = XDR_GETPOS(&tmpxdrs);
607	XDR_DESTROY(&tmpxdrs);
608
609	if (!XDR_PUTBYTES(xdrs, tmpheader, headerlen)) {
610		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
611		return (FALSE);
612	}
613
614	if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
615	    gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
616		if (!xdr_opaque_auth(xdrs, &_null_auth)) {
617			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
618			return (FALSE);
619		}
620	} else {
621		/*
622		 * Checksum serialized RPC header, up to and including
623		 * credential.
624		 */
625		rpcbuf.length = headerlen;
626		rpcbuf.value = tmpheader;
627
628		maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
629		    &rpcbuf, &checksum);
630
631		if (maj_stat != GSS_S_COMPLETE) {
632			log_status("gss_get_mic", gd->gd_mech,
633			    maj_stat, min_stat);
634			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
635				rpc_gss_destroy_context(auth, TRUE);
636			}
637			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
638			return (FALSE);
639		}
640
641		verf.oa_flavor = RPCSEC_GSS;
642		verf.oa_base = checksum.value;
643		verf.oa_length = checksum.length;
644
645		xdr_stat = xdr_opaque_auth(xdrs, &verf);
646		gss_release_buffer(&min_stat, &checksum);
647		if (!xdr_stat) {
648			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
649			return (FALSE);
650		}
651	}
652
653	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
654	    gd->gd_cred.gc_svc == rpc_gss_svc_none) {
655		return (xdr_args(xdrs, args_ptr));
656	}
657	return (xdr_rpc_gss_wrap_data(xdrs, xdr_args, args_ptr,
658		gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
659		gd->gd_cred.gc_seq));
660}
661
662bool_t
663__rpc_gss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, void *xdr_ptr)
664{
665	struct rpc_gss_data	*gd;
666
667	log_debug("in rpc_gss_unwrap()");
668
669	gd = AUTH_PRIVATE(auth);
670
671	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
672	    gd->gd_cred.gc_svc == rpc_gss_svc_none) {
673		return (xdr_func(xdrs, xdr_ptr));
674	}
675	return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
676		gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
677		gd->gd_cred.gc_seq));
678}
679
680int
681rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
682{
683	struct rpc_gss_data	*gd;
684	int			want_conf;
685	OM_uint32		max;
686	OM_uint32		maj_stat, min_stat;
687	int			result;
688
689	gd = AUTH_PRIVATE(auth);
690
691	switch (gd->gd_cred.gc_svc) {
692	case rpc_gss_svc_none:
693		return (max_tp_unit_len);
694		break;
695
696	case rpc_gss_svc_default:
697	case rpc_gss_svc_integrity:
698		want_conf = FALSE;
699		break;
700
701	case rpc_gss_svc_privacy:
702		want_conf = TRUE;
703		break;
704
705	default:
706		return (0);
707	}
708
709	maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
710	    gd->gd_qop, max_tp_unit_len, &max);
711
712	if (maj_stat == GSS_S_COMPLETE) {
713		result = (int) max;
714		if (result < 0)
715			result = 0;
716		return (result);
717	} else {
718		log_status("gss_wrap_size_limit", gd->gd_mech,
719		    maj_stat, min_stat);
720		return (0);
721	}
722}
723