xref: /src/lib/librpcsec_gss/svc_rpcsec_gss.c (revision 143293c14f8de00c6d3de88cd23fc224e7014206) !
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
svc_rpc_gss_init(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
rpc_gss_set_callback(rpc_gss_callback_t * cb)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
rpc_gss_set_svc_name(const char * principal,const char * mechanism,u_int req_time,u_int program,u_int version)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
rpc_gss_get_principal_name(rpc_gss_principal_t * principal,const char * mech,const char * name,const char * node,const char * domain)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
rpc_gss_getcred(struct svc_req * req,rpc_gss_rawcred_t ** rcred,rpc_gss_ucred_t ** ucred,void ** cookie)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
rpc_gss_svc_max_data_length(struct svc_req * req,int max_tp_unit_len)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 *
svc_rpc_gss_find_client(uint32_t clientid)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 *
svc_rpc_gss_create_client(void)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
svc_rpc_gss_destroy_client(struct svc_rpc_gss_client * client)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
svc_rpc_gss_timeout_clients(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
gss_oid_to_str(OM_uint32 * minor_status,gss_OID oid,gss_buffer_t oid_str)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
svc_rpc_gss_build_ucred(struct svc_rpc_gss_client * client,const gss_name_t name)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
svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client * client,struct svc_req * rqst,struct rpc_gss_init_res * gr,struct rpc_gss_cred * gc)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
svc_rpc_gss_validate(struct svc_rpc_gss_client * client,struct rpc_msg * msg,gss_qop_t * qop)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 	oa = &msg->rm_call.cb_cred;
762 
763 	if (oa->oa_length > sizeof(rpchdr) - 8 * BYTES_PER_XDR_UNIT) {
764 		log_debug("auth length %d exceeds maximum", oa->oa_length);
765 		client->cl_state = CLIENT_STALE;
766 		return (FALSE);
767 	}
768 
769 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
770 	buf = rpchdr;
771 	IXDR_PUT_LONG(buf, msg->rm_xid);
772 	IXDR_PUT_ENUM(buf, msg->rm_direction);
773 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
774 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
775 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
776 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
777 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
778 	IXDR_PUT_LONG(buf, oa->oa_length);
779 	if (oa->oa_length) {
780 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
781 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
782 	}
783 	rpcbuf.value = rpchdr;
784 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
785 
786 	checksum.value = msg->rm_call.cb_verf.oa_base;
787 	checksum.length = msg->rm_call.cb_verf.oa_length;
788 
789 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
790 				  &qop_state);
791 
792 	if (maj_stat != GSS_S_COMPLETE) {
793 		log_status("gss_verify_mic", client->cl_mech,
794 		    maj_stat, min_stat);
795 		client->cl_state = CLIENT_STALE;
796 		return (FALSE);
797 	}
798 	*qop = qop_state;
799 	return (TRUE);
800 }
801 
802 static bool_t
svc_rpc_gss_nextverf(struct svc_rpc_gss_client * client,struct svc_req * rqst,u_int seq)803 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
804     struct svc_req *rqst, u_int seq)
805 {
806 	gss_buffer_desc		signbuf;
807 	OM_uint32		maj_stat, min_stat;
808 	uint32_t		nseq;
809 
810 	log_debug("in svc_rpc_gss_nextverf()");
811 
812 	nseq = htonl(seq);
813 	signbuf.value = &nseq;
814 	signbuf.length = sizeof(nseq);
815 
816 	if (client->cl_verf.value)
817 		gss_release_buffer(&min_stat, &client->cl_verf);
818 
819 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
820 	    &signbuf, &client->cl_verf);
821 
822 	if (maj_stat != GSS_S_COMPLETE) {
823 		log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
824 		client->cl_state = CLIENT_STALE;
825 		return (FALSE);
826 	}
827 	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
828 	rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
829 	rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
830 
831 	return (TRUE);
832 }
833 
834 static bool_t
svc_rpc_gss_callback(struct svc_rpc_gss_client * client,struct svc_req * rqst)835 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
836 {
837 	struct svc_rpc_gss_callback *scb;
838 	rpc_gss_lock_t	lock;
839 	void		*cookie;
840 	bool_t		cb_res;
841 	bool_t		result;
842 
843 	/*
844 	 * See if we have a callback for this guy.
845 	 */
846 	result = TRUE;
847 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
848 		if (scb->cb_callback.program == rqst->rq_prog
849 		    && scb->cb_callback.version == rqst->rq_vers) {
850 			/*
851 			 * This one matches. Call the callback and see
852 			 * if it wants to veto or something.
853 			 */
854 			lock.locked = FALSE;
855 			lock.raw_cred = &client->cl_rawcred;
856 			cb_res = scb->cb_callback.callback(rqst,
857 			    client->cl_creds,
858 			    client->cl_ctx,
859 			    &lock,
860 			    &cookie);
861 
862 			if (!cb_res) {
863 				client->cl_state = CLIENT_STALE;
864 				result = FALSE;
865 				break;
866 			}
867 
868 			/*
869 			 * The callback accepted the connection - it
870 			 * is responsible for freeing client->cl_creds
871 			 * now.
872 			 */
873 			client->cl_creds = GSS_C_NO_CREDENTIAL;
874 			client->cl_locked = lock.locked;
875 			client->cl_cookie = cookie;
876 			return (TRUE);
877 		}
878 	}
879 
880 	/*
881 	 * Either no callback exists for this program/version or one
882 	 * of the callbacks rejected the connection. We just need to
883 	 * clean up the delegated client creds, if any.
884 	 */
885 	if (client->cl_creds) {
886 		OM_uint32 min_ver;
887 		gss_release_cred(&min_ver, &client->cl_creds);
888 	}
889 	return (result);
890 }
891 
892 static bool_t
svc_rpc_gss_check_replay(struct svc_rpc_gss_client * client,uint32_t seq)893 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
894 {
895 	u_int32_t offset;
896 	int word, bit;
897 
898 	if (seq <= client->cl_seqlast) {
899 		/*
900 		 * The request sequence number is less than
901 		 * the largest we have seen so far. If it is
902 		 * outside the window or if we have seen a
903 		 * request with this sequence before, silently
904 		 * discard it.
905 		 */
906 		offset = client->cl_seqlast - seq;
907 		if (offset >= SVC_RPC_GSS_SEQWINDOW)
908 			return (FALSE);
909 		word = offset / 32;
910 		bit = offset % 32;
911 		if (client->cl_seqmask[word] & (1 << bit))
912 			return (FALSE);
913 	}
914 
915 	return (TRUE);
916 }
917 
918 static void
svc_rpc_gss_update_seq(struct svc_rpc_gss_client * client,uint32_t seq)919 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
920 {
921 	int offset, i, word, bit;
922 	uint32_t carry, newcarry;
923 	uint32_t* maskp;
924 
925 	maskp = client->cl_seqmask;
926 	if (seq > client->cl_seqlast) {
927 		/*
928 		 * This request has a sequence number greater
929 		 * than any we have seen so far. Advance the
930 		 * seq window and set bit zero of the window
931 		 * (which corresponds to the new sequence
932 		 * number)
933 		 */
934 		offset = seq - client->cl_seqlast;
935 		while (offset >= 32) {
936 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
937 			     i > 0; i--) {
938 				maskp[i] = maskp[i-1];
939 			}
940 			maskp[0] = 0;
941 			offset -= 32;
942 		}
943 		if (offset > 0) {
944 			carry = 0;
945 			for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
946 				newcarry = maskp[i] >> (32 - offset);
947 				maskp[i] = (maskp[i] << offset) | carry;
948 				carry = newcarry;
949 			}
950 		}
951 		maskp[0] |= 1;
952 		client->cl_seqlast = seq;
953 	} else {
954 		offset = client->cl_seqlast - seq;
955 		word = offset / 32;
956 		bit = offset % 32;
957 		maskp[word] |= (1 << bit);
958 	}
959 
960 }
961 
962 enum auth_stat
svc_rpc_gss(struct svc_req * rqst,struct rpc_msg * msg)963 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
964 
965 {
966 	OM_uint32		 min_stat;
967 	XDR	 		 xdrs;
968 	struct svc_rpc_gss_client *client;
969 	struct rpc_gss_cred	 gc;
970 	struct rpc_gss_init_res	 gr;
971 	gss_qop_t		 qop;
972 	int			 call_stat;
973 	enum auth_stat		 result;
974 
975 	log_debug("in svc_rpc_gss()");
976 
977 	/* Garbage collect old clients. */
978 	svc_rpc_gss_timeout_clients();
979 
980 	/* Initialize reply. */
981 	rqst->rq_xprt->xp_verf = _null_auth;
982 
983 	/* Deserialize client credentials. */
984 	if (rqst->rq_cred.oa_length <= 0)
985 		return (AUTH_BADCRED);
986 
987 	memset(&gc, 0, sizeof(gc));
988 
989 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
990 	    rqst->rq_cred.oa_length, XDR_DECODE);
991 
992 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
993 		XDR_DESTROY(&xdrs);
994 		return (AUTH_BADCRED);
995 	}
996 	XDR_DESTROY(&xdrs);
997 
998 	/* Check version. */
999 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
1000 		result = AUTH_BADCRED;
1001 		goto out;
1002 	}
1003 
1004 	/* Check the proc and find the client (or create it) */
1005 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
1006 		if (gc.gc_handle.length != 0) {
1007 			result = AUTH_BADCRED;
1008 			goto out;
1009 		}
1010 		client = svc_rpc_gss_create_client();
1011 	} else {
1012 		if (gc.gc_handle.length != sizeof(uint32_t)) {
1013 			result = AUTH_BADCRED;
1014 			goto out;
1015 		}
1016 		uint32_t *p = gc.gc_handle.value;
1017 		client = svc_rpc_gss_find_client(*p);
1018 		if (!client) {
1019 			/*
1020 			 * Can't find the client - we may have
1021 			 * destroyed it - tell the other side to
1022 			 * re-authenticate.
1023 			 */
1024 			result = RPCSEC_GSS_CREDPROBLEM;
1025 			goto out;
1026 		}
1027 	}
1028 	rqst->rq_clntcred = client;
1029 
1030 	/*
1031 	 * The service and sequence number must be ignored for
1032 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1033 	 */
1034 	if (gc.gc_proc != RPCSEC_GSS_INIT
1035 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1036 		/*
1037 		 * Check for sequence number overflow.
1038 		 */
1039 		if (gc.gc_seq >= MAXSEQ) {
1040 			result = RPCSEC_GSS_CTXPROBLEM;
1041 			goto out;
1042 		}
1043 		client->cl_seq = gc.gc_seq;
1044 
1045 		/*
1046 		 * Check for valid service.
1047 		 */
1048 		if (gc.gc_svc != rpc_gss_svc_none &&
1049 		    gc.gc_svc != rpc_gss_svc_integrity &&
1050 		    gc.gc_svc != rpc_gss_svc_privacy) {
1051 			result = AUTH_BADCRED;
1052 			goto out;
1053 		}
1054 	}
1055 
1056 	/* Handle RPCSEC_GSS control procedure. */
1057 	switch (gc.gc_proc) {
1058 
1059 	case RPCSEC_GSS_INIT:
1060 	case RPCSEC_GSS_CONTINUE_INIT:
1061 		if (rqst->rq_proc != NULLPROC) {
1062 			result = AUTH_REJECTEDCRED;
1063 			break;
1064 		}
1065 
1066 		memset(&gr, 0, sizeof(gr));
1067 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1068 			result = AUTH_REJECTEDCRED;
1069 			break;
1070 		}
1071 
1072 		if (gr.gr_major == GSS_S_COMPLETE) {
1073 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1074 				result = AUTH_REJECTEDCRED;
1075 				break;
1076 			}
1077 		} else {
1078 			rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1079 			rqst->rq_xprt->xp_verf.oa_length = 0;
1080 		}
1081 
1082 		call_stat = svc_sendreply(rqst->rq_xprt,
1083 		    (xdrproc_t) xdr_rpc_gss_init_res,
1084 		    (caddr_t) &gr);
1085 
1086 		gss_release_buffer(&min_stat, &gr.gr_token);
1087 
1088 		if (!call_stat) {
1089 			result = AUTH_FAILED;
1090 			break;
1091 		}
1092 
1093 		if (gr.gr_major == GSS_S_COMPLETE)
1094 			client->cl_state = CLIENT_ESTABLISHED;
1095 
1096 		result = RPCSEC_GSS_NODISPATCH;
1097 		break;
1098 
1099 	case RPCSEC_GSS_DATA:
1100 	case RPCSEC_GSS_DESTROY:
1101 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1102 			result = RPCSEC_GSS_NODISPATCH;
1103 			break;
1104 		}
1105 
1106 		if (!svc_rpc_gss_validate(client, msg, &qop)) {
1107 			result = RPCSEC_GSS_CREDPROBLEM;
1108 			break;
1109 		}
1110 
1111 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1112 			result = RPCSEC_GSS_CTXPROBLEM;
1113 			break;
1114 		}
1115 
1116 		svc_rpc_gss_update_seq(client, gc.gc_seq);
1117 
1118 		/*
1119 		 * Change the SVCAUTH ops on the transport to point at
1120 		 * our own code so that we can unwrap the arguments
1121 		 * and wrap the result. The caller will re-set this on
1122 		 * every request to point to a set of null wrap/unwrap
1123 		 * methods.
1124 		 */
1125 		SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1126 		SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1127 
1128 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1129 			/*
1130 			 * We might be ready to do a callback to the server to
1131 			 * see if it wants to accept/reject the connection.
1132 			 */
1133 			if (!client->cl_done_callback) {
1134 				client->cl_done_callback = TRUE;
1135 				client->cl_qop = qop;
1136 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1137 					client->cl_rawcred.mechanism, qop);
1138 				if (!svc_rpc_gss_callback(client, rqst)) {
1139 					result = AUTH_REJECTEDCRED;
1140 					break;
1141 				}
1142 			}
1143 
1144 			/*
1145 			 * If the server has locked this client to a
1146 			 * particular service+qop pair, enforce that
1147 			 * restriction now.
1148 			 */
1149 			if (client->cl_locked) {
1150 				if (client->cl_rawcred.service != gc.gc_svc) {
1151 					result = AUTH_FAILED;
1152 					break;
1153 				} else if (client->cl_qop != qop) {
1154 					result = AUTH_BADVERF;
1155 					break;
1156 				}
1157 			}
1158 
1159 			/*
1160 			 * If the qop changed, look up the new qop
1161 			 * name for rawcred.
1162 			 */
1163 			if (client->cl_qop != qop) {
1164 				client->cl_qop = qop;
1165 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1166 					client->cl_rawcred.mechanism, qop);
1167 			}
1168 
1169 			/*
1170 			 * Make sure we use the right service value
1171 			 * for unwrap/wrap.
1172 			 */
1173 			client->cl_rawcred.service = gc.gc_svc;
1174 
1175 			result = AUTH_OK;
1176 		} else {
1177 			if (rqst->rq_proc != NULLPROC) {
1178 				result = AUTH_REJECTEDCRED;
1179 				break;
1180 			}
1181 
1182 			call_stat = svc_sendreply(rqst->rq_xprt,
1183 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1184 
1185 			if (!call_stat) {
1186 				result = AUTH_FAILED;
1187 				break;
1188 			}
1189 
1190 			svc_rpc_gss_destroy_client(client);
1191 
1192 			result = RPCSEC_GSS_NODISPATCH;
1193 			break;
1194 		}
1195 		break;
1196 
1197 	default:
1198 		result = AUTH_BADCRED;
1199 		break;
1200 	}
1201 out:
1202 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1203 	return (result);
1204 }
1205 
1206 bool_t
svc_rpc_gss_wrap(SVCAUTH * auth,XDR * xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)1207 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1208 {
1209 	struct svc_rpc_gss_client *client;
1210 
1211 	log_debug("in svc_rpc_gss_wrap()");
1212 
1213 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1214 	if (client->cl_state != CLIENT_ESTABLISHED
1215 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1216 		return xdr_func(xdrs, xdr_ptr);
1217 	}
1218 	return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1219 		client->cl_ctx, client->cl_qop,
1220 		client->cl_rawcred.service, client->cl_seq));
1221 }
1222 
1223 bool_t
svc_rpc_gss_unwrap(SVCAUTH * auth,XDR * xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)1224 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1225 {
1226 	struct svc_rpc_gss_client *client;
1227 
1228 	log_debug("in svc_rpc_gss_unwrap()");
1229 
1230 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1231 	if (client->cl_state != CLIENT_ESTABLISHED
1232 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1233 		return xdr_func(xdrs, xdr_ptr);
1234 	}
1235 	return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1236 		client->cl_ctx, client->cl_qop,
1237 		client->cl_rawcred.service, client->cl_seq));
1238 }
1239