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