xref: /freebsd/lib/librpcsec_gss/svc_rpcsec_gss.c (revision 86c9d9918f1db7cdd968b60f8902466887bcd9e9)
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 				client->cl_sname = sname;
635 				break;
636 			}
637 		}
638 		if (!sname) {
639 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
640 			    (char *) &recv_tok);
641 			return (FALSE);
642 		}
643 	} else {
644 		gr->gr_major = gss_accept_sec_context(
645 			&gr->gr_minor,
646 			&client->cl_ctx,
647 			client->cl_sname->sn_cred,
648 			&recv_tok,
649 			GSS_C_NO_CHANNEL_BINDINGS,
650 			&client->cl_cname,
651 			&mech,
652 			&gr->gr_token,
653 			&ret_flags,
654 			&cred_lifetime,
655 			NULL);
656 	}
657 
658 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
659 
660 	/*
661 	 * If we get an error from gss_accept_sec_context, send the
662 	 * reply anyway so that the client gets a chance to see what
663 	 * is wrong.
664 	 */
665 	if (gr->gr_major != GSS_S_COMPLETE &&
666 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
667 		log_status("accept_sec_context", client->cl_mech,
668 		    gr->gr_major, gr->gr_minor);
669 		client->cl_state = CLIENT_STALE;
670 		return (TRUE);
671 	}
672 
673 	gr->gr_handle.value = &client->cl_id;
674 	gr->gr_handle.length = sizeof(client->cl_id);
675 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
676 
677 	/* Save client info. */
678 	client->cl_mech = mech;
679 	client->cl_qop = GSS_C_QOP_DEFAULT;
680 	client->cl_seq = gc->gc_seq;
681 	client->cl_win = gr->gr_win;
682 	client->cl_done_callback = FALSE;
683 
684 	if (gr->gr_major == GSS_S_COMPLETE) {
685 		gss_buffer_desc	export_name;
686 
687 		/*
688 		 * Change client expiration time to be near when the
689 		 * client creds expire (or 24 hours if we can't figure
690 		 * that out).
691 		 */
692 		if (cred_lifetime == GSS_C_INDEFINITE)
693 			cred_lifetime = time(0) + 24*60*60;
694 
695 		client->cl_expiration = time(0) + cred_lifetime;
696 
697 		/*
698 		 * Fill in cred details in the rawcred structure.
699 		 */
700 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
701 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
702 		maj_stat = gss_export_name(&min_stat, client->cl_cname,
703 		    &export_name);
704 		if (maj_stat != GSS_S_COMPLETE) {
705 			log_status("gss_export_name", client->cl_mech,
706 			    maj_stat, min_stat);
707 			return (FALSE);
708 		}
709 		client->cl_rawcred.client_principal =
710 			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
711 			    + export_name.length);
712 		client->cl_rawcred.client_principal->len = export_name.length;
713 		memcpy(client->cl_rawcred.client_principal->name,
714 		    export_name.value, export_name.length);
715 		gss_release_buffer(&min_stat, &export_name);
716 		client->cl_rawcred.svc_principal =
717 			client->cl_sname->sn_principal;
718 		client->cl_rawcred.service = gc->gc_svc;
719 
720 		/*
721 		 * Use gss_pname_to_uid to map to unix creds. For
722 		 * kerberos5, this uses krb5_aname_to_localname.
723 		 */
724 		svc_rpc_gss_build_ucred(client, client->cl_cname);
725 		gss_release_name(&min_stat, &client->cl_cname);
726 
727 #ifdef DEBUG
728 		{
729 			gss_buffer_desc mechname;
730 
731 			gss_oid_to_str(&min_stat, mech, &mechname);
732 
733 			log_debug("accepted context for %s with "
734 			    "<mech %.*s, qop %d, svc %d>",
735 			    client->cl_rawcred.client_principal->name,
736 			    mechname.length, (char *)mechname.value,
737 			    client->cl_qop, client->rawcred.service);
738 
739 			gss_release_buffer(&min_stat, &mechname);
740 		}
741 #endif /* DEBUG */
742 	}
743 	return (TRUE);
744 }
745 
746 static bool_t
747 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
748 	gss_qop_t *qop)
749 {
750 	struct opaque_auth	*oa;
751 	gss_buffer_desc		 rpcbuf, checksum;
752 	OM_uint32		 maj_stat, min_stat;
753 	gss_qop_t		 qop_state;
754 	int32_t			 rpchdr[128 / sizeof(int32_t)];
755 	int32_t			*buf;
756 
757 	log_debug("in svc_rpc_gss_validate()");
758 
759 	memset(rpchdr, 0, sizeof(rpchdr));
760 
761 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
762 	buf = rpchdr;
763 	IXDR_PUT_LONG(buf, msg->rm_xid);
764 	IXDR_PUT_ENUM(buf, msg->rm_direction);
765 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
766 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
767 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
768 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
769 	oa = &msg->rm_call.cb_cred;
770 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
771 	IXDR_PUT_LONG(buf, oa->oa_length);
772 	if (oa->oa_length) {
773 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
774 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
775 	}
776 	rpcbuf.value = rpchdr;
777 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
778 
779 	checksum.value = msg->rm_call.cb_verf.oa_base;
780 	checksum.length = msg->rm_call.cb_verf.oa_length;
781 
782 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
783 				  &qop_state);
784 
785 	if (maj_stat != GSS_S_COMPLETE) {
786 		log_status("gss_verify_mic", client->cl_mech,
787 		    maj_stat, min_stat);
788 		client->cl_state = CLIENT_STALE;
789 		return (FALSE);
790 	}
791 	*qop = qop_state;
792 	return (TRUE);
793 }
794 
795 static bool_t
796 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
797     struct svc_req *rqst, u_int seq)
798 {
799 	gss_buffer_desc		signbuf;
800 	OM_uint32		maj_stat, min_stat;
801 	uint32_t		nseq;
802 
803 	log_debug("in svc_rpc_gss_nextverf()");
804 
805 	nseq = htonl(seq);
806 	signbuf.value = &nseq;
807 	signbuf.length = sizeof(nseq);
808 
809 	if (client->cl_verf.value)
810 		gss_release_buffer(&min_stat, &client->cl_verf);
811 
812 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
813 	    &signbuf, &client->cl_verf);
814 
815 	if (maj_stat != GSS_S_COMPLETE) {
816 		log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
817 		client->cl_state = CLIENT_STALE;
818 		return (FALSE);
819 	}
820 	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
821 	rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
822 	rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
823 
824 	return (TRUE);
825 }
826 
827 static bool_t
828 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
829 {
830 	struct svc_rpc_gss_callback *scb;
831 	rpc_gss_lock_t	lock;
832 	void		*cookie;
833 	bool_t		cb_res;
834 	bool_t		result;
835 
836 	/*
837 	 * See if we have a callback for this guy.
838 	 */
839 	result = TRUE;
840 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
841 		if (scb->cb_callback.program == rqst->rq_prog
842 		    && scb->cb_callback.version == rqst->rq_vers) {
843 			/*
844 			 * This one matches. Call the callback and see
845 			 * if it wants to veto or something.
846 			 */
847 			lock.locked = FALSE;
848 			lock.raw_cred = &client->cl_rawcred;
849 			cb_res = scb->cb_callback.callback(rqst,
850 			    client->cl_creds,
851 			    client->cl_ctx,
852 			    &lock,
853 			    &cookie);
854 
855 			if (!cb_res) {
856 				client->cl_state = CLIENT_STALE;
857 				result = FALSE;
858 				break;
859 			}
860 
861 			/*
862 			 * The callback accepted the connection - it
863 			 * is responsible for freeing client->cl_creds
864 			 * now.
865 			 */
866 			client->cl_creds = GSS_C_NO_CREDENTIAL;
867 			client->cl_locked = lock.locked;
868 			client->cl_cookie = cookie;
869 			return (TRUE);
870 		}
871 	}
872 
873 	/*
874 	 * Either no callback exists for this program/version or one
875 	 * of the callbacks rejected the connection. We just need to
876 	 * clean up the delegated client creds, if any.
877 	 */
878 	if (client->cl_creds) {
879 		OM_uint32 min_ver;
880 		gss_release_cred(&min_ver, &client->cl_creds);
881 	}
882 	return (result);
883 }
884 
885 static bool_t
886 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
887 {
888 	u_int32_t offset;
889 	int word, bit;
890 
891 	if (seq <= client->cl_seqlast) {
892 		/*
893 		 * The request sequence number is less than
894 		 * the largest we have seen so far. If it is
895 		 * outside the window or if we have seen a
896 		 * request with this sequence before, silently
897 		 * discard it.
898 		 */
899 		offset = client->cl_seqlast - seq;
900 		if (offset >= SVC_RPC_GSS_SEQWINDOW)
901 			return (FALSE);
902 		word = offset / 32;
903 		bit = offset % 32;
904 		if (client->cl_seqmask[word] & (1 << bit))
905 			return (FALSE);
906 	}
907 
908 	return (TRUE);
909 }
910 
911 static void
912 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
913 {
914 	int offset, i, word, bit;
915 	uint32_t carry, newcarry;
916 
917 	if (seq > client->cl_seqlast) {
918 		/*
919 		 * This request has a sequence number greater
920 		 * than any we have seen so far. Advance the
921 		 * seq window and set bit zero of the window
922 		 * (which corresponds to the new sequence
923 		 * number)
924 		 */
925 		offset = seq - client->cl_seqlast;
926 		while (offset > 32) {
927 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
928 			     i > 0; i--) {
929 				client->cl_seqmask[i] = client->cl_seqmask[i-1];
930 			}
931 			client->cl_seqmask[0] = 0;
932 			offset -= 32;
933 		}
934 		carry = 0;
935 		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
936 			newcarry = client->cl_seqmask[i] >> (32 - offset);
937 			client->cl_seqmask[i] =
938 				(client->cl_seqmask[i] << offset) | carry;
939 			carry = newcarry;
940 		}
941 		client->cl_seqmask[0] |= 1;
942 		client->cl_seqlast = seq;
943 	} else {
944 		offset = client->cl_seqlast - seq;
945 		word = offset / 32;
946 		bit = offset % 32;
947 		client->cl_seqmask[word] |= (1 << bit);
948 	}
949 
950 }
951 
952 enum auth_stat
953 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
954 
955 {
956 	OM_uint32		 min_stat;
957 	XDR	 		 xdrs;
958 	struct svc_rpc_gss_client *client;
959 	struct rpc_gss_cred	 gc;
960 	struct rpc_gss_init_res	 gr;
961 	gss_qop_t		 qop;
962 	int			 call_stat;
963 	enum auth_stat		 result;
964 
965 	log_debug("in svc_rpc_gss()");
966 
967 	/* Garbage collect old clients. */
968 	svc_rpc_gss_timeout_clients();
969 
970 	/* Initialize reply. */
971 	rqst->rq_xprt->xp_verf = _null_auth;
972 
973 	/* Deserialize client credentials. */
974 	if (rqst->rq_cred.oa_length <= 0)
975 		return (AUTH_BADCRED);
976 
977 	memset(&gc, 0, sizeof(gc));
978 
979 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
980 	    rqst->rq_cred.oa_length, XDR_DECODE);
981 
982 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
983 		XDR_DESTROY(&xdrs);
984 		return (AUTH_BADCRED);
985 	}
986 	XDR_DESTROY(&xdrs);
987 
988 	/* Check version. */
989 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
990 		result = AUTH_BADCRED;
991 		goto out;
992 	}
993 
994 	/* Check the proc and find the client (or create it) */
995 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
996 		if (gc.gc_handle.length != 0) {
997 			result = AUTH_BADCRED;
998 			goto out;
999 		}
1000 		client = svc_rpc_gss_create_client();
1001 	} else {
1002 		if (gc.gc_handle.length != sizeof(uint32_t)) {
1003 			result = AUTH_BADCRED;
1004 			goto out;
1005 		}
1006 		uint32_t *p = gc.gc_handle.value;
1007 		client = svc_rpc_gss_find_client(*p);
1008 		if (!client) {
1009 			/*
1010 			 * Can't find the client - we may have
1011 			 * destroyed it - tell the other side to
1012 			 * re-authenticate.
1013 			 */
1014 			result = RPCSEC_GSS_CREDPROBLEM;
1015 			goto out;
1016 		}
1017 	}
1018 	rqst->rq_clntcred = client;
1019 
1020 	/*
1021 	 * The service and sequence number must be ignored for
1022 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1023 	 */
1024 	if (gc.gc_proc != RPCSEC_GSS_INIT
1025 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1026 		/*
1027 		 * Check for sequence number overflow.
1028 		 */
1029 		if (gc.gc_seq >= MAXSEQ) {
1030 			result = RPCSEC_GSS_CTXPROBLEM;
1031 			goto out;
1032 		}
1033 		client->cl_seq = gc.gc_seq;
1034 
1035 		/*
1036 		 * Check for valid service.
1037 		 */
1038 		if (gc.gc_svc != rpc_gss_svc_none &&
1039 		    gc.gc_svc != rpc_gss_svc_integrity &&
1040 		    gc.gc_svc != rpc_gss_svc_privacy) {
1041 			result = AUTH_BADCRED;
1042 			goto out;
1043 		}
1044 	}
1045 
1046 	/* Handle RPCSEC_GSS control procedure. */
1047 	switch (gc.gc_proc) {
1048 
1049 	case RPCSEC_GSS_INIT:
1050 	case RPCSEC_GSS_CONTINUE_INIT:
1051 		if (rqst->rq_proc != NULLPROC) {
1052 			result = AUTH_REJECTEDCRED;
1053 			break;
1054 		}
1055 
1056 		memset(&gr, 0, sizeof(gr));
1057 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1058 			result = AUTH_REJECTEDCRED;
1059 			break;
1060 		}
1061 
1062 		if (gr.gr_major == GSS_S_COMPLETE) {
1063 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1064 				result = AUTH_REJECTEDCRED;
1065 				break;
1066 			}
1067 		} else {
1068 			rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1069 			rqst->rq_xprt->xp_verf.oa_length = 0;
1070 		}
1071 
1072 		call_stat = svc_sendreply(rqst->rq_xprt,
1073 		    (xdrproc_t) xdr_rpc_gss_init_res,
1074 		    (caddr_t) &gr);
1075 
1076 		gss_release_buffer(&min_stat, &gr.gr_token);
1077 
1078 		if (!call_stat) {
1079 			result = AUTH_FAILED;
1080 			break;
1081 		}
1082 
1083 		if (gr.gr_major == GSS_S_COMPLETE)
1084 			client->cl_state = CLIENT_ESTABLISHED;
1085 
1086 		result = RPCSEC_GSS_NODISPATCH;
1087 		break;
1088 
1089 	case RPCSEC_GSS_DATA:
1090 	case RPCSEC_GSS_DESTROY:
1091 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1092 			result = RPCSEC_GSS_NODISPATCH;
1093 			break;
1094 		}
1095 
1096 		if (!svc_rpc_gss_validate(client, msg, &qop)) {
1097 			result = RPCSEC_GSS_CREDPROBLEM;
1098 			break;
1099 		}
1100 
1101 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1102 			result = RPCSEC_GSS_CTXPROBLEM;
1103 			break;
1104 		}
1105 
1106 		svc_rpc_gss_update_seq(client, gc.gc_seq);
1107 
1108 		/*
1109 		 * Change the SVCAUTH ops on the transport to point at
1110 		 * our own code so that we can unwrap the arguments
1111 		 * and wrap the result. The caller will re-set this on
1112 		 * every request to point to a set of null wrap/unwrap
1113 		 * methods.
1114 		 */
1115 		SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1116 		SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1117 
1118 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1119 			/*
1120 			 * We might be ready to do a callback to the server to
1121 			 * see if it wants to accept/reject the connection.
1122 			 */
1123 			if (!client->cl_done_callback) {
1124 				client->cl_done_callback = TRUE;
1125 				client->cl_qop = qop;
1126 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1127 					client->cl_rawcred.mechanism, qop);
1128 				if (!svc_rpc_gss_callback(client, rqst)) {
1129 					result = AUTH_REJECTEDCRED;
1130 					break;
1131 				}
1132 			}
1133 
1134 			/*
1135 			 * If the server has locked this client to a
1136 			 * particular service+qop pair, enforce that
1137 			 * restriction now.
1138 			 */
1139 			if (client->cl_locked) {
1140 				if (client->cl_rawcred.service != gc.gc_svc) {
1141 					result = AUTH_FAILED;
1142 					break;
1143 				} else if (client->cl_qop != qop) {
1144 					result = AUTH_BADVERF;
1145 					break;
1146 				}
1147 			}
1148 
1149 			/*
1150 			 * If the qop changed, look up the new qop
1151 			 * name for rawcred.
1152 			 */
1153 			if (client->cl_qop != qop) {
1154 				client->cl_qop = qop;
1155 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1156 					client->cl_rawcred.mechanism, qop);
1157 			}
1158 
1159 			/*
1160 			 * Make sure we use the right service value
1161 			 * for unwrap/wrap.
1162 			 */
1163 			client->cl_rawcred.service = gc.gc_svc;
1164 
1165 			result = AUTH_OK;
1166 		} else {
1167 			if (rqst->rq_proc != NULLPROC) {
1168 				result = AUTH_REJECTEDCRED;
1169 				break;
1170 			}
1171 
1172 			call_stat = svc_sendreply(rqst->rq_xprt,
1173 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1174 
1175 			if (!call_stat) {
1176 				result = AUTH_FAILED;
1177 				break;
1178 			}
1179 
1180 			svc_rpc_gss_destroy_client(client);
1181 
1182 			result = RPCSEC_GSS_NODISPATCH;
1183 			break;
1184 		}
1185 		break;
1186 
1187 	default:
1188 		result = AUTH_BADCRED;
1189 		break;
1190 	}
1191 out:
1192 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1193 	return (result);
1194 }
1195 
1196 bool_t
1197 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1198 {
1199 	struct svc_rpc_gss_client *client;
1200 
1201 	log_debug("in svc_rpc_gss_wrap()");
1202 
1203 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1204 	if (client->cl_state != CLIENT_ESTABLISHED
1205 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1206 		return xdr_func(xdrs, xdr_ptr);
1207 	}
1208 	return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1209 		client->cl_ctx, client->cl_qop,
1210 		client->cl_rawcred.service, client->cl_seq));
1211 }
1212 
1213 bool_t
1214 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1215 {
1216 	struct svc_rpc_gss_client *client;
1217 
1218 	log_debug("in svc_rpc_gss_unwrap()");
1219 
1220 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1221 	if (client->cl_state != CLIENT_ESTABLISHED
1222 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1223 		return xdr_func(xdrs, xdr_ptr);
1224 	}
1225 	return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1226 		client->cl_ctx, client->cl_qop,
1227 		client->cl_rawcred.service, client->cl_seq));
1228 }
1229