1/*-
2 * Copyright (c) 2008 Doug Rabson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$FreeBSD$
27 */
28/*
29  svc_rpcsec_gss.c
30
31  Copyright (c) 2000 The Regents of the University of Michigan.
32  All rights reserved.
33
34  Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
35  All rights reserved, all wrongs reversed.
36
37  Redistribution and use in source and binary forms, with or without
38  modification, are permitted provided that the following conditions
39  are met:
40
41  1. Redistributions of source code must retain the above copyright
42     notice, this list of conditions and the following disclaimer.
43  2. Redistributions in binary form must reproduce the above copyright
44     notice, this list of conditions and the following disclaimer in the
45     documentation and/or other materials provided with the distribution.
46  3. Neither the name of the University nor the names of its
47     contributors may be used to endorse or promote products derived
48     from this software without specific prior written permission.
49
50  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
51  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
57  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61
62  $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
63 */
64
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <pwd.h>
69#include <grp.h>
70#include <errno.h>
71#include <unistd.h>
72#include <sys/queue.h>
73#include <rpc/rpc.h>
74#include <rpc/rpcsec_gss.h>
75#include "rpcsec_gss_int.h"
76
77static bool_t	svc_rpc_gss_initialised = FALSE;
78
79static bool_t   svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
80static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
81static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
82
83static struct svc_auth_ops svc_auth_gss_ops = {
84	svc_rpc_gss_wrap,
85	svc_rpc_gss_unwrap,
86};
87
88struct svc_rpc_gss_callback {
89	SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
90	rpc_gss_callback_t	cb_callback;
91};
92static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
93	svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
94
95struct svc_rpc_gss_svc_name {
96	SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
97	char			*sn_principal;
98	gss_OID			sn_mech;
99	u_int			sn_req_time;
100	gss_cred_id_t		sn_cred;
101	u_int			sn_program;
102	u_int			sn_version;
103};
104static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
105	svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
106
107enum svc_rpc_gss_client_state {
108	CLIENT_NEW,				/* still authenticating */
109	CLIENT_ESTABLISHED,			/* context established */
110	CLIENT_STALE				/* garbage to collect */
111};
112
113#define SVC_RPC_GSS_SEQWINDOW	128
114
115struct svc_rpc_gss_client {
116	TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
117	TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
118	uint32_t		cl_id;
119	time_t			cl_expiration;	/* when to gc */
120	enum svc_rpc_gss_client_state cl_state;	/* client state */
121	bool_t			cl_locked;	/* fixed service+qop */
122	gss_ctx_id_t		cl_ctx;		/* context id */
123	gss_cred_id_t		cl_creds;	/* delegated creds */
124	gss_name_t		cl_cname;	/* client name */
125	struct svc_rpc_gss_svc_name *cl_sname;	/* server name used */
126	rpc_gss_rawcred_t	cl_rawcred;	/* raw credentials */
127	rpc_gss_ucred_t		cl_ucred;	/* unix-style credentials */
128	bool_t			cl_done_callback; /* TRUE after call */
129	void			*cl_cookie;	/* user cookie from callback */
130	gid_t			cl_gid_storage[NGRPS];
131	gss_OID			cl_mech;	/* mechanism */
132	gss_qop_t		cl_qop;		/* quality of protection */
133	u_int			cl_seq;		/* current sequence number */
134	u_int			cl_win;		/* sequence window size */
135	u_int			cl_seqlast;	/* sequence window origin */
136	uint32_t		cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
137	gss_buffer_desc		cl_verf;	/* buffer for verf checksum */
138};
139TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
140
141#define CLIENT_HASH_SIZE	256
142#define CLIENT_MAX		128
143static struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
144static struct svc_rpc_gss_client_list svc_rpc_gss_clients;
145static size_t svc_rpc_gss_client_count;
146static uint32_t svc_rpc_gss_next_clientid = 1;
147
148#ifdef __GNUC__
149static void svc_rpc_gss_init(void) __attribute__ ((constructor));
150#endif
151
152static void
153svc_rpc_gss_init(void)
154{
155	int i;
156
157	if (!svc_rpc_gss_initialised) {
158		for (i = 0; i < CLIENT_HASH_SIZE; i++)
159			TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
160		TAILQ_INIT(&svc_rpc_gss_clients);
161		svc_auth_reg(RPCSEC_GSS, svc_rpc_gss);
162		svc_rpc_gss_initialised = TRUE;
163	}
164}
165
166bool_t
167rpc_gss_set_callback(rpc_gss_callback_t *cb)
168{
169	struct svc_rpc_gss_callback *scb;
170
171	scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
172	if (!scb) {
173		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
174		return (FALSE);
175	}
176	scb->cb_callback = *cb;
177	SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
178
179	return (TRUE);
180}
181
182bool_t
183rpc_gss_set_svc_name(const char *principal, const char *mechanism,
184    u_int req_time, u_int program, u_int version)
185{
186	OM_uint32		maj_stat, min_stat;
187	struct svc_rpc_gss_svc_name *sname;
188	gss_buffer_desc		namebuf;
189	gss_name_t		name;
190	gss_OID			mech_oid;
191	gss_OID_set_desc	oid_set;
192	gss_cred_id_t		cred;
193
194	svc_rpc_gss_init();
195
196	if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
197		return (FALSE);
198	oid_set.count = 1;
199	oid_set.elements = mech_oid;
200
201	namebuf.value = (void *)(intptr_t) principal;
202	namebuf.length = strlen(principal);
203
204	maj_stat = gss_import_name(&min_stat, &namebuf,
205				   GSS_C_NT_HOSTBASED_SERVICE, &name);
206	if (maj_stat != GSS_S_COMPLETE)
207		return (FALSE);
208
209	maj_stat = gss_acquire_cred(&min_stat, name,
210	    req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL);
211	if (maj_stat != GSS_S_COMPLETE)
212		return (FALSE);
213
214	gss_release_name(&min_stat, &name);
215
216	sname = malloc(sizeof(struct svc_rpc_gss_svc_name));
217	if (!sname)
218		return (FALSE);
219	sname->sn_principal = strdup(principal);
220	sname->sn_mech = mech_oid;
221	sname->sn_req_time = req_time;
222	sname->sn_cred = cred;
223	sname->sn_program = program;
224	sname->sn_version = version;
225	SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
226
227	return (TRUE);
228}
229
230bool_t
231rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
232    const char *mech, const char *name, const char *node, const char *domain)
233{
234	OM_uint32		maj_stat, min_stat;
235	gss_OID			mech_oid;
236	size_t			namelen;
237	gss_buffer_desc		buf;
238	gss_name_t		gss_name, gss_mech_name;
239	rpc_gss_principal_t	result;
240
241	svc_rpc_gss_init();
242
243	if (!rpc_gss_mech_to_oid(mech, &mech_oid))
244		return (FALSE);
245
246	/*
247	 * Construct a gss_buffer containing the full name formatted
248	 * as "name/node@domain" where node and domain are optional.
249	 */
250	namelen = strlen(name);
251	if (node) {
252		namelen += strlen(node) + 1;
253	}
254	if (domain) {
255		namelen += strlen(domain) + 1;
256	}
257
258	buf.value = mem_alloc(namelen);
259	buf.length = namelen;
260	strcpy((char *) buf.value, name);
261	if (node) {
262		strcat((char *) buf.value, "/");
263		strcat((char *) buf.value, node);
264	}
265	if (domain) {
266		strcat((char *) buf.value, "@");
267		strcat((char *) buf.value, domain);
268	}
269
270	/*
271	 * Convert that to a gss_name_t and then convert that to a
272	 * mechanism name in the selected mechanism.
273	 */
274	maj_stat = gss_import_name(&min_stat, &buf,
275	    GSS_C_NT_USER_NAME, &gss_name);
276	mem_free(buf.value, buf.length);
277	if (maj_stat != GSS_S_COMPLETE) {
278		log_status("gss_import_name", mech_oid, maj_stat, min_stat);
279		return (FALSE);
280	}
281	maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
282	    &gss_mech_name);
283	if (maj_stat != GSS_S_COMPLETE) {
284		log_status("gss_canonicalize_name", mech_oid, maj_stat,
285		    min_stat);
286		gss_release_name(&min_stat, &gss_name);
287		return (FALSE);
288	}
289	gss_release_name(&min_stat, &gss_name);
290
291	/*
292	 * Export the mechanism name and use that to construct the
293	 * rpc_gss_principal_t result.
294	 */
295	maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
296	if (maj_stat != GSS_S_COMPLETE) {
297		log_status("gss_export_name", mech_oid, maj_stat, min_stat);
298		gss_release_name(&min_stat, &gss_mech_name);
299		return (FALSE);
300	}
301	gss_release_name(&min_stat, &gss_mech_name);
302
303	result = mem_alloc(sizeof(int) + buf.length);
304	if (!result) {
305		gss_release_buffer(&min_stat, &buf);
306		return (FALSE);
307	}
308	result->len = buf.length;
309	memcpy(result->name, buf.value, buf.length);
310	gss_release_buffer(&min_stat, &buf);
311
312	*principal = result;
313	return (TRUE);
314}
315
316bool_t
317rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
318    rpc_gss_ucred_t **ucred, void **cookie)
319{
320	struct svc_rpc_gss_client *client;
321
322	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
323		return (FALSE);
324
325	client = req->rq_clntcred;
326	if (rcred)
327		*rcred = &client->cl_rawcred;
328	if (ucred)
329		*ucred = &client->cl_ucred;
330	if (cookie)
331		*cookie = client->cl_cookie;
332	return (TRUE);
333}
334
335int
336rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
337{
338	struct svc_rpc_gss_client *client = req->rq_clntcred;
339	int			want_conf;
340	OM_uint32		max;
341	OM_uint32		maj_stat, min_stat;
342	int			result;
343
344	switch (client->cl_rawcred.service) {
345	case rpc_gss_svc_none:
346		return (max_tp_unit_len);
347		break;
348
349	case rpc_gss_svc_default:
350	case rpc_gss_svc_integrity:
351		want_conf = FALSE;
352		break;
353
354	case rpc_gss_svc_privacy:
355		want_conf = TRUE;
356		break;
357
358	default:
359		return (0);
360	}
361
362	maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
363	    client->cl_qop, max_tp_unit_len, &max);
364
365	if (maj_stat == GSS_S_COMPLETE) {
366		result = (int) max;
367		if (result < 0)
368			result = 0;
369		return (result);
370	} else {
371		log_status("gss_wrap_size_limit", client->cl_mech,
372		    maj_stat, min_stat);
373		return (0);
374	}
375}
376
377static struct svc_rpc_gss_client *
378svc_rpc_gss_find_client(uint32_t clientid)
379{
380	struct svc_rpc_gss_client *client;
381	struct svc_rpc_gss_client_list *list;
382
383
384	log_debug("in svc_rpc_gss_find_client(%d)", clientid);
385
386	list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE];
387	TAILQ_FOREACH(client, list, cl_link) {
388		if (client->cl_id == clientid) {
389			/*
390			 * Move this client to the front of the LRU
391			 * list.
392			 */
393			TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
394			TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
395			    cl_alllink);
396			return client;
397		}
398	}
399
400	return (NULL);
401}
402
403static struct svc_rpc_gss_client *
404svc_rpc_gss_create_client(void)
405{
406	struct svc_rpc_gss_client *client;
407	struct svc_rpc_gss_client_list *list;
408
409	log_debug("in svc_rpc_gss_create_client()");
410
411	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
412	memset(client, 0, sizeof(struct svc_rpc_gss_client));
413	client->cl_id = svc_rpc_gss_next_clientid++;
414	list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
415	TAILQ_INSERT_HEAD(list, client, cl_link);
416	TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
417
418	/*
419	 * Start the client off with a short expiration time. We will
420	 * try to get a saner value from the client creds later.
421	 */
422	client->cl_state = CLIENT_NEW;
423	client->cl_locked = FALSE;
424	client->cl_expiration = time(0) + 5*60;
425	svc_rpc_gss_client_count++;
426
427	return (client);
428}
429
430static void
431svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
432{
433	struct svc_rpc_gss_client_list *list;
434	OM_uint32 min_stat;
435
436	log_debug("in svc_rpc_gss_destroy_client()");
437
438	if (client->cl_ctx)
439		gss_delete_sec_context(&min_stat,
440		    &client->cl_ctx, GSS_C_NO_BUFFER);
441
442	if (client->cl_cname)
443		gss_release_name(&min_stat, &client->cl_cname);
444
445	if (client->cl_rawcred.client_principal)
446		mem_free(client->cl_rawcred.client_principal,
447		    sizeof(*client->cl_rawcred.client_principal)
448		    + client->cl_rawcred.client_principal->len);
449
450	if (client->cl_verf.value)
451		gss_release_buffer(&min_stat, &client->cl_verf);
452
453	list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
454	TAILQ_REMOVE(list, client, cl_link);
455	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
456	svc_rpc_gss_client_count--;
457	mem_free(client, sizeof(*client));
458}
459
460static void
461svc_rpc_gss_timeout_clients(void)
462{
463	struct svc_rpc_gss_client *client;
464	struct svc_rpc_gss_client *nclient;
465	time_t now = time(0);
466
467	log_debug("in svc_rpc_gss_timeout_clients()");
468	/*
469	 * First enforce the max client limit. We keep
470	 * svc_rpc_gss_clients in LRU order.
471	 */
472	while (svc_rpc_gss_client_count > CLIENT_MAX)
473		svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
474			    svc_rpc_gss_client_list));
475	TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
476		if (client->cl_state == CLIENT_STALE
477		    || now > client->cl_expiration) {
478			log_debug("expiring client %p", client);
479			svc_rpc_gss_destroy_client(client);
480		}
481	}
482}
483
484#ifdef DEBUG
485/*
486 * OID<->string routines.  These are uuuuugly.
487 */
488static OM_uint32
489gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
490{
491	char		numstr[128];
492	unsigned long	number;
493	int		numshift;
494	size_t		string_length;
495	size_t		i;
496	unsigned char	*cp;
497	char		*bp;
498
499	/* Decoded according to krb5/gssapi_krb5.c */
500
501	/* First determine the size of the string */
502	string_length = 0;
503	number = 0;
504	numshift = 0;
505	cp = (unsigned char *) oid->elements;
506	number = (unsigned long) cp[0];
507	sprintf(numstr, "%ld ", number/40);
508	string_length += strlen(numstr);
509	sprintf(numstr, "%ld ", number%40);
510	string_length += strlen(numstr);
511	for (i=1; i<oid->length; i++) {
512		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
513			number = (number << 7) | (cp[i] & 0x7f);
514			numshift += 7;
515		}
516		else {
517			*minor_status = 0;
518			return(GSS_S_FAILURE);
519		}
520		if ((cp[i] & 0x80) == 0) {
521			sprintf(numstr, "%ld ", number);
522			string_length += strlen(numstr);
523			number = 0;
524			numshift = 0;
525		}
526	}
527	/*
528	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
529	 * here for "{ " and "}\0".
530	 */
531	string_length += 4;
532	if ((bp = (char *) mem_alloc(string_length))) {
533		strcpy(bp, "{ ");
534		number = (unsigned long) cp[0];
535		sprintf(numstr, "%ld ", number/40);
536		strcat(bp, numstr);
537		sprintf(numstr, "%ld ", number%40);
538		strcat(bp, numstr);
539		number = 0;
540		cp = (unsigned char *) oid->elements;
541		for (i=1; i<oid->length; i++) {
542			number = (number << 7) | (cp[i] & 0x7f);
543			if ((cp[i] & 0x80) == 0) {
544				sprintf(numstr, "%ld ", number);
545				strcat(bp, numstr);
546				number = 0;
547			}
548		}
549		strcat(bp, "}");
550		oid_str->length = strlen(bp)+1;
551		oid_str->value = (void *) bp;
552		*minor_status = 0;
553		return(GSS_S_COMPLETE);
554	}
555	*minor_status = 0;
556	return(GSS_S_FAILURE);
557}
558#endif
559
560static void
561svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
562    const gss_name_t name)
563{
564	OM_uint32		maj_stat, min_stat;
565	char			buf[128];
566	uid_t			uid;
567	struct passwd		pwd, *pw;
568	rpc_gss_ucred_t		*uc = &client->cl_ucred;
569
570	uc->uid = 65534;
571	uc->gid = 65534;
572	uc->gidlen = 0;
573	uc->gidlist = client->cl_gid_storage;
574
575	maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
576	if (maj_stat != GSS_S_COMPLETE)
577		return;
578
579	getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
580	if (pw) {
581		int len = NGRPS;
582		uc->uid = pw->pw_uid;
583		uc->gid = pw->pw_gid;
584		uc->gidlist = client->cl_gid_storage;
585		getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
586		uc->gidlen = len;
587	}
588}
589
590static bool_t
591svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
592			       struct svc_req *rqst,
593			       struct rpc_gss_init_res *gr,
594			       struct rpc_gss_cred *gc)
595{
596	gss_buffer_desc		recv_tok;
597	gss_OID			mech;
598	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
599	OM_uint32		cred_lifetime;
600	struct svc_rpc_gss_svc_name *sname;
601
602	log_debug("in svc_rpc_gss_accept_context()");
603
604	/* Deserialize arguments. */
605	memset(&recv_tok, 0, sizeof(recv_tok));
606
607	if (!svc_getargs(rqst->rq_xprt,
608		(xdrproc_t) xdr_gss_buffer_desc,
609		(caddr_t) &recv_tok)) {
610		client->cl_state = CLIENT_STALE;
611		return (FALSE);
612	}
613
614	/*
615	 * First time round, try all the server names we have until
616	 * one matches. Afterwards, stick with that one.
617	 */
618	if (!client->cl_sname) {
619		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
620			if (sname->sn_program == rqst->rq_prog
621			    && sname->sn_version == rqst->rq_vers) {
622				gr->gr_major = gss_accept_sec_context(
623					&gr->gr_minor,
624					&client->cl_ctx,
625					sname->sn_cred,
626					&recv_tok,
627					GSS_C_NO_CHANNEL_BINDINGS,
628					&client->cl_cname,
629					&mech,
630					&gr->gr_token,
631					&ret_flags,
632					&cred_lifetime,
633					&client->cl_creds);
634				client->cl_sname = sname;
635				break;
636			}
637		}
638		if (!sname) {
639			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
640			    (char *) &recv_tok);
641			return (FALSE);
642		}
643	} else {
644		gr->gr_major = gss_accept_sec_context(
645			&gr->gr_minor,
646			&client->cl_ctx,
647			client->cl_sname->sn_cred,
648			&recv_tok,
649			GSS_C_NO_CHANNEL_BINDINGS,
650			&client->cl_cname,
651			&mech,
652			&gr->gr_token,
653			&ret_flags,
654			&cred_lifetime,
655			NULL);
656	}
657
658	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
659
660	/*
661	 * If we get an error from gss_accept_sec_context, send the
662	 * reply anyway so that the client gets a chance to see what
663	 * is wrong.
664	 */
665	if (gr->gr_major != GSS_S_COMPLETE &&
666	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
667		log_status("accept_sec_context", client->cl_mech,
668		    gr->gr_major, gr->gr_minor);
669		client->cl_state = CLIENT_STALE;
670		return (TRUE);
671	}
672
673	gr->gr_handle.value = &client->cl_id;
674	gr->gr_handle.length = sizeof(client->cl_id);
675	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
676
677	/* Save client info. */
678	client->cl_mech = mech;
679	client->cl_qop = GSS_C_QOP_DEFAULT;
680	client->cl_seq = gc->gc_seq;
681	client->cl_win = gr->gr_win;
682	client->cl_done_callback = FALSE;
683
684	if (gr->gr_major == GSS_S_COMPLETE) {
685		gss_buffer_desc	export_name;
686
687		/*
688		 * Change client expiration time to be near when the
689		 * client creds expire (or 24 hours if we can't figure
690		 * that out).
691		 */
692		if (cred_lifetime == GSS_C_INDEFINITE)
693			cred_lifetime = time(0) + 24*60*60;
694
695		client->cl_expiration = time(0) + cred_lifetime;
696
697		/*
698		 * Fill in cred details in the rawcred structure.
699		 */
700		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
701		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
702		maj_stat = gss_export_name(&min_stat, client->cl_cname,
703		    &export_name);
704		if (maj_stat != GSS_S_COMPLETE) {
705			log_status("gss_export_name", client->cl_mech,
706			    maj_stat, min_stat);
707			return (FALSE);
708		}
709		client->cl_rawcred.client_principal =
710			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
711			    + export_name.length);
712		client->cl_rawcred.client_principal->len = export_name.length;
713		memcpy(client->cl_rawcred.client_principal->name,
714		    export_name.value, export_name.length);
715		gss_release_buffer(&min_stat, &export_name);
716		client->cl_rawcred.svc_principal =
717			client->cl_sname->sn_principal;
718		client->cl_rawcred.service = gc->gc_svc;
719
720		/*
721		 * Use gss_pname_to_uid to map to unix creds. For
722		 * kerberos5, this uses krb5_aname_to_localname.
723		 */
724		svc_rpc_gss_build_ucred(client, client->cl_cname);
725		gss_release_name(&min_stat, &client->cl_cname);
726
727#ifdef DEBUG
728		{
729			gss_buffer_desc mechname;
730
731			gss_oid_to_str(&min_stat, mech, &mechname);
732
733			log_debug("accepted context for %s with "
734			    "<mech %.*s, qop %d, svc %d>",
735			    client->cl_rawcred.client_principal->name,
736			    mechname.length, (char *)mechname.value,
737			    client->cl_qop, client->rawcred.service);
738
739			gss_release_buffer(&min_stat, &mechname);
740		}
741#endif /* DEBUG */
742	}
743	return (TRUE);
744}
745
746static bool_t
747svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
748	gss_qop_t *qop)
749{
750	struct opaque_auth	*oa;
751	gss_buffer_desc		 rpcbuf, checksum;
752	OM_uint32		 maj_stat, min_stat;
753	gss_qop_t		 qop_state;
754	int32_t			 rpchdr[128 / sizeof(int32_t)];
755	int32_t			*buf;
756
757	log_debug("in svc_rpc_gss_validate()");
758
759	memset(rpchdr, 0, sizeof(rpchdr));
760
761	/* Reconstruct RPC header for signing (from xdr_callmsg). */
762	buf = rpchdr;
763	IXDR_PUT_LONG(buf, msg->rm_xid);
764	IXDR_PUT_ENUM(buf, msg->rm_direction);
765	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
766	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
767	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
768	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
769	oa = &msg->rm_call.cb_cred;
770	IXDR_PUT_ENUM(buf, oa->oa_flavor);
771	IXDR_PUT_LONG(buf, oa->oa_length);
772	if (oa->oa_length) {
773		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
774		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
775	}
776	rpcbuf.value = rpchdr;
777	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
778
779	checksum.value = msg->rm_call.cb_verf.oa_base;
780	checksum.length = msg->rm_call.cb_verf.oa_length;
781
782	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
783				  &qop_state);
784
785	if (maj_stat != GSS_S_COMPLETE) {
786		log_status("gss_verify_mic", client->cl_mech,
787		    maj_stat, min_stat);
788		client->cl_state = CLIENT_STALE;
789		return (FALSE);
790	}
791	*qop = qop_state;
792	return (TRUE);
793}
794
795static bool_t
796svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
797    struct svc_req *rqst, u_int seq)
798{
799	gss_buffer_desc		signbuf;
800	OM_uint32		maj_stat, min_stat;
801	uint32_t		nseq;
802
803	log_debug("in svc_rpc_gss_nextverf()");
804
805	nseq = htonl(seq);
806	signbuf.value = &nseq;
807	signbuf.length = sizeof(nseq);
808
809	if (client->cl_verf.value)
810		gss_release_buffer(&min_stat, &client->cl_verf);
811
812	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
813	    &signbuf, &client->cl_verf);
814
815	if (maj_stat != GSS_S_COMPLETE) {
816		log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
817		client->cl_state = CLIENT_STALE;
818		return (FALSE);
819	}
820	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
821	rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
822	rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
823
824	return (TRUE);
825}
826
827static bool_t
828svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
829{
830	struct svc_rpc_gss_callback *scb;
831	rpc_gss_lock_t	lock;
832	void		*cookie;
833	bool_t		cb_res;
834	bool_t		result;
835
836	/*
837	 * See if we have a callback for this guy.
838	 */
839	result = TRUE;
840	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
841		if (scb->cb_callback.program == rqst->rq_prog
842		    && scb->cb_callback.version == rqst->rq_vers) {
843			/*
844			 * This one matches. Call the callback and see
845			 * if it wants to veto or something.
846			 */
847			lock.locked = FALSE;
848			lock.raw_cred = &client->cl_rawcred;
849			cb_res = scb->cb_callback.callback(rqst,
850			    client->cl_creds,
851			    client->cl_ctx,
852			    &lock,
853			    &cookie);
854
855			if (!cb_res) {
856				client->cl_state = CLIENT_STALE;
857				result = FALSE;
858				break;
859			}
860
861			/*
862			 * The callback accepted the connection - it
863			 * is responsible for freeing client->cl_creds
864			 * now.
865			 */
866			client->cl_creds = GSS_C_NO_CREDENTIAL;
867			client->cl_locked = lock.locked;
868			client->cl_cookie = cookie;
869			return (TRUE);
870		}
871	}
872
873	/*
874	 * Either no callback exists for this program/version or one
875	 * of the callbacks rejected the connection. We just need to
876	 * clean up the delegated client creds, if any.
877	 */
878	if (client->cl_creds) {
879		OM_uint32 min_ver;
880		gss_release_cred(&min_ver, &client->cl_creds);
881	}
882	return (result);
883}
884
885static bool_t
886svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
887{
888	u_int32_t offset;
889	int word, bit;
890
891	if (seq <= client->cl_seqlast) {
892		/*
893		 * The request sequence number is less than
894		 * the largest we have seen so far. If it is
895		 * outside the window or if we have seen a
896		 * request with this sequence before, silently
897		 * discard it.
898		 */
899		offset = client->cl_seqlast - seq;
900		if (offset >= SVC_RPC_GSS_SEQWINDOW)
901			return (FALSE);
902		word = offset / 32;
903		bit = offset % 32;
904		if (client->cl_seqmask[word] & (1 << bit))
905			return (FALSE);
906	}
907
908	return (TRUE);
909}
910
911static void
912svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
913{
914	int offset, i, word, bit;
915	uint32_t carry, newcarry;
916
917	if (seq > client->cl_seqlast) {
918		/*
919		 * This request has a sequence number greater
920		 * than any we have seen so far. Advance the
921		 * seq window and set bit zero of the window
922		 * (which corresponds to the new sequence
923		 * number)
924		 */
925		offset = seq - client->cl_seqlast;
926		while (offset > 32) {
927			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
928			     i > 0; i--) {
929				client->cl_seqmask[i] = client->cl_seqmask[i-1];
930			}
931			client->cl_seqmask[0] = 0;
932			offset -= 32;
933		}
934		carry = 0;
935		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
936			newcarry = client->cl_seqmask[i] >> (32 - offset);
937			client->cl_seqmask[i] =
938				(client->cl_seqmask[i] << offset) | carry;
939			carry = newcarry;
940		}
941		client->cl_seqmask[0] |= 1;
942		client->cl_seqlast = seq;
943	} else {
944		offset = client->cl_seqlast - seq;
945		word = offset / 32;
946		bit = offset % 32;
947		client->cl_seqmask[word] |= (1 << bit);
948	}
949
950}
951
952enum auth_stat
953svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
954
955{
956	OM_uint32		 min_stat;
957	XDR	 		 xdrs;
958	struct svc_rpc_gss_client *client;
959	struct rpc_gss_cred	 gc;
960	struct rpc_gss_init_res	 gr;
961	gss_qop_t		 qop;
962	int			 call_stat;
963	enum auth_stat		 result;
964
965	log_debug("in svc_rpc_gss()");
966
967	/* Garbage collect old clients. */
968	svc_rpc_gss_timeout_clients();
969
970	/* Initialize reply. */
971	rqst->rq_xprt->xp_verf = _null_auth;
972
973	/* Deserialize client credentials. */
974	if (rqst->rq_cred.oa_length <= 0)
975		return (AUTH_BADCRED);
976
977	memset(&gc, 0, sizeof(gc));
978
979	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
980	    rqst->rq_cred.oa_length, XDR_DECODE);
981
982	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
983		XDR_DESTROY(&xdrs);
984		return (AUTH_BADCRED);
985	}
986	XDR_DESTROY(&xdrs);
987
988	/* Check version. */
989	if (gc.gc_version != RPCSEC_GSS_VERSION) {
990		result = AUTH_BADCRED;
991		goto out;
992	}
993
994	/* Check the proc and find the client (or create it) */
995	if (gc.gc_proc == RPCSEC_GSS_INIT) {
996		if (gc.gc_handle.length != 0) {
997			result = AUTH_BADCRED;
998			goto out;
999		}
1000		client = svc_rpc_gss_create_client();
1001	} else {
1002		if (gc.gc_handle.length != sizeof(uint32_t)) {
1003			result = AUTH_BADCRED;
1004			goto out;
1005		}
1006		uint32_t *p = gc.gc_handle.value;
1007		client = svc_rpc_gss_find_client(*p);
1008		if (!client) {
1009			/*
1010			 * Can't find the client - we may have
1011			 * destroyed it - tell the other side to
1012			 * re-authenticate.
1013			 */
1014			result = RPCSEC_GSS_CREDPROBLEM;
1015			goto out;
1016		}
1017	}
1018	rqst->rq_clntcred = client;
1019
1020	/*
1021	 * The service and sequence number must be ignored for
1022	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1023	 */
1024	if (gc.gc_proc != RPCSEC_GSS_INIT
1025	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1026		/*
1027		 * Check for sequence number overflow.
1028		 */
1029		if (gc.gc_seq >= MAXSEQ) {
1030			result = RPCSEC_GSS_CTXPROBLEM;
1031			goto out;
1032		}
1033		client->cl_seq = gc.gc_seq;
1034
1035		/*
1036		 * Check for valid service.
1037		 */
1038		if (gc.gc_svc != rpc_gss_svc_none &&
1039		    gc.gc_svc != rpc_gss_svc_integrity &&
1040		    gc.gc_svc != rpc_gss_svc_privacy) {
1041			result = AUTH_BADCRED;
1042			goto out;
1043		}
1044	}
1045
1046	/* Handle RPCSEC_GSS control procedure. */
1047	switch (gc.gc_proc) {
1048
1049	case RPCSEC_GSS_INIT:
1050	case RPCSEC_GSS_CONTINUE_INIT:
1051		if (rqst->rq_proc != NULLPROC) {
1052			result = AUTH_REJECTEDCRED;
1053			break;
1054		}
1055
1056		memset(&gr, 0, sizeof(gr));
1057		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1058			result = AUTH_REJECTEDCRED;
1059			break;
1060		}
1061
1062		if (gr.gr_major == GSS_S_COMPLETE) {
1063			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1064				result = AUTH_REJECTEDCRED;
1065				break;
1066			}
1067		} else {
1068			rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1069			rqst->rq_xprt->xp_verf.oa_length = 0;
1070		}
1071
1072		call_stat = svc_sendreply(rqst->rq_xprt,
1073		    (xdrproc_t) xdr_rpc_gss_init_res,
1074		    (caddr_t) &gr);
1075
1076		gss_release_buffer(&min_stat, &gr.gr_token);
1077
1078		if (!call_stat) {
1079			result = AUTH_FAILED;
1080			break;
1081		}
1082
1083		if (gr.gr_major == GSS_S_COMPLETE)
1084			client->cl_state = CLIENT_ESTABLISHED;
1085
1086		result = RPCSEC_GSS_NODISPATCH;
1087		break;
1088
1089	case RPCSEC_GSS_DATA:
1090	case RPCSEC_GSS_DESTROY:
1091		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1092			result = RPCSEC_GSS_NODISPATCH;
1093			break;
1094		}
1095
1096		if (!svc_rpc_gss_validate(client, msg, &qop)) {
1097			result = RPCSEC_GSS_CREDPROBLEM;
1098			break;
1099		}
1100
1101		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1102			result = RPCSEC_GSS_CTXPROBLEM;
1103			break;
1104		}
1105
1106		svc_rpc_gss_update_seq(client, gc.gc_seq);
1107
1108		/*
1109		 * Change the SVCAUTH ops on the transport to point at
1110		 * our own code so that we can unwrap the arguments
1111		 * and wrap the result. The caller will re-set this on
1112		 * every request to point to a set of null wrap/unwrap
1113		 * methods.
1114		 */
1115		SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1116		SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1117
1118		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1119			/*
1120			 * We might be ready to do a callback to the server to
1121			 * see if it wants to accept/reject the connection.
1122			 */
1123			if (!client->cl_done_callback) {
1124				client->cl_done_callback = TRUE;
1125				client->cl_qop = qop;
1126				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1127					client->cl_rawcred.mechanism, qop);
1128				if (!svc_rpc_gss_callback(client, rqst)) {
1129					result = AUTH_REJECTEDCRED;
1130					break;
1131				}
1132			}
1133
1134			/*
1135			 * If the server has locked this client to a
1136			 * particular service+qop pair, enforce that
1137			 * restriction now.
1138			 */
1139			if (client->cl_locked) {
1140				if (client->cl_rawcred.service != gc.gc_svc) {
1141					result = AUTH_FAILED;
1142					break;
1143				} else if (client->cl_qop != qop) {
1144					result = AUTH_BADVERF;
1145					break;
1146				}
1147			}
1148
1149			/*
1150			 * If the qop changed, look up the new qop
1151			 * name for rawcred.
1152			 */
1153			if (client->cl_qop != qop) {
1154				client->cl_qop = qop;
1155				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1156					client->cl_rawcred.mechanism, qop);
1157			}
1158
1159			/*
1160			 * Make sure we use the right service value
1161			 * for unwrap/wrap.
1162			 */
1163			client->cl_rawcred.service = gc.gc_svc;
1164
1165			result = AUTH_OK;
1166		} else {
1167			if (rqst->rq_proc != NULLPROC) {
1168				result = AUTH_REJECTEDCRED;
1169				break;
1170			}
1171
1172			call_stat = svc_sendreply(rqst->rq_xprt,
1173			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1174
1175			if (!call_stat) {
1176				result = AUTH_FAILED;
1177				break;
1178			}
1179
1180			svc_rpc_gss_destroy_client(client);
1181
1182			result = RPCSEC_GSS_NODISPATCH;
1183			break;
1184		}
1185		break;
1186
1187	default:
1188		result = AUTH_BADCRED;
1189		break;
1190	}
1191out:
1192	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1193	return (result);
1194}
1195
1196bool_t
1197svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1198{
1199	struct svc_rpc_gss_client *client;
1200
1201	log_debug("in svc_rpc_gss_wrap()");
1202
1203	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1204	if (client->cl_state != CLIENT_ESTABLISHED
1205	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1206		return xdr_func(xdrs, xdr_ptr);
1207	}
1208	return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1209		client->cl_ctx, client->cl_qop,
1210		client->cl_rawcred.service, client->cl_seq));
1211}
1212
1213bool_t
1214svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1215{
1216	struct svc_rpc_gss_client *client;
1217
1218	log_debug("in svc_rpc_gss_unwrap()");
1219
1220	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1221	if (client->cl_state != CLIENT_ESTABLISHED
1222	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1223		return xdr_func(xdrs, xdr_ptr);
1224	}
1225	return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1226		client->cl_ctx, client->cl_qop,
1227		client->cl_rawcred.service, client->cl_seq));
1228}
1229