Line data Source code
1 : /*-
2 : * Copyright (c) 2005 Doug Rabson
3 : * All rights reserved.
4 : *
5 : * Redistribution and use in source and binary forms, with or without
6 : * modification, are permitted provided that the following conditions
7 : * are met:
8 : * 1. Redistributions of source code must retain the above copyright
9 : * notice, this list of conditions and the following disclaimer.
10 : * 2. Redistributions in binary form must reproduce the above copyright
11 : * notice, this list of conditions and the following disclaimer in the
12 : * documentation and/or other materials provided with the distribution.
13 : *
14 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 : * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 : * SUCH DAMAGE.
25 : *
26 : * $FreeBSD: src/lib/libgssapi/gss_mech_switch.c,v 1.2 2006/02/04 09:40:21 dfr Exp $
27 : */
28 :
29 : #include "mech_locl.h"
30 : #include <heim_threads.h>
31 :
32 : #ifndef _PATH_GSS_MECH
33 : #define _PATH_GSS_MECH "/etc/gss/mech"
34 : #endif
35 :
36 : struct _gss_mech_switch_list _gss_mechs = { NULL, NULL } ;
37 : gss_OID_set _gss_mech_oids;
38 : static HEIMDAL_MUTEX _gss_mech_mutex = HEIMDAL_MUTEX_INITIALIZER;
39 :
40 : /*
41 : * Convert a string containing an OID in 'dot' form
42 : * (e.g. 1.2.840.113554.1.2.2) to a gss_OID.
43 : */
44 : static int
45 0 : _gss_string_to_oid(const char* s, gss_OID *oidp)
46 : {
47 0 : int number_count, i, j;
48 0 : size_t byte_count;
49 0 : const char *p, *q;
50 0 : char *res;
51 0 : gss_OID_desc oid;
52 :
53 0 : *oidp = GSS_C_NO_OID;
54 :
55 : /*
56 : * First figure out how many numbers in the oid, then
57 : * calculate the compiled oid size.
58 : */
59 0 : number_count = 0;
60 0 : for (p = s; p; p = q) {
61 0 : q = strchr(p, '.');
62 0 : if (q) q = q + 1;
63 0 : number_count++;
64 : }
65 :
66 : /*
67 : * The first two numbers are in the first byte and each
68 : * subsequent number is encoded in a variable byte sequence.
69 : */
70 0 : if (number_count < 2)
71 0 : return (EINVAL);
72 :
73 : /*
74 : * We do this in two passes. The first pass, we just figure
75 : * out the size. Second time around, we actually encode the
76 : * number.
77 : */
78 0 : res = 0;
79 0 : for (i = 0; i < 2; i++) {
80 0 : byte_count = 0;
81 0 : for (p = s, j = 0; p; p = q, j++) {
82 0 : unsigned int number = 0;
83 :
84 : /*
85 : * Find the end of this number.
86 : */
87 0 : q = strchr(p, '.');
88 0 : if (q) q = q + 1;
89 :
90 : /*
91 : * Read the number of of the string. Don't
92 : * bother with anything except base ten.
93 : */
94 0 : while (*p && *p != '.') {
95 0 : number = 10 * number + (*p - '0');
96 0 : p++;
97 : }
98 :
99 : /*
100 : * Encode the number. The first two numbers
101 : * are packed into the first byte. Subsequent
102 : * numbers are encoded in bytes seven bits at
103 : * a time with the last byte having the high
104 : * bit set.
105 : */
106 0 : if (j == 0) {
107 0 : if (res)
108 0 : *res = number * 40;
109 0 : } else if (j == 1) {
110 0 : if (res) {
111 0 : *res += number;
112 0 : res++;
113 : }
114 0 : byte_count++;
115 0 : } else if (j >= 2) {
116 : /*
117 : * The number is encoded in seven bit chunks.
118 : */
119 : unsigned int t;
120 : unsigned int bytes;
121 :
122 0 : bytes = 0;
123 0 : for (t = number; t; t >>= 7)
124 0 : bytes++;
125 0 : if (bytes == 0) bytes = 1;
126 0 : while (bytes) {
127 0 : if (res) {
128 0 : int bit = 7*(bytes-1);
129 :
130 0 : *res = (number >> bit) & 0x7f;
131 0 : if (bytes != 1)
132 0 : *res |= 0x80;
133 0 : res++;
134 : }
135 0 : byte_count++;
136 0 : bytes--;
137 : }
138 : }
139 : }
140 0 : if (byte_count == 0)
141 0 : return EINVAL;
142 0 : if (!res) {
143 0 : res = malloc(byte_count);
144 0 : if (!res)
145 0 : return (ENOMEM);
146 0 : oid.length = byte_count;
147 0 : oid.elements = res;
148 : }
149 : }
150 :
151 : {
152 0 : OM_uint32 minor_status, tmp;
153 :
154 0 : if (GSS_ERROR(_gss_intern_oid(&minor_status, &oid, oidp))) {
155 0 : _gss_free_oid(&tmp, &oid);
156 0 : return (minor_status);
157 : }
158 :
159 0 : _gss_free_oid(&tmp, &oid);
160 : }
161 :
162 0 : return (0);
163 : }
164 :
165 : #define SYM(name) \
166 : do { \
167 : m->gm_mech.gm_ ## name = (_gss_##name##_t *)dlsym(so, "gss_" #name); \
168 : if (!m->gm_mech.gm_ ## name || \
169 : m->gm_mech.gm_ ##name == gss_ ## name) { \
170 : _gss_mg_log(1, "can't find symbol gss_" #name "\n"); \
171 : goto bad; \
172 : } \
173 : } while (0)
174 :
175 : #define OPTSYM(name) \
176 : do { \
177 : m->gm_mech.gm_ ## name = (_gss_##name##_t *)dlsym(so, "gss_" #name); \
178 : if (m->gm_mech.gm_ ## name == gss_ ## name) \
179 : m->gm_mech.gm_ ## name = NULL; \
180 : } while (0)
181 :
182 : /* mech exports gssspi_XXX, internally referred to as gss_XXX */
183 : #define OPTSPISYM(name) \
184 : do { \
185 : m->gm_mech.gm_ ## name = (_gss_##name##_t *)dlsym(so, "gssspi_" #name); \
186 : } while (0)
187 :
188 : /* mech exports gssspi_XXX, internally referred to as gssspi_XXX */
189 : #define OPTSPISPISYM(name) \
190 : do { \
191 : m->gm_mech.gm_ ## name = (_gss_##name##_t *)dlsym(so, "gssspi_" #name); \
192 : if (m->gm_mech.gm_ ## name == gssspi_ ## name) \
193 : m->gm_mech.gm_ ## name = NULL; \
194 : } while (0)
195 :
196 : #define COMPATSYM(name) \
197 : do { \
198 : m->gm_mech.gm_compat->gmc_ ## name = (_gss_##name##_t *)dlsym(so, "gss_" #name); \
199 : if (m->gm_mech.gm_compat->gmc_ ## name == gss_ ## name) \
200 : m->gm_mech.gm_compat->gmc_ ## name = NULL; \
201 : } while (0)
202 :
203 : #define COMPATSPISYM(name) \
204 : do { \
205 : m->gm_mech.gm_compat->gmc_ ## name = (_gss_##name##_t *)dlsym(so, "gssspi_" #name); \
206 : if (m->gm_mech.gm_compat->gmc_ ## name == gss_ ## name) \
207 : m->gm_mech.gm_compat->gmc_ ## name = NULL; \
208 : } while (0)
209 :
210 : /*
211 : *
212 : */
213 : static int
214 90344 : add_builtin(gssapi_mech_interface mech)
215 : {
216 3576 : struct _gss_mech_switch *m;
217 3576 : OM_uint32 minor_status;
218 :
219 : /* not registering any mech is ok */
220 90344 : if (mech == NULL)
221 43384 : return 0;
222 :
223 45172 : m = calloc(1, sizeof(*m));
224 45172 : if (m == NULL)
225 0 : return ENOMEM;
226 45172 : m->gm_so = NULL;
227 45172 : m->gm_mech = *mech;
228 45172 : _gss_intern_oid(&minor_status, &mech->gm_mech_oid, &m->gm_mech_oid);
229 45172 : if (minor_status) {
230 0 : free(m);
231 0 : return minor_status;
232 : }
233 :
234 45172 : if (gss_add_oid_set_member(&minor_status, &m->gm_mech.gm_mech_oid,
235 : &_gss_mech_oids) != GSS_S_COMPLETE) {
236 0 : free(m);
237 0 : return ENOMEM;
238 : }
239 :
240 : /* pick up the oid sets of names */
241 :
242 45172 : if (m->gm_mech.gm_inquire_names_for_mech)
243 22586 : (*m->gm_mech.gm_inquire_names_for_mech)(&minor_status,
244 21692 : &m->gm_mech.gm_mech_oid, &m->gm_name_types);
245 :
246 67758 : if (m->gm_name_types == NULL &&
247 22586 : gss_create_empty_oid_set(&minor_status,
248 : &m->gm_name_types) != GSS_S_COMPLETE) {
249 0 : free(m);
250 0 : return ENOMEM;
251 : }
252 :
253 45172 : HEIM_TAILQ_INSERT_TAIL(&_gss_mechs, m, gm_link);
254 45172 : return 0;
255 : }
256 :
257 : static void
258 22586 : init_mech_switch_list(void *p)
259 : {
260 22586 : struct _gss_mech_switch_list *mechs = p;
261 :
262 22586 : HEIM_TAILQ_INIT(mechs);
263 22586 : }
264 :
265 : /*
266 : * Load the mechanisms file (/etc/gss/mech).
267 : */
268 : void
269 527027 : _gss_load_mech(void)
270 : {
271 18216 : OM_uint32 major_status, minor_status;
272 18216 : static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
273 : #ifdef HAVE_DLOPEN
274 : FILE *fp;
275 : char buf[256];
276 : char *p;
277 : char *name, *oid, *lib, *kobj;
278 : struct _gss_mech_switch *m;
279 : void *so;
280 : gss_OID mech_oid;
281 : int found;
282 : const char *conf = secure_getenv("GSS_MECH_CONFIG");
283 : #endif
284 :
285 527027 : heim_base_once_f(&once, &_gss_mechs, init_mech_switch_list);
286 :
287 18216 : HEIMDAL_MUTEX_lock(&_gss_mech_mutex);
288 :
289 527027 : if (!HEIM_TAILQ_EMPTY(&_gss_mechs)) {
290 : HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
291 504441 : return;
292 : }
293 :
294 22586 : major_status = gss_create_empty_oid_set(&minor_status,
295 : &_gss_mech_oids);
296 22586 : if (major_status) {
297 : HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
298 0 : return;
299 : }
300 :
301 22586 : if (add_builtin(__gss_krb5_initialize()))
302 0 : _gss_mg_log(1, "Out of memory while adding builtin Kerberos GSS "
303 : "mechanism to the GSS mechanism switch");
304 22586 : if (add_builtin(__gss_spnego_initialize()))
305 0 : _gss_mg_log(1, "Out of memory while adding builtin SPNEGO "
306 : "mechanism to the GSS mechanism switch");
307 22586 : if (add_builtin(__gss_ntlm_initialize()))
308 0 : _gss_mg_log(1, "Out of memory while adding builtin NTLM "
309 : "mechanism to the GSS mechanism switch");
310 :
311 : #ifdef HAVE_DLOPEN
312 : fp = fopen(conf ? conf : _PATH_GSS_MECH, "r");
313 : if (!fp)
314 : goto out;
315 : rk_cloexec_file(fp);
316 :
317 : while (fgets(buf, sizeof(buf), fp)) {
318 : _gss_mo_init *mi;
319 :
320 : if (*buf == '#')
321 : continue;
322 : p = buf;
323 : name = strsep(&p, "\t\n ");
324 : if (p) while (isspace((unsigned char)*p)) p++;
325 : oid = strsep(&p, "\t\n ");
326 : if (p) while (isspace((unsigned char)*p)) p++;
327 : lib = strsep(&p, "\t\n ");
328 : if (p) while (isspace((unsigned char)*p)) p++;
329 : kobj = strsep(&p, "\t\n ");
330 : if (!name || !oid || !lib || !kobj)
331 : continue;
332 :
333 : if (_gss_string_to_oid(oid, &mech_oid))
334 : continue;
335 :
336 : /*
337 : * Check for duplicates, already loaded mechs.
338 : */
339 : found = 0;
340 : HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
341 : if (gss_oid_equal(&m->gm_mech.gm_mech_oid, mech_oid)) {
342 : found = 1;
343 : break;
344 : }
345 : }
346 : if (found)
347 : continue;
348 :
349 : so = dlopen(lib, RTLD_LAZY | RTLD_LOCAL | RTLD_GROUP);
350 : if (so == NULL) {
351 : _gss_mg_log(1, "dlopen: %s\n", dlerror());
352 : goto bad;
353 : }
354 :
355 : m = calloc(1, sizeof(*m));
356 : if (m == NULL)
357 : goto bad;
358 :
359 : m->gm_so = so;
360 : m->gm_mech_oid = mech_oid;
361 : m->gm_mech.gm_name = strdup(name);
362 : m->gm_mech.gm_mech_oid = *mech_oid;
363 : m->gm_mech.gm_flags = 0;
364 : m->gm_mech.gm_compat = calloc(1, sizeof(struct gss_mech_compat_desc_struct));
365 : if (m->gm_mech.gm_compat == NULL)
366 : goto bad;
367 :
368 : major_status = gss_add_oid_set_member(&minor_status,
369 : &m->gm_mech.gm_mech_oid, &_gss_mech_oids);
370 : if (GSS_ERROR(major_status))
371 : goto bad;
372 :
373 : SYM(acquire_cred);
374 : SYM(release_cred);
375 : SYM(init_sec_context);
376 : SYM(accept_sec_context);
377 : SYM(process_context_token);
378 : SYM(delete_sec_context);
379 : SYM(context_time);
380 : SYM(get_mic);
381 : SYM(verify_mic);
382 : SYM(wrap);
383 : SYM(unwrap);
384 : OPTSYM(display_status);
385 : OPTSYM(indicate_mechs);
386 : SYM(compare_name);
387 : SYM(display_name);
388 : SYM(import_name);
389 : SYM(export_name);
390 : SYM(release_name);
391 : OPTSYM(inquire_cred);
392 : SYM(inquire_context);
393 : SYM(wrap_size_limit);
394 : OPTSYM(add_cred);
395 : OPTSYM(inquire_cred_by_mech);
396 : SYM(export_sec_context);
397 : SYM(import_sec_context);
398 : OPTSYM(inquire_names_for_mech);
399 : OPTSYM(inquire_mechs_for_name);
400 : SYM(canonicalize_name);
401 : SYM(duplicate_name);
402 : OPTSYM(inquire_cred_by_oid);
403 : OPTSYM(inquire_sec_context_by_oid);
404 : OPTSYM(set_sec_context_option);
405 : OPTSPISYM(set_cred_option);
406 : OPTSYM(pseudo_random);
407 : OPTSYM(wrap_iov);
408 : OPTSYM(unwrap_iov);
409 : OPTSYM(wrap_iov_length);
410 : OPTSYM(store_cred);
411 : OPTSYM(export_cred);
412 : OPTSYM(import_cred);
413 : OPTSYM(acquire_cred_from);
414 : OPTSYM(acquire_cred_impersonate_name);
415 : #if 0
416 : OPTSYM(iter_creds);
417 : OPTSYM(destroy_cred);
418 : OPTSYM(cred_hold);
419 : OPTSYM(cred_unhold);
420 : OPTSYM(cred_label_get);
421 : OPTSYM(cred_label_set);
422 : #endif
423 : OPTSYM(display_name_ext);
424 : OPTSYM(inquire_name);
425 : OPTSYM(get_name_attribute);
426 : OPTSYM(set_name_attribute);
427 : OPTSYM(delete_name_attribute);
428 : OPTSYM(export_name_composite);
429 : OPTSYM(localname);
430 : OPTSYM(duplicate_cred);
431 : OPTSYM(add_cred_from);
432 : OPTSYM(store_cred_into);
433 : OPTSPISYM(authorize_localname);
434 : OPTSPISPISYM(query_mechanism_info);
435 : OPTSPISPISYM(query_meta_data);
436 : OPTSPISPISYM(exchange_meta_data);
437 :
438 : mi = (_gss_mo_init *)dlsym(so, "gss_mo_init");
439 : if (mi != NULL) {
440 : major_status = mi(&minor_status, mech_oid,
441 : &m->gm_mech.gm_mo, &m->gm_mech.gm_mo_num);
442 : if (GSS_ERROR(major_status))
443 : goto bad;
444 : } else {
445 : /* API-as-SPI compatibility */
446 : COMPATSYM(inquire_saslname_for_mech);
447 : COMPATSYM(inquire_mech_for_saslname);
448 : COMPATSYM(inquire_attrs_for_mech);
449 : COMPATSPISYM(acquire_cred_with_password);
450 : }
451 :
452 : /* pick up the oid sets of names */
453 :
454 : if (m->gm_mech.gm_inquire_names_for_mech)
455 : (*m->gm_mech.gm_inquire_names_for_mech)(&minor_status,
456 : &m->gm_mech.gm_mech_oid, &m->gm_name_types);
457 :
458 : if (m->gm_name_types == NULL)
459 : gss_create_empty_oid_set(&minor_status, &m->gm_name_types);
460 :
461 : HEIM_TAILQ_INSERT_TAIL(&_gss_mechs, m, gm_link);
462 : continue;
463 :
464 : bad:
465 : if (m != NULL) {
466 : free(m->gm_mech.gm_compat);
467 : /* do not free OID, it has been interned */
468 : free((char *)m->gm_mech.gm_name);
469 : free(m);
470 : }
471 : if (so != NULL)
472 : dlclose(so);
473 : continue;
474 : }
475 : fclose(fp);
476 :
477 : out:
478 :
479 : #endif
480 22586 : if (add_builtin(__gss_sanon_initialize()))
481 0 : _gss_mg_log(1, "Out of memory while adding builtin SANON "
482 : "mechanism to the GSS mechanism switch");
483 894 : HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
484 : }
485 :
486 : gssapi_mech_interface
487 67615 : __gss_get_mechanism(gss_const_OID mech)
488 : {
489 1820 : struct _gss_mech_switch *m;
490 :
491 67615 : _gss_load_mech();
492 67615 : HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
493 67615 : if (gss_oid_equal(&m->gm_mech.gm_mech_oid, mech))
494 67615 : return &m->gm_mech;
495 : }
496 0 : return NULL;
497 : }
498 :
499 : gss_OID
500 20271 : _gss_mg_support_mechanism(gss_const_OID mech)
501 : {
502 785 : struct _gss_mech_switch *m;
503 :
504 20271 : _gss_load_mech();
505 20271 : HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
506 20271 : if (gss_oid_equal(&m->gm_mech.gm_mech_oid, mech))
507 20271 : return m->gm_mech_oid;
508 : }
509 0 : return NULL;
510 : }
511 :
512 : enum mech_name_match {
513 : MATCH_NONE = 0,
514 : MATCH_COMPLETE,
515 : MATCH_PARTIAL
516 : };
517 :
518 : static enum mech_name_match
519 0 : match_mech_name(const char *gm_mech_name,
520 : const char *name,
521 : size_t namelen)
522 : {
523 0 : if (gm_mech_name == NULL)
524 0 : return MATCH_NONE;
525 0 : else if (strcasecmp(gm_mech_name, name) == 0)
526 0 : return MATCH_COMPLETE;
527 0 : else if (strncasecmp(gm_mech_name, name, namelen) == 0)
528 0 : return MATCH_PARTIAL;
529 : else
530 0 : return MATCH_NONE;
531 : }
532 :
533 : /*
534 : * Return an OID for a built-in or dynamically loaded mechanism. For
535 : * API compatibility with previous versions, we treat "Kerberos 5"
536 : * as an alias for "krb5". Unique partial matches are supported.
537 : */
538 : GSSAPI_LIB_FUNCTION gss_OID GSSAPI_CALLCONV
539 0 : gss_name_to_oid(const char *name)
540 : {
541 0 : struct _gss_mech_switch *m, *partial = NULL;
542 0 : gss_OID oid = GSS_C_NO_OID;
543 0 : size_t namelen = strlen(name);
544 :
545 0 : if (isdigit((unsigned char)name[0]) &&
546 0 : _gss_string_to_oid(name, &oid) == 0)
547 0 : return oid;
548 :
549 0 : _gss_load_mech();
550 0 : HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
551 0 : enum mech_name_match match;
552 :
553 0 : match = match_mech_name(m->gm_mech.gm_name, name, namelen);
554 0 : if (match == MATCH_NONE &&
555 0 : gss_oid_equal(m->gm_mech_oid, GSS_KRB5_MECHANISM))
556 0 : match = match_mech_name("Kerberos 5", name, namelen);
557 :
558 0 : if (match == MATCH_COMPLETE)
559 0 : return m->gm_mech_oid;
560 0 : else if (match == MATCH_PARTIAL) {
561 0 : if (partial)
562 0 : return NULL;
563 : else
564 0 : partial = m;
565 : }
566 : }
567 :
568 0 : if (partial)
569 0 : return partial->gm_mech_oid;
570 :
571 0 : return NULL;
572 : }
573 :
574 : GSSAPI_LIB_FUNCTION const char * GSSAPI_LIB_CALL
575 0 : gss_oid_to_name(gss_const_OID oid)
576 : {
577 0 : struct _gss_mech_switch *m;
578 :
579 0 : _gss_load_mech();
580 0 : HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
581 0 : if (gss_oid_equal(m->gm_mech_oid, oid))
582 0 : return m->gm_mech.gm_name;
583 : }
584 :
585 0 : return NULL;
586 : }
|