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