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/*-
27 SPDX-License-Identifier: BSD-3-Clause
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	uint32_t* maskp;
917
918	maskp = client->cl_seqmask;
919	if (seq > client->cl_seqlast) {
920		/*
921		 * This request has a sequence number greater
922		 * than any we have seen so far. Advance the
923		 * seq window and set bit zero of the window
924		 * (which corresponds to the new sequence
925		 * number)
926		 */
927		offset = seq - client->cl_seqlast;
928		while (offset >= 32) {
929			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
930			     i > 0; i--) {
931				maskp[i] = maskp[i-1];
932			}
933			maskp[0] = 0;
934			offset -= 32;
935		}
936		if (offset > 0) {
937			carry = 0;
938			for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
939				newcarry = maskp[i] >> (32 - offset);
940				maskp[i] = (maskp[i] << offset) | carry;
941				carry = newcarry;
942			}
943		}
944		maskp[0] |= 1;
945		client->cl_seqlast = seq;
946	} else {
947		offset = client->cl_seqlast - seq;
948		word = offset / 32;
949		bit = offset % 32;
950		maskp[word] |= (1 << bit);
951	}
952
953}
954
955enum auth_stat
956svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
957
958{
959	OM_uint32		 min_stat;
960	XDR	 		 xdrs;
961	struct svc_rpc_gss_client *client;
962	struct rpc_gss_cred	 gc;
963	struct rpc_gss_init_res	 gr;
964	gss_qop_t		 qop;
965	int			 call_stat;
966	enum auth_stat		 result;
967
968	log_debug("in svc_rpc_gss()");
969
970	/* Garbage collect old clients. */
971	svc_rpc_gss_timeout_clients();
972
973	/* Initialize reply. */
974	rqst->rq_xprt->xp_verf = _null_auth;
975
976	/* Deserialize client credentials. */
977	if (rqst->rq_cred.oa_length <= 0)
978		return (AUTH_BADCRED);
979
980	memset(&gc, 0, sizeof(gc));
981
982	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
983	    rqst->rq_cred.oa_length, XDR_DECODE);
984
985	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
986		XDR_DESTROY(&xdrs);
987		return (AUTH_BADCRED);
988	}
989	XDR_DESTROY(&xdrs);
990
991	/* Check version. */
992	if (gc.gc_version != RPCSEC_GSS_VERSION) {
993		result = AUTH_BADCRED;
994		goto out;
995	}
996
997	/* Check the proc and find the client (or create it) */
998	if (gc.gc_proc == RPCSEC_GSS_INIT) {
999		if (gc.gc_handle.length != 0) {
1000			result = AUTH_BADCRED;
1001			goto out;
1002		}
1003		client = svc_rpc_gss_create_client();
1004	} else {
1005		if (gc.gc_handle.length != sizeof(uint32_t)) {
1006			result = AUTH_BADCRED;
1007			goto out;
1008		}
1009		uint32_t *p = gc.gc_handle.value;
1010		client = svc_rpc_gss_find_client(*p);
1011		if (!client) {
1012			/*
1013			 * Can't find the client - we may have
1014			 * destroyed it - tell the other side to
1015			 * re-authenticate.
1016			 */
1017			result = RPCSEC_GSS_CREDPROBLEM;
1018			goto out;
1019		}
1020	}
1021	rqst->rq_clntcred = client;
1022
1023	/*
1024	 * The service and sequence number must be ignored for
1025	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1026	 */
1027	if (gc.gc_proc != RPCSEC_GSS_INIT
1028	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1029		/*
1030		 * Check for sequence number overflow.
1031		 */
1032		if (gc.gc_seq >= MAXSEQ) {
1033			result = RPCSEC_GSS_CTXPROBLEM;
1034			goto out;
1035		}
1036		client->cl_seq = gc.gc_seq;
1037
1038		/*
1039		 * Check for valid service.
1040		 */
1041		if (gc.gc_svc != rpc_gss_svc_none &&
1042		    gc.gc_svc != rpc_gss_svc_integrity &&
1043		    gc.gc_svc != rpc_gss_svc_privacy) {
1044			result = AUTH_BADCRED;
1045			goto out;
1046		}
1047	}
1048
1049	/* Handle RPCSEC_GSS control procedure. */
1050	switch (gc.gc_proc) {
1051
1052	case RPCSEC_GSS_INIT:
1053	case RPCSEC_GSS_CONTINUE_INIT:
1054		if (rqst->rq_proc != NULLPROC) {
1055			result = AUTH_REJECTEDCRED;
1056			break;
1057		}
1058
1059		memset(&gr, 0, sizeof(gr));
1060		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1061			result = AUTH_REJECTEDCRED;
1062			break;
1063		}
1064
1065		if (gr.gr_major == GSS_S_COMPLETE) {
1066			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1067				result = AUTH_REJECTEDCRED;
1068				break;
1069			}
1070		} else {
1071			rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1072			rqst->rq_xprt->xp_verf.oa_length = 0;
1073		}
1074
1075		call_stat = svc_sendreply(rqst->rq_xprt,
1076		    (xdrproc_t) xdr_rpc_gss_init_res,
1077		    (caddr_t) &gr);
1078
1079		gss_release_buffer(&min_stat, &gr.gr_token);
1080
1081		if (!call_stat) {
1082			result = AUTH_FAILED;
1083			break;
1084		}
1085
1086		if (gr.gr_major == GSS_S_COMPLETE)
1087			client->cl_state = CLIENT_ESTABLISHED;
1088
1089		result = RPCSEC_GSS_NODISPATCH;
1090		break;
1091
1092	case RPCSEC_GSS_DATA:
1093	case RPCSEC_GSS_DESTROY:
1094		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1095			result = RPCSEC_GSS_NODISPATCH;
1096			break;
1097		}
1098
1099		if (!svc_rpc_gss_validate(client, msg, &qop)) {
1100			result = RPCSEC_GSS_CREDPROBLEM;
1101			break;
1102		}
1103
1104		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1105			result = RPCSEC_GSS_CTXPROBLEM;
1106			break;
1107		}
1108
1109		svc_rpc_gss_update_seq(client, gc.gc_seq);
1110
1111		/*
1112		 * Change the SVCAUTH ops on the transport to point at
1113		 * our own code so that we can unwrap the arguments
1114		 * and wrap the result. The caller will re-set this on
1115		 * every request to point to a set of null wrap/unwrap
1116		 * methods.
1117		 */
1118		SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1119		SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1120
1121		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1122			/*
1123			 * We might be ready to do a callback to the server to
1124			 * see if it wants to accept/reject the connection.
1125			 */
1126			if (!client->cl_done_callback) {
1127				client->cl_done_callback = TRUE;
1128				client->cl_qop = qop;
1129				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1130					client->cl_rawcred.mechanism, qop);
1131				if (!svc_rpc_gss_callback(client, rqst)) {
1132					result = AUTH_REJECTEDCRED;
1133					break;
1134				}
1135			}
1136
1137			/*
1138			 * If the server has locked this client to a
1139			 * particular service+qop pair, enforce that
1140			 * restriction now.
1141			 */
1142			if (client->cl_locked) {
1143				if (client->cl_rawcred.service != gc.gc_svc) {
1144					result = AUTH_FAILED;
1145					break;
1146				} else if (client->cl_qop != qop) {
1147					result = AUTH_BADVERF;
1148					break;
1149				}
1150			}
1151
1152			/*
1153			 * If the qop changed, look up the new qop
1154			 * name for rawcred.
1155			 */
1156			if (client->cl_qop != qop) {
1157				client->cl_qop = qop;
1158				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1159					client->cl_rawcred.mechanism, qop);
1160			}
1161
1162			/*
1163			 * Make sure we use the right service value
1164			 * for unwrap/wrap.
1165			 */
1166			client->cl_rawcred.service = gc.gc_svc;
1167
1168			result = AUTH_OK;
1169		} else {
1170			if (rqst->rq_proc != NULLPROC) {
1171				result = AUTH_REJECTEDCRED;
1172				break;
1173			}
1174
1175			call_stat = svc_sendreply(rqst->rq_xprt,
1176			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1177
1178			if (!call_stat) {
1179				result = AUTH_FAILED;
1180				break;
1181			}
1182
1183			svc_rpc_gss_destroy_client(client);
1184
1185			result = RPCSEC_GSS_NODISPATCH;
1186			break;
1187		}
1188		break;
1189
1190	default:
1191		result = AUTH_BADCRED;
1192		break;
1193	}
1194out:
1195	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1196	return (result);
1197}
1198
1199bool_t
1200svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1201{
1202	struct svc_rpc_gss_client *client;
1203
1204	log_debug("in svc_rpc_gss_wrap()");
1205
1206	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1207	if (client->cl_state != CLIENT_ESTABLISHED
1208	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1209		return xdr_func(xdrs, xdr_ptr);
1210	}
1211	return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1212		client->cl_ctx, client->cl_qop,
1213		client->cl_rawcred.service, client->cl_seq));
1214}
1215
1216bool_t
1217svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1218{
1219	struct svc_rpc_gss_client *client;
1220
1221	log_debug("in svc_rpc_gss_unwrap()");
1222
1223	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1224	if (client->cl_state != CLIENT_ESTABLISHED
1225	    || client->cl_rawcred.service == rpc_gss_svc_none) {
1226		return xdr_func(xdrs, xdr_ptr);
1227	}
1228	return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1229		client->cl_ctx, client->cl_qop,
1230		client->cl_rawcred.service, client->cl_seq));
1231}
1232