xref: /freebsd/lib/librpcsec_gss/svc_rpcsec_gss.c (revision 0b3105a37d7adcadcb720112fed4dc4e8040be99)
1 /*-
2  * Copyright (c) 2008 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$FreeBSD$
27  */
28 /*
29   svc_rpcsec_gss.c
30 
31   Copyright (c) 2000 The Regents of the University of Michigan.
32   All rights reserved.
33 
34   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
35   All rights reserved, all wrongs reversed.
36 
37   Redistribution and use in source and binary forms, with or without
38   modification, are permitted provided that the following conditions
39   are met:
40 
41   1. Redistributions of source code must retain the above copyright
42      notice, this list of conditions and the following disclaimer.
43   2. Redistributions in binary form must reproduce the above copyright
44      notice, this list of conditions and the following disclaimer in the
45      documentation and/or other materials provided with the distribution.
46   3. Neither the name of the University nor the names of its
47      contributors may be used to endorse or promote products derived
48      from this software without specific prior written permission.
49 
50   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
51   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
57   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 
62   $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
63  */
64 
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <pwd.h>
69 #include <grp.h>
70 #include <errno.h>
71 #include <unistd.h>
72 #include <sys/queue.h>
73 #include <rpc/rpc.h>
74 #include <rpc/rpcsec_gss.h>
75 #include "rpcsec_gss_int.h"
76 
77 static bool_t	svc_rpc_gss_initialised = FALSE;
78 
79 static bool_t   svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
80 static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
81 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
82 
83 static struct svc_auth_ops svc_auth_gss_ops = {
84 	svc_rpc_gss_wrap,
85 	svc_rpc_gss_unwrap,
86 };
87 
88 struct svc_rpc_gss_callback {
89 	SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
90 	rpc_gss_callback_t	cb_callback;
91 };
92 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
93 	svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
94 
95 struct svc_rpc_gss_svc_name {
96 	SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
97 	char			*sn_principal;
98 	gss_OID			sn_mech;
99 	u_int			sn_req_time;
100 	gss_cred_id_t		sn_cred;
101 	u_int			sn_program;
102 	u_int			sn_version;
103 };
104 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
105 	svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
106 
107 enum svc_rpc_gss_client_state {
108 	CLIENT_NEW,				/* still authenticating */
109 	CLIENT_ESTABLISHED,			/* context established */
110 	CLIENT_STALE				/* garbage to collect */
111 };
112 
113 #define SVC_RPC_GSS_SEQWINDOW	128
114 
115 struct svc_rpc_gss_client {
116 	TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
117 	TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
118 	uint32_t		cl_id;
119 	time_t			cl_expiration;	/* when to gc */
120 	enum svc_rpc_gss_client_state cl_state;	/* client state */
121 	bool_t			cl_locked;	/* fixed service+qop */
122 	gss_ctx_id_t		cl_ctx;		/* context id */
123 	gss_cred_id_t		cl_creds;	/* delegated creds */
124 	gss_name_t		cl_cname;	/* client name */
125 	struct svc_rpc_gss_svc_name *cl_sname;	/* server name used */
126 	rpc_gss_rawcred_t	cl_rawcred;	/* raw credentials */
127 	rpc_gss_ucred_t		cl_ucred;	/* unix-style credentials */
128 	bool_t			cl_done_callback; /* TRUE after call */
129 	void			*cl_cookie;	/* user cookie from callback */
130 	gid_t			cl_gid_storage[NGRPS];
131 	gss_OID			cl_mech;	/* mechanism */
132 	gss_qop_t		cl_qop;		/* quality of protection */
133 	u_int			cl_seq;		/* current sequence number */
134 	u_int			cl_win;		/* sequence window size */
135 	u_int			cl_seqlast;	/* sequence window origin */
136 	uint32_t		cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
137 	gss_buffer_desc		cl_verf;	/* buffer for verf checksum */
138 };
139 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
140 
141 #define CLIENT_HASH_SIZE	256
142 #define CLIENT_MAX		128
143 static struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
144 static struct svc_rpc_gss_client_list svc_rpc_gss_clients;
145 static size_t svc_rpc_gss_client_count;
146 static uint32_t svc_rpc_gss_next_clientid = 1;
147 
148 #ifdef __GNUC__
149 static void svc_rpc_gss_init(void) __attribute__ ((constructor));
150 #endif
151 
152 static void
153 svc_rpc_gss_init(void)
154 {
155 	int i;
156 
157 	if (!svc_rpc_gss_initialised) {
158 		for (i = 0; i < CLIENT_HASH_SIZE; i++)
159 			TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
160 		TAILQ_INIT(&svc_rpc_gss_clients);
161 		svc_auth_reg(RPCSEC_GSS, svc_rpc_gss);
162 		svc_rpc_gss_initialised = TRUE;
163 	}
164 }
165 
166 bool_t
167 rpc_gss_set_callback(rpc_gss_callback_t *cb)
168 {
169 	struct svc_rpc_gss_callback *scb;
170 
171 	scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
172 	if (!scb) {
173 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
174 		return (FALSE);
175 	}
176 	scb->cb_callback = *cb;
177 	SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
178 
179 	return (TRUE);
180 }
181 
182 bool_t
183 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
184     u_int req_time, u_int program, u_int version)
185 {
186 	OM_uint32		maj_stat, min_stat;
187 	struct svc_rpc_gss_svc_name *sname;
188 	gss_buffer_desc		namebuf;
189 	gss_name_t		name;
190 	gss_OID			mech_oid;
191 	gss_OID_set_desc	oid_set;
192 	gss_cred_id_t		cred;
193 
194 	svc_rpc_gss_init();
195 
196 	if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
197 		return (FALSE);
198 	oid_set.count = 1;
199 	oid_set.elements = mech_oid;
200 
201 	namebuf.value = (void *)(intptr_t) principal;
202 	namebuf.length = strlen(principal);
203 
204 	maj_stat = gss_import_name(&min_stat, &namebuf,
205 				   GSS_C_NT_HOSTBASED_SERVICE, &name);
206 	if (maj_stat != GSS_S_COMPLETE)
207 		return (FALSE);
208 
209 	maj_stat = gss_acquire_cred(&min_stat, name,
210 	    req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL);
211 	if (maj_stat != GSS_S_COMPLETE)
212 		return (FALSE);
213 
214 	gss_release_name(&min_stat, &name);
215 
216 	sname = malloc(sizeof(struct svc_rpc_gss_svc_name));
217 	if (!sname)
218 		return (FALSE);
219 	sname->sn_principal = strdup(principal);
220 	sname->sn_mech = mech_oid;
221 	sname->sn_req_time = req_time;
222 	sname->sn_cred = cred;
223 	sname->sn_program = program;
224 	sname->sn_version = version;
225 	SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
226 
227 	return (TRUE);
228 }
229 
230 bool_t
231 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
232     const char *mech, const char *name, const char *node, const char *domain)
233 {
234 	OM_uint32		maj_stat, min_stat;
235 	gss_OID			mech_oid;
236 	size_t			namelen;
237 	gss_buffer_desc		buf;
238 	gss_name_t		gss_name, gss_mech_name;
239 	rpc_gss_principal_t	result;
240 
241 	svc_rpc_gss_init();
242 
243 	if (!rpc_gss_mech_to_oid(mech, &mech_oid))
244 		return (FALSE);
245 
246 	/*
247 	 * Construct a gss_buffer containing the full name formatted
248 	 * as "name/node@domain" where node and domain are optional.
249 	 */
250 	namelen = strlen(name);
251 	if (node) {
252 		namelen += strlen(node) + 1;
253 	}
254 	if (domain) {
255 		namelen += strlen(domain) + 1;
256 	}
257 
258 	buf.value = mem_alloc(namelen);
259 	buf.length = namelen;
260 	strcpy((char *) buf.value, name);
261 	if (node) {
262 		strcat((char *) buf.value, "/");
263 		strcat((char *) buf.value, node);
264 	}
265 	if (domain) {
266 		strcat((char *) buf.value, "@");
267 		strcat((char *) buf.value, domain);
268 	}
269 
270 	/*
271 	 * Convert that to a gss_name_t and then convert that to a
272 	 * mechanism name in the selected mechanism.
273 	 */
274 	maj_stat = gss_import_name(&min_stat, &buf,
275 	    GSS_C_NT_USER_NAME, &gss_name);
276 	mem_free(buf.value, buf.length);
277 	if (maj_stat != GSS_S_COMPLETE) {
278 		log_status("gss_import_name", mech_oid, maj_stat, min_stat);
279 		return (FALSE);
280 	}
281 	maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
282 	    &gss_mech_name);
283 	if (maj_stat != GSS_S_COMPLETE) {
284 		log_status("gss_canonicalize_name", mech_oid, maj_stat,
285 		    min_stat);
286 		gss_release_name(&min_stat, &gss_name);
287 		return (FALSE);
288 	}
289 	gss_release_name(&min_stat, &gss_name);
290 
291 	/*
292 	 * Export the mechanism name and use that to construct the
293 	 * rpc_gss_principal_t result.
294 	 */
295 	maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
296 	if (maj_stat != GSS_S_COMPLETE) {
297 		log_status("gss_export_name", mech_oid, maj_stat, min_stat);
298 		gss_release_name(&min_stat, &gss_mech_name);
299 		return (FALSE);
300 	}
301 	gss_release_name(&min_stat, &gss_mech_name);
302 
303 	result = mem_alloc(sizeof(int) + buf.length);
304 	if (!result) {
305 		gss_release_buffer(&min_stat, &buf);
306 		return (FALSE);
307 	}
308 	result->len = buf.length;
309 	memcpy(result->name, buf.value, buf.length);
310 	gss_release_buffer(&min_stat, &buf);
311 
312 	*principal = result;
313 	return (TRUE);
314 }
315 
316 bool_t
317 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
318     rpc_gss_ucred_t **ucred, void **cookie)
319 {
320 	struct svc_rpc_gss_client *client;
321 
322 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
323 		return (FALSE);
324 
325 	client = req->rq_clntcred;
326 	if (rcred)
327 		*rcred = &client->cl_rawcred;
328 	if (ucred)
329 		*ucred = &client->cl_ucred;
330 	if (cookie)
331 		*cookie = client->cl_cookie;
332 	return (TRUE);
333 }
334 
335 int
336 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
337 {
338 	struct svc_rpc_gss_client *client = req->rq_clntcred;
339 	int			want_conf;
340 	OM_uint32		max;
341 	OM_uint32		maj_stat, min_stat;
342 	int			result;
343 
344 	switch (client->cl_rawcred.service) {
345 	case rpc_gss_svc_none:
346 		return (max_tp_unit_len);
347 		break;
348 
349 	case rpc_gss_svc_default:
350 	case rpc_gss_svc_integrity:
351 		want_conf = FALSE;
352 		break;
353 
354 	case rpc_gss_svc_privacy:
355 		want_conf = TRUE;
356 		break;
357 
358 	default:
359 		return (0);
360 	}
361 
362 	maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
363 	    client->cl_qop, max_tp_unit_len, &max);
364 
365 	if (maj_stat == GSS_S_COMPLETE) {
366 		result = (int) max;
367 		if (result < 0)
368 			result = 0;
369 		return (result);
370 	} else {
371 		log_status("gss_wrap_size_limit", client->cl_mech,
372 		    maj_stat, min_stat);
373 		return (0);
374 	}
375 }
376 
377 static struct svc_rpc_gss_client *
378 svc_rpc_gss_find_client(uint32_t clientid)
379 {
380 	struct svc_rpc_gss_client *client;
381 	struct svc_rpc_gss_client_list *list;
382 
383 
384 	log_debug("in svc_rpc_gss_find_client(%d)", clientid);
385 
386 	list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE];
387 	TAILQ_FOREACH(client, list, cl_link) {
388 		if (client->cl_id == clientid) {
389 			/*
390 			 * Move this client to the front of the LRU
391 			 * list.
392 			 */
393 			TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
394 			TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
395 			    cl_alllink);
396 			return client;
397 		}
398 	}
399 
400 	return (NULL);
401 }
402 
403 static struct svc_rpc_gss_client *
404 svc_rpc_gss_create_client(void)
405 {
406 	struct svc_rpc_gss_client *client;
407 	struct svc_rpc_gss_client_list *list;
408 
409 	log_debug("in svc_rpc_gss_create_client()");
410 
411 	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
412 	memset(client, 0, sizeof(struct svc_rpc_gss_client));
413 	client->cl_id = svc_rpc_gss_next_clientid++;
414 	list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
415 	TAILQ_INSERT_HEAD(list, client, cl_link);
416 	TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
417 
418 	/*
419 	 * Start the client off with a short expiration time. We will
420 	 * try to get a saner value from the client creds later.
421 	 */
422 	client->cl_state = CLIENT_NEW;
423 	client->cl_locked = FALSE;
424 	client->cl_expiration = time(0) + 5*60;
425 	svc_rpc_gss_client_count++;
426 
427 	return (client);
428 }
429 
430 static void
431 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
432 {
433 	struct svc_rpc_gss_client_list *list;
434 	OM_uint32 min_stat;
435 
436 	log_debug("in svc_rpc_gss_destroy_client()");
437 
438 	if (client->cl_ctx)
439 		gss_delete_sec_context(&min_stat,
440 		    &client->cl_ctx, GSS_C_NO_BUFFER);
441 
442 	if (client->cl_cname)
443 		gss_release_name(&min_stat, &client->cl_cname);
444 
445 	if (client->cl_rawcred.client_principal)
446 		mem_free(client->cl_rawcred.client_principal,
447 		    sizeof(*client->cl_rawcred.client_principal)
448 		    + client->cl_rawcred.client_principal->len);
449 
450 	if (client->cl_verf.value)
451 		gss_release_buffer(&min_stat, &client->cl_verf);
452 
453 	list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
454 	TAILQ_REMOVE(list, client, cl_link);
455 	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
456 	svc_rpc_gss_client_count--;
457 	mem_free(client, sizeof(*client));
458 }
459 
460 static void
461 svc_rpc_gss_timeout_clients(void)
462 {
463 	struct svc_rpc_gss_client *client;
464 	struct svc_rpc_gss_client *nclient;
465 	time_t now = time(0);
466 
467 	log_debug("in svc_rpc_gss_timeout_clients()");
468 	/*
469 	 * First enforce the max client limit. We keep
470 	 * svc_rpc_gss_clients in LRU order.
471 	 */
472 	while (svc_rpc_gss_client_count > CLIENT_MAX)
473 		svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
474 			    svc_rpc_gss_client_list));
475 	TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
476 		if (client->cl_state == CLIENT_STALE
477 		    || now > client->cl_expiration) {
478 			log_debug("expiring client %p", client);
479 			svc_rpc_gss_destroy_client(client);
480 		}
481 	}
482 }
483 
484 #ifdef DEBUG
485 /*
486  * OID<->string routines.  These are uuuuugly.
487  */
488 static OM_uint32
489 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
490 {
491 	char		numstr[128];
492 	unsigned long	number;
493 	int		numshift;
494 	size_t		string_length;
495 	size_t		i;
496 	unsigned char	*cp;
497 	char		*bp;
498 
499 	/* Decoded according to krb5/gssapi_krb5.c */
500 
501 	/* First determine the size of the string */
502 	string_length = 0;
503 	number = 0;
504 	numshift = 0;
505 	cp = (unsigned char *) oid->elements;
506 	number = (unsigned long) cp[0];
507 	sprintf(numstr, "%ld ", number/40);
508 	string_length += strlen(numstr);
509 	sprintf(numstr, "%ld ", number%40);
510 	string_length += strlen(numstr);
511 	for (i=1; i<oid->length; i++) {
512 		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
513 			number = (number << 7) | (cp[i] & 0x7f);
514 			numshift += 7;
515 		}
516 		else {
517 			*minor_status = 0;
518 			return(GSS_S_FAILURE);
519 		}
520 		if ((cp[i] & 0x80) == 0) {
521 			sprintf(numstr, "%ld ", number);
522 			string_length += strlen(numstr);
523 			number = 0;
524 			numshift = 0;
525 		}
526 	}
527 	/*
528 	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
529 	 * here for "{ " and "}\0".
530 	 */
531 	string_length += 4;
532 	if ((bp = (char *) mem_alloc(string_length))) {
533 		strcpy(bp, "{ ");
534 		number = (unsigned long) cp[0];
535 		sprintf(numstr, "%ld ", number/40);
536 		strcat(bp, numstr);
537 		sprintf(numstr, "%ld ", number%40);
538 		strcat(bp, numstr);
539 		number = 0;
540 		cp = (unsigned char *) oid->elements;
541 		for (i=1; i<oid->length; i++) {
542 			number = (number << 7) | (cp[i] & 0x7f);
543 			if ((cp[i] & 0x80) == 0) {
544 				sprintf(numstr, "%ld ", number);
545 				strcat(bp, numstr);
546 				number = 0;
547 			}
548 		}
549 		strcat(bp, "}");
550 		oid_str->length = strlen(bp)+1;
551 		oid_str->value = (void *) bp;
552 		*minor_status = 0;
553 		return(GSS_S_COMPLETE);
554 	}
555 	*minor_status = 0;
556 	return(GSS_S_FAILURE);
557 }
558 #endif
559 
560 static void
561 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
562     const gss_name_t name)
563 {
564 	OM_uint32		maj_stat, min_stat;
565 	char			buf[128];
566 	uid_t			uid;
567 	struct passwd		pwd, *pw;
568 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
569 
570 	uc->uid = 65534;
571 	uc->gid = 65534;
572 	uc->gidlen = 0;
573 	uc->gidlist = client->cl_gid_storage;
574 
575 	maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
576 	if (maj_stat != GSS_S_COMPLETE)
577 		return;
578 
579 	getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
580 	if (pw) {
581 		int len = NGRPS;
582 		uc->uid = pw->pw_uid;
583 		uc->gid = pw->pw_gid;
584 		uc->gidlist = client->cl_gid_storage;
585 		getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
586 		uc->gidlen = len;
587 	}
588 }
589 
590 static bool_t
591 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
592 			       struct svc_req *rqst,
593 			       struct rpc_gss_init_res *gr,
594 			       struct rpc_gss_cred *gc)
595 {
596 	gss_buffer_desc		recv_tok;
597 	gss_OID			mech;
598 	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
599 	OM_uint32		cred_lifetime;
600 	struct svc_rpc_gss_svc_name *sname;
601 
602 	log_debug("in svc_rpc_gss_accept_context()");
603 
604 	/* Deserialize arguments. */
605 	memset(&recv_tok, 0, sizeof(recv_tok));
606 
607 	if (!svc_getargs(rqst->rq_xprt,
608 		(xdrproc_t) xdr_gss_buffer_desc,
609 		(caddr_t) &recv_tok)) {
610 		client->cl_state = CLIENT_STALE;
611 		return (FALSE);
612 	}
613 
614 	/*
615 	 * First time round, try all the server names we have until
616 	 * one matches. Afterwards, stick with that one.
617 	 */
618 	if (!client->cl_sname) {
619 		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
620 			if (sname->sn_program == rqst->rq_prog
621 			    && sname->sn_version == rqst->rq_vers) {
622 				gr->gr_major = gss_accept_sec_context(
623 					&gr->gr_minor,
624 					&client->cl_ctx,
625 					sname->sn_cred,
626 					&recv_tok,
627 					GSS_C_NO_CHANNEL_BINDINGS,
628 					&client->cl_cname,
629 					&mech,
630 					&gr->gr_token,
631 					&ret_flags,
632 					&cred_lifetime,
633 					&client->cl_creds);
634 				if (gr->gr_major == GSS_S_COMPLETE
635 				    || gr->gr_major == GSS_S_CONTINUE_NEEDED) {
636 					client->cl_sname = sname;
637 					break;
638 				}
639 				client->cl_sname = sname;
640 				break;
641 			}
642 		}
643 		if (!sname) {
644 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
645 			    (char *) &recv_tok);
646 			return (FALSE);
647 		}
648 	} else {
649 		gr->gr_major = gss_accept_sec_context(
650 			&gr->gr_minor,
651 			&client->cl_ctx,
652 			client->cl_sname->sn_cred,
653 			&recv_tok,
654 			GSS_C_NO_CHANNEL_BINDINGS,
655 			&client->cl_cname,
656 			&mech,
657 			&gr->gr_token,
658 			&ret_flags,
659 			&cred_lifetime,
660 			NULL);
661 	}
662 
663 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
664 
665 	/*
666 	 * If we get an error from gss_accept_sec_context, send the
667 	 * reply anyway so that the client gets a chance to see what
668 	 * is wrong.
669 	 */
670 	if (gr->gr_major != GSS_S_COMPLETE &&
671 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
672 		log_status("accept_sec_context", client->cl_mech,
673 		    gr->gr_major, gr->gr_minor);
674 		client->cl_state = CLIENT_STALE;
675 		return (TRUE);
676 	}
677 
678 	gr->gr_handle.value = &client->cl_id;
679 	gr->gr_handle.length = sizeof(client->cl_id);
680 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
681 
682 	/* Save client info. */
683 	client->cl_mech = mech;
684 	client->cl_qop = GSS_C_QOP_DEFAULT;
685 	client->cl_seq = gc->gc_seq;
686 	client->cl_win = gr->gr_win;
687 	client->cl_done_callback = FALSE;
688 
689 	if (gr->gr_major == GSS_S_COMPLETE) {
690 		gss_buffer_desc	export_name;
691 
692 		/*
693 		 * Change client expiration time to be near when the
694 		 * client creds expire (or 24 hours if we can't figure
695 		 * that out).
696 		 */
697 		if (cred_lifetime == GSS_C_INDEFINITE)
698 			cred_lifetime = time(0) + 24*60*60;
699 
700 		client->cl_expiration = time(0) + cred_lifetime;
701 
702 		/*
703 		 * Fill in cred details in the rawcred structure.
704 		 */
705 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
706 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
707 		maj_stat = gss_export_name(&min_stat, client->cl_cname,
708 		    &export_name);
709 		if (maj_stat != GSS_S_COMPLETE) {
710 			log_status("gss_export_name", client->cl_mech,
711 			    maj_stat, min_stat);
712 			return (FALSE);
713 		}
714 		client->cl_rawcred.client_principal =
715 			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
716 			    + export_name.length);
717 		client->cl_rawcred.client_principal->len = export_name.length;
718 		memcpy(client->cl_rawcred.client_principal->name,
719 		    export_name.value, export_name.length);
720 		gss_release_buffer(&min_stat, &export_name);
721 		client->cl_rawcred.svc_principal =
722 			client->cl_sname->sn_principal;
723 		client->cl_rawcred.service = gc->gc_svc;
724 
725 		/*
726 		 * Use gss_pname_to_uid to map to unix creds. For
727 		 * kerberos5, this uses krb5_aname_to_localname.
728 		 */
729 		svc_rpc_gss_build_ucred(client, client->cl_cname);
730 		gss_release_name(&min_stat, &client->cl_cname);
731 
732 #ifdef DEBUG
733 		{
734 			gss_buffer_desc mechname;
735 
736 			gss_oid_to_str(&min_stat, mech, &mechname);
737 
738 			log_debug("accepted context for %s with "
739 			    "<mech %.*s, qop %d, svc %d>",
740 			    client->cl_rawcred.client_principal->name,
741 			    mechname.length, (char *)mechname.value,
742 			    client->cl_qop, client->rawcred.service);
743 
744 			gss_release_buffer(&min_stat, &mechname);
745 		}
746 #endif /* DEBUG */
747 	}
748 	return (TRUE);
749 }
750 
751 static bool_t
752 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
753 	gss_qop_t *qop)
754 {
755 	struct opaque_auth	*oa;
756 	gss_buffer_desc		 rpcbuf, checksum;
757 	OM_uint32		 maj_stat, min_stat;
758 	gss_qop_t		 qop_state;
759 	int32_t			 rpchdr[128 / sizeof(int32_t)];
760 	int32_t			*buf;
761 
762 	log_debug("in svc_rpc_gss_validate()");
763 
764 	memset(rpchdr, 0, sizeof(rpchdr));
765 
766 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
767 	buf = rpchdr;
768 	IXDR_PUT_LONG(buf, msg->rm_xid);
769 	IXDR_PUT_ENUM(buf, msg->rm_direction);
770 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
771 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
772 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
773 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
774 	oa = &msg->rm_call.cb_cred;
775 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
776 	IXDR_PUT_LONG(buf, oa->oa_length);
777 	if (oa->oa_length) {
778 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
779 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
780 	}
781 	rpcbuf.value = rpchdr;
782 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
783 
784 	checksum.value = msg->rm_call.cb_verf.oa_base;
785 	checksum.length = msg->rm_call.cb_verf.oa_length;
786 
787 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
788 				  &qop_state);
789 
790 	if (maj_stat != GSS_S_COMPLETE) {
791 		log_status("gss_verify_mic", client->cl_mech,
792 		    maj_stat, min_stat);
793 		client->cl_state = CLIENT_STALE;
794 		return (FALSE);
795 	}
796 	*qop = qop_state;
797 	return (TRUE);
798 }
799 
800 static bool_t
801 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
802     struct svc_req *rqst, u_int seq)
803 {
804 	gss_buffer_desc		signbuf;
805 	OM_uint32		maj_stat, min_stat;
806 	uint32_t		nseq;
807 
808 	log_debug("in svc_rpc_gss_nextverf()");
809 
810 	nseq = htonl(seq);
811 	signbuf.value = &nseq;
812 	signbuf.length = sizeof(nseq);
813 
814 	if (client->cl_verf.value)
815 		gss_release_buffer(&min_stat, &client->cl_verf);
816 
817 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
818 	    &signbuf, &client->cl_verf);
819 
820 	if (maj_stat != GSS_S_COMPLETE) {
821 		log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
822 		client->cl_state = CLIENT_STALE;
823 		return (FALSE);
824 	}
825 	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
826 	rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
827 	rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
828 
829 	return (TRUE);
830 }
831 
832 static bool_t
833 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
834 {
835 	struct svc_rpc_gss_callback *scb;
836 	rpc_gss_lock_t	lock;
837 	void		*cookie;
838 	bool_t		cb_res;
839 	bool_t		result;
840 
841 	/*
842 	 * See if we have a callback for this guy.
843 	 */
844 	result = TRUE;
845 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
846 		if (scb->cb_callback.program == rqst->rq_prog
847 		    && scb->cb_callback.version == rqst->rq_vers) {
848 			/*
849 			 * This one matches. Call the callback and see
850 			 * if it wants to veto or something.
851 			 */
852 			lock.locked = FALSE;
853 			lock.raw_cred = &client->cl_rawcred;
854 			cb_res = scb->cb_callback.callback(rqst,
855 			    client->cl_creds,
856 			    client->cl_ctx,
857 			    &lock,
858 			    &cookie);
859 
860 			if (!cb_res) {
861 				client->cl_state = CLIENT_STALE;
862 				result = FALSE;
863 				break;
864 			}
865 
866 			/*
867 			 * The callback accepted the connection - it
868 			 * is responsible for freeing client->cl_creds
869 			 * now.
870 			 */
871 			client->cl_creds = GSS_C_NO_CREDENTIAL;
872 			client->cl_locked = lock.locked;
873 			client->cl_cookie = cookie;
874 			return (TRUE);
875 		}
876 	}
877 
878 	/*
879 	 * Either no callback exists for this program/version or one
880 	 * of the callbacks rejected the connection. We just need to
881 	 * clean up the delegated client creds, if any.
882 	 */
883 	if (client->cl_creds) {
884 		OM_uint32 min_ver;
885 		gss_release_cred(&min_ver, &client->cl_creds);
886 	}
887 	return (result);
888 }
889 
890 static bool_t
891 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
892 {
893 	u_int32_t offset;
894 	int word, bit;
895 
896 	if (seq <= client->cl_seqlast) {
897 		/*
898 		 * The request sequence number is less than
899 		 * the largest we have seen so far. If it is
900 		 * outside the window or if we have seen a
901 		 * request with this sequence before, silently
902 		 * discard it.
903 		 */
904 		offset = client->cl_seqlast - seq;
905 		if (offset >= SVC_RPC_GSS_SEQWINDOW)
906 			return (FALSE);
907 		word = offset / 32;
908 		bit = offset % 32;
909 		if (client->cl_seqmask[word] & (1 << bit))
910 			return (FALSE);
911 	}
912 
913 	return (TRUE);
914 }
915 
916 static void
917 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
918 {
919 	int offset, i, word, bit;
920 	uint32_t carry, newcarry;
921 
922 	if (seq > client->cl_seqlast) {
923 		/*
924 		 * This request has a sequence number greater
925 		 * than any we have seen so far. Advance the
926 		 * seq window and set bit zero of the window
927 		 * (which corresponds to the new sequence
928 		 * number)
929 		 */
930 		offset = seq - client->cl_seqlast;
931 		while (offset > 32) {
932 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
933 			     i > 0; i--) {
934 				client->cl_seqmask[i] = client->cl_seqmask[i-1];
935 			}
936 			client->cl_seqmask[0] = 0;
937 			offset -= 32;
938 		}
939 		carry = 0;
940 		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
941 			newcarry = client->cl_seqmask[i] >> (32 - offset);
942 			client->cl_seqmask[i] =
943 				(client->cl_seqmask[i] << offset) | carry;
944 			carry = newcarry;
945 		}
946 		client->cl_seqmask[0] |= 1;
947 		client->cl_seqlast = seq;
948 	} else {
949 		offset = client->cl_seqlast - seq;
950 		word = offset / 32;
951 		bit = offset % 32;
952 		client->cl_seqmask[word] |= (1 << bit);
953 	}
954 
955 }
956 
957 enum auth_stat
958 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
959 
960 {
961 	OM_uint32		 min_stat;
962 	XDR	 		 xdrs;
963 	struct svc_rpc_gss_client *client;
964 	struct rpc_gss_cred	 gc;
965 	struct rpc_gss_init_res	 gr;
966 	gss_qop_t		 qop;
967 	int			 call_stat;
968 	enum auth_stat		 result;
969 
970 	log_debug("in svc_rpc_gss()");
971 
972 	/* Garbage collect old clients. */
973 	svc_rpc_gss_timeout_clients();
974 
975 	/* Initialize reply. */
976 	rqst->rq_xprt->xp_verf = _null_auth;
977 
978 	/* Deserialize client credentials. */
979 	if (rqst->rq_cred.oa_length <= 0)
980 		return (AUTH_BADCRED);
981 
982 	memset(&gc, 0, sizeof(gc));
983 
984 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
985 	    rqst->rq_cred.oa_length, XDR_DECODE);
986 
987 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
988 		XDR_DESTROY(&xdrs);
989 		return (AUTH_BADCRED);
990 	}
991 	XDR_DESTROY(&xdrs);
992 
993 	/* Check version. */
994 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
995 		result = AUTH_BADCRED;
996 		goto out;
997 	}
998 
999 	/* Check the proc and find the client (or create it) */
1000 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
1001 		if (gc.gc_handle.length != 0) {
1002 			result = AUTH_BADCRED;
1003 			goto out;
1004 		}
1005 		client = svc_rpc_gss_create_client();
1006 	} else {
1007 		if (gc.gc_handle.length != sizeof(uint32_t)) {
1008 			result = AUTH_BADCRED;
1009 			goto out;
1010 		}
1011 		uint32_t *p = gc.gc_handle.value;
1012 		client = svc_rpc_gss_find_client(*p);
1013 		if (!client) {
1014 			/*
1015 			 * Can't find the client - we may have
1016 			 * destroyed it - tell the other side to
1017 			 * re-authenticate.
1018 			 */
1019 			result = RPCSEC_GSS_CREDPROBLEM;
1020 			goto out;
1021 		}
1022 	}
1023 	rqst->rq_clntcred = client;
1024 
1025 	/*
1026 	 * The service and sequence number must be ignored for
1027 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1028 	 */
1029 	if (gc.gc_proc != RPCSEC_GSS_INIT
1030 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1031 		/*
1032 		 * Check for sequence number overflow.
1033 		 */
1034 		if (gc.gc_seq >= MAXSEQ) {
1035 			result = RPCSEC_GSS_CTXPROBLEM;
1036 			goto out;
1037 		}
1038 		client->cl_seq = gc.gc_seq;
1039 
1040 		/*
1041 		 * Check for valid service.
1042 		 */
1043 		if (gc.gc_svc != rpc_gss_svc_none &&
1044 		    gc.gc_svc != rpc_gss_svc_integrity &&
1045 		    gc.gc_svc != rpc_gss_svc_privacy) {
1046 			result = AUTH_BADCRED;
1047 			goto out;
1048 		}
1049 	}
1050 
1051 	/* Handle RPCSEC_GSS control procedure. */
1052 	switch (gc.gc_proc) {
1053 
1054 	case RPCSEC_GSS_INIT:
1055 	case RPCSEC_GSS_CONTINUE_INIT:
1056 		if (rqst->rq_proc != NULLPROC) {
1057 			result = AUTH_REJECTEDCRED;
1058 			break;
1059 		}
1060 
1061 		memset(&gr, 0, sizeof(gr));
1062 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1063 			result = AUTH_REJECTEDCRED;
1064 			break;
1065 		}
1066 
1067 		if (gr.gr_major == GSS_S_COMPLETE) {
1068 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1069 				result = AUTH_REJECTEDCRED;
1070 				break;
1071 			}
1072 		} else {
1073 			rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1074 			rqst->rq_xprt->xp_verf.oa_length = 0;
1075 		}
1076 
1077 		call_stat = svc_sendreply(rqst->rq_xprt,
1078 		    (xdrproc_t) xdr_rpc_gss_init_res,
1079 		    (caddr_t) &gr);
1080 
1081 		gss_release_buffer(&min_stat, &gr.gr_token);
1082 
1083 		if (!call_stat) {
1084 			result = AUTH_FAILED;
1085 			break;
1086 		}
1087 
1088 		if (gr.gr_major == GSS_S_COMPLETE)
1089 			client->cl_state = CLIENT_ESTABLISHED;
1090 
1091 		result = RPCSEC_GSS_NODISPATCH;
1092 		break;
1093 
1094 	case RPCSEC_GSS_DATA:
1095 	case RPCSEC_GSS_DESTROY:
1096 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1097 			result = RPCSEC_GSS_NODISPATCH;
1098 			break;
1099 		}
1100 
1101 		if (!svc_rpc_gss_validate(client, msg, &qop)) {
1102 			result = RPCSEC_GSS_CREDPROBLEM;
1103 			break;
1104 		}
1105 
1106 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1107 			result = RPCSEC_GSS_CTXPROBLEM;
1108 			break;
1109 		}
1110 
1111 		svc_rpc_gss_update_seq(client, gc.gc_seq);
1112 
1113 		/*
1114 		 * Change the SVCAUTH ops on the transport to point at
1115 		 * our own code so that we can unwrap the arguments
1116 		 * and wrap the result. The caller will re-set this on
1117 		 * every request to point to a set of null wrap/unwrap
1118 		 * methods.
1119 		 */
1120 		SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1121 		SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1122 
1123 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1124 			/*
1125 			 * We might be ready to do a callback to the server to
1126 			 * see if it wants to accept/reject the connection.
1127 			 */
1128 			if (!client->cl_done_callback) {
1129 				client->cl_done_callback = TRUE;
1130 				client->cl_qop = qop;
1131 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1132 					client->cl_rawcred.mechanism, qop);
1133 				if (!svc_rpc_gss_callback(client, rqst)) {
1134 					result = AUTH_REJECTEDCRED;
1135 					break;
1136 				}
1137 			}
1138 
1139 			/*
1140 			 * If the server has locked this client to a
1141 			 * particular service+qop pair, enforce that
1142 			 * restriction now.
1143 			 */
1144 			if (client->cl_locked) {
1145 				if (client->cl_rawcred.service != gc.gc_svc) {
1146 					result = AUTH_FAILED;
1147 					break;
1148 				} else if (client->cl_qop != qop) {
1149 					result = AUTH_BADVERF;
1150 					break;
1151 				}
1152 			}
1153 
1154 			/*
1155 			 * If the qop changed, look up the new qop
1156 			 * name for rawcred.
1157 			 */
1158 			if (client->cl_qop != qop) {
1159 				client->cl_qop = qop;
1160 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1161 					client->cl_rawcred.mechanism, qop);
1162 			}
1163 
1164 			/*
1165 			 * Make sure we use the right service value
1166 			 * for unwrap/wrap.
1167 			 */
1168 			client->cl_rawcred.service = gc.gc_svc;
1169 
1170 			result = AUTH_OK;
1171 		} else {
1172 			if (rqst->rq_proc != NULLPROC) {
1173 				result = AUTH_REJECTEDCRED;
1174 				break;
1175 			}
1176 
1177 			call_stat = svc_sendreply(rqst->rq_xprt,
1178 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1179 
1180 			if (!call_stat) {
1181 				result = AUTH_FAILED;
1182 				break;
1183 			}
1184 
1185 			svc_rpc_gss_destroy_client(client);
1186 
1187 			result = RPCSEC_GSS_NODISPATCH;
1188 			break;
1189 		}
1190 		break;
1191 
1192 	default:
1193 		result = AUTH_BADCRED;
1194 		break;
1195 	}
1196 out:
1197 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1198 	return (result);
1199 }
1200 
1201 bool_t
1202 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1203 {
1204 	struct svc_rpc_gss_client *client;
1205 
1206 	log_debug("in svc_rpc_gss_wrap()");
1207 
1208 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1209 	if (client->cl_state != CLIENT_ESTABLISHED
1210 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1211 		return xdr_func(xdrs, xdr_ptr);
1212 	}
1213 	return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1214 		client->cl_ctx, client->cl_qop,
1215 		client->cl_rawcred.service, client->cl_seq));
1216 }
1217 
1218 bool_t
1219 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1220 {
1221 	struct svc_rpc_gss_client *client;
1222 
1223 	log_debug("in svc_rpc_gss_unwrap()");
1224 
1225 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1226 	if (client->cl_state != CLIENT_ESTABLISHED
1227 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1228 		return xdr_func(xdrs, xdr_ptr);
1229 	}
1230 	return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1231 		client->cl_ctx, client->cl_qop,
1232 		client->cl_rawcred.service, client->cl_seq));
1233 }
1234