xref: /freebsd/crypto/krb5/src/lib/rpc/auth_gss.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert /* lib/rpc/auth_gss.c */
2*7f2fe78bSCy Schubert /*
3*7f2fe78bSCy Schubert   Copyright (c) 2000 The Regents of the University of Michigan.
4*7f2fe78bSCy Schubert   All rights reserved.
5*7f2fe78bSCy Schubert 
6*7f2fe78bSCy Schubert   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
7*7f2fe78bSCy Schubert   All rights reserved, all wrongs reversed.
8*7f2fe78bSCy Schubert 
9*7f2fe78bSCy Schubert   Redistribution and use in source and binary forms, with or without
10*7f2fe78bSCy Schubert   modification, are permitted provided that the following conditions
11*7f2fe78bSCy Schubert   are met:
12*7f2fe78bSCy Schubert 
13*7f2fe78bSCy Schubert   1. Redistributions of source code must retain the above copyright
14*7f2fe78bSCy Schubert      notice, this list of conditions and the following disclaimer.
15*7f2fe78bSCy Schubert   2. Redistributions in binary form must reproduce the above copyright
16*7f2fe78bSCy Schubert      notice, this list of conditions and the following disclaimer in the
17*7f2fe78bSCy Schubert      documentation and/or other materials provided with the distribution.
18*7f2fe78bSCy Schubert   3. Neither the name of the University nor the names of its
19*7f2fe78bSCy Schubert      contributors may be used to endorse or promote products derived
20*7f2fe78bSCy Schubert      from this software without specific prior written permission.
21*7f2fe78bSCy Schubert 
22*7f2fe78bSCy Schubert   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
23*7f2fe78bSCy Schubert   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24*7f2fe78bSCy Schubert   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25*7f2fe78bSCy Schubert   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26*7f2fe78bSCy Schubert   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27*7f2fe78bSCy Schubert   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28*7f2fe78bSCy Schubert   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29*7f2fe78bSCy Schubert   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30*7f2fe78bSCy Schubert   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31*7f2fe78bSCy Schubert   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32*7f2fe78bSCy Schubert   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33*7f2fe78bSCy Schubert 
34*7f2fe78bSCy Schubert   Id: auth_gss.c,v 1.35 2002/10/15 21:25:25 kwc Exp
35*7f2fe78bSCy Schubert */
36*7f2fe78bSCy Schubert 
37*7f2fe78bSCy Schubert /* RPCSEC_GSS client routines. */
38*7f2fe78bSCy Schubert 
39*7f2fe78bSCy Schubert #include <stdio.h>
40*7f2fe78bSCy Schubert #include <stdlib.h>
41*7f2fe78bSCy Schubert #include <unistd.h>
42*7f2fe78bSCy Schubert #include <string.h>
43*7f2fe78bSCy Schubert #include <errno.h>
44*7f2fe78bSCy Schubert #include <gssrpc/types.h>
45*7f2fe78bSCy Schubert #include <gssrpc/xdr.h>
46*7f2fe78bSCy Schubert #include <gssrpc/auth.h>
47*7f2fe78bSCy Schubert #include <gssrpc/auth_gss.h>
48*7f2fe78bSCy Schubert #include <gssrpc/clnt.h>
49*7f2fe78bSCy Schubert #include <netinet/in.h>
50*7f2fe78bSCy Schubert #ifdef HAVE_HEIMDAL
51*7f2fe78bSCy Schubert #include <gssapi.h>
52*7f2fe78bSCy Schubert #define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
53*7f2fe78bSCy Schubert #else
54*7f2fe78bSCy Schubert #include <gssapi/gssapi.h>
55*7f2fe78bSCy Schubert #include <gssapi/gssapi_generic.h>
56*7f2fe78bSCy Schubert #endif
57*7f2fe78bSCy Schubert 
58*7f2fe78bSCy Schubert #ifdef DEBUG_GSSAPI
59*7f2fe78bSCy Schubert int auth_debug_gss = DEBUG_GSSAPI;
60*7f2fe78bSCy Schubert int misc_debug_gss = DEBUG_GSSAPI;
61*7f2fe78bSCy Schubert #endif
62*7f2fe78bSCy Schubert 
63*7f2fe78bSCy Schubert static void	authgss_nextverf(AUTH *);
64*7f2fe78bSCy Schubert static bool_t	authgss_marshal(AUTH *, XDR *);
65*7f2fe78bSCy Schubert static bool_t	authgss_refresh(AUTH *, struct rpc_msg *);
66*7f2fe78bSCy Schubert static bool_t	authgss_validate(AUTH *, struct opaque_auth *);
67*7f2fe78bSCy Schubert static void	authgss_destroy(AUTH *);
68*7f2fe78bSCy Schubert static void	authgss_destroy_context(AUTH *);
69*7f2fe78bSCy Schubert static bool_t	authgss_wrap(AUTH *, XDR *, xdrproc_t, caddr_t);
70*7f2fe78bSCy Schubert static bool_t	authgss_unwrap(AUTH *, XDR *, xdrproc_t, caddr_t);
71*7f2fe78bSCy Schubert 
72*7f2fe78bSCy Schubert 
73*7f2fe78bSCy Schubert /*
74*7f2fe78bSCy Schubert  * from mit-krb5-1.2.1 mechglue/mglueP.h:
75*7f2fe78bSCy Schubert  * Array of context IDs typed by mechanism OID
76*7f2fe78bSCy Schubert  */
77*7f2fe78bSCy Schubert typedef struct gss_union_ctx_id_t {
78*7f2fe78bSCy Schubert   gss_OID     mech_type;
79*7f2fe78bSCy Schubert   gss_ctx_id_t    internal_ctx_id;
80*7f2fe78bSCy Schubert } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
81*7f2fe78bSCy Schubert 
82*7f2fe78bSCy Schubert static struct auth_ops authgss_ops = {
83*7f2fe78bSCy Schubert 	authgss_nextverf,
84*7f2fe78bSCy Schubert 	authgss_marshal,
85*7f2fe78bSCy Schubert 	authgss_validate,
86*7f2fe78bSCy Schubert 	authgss_refresh,
87*7f2fe78bSCy Schubert 	authgss_destroy,
88*7f2fe78bSCy Schubert 	authgss_wrap,
89*7f2fe78bSCy Schubert 	authgss_unwrap
90*7f2fe78bSCy Schubert };
91*7f2fe78bSCy Schubert 
92*7f2fe78bSCy Schubert #ifdef DEBUG
93*7f2fe78bSCy Schubert 
94*7f2fe78bSCy Schubert /* useful as i add more mechanisms */
95*7f2fe78bSCy Schubert void
print_rpc_gss_sec(struct rpc_gss_sec * ptr)96*7f2fe78bSCy Schubert print_rpc_gss_sec(struct rpc_gss_sec *ptr)
97*7f2fe78bSCy Schubert {
98*7f2fe78bSCy Schubert 	int i;
99*7f2fe78bSCy Schubert 	char *p;
100*7f2fe78bSCy Schubert 
101*7f2fe78bSCy Schubert 	log_debug("rpc_gss_sec:");
102*7f2fe78bSCy Schubert 	if(ptr->mech == NULL)
103*7f2fe78bSCy Schubert 		log_debug("NULL gss_OID mech");
104*7f2fe78bSCy Schubert 	else {
105*7f2fe78bSCy Schubert 		fprintf(stderr, "     mechanism_OID: {");
106*7f2fe78bSCy Schubert 		p = (char *)ptr->mech->elements;
107*7f2fe78bSCy Schubert 		for (i=0; i < ptr->mech->length; i++)
108*7f2fe78bSCy Schubert 			/* First byte of OIDs encoded to save a byte */
109*7f2fe78bSCy Schubert 			if (i == 0) {
110*7f2fe78bSCy Schubert 				int first, second;
111*7f2fe78bSCy Schubert 				if (*p < 40) {
112*7f2fe78bSCy Schubert 					first = 0;
113*7f2fe78bSCy Schubert 					second = *p;
114*7f2fe78bSCy Schubert 				}
115*7f2fe78bSCy Schubert 				else if (40 <= *p && *p < 80) {
116*7f2fe78bSCy Schubert 					first = 1;
117*7f2fe78bSCy Schubert 					second = *p - 40;
118*7f2fe78bSCy Schubert 				}
119*7f2fe78bSCy Schubert 				else if (80 <= *p && *p < 127) {
120*7f2fe78bSCy Schubert 					first = 2;
121*7f2fe78bSCy Schubert 					second = *p - 80;
122*7f2fe78bSCy Schubert 				}
123*7f2fe78bSCy Schubert 				else {
124*7f2fe78bSCy Schubert 					/* Invalid value! */
125*7f2fe78bSCy Schubert 					first = -1;
126*7f2fe78bSCy Schubert 					second = -1;
127*7f2fe78bSCy Schubert 				}
128*7f2fe78bSCy Schubert 				fprintf(stderr, " %u %u", first, second);
129*7f2fe78bSCy Schubert 				p++;
130*7f2fe78bSCy Schubert 			}
131*7f2fe78bSCy Schubert 			else {
132*7f2fe78bSCy Schubert 				fprintf(stderr, " %u", (unsigned char)*p++);
133*7f2fe78bSCy Schubert 			}
134*7f2fe78bSCy Schubert 		fprintf(stderr, " }\n");
135*7f2fe78bSCy Schubert 	}
136*7f2fe78bSCy Schubert 	fprintf(stderr, "     qop: %d\n", ptr->qop);
137*7f2fe78bSCy Schubert 	fprintf(stderr, "     service: %d\n", ptr->svc);
138*7f2fe78bSCy Schubert 	fprintf(stderr, "     cred: %p\n", ptr->cred);
139*7f2fe78bSCy Schubert 	fprintf(stderr, "     req_flags: 0x%08x", ptr->req_flags);
140*7f2fe78bSCy Schubert }
141*7f2fe78bSCy Schubert #endif /*DEBUG*/
142*7f2fe78bSCy Schubert 
143*7f2fe78bSCy Schubert struct rpc_gss_data {
144*7f2fe78bSCy Schubert 	bool_t			 established;	/* context established */
145*7f2fe78bSCy Schubert 	bool_t			 inprogress;
146*7f2fe78bSCy Schubert   gss_buffer_desc      gc_wire_verf; /* save GSS_S_COMPLETE NULL RPC verfier
147*7f2fe78bSCy Schubert                                    * to process at end of context negotiation*/
148*7f2fe78bSCy Schubert 	CLIENT			*clnt;		/* client handle */
149*7f2fe78bSCy Schubert 	gss_name_t		 name;		/* service name */
150*7f2fe78bSCy Schubert 	struct rpc_gss_sec	 sec;		/* security tuple */
151*7f2fe78bSCy Schubert 	gss_ctx_id_t		 ctx;		/* context id */
152*7f2fe78bSCy Schubert 	struct rpc_gss_cred	 gc;		/* client credentials */
153*7f2fe78bSCy Schubert 	uint32_t		 win;		/* sequence window */
154*7f2fe78bSCy Schubert };
155*7f2fe78bSCy Schubert 
156*7f2fe78bSCy Schubert #define	AUTH_PRIVATE(auth)	((struct rpc_gss_data *)auth->ah_private)
157*7f2fe78bSCy Schubert 
158*7f2fe78bSCy Schubert static struct timeval AUTH_TIMEOUT = { 25, 0 };
159*7f2fe78bSCy Schubert 
160*7f2fe78bSCy Schubert AUTH *
authgss_create(CLIENT * clnt,gss_name_t name,struct rpc_gss_sec * sec)161*7f2fe78bSCy Schubert authgss_create(CLIENT *clnt, gss_name_t name, struct rpc_gss_sec *sec)
162*7f2fe78bSCy Schubert {
163*7f2fe78bSCy Schubert 	AUTH			*auth, *save_auth;
164*7f2fe78bSCy Schubert 	struct rpc_gss_data	*gd;
165*7f2fe78bSCy Schubert 	OM_uint32		min_stat = 0;
166*7f2fe78bSCy Schubert 
167*7f2fe78bSCy Schubert 	log_debug("in authgss_create()");
168*7f2fe78bSCy Schubert 
169*7f2fe78bSCy Schubert 	memset(&rpc_createerr, 0, sizeof(rpc_createerr));
170*7f2fe78bSCy Schubert 
171*7f2fe78bSCy Schubert 	if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
172*7f2fe78bSCy Schubert 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
173*7f2fe78bSCy Schubert 		rpc_createerr.cf_error.re_errno = ENOMEM;
174*7f2fe78bSCy Schubert 		return (NULL);
175*7f2fe78bSCy Schubert 	}
176*7f2fe78bSCy Schubert 	if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
177*7f2fe78bSCy Schubert 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
178*7f2fe78bSCy Schubert 		rpc_createerr.cf_error.re_errno = ENOMEM;
179*7f2fe78bSCy Schubert 		free(auth);
180*7f2fe78bSCy Schubert 		return (NULL);
181*7f2fe78bSCy Schubert 	}
182*7f2fe78bSCy Schubert 	if (name != GSS_C_NO_NAME) {
183*7f2fe78bSCy Schubert 		if (gss_duplicate_name(&min_stat, name, &gd->name)
184*7f2fe78bSCy Schubert 						!= GSS_S_COMPLETE) {
185*7f2fe78bSCy Schubert 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
186*7f2fe78bSCy Schubert 			rpc_createerr.cf_error.re_errno = ENOMEM;
187*7f2fe78bSCy Schubert 			free(auth);
188*7f2fe78bSCy Schubert 			free(gd);
189*7f2fe78bSCy Schubert 			return (NULL);
190*7f2fe78bSCy Schubert 		}
191*7f2fe78bSCy Schubert 	}
192*7f2fe78bSCy Schubert 	else
193*7f2fe78bSCy Schubert 		gd->name = name;
194*7f2fe78bSCy Schubert 
195*7f2fe78bSCy Schubert 	gd->clnt = clnt;
196*7f2fe78bSCy Schubert 	gd->ctx = GSS_C_NO_CONTEXT;
197*7f2fe78bSCy Schubert 	gd->sec = *sec;
198*7f2fe78bSCy Schubert 
199*7f2fe78bSCy Schubert 	gd->gc.gc_v = RPCSEC_GSS_VERSION;
200*7f2fe78bSCy Schubert 	gd->gc.gc_proc = RPCSEC_GSS_INIT;
201*7f2fe78bSCy Schubert 	gd->gc.gc_svc = gd->sec.svc;
202*7f2fe78bSCy Schubert 
203*7f2fe78bSCy Schubert 	auth->ah_ops = &authgss_ops;
204*7f2fe78bSCy Schubert 	auth->ah_private = (caddr_t)gd;
205*7f2fe78bSCy Schubert 
206*7f2fe78bSCy Schubert 	save_auth = clnt->cl_auth;
207*7f2fe78bSCy Schubert 	clnt->cl_auth = auth;
208*7f2fe78bSCy Schubert 
209*7f2fe78bSCy Schubert 	if (!authgss_refresh(auth, NULL))
210*7f2fe78bSCy Schubert 		auth = NULL;
211*7f2fe78bSCy Schubert 
212*7f2fe78bSCy Schubert 	clnt->cl_auth = save_auth;
213*7f2fe78bSCy Schubert 
214*7f2fe78bSCy Schubert 	log_debug("authgss_create returning auth 0x%08x", auth);
215*7f2fe78bSCy Schubert 	return (auth);
216*7f2fe78bSCy Schubert }
217*7f2fe78bSCy Schubert 
218*7f2fe78bSCy Schubert AUTH *
authgss_create_default(CLIENT * clnt,char * service,struct rpc_gss_sec * sec)219*7f2fe78bSCy Schubert authgss_create_default(CLIENT *clnt, char *service, struct rpc_gss_sec *sec)
220*7f2fe78bSCy Schubert {
221*7f2fe78bSCy Schubert 	AUTH			*auth;
222*7f2fe78bSCy Schubert 	OM_uint32		 maj_stat = 0, min_stat = 0;
223*7f2fe78bSCy Schubert 	gss_buffer_desc		 sname;
224*7f2fe78bSCy Schubert 	gss_name_t		 name;
225*7f2fe78bSCy Schubert 
226*7f2fe78bSCy Schubert 	log_debug("in authgss_create_default()");
227*7f2fe78bSCy Schubert 
228*7f2fe78bSCy Schubert 
229*7f2fe78bSCy Schubert 	sname.value = service;
230*7f2fe78bSCy Schubert 	sname.length = strlen(service);
231*7f2fe78bSCy Schubert 
232*7f2fe78bSCy Schubert 	maj_stat = gss_import_name(&min_stat, &sname,
233*7f2fe78bSCy Schubert 		(gss_OID)gss_nt_service_name,
234*7f2fe78bSCy Schubert 		&name);
235*7f2fe78bSCy Schubert 
236*7f2fe78bSCy Schubert 	if (maj_stat != GSS_S_COMPLETE) {
237*7f2fe78bSCy Schubert 		log_status("gss_import_name", maj_stat, min_stat);
238*7f2fe78bSCy Schubert 		rpc_createerr.cf_stat = RPC_AUTHERROR;
239*7f2fe78bSCy Schubert 		return (NULL);
240*7f2fe78bSCy Schubert 	}
241*7f2fe78bSCy Schubert 
242*7f2fe78bSCy Schubert 	auth = authgss_create(clnt, name, sec);
243*7f2fe78bSCy Schubert 
244*7f2fe78bSCy Schubert  	if (name != GSS_C_NO_NAME)
245*7f2fe78bSCy Schubert  		gss_release_name(&min_stat, &name);
246*7f2fe78bSCy Schubert 
247*7f2fe78bSCy Schubert 	log_debug("authgss_create_default returning auth 0x%08x", auth);
248*7f2fe78bSCy Schubert 	return (auth);
249*7f2fe78bSCy Schubert }
250*7f2fe78bSCy Schubert 
251*7f2fe78bSCy Schubert bool_t
authgss_get_private_data(AUTH * auth,struct authgss_private_data * pd)252*7f2fe78bSCy Schubert authgss_get_private_data(AUTH *auth, struct authgss_private_data *pd)
253*7f2fe78bSCy Schubert {
254*7f2fe78bSCy Schubert 	struct rpc_gss_data	*gd;
255*7f2fe78bSCy Schubert 
256*7f2fe78bSCy Schubert 	log_debug("in authgss_get_private_data()");
257*7f2fe78bSCy Schubert 
258*7f2fe78bSCy Schubert 	if (!auth || !pd)
259*7f2fe78bSCy Schubert 		return (FALSE);
260*7f2fe78bSCy Schubert 
261*7f2fe78bSCy Schubert 	gd = AUTH_PRIVATE(auth);
262*7f2fe78bSCy Schubert 
263*7f2fe78bSCy Schubert 	if (!gd || !gd->established)
264*7f2fe78bSCy Schubert 		return (FALSE);
265*7f2fe78bSCy Schubert 
266*7f2fe78bSCy Schubert 	pd->pd_ctx = gd->ctx;
267*7f2fe78bSCy Schubert 	pd->pd_ctx_hndl = gd->gc.gc_ctx;
268*7f2fe78bSCy Schubert 	pd->pd_seq_win = gd->win;
269*7f2fe78bSCy Schubert 
270*7f2fe78bSCy Schubert 	return (TRUE);
271*7f2fe78bSCy Schubert }
272*7f2fe78bSCy Schubert 
273*7f2fe78bSCy Schubert static void
authgss_nextverf(AUTH * auth)274*7f2fe78bSCy Schubert authgss_nextverf(AUTH *auth)
275*7f2fe78bSCy Schubert {
276*7f2fe78bSCy Schubert 	log_debug("in authgss_nextverf()\n");
277*7f2fe78bSCy Schubert 	/* no action necessary */
278*7f2fe78bSCy Schubert }
279*7f2fe78bSCy Schubert 
280*7f2fe78bSCy Schubert static bool_t
authgss_marshal(AUTH * auth,XDR * xdrs)281*7f2fe78bSCy Schubert authgss_marshal(AUTH *auth, XDR *xdrs)
282*7f2fe78bSCy Schubert {
283*7f2fe78bSCy Schubert 	XDR			 tmpxdrs;
284*7f2fe78bSCy Schubert 	char			 tmp[MAX_AUTH_BYTES];
285*7f2fe78bSCy Schubert 	struct rpc_gss_data	*gd;
286*7f2fe78bSCy Schubert 	gss_buffer_desc		 rpcbuf, checksum;
287*7f2fe78bSCy Schubert 	OM_uint32		 maj_stat, min_stat;
288*7f2fe78bSCy Schubert 	bool_t			 xdr_stat;
289*7f2fe78bSCy Schubert 
290*7f2fe78bSCy Schubert 	log_debug("in authgss_marshal()");
291*7f2fe78bSCy Schubert 
292*7f2fe78bSCy Schubert 	gd = AUTH_PRIVATE(auth);
293*7f2fe78bSCy Schubert 
294*7f2fe78bSCy Schubert 	if (gd->established)
295*7f2fe78bSCy Schubert 		gd->gc.gc_seq++;
296*7f2fe78bSCy Schubert 
297*7f2fe78bSCy Schubert 	xdrmem_create(&tmpxdrs, tmp, sizeof(tmp), XDR_ENCODE);
298*7f2fe78bSCy Schubert 
299*7f2fe78bSCy Schubert 	if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gc)) {
300*7f2fe78bSCy Schubert 		XDR_DESTROY(&tmpxdrs);
301*7f2fe78bSCy Schubert 		return (FALSE);
302*7f2fe78bSCy Schubert 	}
303*7f2fe78bSCy Schubert 	auth->ah_cred.oa_flavor = RPCSEC_GSS;
304*7f2fe78bSCy Schubert 	auth->ah_cred.oa_base = tmp;
305*7f2fe78bSCy Schubert 	auth->ah_cred.oa_length = XDR_GETPOS(&tmpxdrs);
306*7f2fe78bSCy Schubert 
307*7f2fe78bSCy Schubert 	XDR_DESTROY(&tmpxdrs);
308*7f2fe78bSCy Schubert 
309*7f2fe78bSCy Schubert 	if (!xdr_opaque_auth(xdrs, &auth->ah_cred))
310*7f2fe78bSCy Schubert 		return (FALSE);
311*7f2fe78bSCy Schubert 
312*7f2fe78bSCy Schubert 	if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
313*7f2fe78bSCy Schubert 	    gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
314*7f2fe78bSCy Schubert 		return (xdr_opaque_auth(xdrs, &gssrpc__null_auth));
315*7f2fe78bSCy Schubert 	}
316*7f2fe78bSCy Schubert 	/* Checksum serialized RPC header, up to and including credential. */
317*7f2fe78bSCy Schubert 	rpcbuf.length = XDR_GETPOS(xdrs);
318*7f2fe78bSCy Schubert 	XDR_SETPOS(xdrs, 0);
319*7f2fe78bSCy Schubert 	rpcbuf.value = XDR_INLINE(xdrs, (int)rpcbuf.length);
320*7f2fe78bSCy Schubert 
321*7f2fe78bSCy Schubert 	maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
322*7f2fe78bSCy Schubert 			    &rpcbuf, &checksum);
323*7f2fe78bSCy Schubert 
324*7f2fe78bSCy Schubert 	if (maj_stat != GSS_S_COMPLETE) {
325*7f2fe78bSCy Schubert 		log_status("gss_get_mic", maj_stat, min_stat);
326*7f2fe78bSCy Schubert 		if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
327*7f2fe78bSCy Schubert 			gd->established = FALSE;
328*7f2fe78bSCy Schubert 			authgss_destroy_context(auth);
329*7f2fe78bSCy Schubert 		}
330*7f2fe78bSCy Schubert 		return (FALSE);
331*7f2fe78bSCy Schubert 	}
332*7f2fe78bSCy Schubert 	auth->ah_verf.oa_flavor = RPCSEC_GSS;
333*7f2fe78bSCy Schubert 	auth->ah_verf.oa_base = checksum.value;
334*7f2fe78bSCy Schubert 	auth->ah_verf.oa_length = checksum.length;
335*7f2fe78bSCy Schubert 
336*7f2fe78bSCy Schubert 	xdr_stat = xdr_opaque_auth(xdrs, &auth->ah_verf);
337*7f2fe78bSCy Schubert 	gss_release_buffer(&min_stat, &checksum);
338*7f2fe78bSCy Schubert 
339*7f2fe78bSCy Schubert 	return (xdr_stat);
340*7f2fe78bSCy Schubert }
341*7f2fe78bSCy Schubert 
342*7f2fe78bSCy Schubert static bool_t
authgss_validate(AUTH * auth,struct opaque_auth * verf)343*7f2fe78bSCy Schubert authgss_validate(AUTH *auth, struct opaque_auth *verf)
344*7f2fe78bSCy Schubert {
345*7f2fe78bSCy Schubert 	struct rpc_gss_data	*gd;
346*7f2fe78bSCy Schubert 	uint32_t		 num;
347*7f2fe78bSCy Schubert 	gss_qop_t		 qop_state;
348*7f2fe78bSCy Schubert 	gss_buffer_desc		 signbuf, checksum;
349*7f2fe78bSCy Schubert 	OM_uint32		 maj_stat, min_stat;
350*7f2fe78bSCy Schubert 
351*7f2fe78bSCy Schubert 	log_debug("in authgss_validate()");
352*7f2fe78bSCy Schubert 
353*7f2fe78bSCy Schubert 	gd = AUTH_PRIVATE(auth);
354*7f2fe78bSCy Schubert 
355*7f2fe78bSCy Schubert 	if (gd->established == FALSE) {
356*7f2fe78bSCy Schubert 		/* would like to do this only on NULL rpc - gc->established is good enough.
357*7f2fe78bSCy Schubert 		 * save the on the wire verifier to validate last INIT phase packet
358*7f2fe78bSCy Schubert 		 * after decode if the major status is GSS_S_COMPLETE
359*7f2fe78bSCy Schubert 		 */
360*7f2fe78bSCy Schubert 		if ((gd->gc_wire_verf.value = mem_alloc(verf->oa_length)) == NULL) {
361*7f2fe78bSCy Schubert 			fprintf(stderr, "gss_validate: out of memory\n");
362*7f2fe78bSCy Schubert 			return (FALSE);
363*7f2fe78bSCy Schubert 		}
364*7f2fe78bSCy Schubert 		memcpy(gd->gc_wire_verf.value, verf->oa_base, verf->oa_length);
365*7f2fe78bSCy Schubert 		gd->gc_wire_verf.length = verf->oa_length;
366*7f2fe78bSCy Schubert 		return (TRUE);
367*7f2fe78bSCy Schubert   	}
368*7f2fe78bSCy Schubert 
369*7f2fe78bSCy Schubert 	if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
370*7f2fe78bSCy Schubert 	    gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
371*7f2fe78bSCy Schubert 		num = htonl(gd->win);
372*7f2fe78bSCy Schubert 	}
373*7f2fe78bSCy Schubert 	else num = htonl(gd->gc.gc_seq);
374*7f2fe78bSCy Schubert 
375*7f2fe78bSCy Schubert 	signbuf.value = &num;
376*7f2fe78bSCy Schubert 	signbuf.length = sizeof(num);
377*7f2fe78bSCy Schubert 
378*7f2fe78bSCy Schubert 	checksum.value = verf->oa_base;
379*7f2fe78bSCy Schubert 	checksum.length = verf->oa_length;
380*7f2fe78bSCy Schubert 
381*7f2fe78bSCy Schubert 	maj_stat = gss_verify_mic(&min_stat, gd->ctx, &signbuf,
382*7f2fe78bSCy Schubert 				  &checksum, &qop_state);
383*7f2fe78bSCy Schubert 	if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) {
384*7f2fe78bSCy Schubert 		log_status("gss_verify_mic", maj_stat, min_stat);
385*7f2fe78bSCy Schubert 		if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
386*7f2fe78bSCy Schubert 			gd->established = FALSE;
387*7f2fe78bSCy Schubert 			authgss_destroy_context(auth);
388*7f2fe78bSCy Schubert 		}
389*7f2fe78bSCy Schubert 		return (FALSE);
390*7f2fe78bSCy Schubert 	}
391*7f2fe78bSCy Schubert 	return (TRUE);
392*7f2fe78bSCy Schubert }
393*7f2fe78bSCy Schubert 
394*7f2fe78bSCy Schubert static bool_t
authgss_refresh(AUTH * auth,struct rpc_msg * msg)395*7f2fe78bSCy Schubert authgss_refresh(AUTH *auth, struct rpc_msg *msg)
396*7f2fe78bSCy Schubert {
397*7f2fe78bSCy Schubert 	struct rpc_gss_data	*gd;
398*7f2fe78bSCy Schubert 	struct rpc_gss_init_res	 gr;
399*7f2fe78bSCy Schubert 	gss_buffer_desc		*recv_tokenp, send_token;
400*7f2fe78bSCy Schubert 	OM_uint32		 maj_stat, min_stat, call_stat, ret_flags;
401*7f2fe78bSCy Schubert 
402*7f2fe78bSCy Schubert 	log_debug("in authgss_refresh()");
403*7f2fe78bSCy Schubert 
404*7f2fe78bSCy Schubert 	gd = AUTH_PRIVATE(auth);
405*7f2fe78bSCy Schubert 
406*7f2fe78bSCy Schubert 	if (gd->established || gd->inprogress)
407*7f2fe78bSCy Schubert 		return (TRUE);
408*7f2fe78bSCy Schubert 
409*7f2fe78bSCy Schubert 	/* GSS context establishment loop. */
410*7f2fe78bSCy Schubert 	memset(&gr, 0, sizeof(gr));
411*7f2fe78bSCy Schubert 	recv_tokenp = GSS_C_NO_BUFFER;
412*7f2fe78bSCy Schubert 
413*7f2fe78bSCy Schubert #ifdef DEBUG
414*7f2fe78bSCy Schubert 	print_rpc_gss_sec(&gd->sec);
415*7f2fe78bSCy Schubert #endif /*DEBUG*/
416*7f2fe78bSCy Schubert 
417*7f2fe78bSCy Schubert 	for (;;) {
418*7f2fe78bSCy Schubert 		gd->inprogress = TRUE;
419*7f2fe78bSCy Schubert 		maj_stat = gss_init_sec_context(&min_stat,
420*7f2fe78bSCy Schubert 						gd->sec.cred,
421*7f2fe78bSCy Schubert 						&gd->ctx,
422*7f2fe78bSCy Schubert 						gd->name,
423*7f2fe78bSCy Schubert 						gd->sec.mech,
424*7f2fe78bSCy Schubert 						gd->sec.req_flags,
425*7f2fe78bSCy Schubert 						0,		/* time req */
426*7f2fe78bSCy Schubert 						GSS_C_NO_CHANNEL_BINDINGS,
427*7f2fe78bSCy Schubert 						recv_tokenp,
428*7f2fe78bSCy Schubert 						NULL,		/* used mech */
429*7f2fe78bSCy Schubert 						&send_token,
430*7f2fe78bSCy Schubert 						&ret_flags,
431*7f2fe78bSCy Schubert 						NULL);		/* time rec */
432*7f2fe78bSCy Schubert 
433*7f2fe78bSCy Schubert 		log_status("gss_init_sec_context", maj_stat, min_stat);
434*7f2fe78bSCy Schubert 		if (recv_tokenp != GSS_C_NO_BUFFER) {
435*7f2fe78bSCy Schubert 			free(gr.gr_token.value);
436*7f2fe78bSCy Schubert 			gr.gr_token.value = NULL;
437*7f2fe78bSCy Schubert 			recv_tokenp = GSS_C_NO_BUFFER;
438*7f2fe78bSCy Schubert 		}
439*7f2fe78bSCy Schubert 		if (maj_stat != GSS_S_COMPLETE &&
440*7f2fe78bSCy Schubert 		    maj_stat != GSS_S_CONTINUE_NEEDED) {
441*7f2fe78bSCy Schubert 			log_status("gss_init_sec_context (error)", maj_stat, min_stat);
442*7f2fe78bSCy Schubert 			break;
443*7f2fe78bSCy Schubert 		}
444*7f2fe78bSCy Schubert 		if (send_token.length != 0) {
445*7f2fe78bSCy Schubert 			memset(&gr, 0, sizeof(gr));
446*7f2fe78bSCy Schubert 
447*7f2fe78bSCy Schubert 			call_stat = clnt_call(gd->clnt, NULLPROC,
448*7f2fe78bSCy Schubert 					      xdr_rpc_gss_init_args,
449*7f2fe78bSCy Schubert 					      &send_token,
450*7f2fe78bSCy Schubert 					      xdr_rpc_gss_init_res,
451*7f2fe78bSCy Schubert 					      (caddr_t)&gr, AUTH_TIMEOUT);
452*7f2fe78bSCy Schubert 
453*7f2fe78bSCy Schubert 			gss_release_buffer(&min_stat, &send_token);
454*7f2fe78bSCy Schubert 
455*7f2fe78bSCy Schubert 			log_debug("authgss_refresh: call_stat=%d", call_stat);
456*7f2fe78bSCy Schubert 			log_debug("%s", clnt_sperror(gd->clnt, "authgss_refresh"));
457*7f2fe78bSCy Schubert 			if (call_stat != RPC_SUCCESS ||
458*7f2fe78bSCy Schubert 			    (gr.gr_major != GSS_S_COMPLETE &&
459*7f2fe78bSCy Schubert 			     gr.gr_major != GSS_S_CONTINUE_NEEDED))
460*7f2fe78bSCy Schubert 				break;
461*7f2fe78bSCy Schubert 
462*7f2fe78bSCy Schubert 			if (gr.gr_ctx.length != 0) {
463*7f2fe78bSCy Schubert 				free(gd->gc.gc_ctx.value);
464*7f2fe78bSCy Schubert 				gd->gc.gc_ctx = gr.gr_ctx;
465*7f2fe78bSCy Schubert 			}
466*7f2fe78bSCy Schubert 			if (gr.gr_token.length != 0) {
467*7f2fe78bSCy Schubert 				if (maj_stat != GSS_S_CONTINUE_NEEDED)
468*7f2fe78bSCy Schubert 					break;
469*7f2fe78bSCy Schubert 				recv_tokenp = &gr.gr_token;
470*7f2fe78bSCy Schubert 			}
471*7f2fe78bSCy Schubert 			gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
472*7f2fe78bSCy Schubert 		}
473*7f2fe78bSCy Schubert 
474*7f2fe78bSCy Schubert 		/* GSS_S_COMPLETE => check gss header verifier, usually checked in
475*7f2fe78bSCy Schubert 		 * gss_validate
476*7f2fe78bSCy Schubert 		 */
477*7f2fe78bSCy Schubert 		if (maj_stat == GSS_S_COMPLETE) {
478*7f2fe78bSCy Schubert 			gss_buffer_desc   bufin;
479*7f2fe78bSCy Schubert 			gss_buffer_desc   bufout;
480*7f2fe78bSCy Schubert 			uint32_t seq;
481*7f2fe78bSCy Schubert 			gss_qop_t qop_state = 0;
482*7f2fe78bSCy Schubert 
483*7f2fe78bSCy Schubert 			seq = htonl(gr.gr_win);
484*7f2fe78bSCy Schubert 			bufin.value = (u_char *)&seq;
485*7f2fe78bSCy Schubert 			bufin.length = sizeof(seq);
486*7f2fe78bSCy Schubert 			bufout.value = (u_char *)gd->gc_wire_verf.value;
487*7f2fe78bSCy Schubert 			bufout.length = gd->gc_wire_verf.length;
488*7f2fe78bSCy Schubert 
489*7f2fe78bSCy Schubert 			log_debug("authgss_refresh: GSS_S_COMPLETE: calling verify_mic");
490*7f2fe78bSCy Schubert 			maj_stat = gss_verify_mic(&min_stat,gd->ctx,
491*7f2fe78bSCy Schubert 				&bufin, &bufout, &qop_state);
492*7f2fe78bSCy Schubert 			free(gd->gc_wire_verf.value);
493*7f2fe78bSCy Schubert 			gd->gc_wire_verf.length = 0;
494*7f2fe78bSCy Schubert 			gd->gc_wire_verf.value = NULL;
495*7f2fe78bSCy Schubert 
496*7f2fe78bSCy Schubert 			if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) {
497*7f2fe78bSCy Schubert 				log_status("gss_verify_mic", maj_stat, min_stat);
498*7f2fe78bSCy Schubert 				if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
499*7f2fe78bSCy Schubert 					gd->established = FALSE;
500*7f2fe78bSCy Schubert 					authgss_destroy_context(auth);
501*7f2fe78bSCy Schubert 				}
502*7f2fe78bSCy Schubert 				return (FALSE);
503*7f2fe78bSCy Schubert 			}
504*7f2fe78bSCy Schubert 			gd->established = TRUE;
505*7f2fe78bSCy Schubert 			gd->inprogress = FALSE;
506*7f2fe78bSCy Schubert 			gd->gc.gc_proc = RPCSEC_GSS_DATA;
507*7f2fe78bSCy Schubert 			gd->gc.gc_seq = 0;
508*7f2fe78bSCy Schubert 			gd->win = gr.gr_win;
509*7f2fe78bSCy Schubert 			break;
510*7f2fe78bSCy Schubert 		}
511*7f2fe78bSCy Schubert 	}
512*7f2fe78bSCy Schubert 	log_status("authgss_refresh: at end of context negotiation", maj_stat, min_stat);
513*7f2fe78bSCy Schubert 	/* End context negotiation loop. */
514*7f2fe78bSCy Schubert 	if (gd->gc.gc_proc != RPCSEC_GSS_DATA) {
515*7f2fe78bSCy Schubert 		log_debug("authgss_refresh: returning ERROR (gc_proc %d)", gd->gc.gc_proc);
516*7f2fe78bSCy Schubert 		free(gr.gr_token.value);
517*7f2fe78bSCy Schubert 		authgss_destroy(auth);
518*7f2fe78bSCy Schubert 		auth = NULL;
519*7f2fe78bSCy Schubert 		rpc_createerr.cf_stat = RPC_AUTHERROR;
520*7f2fe78bSCy Schubert 
521*7f2fe78bSCy Schubert 		return (FALSE);
522*7f2fe78bSCy Schubert 	}
523*7f2fe78bSCy Schubert 	log_debug("authgss_refresh: returning SUCCESS");
524*7f2fe78bSCy Schubert 	return (TRUE);
525*7f2fe78bSCy Schubert }
526*7f2fe78bSCy Schubert 
527*7f2fe78bSCy Schubert bool_t
authgss_service(AUTH * auth,int svc)528*7f2fe78bSCy Schubert authgss_service(AUTH *auth, int svc)
529*7f2fe78bSCy Schubert {
530*7f2fe78bSCy Schubert 	struct rpc_gss_data	*gd;
531*7f2fe78bSCy Schubert 
532*7f2fe78bSCy Schubert 	log_debug("in authgss_service()");
533*7f2fe78bSCy Schubert 
534*7f2fe78bSCy Schubert 	if (!auth)
535*7f2fe78bSCy Schubert 		return(FALSE);
536*7f2fe78bSCy Schubert 	gd = AUTH_PRIVATE(auth);
537*7f2fe78bSCy Schubert 	if (!gd || !gd->established)
538*7f2fe78bSCy Schubert 		return (FALSE);
539*7f2fe78bSCy Schubert 	gd->sec.svc = svc;
540*7f2fe78bSCy Schubert 	gd->gc.gc_svc = svc;
541*7f2fe78bSCy Schubert 	return (TRUE);
542*7f2fe78bSCy Schubert }
543*7f2fe78bSCy Schubert 
544*7f2fe78bSCy Schubert static void
authgss_destroy_context(AUTH * auth)545*7f2fe78bSCy Schubert authgss_destroy_context(AUTH *auth)
546*7f2fe78bSCy Schubert {
547*7f2fe78bSCy Schubert 	struct rpc_gss_data	*gd;
548*7f2fe78bSCy Schubert 	OM_uint32		 min_stat;
549*7f2fe78bSCy Schubert 
550*7f2fe78bSCy Schubert 	log_debug("in authgss_destroy_context()");
551*7f2fe78bSCy Schubert 
552*7f2fe78bSCy Schubert 	gd = AUTH_PRIVATE(auth);
553*7f2fe78bSCy Schubert 
554*7f2fe78bSCy Schubert 	if (gd->gc.gc_ctx.length != 0) {
555*7f2fe78bSCy Schubert 		if (gd->established) {
556*7f2fe78bSCy Schubert 			gd->gc.gc_proc = RPCSEC_GSS_DESTROY;
557*7f2fe78bSCy Schubert 			(void)clnt_call(gd->clnt, NULLPROC, xdr_void, NULL,
558*7f2fe78bSCy Schubert 					xdr_void, NULL, AUTH_TIMEOUT);
559*7f2fe78bSCy Schubert 			log_debug("%s",
560*7f2fe78bSCy Schubert 				  clnt_sperror(gd->clnt,
561*7f2fe78bSCy Schubert 					       "authgss_destroy_context"));
562*7f2fe78bSCy Schubert 		}
563*7f2fe78bSCy Schubert 		free(gd->gc.gc_ctx.value);
564*7f2fe78bSCy Schubert 		/* XXX ANDROS check size of context  - should be 8 */
565*7f2fe78bSCy Schubert 		memset(&gd->gc.gc_ctx, 0, sizeof(gd->gc.gc_ctx));
566*7f2fe78bSCy Schubert 	}
567*7f2fe78bSCy Schubert 	if (gd->ctx != GSS_C_NO_CONTEXT) {
568*7f2fe78bSCy Schubert 		gss_delete_sec_context(&min_stat, &gd->ctx, NULL);
569*7f2fe78bSCy Schubert 		gd->ctx = GSS_C_NO_CONTEXT;
570*7f2fe78bSCy Schubert 	}
571*7f2fe78bSCy Schubert 	gd->established = FALSE;
572*7f2fe78bSCy Schubert 
573*7f2fe78bSCy Schubert 	log_debug("finished authgss_destroy_context()");
574*7f2fe78bSCy Schubert }
575*7f2fe78bSCy Schubert 
576*7f2fe78bSCy Schubert static void
authgss_destroy(AUTH * auth)577*7f2fe78bSCy Schubert authgss_destroy(AUTH *auth)
578*7f2fe78bSCy Schubert {
579*7f2fe78bSCy Schubert 	struct rpc_gss_data	*gd;
580*7f2fe78bSCy Schubert 	OM_uint32		 min_stat;
581*7f2fe78bSCy Schubert 
582*7f2fe78bSCy Schubert 	log_debug("in authgss_destroy()");
583*7f2fe78bSCy Schubert 
584*7f2fe78bSCy Schubert 	gd = AUTH_PRIVATE(auth);
585*7f2fe78bSCy Schubert 
586*7f2fe78bSCy Schubert 	authgss_destroy_context(auth);
587*7f2fe78bSCy Schubert 
588*7f2fe78bSCy Schubert 	if (gd->name != GSS_C_NO_NAME)
589*7f2fe78bSCy Schubert 		gss_release_name(&min_stat, &gd->name);
590*7f2fe78bSCy Schubert 
591*7f2fe78bSCy Schubert 	free(gd);
592*7f2fe78bSCy Schubert 	free(auth);
593*7f2fe78bSCy Schubert }
594*7f2fe78bSCy Schubert 
595*7f2fe78bSCy Schubert bool_t
authgss_wrap(AUTH * auth,XDR * xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)596*7f2fe78bSCy Schubert authgss_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
597*7f2fe78bSCy Schubert {
598*7f2fe78bSCy Schubert 	struct rpc_gss_data	*gd;
599*7f2fe78bSCy Schubert 
600*7f2fe78bSCy Schubert 	log_debug("in authgss_wrap()");
601*7f2fe78bSCy Schubert 
602*7f2fe78bSCy Schubert 	gd = AUTH_PRIVATE(auth);
603*7f2fe78bSCy Schubert 
604*7f2fe78bSCy Schubert 	if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
605*7f2fe78bSCy Schubert 		return ((*xdr_func)(xdrs, xdr_ptr));
606*7f2fe78bSCy Schubert 	}
607*7f2fe78bSCy Schubert 	return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
608*7f2fe78bSCy Schubert 				 gd->ctx, gd->sec.qop,
609*7f2fe78bSCy Schubert 				 gd->sec.svc, gd->gc.gc_seq));
610*7f2fe78bSCy Schubert }
611*7f2fe78bSCy Schubert 
612*7f2fe78bSCy Schubert bool_t
authgss_unwrap(AUTH * auth,XDR * xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)613*7f2fe78bSCy Schubert authgss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
614*7f2fe78bSCy Schubert {
615*7f2fe78bSCy Schubert 	struct rpc_gss_data	*gd;
616*7f2fe78bSCy Schubert 
617*7f2fe78bSCy Schubert 	log_debug("in authgss_unwrap()");
618*7f2fe78bSCy Schubert 
619*7f2fe78bSCy Schubert 	gd = AUTH_PRIVATE(auth);
620*7f2fe78bSCy Schubert 
621*7f2fe78bSCy Schubert 	if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
622*7f2fe78bSCy Schubert 		return ((*xdr_func)(xdrs, xdr_ptr));
623*7f2fe78bSCy Schubert 	}
624*7f2fe78bSCy Schubert 	return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
625*7f2fe78bSCy Schubert 				 gd->ctx, gd->sec.qop,
626*7f2fe78bSCy Schubert 				 gd->sec.svc, gd->gc.gc_seq));
627*7f2fe78bSCy Schubert }
628