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