1153838Sdfr/*-
2153838Sdfr * Copyright (c) 2005 Doug Rabson
3153838Sdfr * All rights reserved.
4153838Sdfr *
5153838Sdfr * Redistribution and use in source and binary forms, with or without
6153838Sdfr * modification, are permitted provided that the following conditions
7153838Sdfr * are met:
8153838Sdfr * 1. Redistributions of source code must retain the above copyright
9153838Sdfr *    notice, this list of conditions and the following disclaimer.
10153838Sdfr * 2. Redistributions in binary form must reproduce the above copyright
11153838Sdfr *    notice, this list of conditions and the following disclaimer in the
12153838Sdfr *    documentation and/or other materials provided with the distribution.
13153838Sdfr *
14153838Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15153838Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16153838Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17153838Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18153838Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19153838Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20153838Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21153838Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22153838Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23153838Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24153838Sdfr * SUCH DAMAGE.
25153838Sdfr *
26153838Sdfr *	$FreeBSD$
27153838Sdfr */
28153838Sdfr
29153838Sdfr#include <gssapi/gssapi.h>
30178828Sdfr#include <ctype.h>
31153838Sdfr#include <dlfcn.h>
32153838Sdfr#include <errno.h>
33153838Sdfr#include <stdio.h>
34153838Sdfr#include <stdlib.h>
35153838Sdfr#include <string.h>
36153838Sdfr
37153838Sdfr#include "mech_switch.h"
38153838Sdfr#include "utils.h"
39153838Sdfr
40153838Sdfr#ifndef _PATH_GSS_MECH
41153838Sdfr#define _PATH_GSS_MECH	"/etc/gss/mech"
42153838Sdfr#endif
43153838Sdfr
44153838Sdfrstruct _gss_mech_switch_list _gss_mechs =
45201145Santoine	SLIST_HEAD_INITIALIZER(_gss_mechs);
46153838Sdfrgss_OID_set _gss_mech_oids;
47153838Sdfr
48153838Sdfr/*
49153838Sdfr * Convert a string containing an OID in 'dot' form
50153838Sdfr * (e.g. 1.2.840.113554.1.2.2) to a gss_OID.
51153838Sdfr */
52153838Sdfrstatic int
53153838Sdfr_gss_string_to_oid(const char* s, gss_OID oid)
54153838Sdfr{
55153838Sdfr	int			number_count, i, j;
56153838Sdfr	int			byte_count;
57153838Sdfr	const char		*p, *q;
58153838Sdfr	char			*res;
59153838Sdfr
60178828Sdfr	oid->length = 0;
61178828Sdfr	oid->elements = NULL;
62178828Sdfr
63153838Sdfr	/*
64153838Sdfr	 * First figure out how many numbers in the oid, then
65153838Sdfr	 * calculate the compiled oid size.
66153838Sdfr	 */
67153838Sdfr	number_count = 0;
68153838Sdfr	for (p = s; p; p = q) {
69153838Sdfr		q = strchr(p, '.');
70153838Sdfr		if (q) q = q + 1;
71153838Sdfr		number_count++;
72153838Sdfr	}
73153838Sdfr
74153838Sdfr	/*
75153838Sdfr	 * The first two numbers are in the first byte and each
76153838Sdfr	 * subsequent number is encoded in a variable byte sequence.
77153838Sdfr	 */
78153838Sdfr	if (number_count < 2)
79153838Sdfr		return (EINVAL);
80153838Sdfr
81153838Sdfr	/*
82153838Sdfr	 * We do this in two passes. The first pass, we just figure
83153838Sdfr	 * out the size. Second time around, we actually encode the
84153838Sdfr	 * number.
85153838Sdfr	 */
86153838Sdfr	res = 0;
87153838Sdfr	for (i = 0; i < 2; i++) {
88153838Sdfr		byte_count = 0;
89153838Sdfr		for (p = s, j = 0; p; p = q, j++) {
90153838Sdfr			unsigned int number = 0;
91153838Sdfr
92153838Sdfr			/*
93153838Sdfr			 * Find the end of this number.
94153838Sdfr			 */
95153838Sdfr			q = strchr(p, '.');
96153838Sdfr			if (q) q = q + 1;
97153838Sdfr
98153838Sdfr			/*
99153838Sdfr			 * Read the number of of the string. Don't
100153838Sdfr			 * bother with anything except base ten.
101153838Sdfr			 */
102153838Sdfr			while (*p && *p != '.') {
103153838Sdfr				number = 10 * number + (*p - '0');
104153838Sdfr				p++;
105153838Sdfr			}
106153838Sdfr
107153838Sdfr			/*
108153838Sdfr			 * Encode the number. The first two numbers
109153838Sdfr			 * are packed into the first byte. Subsequent
110153838Sdfr			 * numbers are encoded in bytes seven bits at
111153838Sdfr			 * a time with the last byte having the high
112153838Sdfr			 * bit set.
113153838Sdfr			 */
114153838Sdfr			if (j == 0) {
115153838Sdfr				if (res)
116153838Sdfr					*res = number * 40;
117153838Sdfr			} else if (j == 1) {
118153838Sdfr				if (res) {
119153838Sdfr					*res += number;
120153838Sdfr					res++;
121153838Sdfr				}
122153838Sdfr				byte_count++;
123153838Sdfr			} else if (j >= 2) {
124153838Sdfr				/*
125153838Sdfr				 * The number is encoded in seven bit chunks.
126153838Sdfr				 */
127153838Sdfr				unsigned int t;
128153838Sdfr				int bytes;
129153838Sdfr
130153838Sdfr				bytes = 0;
131153838Sdfr				for (t = number; t; t >>= 7)
132153838Sdfr					bytes++;
133153838Sdfr				if (bytes == 0) bytes = 1;
134153838Sdfr				while (bytes) {
135153838Sdfr					if (res) {
136153838Sdfr						int bit = 7*(bytes-1);
137153838Sdfr
138153838Sdfr						*res = (number >> bit) & 0x7f;
139153838Sdfr						if (bytes != 1)
140153838Sdfr							*res |= 0x80;
141153838Sdfr						res++;
142153838Sdfr					}
143153838Sdfr					byte_count++;
144153838Sdfr					bytes--;
145153838Sdfr				}
146153838Sdfr			}
147153838Sdfr		}
148153838Sdfr		if (!res) {
149153838Sdfr			res = malloc(byte_count);
150153838Sdfr			if (!res)
151153838Sdfr				return (ENOMEM);
152153838Sdfr			oid->length = byte_count;
153153838Sdfr			oid->elements = res;
154153838Sdfr		}
155153838Sdfr	}
156153838Sdfr
157153838Sdfr	return (0);
158153838Sdfr}
159153838Sdfr
160153838Sdfr
161178828Sdfr#define SYM(name)						\
162178828Sdfrdo {								\
163178828Sdfr	snprintf(buf, sizeof(buf), "%s_%s",			\
164178828Sdfr	    m->gm_name_prefix, #name);				\
165178828Sdfr	m->gm_ ## name = dlsym(so, buf);			\
166178828Sdfr	if (!m->gm_ ## name) {					\
167178828Sdfr		fprintf(stderr, "can't find symbol %s\n", buf);	\
168178828Sdfr		goto bad;					\
169178828Sdfr	}							\
170153838Sdfr} while (0)
171153838Sdfr
172178828Sdfr#define OPTSYM(name)				\
173178828Sdfrdo {						\
174178828Sdfr	snprintf(buf, sizeof(buf), "%s_%s",	\
175178828Sdfr	    m->gm_name_prefix, #name);		\
176178828Sdfr	m->gm_ ## name = dlsym(so, buf);	\
177153838Sdfr} while (0)
178153838Sdfr
179153838Sdfr/*
180153838Sdfr * Load the mechanisms file (/etc/gss/mech).
181153838Sdfr */
182153838Sdfrvoid
183153838Sdfr_gss_load_mech(void)
184153838Sdfr{
185153838Sdfr	OM_uint32	major_status, minor_status;
186153838Sdfr	FILE		*fp;
187153838Sdfr	char		buf[256];
188153838Sdfr	char		*p;
189153838Sdfr	char		*name, *oid, *lib, *kobj;
190153838Sdfr	struct _gss_mech_switch *m;
191153838Sdfr	int		count;
192153838Sdfr	void		*so;
193178828Sdfr	const char	*(*prefix_fn)(void);
194153838Sdfr
195153838Sdfr	if (SLIST_FIRST(&_gss_mechs))
196153838Sdfr		return;
197153838Sdfr
198153838Sdfr	major_status = gss_create_empty_oid_set(&minor_status,
199153838Sdfr	    &_gss_mech_oids);
200153838Sdfr	if (major_status)
201153838Sdfr		return;
202153838Sdfr
203153838Sdfr	fp = fopen(_PATH_GSS_MECH, "r");
204153838Sdfr	if (!fp) {
205153838Sdfr		perror(_PATH_GSS_MECH);
206153838Sdfr		return;
207153838Sdfr	}
208153838Sdfr
209153838Sdfr	count = 0;
210153838Sdfr	while (fgets(buf, sizeof(buf), fp)) {
211153838Sdfr		if (*buf == '#')
212153838Sdfr			continue;
213153838Sdfr		p = buf;
214153838Sdfr		name = strsep(&p, "\t\n ");
215155287Sdfr		if (p) while (isspace(*p)) p++;
216153838Sdfr		oid = strsep(&p, "\t\n ");
217155287Sdfr		if (p) while (isspace(*p)) p++;
218153838Sdfr		lib = strsep(&p, "\t\n ");
219155287Sdfr		if (p) while (isspace(*p)) p++;
220153838Sdfr		kobj = strsep(&p, "\t\n ");
221153838Sdfr		if (!name || !oid || !lib || !kobj)
222153838Sdfr			continue;
223153838Sdfr
224153838Sdfr		so = dlopen(lib, RTLD_LOCAL);
225153838Sdfr		if (!so) {
226153838Sdfr			fprintf(stderr, "dlopen: %s\n", dlerror());
227153838Sdfr			continue;
228153838Sdfr		}
229153838Sdfr
230153838Sdfr		m = malloc(sizeof(struct _gss_mech_switch));
231153838Sdfr		if (!m)
232153838Sdfr			break;
233153838Sdfr		m->gm_so = so;
234153838Sdfr		if (_gss_string_to_oid(oid, &m->gm_mech_oid)) {
235153838Sdfr			free(m);
236153838Sdfr			continue;
237153838Sdfr		}
238153838Sdfr
239178828Sdfr		prefix_fn = (const char *(*)(void))
240178828Sdfr			dlsym(so, "_gss_name_prefix");
241178828Sdfr		if (prefix_fn)
242178828Sdfr			m->gm_name_prefix = prefix_fn();
243178828Sdfr		else
244178828Sdfr			m->gm_name_prefix = "gss";
245178828Sdfr
246153838Sdfr		major_status = gss_add_oid_set_member(&minor_status,
247153838Sdfr		    &m->gm_mech_oid, &_gss_mech_oids);
248153838Sdfr		if (major_status) {
249153838Sdfr			free(m->gm_mech_oid.elements);
250153838Sdfr			free(m);
251153838Sdfr			continue;
252153838Sdfr		}
253153838Sdfr
254153838Sdfr		SYM(acquire_cred);
255153838Sdfr		SYM(release_cred);
256153838Sdfr		SYM(init_sec_context);
257153838Sdfr		SYM(accept_sec_context);
258153838Sdfr		SYM(process_context_token);
259153838Sdfr		SYM(delete_sec_context);
260153838Sdfr		SYM(context_time);
261153838Sdfr		SYM(get_mic);
262153838Sdfr		SYM(verify_mic);
263153838Sdfr		SYM(wrap);
264153838Sdfr		SYM(unwrap);
265153838Sdfr		SYM(display_status);
266178828Sdfr		OPTSYM(indicate_mechs);
267153838Sdfr		SYM(compare_name);
268153838Sdfr		SYM(display_name);
269153838Sdfr		SYM(import_name);
270153838Sdfr		SYM(export_name);
271153838Sdfr		SYM(release_name);
272153838Sdfr		SYM(inquire_cred);
273153838Sdfr		SYM(inquire_context);
274153838Sdfr		SYM(wrap_size_limit);
275153838Sdfr		SYM(add_cred);
276153838Sdfr		SYM(inquire_cred_by_mech);
277153838Sdfr		SYM(export_sec_context);
278153838Sdfr		SYM(import_sec_context);
279153838Sdfr		SYM(inquire_names_for_mech);
280153838Sdfr		SYM(inquire_mechs_for_name);
281153838Sdfr		SYM(canonicalize_name);
282153838Sdfr		SYM(duplicate_name);
283178828Sdfr		OPTSYM(inquire_sec_context_by_oid);
284178828Sdfr		OPTSYM(inquire_cred_by_oid);
285178828Sdfr		OPTSYM(set_sec_context_option);
286178828Sdfr		OPTSYM(set_cred_option);
287178828Sdfr		OPTSYM(pseudo_random);
288181344Sdfr		OPTSYM(pname_to_uid);
289153838Sdfr
290153838Sdfr		SLIST_INSERT_HEAD(&_gss_mechs, m, gm_link);
291153838Sdfr		count++;
292153838Sdfr		continue;
293153838Sdfr
294153838Sdfr	bad:
295153838Sdfr		free(m->gm_mech_oid.elements);
296153838Sdfr		free(m);
297153838Sdfr		dlclose(so);
298153838Sdfr		continue;
299153838Sdfr	}
300153838Sdfr	fclose(fp);
301153838Sdfr}
302153838Sdfr
303153838Sdfrstruct _gss_mech_switch *
304153838Sdfr_gss_find_mech_switch(gss_OID mech)
305153838Sdfr{
306153838Sdfr	struct _gss_mech_switch *m;
307153838Sdfr
308153838Sdfr	_gss_load_mech();
309153838Sdfr	SLIST_FOREACH(m, &_gss_mechs, gm_link) {
310178828Sdfr		if (gss_oid_equal(&m->gm_mech_oid, mech))
311153838Sdfr			return m;
312153838Sdfr	}
313153838Sdfr	return (0);
314153838Sdfr}
315