1133123Sjmg/*-
2133123Sjmg * Copyright (c) 2008 Doug Rabson
3133123Sjmg * All rights reserved.
4133123Sjmg *
5133123Sjmg * Redistribution and use in source and binary forms, with or without
6133123Sjmg * modification, are permitted provided that the following conditions
7133123Sjmg * are met:
8133123Sjmg * 1. Redistributions of source code must retain the above copyright
9133123Sjmg *    notice, this list of conditions and the following disclaimer.
10133123Sjmg * 2. Redistributions in binary form must reproduce the above copyright
11133123Sjmg *    notice, this list of conditions and the following disclaimer in the
12133123Sjmg *    documentation and/or other materials provided with the distribution.
13133123Sjmg *
14133123Sjmg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15133123Sjmg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16133123Sjmg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17133123Sjmg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18133123Sjmg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19133123Sjmg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20133123Sjmg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21133123Sjmg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22133123Sjmg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23133123Sjmg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24133123Sjmg * SUCH DAMAGE.
25133123Sjmg *
26133123Sjmg *	$FreeBSD$
27133123Sjmg */
28133123Sjmg
29133123Sjmg#include <ctype.h>
30133123Sjmg#include <stdio.h>
31133123Sjmg#include <string.h>
32133123Sjmg#include <stdlib.h>
33133123Sjmg#include <errno.h>
34143864Sjmg#include <sys/queue.h>
35133123Sjmg#include <rpc/rpc.h>
36133123Sjmg#include <rpc/rpcsec_gss.h>
37133123Sjmg
38133123Sjmg#include "rpcsec_gss_int.h"
39133123Sjmg
40133123Sjmg#ifndef _PATH_GSS_MECH
41133123Sjmg#define _PATH_GSS_MECH	"/etc/gss/mech"
42133123Sjmg#endif
43133123Sjmg
44133123Sjmg#ifndef _PATH_GSS_QOP
45133123Sjmg#define _PATH_GSS_QOP	"/etc/gss/qop"
46133123Sjmg#endif
47133123Sjmg
48133123Sjmgstruct mech_info {
49133123Sjmg	SLIST_ENTRY(mech_info) link;
50133123Sjmg	char		*name;
51133123Sjmg	gss_OID_desc	oid;
52133123Sjmg	const char	**qops;
53133123Sjmg	char		*lib;
54133123Sjmg	char		*kobj;
55133123Sjmg};
56133123SjmgSLIST_HEAD(mech_info_list, mech_info);
57133123Sjmg
58133123Sjmgstatic struct mech_info_list mechs = SLIST_HEAD_INITIALIZER(mechs);
59133123Sjmgstatic const char **mech_names;
60133123Sjmg
61133123Sjmgstruct qop_info {
62133123Sjmg	SLIST_ENTRY(qop_info) link;
63133123Sjmg	char		*name;
64143864Sjmg	char*		mech;
65133123Sjmg	u_int		qop;
66133123Sjmg};
67133123SjmgSLIST_HEAD(qop_info_list, qop_info);
68133123Sjmg
69133123Sjmgstatic struct qop_info_list qops = SLIST_HEAD_INITIALIZER(qops);
70133123Sjmg
71228975Suqsstatic int
72133123Sjmg_rpc_gss_string_to_oid(const char* s, gss_OID oid)
73228975Suqs{
74133123Sjmg	int			number_count, i, j;
75133123Sjmg	int			byte_count;
76133123Sjmg	const char		*p, *q;
77133123Sjmg	char			*res;
78133123Sjmg
79133123Sjmg	/*
80133123Sjmg	 * First figure out how many numbers in the oid, then
81133123Sjmg	 * calculate the compiled oid size.
82133123Sjmg	 */
83133123Sjmg	number_count = 0;
84133123Sjmg	for (p = s; p; p = q) {
85133123Sjmg		q = strchr(p, '.');
86133123Sjmg		if (q) q = q + 1;
87133123Sjmg		number_count++;
88133123Sjmg	}
89133123Sjmg
90228975Suqs	/*
91133123Sjmg	 * The first two numbers are in the first byte and each
92133123Sjmg	 * subsequent number is encoded in a variable byte sequence.
93133123Sjmg	 */
94133123Sjmg	if (number_count < 2)
95133123Sjmg		return (EINVAL);
96133123Sjmg
97133123Sjmg	/*
98133123Sjmg	 * We do this in two passes. The first pass, we just figure
99133123Sjmg	 * out the size. Second time around, we actually encode the
100133123Sjmg	 * number.
101133123Sjmg	 */
102133123Sjmg	res = 0;
103133123Sjmg	for (i = 0; i < 2; i++) {
104133123Sjmg		byte_count = 0;
105133123Sjmg		for (p = s, j = 0; p; p = q, j++) {
106133123Sjmg			u_int number = 0;
107133123Sjmg
108133123Sjmg			/*
109133123Sjmg			 * Find the end of this number.
110133123Sjmg			 */
111133123Sjmg			q = strchr(p, '.');
112133123Sjmg			if (q) q = q + 1;
113133123Sjmg
114133123Sjmg			/*
115133123Sjmg			 * Read the number of of the string. Don't
116133123Sjmg			 * bother with anything except base ten.
117133123Sjmg			 */
118133123Sjmg			while (*p && *p != '.') {
119133123Sjmg				number = 10 * number + (*p - '0');
120133123Sjmg				p++;
121133123Sjmg			}
122133123Sjmg
123133123Sjmg			/*
124133123Sjmg			 * Encode the number. The first two numbers
125133123Sjmg			 * are packed into the first byte. Subsequent
126133123Sjmg			 * numbers are encoded in bytes seven bits at
127133123Sjmg			 * a time with the last byte having the high
128133123Sjmg			 * bit set.
129133123Sjmg			 */
130133123Sjmg			if (j == 0) {
131133123Sjmg				if (res)
132133123Sjmg					*res = number * 40;
133133123Sjmg			} else if (j == 1) {
134133123Sjmg				if (res) {
135133123Sjmg					*res += number;
136133123Sjmg					res++;
137133123Sjmg				}
138133123Sjmg				byte_count++;
139133123Sjmg			} else if (j >= 2) {
140133123Sjmg				/*
141133123Sjmg				 * The number is encoded in seven bit chunks.
142133123Sjmg				 */
143				u_int t;
144				int bytes;
145
146				bytes = 0;
147				for (t = number; t; t >>= 7)
148					bytes++;
149				if (bytes == 0) bytes = 1;
150				while (bytes) {
151					if (res) {
152						int bit = 7*(bytes-1);
153
154						*res = (number >> bit) & 0x7f;
155						if (bytes != 1)
156							*res |= 0x80;
157						res++;
158					}
159					byte_count++;
160					bytes--;
161				}
162			}
163		}
164		if (!res) {
165			res = malloc(byte_count);
166			if (!res)
167				return (ENOMEM);
168			oid->length = byte_count;
169			oid->elements = res;
170		}
171	}
172
173	return (0);
174}
175
176static void
177_rpc_gss_load_mech(void)
178{
179	FILE		*fp;
180	char		buf[256];
181	char		*p;
182	char		*name, *oid, *lib, *kobj;
183	struct mech_info *info;
184	int		count;
185	const char	**pp;
186
187	if (SLIST_FIRST(&mechs))
188		return;
189
190	fp = fopen(_PATH_GSS_MECH, "r");
191	if (!fp)
192		return;
193
194	count = 0;
195	while (fgets(buf, sizeof(buf), fp)) {
196		if (*buf == '#')
197			continue;
198		p = buf;
199		name = strsep(&p, "\t\n ");
200		if (p) while (isspace(*p)) p++;
201		oid = strsep(&p, "\t\n ");
202		if (p) while (isspace(*p)) p++;
203		lib = strsep(&p, "\t\n ");
204		if (p) while (isspace(*p)) p++;
205		kobj = strsep(&p, "\t\n ");
206		if (!name || !oid || !lib || !kobj)
207			continue;
208
209		info = malloc(sizeof(struct mech_info));
210		if (!info)
211			break;
212		if (_rpc_gss_string_to_oid(oid, &info->oid)) {
213			free(info);
214			continue;
215		}
216		info->name = strdup(name);
217		info->qops = NULL;
218		info->lib = strdup(lib);
219		info->kobj = strdup(kobj);
220		SLIST_INSERT_HEAD(&mechs, info, link);
221		count++;
222	}
223	fclose(fp);
224
225	mech_names = malloc((count + 1) * sizeof(char*));
226	pp = mech_names;
227	SLIST_FOREACH(info, &mechs, link) {
228		*pp++ = info->name;
229	}
230	*pp = NULL;
231}
232
233static void
234_rpc_gss_load_qop(void)
235{
236	FILE		*fp;
237	char		buf[256];
238	char		*p;
239	char		*name, *num, *mech;
240	struct mech_info *minfo;
241	struct qop_info *info;
242	int		count;
243	const char	**mech_qops;
244	const char	**pp;
245
246	if (SLIST_FIRST(&qops))
247		return;
248
249	fp = fopen(_PATH_GSS_QOP, "r");
250	if (!fp)
251		return;
252
253	while (fgets(buf, sizeof(buf), fp)) {
254		if (*buf == '#')
255			continue;
256		p = buf;
257		name = strsep(&p, "\t\n ");
258		if (p) while (isspace(*p)) p++;
259		num = strsep(&p, "\t\n ");
260		if (p) while (isspace(*p)) p++;
261		mech = strsep(&p, "\t\n ");
262		if (!name || !num || !mech)
263			continue;
264
265		info = malloc(sizeof(struct qop_info));
266		if (!info)
267			break;
268		info->name = strdup(name);
269		info->qop = strtoul(name, 0, 0);
270		info->mech = strdup(mech);
271		SLIST_INSERT_HEAD(&qops, info, link);
272	}
273	fclose(fp);
274
275	/*
276	 * Compile lists of qops for each mechanism.
277	 */
278	SLIST_FOREACH(minfo, &mechs, link) {
279		count = 0;
280		SLIST_FOREACH(info, &qops, link) {
281			if (strcmp(info->mech, minfo->name) == 0)
282				count++;
283		}
284		mech_qops = malloc((count + 1) * sizeof(char*));
285		pp = mech_qops;
286		SLIST_FOREACH(info, &qops, link) {
287			if (strcmp(info->mech, minfo->name) == 0)
288				*pp++ = info->name;
289		}
290		*pp = NULL;
291		minfo->qops = mech_qops;
292	}
293}
294
295bool_t
296rpc_gss_mech_to_oid(const char *mech, gss_OID *oid_ret)
297{
298	struct mech_info *info;
299
300	_rpc_gss_load_mech();
301	SLIST_FOREACH(info, &mechs, link) {
302		if (!strcmp(info->name, mech)) {
303			*oid_ret = &info->oid;
304			return (TRUE);
305		}
306	}
307	_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT);
308	return (FALSE);
309}
310
311bool_t
312rpc_gss_oid_to_mech(gss_OID oid, const char **mech_ret)
313{
314	struct mech_info *info;
315
316	_rpc_gss_load_mech();
317	SLIST_FOREACH(info, &mechs, link) {
318		if (oid->length == info->oid.length
319		    && !memcmp(oid->elements, info->oid.elements,
320			oid->length)) {
321			*mech_ret = info->name;
322			return (TRUE);
323		}
324	}
325	_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT);
326	return (FALSE);
327}
328
329bool_t
330rpc_gss_qop_to_num(const char *qop, const char *mech, u_int *num_ret)
331{
332	struct qop_info *info;
333
334	_rpc_gss_load_qop();
335	SLIST_FOREACH(info, &qops, link) {
336		if (strcmp(info->name, qop) == 0
337		    && strcmp(info->mech, mech) == 0) {
338			*num_ret = info->qop;
339			return (TRUE);
340		}
341	}
342	_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT);
343	return (FALSE);
344}
345
346const char *
347_rpc_gss_num_to_qop(const char *mech, u_int num)
348{
349	struct qop_info *info;
350
351	if (num == GSS_C_QOP_DEFAULT)
352		return "default";
353
354	_rpc_gss_load_qop();
355	SLIST_FOREACH(info, &qops, link) {
356		if (info->qop == num && strcmp(info->mech, mech) == 0) {
357			return (info->name);
358		}
359	}
360	return (NULL);
361}
362
363const char **
364rpc_gss_get_mechanisms(void)
365{
366
367	_rpc_gss_load_mech();
368	return (mech_names);
369}
370
371const char **
372rpc_gss_get_mech_info(const char *mech, rpc_gss_service_t *service)
373{
374	struct mech_info *info;
375
376	_rpc_gss_load_mech();
377	_rpc_gss_load_qop();
378	SLIST_FOREACH(info, &mechs, link) {
379		if (!strcmp(mech, info->name)) {
380			/*
381			 * I'm not sure what to do with service
382			 * here. The Solaris manpages are not clear on
383			 * the subject and the OpenSolaris code just
384			 * sets it to rpc_gss_svc_privacy
385			 * unconditionally with a comment noting that
386			 * it is bogus.
387			 */
388			*service = rpc_gss_svc_privacy;
389			return info->qops;
390		}
391	}
392
393	_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT);
394	return (NULL);
395}
396
397bool_t
398rpc_gss_get_versions(u_int *vers_hi, u_int *vers_lo)
399{
400
401	*vers_hi = 1;
402	*vers_lo = 1;
403	return (TRUE);
404}
405
406bool_t
407rpc_gss_is_installed(const char *mech)
408{
409	struct mech_info *info;
410
411	_rpc_gss_load_mech();
412	SLIST_FOREACH(info, &mechs, link)
413		if (!strcmp(mech, info->name))
414			return (TRUE);
415	return (FALSE);
416}
417
418