xref: /freebsd/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c (revision 62fd20bde5a6473143ae5091a562241eca156c30)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  * Copyright (c) 1990 The Regents of the University of California.
4  *
5  * Copyright (c) 2008 Doug Rabson
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 /*
30   svc_rpcsec_gss.c
31 
32   Copyright (c) 2000 The Regents of the University of Michigan.
33   All rights reserved.
34 
35   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
36   All rights reserved, all wrongs reversed.
37 
38   Redistribution and use in source and binary forms, with or without
39   modification, are permitted provided that the following conditions
40   are met:
41 
42   1. Redistributions of source code must retain the above copyright
43      notice, this list of conditions and the following disclaimer.
44   2. Redistributions in binary form must reproduce the above copyright
45      notice, this list of conditions and the following disclaimer in the
46      documentation and/or other materials provided with the distribution.
47   3. Neither the name of the University nor the names of its
48      contributors may be used to endorse or promote products derived
49      from this software without specific prior written permission.
50 
51   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
52   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
54   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
58   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
59   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
60   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
61   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 
63   $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
64  */
65 
66 #include <sys/param.h>
67 #include <sys/systm.h>
68 #include <sys/jail.h>
69 #include <sys/kernel.h>
70 #include <sys/kobj.h>
71 #include <sys/lock.h>
72 #include <sys/malloc.h>
73 #include <sys/mbuf.h>
74 #include <sys/mutex.h>
75 #include <sys/proc.h>
76 #include <sys/sx.h>
77 #include <sys/ucred.h>
78 
79 #include <rpc/rpc.h>
80 #include <rpc/rpcsec_gss.h>
81 
82 #include "rpcsec_gss_int.h"
83 
84 static bool_t   svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
85 static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
86 static void     svc_rpc_gss_release(SVCAUTH *);
87 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
88 static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
89 
90 static const struct svc_auth_ops svc_auth_gss_ops = {
91 	.svc_ah_wrap =		svc_rpc_gss_wrap,
92 	.svc_ah_unwrap =	svc_rpc_gss_unwrap,
93 	.svc_ah_release =	svc_rpc_gss_release,
94 };
95 
96 struct sx svc_rpc_gss_lock;
97 
98 struct svc_rpc_gss_callback {
99 	SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
100 	rpc_gss_callback_t	cb_callback;
101 };
102 SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback);
103 KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_callback_list,
104     svc_rpc_gss_callbacks) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
105 
106 struct svc_rpc_gss_svc_name {
107 	SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
108 	char			*sn_principal;
109 	gss_OID			sn_mech;
110 	u_int			sn_req_time;
111 	gss_cred_id_t		sn_cred;
112 	u_int			sn_program;
113 	u_int			sn_version;
114 };
115 SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name);
116 KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_svc_name_list,
117     svc_rpc_gss_svc_names) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
118 
119 enum svc_rpc_gss_client_state {
120 	CLIENT_NEW,				/* still authenticating */
121 	CLIENT_ESTABLISHED,			/* context established */
122 	CLIENT_STALE				/* garbage to collect */
123 };
124 
125 #define SVC_RPC_GSS_SEQWINDOW	128
126 
127 struct svc_rpc_gss_clientid {
128 	unsigned long		ci_hostid;
129 	uint32_t		ci_boottime;
130 	uint32_t		ci_id;
131 };
132 
133 struct svc_rpc_gss_client {
134 	TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
135 	TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
136 	volatile u_int		cl_refs;
137 	struct sx		cl_lock;
138 	struct svc_rpc_gss_clientid cl_id;
139 	time_t			cl_expiration;	/* when to gc */
140 	enum svc_rpc_gss_client_state cl_state;	/* client state */
141 	bool_t			cl_locked;	/* fixed service+qop */
142 	gss_ctx_id_t		cl_ctx;		/* context id */
143 	gss_cred_id_t		cl_creds;	/* delegated creds */
144 	gss_name_t		cl_cname;	/* client name */
145 	struct svc_rpc_gss_svc_name *cl_sname;	/* server name used */
146 	rpc_gss_rawcred_t	cl_rawcred;	/* raw credentials */
147 	rpc_gss_ucred_t		cl_ucred;	/* unix-style credentials */
148 	struct ucred		*cl_cred;	/* kernel-style credentials */
149 	int			cl_rpcflavor;	/* RPC pseudo sec flavor */
150 	bool_t			cl_done_callback; /* TRUE after call */
151 	void			*cl_cookie;	/* user cookie from callback */
152 	gid_t			cl_gid_storage[NGROUPS];
153 	gss_OID			cl_mech;	/* mechanism */
154 	gss_qop_t		cl_qop;		/* quality of protection */
155 	uint32_t		cl_seqlast;	/* sequence window origin */
156 	uint32_t		cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
157 };
158 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
159 
160 /*
161  * This structure holds enough information to unwrap arguments or wrap
162  * results for a given request. We use the rq_clntcred area for this
163  * (which is a per-request buffer).
164  */
165 struct svc_rpc_gss_cookedcred {
166 	struct svc_rpc_gss_client *cc_client;
167 	rpc_gss_service_t	cc_service;
168 	uint32_t		cc_seq;
169 };
170 
171 #define CLIENT_HASH_SIZE	256
172 #define CLIENT_MAX		1024
173 u_int svc_rpc_gss_client_max = CLIENT_MAX;
174 u_int svc_rpc_gss_client_hash_size = CLIENT_HASH_SIZE;
175 
176 SYSCTL_DECL(_kern_rpc);
177 SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
178     "GSS");
179 
180 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW,
181     &svc_rpc_gss_client_max, 0,
182     "Max number of rpc-gss clients");
183 
184 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_hash, CTLFLAG_RDTUN,
185     &svc_rpc_gss_client_hash_size, 0,
186     "Size of rpc-gss client hash table");
187 
188 static u_int svc_rpc_gss_lifetime_max = 0;
189 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, lifetime_max, CTLFLAG_RW,
190     &svc_rpc_gss_lifetime_max, 0,
191     "Maximum lifetime (seconds) of rpc-gss clients");
192 
193 static u_int svc_rpc_gss_client_count;
194 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
195     &svc_rpc_gss_client_count, 0,
196     "Number of rpc-gss clients");
197 
198 KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list *, svc_rpc_gss_client_hash);
199 KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list, svc_rpc_gss_clients);
200 KGSS_VNET_DEFINE_STATIC(uint32_t, svc_rpc_gss_next_clientid) = 1;
201 
202 static void
svc_rpc_gss_init(void * unused __unused)203 svc_rpc_gss_init(void *unused __unused)
204 {
205 
206 	svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
207 	sx_init(&svc_rpc_gss_lock, "gsslock");
208 }
209 SYSINIT(svc_rpc_gss_init, SI_SUB_VFS, SI_ORDER_ANY,
210     svc_rpc_gss_init, NULL);
211 
212 static void
svc_rpc_gss_cleanup(void * unused __unused)213 svc_rpc_gss_cleanup(void *unused __unused)
214 {
215 
216 	sx_destroy(&svc_rpc_gss_lock);
217 }
218 SYSUNINIT(svc_rpc_gss_cleanup, SI_SUB_VFS, SI_ORDER_ANY,
219     svc_rpc_gss_cleanup, NULL);
220 
221 static void
svc_rpc_gss_vnetinit(void * unused __unused)222 svc_rpc_gss_vnetinit(void *unused __unused)
223 {
224 	int i;
225 
226 	KGSS_VNET(svc_rpc_gss_client_hash) = mem_alloc(
227 	    sizeof(struct svc_rpc_gss_client_list) *
228 	    svc_rpc_gss_client_hash_size);
229 	for (i = 0; i < svc_rpc_gss_client_hash_size; i++)
230 		TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_client_hash)[i]);
231 	TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_clients));
232 }
233 VNET_SYSINIT(svc_rpc_gss_vnetinit, SI_SUB_VNET_DONE, SI_ORDER_ANY,
234     svc_rpc_gss_vnetinit, NULL);
235 
236 static void
svc_rpc_gss_vnet_cleanup(void * unused __unused)237 svc_rpc_gss_vnet_cleanup(void *unused __unused)
238 {
239 
240 	mem_free(KGSS_VNET(svc_rpc_gss_client_hash),
241 	    sizeof(struct svc_rpc_gss_client_list) *
242 	    svc_rpc_gss_client_hash_size);
243 }
244 VNET_SYSUNINIT(svc_rpc_gss_vnet_cleanup, SI_SUB_VNET_DONE, SI_ORDER_ANY,
245     svc_rpc_gss_vnet_cleanup, NULL);
246 
247 bool_t
rpc_gss_set_callback(rpc_gss_callback_t * cb)248 rpc_gss_set_callback(rpc_gss_callback_t *cb)
249 {
250 	struct svc_rpc_gss_callback *scb;
251 
252 	scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
253 	if (!scb) {
254 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
255 		return (FALSE);
256 	}
257 	scb->cb_callback = *cb;
258 	sx_xlock(&svc_rpc_gss_lock);
259 	SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_callbacks), scb, cb_link);
260 	sx_xunlock(&svc_rpc_gss_lock);
261 
262 	return (TRUE);
263 }
264 
265 void
rpc_gss_clear_callback(rpc_gss_callback_t * cb)266 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
267 {
268 	struct svc_rpc_gss_callback *scb;
269 
270 	sx_xlock(&svc_rpc_gss_lock);
271 	SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
272 		if (scb->cb_callback.program == cb->program
273 		    && scb->cb_callback.version == cb->version
274 		    && scb->cb_callback.callback == cb->callback) {
275 			SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_callbacks), scb,
276 			    svc_rpc_gss_callback, cb_link);
277 			sx_xunlock(&svc_rpc_gss_lock);
278 			mem_free(scb, sizeof(*scb));
279 			return;
280 		}
281 	}
282 	sx_xunlock(&svc_rpc_gss_lock);
283 }
284 
285 static bool_t
rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name * sname)286 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
287 {
288 	OM_uint32		maj_stat, min_stat;
289 	gss_buffer_desc		namebuf;
290 	gss_name_t		name;
291 	gss_OID_set_desc	oid_set;
292 
293 	oid_set.count = 1;
294 	oid_set.elements = sname->sn_mech;
295 
296 	namebuf.value = (void *) sname->sn_principal;
297 	namebuf.length = strlen(sname->sn_principal);
298 
299 	maj_stat = gss_import_name(&min_stat, &namebuf,
300 				   GSS_C_NT_HOSTBASED_SERVICE, &name);
301 	if (maj_stat != GSS_S_COMPLETE)
302 		return (FALSE);
303 
304 	if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
305 		gss_release_cred(&min_stat, &sname->sn_cred);
306 
307 	maj_stat = gss_acquire_cred(&min_stat, name,
308 	    sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
309 	    NULL, NULL);
310 	if (maj_stat != GSS_S_COMPLETE) {
311 		gss_release_name(&min_stat, &name);
312 		return (FALSE);
313 	}
314 	gss_release_name(&min_stat, &name);
315 
316 	return (TRUE);
317 }
318 
319 bool_t
rpc_gss_set_svc_name(const char * principal,const char * mechanism,u_int req_time,u_int program,u_int version)320 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
321     u_int req_time, u_int program, u_int version)
322 {
323 	struct svc_rpc_gss_svc_name *sname;
324 	gss_OID			mech_oid;
325 
326 	if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
327 		return (FALSE);
328 
329 	sname = mem_alloc(sizeof(*sname));
330 	if (!sname)
331 		return (FALSE);
332 	sname->sn_principal = strdup(principal, M_RPC);
333 	sname->sn_mech = mech_oid;
334 	sname->sn_req_time = req_time;
335 	sname->sn_cred = GSS_C_NO_CREDENTIAL;
336 	sname->sn_program = program;
337 	sname->sn_version = version;
338 
339 	if (!rpc_gss_acquire_svc_cred(sname)) {
340 		free(sname->sn_principal, M_RPC);
341 		mem_free(sname, sizeof(*sname));
342 		return (FALSE);
343 	}
344 
345 	sx_xlock(&svc_rpc_gss_lock);
346 	SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_svc_names), sname, sn_link);
347 	sx_xunlock(&svc_rpc_gss_lock);
348 
349 	return (TRUE);
350 }
351 
352 void
rpc_gss_clear_svc_name(u_int program,u_int version)353 rpc_gss_clear_svc_name(u_int program, u_int version)
354 {
355 	OM_uint32		min_stat;
356 	struct svc_rpc_gss_svc_name *sname;
357 
358 	sx_xlock(&svc_rpc_gss_lock);
359 	SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names), sn_link) {
360 		if (sname->sn_program == program
361 		    && sname->sn_version == version) {
362 			SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_svc_names), sname,
363 			    svc_rpc_gss_svc_name, sn_link);
364 			sx_xunlock(&svc_rpc_gss_lock);
365 			gss_release_cred(&min_stat, &sname->sn_cred);
366 			free(sname->sn_principal, M_RPC);
367 			mem_free(sname, sizeof(*sname));
368 			return;
369 		}
370 	}
371 	sx_xunlock(&svc_rpc_gss_lock);
372 }
373 
374 bool_t
rpc_gss_get_principal_name(rpc_gss_principal_t * principal,const char * mech,const char * name,const char * node,const char * domain)375 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
376     const char *mech, const char *name, const char *node, const char *domain)
377 {
378 	OM_uint32		maj_stat, min_stat;
379 	gss_OID			mech_oid;
380 	size_t			namelen;
381 	gss_buffer_desc		buf;
382 	gss_name_t		gss_name, gss_mech_name;
383 	rpc_gss_principal_t	result;
384 
385 	if (!rpc_gss_mech_to_oid(mech, &mech_oid))
386 		return (FALSE);
387 
388 	/*
389 	 * Construct a gss_buffer containing the full name formatted
390 	 * as "name/node@domain" where node and domain are optional.
391 	 */
392 	namelen = strlen(name) + 1;
393 	if (node) {
394 		namelen += strlen(node) + 1;
395 	}
396 	if (domain) {
397 		namelen += strlen(domain) + 1;
398 	}
399 
400 	buf.value = mem_alloc(namelen);
401 	buf.length = namelen;
402 	strcpy((char *) buf.value, name);
403 	if (node) {
404 		strcat((char *) buf.value, "/");
405 		strcat((char *) buf.value, node);
406 	}
407 	if (domain) {
408 		strcat((char *) buf.value, "@");
409 		strcat((char *) buf.value, domain);
410 	}
411 
412 	/*
413 	 * Convert that to a gss_name_t and then convert that to a
414 	 * mechanism name in the selected mechanism.
415 	 */
416 	maj_stat = gss_import_name(&min_stat, &buf,
417 	    GSS_C_NT_USER_NAME, &gss_name);
418 	mem_free(buf.value, buf.length);
419 	if (maj_stat != GSS_S_COMPLETE) {
420 		rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
421 		return (FALSE);
422 	}
423 	maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
424 	    &gss_mech_name);
425 	if (maj_stat != GSS_S_COMPLETE) {
426 		rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
427 		    min_stat);
428 		gss_release_name(&min_stat, &gss_name);
429 		return (FALSE);
430 	}
431 	gss_release_name(&min_stat, &gss_name);
432 
433 	/*
434 	 * Export the mechanism name and use that to construct the
435 	 * rpc_gss_principal_t result.
436 	 */
437 	maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
438 	if (maj_stat != GSS_S_COMPLETE) {
439 		rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
440 		gss_release_name(&min_stat, &gss_mech_name);
441 		return (FALSE);
442 	}
443 	gss_release_name(&min_stat, &gss_mech_name);
444 
445 	result = mem_alloc(sizeof(int) + buf.length);
446 	if (!result) {
447 		gss_release_buffer(&min_stat, &buf);
448 		return (FALSE);
449 	}
450 	result->len = buf.length;
451 	memcpy(result->name, buf.value, buf.length);
452 	gss_release_buffer(&min_stat, &buf);
453 
454 	*principal = result;
455 	return (TRUE);
456 }
457 
458 /*
459  * Note that the ip_addr and srv_principal pointers can point to the same
460  * buffer, so long as ip_addr is at least strlen(srv_name) + 1 > srv_principal.
461  */
462 bool_t
rpc_gss_ip_to_srv_principal(char * ip_addr,const char * srv_name,char * srv_principal)463 rpc_gss_ip_to_srv_principal(char *ip_addr, const char *srv_name,
464     char *srv_principal)
465 {
466 	OM_uint32		maj_stat, min_stat;
467 	size_t			len;
468 
469 	/*
470 	 * First fill in the service name and '@'.
471 	 */
472 	len = strlen(srv_name);
473 	if (len > NI_MAXSERV)
474 		return (FALSE);
475 	memcpy(srv_principal, srv_name, len);
476 	srv_principal[len] = '@';
477 
478 	/*
479 	 * Do reverse DNS to get the DNS name for the ip_addr.
480 	 */
481 	maj_stat = gss_ip_to_dns(&min_stat, ip_addr, &srv_principal[len + 1]);
482 	if (maj_stat != GSS_S_COMPLETE) {
483 		rpc_gss_log_status("gss_ip_to_dns", NULL, maj_stat, min_stat);
484 		return (FALSE);
485 	}
486 	return (TRUE);
487 }
488 
489 bool_t
rpc_gss_getcred(struct svc_req * req,rpc_gss_rawcred_t ** rcred,rpc_gss_ucred_t ** ucred,void ** cookie)490 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
491     rpc_gss_ucred_t **ucred, void **cookie)
492 {
493 	struct svc_rpc_gss_cookedcred *cc;
494 	struct svc_rpc_gss_client *client;
495 
496 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
497 		return (FALSE);
498 
499 	cc = req->rq_clntcred;
500 	client = cc->cc_client;
501 	if (rcred)
502 		*rcred = &client->cl_rawcred;
503 	if (ucred)
504 		*ucred = &client->cl_ucred;
505 	if (cookie)
506 		*cookie = client->cl_cookie;
507 	return (TRUE);
508 }
509 
510 /*
511  * This simpler interface is used by svc_getcred to copy the cred data
512  * into a kernel cred structure.
513  */
514 static int
rpc_gss_svc_getcred(struct svc_req * req,struct ucred ** crp,int * flavorp)515 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
516 {
517 	struct ucred *cr;
518 	struct svc_rpc_gss_cookedcred *cc;
519 	struct svc_rpc_gss_client *client;
520 	rpc_gss_ucred_t *uc;
521 
522 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
523 		return (FALSE);
524 
525 	cc = req->rq_clntcred;
526 	client = cc->cc_client;
527 
528 	if (flavorp)
529 		*flavorp = client->cl_rpcflavor;
530 
531 	if (client->cl_cred) {
532 		*crp = crhold(client->cl_cred);
533 		return (TRUE);
534 	}
535 
536 	uc = &client->cl_ucred;
537 	cr = client->cl_cred = crget();
538 	cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
539 	cr->cr_rgid = cr->cr_svgid = uc->gid;
540 	crsetgroups_and_egid(cr, uc->gidlen, uc->gidlist, uc->gid);
541 	cr->cr_prison = curthread->td_ucred->cr_prison;
542 	prison_hold(cr->cr_prison);
543 	*crp = crhold(cr);
544 
545 	return (TRUE);
546 }
547 
548 int
rpc_gss_svc_max_data_length(struct svc_req * req,int max_tp_unit_len)549 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
550 {
551 	struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
552 	struct svc_rpc_gss_client *client = cc->cc_client;
553 	int			want_conf;
554 	OM_uint32		max;
555 	OM_uint32		maj_stat, min_stat;
556 	int			result;
557 
558 	switch (client->cl_rawcred.service) {
559 	case rpc_gss_svc_none:
560 		return (max_tp_unit_len);
561 		break;
562 
563 	case rpc_gss_svc_default:
564 	case rpc_gss_svc_integrity:
565 		want_conf = FALSE;
566 		break;
567 
568 	case rpc_gss_svc_privacy:
569 		want_conf = TRUE;
570 		break;
571 
572 	default:
573 		return (0);
574 	}
575 
576 	maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
577 	    client->cl_qop, max_tp_unit_len, &max);
578 
579 	if (maj_stat == GSS_S_COMPLETE) {
580 		result = (int) max;
581 		if (result < 0)
582 			result = 0;
583 		return (result);
584 	} else {
585 		rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
586 		    maj_stat, min_stat);
587 		return (0);
588 	}
589 }
590 
591 static struct svc_rpc_gss_client *
svc_rpc_gss_find_client(struct svc_rpc_gss_clientid * id)592 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
593 {
594 	struct svc_rpc_gss_client *client;
595 	struct svc_rpc_gss_client_list *list;
596 	struct timeval boottime;
597 	unsigned long hostid;
598 
599 	rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
600 
601 	getcredhostid(curthread->td_ucred, &hostid);
602 	getboottime(&boottime);
603 	if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
604 		return (NULL);
605 
606 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
607 	    [id->ci_id % svc_rpc_gss_client_hash_size];
608 	sx_xlock(&svc_rpc_gss_lock);
609 	TAILQ_FOREACH(client, list, cl_link) {
610 		if (client->cl_id.ci_id == id->ci_id) {
611 			/*
612 			 * Move this client to the front of the LRU
613 			 * list.
614 			 */
615 			TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client,
616 			    cl_alllink);
617 			TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients),
618 			    client, cl_alllink);
619 			refcount_acquire(&client->cl_refs);
620 			break;
621 		}
622 	}
623 	sx_xunlock(&svc_rpc_gss_lock);
624 
625 	return (client);
626 }
627 
628 static struct svc_rpc_gss_client *
svc_rpc_gss_create_client(void)629 svc_rpc_gss_create_client(void)
630 {
631 	struct svc_rpc_gss_client *client;
632 	struct svc_rpc_gss_client_list *list;
633 	struct timeval boottime;
634 	unsigned long hostid;
635 
636 	rpc_gss_log_debug("in svc_rpc_gss_create_client()");
637 
638 	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
639 	memset(client, 0, sizeof(struct svc_rpc_gss_client));
640 
641 	/*
642 	 * Set the initial value of cl_refs to two.  One for the caller
643 	 * and the other to hold onto the client structure until it expires.
644 	 */
645 	refcount_init(&client->cl_refs, 2);
646 	sx_init(&client->cl_lock, "GSS-client");
647 	getcredhostid(curthread->td_ucred, &hostid);
648 	client->cl_id.ci_hostid = hostid;
649 	getboottime(&boottime);
650 	client->cl_id.ci_boottime = boottime.tv_sec;
651 	client->cl_id.ci_id = KGSS_VNET(svc_rpc_gss_next_clientid)++;
652 
653 	/*
654 	 * Start the client off with a short expiration time. We will
655 	 * try to get a saner value from the client creds later.
656 	 */
657 	client->cl_state = CLIENT_NEW;
658 	client->cl_locked = FALSE;
659 	client->cl_expiration = time_uptime + 5*60;
660 
661 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
662 	    [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
663 	sx_xlock(&svc_rpc_gss_lock);
664 	TAILQ_INSERT_HEAD(list, client, cl_link);
665 	TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink);
666 	svc_rpc_gss_client_count++;
667 	sx_xunlock(&svc_rpc_gss_lock);
668 	return (client);
669 }
670 
671 static void
svc_rpc_gss_destroy_client(struct svc_rpc_gss_client * client)672 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
673 {
674 	OM_uint32 min_stat;
675 
676 	rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
677 
678 	if (client->cl_ctx)
679 		gss_delete_sec_context(&min_stat,
680 		    &client->cl_ctx, GSS_C_NO_BUFFER);
681 
682 	if (client->cl_cname)
683 		gss_release_name(&min_stat, &client->cl_cname);
684 
685 	if (client->cl_rawcred.client_principal)
686 		mem_free(client->cl_rawcred.client_principal,
687 		    sizeof(*client->cl_rawcred.client_principal)
688 		    + client->cl_rawcred.client_principal->len);
689 
690 	if (client->cl_cred)
691 		crfree(client->cl_cred);
692 
693 	sx_destroy(&client->cl_lock);
694 	mem_free(client, sizeof(*client));
695 }
696 
697 /*
698  * Drop a reference to a client and free it if that was the last reference.
699  */
700 static void
svc_rpc_gss_release_client(struct svc_rpc_gss_client * client)701 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
702 {
703 
704 	if (!refcount_release(&client->cl_refs))
705 		return;
706 	svc_rpc_gss_destroy_client(client);
707 }
708 
709 /*
710  * Remove a client from our global lists.
711  * Must be called with svc_rpc_gss_lock held.
712  */
713 static void
svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client * client)714 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
715 {
716 	struct svc_rpc_gss_client_list *list;
717 
718 	sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
719 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
720 	    [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
721 	TAILQ_REMOVE(list, client, cl_link);
722 	TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink);
723 	svc_rpc_gss_client_count--;
724 }
725 
726 /*
727  * Remove a client from our global lists and free it if we can.
728  */
729 static void
svc_rpc_gss_forget_client(struct svc_rpc_gss_client * client)730 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
731 {
732 	struct svc_rpc_gss_client_list *list;
733 	struct svc_rpc_gss_client *tclient;
734 
735 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
736 	    [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
737 	sx_xlock(&svc_rpc_gss_lock);
738 	TAILQ_FOREACH(tclient, list, cl_link) {
739 		/*
740 		 * Make sure this client has not already been removed
741 		 * from the lists by svc_rpc_gss_forget_client() or
742 		 * svc_rpc_gss_forget_client_locked().
743 		 */
744 		if (client == tclient) {
745 			svc_rpc_gss_forget_client_locked(client);
746 			sx_xunlock(&svc_rpc_gss_lock);
747 			svc_rpc_gss_release_client(client);
748 			return;
749 		}
750 	}
751 	sx_xunlock(&svc_rpc_gss_lock);
752 }
753 
754 static void
svc_rpc_gss_timeout_clients(void)755 svc_rpc_gss_timeout_clients(void)
756 {
757 	struct svc_rpc_gss_client *client;
758 	time_t now = time_uptime;
759 
760 	rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
761 
762 	/*
763 	 * First enforce the max client limit. We keep
764 	 * svc_rpc_gss_clients in LRU order.
765 	 */
766 	sx_xlock(&svc_rpc_gss_lock);
767 	client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients),
768 	    svc_rpc_gss_client_list);
769 	while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
770 		svc_rpc_gss_forget_client_locked(client);
771 		sx_xunlock(&svc_rpc_gss_lock);
772 		svc_rpc_gss_release_client(client);
773 		sx_xlock(&svc_rpc_gss_lock);
774 		client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients),
775 		    svc_rpc_gss_client_list);
776 	}
777 again:
778 	TAILQ_FOREACH(client, &KGSS_VNET(svc_rpc_gss_clients), cl_alllink) {
779 		if (client->cl_state == CLIENT_STALE
780 		    || now > client->cl_expiration) {
781 			svc_rpc_gss_forget_client_locked(client);
782 			sx_xunlock(&svc_rpc_gss_lock);
783 			rpc_gss_log_debug("expiring client %p", client);
784 			svc_rpc_gss_release_client(client);
785 			sx_xlock(&svc_rpc_gss_lock);
786 			goto again;
787 		}
788 	}
789 	sx_xunlock(&svc_rpc_gss_lock);
790 }
791 
792 #ifdef DEBUG
793 /*
794  * OID<->string routines.  These are uuuuugly.
795  */
796 static OM_uint32
gss_oid_to_str(OM_uint32 * minor_status,gss_OID oid,gss_buffer_t oid_str)797 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
798 {
799 	char		numstr[128];
800 	unsigned long	number;
801 	int		numshift;
802 	size_t		string_length;
803 	size_t		i;
804 	unsigned char	*cp;
805 	char		*bp;
806 
807 	/* Decoded according to krb5/gssapi_krb5.c */
808 
809 	/* First determine the size of the string */
810 	string_length = 0;
811 	number = 0;
812 	numshift = 0;
813 	cp = (unsigned char *) oid->elements;
814 	number = (unsigned long) cp[0];
815 	sprintf(numstr, "%ld ", number/40);
816 	string_length += strlen(numstr);
817 	sprintf(numstr, "%ld ", number%40);
818 	string_length += strlen(numstr);
819 	for (i=1; i<oid->length; i++) {
820 		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
821 			number = (number << 7) | (cp[i] & 0x7f);
822 			numshift += 7;
823 		}
824 		else {
825 			*minor_status = 0;
826 			return(GSS_S_FAILURE);
827 		}
828 		if ((cp[i] & 0x80) == 0) {
829 			sprintf(numstr, "%ld ", number);
830 			string_length += strlen(numstr);
831 			number = 0;
832 			numshift = 0;
833 		}
834 	}
835 	/*
836 	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
837 	 * here for "{ " and "}\0".
838 	 */
839 	string_length += 4;
840 	if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
841 		strcpy(bp, "{ ");
842 		number = (unsigned long) cp[0];
843 		sprintf(numstr, "%ld ", number/40);
844 		strcat(bp, numstr);
845 		sprintf(numstr, "%ld ", number%40);
846 		strcat(bp, numstr);
847 		number = 0;
848 		cp = (unsigned char *) oid->elements;
849 		for (i=1; i<oid->length; i++) {
850 			number = (number << 7) | (cp[i] & 0x7f);
851 			if ((cp[i] & 0x80) == 0) {
852 				sprintf(numstr, "%ld ", number);
853 				strcat(bp, numstr);
854 				number = 0;
855 			}
856 		}
857 		strcat(bp, "}");
858 		oid_str->length = strlen(bp)+1;
859 		oid_str->value = (void *) bp;
860 		*minor_status = 0;
861 		return(GSS_S_COMPLETE);
862 	}
863 	*minor_status = 0;
864 	return(GSS_S_FAILURE);
865 }
866 #endif
867 
868 static void
svc_rpc_gss_build_ucred(struct svc_rpc_gss_client * client,const gss_name_t name)869 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
870     const gss_name_t name)
871 {
872 	OM_uint32		maj_stat, min_stat;
873 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
874 	int			numgroups;
875 
876 	uc->uid = 65534;
877 	uc->gid = 65534;
878 	uc->gidlist = client->cl_gid_storage;
879 
880 	numgroups = NGROUPS;
881 	maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
882 	    &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
883 	if (GSS_ERROR(maj_stat))
884 		uc->gidlen = 0;
885 	else
886 		uc->gidlen = numgroups;
887 }
888 
889 static void
svc_rpc_gss_set_flavor(struct svc_rpc_gss_client * client)890 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
891 {
892 	static gss_OID_desc krb5_mech_oid =
893 		{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
894 
895 	/*
896 	 * Attempt to translate mech type and service into a
897 	 * 'pseudo flavor'. Hardwire in krb5 support for now.
898 	 */
899 	if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
900 		switch (client->cl_rawcred.service) {
901 		case rpc_gss_svc_default:
902 		case rpc_gss_svc_none:
903 			client->cl_rpcflavor = RPCSEC_GSS_KRB5;
904 			break;
905 		case rpc_gss_svc_integrity:
906 			client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
907 			break;
908 		case rpc_gss_svc_privacy:
909 			client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
910 			break;
911 		}
912 	} else {
913 		client->cl_rpcflavor = RPCSEC_GSS;
914 	}
915 }
916 
917 static bool_t
svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client * client,struct svc_req * rqst,struct rpc_gss_init_res * gr,struct rpc_gss_cred * gc)918 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
919 			       struct svc_req *rqst,
920 			       struct rpc_gss_init_res *gr,
921 			       struct rpc_gss_cred *gc)
922 {
923 	gss_buffer_desc		recv_tok;
924 	gss_OID			mech;
925 	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
926 	OM_uint32		cred_lifetime;
927 	struct svc_rpc_gss_svc_name *sname;
928 	gss_buffer_desc		export_name;
929 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
930 	int			numgroups;
931 	static enum krb_imp	my_krb_imp = KRBIMP_UNKNOWN;
932 
933 	rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
934 
935 	if (my_krb_imp == KRBIMP_UNKNOWN) {
936 		maj_stat = gss_supports_lucid(&min_stat, NULL);
937 		if (maj_stat == GSS_S_COMPLETE)
938 			my_krb_imp = KRBIMP_MIT;
939 		else
940 			my_krb_imp = KRBIMP_HEIMDALV1;
941 		min_stat = 0;
942 	}
943 
944 	if (my_krb_imp == KRBIMP_MIT) {
945 		uc->uid = 65534;
946 		uc->gid = 65534;
947 		uc->gidlist = client->cl_gid_storage;
948 		numgroups = NGROUPS;
949 	}
950 
951 	/* Deserialize arguments. */
952 	memset(&recv_tok, 0, sizeof(recv_tok));
953 
954 	if (!svc_getargs(rqst,
955 		(xdrproc_t) xdr_gss_buffer_desc,
956 		(caddr_t) &recv_tok)) {
957 		client->cl_state = CLIENT_STALE;
958 		return (FALSE);
959 	}
960 
961 	/*
962 	 * First time round, try all the server names we have until
963 	 * one matches. Afterwards, stick with that one.
964 	 */
965 	sx_xlock(&svc_rpc_gss_lock);
966 	if (!client->cl_sname) {
967 		SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names),
968 		    sn_link) {
969 			if (sname->sn_program == rqst->rq_prog
970 			    && sname->sn_version == rqst->rq_vers) {
971 			retry:
972 				if (my_krb_imp == KRBIMP_MIT)
973 					gr->gr_major =
974 					    gss_accept_sec_context_lucid_v1(
975 						&gr->gr_minor,
976 						&client->cl_ctx,
977 						sname->sn_cred,
978 						&recv_tok,
979 						GSS_C_NO_CHANNEL_BINDINGS,
980 						&client->cl_cname,
981 						&mech,
982 						&gr->gr_token,
983 						&ret_flags,
984 						&cred_lifetime,
985 						&client->cl_creds,
986 						&export_name,
987 						&uc->uid,
988 						&uc->gid,
989 						&numgroups,
990 						&uc->gidlist[0]);
991 				else
992 					gr->gr_major = gss_accept_sec_context(
993 						&gr->gr_minor,
994 						&client->cl_ctx,
995 						sname->sn_cred,
996 						&recv_tok,
997 						GSS_C_NO_CHANNEL_BINDINGS,
998 						&client->cl_cname,
999 						&mech,
1000 						&gr->gr_token,
1001 						&ret_flags,
1002 						&cred_lifetime,
1003 						&client->cl_creds);
1004 				if (gr->gr_major ==
1005 				    GSS_S_CREDENTIALS_EXPIRED) {
1006 					/*
1007 					 * Either our creds really did
1008 					 * expire or gssd was
1009 					 * restarted.
1010 					 */
1011 					if (rpc_gss_acquire_svc_cred(sname))
1012 						goto retry;
1013 				}
1014 				client->cl_sname = sname;
1015 				break;
1016 			}
1017 		}
1018 		if (!sname) {
1019 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1020 			    (char *) &recv_tok);
1021 			sx_xunlock(&svc_rpc_gss_lock);
1022 			return (FALSE);
1023 		}
1024 	} else {
1025 		if (my_krb_imp == KRBIMP_MIT)
1026 			gr->gr_major = gss_accept_sec_context_lucid_v1(
1027 				&gr->gr_minor,
1028 				&client->cl_ctx,
1029 				client->cl_sname->sn_cred,
1030 				&recv_tok,
1031 				GSS_C_NO_CHANNEL_BINDINGS,
1032 				&client->cl_cname,
1033 				&mech,
1034 				&gr->gr_token,
1035 				&ret_flags,
1036 				&cred_lifetime,
1037 				NULL,
1038 				&export_name,
1039 				&uc->uid,
1040 				&uc->gid,
1041 				&numgroups,
1042 				&uc->gidlist[0]);
1043 		else
1044 			gr->gr_major = gss_accept_sec_context(
1045 				&gr->gr_minor,
1046 				&client->cl_ctx,
1047 				client->cl_sname->sn_cred,
1048 				&recv_tok,
1049 				GSS_C_NO_CHANNEL_BINDINGS,
1050 				&client->cl_cname,
1051 				&mech,
1052 				&gr->gr_token,
1053 				&ret_flags,
1054 				&cred_lifetime,
1055 				NULL);
1056 	}
1057 	sx_xunlock(&svc_rpc_gss_lock);
1058 
1059 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
1060 
1061 	/*
1062 	 * If we get an error from gss_accept_sec_context, send the
1063 	 * reply anyway so that the client gets a chance to see what
1064 	 * is wrong.
1065 	 */
1066 	if (gr->gr_major != GSS_S_COMPLETE &&
1067 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
1068 		rpc_gss_log_status("accept_sec_context", client->cl_mech,
1069 		    gr->gr_major, gr->gr_minor);
1070 		client->cl_state = CLIENT_STALE;
1071 		if (my_krb_imp == KRBIMP_MIT)
1072 			uc->gidlen = 0;
1073 		return (TRUE);
1074 	}
1075 	if (my_krb_imp == KRBIMP_MIT)
1076 		uc->gidlen = numgroups;
1077 
1078 	gr->gr_handle.value = &client->cl_id;
1079 	gr->gr_handle.length = sizeof(client->cl_id);
1080 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
1081 
1082 	/* Save client info. */
1083 	client->cl_mech = mech;
1084 	client->cl_qop = GSS_C_QOP_DEFAULT;
1085 	client->cl_done_callback = FALSE;
1086 
1087 	if (gr->gr_major == GSS_S_COMPLETE) {
1088 		/*
1089 		 * Change client expiration time to be near when the
1090 		 * client creds expire (or 24 hours if we can't figure
1091 		 * that out).
1092 		 */
1093 		if (cred_lifetime == GSS_C_INDEFINITE)
1094 			cred_lifetime = 24*60*60;
1095 
1096 		/*
1097 		 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set.
1098 		 */
1099 		if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime >
1100 		    svc_rpc_gss_lifetime_max)
1101 			cred_lifetime = svc_rpc_gss_lifetime_max;
1102 
1103 		client->cl_expiration = time_uptime + cred_lifetime;
1104 
1105 		/*
1106 		 * Fill in cred details in the rawcred structure.
1107 		 */
1108 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
1109 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
1110 		maj_stat = GSS_S_COMPLETE;
1111 		if (my_krb_imp != KRBIMP_MIT)
1112 			maj_stat = gss_export_name(&min_stat, client->cl_cname,
1113 			    &export_name);
1114 		if (maj_stat != GSS_S_COMPLETE) {
1115 			rpc_gss_log_status("gss_export_name", client->cl_mech,
1116 			    maj_stat, min_stat);
1117 			return (FALSE);
1118 		}
1119 		client->cl_rawcred.client_principal =
1120 			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
1121 			    + export_name.length);
1122 		client->cl_rawcred.client_principal->len = export_name.length;
1123 		memcpy(client->cl_rawcred.client_principal->name,
1124 		    export_name.value, export_name.length);
1125 		gss_release_buffer(&min_stat, &export_name);
1126 		client->cl_rawcred.svc_principal =
1127 			client->cl_sname->sn_principal;
1128 		client->cl_rawcred.service = gc->gc_svc;
1129 
1130 		/*
1131 		 * Use gss_pname_to_uid to map to unix creds. For
1132 		 * kerberos5, this uses krb5_aname_to_localname.
1133 		 */
1134 		if (my_krb_imp != KRBIMP_MIT)
1135 			svc_rpc_gss_build_ucred(client, client->cl_cname);
1136 		svc_rpc_gss_set_flavor(client);
1137 		gss_release_name(&min_stat, &client->cl_cname);
1138 
1139 #ifdef DEBUG
1140 		{
1141 			gss_buffer_desc mechname;
1142 
1143 			gss_oid_to_str(&min_stat, mech, &mechname);
1144 
1145 			rpc_gss_log_debug("accepted context for %s with "
1146 			    "<mech %.*s, qop %d, svc %d>",
1147 			    client->cl_rawcred.client_principal->name,
1148 			    mechname.length, (char *)mechname.value,
1149 			    client->cl_qop, client->cl_rawcred.service);
1150 
1151 			gss_release_buffer(&min_stat, &mechname);
1152 		}
1153 #endif /* DEBUG */
1154 	}
1155 	return (TRUE);
1156 }
1157 
1158 static bool_t
svc_rpc_gss_validate(struct svc_rpc_gss_client * client,struct rpc_msg * msg,gss_qop_t * qop,rpc_gss_proc_t gcproc)1159 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1160     gss_qop_t *qop, rpc_gss_proc_t gcproc)
1161 {
1162 	struct opaque_auth	*oa;
1163 	gss_buffer_desc		 rpcbuf, checksum;
1164 	OM_uint32		 maj_stat, min_stat;
1165 	gss_qop_t		 qop_state;
1166 	int32_t			 rpchdr[128 / sizeof(int32_t)];
1167 	int32_t			*buf;
1168 
1169 	rpc_gss_log_debug("in svc_rpc_gss_validate()");
1170 
1171 	memset(rpchdr, 0, sizeof(rpchdr));
1172 
1173 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
1174 	buf = rpchdr;
1175 	IXDR_PUT_LONG(buf, msg->rm_xid);
1176 	IXDR_PUT_ENUM(buf, msg->rm_direction);
1177 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1178 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1179 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1180 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1181 	oa = &msg->rm_call.cb_cred;
1182 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
1183 	IXDR_PUT_LONG(buf, oa->oa_length);
1184 	if (oa->oa_length) {
1185 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1186 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1187 	}
1188 	rpcbuf.value = rpchdr;
1189 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1190 
1191 	checksum.value = msg->rm_call.cb_verf.oa_base;
1192 	checksum.length = msg->rm_call.cb_verf.oa_length;
1193 
1194 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1195 				  &qop_state);
1196 
1197 	if (maj_stat != GSS_S_COMPLETE) {
1198 		rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1199 		    maj_stat, min_stat);
1200 		/*
1201 		 * A bug in some versions of the Linux client generates a
1202 		 * Destroy operation with a bogus encrypted checksum. Deleting
1203 		 * the credential handle for that case causes the mount to fail.
1204 		 * Since the checksum is bogus (gss_verify_mic() failed), it
1205 		 * doesn't make sense to destroy the handle and not doing so
1206 		 * fixes the Linux mount.
1207 		 */
1208 		if (gcproc != RPCSEC_GSS_DESTROY)
1209 			client->cl_state = CLIENT_STALE;
1210 		return (FALSE);
1211 	}
1212 
1213 	*qop = qop_state;
1214 	return (TRUE);
1215 }
1216 
1217 static bool_t
svc_rpc_gss_nextverf(struct svc_rpc_gss_client * client,struct svc_req * rqst,u_int seq)1218 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1219     struct svc_req *rqst, u_int seq)
1220 {
1221 	gss_buffer_desc		signbuf;
1222 	gss_buffer_desc		mic;
1223 	OM_uint32		maj_stat, min_stat;
1224 	uint32_t		nseq;
1225 
1226 	rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1227 
1228 	nseq = htonl(seq);
1229 	signbuf.value = &nseq;
1230 	signbuf.length = sizeof(nseq);
1231 
1232 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1233 	    &signbuf, &mic);
1234 
1235 	if (maj_stat != GSS_S_COMPLETE) {
1236 		rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1237 		client->cl_state = CLIENT_STALE;
1238 		return (FALSE);
1239 	}
1240 
1241 	KASSERT(mic.length <= MAX_AUTH_BYTES,
1242 	    ("MIC too large for RPCSEC_GSS"));
1243 
1244 	rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1245 	rqst->rq_verf.oa_length = mic.length;
1246 	bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1247 
1248 	gss_release_buffer(&min_stat, &mic);
1249 
1250 	return (TRUE);
1251 }
1252 
1253 static bool_t
svc_rpc_gss_callback(struct svc_rpc_gss_client * client,struct svc_req * rqst)1254 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1255 {
1256 	struct svc_rpc_gss_callback *scb;
1257 	rpc_gss_lock_t	lock;
1258 	void		*cookie;
1259 	bool_t		cb_res;
1260 	bool_t		result;
1261 
1262 	/*
1263 	 * See if we have a callback for this guy.
1264 	 */
1265 	result = TRUE;
1266 	SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
1267 		if (scb->cb_callback.program == rqst->rq_prog
1268 		    && scb->cb_callback.version == rqst->rq_vers) {
1269 			/*
1270 			 * This one matches. Call the callback and see
1271 			 * if it wants to veto or something.
1272 			 */
1273 			lock.locked = FALSE;
1274 			lock.raw_cred = &client->cl_rawcred;
1275 			cb_res = scb->cb_callback.callback(rqst,
1276 			    client->cl_creds,
1277 			    client->cl_ctx,
1278 			    &lock,
1279 			    &cookie);
1280 
1281 			if (!cb_res) {
1282 				client->cl_state = CLIENT_STALE;
1283 				result = FALSE;
1284 				break;
1285 			}
1286 
1287 			/*
1288 			 * The callback accepted the connection - it
1289 			 * is responsible for freeing client->cl_creds
1290 			 * now.
1291 			 */
1292 			client->cl_creds = GSS_C_NO_CREDENTIAL;
1293 			client->cl_locked = lock.locked;
1294 			client->cl_cookie = cookie;
1295 			return (TRUE);
1296 		}
1297 	}
1298 
1299 	/*
1300 	 * Either no callback exists for this program/version or one
1301 	 * of the callbacks rejected the connection. We just need to
1302 	 * clean up the delegated client creds, if any.
1303 	 */
1304 	if (client->cl_creds) {
1305 		OM_uint32 min_ver;
1306 		gss_release_cred(&min_ver, &client->cl_creds);
1307 	}
1308 	return (result);
1309 }
1310 
1311 static bool_t
svc_rpc_gss_check_replay(struct svc_rpc_gss_client * client,uint32_t seq)1312 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1313 {
1314 	uint32_t offset;
1315 	int word, bit;
1316 	bool_t result;
1317 
1318 	sx_xlock(&client->cl_lock);
1319 	if (seq <= client->cl_seqlast) {
1320 		/*
1321 		 * The request sequence number is less than
1322 		 * the largest we have seen so far. If it is
1323 		 * outside the window or if we have seen a
1324 		 * request with this sequence before, silently
1325 		 * discard it.
1326 		 */
1327 		offset = client->cl_seqlast - seq;
1328 		if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1329 			result = FALSE;
1330 			goto out;
1331 		}
1332 		word = offset / 32;
1333 		bit = offset % 32;
1334 		if (client->cl_seqmask[word] & (1 << bit)) {
1335 			result = FALSE;
1336 			goto out;
1337 		}
1338 	}
1339 
1340 	result = TRUE;
1341 out:
1342 	sx_xunlock(&client->cl_lock);
1343 	return (result);
1344 }
1345 
1346 static void
svc_rpc_gss_update_seq(struct svc_rpc_gss_client * client,uint32_t seq)1347 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1348 {
1349 	int offset, i, word, bit;
1350 	uint32_t carry, newcarry;
1351 
1352 	sx_xlock(&client->cl_lock);
1353 	if (seq > client->cl_seqlast) {
1354 		/*
1355 		 * This request has a sequence number greater
1356 		 * than any we have seen so far. Advance the
1357 		 * seq window and set bit zero of the window
1358 		 * (which corresponds to the new sequence
1359 		 * number)
1360 		 */
1361 		offset = seq - client->cl_seqlast;
1362 		while (offset > 32) {
1363 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1364 			     i > 0; i--) {
1365 				client->cl_seqmask[i] = client->cl_seqmask[i-1];
1366 			}
1367 			client->cl_seqmask[0] = 0;
1368 			offset -= 32;
1369 		}
1370 		carry = 0;
1371 		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1372 			newcarry = client->cl_seqmask[i] >> (32 - offset);
1373 			client->cl_seqmask[i] =
1374 				(client->cl_seqmask[i] << offset) | carry;
1375 			carry = newcarry;
1376 		}
1377 		client->cl_seqmask[0] |= 1;
1378 		client->cl_seqlast = seq;
1379 	} else {
1380 		offset = client->cl_seqlast - seq;
1381 		word = offset / 32;
1382 		bit = offset % 32;
1383 		client->cl_seqmask[word] |= (1 << bit);
1384 	}
1385 	sx_xunlock(&client->cl_lock);
1386 }
1387 
1388 enum auth_stat
svc_rpc_gss(struct svc_req * rqst,struct rpc_msg * msg)1389 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1390 
1391 {
1392 	OM_uint32		 min_stat;
1393 	XDR	 		 xdrs;
1394 	struct svc_rpc_gss_cookedcred *cc;
1395 	struct svc_rpc_gss_client *client;
1396 	struct rpc_gss_cred	 gc;
1397 	struct rpc_gss_init_res	 gr;
1398 	gss_qop_t		 qop;
1399 	int			 call_stat;
1400 	enum auth_stat		 result;
1401 
1402 	KGSS_CURVNET_SET_QUIET(KGSS_TD_TO_VNET(curthread));
1403 	rpc_gss_log_debug("in svc_rpc_gss()");
1404 
1405 	/* Garbage collect old clients. */
1406 	svc_rpc_gss_timeout_clients();
1407 
1408 	/* Initialize reply. */
1409 	rqst->rq_verf = _null_auth;
1410 
1411 	/* Deserialize client credentials. */
1412 	if (rqst->rq_cred.oa_length <= 0) {
1413 		KGSS_CURVNET_RESTORE();
1414 		return (AUTH_BADCRED);
1415 	}
1416 
1417 	memset(&gc, 0, sizeof(gc));
1418 
1419 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1420 	    rqst->rq_cred.oa_length, XDR_DECODE);
1421 
1422 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1423 		XDR_DESTROY(&xdrs);
1424 		KGSS_CURVNET_RESTORE();
1425 		return (AUTH_BADCRED);
1426 	}
1427 	XDR_DESTROY(&xdrs);
1428 
1429 	client = NULL;
1430 
1431 	/* Check version. */
1432 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
1433 		result = AUTH_BADCRED;
1434 		goto out;
1435 	}
1436 
1437 	/* Check the proc and find the client (or create it) */
1438 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
1439 		if (gc.gc_handle.length != 0) {
1440 			result = AUTH_BADCRED;
1441 			goto out;
1442 		}
1443 		client = svc_rpc_gss_create_client();
1444 	} else {
1445 		struct svc_rpc_gss_clientid *p;
1446 		if (gc.gc_handle.length != sizeof(*p)) {
1447 			result = AUTH_BADCRED;
1448 			goto out;
1449 		}
1450 		p = gc.gc_handle.value;
1451 		client = svc_rpc_gss_find_client(p);
1452 		if (!client) {
1453 			/*
1454 			 * Can't find the client - we may have
1455 			 * destroyed it - tell the other side to
1456 			 * re-authenticate.
1457 			 */
1458 			result = RPCSEC_GSS_CREDPROBLEM;
1459 			goto out;
1460 		}
1461 	}
1462 	cc = rqst->rq_clntcred;
1463 	cc->cc_client = client;
1464 	cc->cc_service = gc.gc_svc;
1465 	cc->cc_seq = gc.gc_seq;
1466 
1467 	/*
1468 	 * The service and sequence number must be ignored for
1469 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1470 	 */
1471 	if (gc.gc_proc != RPCSEC_GSS_INIT
1472 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1473 		/*
1474 		 * Check for sequence number overflow.
1475 		 */
1476 		if (gc.gc_seq >= MAXSEQ) {
1477 			result = RPCSEC_GSS_CTXPROBLEM;
1478 			goto out;
1479 		}
1480 
1481 		/*
1482 		 * Check for valid service.
1483 		 */
1484 		if (gc.gc_svc != rpc_gss_svc_none &&
1485 		    gc.gc_svc != rpc_gss_svc_integrity &&
1486 		    gc.gc_svc != rpc_gss_svc_privacy) {
1487 			result = AUTH_BADCRED;
1488 			goto out;
1489 		}
1490 	}
1491 
1492 	/* Handle RPCSEC_GSS control procedure. */
1493 	switch (gc.gc_proc) {
1494 
1495 	case RPCSEC_GSS_INIT:
1496 	case RPCSEC_GSS_CONTINUE_INIT:
1497 		if (rqst->rq_proc != NULLPROC) {
1498 			result = AUTH_REJECTEDCRED;
1499 			break;
1500 		}
1501 
1502 		memset(&gr, 0, sizeof(gr));
1503 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1504 			result = AUTH_REJECTEDCRED;
1505 			break;
1506 		}
1507 
1508 		if (gr.gr_major == GSS_S_COMPLETE) {
1509 			/*
1510 			 * We borrow the space for the call verf to
1511 			 * pack our reply verf.
1512 			 */
1513 			rqst->rq_verf = msg->rm_call.cb_verf;
1514 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1515 				result = AUTH_REJECTEDCRED;
1516 				break;
1517 			}
1518 		} else {
1519 			rqst->rq_verf = _null_auth;
1520 		}
1521 
1522 		call_stat = svc_sendreply(rqst,
1523 		    (xdrproc_t) xdr_rpc_gss_init_res,
1524 		    (caddr_t) &gr);
1525 
1526 		gss_release_buffer(&min_stat, &gr.gr_token);
1527 
1528 		if (!call_stat) {
1529 			result = AUTH_FAILED;
1530 			break;
1531 		}
1532 
1533 		if (gr.gr_major == GSS_S_COMPLETE)
1534 			client->cl_state = CLIENT_ESTABLISHED;
1535 
1536 		result = RPCSEC_GSS_NODISPATCH;
1537 		break;
1538 
1539 	case RPCSEC_GSS_DATA:
1540 	case RPCSEC_GSS_DESTROY:
1541 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1542 			result = RPCSEC_GSS_NODISPATCH;
1543 			break;
1544 		}
1545 
1546 		if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1547 			result = RPCSEC_GSS_CREDPROBLEM;
1548 			break;
1549 		}
1550 
1551 		/*
1552 		 * We borrow the space for the call verf to pack our
1553 		 * reply verf.
1554 		 */
1555 		rqst->rq_verf = msg->rm_call.cb_verf;
1556 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1557 			result = RPCSEC_GSS_CTXPROBLEM;
1558 			break;
1559 		}
1560 
1561 		svc_rpc_gss_update_seq(client, gc.gc_seq);
1562 
1563 		/*
1564 		 * Change the SVCAUTH ops on the request to point at
1565 		 * our own code so that we can unwrap the arguments
1566 		 * and wrap the result. The caller will re-set this on
1567 		 * every request to point to a set of null wrap/unwrap
1568 		 * methods. Acquire an extra reference to the client
1569 		 * which will be released by svc_rpc_gss_release()
1570 		 * after the request has finished processing.
1571 		 */
1572 		refcount_acquire(&client->cl_refs);
1573 		rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1574 		rqst->rq_auth.svc_ah_private = cc;
1575 
1576 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1577 			/*
1578 			 * We might be ready to do a callback to the server to
1579 			 * see if it wants to accept/reject the connection.
1580 			 */
1581 			sx_xlock(&client->cl_lock);
1582 			if (!client->cl_done_callback) {
1583 				client->cl_done_callback = TRUE;
1584 				client->cl_qop = qop;
1585 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1586 					client->cl_rawcred.mechanism, qop);
1587 				if (!svc_rpc_gss_callback(client, rqst)) {
1588 					result = AUTH_REJECTEDCRED;
1589 					sx_xunlock(&client->cl_lock);
1590 					break;
1591 				}
1592 			}
1593 			sx_xunlock(&client->cl_lock);
1594 
1595 			/*
1596 			 * If the server has locked this client to a
1597 			 * particular service+qop pair, enforce that
1598 			 * restriction now.
1599 			 */
1600 			if (client->cl_locked) {
1601 				if (client->cl_rawcred.service != gc.gc_svc) {
1602 					result = AUTH_FAILED;
1603 					break;
1604 				} else if (client->cl_qop != qop) {
1605 					result = AUTH_BADVERF;
1606 					break;
1607 				}
1608 			}
1609 
1610 			/*
1611 			 * If the qop changed, look up the new qop
1612 			 * name for rawcred.
1613 			 */
1614 			if (client->cl_qop != qop) {
1615 				client->cl_qop = qop;
1616 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1617 					client->cl_rawcred.mechanism, qop);
1618 			}
1619 
1620 			/*
1621 			 * Make sure we use the right service value
1622 			 * for unwrap/wrap.
1623 			 */
1624 			if (client->cl_rawcred.service != gc.gc_svc) {
1625 				client->cl_rawcred.service = gc.gc_svc;
1626 				svc_rpc_gss_set_flavor(client);
1627 			}
1628 
1629 			result = AUTH_OK;
1630 		} else {
1631 			if (rqst->rq_proc != NULLPROC) {
1632 				result = AUTH_REJECTEDCRED;
1633 				break;
1634 			}
1635 
1636 			call_stat = svc_sendreply(rqst,
1637 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1638 
1639 			if (!call_stat) {
1640 				result = AUTH_FAILED;
1641 				break;
1642 			}
1643 
1644 			svc_rpc_gss_forget_client(client);
1645 
1646 			result = RPCSEC_GSS_NODISPATCH;
1647 			break;
1648 		}
1649 		break;
1650 
1651 	default:
1652 		result = AUTH_BADCRED;
1653 		break;
1654 	}
1655 out:
1656 	if (client)
1657 		svc_rpc_gss_release_client(client);
1658 
1659 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1660 	KGSS_CURVNET_RESTORE();
1661 	return (result);
1662 }
1663 
1664 static bool_t
svc_rpc_gss_wrap(SVCAUTH * auth,struct mbuf ** mp)1665 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1666 {
1667 	struct svc_rpc_gss_cookedcred *cc;
1668 	struct svc_rpc_gss_client *client;
1669 
1670 	rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1671 
1672 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1673 	client = cc->cc_client;
1674 	if (client->cl_state != CLIENT_ESTABLISHED
1675 	    || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1676 		return (TRUE);
1677 	}
1678 
1679 	return (xdr_rpc_gss_wrap_data(mp,
1680 		client->cl_ctx, client->cl_qop,
1681 		cc->cc_service, cc->cc_seq));
1682 }
1683 
1684 static bool_t
svc_rpc_gss_unwrap(SVCAUTH * auth,struct mbuf ** mp)1685 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1686 {
1687 	struct svc_rpc_gss_cookedcred *cc;
1688 	struct svc_rpc_gss_client *client;
1689 
1690 	rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1691 
1692 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1693 	client = cc->cc_client;
1694 	if (client->cl_state != CLIENT_ESTABLISHED
1695 	    || cc->cc_service == rpc_gss_svc_none) {
1696 		return (TRUE);
1697 	}
1698 
1699 	return (xdr_rpc_gss_unwrap_data(mp,
1700 		client->cl_ctx, client->cl_qop,
1701 		cc->cc_service, cc->cc_seq));
1702 }
1703 
1704 static void
svc_rpc_gss_release(SVCAUTH * auth)1705 svc_rpc_gss_release(SVCAUTH *auth)
1706 {
1707 	struct svc_rpc_gss_cookedcred *cc;
1708 	struct svc_rpc_gss_client *client;
1709 
1710 	rpc_gss_log_debug("in svc_rpc_gss_release()");
1711 
1712 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1713 	client = cc->cc_client;
1714 	svc_rpc_gss_release_client(client);
1715 }
1716