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