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