xref: /freebsd/lib/librpcsec_gss/rpcsec_gss.c (revision 315ee00f)
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 /*
27   SPDX-License-Identifier: BSD-3-Clause
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 
77 static void	rpc_gss_nextverf(AUTH*);
78 static bool_t	rpc_gss_marshal(AUTH *, XDR *);
79 static bool_t	rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
80 static bool_t	rpc_gss_refresh(AUTH *, void *);
81 static bool_t	rpc_gss_validate(AUTH *, struct opaque_auth *);
82 static void	rpc_gss_destroy(AUTH *);
83 static void	rpc_gss_destroy_context(AUTH *, bool_t);
84 
85 static 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 
93 enum rpcsec_gss_state {
94 	RPCSEC_GSS_START,
95 	RPCSEC_GSS_CONTEXT,
96 	RPCSEC_GSS_ESTABLISHED
97 };
98 
99 struct 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 
117 static struct timeval AUTH_TIMEOUT = { 25, 0 };
118 
119 AUTH *
120 rpc_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 
227 bool_t
228 rpc_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 
252 static void
253 rpc_gss_nextverf(__unused AUTH *auth)
254 {
255 
256 	/* not used */
257 }
258 
259 static bool_t
260 rpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs)
261 {
262 
263 	/* not used */
264 	return (FALSE);
265 }
266 
267 static bool_t
268 rpc_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 
320 static bool_t
321 rpc_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 
487 static bool_t
488 rpc_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 
510 static void
511 rpc_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 
541 static void
542 rpc_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 
563 bool_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 
662 bool_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 
680 int
681 rpc_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