1181344Sdfr/*-
2181344Sdfr * Copyright (c) 2008 Doug Rabson
3181344Sdfr * All rights reserved.
4181344Sdfr *
5181344Sdfr * Redistribution and use in source and binary forms, with or without
6181344Sdfr * modification, are permitted provided that the following conditions
7181344Sdfr * are met:
8181344Sdfr * 1. Redistributions of source code must retain the above copyright
9181344Sdfr *    notice, this list of conditions and the following disclaimer.
10181344Sdfr * 2. Redistributions in binary form must reproduce the above copyright
11181344Sdfr *    notice, this list of conditions and the following disclaimer in the
12181344Sdfr *    documentation and/or other materials provided with the distribution.
13181344Sdfr *
14181344Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15181344Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16181344Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17181344Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18181344Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19181344Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20181344Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21181344Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22181344Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23181344Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24181344Sdfr * SUCH DAMAGE.
25181344Sdfr *
26181344Sdfr *	$FreeBSD$
27181344Sdfr */
28181344Sdfr/*
29181344Sdfr  auth_gss.c
30181344Sdfr
31181344Sdfr  RPCSEC_GSS client routines.
32181344Sdfr
33181344Sdfr  Copyright (c) 2000 The Regents of the University of Michigan.
34181344Sdfr  All rights reserved.
35181344Sdfr
36181344Sdfr  Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
37181344Sdfr  All rights reserved, all wrongs reversed.
38181344Sdfr
39181344Sdfr  Redistribution and use in source and binary forms, with or without
40181344Sdfr  modification, are permitted provided that the following conditions
41181344Sdfr  are met:
42181344Sdfr
43181344Sdfr  1. Redistributions of source code must retain the above copyright
44181344Sdfr     notice, this list of conditions and the following disclaimer.
45181344Sdfr  2. Redistributions in binary form must reproduce the above copyright
46181344Sdfr     notice, this list of conditions and the following disclaimer in the
47181344Sdfr     documentation and/or other materials provided with the distribution.
48181344Sdfr  3. Neither the name of the University nor the names of its
49181344Sdfr     contributors may be used to endorse or promote products derived
50181344Sdfr     from this software without specific prior written permission.
51181344Sdfr
52181344Sdfr  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
53181344Sdfr  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54181344Sdfr  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55181344Sdfr  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56181344Sdfr  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57181344Sdfr  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58181344Sdfr  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
59181344Sdfr  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60181344Sdfr  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61181344Sdfr  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62181344Sdfr  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63181344Sdfr
64181344Sdfr  $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
65181344Sdfr*/
66181344Sdfr
67181344Sdfr#include <stdio.h>
68181344Sdfr#include <stdlib.h>
69181344Sdfr#include <unistd.h>
70181344Sdfr#include <string.h>
71181344Sdfr#include <errno.h>
72181344Sdfr#include <netinet/in.h>
73181344Sdfr#include <rpc/rpc.h>
74181344Sdfr#include <rpc/rpcsec_gss.h>
75181344Sdfr#include "rpcsec_gss_int.h"
76181344Sdfr
77181344Sdfrstatic void	rpc_gss_nextverf(AUTH*);
78181344Sdfrstatic bool_t	rpc_gss_marshal(AUTH *, XDR *);
79181344Sdfrstatic bool_t	rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
80181344Sdfrstatic bool_t	rpc_gss_refresh(AUTH *, void *);
81181344Sdfrstatic bool_t	rpc_gss_validate(AUTH *, struct opaque_auth *);
82181344Sdfrstatic void	rpc_gss_destroy(AUTH *);
83181344Sdfrstatic void	rpc_gss_destroy_context(AUTH *, bool_t);
84181344Sdfr
85181344Sdfrstatic struct auth_ops rpc_gss_ops = {
86181344Sdfr	rpc_gss_nextverf,
87181344Sdfr	rpc_gss_marshal,
88181344Sdfr	rpc_gss_validate,
89181344Sdfr	rpc_gss_refresh,
90181344Sdfr	rpc_gss_destroy
91181344Sdfr};
92181344Sdfr
93181344Sdfrenum rpcsec_gss_state {
94181344Sdfr	RPCSEC_GSS_START,
95181344Sdfr	RPCSEC_GSS_CONTEXT,
96181344Sdfr	RPCSEC_GSS_ESTABLISHED
97181344Sdfr};
98181344Sdfr
99181344Sdfrstruct rpc_gss_data {
100181344Sdfr	rpc_gss_options_req_t	gd_options;	/* GSS context options */
101181344Sdfr	enum rpcsec_gss_state	gd_state;	/* connection state */
102181344Sdfr	gss_buffer_desc		gd_verf;	/* save GSS_S_COMPLETE
103181344Sdfr						 * NULL RPC verfier to
104181344Sdfr						 * process at end of
105181344Sdfr						 * context negotiation */
106181344Sdfr	CLIENT			*gd_clnt;	/* client handle */
107181344Sdfr	gss_name_t		gd_name;	/* service name */
108181344Sdfr	gss_OID			gd_mech;	/* mechanism to use */
109181344Sdfr	gss_qop_t		gd_qop;		/* quality of protection */
110181344Sdfr	gss_ctx_id_t		gd_ctx;		/* context id */
111181344Sdfr	struct rpc_gss_cred	gd_cred;	/* client credentials */
112181344Sdfr	u_int			gd_win;		/* sequence window */
113181344Sdfr};
114181344Sdfr
115181344Sdfr#define	AUTH_PRIVATE(auth)	((struct rpc_gss_data *)auth->ah_private)
116181344Sdfr
117181344Sdfrstatic struct timeval AUTH_TIMEOUT = { 25, 0 };
118181344Sdfr
119181344SdfrAUTH *
120181344Sdfrrpc_gss_seccreate(CLIENT *clnt, const char *principal,
121181344Sdfr    const char *mechanism, rpc_gss_service_t service, const char *qop,
122181344Sdfr    rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
123181344Sdfr{
124181344Sdfr	AUTH			*auth, *save_auth;
125181344Sdfr	rpc_gss_options_ret_t	options;
126181344Sdfr	gss_OID			oid;
127181344Sdfr	u_int			qop_num;
128181344Sdfr	struct rpc_gss_data	*gd;
129181344Sdfr	OM_uint32		maj_stat = 0, min_stat = 0;
130181344Sdfr	gss_buffer_desc		principal_desc;
131181344Sdfr
132181344Sdfr	/*
133181344Sdfr	 * Bail out now if we don't know this mechanism.
134181344Sdfr	 */
135181344Sdfr	if (!rpc_gss_mech_to_oid(mechanism, &oid))
136181344Sdfr		return (NULL);
137181344Sdfr
138181344Sdfr	if (qop) {
139181344Sdfr		if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
140181344Sdfr			return (NULL);
141181344Sdfr	} else {
142181344Sdfr		qop_num = GSS_C_QOP_DEFAULT;
143181344Sdfr	}
144181344Sdfr
145181344Sdfr	/*
146181344Sdfr	 * If the caller doesn't want the options, point at local
147181344Sdfr	 * storage to simplify the code below.
148181344Sdfr	 */
149181344Sdfr	if (!options_ret)
150181344Sdfr		options_ret = &options;
151181344Sdfr
152181344Sdfr	/*
153181344Sdfr	 * Default service is integrity.
154181344Sdfr	 */
155181344Sdfr	if (service == rpc_gss_svc_default)
156181344Sdfr		service = rpc_gss_svc_integrity;
157181344Sdfr
158181344Sdfr	memset(options_ret, 0, sizeof(*options_ret));
159181344Sdfr
160181344Sdfr	log_debug("in rpc_gss_seccreate()");
161181344Sdfr
162181344Sdfr	memset(&rpc_createerr, 0, sizeof(rpc_createerr));
163181344Sdfr
164181344Sdfr	auth = mem_alloc(sizeof(*auth));
165181344Sdfr	if (auth == NULL) {
166181344Sdfr		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
167181344Sdfr		rpc_createerr.cf_error.re_errno = ENOMEM;
168181344Sdfr		return (NULL);
169181344Sdfr	}
170181344Sdfr	gd = mem_alloc(sizeof(*gd));
171181344Sdfr	if (gd == NULL) {
172181344Sdfr		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
173181344Sdfr		rpc_createerr.cf_error.re_errno = ENOMEM;
174181344Sdfr		free(auth);
175181344Sdfr		return (NULL);
176181344Sdfr	}
177181344Sdfr
178181344Sdfr	auth->ah_ops = &rpc_gss_ops;
179181344Sdfr	auth->ah_private = (caddr_t) gd;
180181344Sdfr	auth->ah_cred.oa_flavor = RPCSEC_GSS;
181181344Sdfr
182181344Sdfr	principal_desc.value = (void *)(intptr_t) principal;
183181344Sdfr	principal_desc.length = strlen(principal);
184181344Sdfr	maj_stat = gss_import_name(&min_stat, &principal_desc,
185181344Sdfr	    GSS_C_NT_HOSTBASED_SERVICE, &gd->gd_name);
186181344Sdfr	if (maj_stat != GSS_S_COMPLETE) {
187181344Sdfr		options_ret->major_status = maj_stat;
188181344Sdfr		options_ret->minor_status = min_stat;
189181344Sdfr		goto bad;
190181344Sdfr	}
191181344Sdfr
192181344Sdfr	if (options_req) {
193181344Sdfr		gd->gd_options = *options_req;
194181344Sdfr	} else {
195181344Sdfr		gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
196181344Sdfr		gd->gd_options.time_req = 0;
197181344Sdfr		gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
198181344Sdfr		gd->gd_options.input_channel_bindings = NULL;
199181344Sdfr	}
200181344Sdfr	gd->gd_clnt = clnt;
201181344Sdfr	gd->gd_ctx = GSS_C_NO_CONTEXT;
202181344Sdfr	gd->gd_mech = oid;
203181344Sdfr	gd->gd_qop = qop_num;
204181344Sdfr
205181344Sdfr	gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
206181344Sdfr	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
207181344Sdfr	gd->gd_cred.gc_seq = 0;
208181344Sdfr	gd->gd_cred.gc_svc = service;
209181344Sdfr
210181344Sdfr	save_auth = clnt->cl_auth;
211181344Sdfr
212181344Sdfr	clnt->cl_auth = auth;
213181344Sdfr	if (!rpc_gss_init(auth, options_ret)) {
214181344Sdfr		clnt->cl_auth = save_auth;
215181344Sdfr		goto bad;
216181344Sdfr	}
217181344Sdfr
218181344Sdfr	clnt->cl_auth = save_auth;
219181344Sdfr
220181344Sdfr	return (auth);
221181344Sdfr
222181344Sdfr bad:
223181344Sdfr	AUTH_DESTROY(auth);
224181344Sdfr	return (NULL);
225181344Sdfr}
226181344Sdfr
227181344Sdfrbool_t
228181344Sdfrrpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
229181344Sdfr{
230181344Sdfr	struct rpc_gss_data	*gd;
231181344Sdfr	u_int			qop_num;
232181344Sdfr	const char		*mechanism;
233181344Sdfr
234181344Sdfr	gd = AUTH_PRIVATE(auth);
235181344Sdfr	if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
236181344Sdfr		return (FALSE);
237181344Sdfr	}
238181344Sdfr
239181344Sdfr	if (qop) {
240181344Sdfr		if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
241181344Sdfr			return (FALSE);
242181344Sdfr		}
243181344Sdfr	} else {
244181344Sdfr		qop_num = GSS_C_QOP_DEFAULT;
245181344Sdfr	}
246181344Sdfr
247181344Sdfr	gd->gd_cred.gc_svc = service;
248181344Sdfr	gd->gd_qop = qop_num;
249181344Sdfr	return (TRUE);
250181344Sdfr}
251181344Sdfr
252181344Sdfrstatic void
253181344Sdfrrpc_gss_nextverf(__unused AUTH *auth)
254181344Sdfr{
255181344Sdfr
256181344Sdfr	/* not used */
257181344Sdfr}
258181344Sdfr
259181344Sdfrstatic bool_t
260181344Sdfrrpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs)
261181344Sdfr{
262181344Sdfr
263181344Sdfr	/* not used */
264181344Sdfr	return (FALSE);
265181344Sdfr}
266181344Sdfr
267181344Sdfrstatic bool_t
268181344Sdfrrpc_gss_validate(AUTH *auth, struct opaque_auth *verf)
269181344Sdfr{
270181344Sdfr	struct rpc_gss_data	*gd;
271181344Sdfr	gss_qop_t		qop_state;
272181344Sdfr	uint32_t		num;
273181344Sdfr	gss_buffer_desc		signbuf, checksum;
274181344Sdfr	OM_uint32		maj_stat, min_stat;
275181344Sdfr
276181344Sdfr	log_debug("in rpc_gss_validate()");
277181344Sdfr
278181344Sdfr	gd = AUTH_PRIVATE(auth);
279181344Sdfr
280181344Sdfr	if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
281181344Sdfr		/*
282181344Sdfr		 * Save the on the wire verifier to validate last INIT
283181344Sdfr		 * phase packet after decode if the major status is
284181344Sdfr		 * GSS_S_COMPLETE.
285181344Sdfr		 */
286181344Sdfr		if (gd->gd_verf.value)
287181344Sdfr			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
288181344Sdfr			    (char *) &gd->gd_verf);
289181344Sdfr		gd->gd_verf.value = mem_alloc(verf->oa_length);
290181344Sdfr		if (gd->gd_verf.value == NULL) {
291181344Sdfr			fprintf(stderr, "gss_validate: out of memory\n");
292181344Sdfr			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
293181344Sdfr			return (FALSE);
294181344Sdfr		}
295181344Sdfr		memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
296181344Sdfr		gd->gd_verf.length = verf->oa_length;
297181344Sdfr		return (TRUE);
298181344Sdfr	}
299181344Sdfr
300181344Sdfr	num = htonl(gd->gd_cred.gc_seq);
301181344Sdfr	signbuf.value = &num;
302181344Sdfr	signbuf.length = sizeof(num);
303181344Sdfr
304181344Sdfr	checksum.value = verf->oa_base;
305181344Sdfr	checksum.length = verf->oa_length;
306181344Sdfr
307181344Sdfr	maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, &signbuf,
308181344Sdfr	    &checksum, &qop_state);
309181344Sdfr	if (maj_stat != GSS_S_COMPLETE || qop_state != gd->gd_qop) {
310181344Sdfr		log_status("gss_verify_mic", gd->gd_mech, maj_stat, min_stat);
311181344Sdfr		if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
312181344Sdfr			rpc_gss_destroy_context(auth, TRUE);
313181344Sdfr		}
314181344Sdfr		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
315181344Sdfr		return (FALSE);
316181344Sdfr	}
317181344Sdfr	return (TRUE);
318181344Sdfr}
319181344Sdfr
320181344Sdfrstatic bool_t
321181344Sdfrrpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
322181344Sdfr{
323181344Sdfr	struct rpc_gss_data	*gd;
324181344Sdfr	struct rpc_gss_init_res	 gr;
325181344Sdfr	gss_buffer_desc		*recv_tokenp, recv_token, send_token;
326181344Sdfr	OM_uint32		 maj_stat, min_stat, call_stat;
327181344Sdfr	const char		*mech;
328181344Sdfr
329181344Sdfr	log_debug("in rpc_gss_refresh()");
330181344Sdfr
331181344Sdfr	gd = AUTH_PRIVATE(auth);
332181344Sdfr
333181344Sdfr	if (gd->gd_state != RPCSEC_GSS_START)
334181344Sdfr		return (TRUE);
335181344Sdfr
336181344Sdfr	/* GSS context establishment loop. */
337181344Sdfr	gd->gd_state = RPCSEC_GSS_CONTEXT;
338181344Sdfr	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
339181344Sdfr	gd->gd_cred.gc_seq = 0;
340181344Sdfr
341181344Sdfr	memset(&recv_token, 0, sizeof(recv_token));
342181344Sdfr	memset(&gr, 0, sizeof(gr));
343181344Sdfr	recv_tokenp = GSS_C_NO_BUFFER;
344181344Sdfr
345181344Sdfr	for (;;) {
346181344Sdfr		maj_stat = gss_init_sec_context(&min_stat,
347181344Sdfr		    gd->gd_options.my_cred,
348181344Sdfr		    &gd->gd_ctx,
349181344Sdfr		    gd->gd_name,
350181344Sdfr		    gd->gd_mech,
351181344Sdfr		    gd->gd_options.req_flags,
352181344Sdfr		    gd->gd_options.time_req,
353181344Sdfr		    gd->gd_options.input_channel_bindings,
354181344Sdfr		    recv_tokenp,
355181344Sdfr		    &gd->gd_mech,	/* used mech */
356181344Sdfr		    &send_token,
357181344Sdfr		    &options_ret->ret_flags,
358181344Sdfr		    &options_ret->time_req);
359181344Sdfr
360181344Sdfr		/*
361181344Sdfr		 * Free the token which we got from the server (if
362181344Sdfr		 * any).  Remember that this was allocated by XDR, not
363181344Sdfr		 * GSS-API.
364181344Sdfr		 */
365181344Sdfr		if (recv_tokenp != GSS_C_NO_BUFFER) {
366181344Sdfr			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
367181344Sdfr			    (char *) &recv_token);
368181344Sdfr			recv_tokenp = GSS_C_NO_BUFFER;
369181344Sdfr		}
370181344Sdfr		if (maj_stat != GSS_S_COMPLETE &&
371181344Sdfr		    maj_stat != GSS_S_CONTINUE_NEEDED) {
372181344Sdfr			log_status("gss_init_sec_context", gd->gd_mech,
373181344Sdfr			    maj_stat, min_stat);
374181344Sdfr			options_ret->major_status = maj_stat;
375181344Sdfr			options_ret->minor_status = min_stat;
376181344Sdfr			break;
377181344Sdfr		}
378181344Sdfr		if (send_token.length != 0) {
379181344Sdfr			memset(&gr, 0, sizeof(gr));
380181344Sdfr
381181344Sdfr			call_stat = clnt_call(gd->gd_clnt, NULLPROC,
382181344Sdfr			    (xdrproc_t)xdr_gss_buffer_desc,
383181344Sdfr			    &send_token,
384181344Sdfr			    (xdrproc_t)xdr_rpc_gss_init_res,
385181344Sdfr			    (caddr_t)&gr, AUTH_TIMEOUT);
386181344Sdfr
387181344Sdfr			gss_release_buffer(&min_stat, &send_token);
388181344Sdfr
389181344Sdfr			if (call_stat != RPC_SUCCESS)
390181344Sdfr				break;
391181344Sdfr
392181344Sdfr			if (gr.gr_major != GSS_S_COMPLETE &&
393181344Sdfr			    gr.gr_major != GSS_S_CONTINUE_NEEDED) {
394181344Sdfr				log_status("server reply", gd->gd_mech,
395181344Sdfr				    gr.gr_major, gr.gr_minor);
396181344Sdfr				options_ret->major_status = gr.gr_major;
397181344Sdfr				options_ret->minor_status = gr.gr_minor;
398181344Sdfr				break;
399181344Sdfr			}
400181344Sdfr
401181344Sdfr			/*
402181344Sdfr			 * Save the server's gr_handle value, freeing
403181344Sdfr			 * what we have already (remember that this
404181344Sdfr			 * was allocated by XDR, not GSS-API).
405181344Sdfr			 */
406181344Sdfr			if (gr.gr_handle.length != 0) {
407181344Sdfr				xdr_free((xdrproc_t) xdr_gss_buffer_desc,
408181344Sdfr				    (char *) &gd->gd_cred.gc_handle);
409181344Sdfr				gd->gd_cred.gc_handle = gr.gr_handle;
410181344Sdfr			}
411181344Sdfr
412181344Sdfr			/*
413181344Sdfr			 * Save the server's token as well.
414181344Sdfr			 */
415181344Sdfr			if (gr.gr_token.length != 0) {
416181344Sdfr				recv_token = gr.gr_token;
417181344Sdfr				recv_tokenp = &recv_token;
418181344Sdfr			}
419181344Sdfr
420181344Sdfr			/*
421181344Sdfr			 * Since we have copied out all the bits of gr
422181344Sdfr			 * which XDR allocated for us, we don't need
423181344Sdfr			 * to free it.
424181344Sdfr			 */
425181344Sdfr			gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
426181344Sdfr		}
427181344Sdfr
428181344Sdfr		if (maj_stat == GSS_S_COMPLETE) {
429181344Sdfr			gss_buffer_desc   bufin;
430181344Sdfr			u_int seq, qop_state = 0;
431181344Sdfr
432181344Sdfr			/*
433181344Sdfr			 * gss header verifier,
434181344Sdfr			 * usually checked in gss_validate
435181344Sdfr			 */
436181344Sdfr			seq = htonl(gr.gr_win);
437181344Sdfr			bufin.value = (unsigned char *)&seq;
438181344Sdfr			bufin.length = sizeof(seq);
439181344Sdfr
440181344Sdfr			maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
441181344Sdfr			    &bufin, &gd->gd_verf, &qop_state);
442181344Sdfr
443181344Sdfr			if (maj_stat != GSS_S_COMPLETE ||
444181344Sdfr			    qop_state != gd->gd_qop) {
445181344Sdfr				log_status("gss_verify_mic", gd->gd_mech,
446181344Sdfr				    maj_stat, min_stat);
447181344Sdfr				if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
448181344Sdfr					rpc_gss_destroy_context(auth, TRUE);
449181344Sdfr				}
450181344Sdfr				_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
451181344Sdfr				    EPERM);
452181344Sdfr				options_ret->major_status = maj_stat;
453181344Sdfr				options_ret->minor_status = min_stat;
454181344Sdfr				return (FALSE);
455181344Sdfr			}
456181344Sdfr
457181344Sdfr			options_ret->major_status = GSS_S_COMPLETE;
458181344Sdfr			options_ret->minor_status = 0;
459181344Sdfr			options_ret->rpcsec_version = gd->gd_cred.gc_version;
460181344Sdfr			options_ret->gss_context = gd->gd_ctx;
461181344Sdfr			if (rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
462181344Sdfr				strlcpy(options_ret->actual_mechanism,
463181344Sdfr				    mech,
464181344Sdfr				    sizeof(options_ret->actual_mechanism));
465181344Sdfr			}
466181344Sdfr
467181344Sdfr			gd->gd_state = RPCSEC_GSS_ESTABLISHED;
468181344Sdfr			gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
469181344Sdfr			gd->gd_cred.gc_seq = 0;
470181344Sdfr			gd->gd_win = gr.gr_win;
471181344Sdfr			break;
472181344Sdfr		}
473181344Sdfr	}
474181344Sdfr	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
475181344Sdfr	    (char *) &gd->gd_verf);
476181344Sdfr
477181344Sdfr	/* End context negotiation loop. */
478181344Sdfr	if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
479181344Sdfr		rpc_createerr.cf_stat = RPC_AUTHERROR;
480181344Sdfr		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
481181344Sdfr		return (FALSE);
482181344Sdfr	}
483181344Sdfr
484181344Sdfr	return (TRUE);
485181344Sdfr}
486181344Sdfr
487181344Sdfrstatic bool_t
488181344Sdfrrpc_gss_refresh(AUTH *auth, void *msg)
489181344Sdfr{
490181344Sdfr	struct rpc_msg *reply = (struct rpc_msg *) msg;
491181344Sdfr	rpc_gss_options_ret_t options;
492181344Sdfr
493181344Sdfr	/*
494181344Sdfr	 * If the error was RPCSEC_GSS_CREDPROBLEM of
495181344Sdfr	 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
496181344Sdfr	 * other errors are fatal.
497181344Sdfr	 */
498181344Sdfr	if (reply->rm_reply.rp_stat == MSG_DENIED
499181344Sdfr	    && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
500181344Sdfr	    && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
501181344Sdfr		|| reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
502181344Sdfr		rpc_gss_destroy_context(auth, FALSE);
503181344Sdfr		memset(&options, 0, sizeof(options));
504181344Sdfr		return (rpc_gss_init(auth, &options));
505181344Sdfr	}
506181344Sdfr
507181344Sdfr	return (FALSE);
508181344Sdfr}
509181344Sdfr
510181344Sdfrstatic void
511181344Sdfrrpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
512181344Sdfr{
513181344Sdfr	struct rpc_gss_data	*gd;
514181344Sdfr	OM_uint32		 min_stat;
515181344Sdfr
516181344Sdfr	log_debug("in rpc_gss_destroy_context()");
517181344Sdfr
518181344Sdfr	gd = AUTH_PRIVATE(auth);
519181344Sdfr
520181344Sdfr	if (gd->gd_state == RPCSEC_GSS_ESTABLISHED && send_destroy) {
521181344Sdfr		gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
522181344Sdfr		clnt_call(gd->gd_clnt, NULLPROC,
523181344Sdfr		    (xdrproc_t)xdr_void, NULL,
524181344Sdfr		    (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
525181344Sdfr	}
526181344Sdfr
527181344Sdfr	/*
528181344Sdfr	 * Free the context token. Remember that this was
529181344Sdfr	 * allocated by XDR, not GSS-API.
530181344Sdfr	 */
531181344Sdfr	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
532181344Sdfr	    (char *) &gd->gd_cred.gc_handle);
533181344Sdfr	gd->gd_cred.gc_handle.length = 0;
534181344Sdfr
535181344Sdfr	if (gd->gd_ctx != GSS_C_NO_CONTEXT)
536181344Sdfr		gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
537181344Sdfr
538181344Sdfr	gd->gd_state = RPCSEC_GSS_START;
539181344Sdfr}
540181344Sdfr
541181344Sdfrstatic void
542181344Sdfrrpc_gss_destroy(AUTH *auth)
543181344Sdfr{
544181344Sdfr	struct rpc_gss_data	*gd;
545181344Sdfr	OM_uint32		 min_stat;
546181344Sdfr
547181344Sdfr	log_debug("in rpc_gss_destroy()");
548181344Sdfr
549181344Sdfr	gd = AUTH_PRIVATE(auth);
550181344Sdfr
551181344Sdfr	rpc_gss_destroy_context(auth, TRUE);
552181344Sdfr
553181344Sdfr	if (gd->gd_name != GSS_C_NO_NAME)
554181344Sdfr		gss_release_name(&min_stat, &gd->gd_name);
555181344Sdfr	if (gd->gd_verf.value)
556181344Sdfr		xdr_free((xdrproc_t) xdr_gss_buffer_desc,
557181344Sdfr		    (char *) &gd->gd_verf);
558181344Sdfr
559181344Sdfr	mem_free(gd, sizeof(*gd));
560181344Sdfr	mem_free(auth, sizeof(*auth));
561181344Sdfr}
562181344Sdfr
563181344Sdfrbool_t
564181344Sdfr__rpc_gss_wrap(AUTH *auth, void *header, size_t headerlen,
565181344Sdfr    XDR* xdrs, xdrproc_t xdr_args, void *args_ptr)
566181344Sdfr{
567181344Sdfr	XDR			 tmpxdrs;
568181344Sdfr	char			 credbuf[MAX_AUTH_BYTES];
569181344Sdfr	char			 tmpheader[MAX_AUTH_BYTES];
570181344Sdfr	struct opaque_auth	 creds, verf;
571181344Sdfr	struct rpc_gss_data	*gd;
572181344Sdfr	gss_buffer_desc		 rpcbuf, checksum;
573181344Sdfr	OM_uint32		 maj_stat, min_stat;
574181344Sdfr	bool_t			 xdr_stat;
575181344Sdfr
576181344Sdfr	log_debug("in rpc_gss_wrap()");
577181344Sdfr
578181344Sdfr	gd = AUTH_PRIVATE(auth);
579181344Sdfr
580181344Sdfr	if (gd->gd_state == RPCSEC_GSS_ESTABLISHED)
581181344Sdfr		gd->gd_cred.gc_seq++;
582181344Sdfr
583181344Sdfr	/*
584181344Sdfr	 * We need to encode our creds and then put the header and
585181344Sdfr	 * creds together in a buffer so that we can create a checksum
586181344Sdfr	 * for the verf.
587181344Sdfr	 */
588181344Sdfr	xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
589181344Sdfr	if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gd_cred)) {
590181344Sdfr		XDR_DESTROY(&tmpxdrs);
591181344Sdfr		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
592181344Sdfr		return (FALSE);
593181344Sdfr	}
594181344Sdfr	creds.oa_flavor = RPCSEC_GSS;
595181344Sdfr	creds.oa_base = credbuf;
596181344Sdfr	creds.oa_length = XDR_GETPOS(&tmpxdrs);
597181344Sdfr	XDR_DESTROY(&tmpxdrs);
598181344Sdfr
599181344Sdfr	xdrmem_create(&tmpxdrs, tmpheader, sizeof(tmpheader), XDR_ENCODE);
600181344Sdfr	if (!XDR_PUTBYTES(&tmpxdrs, header, headerlen) ||
601181344Sdfr	    !xdr_opaque_auth(&tmpxdrs, &creds)) {
602181344Sdfr		XDR_DESTROY(&tmpxdrs);
603181344Sdfr		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
604181344Sdfr		return (FALSE);
605181344Sdfr	}
606181344Sdfr	headerlen = XDR_GETPOS(&tmpxdrs);
607181344Sdfr	XDR_DESTROY(&tmpxdrs);
608181344Sdfr
609181344Sdfr	if (!XDR_PUTBYTES(xdrs, tmpheader, headerlen)) {
610181344Sdfr		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
611181344Sdfr		return (FALSE);
612181344Sdfr	}
613181344Sdfr
614181344Sdfr	if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
615181344Sdfr	    gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
616181344Sdfr		if (!xdr_opaque_auth(xdrs, &_null_auth)) {
617181344Sdfr			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
618181344Sdfr			return (FALSE);
619181344Sdfr		}
620181344Sdfr	} else {
621181344Sdfr		/*
622181344Sdfr		 * Checksum serialized RPC header, up to and including
623181344Sdfr		 * credential.
624181344Sdfr		 */
625181344Sdfr		rpcbuf.length = headerlen;
626181344Sdfr		rpcbuf.value = tmpheader;
627181344Sdfr
628181344Sdfr		maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
629181344Sdfr		    &rpcbuf, &checksum);
630181344Sdfr
631181344Sdfr		if (maj_stat != GSS_S_COMPLETE) {
632181344Sdfr			log_status("gss_get_mic", gd->gd_mech,
633181344Sdfr			    maj_stat, min_stat);
634181344Sdfr			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
635181344Sdfr				rpc_gss_destroy_context(auth, TRUE);
636181344Sdfr			}
637181344Sdfr			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
638181344Sdfr			return (FALSE);
639181344Sdfr		}
640181344Sdfr
641181344Sdfr		verf.oa_flavor = RPCSEC_GSS;
642181344Sdfr		verf.oa_base = checksum.value;
643181344Sdfr		verf.oa_length = checksum.length;
644181344Sdfr
645181344Sdfr		xdr_stat = xdr_opaque_auth(xdrs, &verf);
646181344Sdfr		gss_release_buffer(&min_stat, &checksum);
647181344Sdfr		if (!xdr_stat) {
648181344Sdfr			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
649181344Sdfr			return (FALSE);
650181344Sdfr		}
651181344Sdfr	}
652181344Sdfr
653181344Sdfr	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
654181344Sdfr	    gd->gd_cred.gc_svc == rpc_gss_svc_none) {
655181344Sdfr		return (xdr_args(xdrs, args_ptr));
656181344Sdfr	}
657181344Sdfr	return (xdr_rpc_gss_wrap_data(xdrs, xdr_args, args_ptr,
658181344Sdfr		gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
659181344Sdfr		gd->gd_cred.gc_seq));
660181344Sdfr}
661181344Sdfr
662181344Sdfrbool_t
663181344Sdfr__rpc_gss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, void *xdr_ptr)
664181344Sdfr{
665181344Sdfr	struct rpc_gss_data	*gd;
666181344Sdfr
667181344Sdfr	log_debug("in rpc_gss_unwrap()");
668181344Sdfr
669181344Sdfr	gd = AUTH_PRIVATE(auth);
670181344Sdfr
671181344Sdfr	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
672181344Sdfr	    gd->gd_cred.gc_svc == rpc_gss_svc_none) {
673181344Sdfr		return (xdr_func(xdrs, xdr_ptr));
674181344Sdfr	}
675181344Sdfr	return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
676181344Sdfr		gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
677181344Sdfr		gd->gd_cred.gc_seq));
678181344Sdfr}
679181344Sdfr
680181344Sdfrint
681181344Sdfrrpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
682181344Sdfr{
683181344Sdfr	struct rpc_gss_data	*gd;
684181344Sdfr	int			want_conf;
685181344Sdfr	OM_uint32		max;
686181344Sdfr	OM_uint32		maj_stat, min_stat;
687181344Sdfr	int			result;
688181344Sdfr
689181344Sdfr	gd = AUTH_PRIVATE(auth);
690181344Sdfr
691181344Sdfr	switch (gd->gd_cred.gc_svc) {
692181344Sdfr	case rpc_gss_svc_none:
693181344Sdfr		return (max_tp_unit_len);
694181344Sdfr		break;
695181344Sdfr
696181344Sdfr	case rpc_gss_svc_default:
697181344Sdfr	case rpc_gss_svc_integrity:
698181344Sdfr		want_conf = FALSE;
699181344Sdfr		break;
700181344Sdfr
701181344Sdfr	case rpc_gss_svc_privacy:
702181344Sdfr		want_conf = TRUE;
703181344Sdfr		break;
704181344Sdfr
705181344Sdfr	default:
706181344Sdfr		return (0);
707181344Sdfr	}
708181344Sdfr
709181344Sdfr	maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
710181344Sdfr	    gd->gd_qop, max_tp_unit_len, &max);
711181344Sdfr
712181344Sdfr	if (maj_stat == GSS_S_COMPLETE) {
713181344Sdfr		result = (int) max;
714181344Sdfr		if (result < 0)
715181344Sdfr			result = 0;
716181344Sdfr		return (result);
717181344Sdfr	} else {
718181344Sdfr		log_status("gss_wrap_size_limit", gd->gd_mech,
719181344Sdfr		    maj_stat, min_stat);
720181344Sdfr		return (0);
721181344Sdfr	}
722181344Sdfr}
723