1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  * Copyright (c) 1990 The Regents of the University of California.
4  *
5  * Copyright (c) 2008 Doug Rabson
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 /*
30   svc_rpcsec_gss.c
31 
32   Copyright (c) 2000 The Regents of the University of Michigan.
33   All rights reserved.
34 
35   Copyright (c) 2000 Dug Song <[email protected]>.
36   All rights reserved, all wrongs reversed.
37 
38   Redistribution and use in source and binary forms, with or without
39   modification, are permitted provided that the following conditions
40   are met:
41 
42   1. Redistributions of source code must retain the above copyright
43      notice, this list of conditions and the following disclaimer.
44   2. Redistributions in binary form must reproduce the above copyright
45      notice, this list of conditions and the following disclaimer in the
46      documentation and/or other materials provided with the distribution.
47   3. Neither the name of the University nor the names of its
48      contributors may be used to endorse or promote products derived
49      from this software without specific prior written permission.
50 
51   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
52   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
54   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
58   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
59   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
60   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
61   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 
63   $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
64  */
65 
66 #include <sys/cdefs.h>
67 __FBSDID("$FreeBSD$");
68 
69 #include <sys/param.h>
70 #include <sys/systm.h>
71 #include <sys/jail.h>
72 #include <sys/kernel.h>
73 #include <sys/kobj.h>
74 #include <sys/lock.h>
75 #include <sys/malloc.h>
76 #include <sys/mbuf.h>
77 #include <sys/mutex.h>
78 #include <sys/proc.h>
79 #include <sys/sx.h>
80 #include <sys/ucred.h>
81 
82 #include <rpc/rpc.h>
83 #include <rpc/rpcsec_gss.h>
84 
85 #include "rpcsec_gss_int.h"
86 
87 static bool_t   svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
88 static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
89 static void     svc_rpc_gss_release(SVCAUTH *);
90 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
91 static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
92 
93 static struct svc_auth_ops svc_auth_gss_ops = {
94 	svc_rpc_gss_wrap,
95 	svc_rpc_gss_unwrap,
96 	svc_rpc_gss_release,
97 };
98 
99 struct sx svc_rpc_gss_lock;
100 
101 struct svc_rpc_gss_callback {
102 	SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
103 	rpc_gss_callback_t	cb_callback;
104 };
105 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
106 	svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
107 
108 struct svc_rpc_gss_svc_name {
109 	SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
110 	char			*sn_principal;
111 	gss_OID			sn_mech;
112 	u_int			sn_req_time;
113 	gss_cred_id_t		sn_cred;
114 	u_int			sn_program;
115 	u_int			sn_version;
116 };
117 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
118 	svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
119 
120 enum svc_rpc_gss_client_state {
121 	CLIENT_NEW,				/* still authenticating */
122 	CLIENT_ESTABLISHED,			/* context established */
123 	CLIENT_STALE				/* garbage to collect */
124 };
125 
126 #define SVC_RPC_GSS_SEQWINDOW	128
127 
128 struct svc_rpc_gss_clientid {
129 	unsigned long		ci_hostid;
130 	uint32_t		ci_boottime;
131 	uint32_t		ci_id;
132 };
133 
134 struct svc_rpc_gss_client {
135 	TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
136 	TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
137 	volatile u_int		cl_refs;
138 	struct sx		cl_lock;
139 	struct svc_rpc_gss_clientid cl_id;
140 	time_t			cl_expiration;	/* when to gc */
141 	enum svc_rpc_gss_client_state cl_state;	/* client state */
142 	bool_t			cl_locked;	/* fixed service+qop */
143 	gss_ctx_id_t		cl_ctx;		/* context id */
144 	gss_cred_id_t		cl_creds;	/* delegated creds */
145 	gss_name_t		cl_cname;	/* client name */
146 	struct svc_rpc_gss_svc_name *cl_sname;	/* server name used */
147 	rpc_gss_rawcred_t	cl_rawcred;	/* raw credentials */
148 	rpc_gss_ucred_t		cl_ucred;	/* unix-style credentials */
149 	struct ucred		*cl_cred;	/* kernel-style credentials */
150 	int			cl_rpcflavor;	/* RPC pseudo sec flavor */
151 	bool_t			cl_done_callback; /* TRUE after call */
152 	void			*cl_cookie;	/* user cookie from callback */
153 	gid_t			cl_gid_storage[NGROUPS];
154 	gss_OID			cl_mech;	/* mechanism */
155 	gss_qop_t		cl_qop;		/* quality of protection */
156 	uint32_t		cl_seqlast;	/* sequence window origin */
157 	uint32_t		cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
158 };
159 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
160 
161 /*
162  * This structure holds enough information to unwrap arguments or wrap
163  * results for a given request. We use the rq_clntcred area for this
164  * (which is a per-request buffer).
165  */
166 struct svc_rpc_gss_cookedcred {
167 	struct svc_rpc_gss_client *cc_client;
168 	rpc_gss_service_t	cc_service;
169 	uint32_t		cc_seq;
170 };
171 
172 #define CLIENT_HASH_SIZE	256
173 #define CLIENT_MAX		128
174 u_int svc_rpc_gss_client_max = CLIENT_MAX;
175 
176 SYSCTL_NODE(_kern, OID_AUTO, rpc, CTLFLAG_RW, 0, "RPC");
177 SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW, 0, "GSS");
178 
179 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW,
180     &svc_rpc_gss_client_max, 0,
181     "Max number of rpc-gss clients");
182 
183 static u_int svc_rpc_gss_client_count;
184 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
185     &svc_rpc_gss_client_count, 0,
186     "Number of rpc-gss clients");
187 
188 struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
189 struct svc_rpc_gss_client_list svc_rpc_gss_clients;
190 static uint32_t svc_rpc_gss_next_clientid = 1;
191 
192 static void
svc_rpc_gss_init(void * arg)193 svc_rpc_gss_init(void *arg)
194 {
195 	int i;
196 
197 	for (i = 0; i < CLIENT_HASH_SIZE; i++)
198 		TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
199 	TAILQ_INIT(&svc_rpc_gss_clients);
200 	svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
201 	sx_init(&svc_rpc_gss_lock, "gsslock");
202 }
203 SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
204 
205 bool_t
rpc_gss_set_callback(rpc_gss_callback_t * cb)206 rpc_gss_set_callback(rpc_gss_callback_t *cb)
207 {
208 	struct svc_rpc_gss_callback *scb;
209 
210 	scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
211 	if (!scb) {
212 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
213 		return (FALSE);
214 	}
215 	scb->cb_callback = *cb;
216 	sx_xlock(&svc_rpc_gss_lock);
217 	SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
218 	sx_xunlock(&svc_rpc_gss_lock);
219 
220 	return (TRUE);
221 }
222 
223 void
rpc_gss_clear_callback(rpc_gss_callback_t * cb)224 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
225 {
226 	struct svc_rpc_gss_callback *scb;
227 
228 	sx_xlock(&svc_rpc_gss_lock);
229 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
230 		if (scb->cb_callback.program == cb->program
231 		    && scb->cb_callback.version == cb->version
232 		    && scb->cb_callback.callback == cb->callback) {
233 			SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
234 			    svc_rpc_gss_callback, cb_link);
235 			sx_xunlock(&svc_rpc_gss_lock);
236 			mem_free(scb, sizeof(*scb));
237 			return;
238 		}
239 	}
240 	sx_xunlock(&svc_rpc_gss_lock);
241 }
242 
243 static bool_t
rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name * sname)244 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
245 {
246 	OM_uint32		maj_stat, min_stat;
247 	gss_buffer_desc		namebuf;
248 	gss_name_t		name;
249 	gss_OID_set_desc	oid_set;
250 
251 	oid_set.count = 1;
252 	oid_set.elements = sname->sn_mech;
253 
254 	namebuf.value = (void *) sname->sn_principal;
255 	namebuf.length = strlen(sname->sn_principal);
256 
257 	maj_stat = gss_import_name(&min_stat, &namebuf,
258 				   GSS_C_NT_HOSTBASED_SERVICE, &name);
259 	if (maj_stat != GSS_S_COMPLETE)
260 		return (FALSE);
261 
262 	if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
263 		gss_release_cred(&min_stat, &sname->sn_cred);
264 
265 	maj_stat = gss_acquire_cred(&min_stat, name,
266 	    sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
267 	    NULL, NULL);
268 	if (maj_stat != GSS_S_COMPLETE) {
269 		gss_release_name(&min_stat, &name);
270 		return (FALSE);
271 	}
272 	gss_release_name(&min_stat, &name);
273 
274 	return (TRUE);
275 }
276 
277 bool_t
rpc_gss_set_svc_name(const char * principal,const char * mechanism,u_int req_time,u_int program,u_int version)278 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
279     u_int req_time, u_int program, u_int version)
280 {
281 	struct svc_rpc_gss_svc_name *sname;
282 	gss_OID			mech_oid;
283 
284 	if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
285 		return (FALSE);
286 
287 	sname = mem_alloc(sizeof(*sname));
288 	if (!sname)
289 		return (FALSE);
290 	sname->sn_principal = strdup(principal, M_RPC);
291 	sname->sn_mech = mech_oid;
292 	sname->sn_req_time = req_time;
293 	sname->sn_cred = GSS_C_NO_CREDENTIAL;
294 	sname->sn_program = program;
295 	sname->sn_version = version;
296 
297 	if (!rpc_gss_acquire_svc_cred(sname)) {
298 		free(sname->sn_principal, M_RPC);
299 		mem_free(sname, sizeof(*sname));
300 		return (FALSE);
301 	}
302 
303 	sx_xlock(&svc_rpc_gss_lock);
304 	SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
305 	sx_xunlock(&svc_rpc_gss_lock);
306 
307 	return (TRUE);
308 }
309 
310 void
rpc_gss_clear_svc_name(u_int program,u_int version)311 rpc_gss_clear_svc_name(u_int program, u_int version)
312 {
313 	OM_uint32		min_stat;
314 	struct svc_rpc_gss_svc_name *sname;
315 
316 	sx_xlock(&svc_rpc_gss_lock);
317 	SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
318 		if (sname->sn_program == program
319 		    && sname->sn_version == version) {
320 			SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
321 			    svc_rpc_gss_svc_name, sn_link);
322 			sx_xunlock(&svc_rpc_gss_lock);
323 			gss_release_cred(&min_stat, &sname->sn_cred);
324 			free(sname->sn_principal, M_RPC);
325 			mem_free(sname, sizeof(*sname));
326 			return;
327 		}
328 	}
329 	sx_xunlock(&svc_rpc_gss_lock);
330 }
331 
332 bool_t
rpc_gss_get_principal_name(rpc_gss_principal_t * principal,const char * mech,const char * name,const char * node,const char * domain)333 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
334     const char *mech, const char *name, const char *node, const char *domain)
335 {
336 	OM_uint32		maj_stat, min_stat;
337 	gss_OID			mech_oid;
338 	size_t			namelen;
339 	gss_buffer_desc		buf;
340 	gss_name_t		gss_name, gss_mech_name;
341 	rpc_gss_principal_t	result;
342 
343 	if (!rpc_gss_mech_to_oid(mech, &mech_oid))
344 		return (FALSE);
345 
346 	/*
347 	 * Construct a gss_buffer containing the full name formatted
348 	 * as "name/node@domain" where node and domain are optional.
349 	 */
350 	namelen = strlen(name) + 1;
351 	if (node) {
352 		namelen += strlen(node) + 1;
353 	}
354 	if (domain) {
355 		namelen += strlen(domain) + 1;
356 	}
357 
358 	buf.value = mem_alloc(namelen);
359 	buf.length = namelen;
360 	strcpy((char *) buf.value, name);
361 	if (node) {
362 		strcat((char *) buf.value, "/");
363 		strcat((char *) buf.value, node);
364 	}
365 	if (domain) {
366 		strcat((char *) buf.value, "@");
367 		strcat((char *) buf.value, domain);
368 	}
369 
370 	/*
371 	 * Convert that to a gss_name_t and then convert that to a
372 	 * mechanism name in the selected mechanism.
373 	 */
374 	maj_stat = gss_import_name(&min_stat, &buf,
375 	    GSS_C_NT_USER_NAME, &gss_name);
376 	mem_free(buf.value, buf.length);
377 	if (maj_stat != GSS_S_COMPLETE) {
378 		rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
379 		return (FALSE);
380 	}
381 	maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
382 	    &gss_mech_name);
383 	if (maj_stat != GSS_S_COMPLETE) {
384 		rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
385 		    min_stat);
386 		gss_release_name(&min_stat, &gss_name);
387 		return (FALSE);
388 	}
389 	gss_release_name(&min_stat, &gss_name);
390 
391 	/*
392 	 * Export the mechanism name and use that to construct the
393 	 * rpc_gss_principal_t result.
394 	 */
395 	maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
396 	if (maj_stat != GSS_S_COMPLETE) {
397 		rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
398 		gss_release_name(&min_stat, &gss_mech_name);
399 		return (FALSE);
400 	}
401 	gss_release_name(&min_stat, &gss_mech_name);
402 
403 	result = mem_alloc(sizeof(int) + buf.length);
404 	if (!result) {
405 		gss_release_buffer(&min_stat, &buf);
406 		return (FALSE);
407 	}
408 	result->len = buf.length;
409 	memcpy(result->name, buf.value, buf.length);
410 	gss_release_buffer(&min_stat, &buf);
411 
412 	*principal = result;
413 	return (TRUE);
414 }
415 
416 bool_t
rpc_gss_getcred(struct svc_req * req,rpc_gss_rawcred_t ** rcred,rpc_gss_ucred_t ** ucred,void ** cookie)417 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
418     rpc_gss_ucred_t **ucred, void **cookie)
419 {
420 	struct svc_rpc_gss_cookedcred *cc;
421 	struct svc_rpc_gss_client *client;
422 
423 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
424 		return (FALSE);
425 
426 	cc = req->rq_clntcred;
427 	client = cc->cc_client;
428 	if (rcred)
429 		*rcred = &client->cl_rawcred;
430 	if (ucred)
431 		*ucred = &client->cl_ucred;
432 	if (cookie)
433 		*cookie = client->cl_cookie;
434 	return (TRUE);
435 }
436 
437 /*
438  * This simpler interface is used by svc_getcred to copy the cred data
439  * into a kernel cred structure.
440  */
441 static int
rpc_gss_svc_getcred(struct svc_req * req,struct ucred ** crp,int * flavorp)442 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
443 {
444 	struct ucred *cr;
445 	struct svc_rpc_gss_cookedcred *cc;
446 	struct svc_rpc_gss_client *client;
447 	rpc_gss_ucred_t *uc;
448 
449 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
450 		return (FALSE);
451 
452 	cc = req->rq_clntcred;
453 	client = cc->cc_client;
454 
455 	if (flavorp)
456 		*flavorp = client->cl_rpcflavor;
457 
458 	if (client->cl_cred) {
459 		*crp = crhold(client->cl_cred);
460 		return (TRUE);
461 	}
462 
463 	uc = &client->cl_ucred;
464 	cr = client->cl_cred = crget();
465 	cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
466 	cr->cr_rgid = cr->cr_svgid = uc->gid;
467 	crsetgroups(cr, uc->gidlen, uc->gidlist);
468 	cr->cr_prison = &prison0;
469 	prison_hold(cr->cr_prison);
470 	*crp = crhold(cr);
471 
472 	return (TRUE);
473 }
474 
475 int
rpc_gss_svc_max_data_length(struct svc_req * req,int max_tp_unit_len)476 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
477 {
478 	struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
479 	struct svc_rpc_gss_client *client = cc->cc_client;
480 	int			want_conf;
481 	OM_uint32		max;
482 	OM_uint32		maj_stat, min_stat;
483 	int			result;
484 
485 	switch (client->cl_rawcred.service) {
486 	case rpc_gss_svc_none:
487 		return (max_tp_unit_len);
488 		break;
489 
490 	case rpc_gss_svc_default:
491 	case rpc_gss_svc_integrity:
492 		want_conf = FALSE;
493 		break;
494 
495 	case rpc_gss_svc_privacy:
496 		want_conf = TRUE;
497 		break;
498 
499 	default:
500 		return (0);
501 	}
502 
503 	maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
504 	    client->cl_qop, max_tp_unit_len, &max);
505 
506 	if (maj_stat == GSS_S_COMPLETE) {
507 		result = (int) max;
508 		if (result < 0)
509 			result = 0;
510 		return (result);
511 	} else {
512 		rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
513 		    maj_stat, min_stat);
514 		return (0);
515 	}
516 }
517 
518 static struct svc_rpc_gss_client *
svc_rpc_gss_find_client(struct svc_rpc_gss_clientid * id)519 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
520 {
521 	struct svc_rpc_gss_client *client;
522 	struct svc_rpc_gss_client_list *list;
523 	struct timeval boottime;
524 	unsigned long hostid;
525 
526 	rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
527 
528 	getcredhostid(curthread->td_ucred, &hostid);
529 	getboottime(&boottime);
530 	if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
531 		return (NULL);
532 
533 	list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
534 	sx_xlock(&svc_rpc_gss_lock);
535 	TAILQ_FOREACH(client, list, cl_link) {
536 		if (client->cl_id.ci_id == id->ci_id) {
537 			/*
538 			 * Move this client to the front of the LRU
539 			 * list.
540 			 */
541 			TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
542 			TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
543 			    cl_alllink);
544 			refcount_acquire(&client->cl_refs);
545 			break;
546 		}
547 	}
548 	sx_xunlock(&svc_rpc_gss_lock);
549 
550 	return (client);
551 }
552 
553 static struct svc_rpc_gss_client *
svc_rpc_gss_create_client(void)554 svc_rpc_gss_create_client(void)
555 {
556 	struct svc_rpc_gss_client *client;
557 	struct svc_rpc_gss_client_list *list;
558 	struct timeval boottime;
559 	unsigned long hostid;
560 
561 	rpc_gss_log_debug("in svc_rpc_gss_create_client()");
562 
563 	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
564 	memset(client, 0, sizeof(struct svc_rpc_gss_client));
565 
566 	/*
567 	 * Set the initial value of cl_refs to two.  One for the caller
568 	 * and the other to hold onto the client structure until it expires.
569 	 */
570 	refcount_init(&client->cl_refs, 2);
571 	sx_init(&client->cl_lock, "GSS-client");
572 	getcredhostid(curthread->td_ucred, &hostid);
573 	client->cl_id.ci_hostid = hostid;
574 	getboottime(&boottime);
575 	client->cl_id.ci_boottime = boottime.tv_sec;
576 	client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
577 
578 	/*
579 	 * Start the client off with a short expiration time. We will
580 	 * try to get a saner value from the client creds later.
581 	 */
582 	client->cl_state = CLIENT_NEW;
583 	client->cl_locked = FALSE;
584 	client->cl_expiration = time_uptime + 5*60;
585 
586 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
587 	sx_xlock(&svc_rpc_gss_lock);
588 	TAILQ_INSERT_HEAD(list, client, cl_link);
589 	TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
590 	svc_rpc_gss_client_count++;
591 	sx_xunlock(&svc_rpc_gss_lock);
592 	return (client);
593 }
594 
595 static void
svc_rpc_gss_destroy_client(struct svc_rpc_gss_client * client)596 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
597 {
598 	OM_uint32 min_stat;
599 
600 	rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
601 
602 	if (client->cl_ctx)
603 		gss_delete_sec_context(&min_stat,
604 		    &client->cl_ctx, GSS_C_NO_BUFFER);
605 
606 	if (client->cl_cname)
607 		gss_release_name(&min_stat, &client->cl_cname);
608 
609 	if (client->cl_rawcred.client_principal)
610 		mem_free(client->cl_rawcred.client_principal,
611 		    sizeof(*client->cl_rawcred.client_principal)
612 		    + client->cl_rawcred.client_principal->len);
613 
614 	if (client->cl_cred)
615 		crfree(client->cl_cred);
616 
617 	sx_destroy(&client->cl_lock);
618 	mem_free(client, sizeof(*client));
619 }
620 
621 /*
622  * Drop a reference to a client and free it if that was the last reference.
623  */
624 static void
svc_rpc_gss_release_client(struct svc_rpc_gss_client * client)625 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
626 {
627 
628 	if (!refcount_release(&client->cl_refs))
629 		return;
630 	svc_rpc_gss_destroy_client(client);
631 }
632 
633 /*
634  * Remove a client from our global lists.
635  * Must be called with svc_rpc_gss_lock held.
636  */
637 static void
svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client * client)638 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
639 {
640 	struct svc_rpc_gss_client_list *list;
641 
642 	sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
643 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
644 	TAILQ_REMOVE(list, client, cl_link);
645 	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
646 	svc_rpc_gss_client_count--;
647 }
648 
649 /*
650  * Remove a client from our global lists and free it if we can.
651  */
652 static void
svc_rpc_gss_forget_client(struct svc_rpc_gss_client * client)653 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
654 {
655 	struct svc_rpc_gss_client_list *list;
656 	struct svc_rpc_gss_client *tclient;
657 
658 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
659 	sx_xlock(&svc_rpc_gss_lock);
660 	TAILQ_FOREACH(tclient, list, cl_link) {
661 		/*
662 		 * Make sure this client has not already been removed
663 		 * from the lists by svc_rpc_gss_forget_client() or
664 		 * svc_rpc_gss_forget_client_locked().
665 		 */
666 		if (client == tclient) {
667 			svc_rpc_gss_forget_client_locked(client);
668 			sx_xunlock(&svc_rpc_gss_lock);
669 			svc_rpc_gss_release_client(client);
670 			return;
671 		}
672 	}
673 	sx_xunlock(&svc_rpc_gss_lock);
674 }
675 
676 static void
svc_rpc_gss_timeout_clients(void)677 svc_rpc_gss_timeout_clients(void)
678 {
679 	struct svc_rpc_gss_client *client;
680 	time_t now = time_uptime;
681 
682 	rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
683 
684 	/*
685 	 * First enforce the max client limit. We keep
686 	 * svc_rpc_gss_clients in LRU order.
687 	 */
688 	sx_xlock(&svc_rpc_gss_lock);
689 	client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
690 	while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
691 		svc_rpc_gss_forget_client_locked(client);
692 		sx_xunlock(&svc_rpc_gss_lock);
693 		svc_rpc_gss_release_client(client);
694 		sx_xlock(&svc_rpc_gss_lock);
695 		client = TAILQ_LAST(&svc_rpc_gss_clients,
696 		    svc_rpc_gss_client_list);
697 	}
698 again:
699 	TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
700 		if (client->cl_state == CLIENT_STALE
701 		    || now > client->cl_expiration) {
702 			svc_rpc_gss_forget_client_locked(client);
703 			sx_xunlock(&svc_rpc_gss_lock);
704 			rpc_gss_log_debug("expiring client %p", client);
705 			svc_rpc_gss_release_client(client);
706 			sx_xlock(&svc_rpc_gss_lock);
707 			goto again;
708 		}
709 	}
710 	sx_xunlock(&svc_rpc_gss_lock);
711 }
712 
713 #ifdef DEBUG
714 /*
715  * OID<->string routines.  These are uuuuugly.
716  */
717 static OM_uint32
gss_oid_to_str(OM_uint32 * minor_status,gss_OID oid,gss_buffer_t oid_str)718 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
719 {
720 	char		numstr[128];
721 	unsigned long	number;
722 	int		numshift;
723 	size_t		string_length;
724 	size_t		i;
725 	unsigned char	*cp;
726 	char		*bp;
727 
728 	/* Decoded according to krb5/gssapi_krb5.c */
729 
730 	/* First determine the size of the string */
731 	string_length = 0;
732 	number = 0;
733 	numshift = 0;
734 	cp = (unsigned char *) oid->elements;
735 	number = (unsigned long) cp[0];
736 	sprintf(numstr, "%ld ", number/40);
737 	string_length += strlen(numstr);
738 	sprintf(numstr, "%ld ", number%40);
739 	string_length += strlen(numstr);
740 	for (i=1; i<oid->length; i++) {
741 		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
742 			number = (number << 7) | (cp[i] & 0x7f);
743 			numshift += 7;
744 		}
745 		else {
746 			*minor_status = 0;
747 			return(GSS_S_FAILURE);
748 		}
749 		if ((cp[i] & 0x80) == 0) {
750 			sprintf(numstr, "%ld ", number);
751 			string_length += strlen(numstr);
752 			number = 0;
753 			numshift = 0;
754 		}
755 	}
756 	/*
757 	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
758 	 * here for "{ " and "}\0".
759 	 */
760 	string_length += 4;
761 	if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
762 		strcpy(bp, "{ ");
763 		number = (unsigned long) cp[0];
764 		sprintf(numstr, "%ld ", number/40);
765 		strcat(bp, numstr);
766 		sprintf(numstr, "%ld ", number%40);
767 		strcat(bp, numstr);
768 		number = 0;
769 		cp = (unsigned char *) oid->elements;
770 		for (i=1; i<oid->length; i++) {
771 			number = (number << 7) | (cp[i] & 0x7f);
772 			if ((cp[i] & 0x80) == 0) {
773 				sprintf(numstr, "%ld ", number);
774 				strcat(bp, numstr);
775 				number = 0;
776 			}
777 		}
778 		strcat(bp, "}");
779 		oid_str->length = strlen(bp)+1;
780 		oid_str->value = (void *) bp;
781 		*minor_status = 0;
782 		return(GSS_S_COMPLETE);
783 	}
784 	*minor_status = 0;
785 	return(GSS_S_FAILURE);
786 }
787 #endif
788 
789 static void
svc_rpc_gss_build_ucred(struct svc_rpc_gss_client * client,const gss_name_t name)790 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
791     const gss_name_t name)
792 {
793 	OM_uint32		maj_stat, min_stat;
794 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
795 	int			numgroups;
796 
797 	uc->uid = 65534;
798 	uc->gid = 65534;
799 	uc->gidlist = client->cl_gid_storage;
800 
801 	numgroups = NGROUPS;
802 	maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
803 	    &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
804 	if (GSS_ERROR(maj_stat))
805 		uc->gidlen = 0;
806 	else
807 		uc->gidlen = numgroups;
808 }
809 
810 static void
svc_rpc_gss_set_flavor(struct svc_rpc_gss_client * client)811 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
812 {
813 	static gss_OID_desc krb5_mech_oid =
814 		{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
815 
816 	/*
817 	 * Attempt to translate mech type and service into a
818 	 * 'pseudo flavor'. Hardwire in krb5 support for now.
819 	 */
820 	if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
821 		switch (client->cl_rawcred.service) {
822 		case rpc_gss_svc_default:
823 		case rpc_gss_svc_none:
824 			client->cl_rpcflavor = RPCSEC_GSS_KRB5;
825 			break;
826 		case rpc_gss_svc_integrity:
827 			client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
828 			break;
829 		case rpc_gss_svc_privacy:
830 			client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
831 			break;
832 		}
833 	} else {
834 		client->cl_rpcflavor = RPCSEC_GSS;
835 	}
836 }
837 
838 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)839 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
840 			       struct svc_req *rqst,
841 			       struct rpc_gss_init_res *gr,
842 			       struct rpc_gss_cred *gc)
843 {
844 	gss_buffer_desc		recv_tok;
845 	gss_OID			mech;
846 	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
847 	OM_uint32		cred_lifetime;
848 	struct svc_rpc_gss_svc_name *sname;
849 
850 	rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
851 
852 	/* Deserialize arguments. */
853 	memset(&recv_tok, 0, sizeof(recv_tok));
854 
855 	if (!svc_getargs(rqst,
856 		(xdrproc_t) xdr_gss_buffer_desc,
857 		(caddr_t) &recv_tok)) {
858 		client->cl_state = CLIENT_STALE;
859 		return (FALSE);
860 	}
861 
862 	/*
863 	 * First time round, try all the server names we have until
864 	 * one matches. Afterwards, stick with that one.
865 	 */
866 	sx_xlock(&svc_rpc_gss_lock);
867 	if (!client->cl_sname) {
868 		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
869 			if (sname->sn_program == rqst->rq_prog
870 			    && sname->sn_version == rqst->rq_vers) {
871 			retry:
872 				gr->gr_major = gss_accept_sec_context(
873 					&gr->gr_minor,
874 					&client->cl_ctx,
875 					sname->sn_cred,
876 					&recv_tok,
877 					GSS_C_NO_CHANNEL_BINDINGS,
878 					&client->cl_cname,
879 					&mech,
880 					&gr->gr_token,
881 					&ret_flags,
882 					&cred_lifetime,
883 					&client->cl_creds);
884 				if (gr->gr_major ==
885 				    GSS_S_CREDENTIALS_EXPIRED) {
886 					/*
887 					 * Either our creds really did
888 					 * expire or gssd was
889 					 * restarted.
890 					 */
891 					if (rpc_gss_acquire_svc_cred(sname))
892 						goto retry;
893 				}
894 				client->cl_sname = sname;
895 				break;
896 			}
897 		}
898 		if (!sname) {
899 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
900 			    (char *) &recv_tok);
901 			sx_xunlock(&svc_rpc_gss_lock);
902 			return (FALSE);
903 		}
904 	} else {
905 		gr->gr_major = gss_accept_sec_context(
906 			&gr->gr_minor,
907 			&client->cl_ctx,
908 			client->cl_sname->sn_cred,
909 			&recv_tok,
910 			GSS_C_NO_CHANNEL_BINDINGS,
911 			&client->cl_cname,
912 			&mech,
913 			&gr->gr_token,
914 			&ret_flags,
915 			&cred_lifetime,
916 			NULL);
917 	}
918 	sx_xunlock(&svc_rpc_gss_lock);
919 
920 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
921 
922 	/*
923 	 * If we get an error from gss_accept_sec_context, send the
924 	 * reply anyway so that the client gets a chance to see what
925 	 * is wrong.
926 	 */
927 	if (gr->gr_major != GSS_S_COMPLETE &&
928 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
929 		rpc_gss_log_status("accept_sec_context", client->cl_mech,
930 		    gr->gr_major, gr->gr_minor);
931 		client->cl_state = CLIENT_STALE;
932 		return (TRUE);
933 	}
934 
935 	gr->gr_handle.value = &client->cl_id;
936 	gr->gr_handle.length = sizeof(client->cl_id);
937 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
938 
939 	/* Save client info. */
940 	client->cl_mech = mech;
941 	client->cl_qop = GSS_C_QOP_DEFAULT;
942 	client->cl_done_callback = FALSE;
943 
944 	if (gr->gr_major == GSS_S_COMPLETE) {
945 		gss_buffer_desc	export_name;
946 
947 		/*
948 		 * Change client expiration time to be near when the
949 		 * client creds expire (or 24 hours if we can't figure
950 		 * that out).
951 		 */
952 		if (cred_lifetime == GSS_C_INDEFINITE)
953 			cred_lifetime = time_uptime + 24*60*60;
954 
955 		client->cl_expiration = time_uptime + cred_lifetime;
956 
957 		/*
958 		 * Fill in cred details in the rawcred structure.
959 		 */
960 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
961 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
962 		maj_stat = gss_export_name(&min_stat, client->cl_cname,
963 		    &export_name);
964 		if (maj_stat != GSS_S_COMPLETE) {
965 			rpc_gss_log_status("gss_export_name", client->cl_mech,
966 			    maj_stat, min_stat);
967 			return (FALSE);
968 		}
969 		client->cl_rawcred.client_principal =
970 			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
971 			    + export_name.length);
972 		client->cl_rawcred.client_principal->len = export_name.length;
973 		memcpy(client->cl_rawcred.client_principal->name,
974 		    export_name.value, export_name.length);
975 		gss_release_buffer(&min_stat, &export_name);
976 		client->cl_rawcred.svc_principal =
977 			client->cl_sname->sn_principal;
978 		client->cl_rawcred.service = gc->gc_svc;
979 
980 		/*
981 		 * Use gss_pname_to_uid to map to unix creds. For
982 		 * kerberos5, this uses krb5_aname_to_localname.
983 		 */
984 		svc_rpc_gss_build_ucred(client, client->cl_cname);
985 		svc_rpc_gss_set_flavor(client);
986 		gss_release_name(&min_stat, &client->cl_cname);
987 
988 #ifdef DEBUG
989 		{
990 			gss_buffer_desc mechname;
991 
992 			gss_oid_to_str(&min_stat, mech, &mechname);
993 
994 			rpc_gss_log_debug("accepted context for %s with "
995 			    "<mech %.*s, qop %d, svc %d>",
996 			    client->cl_rawcred.client_principal->name,
997 			    mechname.length, (char *)mechname.value,
998 			    client->cl_qop, client->cl_rawcred.service);
999 
1000 			gss_release_buffer(&min_stat, &mechname);
1001 		}
1002 #endif /* DEBUG */
1003 	}
1004 	return (TRUE);
1005 }
1006 
1007 static bool_t
svc_rpc_gss_validate(struct svc_rpc_gss_client * client,struct rpc_msg * msg,gss_qop_t * qop,rpc_gss_proc_t gcproc)1008 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1009     gss_qop_t *qop, rpc_gss_proc_t gcproc)
1010 {
1011 	struct opaque_auth	*oa;
1012 	gss_buffer_desc		 rpcbuf, checksum;
1013 	OM_uint32		 maj_stat, min_stat;
1014 	gss_qop_t		 qop_state;
1015 	int32_t			 rpchdr[128 / sizeof(int32_t)];
1016 	int32_t			*buf;
1017 
1018 	rpc_gss_log_debug("in svc_rpc_gss_validate()");
1019 
1020 	memset(rpchdr, 0, sizeof(rpchdr));
1021 
1022 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
1023 	buf = rpchdr;
1024 	IXDR_PUT_LONG(buf, msg->rm_xid);
1025 	IXDR_PUT_ENUM(buf, msg->rm_direction);
1026 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1027 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1028 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1029 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1030 	oa = &msg->rm_call.cb_cred;
1031 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
1032 	IXDR_PUT_LONG(buf, oa->oa_length);
1033 	if (oa->oa_length) {
1034 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1035 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1036 	}
1037 	rpcbuf.value = rpchdr;
1038 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1039 
1040 	checksum.value = msg->rm_call.cb_verf.oa_base;
1041 	checksum.length = msg->rm_call.cb_verf.oa_length;
1042 
1043 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1044 				  &qop_state);
1045 
1046 	if (maj_stat != GSS_S_COMPLETE) {
1047 		rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1048 		    maj_stat, min_stat);
1049 		/*
1050 		 * A bug in some versions of the Linux client generates a
1051 		 * Destroy operation with a bogus encrypted checksum. Deleting
1052 		 * the credential handle for that case causes the mount to fail.
1053 		 * Since the checksum is bogus (gss_verify_mic() failed), it
1054 		 * doesn't make sense to destroy the handle and not doing so
1055 		 * fixes the Linux mount.
1056 		 */
1057 		if (gcproc != RPCSEC_GSS_DESTROY)
1058 			client->cl_state = CLIENT_STALE;
1059 		return (FALSE);
1060 	}
1061 
1062 	*qop = qop_state;
1063 	return (TRUE);
1064 }
1065 
1066 static bool_t
svc_rpc_gss_nextverf(struct svc_rpc_gss_client * client,struct svc_req * rqst,u_int seq)1067 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1068     struct svc_req *rqst, u_int seq)
1069 {
1070 	gss_buffer_desc		signbuf;
1071 	gss_buffer_desc		mic;
1072 	OM_uint32		maj_stat, min_stat;
1073 	uint32_t		nseq;
1074 
1075 	rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1076 
1077 	nseq = htonl(seq);
1078 	signbuf.value = &nseq;
1079 	signbuf.length = sizeof(nseq);
1080 
1081 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1082 	    &signbuf, &mic);
1083 
1084 	if (maj_stat != GSS_S_COMPLETE) {
1085 		rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1086 		client->cl_state = CLIENT_STALE;
1087 		return (FALSE);
1088 	}
1089 
1090 	KASSERT(mic.length <= MAX_AUTH_BYTES,
1091 	    ("MIC too large for RPCSEC_GSS"));
1092 
1093 	rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1094 	rqst->rq_verf.oa_length = mic.length;
1095 	bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1096 
1097 	gss_release_buffer(&min_stat, &mic);
1098 
1099 	return (TRUE);
1100 }
1101 
1102 static bool_t
svc_rpc_gss_callback(struct svc_rpc_gss_client * client,struct svc_req * rqst)1103 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1104 {
1105 	struct svc_rpc_gss_callback *scb;
1106 	rpc_gss_lock_t	lock;
1107 	void		*cookie;
1108 	bool_t		cb_res;
1109 	bool_t		result;
1110 
1111 	/*
1112 	 * See if we have a callback for this guy.
1113 	 */
1114 	result = TRUE;
1115 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1116 		if (scb->cb_callback.program == rqst->rq_prog
1117 		    && scb->cb_callback.version == rqst->rq_vers) {
1118 			/*
1119 			 * This one matches. Call the callback and see
1120 			 * if it wants to veto or something.
1121 			 */
1122 			lock.locked = FALSE;
1123 			lock.raw_cred = &client->cl_rawcred;
1124 			cb_res = scb->cb_callback.callback(rqst,
1125 			    client->cl_creds,
1126 			    client->cl_ctx,
1127 			    &lock,
1128 			    &cookie);
1129 
1130 			if (!cb_res) {
1131 				client->cl_state = CLIENT_STALE;
1132 				result = FALSE;
1133 				break;
1134 			}
1135 
1136 			/*
1137 			 * The callback accepted the connection - it
1138 			 * is responsible for freeing client->cl_creds
1139 			 * now.
1140 			 */
1141 			client->cl_creds = GSS_C_NO_CREDENTIAL;
1142 			client->cl_locked = lock.locked;
1143 			client->cl_cookie = cookie;
1144 			return (TRUE);
1145 		}
1146 	}
1147 
1148 	/*
1149 	 * Either no callback exists for this program/version or one
1150 	 * of the callbacks rejected the connection. We just need to
1151 	 * clean up the delegated client creds, if any.
1152 	 */
1153 	if (client->cl_creds) {
1154 		OM_uint32 min_ver;
1155 		gss_release_cred(&min_ver, &client->cl_creds);
1156 	}
1157 	return (result);
1158 }
1159 
1160 static bool_t
svc_rpc_gss_check_replay(struct svc_rpc_gss_client * client,uint32_t seq)1161 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1162 {
1163 	u_int32_t offset;
1164 	int word, bit;
1165 	bool_t result;
1166 
1167 	sx_xlock(&client->cl_lock);
1168 	if (seq <= client->cl_seqlast) {
1169 		/*
1170 		 * The request sequence number is less than
1171 		 * the largest we have seen so far. If it is
1172 		 * outside the window or if we have seen a
1173 		 * request with this sequence before, silently
1174 		 * discard it.
1175 		 */
1176 		offset = client->cl_seqlast - seq;
1177 		if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1178 			result = FALSE;
1179 			goto out;
1180 		}
1181 		word = offset / 32;
1182 		bit = offset % 32;
1183 		if (client->cl_seqmask[word] & (1 << bit)) {
1184 			result = FALSE;
1185 			goto out;
1186 		}
1187 	}
1188 
1189 	result = TRUE;
1190 out:
1191 	sx_xunlock(&client->cl_lock);
1192 	return (result);
1193 }
1194 
1195 static void
svc_rpc_gss_update_seq(struct svc_rpc_gss_client * client,uint32_t seq)1196 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1197 {
1198 	int offset, i, word, bit;
1199 	uint32_t carry, newcarry;
1200 
1201 	sx_xlock(&client->cl_lock);
1202 	if (seq > client->cl_seqlast) {
1203 		/*
1204 		 * This request has a sequence number greater
1205 		 * than any we have seen so far. Advance the
1206 		 * seq window and set bit zero of the window
1207 		 * (which corresponds to the new sequence
1208 		 * number)
1209 		 */
1210 		offset = seq - client->cl_seqlast;
1211 		while (offset > 32) {
1212 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1213 			     i > 0; i--) {
1214 				client->cl_seqmask[i] = client->cl_seqmask[i-1];
1215 			}
1216 			client->cl_seqmask[0] = 0;
1217 			offset -= 32;
1218 		}
1219 		carry = 0;
1220 		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1221 			newcarry = client->cl_seqmask[i] >> (32 - offset);
1222 			client->cl_seqmask[i] =
1223 				(client->cl_seqmask[i] << offset) | carry;
1224 			carry = newcarry;
1225 		}
1226 		client->cl_seqmask[0] |= 1;
1227 		client->cl_seqlast = seq;
1228 	} else {
1229 		offset = client->cl_seqlast - seq;
1230 		word = offset / 32;
1231 		bit = offset % 32;
1232 		client->cl_seqmask[word] |= (1 << bit);
1233 	}
1234 	sx_xunlock(&client->cl_lock);
1235 }
1236 
1237 enum auth_stat
svc_rpc_gss(struct svc_req * rqst,struct rpc_msg * msg)1238 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1239 
1240 {
1241 	OM_uint32		 min_stat;
1242 	XDR	 		 xdrs;
1243 	struct svc_rpc_gss_cookedcred *cc;
1244 	struct svc_rpc_gss_client *client;
1245 	struct rpc_gss_cred	 gc;
1246 	struct rpc_gss_init_res	 gr;
1247 	gss_qop_t		 qop;
1248 	int			 call_stat;
1249 	enum auth_stat		 result;
1250 
1251 	rpc_gss_log_debug("in svc_rpc_gss()");
1252 
1253 	/* Garbage collect old clients. */
1254 	svc_rpc_gss_timeout_clients();
1255 
1256 	/* Initialize reply. */
1257 	rqst->rq_verf = _null_auth;
1258 
1259 	/* Deserialize client credentials. */
1260 	if (rqst->rq_cred.oa_length <= 0)
1261 		return (AUTH_BADCRED);
1262 
1263 	memset(&gc, 0, sizeof(gc));
1264 
1265 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1266 	    rqst->rq_cred.oa_length, XDR_DECODE);
1267 
1268 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1269 		XDR_DESTROY(&xdrs);
1270 		return (AUTH_BADCRED);
1271 	}
1272 	XDR_DESTROY(&xdrs);
1273 
1274 	client = NULL;
1275 
1276 	/* Check version. */
1277 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
1278 		result = AUTH_BADCRED;
1279 		goto out;
1280 	}
1281 
1282 	/* Check the proc and find the client (or create it) */
1283 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
1284 		if (gc.gc_handle.length != 0) {
1285 			result = AUTH_BADCRED;
1286 			goto out;
1287 		}
1288 		client = svc_rpc_gss_create_client();
1289 	} else {
1290 		struct svc_rpc_gss_clientid *p;
1291 		if (gc.gc_handle.length != sizeof(*p)) {
1292 			result = AUTH_BADCRED;
1293 			goto out;
1294 		}
1295 		p = gc.gc_handle.value;
1296 		client = svc_rpc_gss_find_client(p);
1297 		if (!client) {
1298 			/*
1299 			 * Can't find the client - we may have
1300 			 * destroyed it - tell the other side to
1301 			 * re-authenticate.
1302 			 */
1303 			result = RPCSEC_GSS_CREDPROBLEM;
1304 			goto out;
1305 		}
1306 	}
1307 	cc = rqst->rq_clntcred;
1308 	cc->cc_client = client;
1309 	cc->cc_service = gc.gc_svc;
1310 	cc->cc_seq = gc.gc_seq;
1311 
1312 	/*
1313 	 * The service and sequence number must be ignored for
1314 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1315 	 */
1316 	if (gc.gc_proc != RPCSEC_GSS_INIT
1317 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1318 		/*
1319 		 * Check for sequence number overflow.
1320 		 */
1321 		if (gc.gc_seq >= MAXSEQ) {
1322 			result = RPCSEC_GSS_CTXPROBLEM;
1323 			goto out;
1324 		}
1325 
1326 		/*
1327 		 * Check for valid service.
1328 		 */
1329 		if (gc.gc_svc != rpc_gss_svc_none &&
1330 		    gc.gc_svc != rpc_gss_svc_integrity &&
1331 		    gc.gc_svc != rpc_gss_svc_privacy) {
1332 			result = AUTH_BADCRED;
1333 			goto out;
1334 		}
1335 	}
1336 
1337 	/* Handle RPCSEC_GSS control procedure. */
1338 	switch (gc.gc_proc) {
1339 
1340 	case RPCSEC_GSS_INIT:
1341 	case RPCSEC_GSS_CONTINUE_INIT:
1342 		if (rqst->rq_proc != NULLPROC) {
1343 			result = AUTH_REJECTEDCRED;
1344 			break;
1345 		}
1346 
1347 		memset(&gr, 0, sizeof(gr));
1348 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1349 			result = AUTH_REJECTEDCRED;
1350 			break;
1351 		}
1352 
1353 		if (gr.gr_major == GSS_S_COMPLETE) {
1354 			/*
1355 			 * We borrow the space for the call verf to
1356 			 * pack our reply verf.
1357 			 */
1358 			rqst->rq_verf = msg->rm_call.cb_verf;
1359 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1360 				result = AUTH_REJECTEDCRED;
1361 				break;
1362 			}
1363 		} else {
1364 			rqst->rq_verf = _null_auth;
1365 		}
1366 
1367 		call_stat = svc_sendreply(rqst,
1368 		    (xdrproc_t) xdr_rpc_gss_init_res,
1369 		    (caddr_t) &gr);
1370 
1371 		gss_release_buffer(&min_stat, &gr.gr_token);
1372 
1373 		if (!call_stat) {
1374 			result = AUTH_FAILED;
1375 			break;
1376 		}
1377 
1378 		if (gr.gr_major == GSS_S_COMPLETE)
1379 			client->cl_state = CLIENT_ESTABLISHED;
1380 
1381 		result = RPCSEC_GSS_NODISPATCH;
1382 		break;
1383 
1384 	case RPCSEC_GSS_DATA:
1385 	case RPCSEC_GSS_DESTROY:
1386 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1387 			result = RPCSEC_GSS_NODISPATCH;
1388 			break;
1389 		}
1390 
1391 		if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1392 			result = RPCSEC_GSS_CREDPROBLEM;
1393 			break;
1394 		}
1395 
1396 		/*
1397 		 * We borrow the space for the call verf to pack our
1398 		 * reply verf.
1399 		 */
1400 		rqst->rq_verf = msg->rm_call.cb_verf;
1401 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1402 			result = RPCSEC_GSS_CTXPROBLEM;
1403 			break;
1404 		}
1405 
1406 		svc_rpc_gss_update_seq(client, gc.gc_seq);
1407 
1408 		/*
1409 		 * Change the SVCAUTH ops on the request to point at
1410 		 * our own code so that we can unwrap the arguments
1411 		 * and wrap the result. The caller will re-set this on
1412 		 * every request to point to a set of null wrap/unwrap
1413 		 * methods. Acquire an extra reference to the client
1414 		 * which will be released by svc_rpc_gss_release()
1415 		 * after the request has finished processing.
1416 		 */
1417 		refcount_acquire(&client->cl_refs);
1418 		rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1419 		rqst->rq_auth.svc_ah_private = cc;
1420 
1421 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1422 			/*
1423 			 * We might be ready to do a callback to the server to
1424 			 * see if it wants to accept/reject the connection.
1425 			 */
1426 			sx_xlock(&client->cl_lock);
1427 			if (!client->cl_done_callback) {
1428 				client->cl_done_callback = TRUE;
1429 				client->cl_qop = qop;
1430 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1431 					client->cl_rawcred.mechanism, qop);
1432 				if (!svc_rpc_gss_callback(client, rqst)) {
1433 					result = AUTH_REJECTEDCRED;
1434 					sx_xunlock(&client->cl_lock);
1435 					break;
1436 				}
1437 			}
1438 			sx_xunlock(&client->cl_lock);
1439 
1440 			/*
1441 			 * If the server has locked this client to a
1442 			 * particular service+qop pair, enforce that
1443 			 * restriction now.
1444 			 */
1445 			if (client->cl_locked) {
1446 				if (client->cl_rawcred.service != gc.gc_svc) {
1447 					result = AUTH_FAILED;
1448 					break;
1449 				} else if (client->cl_qop != qop) {
1450 					result = AUTH_BADVERF;
1451 					break;
1452 				}
1453 			}
1454 
1455 			/*
1456 			 * If the qop changed, look up the new qop
1457 			 * name for rawcred.
1458 			 */
1459 			if (client->cl_qop != qop) {
1460 				client->cl_qop = qop;
1461 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1462 					client->cl_rawcred.mechanism, qop);
1463 			}
1464 
1465 			/*
1466 			 * Make sure we use the right service value
1467 			 * for unwrap/wrap.
1468 			 */
1469 			if (client->cl_rawcred.service != gc.gc_svc) {
1470 				client->cl_rawcred.service = gc.gc_svc;
1471 				svc_rpc_gss_set_flavor(client);
1472 			}
1473 
1474 			result = AUTH_OK;
1475 		} else {
1476 			if (rqst->rq_proc != NULLPROC) {
1477 				result = AUTH_REJECTEDCRED;
1478 				break;
1479 			}
1480 
1481 			call_stat = svc_sendreply(rqst,
1482 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1483 
1484 			if (!call_stat) {
1485 				result = AUTH_FAILED;
1486 				break;
1487 			}
1488 
1489 			svc_rpc_gss_forget_client(client);
1490 
1491 			result = RPCSEC_GSS_NODISPATCH;
1492 			break;
1493 		}
1494 		break;
1495 
1496 	default:
1497 		result = AUTH_BADCRED;
1498 		break;
1499 	}
1500 out:
1501 	if (client)
1502 		svc_rpc_gss_release_client(client);
1503 
1504 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1505 	return (result);
1506 }
1507 
1508 static bool_t
svc_rpc_gss_wrap(SVCAUTH * auth,struct mbuf ** mp)1509 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1510 {
1511 	struct svc_rpc_gss_cookedcred *cc;
1512 	struct svc_rpc_gss_client *client;
1513 
1514 	rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1515 
1516 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1517 	client = cc->cc_client;
1518 	if (client->cl_state != CLIENT_ESTABLISHED
1519 	    || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1520 		return (TRUE);
1521 	}
1522 
1523 	return (xdr_rpc_gss_wrap_data(mp,
1524 		client->cl_ctx, client->cl_qop,
1525 		cc->cc_service, cc->cc_seq));
1526 }
1527 
1528 static bool_t
svc_rpc_gss_unwrap(SVCAUTH * auth,struct mbuf ** mp)1529 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1530 {
1531 	struct svc_rpc_gss_cookedcred *cc;
1532 	struct svc_rpc_gss_client *client;
1533 
1534 	rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1535 
1536 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1537 	client = cc->cc_client;
1538 	if (client->cl_state != CLIENT_ESTABLISHED
1539 	    || cc->cc_service == rpc_gss_svc_none) {
1540 		return (TRUE);
1541 	}
1542 
1543 	return (xdr_rpc_gss_unwrap_data(mp,
1544 		client->cl_ctx, client->cl_qop,
1545 		cc->cc_service, cc->cc_seq));
1546 }
1547 
1548 static void
svc_rpc_gss_release(SVCAUTH * auth)1549 svc_rpc_gss_release(SVCAUTH *auth)
1550 {
1551 	struct svc_rpc_gss_cookedcred *cc;
1552 	struct svc_rpc_gss_client *client;
1553 
1554 	rpc_gss_log_debug("in svc_rpc_gss_release()");
1555 
1556 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1557 	client = cc->cc_client;
1558 	svc_rpc_gss_release_client(client);
1559 }
1560