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 /*-
27  SPDX-License-Identifier: BSD-3-Clause
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 	uint32_t* maskp;
917 
918 	maskp = client->cl_seqmask;
919 	if (seq > client->cl_seqlast) {
920 		/*
921 		 * This request has a sequence number greater
922 		 * than any we have seen so far. Advance the
923 		 * seq window and set bit zero of the window
924 		 * (which corresponds to the new sequence
925 		 * number)
926 		 */
927 		offset = seq - client->cl_seqlast;
928 		while (offset >= 32) {
929 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
930 			     i > 0; i--) {
931 				maskp[i] = maskp[i-1];
932 			}
933 			maskp[0] = 0;
934 			offset -= 32;
935 		}
936 		if (offset > 0) {
937 			carry = 0;
938 			for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
939 				newcarry = maskp[i] >> (32 - offset);
940 				maskp[i] = (maskp[i] << offset) | carry;
941 				carry = newcarry;
942 			}
943 		}
944 		maskp[0] |= 1;
945 		client->cl_seqlast = seq;
946 	} else {
947 		offset = client->cl_seqlast - seq;
948 		word = offset / 32;
949 		bit = offset % 32;
950 		maskp[word] |= (1 << bit);
951 	}
952 
953 }
954 
955 enum auth_stat
956 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
957 
958 {
959 	OM_uint32		 min_stat;
960 	XDR	 		 xdrs;
961 	struct svc_rpc_gss_client *client;
962 	struct rpc_gss_cred	 gc;
963 	struct rpc_gss_init_res	 gr;
964 	gss_qop_t		 qop;
965 	int			 call_stat;
966 	enum auth_stat		 result;
967 
968 	log_debug("in svc_rpc_gss()");
969 
970 	/* Garbage collect old clients. */
971 	svc_rpc_gss_timeout_clients();
972 
973 	/* Initialize reply. */
974 	rqst->rq_xprt->xp_verf = _null_auth;
975 
976 	/* Deserialize client credentials. */
977 	if (rqst->rq_cred.oa_length <= 0)
978 		return (AUTH_BADCRED);
979 
980 	memset(&gc, 0, sizeof(gc));
981 
982 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
983 	    rqst->rq_cred.oa_length, XDR_DECODE);
984 
985 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
986 		XDR_DESTROY(&xdrs);
987 		return (AUTH_BADCRED);
988 	}
989 	XDR_DESTROY(&xdrs);
990 
991 	/* Check version. */
992 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
993 		result = AUTH_BADCRED;
994 		goto out;
995 	}
996 
997 	/* Check the proc and find the client (or create it) */
998 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
999 		if (gc.gc_handle.length != 0) {
1000 			result = AUTH_BADCRED;
1001 			goto out;
1002 		}
1003 		client = svc_rpc_gss_create_client();
1004 	} else {
1005 		if (gc.gc_handle.length != sizeof(uint32_t)) {
1006 			result = AUTH_BADCRED;
1007 			goto out;
1008 		}
1009 		uint32_t *p = gc.gc_handle.value;
1010 		client = svc_rpc_gss_find_client(*p);
1011 		if (!client) {
1012 			/*
1013 			 * Can't find the client - we may have
1014 			 * destroyed it - tell the other side to
1015 			 * re-authenticate.
1016 			 */
1017 			result = RPCSEC_GSS_CREDPROBLEM;
1018 			goto out;
1019 		}
1020 	}
1021 	rqst->rq_clntcred = client;
1022 
1023 	/*
1024 	 * The service and sequence number must be ignored for
1025 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1026 	 */
1027 	if (gc.gc_proc != RPCSEC_GSS_INIT
1028 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1029 		/*
1030 		 * Check for sequence number overflow.
1031 		 */
1032 		if (gc.gc_seq >= MAXSEQ) {
1033 			result = RPCSEC_GSS_CTXPROBLEM;
1034 			goto out;
1035 		}
1036 		client->cl_seq = gc.gc_seq;
1037 
1038 		/*
1039 		 * Check for valid service.
1040 		 */
1041 		if (gc.gc_svc != rpc_gss_svc_none &&
1042 		    gc.gc_svc != rpc_gss_svc_integrity &&
1043 		    gc.gc_svc != rpc_gss_svc_privacy) {
1044 			result = AUTH_BADCRED;
1045 			goto out;
1046 		}
1047 	}
1048 
1049 	/* Handle RPCSEC_GSS control procedure. */
1050 	switch (gc.gc_proc) {
1051 
1052 	case RPCSEC_GSS_INIT:
1053 	case RPCSEC_GSS_CONTINUE_INIT:
1054 		if (rqst->rq_proc != NULLPROC) {
1055 			result = AUTH_REJECTEDCRED;
1056 			break;
1057 		}
1058 
1059 		memset(&gr, 0, sizeof(gr));
1060 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1061 			result = AUTH_REJECTEDCRED;
1062 			break;
1063 		}
1064 
1065 		if (gr.gr_major == GSS_S_COMPLETE) {
1066 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1067 				result = AUTH_REJECTEDCRED;
1068 				break;
1069 			}
1070 		} else {
1071 			rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1072 			rqst->rq_xprt->xp_verf.oa_length = 0;
1073 		}
1074 
1075 		call_stat = svc_sendreply(rqst->rq_xprt,
1076 		    (xdrproc_t) xdr_rpc_gss_init_res,
1077 		    (caddr_t) &gr);
1078 
1079 		gss_release_buffer(&min_stat, &gr.gr_token);
1080 
1081 		if (!call_stat) {
1082 			result = AUTH_FAILED;
1083 			break;
1084 		}
1085 
1086 		if (gr.gr_major == GSS_S_COMPLETE)
1087 			client->cl_state = CLIENT_ESTABLISHED;
1088 
1089 		result = RPCSEC_GSS_NODISPATCH;
1090 		break;
1091 
1092 	case RPCSEC_GSS_DATA:
1093 	case RPCSEC_GSS_DESTROY:
1094 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1095 			result = RPCSEC_GSS_NODISPATCH;
1096 			break;
1097 		}
1098 
1099 		if (!svc_rpc_gss_validate(client, msg, &qop)) {
1100 			result = RPCSEC_GSS_CREDPROBLEM;
1101 			break;
1102 		}
1103 
1104 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1105 			result = RPCSEC_GSS_CTXPROBLEM;
1106 			break;
1107 		}
1108 
1109 		svc_rpc_gss_update_seq(client, gc.gc_seq);
1110 
1111 		/*
1112 		 * Change the SVCAUTH ops on the transport to point at
1113 		 * our own code so that we can unwrap the arguments
1114 		 * and wrap the result. The caller will re-set this on
1115 		 * every request to point to a set of null wrap/unwrap
1116 		 * methods.
1117 		 */
1118 		SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1119 		SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1120 
1121 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1122 			/*
1123 			 * We might be ready to do a callback to the server to
1124 			 * see if it wants to accept/reject the connection.
1125 			 */
1126 			if (!client->cl_done_callback) {
1127 				client->cl_done_callback = TRUE;
1128 				client->cl_qop = qop;
1129 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1130 					client->cl_rawcred.mechanism, qop);
1131 				if (!svc_rpc_gss_callback(client, rqst)) {
1132 					result = AUTH_REJECTEDCRED;
1133 					break;
1134 				}
1135 			}
1136 
1137 			/*
1138 			 * If the server has locked this client to a
1139 			 * particular service+qop pair, enforce that
1140 			 * restriction now.
1141 			 */
1142 			if (client->cl_locked) {
1143 				if (client->cl_rawcred.service != gc.gc_svc) {
1144 					result = AUTH_FAILED;
1145 					break;
1146 				} else if (client->cl_qop != qop) {
1147 					result = AUTH_BADVERF;
1148 					break;
1149 				}
1150 			}
1151 
1152 			/*
1153 			 * If the qop changed, look up the new qop
1154 			 * name for rawcred.
1155 			 */
1156 			if (client->cl_qop != qop) {
1157 				client->cl_qop = qop;
1158 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1159 					client->cl_rawcred.mechanism, qop);
1160 			}
1161 
1162 			/*
1163 			 * Make sure we use the right service value
1164 			 * for unwrap/wrap.
1165 			 */
1166 			client->cl_rawcred.service = gc.gc_svc;
1167 
1168 			result = AUTH_OK;
1169 		} else {
1170 			if (rqst->rq_proc != NULLPROC) {
1171 				result = AUTH_REJECTEDCRED;
1172 				break;
1173 			}
1174 
1175 			call_stat = svc_sendreply(rqst->rq_xprt,
1176 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1177 
1178 			if (!call_stat) {
1179 				result = AUTH_FAILED;
1180 				break;
1181 			}
1182 
1183 			svc_rpc_gss_destroy_client(client);
1184 
1185 			result = RPCSEC_GSS_NODISPATCH;
1186 			break;
1187 		}
1188 		break;
1189 
1190 	default:
1191 		result = AUTH_BADCRED;
1192 		break;
1193 	}
1194 out:
1195 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1196 	return (result);
1197 }
1198 
1199 bool_t
1200 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1201 {
1202 	struct svc_rpc_gss_client *client;
1203 
1204 	log_debug("in svc_rpc_gss_wrap()");
1205 
1206 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1207 	if (client->cl_state != CLIENT_ESTABLISHED
1208 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1209 		return xdr_func(xdrs, xdr_ptr);
1210 	}
1211 	return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1212 		client->cl_ctx, client->cl_qop,
1213 		client->cl_rawcred.service, client->cl_seq));
1214 }
1215 
1216 bool_t
1217 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1218 {
1219 	struct svc_rpc_gss_client *client;
1220 
1221 	log_debug("in svc_rpc_gss_unwrap()");
1222 
1223 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1224 	if (client->cl_state != CLIENT_ESTABLISHED
1225 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1226 		return xdr_func(xdrs, xdr_ptr);
1227 	}
1228 	return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1229 		client->cl_ctx, client->cl_qop,
1230 		client->cl_rawcred.service, client->cl_seq));
1231 }
1232