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