xref: /freebsd/lib/librpcsec_gss/rpcsec_gss.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
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 
79 static void	rpc_gss_nextverf(AUTH*);
80 static bool_t	rpc_gss_marshal(AUTH *, XDR *);
81 static bool_t	rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
82 static bool_t	rpc_gss_refresh(AUTH *, void *);
83 static bool_t	rpc_gss_validate(AUTH *, struct opaque_auth *);
84 static void	rpc_gss_destroy(AUTH *);
85 static void	rpc_gss_destroy_context(AUTH *, bool_t);
86 
87 static 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 
95 enum rpcsec_gss_state {
96 	RPCSEC_GSS_START,
97 	RPCSEC_GSS_CONTEXT,
98 	RPCSEC_GSS_ESTABLISHED
99 };
100 
101 struct 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 
119 static struct timeval AUTH_TIMEOUT = { 25, 0 };
120 
121 AUTH *
122 rpc_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 
229 bool_t
230 rpc_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 
254 static void
255 rpc_gss_nextverf(__unused AUTH *auth)
256 {
257 
258 	/* not used */
259 }
260 
261 static bool_t
262 rpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs)
263 {
264 
265 	/* not used */
266 	return (FALSE);
267 }
268 
269 static bool_t
270 rpc_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 
322 static bool_t
323 rpc_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 
489 static bool_t
490 rpc_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 
512 static void
513 rpc_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 
543 static void
544 rpc_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 
565 bool_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 
664 bool_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 
682 int
683 rpc_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