xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/gssapi/mech/gss_mech_switch.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
1 /*	$NetBSD: gss_mech_switch.c,v 1.3 2023/06/19 21:41:43 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2005 Doug Rabson
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  *	$FreeBSD: src/lib/libgssapi/gss_mech_switch.c,v 1.2 2006/02/04 09:40:21 dfr Exp $
29  */
30 
31 #include "mech_locl.h"
32 #include <heim_threads.h>
33 
34 #ifndef _PATH_GSS_MECH
35 #define _PATH_GSS_MECH	"/etc/gss/mech"
36 #endif
37 
38 struct _gss_mech_switch_list _gss_mechs = { NULL } ;
39 gss_OID_set _gss_mech_oids;
40 static HEIMDAL_MUTEX _gss_mech_mutex = HEIMDAL_MUTEX_INITIALIZER;
41 
42 /*
43  * Convert a string containing an OID in 'dot' form
44  * (e.g. 1.2.840.113554.1.2.2) to a gss_OID.
45  */
46 static int
_gss_string_to_oid(const char * s,gss_OID oid)47 _gss_string_to_oid(const char* s, gss_OID oid)
48 {
49 	int			number_count, i, j;
50 	size_t			byte_count;
51 	const char		*p, *q;
52 	char			*res;
53 
54 	oid->length = 0;
55 	oid->elements = NULL;
56 
57 	/*
58 	 * First figure out how many numbers in the oid, then
59 	 * calculate the compiled oid size.
60 	 */
61 	number_count = 0;
62 	for (p = s; p; p = q) {
63 		q = strchr(p, '.');
64 		if (q) q = q + 1;
65 		number_count++;
66 	}
67 
68 	/*
69 	 * The first two numbers are in the first byte and each
70 	 * subsequent number is encoded in a variable byte sequence.
71 	 */
72 	if (number_count < 2)
73 		return (EINVAL);
74 
75 	/*
76 	 * We do this in two passes. The first pass, we just figure
77 	 * out the size. Second time around, we actually encode the
78 	 * number.
79 	 */
80 	res = 0;
81 	for (i = 0; i < 2; i++) {
82 		byte_count = 0;
83 		for (p = s, j = 0; p; p = q, j++) {
84 			unsigned int number = 0;
85 
86 			/*
87 			 * Find the end of this number.
88 			 */
89 			q = strchr(p, '.');
90 			if (q) q = q + 1;
91 
92 			/*
93 			 * Read the number of of the string. Don't
94 			 * bother with anything except base ten.
95 			 */
96 			while (*p && *p != '.') {
97 				number = 10 * number + (*p - '0');
98 				p++;
99 			}
100 
101 			/*
102 			 * Encode the number. The first two numbers
103 			 * are packed into the first byte. Subsequent
104 			 * numbers are encoded in bytes seven bits at
105 			 * a time with the last byte having the high
106 			 * bit set.
107 			 */
108 			if (j == 0) {
109 				if (res)
110 					*res = number * 40;
111 			} else if (j == 1) {
112 				if (res) {
113 					*res += number;
114 					res++;
115 				}
116 				byte_count++;
117 			} else if (j >= 2) {
118 				/*
119 				 * The number is encoded in seven bit chunks.
120 				 */
121 				unsigned int t;
122 				unsigned int bytes;
123 
124 				bytes = 0;
125 				for (t = number; t; t >>= 7)
126 					bytes++;
127 				if (bytes == 0) bytes = 1;
128 				while (bytes) {
129 					if (res) {
130 						int bit = 7*(bytes-1);
131 
132 						*res = (number >> bit) & 0x7f;
133 						if (bytes != 1)
134 							*res |= 0x80;
135 						res++;
136 					}
137 					byte_count++;
138 					bytes--;
139 				}
140 			}
141 		}
142                 if (byte_count == 0)
143                     return EINVAL;
144 		if (!res) {
145 			res = malloc(byte_count);
146 			if (!res)
147 				return (ENOMEM);
148 			oid->length = byte_count;
149 			oid->elements = res;
150 		}
151 	}
152 
153 	return (0);
154 }
155 
156 #define SYM(name)							\
157 do {									\
158 	m->gm_mech.gm_ ## name = dlsym(so, "gss_" #name);		\
159 	if (!m->gm_mech.gm_ ## name ||					\
160 	    m->gm_mech.gm_ ##name == gss_ ## name) {			\
161 		fprintf(stderr, "can't find symbol gss_" #name "\n");	\
162 		goto bad;						\
163 	}								\
164 } while (0)
165 
166 #define OPTSYM(name)							\
167 do {									\
168 	m->gm_mech.gm_ ## name = dlsym(so, "gss_" #name);		\
169 	if (m->gm_mech.gm_ ## name == gss_ ## name)			\
170 		m->gm_mech.gm_ ## name = NULL;				\
171 } while (0)
172 
173 #define OPTSPISYM(name)							\
174 do {									\
175 	m->gm_mech.gm_ ## name = dlsym(so, "gssspi_" #name);		\
176 } while (0)
177 
178 #define COMPATSYM(name)							\
179 do {									\
180 	m->gm_mech.gm_compat->gmc_ ## name = dlsym(so, "gss_" #name);	\
181 	if (m->gm_mech.gm_compat->gmc_ ## name == gss_ ## name)		\
182 		m->gm_mech.gm_compat->gmc_ ## name = NULL;		\
183 } while (0)
184 
185 #define COMPATSPISYM(name)						\
186 do {									\
187 	m->gm_mech.gm_compat->gmc_ ## name = dlsym(so, "gssspi_" #name);\
188 	if (m->gm_mech.gm_compat->gmc_ ## name == gss_ ## name)		\
189 		m->gm_mech.gm_compat->gmc_ ## name = NULL;		\
190 } while (0)
191 
192 /*
193  *
194  */
195 static int
add_builtin(gssapi_mech_interface mech)196 add_builtin(gssapi_mech_interface mech)
197 {
198     struct _gss_mech_switch *m;
199     OM_uint32 minor_status;
200 
201     /* not registering any mech is ok */
202     if (mech == NULL)
203 	return 0;
204 
205     m = calloc(1, sizeof(*m));
206     if (m == NULL)
207 	return ENOMEM;
208     m->gm_so = NULL;
209     m->gm_mech = *mech;
210     m->gm_mech_oid = mech->gm_mech_oid; /* XXX */
211     gss_add_oid_set_member(&minor_status,
212 			   &m->gm_mech.gm_mech_oid, &_gss_mech_oids);
213 
214     /* pick up the oid sets of names */
215 
216     if (m->gm_mech.gm_inquire_names_for_mech)
217 	(*m->gm_mech.gm_inquire_names_for_mech)(&minor_status,
218 	    &m->gm_mech.gm_mech_oid, &m->gm_name_types);
219 
220     if (m->gm_name_types == NULL)
221 	gss_create_empty_oid_set(&minor_status, &m->gm_name_types);
222 
223     HEIM_SLIST_INSERT_HEAD(&_gss_mechs, m, gm_link);
224     return 0;
225 }
226 
227 /*
228  * Load the mechanisms file (/etc/gss/mech).
229  */
230 void
_gss_load_mech(void)231 _gss_load_mech(void)
232 {
233 	OM_uint32	major_status, minor_status;
234 	FILE		*fp;
235 	char		buf[256];
236 	char		*p;
237 	char		*name, *oid, *lib, *kobj;
238 	struct _gss_mech_switch *m;
239 	void		*so;
240 	gss_OID_desc	mech_oid;
241 	int		found;
242 
243 
244 	HEIMDAL_MUTEX_lock(&_gss_mech_mutex);
245 
246 	if (HEIM_SLIST_FIRST(&_gss_mechs)) {
247 		HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
248 		return;
249 	}
250 
251 	major_status = gss_create_empty_oid_set(&minor_status,
252 	    &_gss_mech_oids);
253 	if (major_status) {
254 		HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
255 		return;
256 	}
257 
258 	add_builtin(__gss_krb5_initialize());
259 	add_builtin(__gss_spnego_initialize());
260 	add_builtin(__gss_ntlm_initialize());
261 
262 #ifdef HAVE_DLOPEN
263 	fp = fopen(_PATH_GSS_MECH, "r");
264 	if (!fp) {
265 		HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
266 		return;
267 	}
268 	rk_cloexec_file(fp);
269 
270 	while (fgets(buf, sizeof(buf), fp)) {
271 		_gss_mo_init *mi;
272 
273 		if (*buf == '#')
274 			continue;
275 		p = buf;
276 		name = strsep(&p, "\t\n ");
277 		if (p) while (isspace((unsigned char)*p)) p++;
278 		oid = strsep(&p, "\t\n ");
279 		if (p) while (isspace((unsigned char)*p)) p++;
280 		lib = strsep(&p, "\t\n ");
281 		if (p) while (isspace((unsigned char)*p)) p++;
282 		kobj = strsep(&p, "\t\n ");
283 		if (!name || !oid || !lib || !kobj)
284 			continue;
285 
286 		if (_gss_string_to_oid(oid, &mech_oid))
287 			continue;
288 
289 		/*
290 		 * Check for duplicates, already loaded mechs.
291 		 */
292 		found = 0;
293 		HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
294 			if (gss_oid_equal(&m->gm_mech.gm_mech_oid, &mech_oid)) {
295 				found = 1;
296 				free(mech_oid.elements);
297 				break;
298 			}
299 		}
300 		if (found)
301 			continue;
302 
303 #ifndef RTLD_LOCAL
304 #define RTLD_LOCAL 0
305 #endif
306 
307 #ifndef RTLD_GROUP
308 #define RTLD_GROUP 0
309 #endif
310 
311 		so = dlopen(lib, RTLD_LAZY | RTLD_LOCAL | RTLD_GROUP);
312 		if (so == NULL) {
313 /*			fprintf(stderr, "dlopen: %s\n", dlerror()); */
314 			goto bad;
315 		}
316 
317 		m = calloc(1, sizeof(*m));
318 		if (m == NULL)
319 			goto bad;
320 
321 		m->gm_so = so;
322 		m->gm_mech_oid = mech_oid;
323 		m->gm_mech.gm_name = strdup(name);
324 		m->gm_mech.gm_mech_oid = mech_oid;
325 		m->gm_mech.gm_flags = 0;
326 		m->gm_mech.gm_compat = calloc(1, sizeof(struct gss_mech_compat_desc_struct));
327 		if (m->gm_mech.gm_compat == NULL)
328 			goto bad;
329 
330 		major_status = gss_add_oid_set_member(&minor_status,
331 		    &m->gm_mech.gm_mech_oid, &_gss_mech_oids);
332 		if (GSS_ERROR(major_status))
333 			goto bad;
334 
335 		SYM(acquire_cred);
336 		SYM(release_cred);
337 		SYM(init_sec_context);
338 		SYM(accept_sec_context);
339 		SYM(process_context_token);
340 		SYM(delete_sec_context);
341 		SYM(context_time);
342 		SYM(get_mic);
343 		SYM(verify_mic);
344 		SYM(wrap);
345 		SYM(unwrap);
346 		SYM(display_status);
347 		SYM(indicate_mechs);
348 		SYM(compare_name);
349 		SYM(display_name);
350 		SYM(import_name);
351 		SYM(export_name);
352 		SYM(release_name);
353 		SYM(inquire_cred);
354 		SYM(inquire_context);
355 		SYM(wrap_size_limit);
356 		SYM(add_cred);
357 		SYM(inquire_cred_by_mech);
358 		SYM(export_sec_context);
359 		SYM(import_sec_context);
360 		SYM(inquire_names_for_mech);
361 		SYM(inquire_mechs_for_name);
362 		SYM(canonicalize_name);
363 		SYM(duplicate_name);
364 		OPTSYM(inquire_cred_by_oid);
365 		OPTSYM(inquire_sec_context_by_oid);
366 		OPTSYM(set_sec_context_option);
367 		OPTSPISYM(set_cred_option);
368 		OPTSYM(pseudo_random);
369 		OPTSYM(wrap_iov);
370 		OPTSYM(unwrap_iov);
371 		OPTSYM(wrap_iov_length);
372 		OPTSYM(store_cred);
373 		OPTSYM(export_cred);
374 		OPTSYM(import_cred);
375 #if 0
376 		OPTSYM(acquire_cred_ext);
377 		OPTSYM(iter_creds);
378 		OPTSYM(destroy_cred);
379 		OPTSYM(cred_hold);
380 		OPTSYM(cred_unhold);
381 		OPTSYM(cred_label_get);
382 		OPTSYM(cred_label_set);
383 #endif
384 		OPTSYM(display_name_ext);
385 		OPTSYM(inquire_name);
386 		OPTSYM(get_name_attribute);
387 		OPTSYM(set_name_attribute);
388 		OPTSYM(delete_name_attribute);
389 		OPTSYM(export_name_composite);
390 		OPTSYM(localname);
391 		OPTSPISYM(authorize_localname);
392 
393 		mi = dlsym(so, "gss_mo_init");
394 		if (mi != NULL) {
395 			major_status = mi(&minor_status, &mech_oid,
396 					  &m->gm_mech.gm_mo, &m->gm_mech.gm_mo_num);
397 			if (GSS_ERROR(major_status))
398 				goto bad;
399 		} else {
400 			/* API-as-SPI compatibility */
401 			COMPATSYM(inquire_saslname_for_mech);
402 			COMPATSYM(inquire_mech_for_saslname);
403 			COMPATSYM(inquire_attrs_for_mech);
404 			COMPATSPISYM(acquire_cred_with_password);
405 		}
406 
407 		/* pick up the oid sets of names */
408 
409 		if (m->gm_mech.gm_inquire_names_for_mech)
410 			(*m->gm_mech.gm_inquire_names_for_mech)(&minor_status,
411 			&m->gm_mech.gm_mech_oid, &m->gm_name_types);
412 
413 		if (m->gm_name_types == NULL)
414 			gss_create_empty_oid_set(&minor_status, &m->gm_name_types);
415 
416 		HEIM_SLIST_INSERT_HEAD(&_gss_mechs, m, gm_link);
417 		continue;
418 
419 	bad:
420 		if (m != NULL) {
421 			free(m->gm_mech.gm_compat);
422 			free(m->gm_mech.gm_mech_oid.elements);
423 			free((char *)m->gm_mech.gm_name);
424 			free(m);
425 		}
426 		dlclose(so);
427 		continue;
428 	}
429 	fclose(fp);
430 #endif
431 	HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
432 }
433 
434 gssapi_mech_interface
__gss_get_mechanism(gss_const_OID mech)435 __gss_get_mechanism(gss_const_OID mech)
436 {
437         struct _gss_mech_switch	*m;
438 
439 	_gss_load_mech();
440 	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
441 		if (gss_oid_equal(&m->gm_mech.gm_mech_oid, mech))
442 			return &m->gm_mech;
443 	}
444 	return NULL;
445 }
446