Home | History | Annotate | Line # | Download | only in mech
      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
     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
    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
    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
    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