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
143struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
144struct 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				if (gr->gr_major == GSS_S_COMPLETE
635				    || gr->gr_major == GSS_S_CONTINUE_NEEDED) {
636					client->cl_sname = sname;
637					break;
638				}
639				client->cl_sname = sname;
640				break;
641			}
642		}
643		if (!sname) {
644			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
645			    (char *) &recv_tok);
646			return (FALSE);
647		}
648	} else {
649		gr->gr_major = gss_accept_sec_context(
650			&gr->gr_minor,
651			&client->cl_ctx,
652			client->cl_sname->sn_cred,
653			&recv_tok,
654			GSS_C_NO_CHANNEL_BINDINGS,
655			&client->cl_cname,
656			&mech,
657			&gr->gr_token,
658			&ret_flags,
659			&cred_lifetime,
660			NULL);
661	}
662
663	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
664
665	/*
666	 * If we get an error from gss_accept_sec_context, send the
667	 * reply anyway so that the client gets a chance to see what
668	 * is wrong.
669	 */
670	if (gr->gr_major != GSS_S_COMPLETE &&
671	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
672		log_status("accept_sec_context", client->cl_mech,
673		    gr->gr_major, gr->gr_minor);
674		client->cl_state = CLIENT_STALE;
675		return (TRUE);
676	}
677
678	gr->gr_handle.value = &client->cl_id;
679	gr->gr_handle.length = sizeof(client->cl_id);
680	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
681
682	/* Save client info. */
683	client->cl_mech = mech;
684	client->cl_qop = GSS_C_QOP_DEFAULT;
685	client->cl_seq = gc->gc_seq;
686	client->cl_win = gr->gr_win;
687	client->cl_done_callback = FALSE;
688
689	if (gr->gr_major == GSS_S_COMPLETE) {
690		gss_buffer_desc	export_name;
691
692		/*
693		 * Change client expiration time to be near when the
694		 * client creds expire (or 24 hours if we can't figure
695		 * that out).
696		 */
697		if (cred_lifetime == GSS_C_INDEFINITE)
698			cred_lifetime = time(0) + 24*60*60;
699
700		client->cl_expiration = time(0) + cred_lifetime;
701
702		/*
703		 * Fill in cred details in the rawcred structure.
704		 */
705		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
706		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
707		maj_stat = gss_export_name(&min_stat, client->cl_cname,
708		    &export_name);
709		if (maj_stat != GSS_S_COMPLETE) {
710			log_status("gss_export_name", client->cl_mech,
711			    maj_stat, min_stat);
712			return (FALSE);
713		}
714		client->cl_rawcred.client_principal =
715			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
716			    + export_name.length);
717		client->cl_rawcred.client_principal->len = export_name.length;
718		memcpy(client->cl_rawcred.client_principal->name,
719		    export_name.value, export_name.length);
720		gss_release_buffer(&min_stat, &export_name);
721		client->cl_rawcred.svc_principal =
722			client->cl_sname->sn_principal;
723		client->cl_rawcred.service = gc->gc_svc;
724
725		/*
726		 * Use gss_pname_to_uid to map to unix creds. For
727		 * kerberos5, this uses krb5_aname_to_localname.
728		 */
729		svc_rpc_gss_build_ucred(client, client->cl_cname);
730		gss_release_name(&min_stat, &client->cl_cname);
731
732#ifdef DEBUG
733		{
734			gss_buffer_desc mechname;
735
736			gss_oid_to_str(&min_stat, mech, &mechname);
737
738			log_debug("accepted context for %s with "
739			    "<mech %.*s, qop %d, svc %d>",
740			    client->cl_rawcred.client_principal->name,
741			    mechname.length, (char *)mechname.value,
742			    client->cl_qop, client->rawcred.service);
743
744			gss_release_buffer(&min_stat, &mechname);
745		}
746#endif /* DEBUG */
747	}
748	return (TRUE);
749}
750
751static bool_t
752svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
753	gss_qop_t *qop)
754{
755	struct opaque_auth	*oa;
756	gss_buffer_desc		 rpcbuf, checksum;
757	OM_uint32		 maj_stat, min_stat;
758	gss_qop_t		 qop_state;
759	int32_t			 rpchdr[128 / sizeof(int32_t)];
760	int32_t			*buf;
761
762	log_debug("in svc_rpc_gss_validate()");
763
764	memset(rpchdr, 0, sizeof(rpchdr));
765
766	/* Reconstruct RPC header for signing (from xdr_callmsg). */
767	buf = rpchdr;
768	IXDR_PUT_LONG(buf, msg->rm_xid);
769	IXDR_PUT_ENUM(buf, msg->rm_direction);
770	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
771	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
772	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
773	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
774	oa = &msg->rm_call.cb_cred;
775	IXDR_PUT_ENUM(buf, oa->oa_flavor);
776	IXDR_PUT_LONG(buf, oa->oa_length);
777	if (oa->oa_length) {
778		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
779		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
780	}
781	rpcbuf.value = rpchdr;
782	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
783
784	checksum.value = msg->rm_call.cb_verf.oa_base;
785	checksum.length = msg->rm_call.cb_verf.oa_length;
786
787	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
788				  &qop_state);
789
790	if (maj_stat != GSS_S_COMPLETE) {
791		log_status("gss_verify_mic", client->cl_mech,
792		    maj_stat, min_stat);
793		client->cl_state = CLIENT_STALE;
794		return (FALSE);
795	}
796	*qop = qop_state;
797	return (TRUE);
798}
799
800static bool_t
801svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
802    struct svc_req *rqst, u_int seq)
803{
804	gss_buffer_desc		signbuf;
805	OM_uint32		maj_stat, min_stat;
806	uint32_t		nseq;
807
808	log_debug("in svc_rpc_gss_nextverf()");
809
810	nseq = htonl(seq);
811	signbuf.value = &nseq;
812	signbuf.length = sizeof(nseq);
813
814	if (client->cl_verf.value)
815		gss_release_buffer(&min_stat, &client->cl_verf);
816
817	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
818	    &signbuf, &client->cl_verf);
819
820	if (maj_stat != GSS_S_COMPLETE) {
821		log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
822		client->cl_state = CLIENT_STALE;
823		return (FALSE);
824	}
825	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
826	rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
827	rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
828
829	return (TRUE);
830}
831
832static bool_t
833svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
834{
835	struct svc_rpc_gss_callback *scb;
836	rpc_gss_lock_t	lock;
837	void		*cookie;
838	bool_t		cb_res;
839	bool_t		result;
840
841	/*
842	 * See if we have a callback for this guy.
843	 */
844	result = TRUE;
845	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
846		if (scb->cb_callback.program == rqst->rq_prog
847		    && scb->cb_callback.version == rqst->rq_vers) {
848			/*
849			 * This one matches. Call the callback and see
850			 * if it wants to veto or something.
851			 */
852			lock.locked = FALSE;
853			lock.raw_cred = &client->cl_rawcred;
854			cb_res = scb->cb_callback.callback(rqst,
855			    client->cl_creds,
856			    client->cl_ctx,
857			    &lock,
858			    &cookie);
859
860			if (!cb_res) {
861				client->cl_state = CLIENT_STALE;
862				result = FALSE;
863				break;
864			}
865
866			/*
867			 * The callback accepted the connection - it
868			 * is responsible for freeing client->cl_creds
869			 * now.
870			 */
871			client->cl_creds = GSS_C_NO_CREDENTIAL;
872			client->cl_locked = lock.locked;
873			client->cl_cookie = cookie;
874			return (TRUE);
875		}
876	}
877
878	/*
879	 * Either no callback exists for this program/version or one
880	 * of the callbacks rejected the connection. We just need to
881	 * clean up the delegated client creds, if any.
882	 */
883	if (client->cl_creds) {
884		OM_uint32 min_ver;
885		gss_release_cred(&min_ver, &client->cl_creds);
886	}
887	return (result);
888}
889
890static bool_t
891svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
892{
893	u_int32_t offset;
894	int word, bit;
895
896	if (seq <= client->cl_seqlast) {
897		/*
898		 * The request sequence number is less than
899		 * the largest we have seen so far. If it is
900		 * outside the window or if we have seen a
901		 * request with this sequence before, silently
902		 * discard it.
903		 */
904		offset = client->cl_seqlast - seq;
905		if (offset >= SVC_RPC_GSS_SEQWINDOW)
906			return (FALSE);
907		word = offset / 32;
908		bit = offset % 32;
909		if (client->cl_seqmask[word] & (1 << bit))
910			return (FALSE);
911	}
912
913	return (TRUE);
914}
915
916static void
917svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
918{
919	int offset, i, word, bit;
920	uint32_t carry, newcarry;
921
922	if (seq > client->cl_seqlast) {
923		/*
924		 * This request has a sequence number greater
925		 * than any we have seen so far. Advance the
926		 * seq window and set bit zero of the window
927		 * (which corresponds to the new sequence
928		 * number)
929		 */
930		offset = seq - client->cl_seqlast;
931		while (offset > 32) {
932			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
933			     i > 0; i--) {
934				client->cl_seqmask[i] = client->cl_seqmask[i-1];
935			}
936			client->cl_seqmask[0] = 0;
937			offset -= 32;
938		}
939		carry = 0;
940		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
941			newcarry = client->cl_seqmask[i] >> (32 - offset);
942			client->cl_seqmask[i] =
943				(client->cl_seqmask[i] << offset) | carry;
944			carry = newcarry;
945		}
946		client->cl_seqmask[0] |= 1;
947		client->cl_seqlast = seq;
948	} else {
949		offset = client->cl_seqlast - seq;
950		word = offset / 32;
951		bit = offset % 32;
952		client->cl_seqmask[word] |= (1 << bit);
953	}
954
955}
956
957enum auth_stat
958svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
959
960{
961	OM_uint32		 min_stat;
962	XDR	 		 xdrs;
963	struct svc_rpc_gss_client *client;
964	struct rpc_gss_cred	 gc;
965	struct rpc_gss_init_res	 gr;
966	gss_qop_t		 qop;
967	int			 call_stat;
968	enum auth_stat		 result;
969
970	log_debug("in svc_rpc_gss()");
971
972	/* Garbage collect old clients. */
973	svc_rpc_gss_timeout_clients();
974
975	/* Initialize reply. */
976	rqst->rq_xprt->xp_verf = _null_auth;
977
978	/* Deserialize client credentials. */
979	if (rqst->rq_cred.oa_length <= 0)
980		return (AUTH_BADCRED);
981
982	memset(&gc, 0, sizeof(gc));
983
984	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
985	    rqst->rq_cred.oa_length, XDR_DECODE);
986
987	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
988		XDR_DESTROY(&xdrs);
989		return (AUTH_BADCRED);
990	}
991	XDR_DESTROY(&xdrs);
992
993	/* Check version. */
994	if (gc.gc_version != RPCSEC_GSS_VERSION) {
995		result = AUTH_BADCRED;
996		goto out;
997	}
998
999	/* Check the proc and find the client (or create it) */
1000	if (gc.gc_proc == RPCSEC_GSS_INIT) {
1001		if (gc.gc_handle.length != 0) {
1002			result = AUTH_BADCRED;
1003			goto out;
1004		}
1005		client = svc_rpc_gss_create_client();
1006	} else {
1007		if (gc.gc_handle.length != sizeof(uint32_t)) {
1008			result = AUTH_BADCRED;
1009			goto out;
1010		}
1011		uint32_t *p = gc.gc_handle.value;
1012		client = svc_rpc_gss_find_client(*p);
1013		if (!client) {
1014			/*
1015			 * Can't find the client - we may have
1016			 * destroyed it - tell the other side to
1017			 * re-authenticate.
1018			 */
1019			result = RPCSEC_GSS_CREDPROBLEM;
1020			goto out;
1021		}
1022	}
1023	rqst->rq_clntcred = client;
1024
1025	/*
1026	 * The service and sequence number must be ignored for
1027	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1028	 */
1029	if (gc.gc_proc != RPCSEC_GSS_INIT
1030	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1031		/*
1032		 * Check for sequence number overflow.
1033		 */
1034		if (gc.gc_seq >= MAXSEQ) {
1035			result = RPCSEC_GSS_CTXPROBLEM;
1036			goto out;
1037		}
1038		client->cl_seq = gc.gc_seq;
1039
1040		/*
1041		 * Check for valid service.
1042		 */
1043		if (gc.gc_svc != rpc_gss_svc_none &&
1044		    gc.gc_svc != rpc_gss_svc_integrity &&
1045		    gc.gc_svc != rpc_gss_svc_privacy) {
1046			result = AUTH_BADCRED;
1047			goto out;
1048		}
1049	}
1050
1051	/* Handle RPCSEC_GSS control procedure. */
1052	switch (gc.gc_proc) {
1053
1054	case RPCSEC_GSS_INIT:
1055	case RPCSEC_GSS_CONTINUE_INIT:
1056		if (rqst->rq_proc != NULLPROC) {
1057			result = AUTH_REJECTEDCRED;
1058			break;
1059		}
1060
1061		memset(&gr, 0, sizeof(gr));
1062		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1063			result = AUTH_REJECTEDCRED;
1064			break;
1065		}
1066
1067		if (gr.gr_major == GSS_S_COMPLETE) {
1068			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1069				result = AUTH_REJECTEDCRED;
1070				break;
1071			}
1072		} else {
1073			rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1074			rqst->rq_xprt->xp_verf.oa_length = 0;
1075		}
1076
1077		call_stat = svc_sendreply(rqst->rq_xprt,
1078		    (xdrproc_t) xdr_rpc_gss_init_res,
1079		    (caddr_t) &gr);
1080
1081		gss_release_buffer(&min_stat, &gr.gr_token);
1082
1083		if (!call_stat) {
1084			result = AUTH_FAILED;
1085			break;
1086		}
1087
1088		if (gr.gr_major == GSS_S_COMPLETE)
1089			client->cl_state = CLIENT_ESTABLISHED;
1090
1091		result = RPCSEC_GSS_NODISPATCH;
1092		break;
1093
1094	case RPCSEC_GSS_DATA:
1095	case RPCSEC_GSS_DESTROY:
1096		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1097			result = RPCSEC_GSS_NODISPATCH;
1098			break;
1099		}
1100
1101		if (!svc_rpc_gss_validate(client, msg, &qop)) {
1102			result = RPCSEC_GSS_CREDPROBLEM;
1103			break;
1104		}
1105
1106		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1107			result = RPCSEC_GSS_CTXPROBLEM;
1108			break;
1109		}
1110
1111		svc_rpc_gss_update_seq(client, gc.gc_seq);
1112
1113		/*
1114		 * Change the SVCAUTH ops on the transport to point at
1115		 * our own code so that we can unwrap the arguments
1116		 * and wrap the result. The caller will re-set this on
1117		 * every request to point to a set of null wrap/unwrap
1118		 * methods.
1119		 */
1120		SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1121		SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1122
1123		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1124			/*
1125			 * We might be ready to do a callback to the server to
1126			 * see if it wants to accept/reject the connection.
1127			 */
1128			if (!client->cl_done_callback) {
1129				client->cl_done_callback = TRUE;
1130				client->cl_qop = qop;
1131				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1132					client->cl_rawcred.mechanism, qop);
1133				if (!svc_rpc_gss_callback(client, rqst)) {
1134					result = AUTH_REJECTEDCRED;
1135					break;
1136				}
1137			}
1138
1139			/*
1140			 * If the server has locked this client to a
1141			 * particular service+qop pair, enforce that
1142			 * restriction now.
1143			 */
1144			if (client->cl_locked) {
1145				if (client->cl_rawcred.service != gc.gc_svc) {
1146					result = AUTH_FAILED;
1147					break;
1148				} else if (client->cl_qop != qop) {
1149					result = AUTH_BADVERF;
1150					break;
1151				}
1152			}
1153
1154			/*
1155			 * If the qop changed, look up the new qop
1156			 * name for rawcred.
1157			 */
1158			if (client->cl_qop != qop) {
1159				client->cl_qop = qop;
1160				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1161					client->cl_rawcred.mechanism, qop);
1162			}
1163
1164			/*
1165			 * Make sure we use the right service value
1166			 * for unwrap/wrap.
1167			 */
1168			client->cl_rawcred.service = gc.gc_svc;
1169
1170			result = AUTH_OK;
1171		} else {
1172			if (rqst->rq_proc != NULLPROC) {
1173				result = AUTH_REJECTEDCRED;
1174				break;
1175			}
1176
1177			call_stat = svc_sendreply(rqst->rq_xprt,
1178			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1179
1180			if (!call_stat) {
1181				result = AUTH_FAILED;
1182				break;
1183			}
1184
1185			svc_rpc_gss_destroy_client(client);
1186
1187			result = RPCSEC_GSS_NODISPATCH;
1188			break;
1189		}
1190		break;
1191
1192	default:
1193		result = AUTH_BADCRED;
1194		break;
1195	}
1196out:
1197	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1198	return (result);
1199}
1200
1201bool_t
1202svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1203{
1204	struct svc_rpc_gss_client *client;
1205
1206	log_debug("in svc_rpc_gss_wrap()");
1207
1208	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1209	if (client->cl_state != CLIENT_ESTABLISHED
1210	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1211		return xdr_func(xdrs, xdr_ptr);
1212	}
1213	return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1214		client->cl_ctx, client->cl_qop,
1215		client->cl_rawcred.service, client->cl_seq));
1216}
1217
1218bool_t
1219svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1220{
1221	struct svc_rpc_gss_client *client;
1222
1223	log_debug("in svc_rpc_gss_unwrap()");
1224
1225	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1226	if (client->cl_state != CLIENT_ESTABLISHED
1227	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1228		return xdr_func(xdrs, xdr_ptr);
1229	}
1230	return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1231		client->cl_ctx, client->cl_qop,
1232		client->cl_rawcred.service, client->cl_seq));
1233}
1234