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