1226031Sstas/*- 2226031Sstas * Copyright (c) 2005 Doug Rabson 3226031Sstas * All rights reserved. 4226031Sstas * 5226031Sstas * Redistribution and use in source and binary forms, with or without 6226031Sstas * modification, are permitted provided that the following conditions 7226031Sstas * are met: 8226031Sstas * 1. Redistributions of source code must retain the above copyright 9226031Sstas * notice, this list of conditions and the following disclaimer. 10226031Sstas * 2. Redistributions in binary form must reproduce the above copyright 11226031Sstas * notice, this list of conditions and the following disclaimer in the 12226031Sstas * documentation and/or other materials provided with the distribution. 13226031Sstas * 14226031Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17226031Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24226031Sstas * SUCH DAMAGE. 25226031Sstas * 26226031Sstas * $FreeBSD: src/lib/libgssapi/gss_mech_switch.c,v 1.2 2006/02/04 09:40:21 dfr Exp $ 27226031Sstas */ 28226031Sstas 29226031Sstas#include "mech_locl.h" 30226031Sstas#include <heim_threads.h> 31226031Sstas 32226031Sstas#ifndef _PATH_GSS_MECH 33226031Sstas#define _PATH_GSS_MECH "/etc/gss/mech" 34226031Sstas#endif 35226031Sstas 36226031Sstasstruct _gss_mech_switch_list _gss_mechs = { NULL } ; 37226031Sstasgss_OID_set _gss_mech_oids; 38226031Sstasstatic HEIMDAL_MUTEX _gss_mech_mutex = HEIMDAL_MUTEX_INITIALIZER; 39226031Sstas 40226031Sstas/* 41226031Sstas * Convert a string containing an OID in 'dot' form 42226031Sstas * (e.g. 1.2.840.113554.1.2.2) to a gss_OID. 43226031Sstas */ 44226031Sstasstatic int 45226031Sstas_gss_string_to_oid(const char* s, gss_OID oid) 46226031Sstas{ 47226031Sstas int number_count, i, j; 48226031Sstas size_t byte_count; 49226031Sstas const char *p, *q; 50226031Sstas char *res; 51226031Sstas 52226031Sstas oid->length = 0; 53226031Sstas oid->elements = NULL; 54226031Sstas 55226031Sstas /* 56226031Sstas * First figure out how many numbers in the oid, then 57226031Sstas * calculate the compiled oid size. 58226031Sstas */ 59226031Sstas number_count = 0; 60226031Sstas for (p = s; p; p = q) { 61226031Sstas q = strchr(p, '.'); 62226031Sstas if (q) q = q + 1; 63226031Sstas number_count++; 64226031Sstas } 65226031Sstas 66226031Sstas /* 67226031Sstas * The first two numbers are in the first byte and each 68226031Sstas * subsequent number is encoded in a variable byte sequence. 69226031Sstas */ 70226031Sstas if (number_count < 2) 71226031Sstas return (EINVAL); 72226031Sstas 73226031Sstas /* 74226031Sstas * We do this in two passes. The first pass, we just figure 75226031Sstas * out the size. Second time around, we actually encode the 76226031Sstas * number. 77226031Sstas */ 78226031Sstas res = 0; 79226031Sstas for (i = 0; i < 2; i++) { 80226031Sstas byte_count = 0; 81226031Sstas for (p = s, j = 0; p; p = q, j++) { 82226031Sstas unsigned int number = 0; 83226031Sstas 84226031Sstas /* 85226031Sstas * Find the end of this number. 86226031Sstas */ 87226031Sstas q = strchr(p, '.'); 88226031Sstas if (q) q = q + 1; 89226031Sstas 90226031Sstas /* 91226031Sstas * Read the number of of the string. Don't 92226031Sstas * bother with anything except base ten. 93226031Sstas */ 94226031Sstas while (*p && *p != '.') { 95226031Sstas number = 10 * number + (*p - '0'); 96226031Sstas p++; 97226031Sstas } 98226031Sstas 99226031Sstas /* 100226031Sstas * Encode the number. The first two numbers 101226031Sstas * are packed into the first byte. Subsequent 102226031Sstas * numbers are encoded in bytes seven bits at 103226031Sstas * a time with the last byte having the high 104226031Sstas * bit set. 105226031Sstas */ 106226031Sstas if (j == 0) { 107226031Sstas if (res) 108226031Sstas *res = number * 40; 109226031Sstas } else if (j == 1) { 110226031Sstas if (res) { 111226031Sstas *res += number; 112226031Sstas res++; 113226031Sstas } 114226031Sstas byte_count++; 115226031Sstas } else if (j >= 2) { 116226031Sstas /* 117226031Sstas * The number is encoded in seven bit chunks. 118226031Sstas */ 119226031Sstas unsigned int t; 120226031Sstas unsigned int bytes; 121226031Sstas 122226031Sstas bytes = 0; 123226031Sstas for (t = number; t; t >>= 7) 124226031Sstas bytes++; 125226031Sstas if (bytes == 0) bytes = 1; 126226031Sstas while (bytes) { 127226031Sstas if (res) { 128226031Sstas int bit = 7*(bytes-1); 129226031Sstas 130226031Sstas *res = (number >> bit) & 0x7f; 131226031Sstas if (bytes != 1) 132226031Sstas *res |= 0x80; 133226031Sstas res++; 134226031Sstas } 135226031Sstas byte_count++; 136226031Sstas bytes--; 137226031Sstas } 138226031Sstas } 139226031Sstas } 140226031Sstas if (!res) { 141226031Sstas res = malloc(byte_count); 142226031Sstas if (!res) 143226031Sstas return (ENOMEM); 144226031Sstas oid->length = byte_count; 145226031Sstas oid->elements = res; 146226031Sstas } 147226031Sstas } 148226031Sstas 149226031Sstas return (0); 150226031Sstas} 151226031Sstas 152226031Sstas#define SYM(name) \ 153226031Sstasdo { \ 154226031Sstas m->gm_mech.gm_ ## name = dlsym(so, "gss_" #name); \ 155226031Sstas if (!m->gm_mech.gm_ ## name || \ 156226031Sstas m->gm_mech.gm_ ##name == gss_ ## name) { \ 157226031Sstas fprintf(stderr, "can't find symbol gss_" #name "\n"); \ 158226031Sstas goto bad; \ 159226031Sstas } \ 160226031Sstas} while (0) 161226031Sstas 162226031Sstas#define OPTSYM(name) \ 163226031Sstasdo { \ 164226031Sstas m->gm_mech.gm_ ## name = dlsym(so, "gss_" #name); \ 165226031Sstas if (m->gm_mech.gm_ ## name == gss_ ## name) \ 166226031Sstas m->gm_mech.gm_ ## name = NULL; \ 167226031Sstas} while (0) 168226031Sstas 169226031Sstas#define OPTSPISYM(name) \ 170226031Sstasdo { \ 171226031Sstas m->gm_mech.gm_ ## name = dlsym(so, "gssspi_" #name); \ 172226031Sstas} while (0) 173226031Sstas 174226031Sstas#define COMPATSYM(name) \ 175226031Sstasdo { \ 176226031Sstas m->gm_mech.gm_compat->gmc_ ## name = dlsym(so, "gss_" #name); \ 177226031Sstas if (m->gm_mech.gm_compat->gmc_ ## name == gss_ ## name) \ 178226031Sstas m->gm_mech.gm_compat->gmc_ ## name = NULL; \ 179226031Sstas} while (0) 180226031Sstas 181226031Sstas#define COMPATSPISYM(name) \ 182226031Sstasdo { \ 183226031Sstas m->gm_mech.gm_compat->gmc_ ## name = dlsym(so, "gssspi_" #name);\ 184226031Sstas if (m->gm_mech.gm_compat->gmc_ ## name == gss_ ## name) \ 185226031Sstas m->gm_mech.gm_compat->gmc_ ## name = NULL; \ 186226031Sstas} while (0) 187226031Sstas 188226031Sstas/* 189226031Sstas * 190226031Sstas */ 191226031Sstasstatic int 192226031Sstasadd_builtin(gssapi_mech_interface mech) 193226031Sstas{ 194226031Sstas struct _gss_mech_switch *m; 195226031Sstas OM_uint32 minor_status; 196226031Sstas 197226031Sstas /* not registering any mech is ok */ 198226031Sstas if (mech == NULL) 199226031Sstas return 0; 200226031Sstas 201226031Sstas m = calloc(1, sizeof(*m)); 202226031Sstas if (m == NULL) 203226031Sstas return ENOMEM; 204226031Sstas m->gm_so = NULL; 205226031Sstas m->gm_mech = *mech; 206226031Sstas m->gm_mech_oid = mech->gm_mech_oid; /* XXX */ 207226031Sstas gss_add_oid_set_member(&minor_status, 208226031Sstas &m->gm_mech.gm_mech_oid, &_gss_mech_oids); 209226031Sstas 210226031Sstas /* pick up the oid sets of names */ 211226031Sstas 212226031Sstas if (m->gm_mech.gm_inquire_names_for_mech) 213226031Sstas (*m->gm_mech.gm_inquire_names_for_mech)(&minor_status, 214226031Sstas &m->gm_mech.gm_mech_oid, &m->gm_name_types); 215226031Sstas 216226031Sstas if (m->gm_name_types == NULL) 217226031Sstas gss_create_empty_oid_set(&minor_status, &m->gm_name_types); 218226031Sstas 219226031Sstas HEIM_SLIST_INSERT_HEAD(&_gss_mechs, m, gm_link); 220226031Sstas return 0; 221226031Sstas} 222226031Sstas 223226031Sstas/* 224226031Sstas * Load the mechanisms file (/etc/gss/mech). 225226031Sstas */ 226226031Sstasvoid 227226031Sstas_gss_load_mech(void) 228226031Sstas{ 229226031Sstas OM_uint32 major_status, minor_status; 230226031Sstas FILE *fp; 231226031Sstas char buf[256]; 232226031Sstas char *p; 233226031Sstas char *name, *oid, *lib, *kobj; 234226031Sstas struct _gss_mech_switch *m; 235226031Sstas void *so; 236226031Sstas gss_OID_desc mech_oid; 237226031Sstas int found; 238226031Sstas 239226031Sstas 240226031Sstas HEIMDAL_MUTEX_lock(&_gss_mech_mutex); 241226031Sstas 242226031Sstas if (HEIM_SLIST_FIRST(&_gss_mechs)) { 243226031Sstas HEIMDAL_MUTEX_unlock(&_gss_mech_mutex); 244226031Sstas return; 245226031Sstas } 246226031Sstas 247226031Sstas major_status = gss_create_empty_oid_set(&minor_status, 248226031Sstas &_gss_mech_oids); 249226031Sstas if (major_status) { 250226031Sstas HEIMDAL_MUTEX_unlock(&_gss_mech_mutex); 251226031Sstas return; 252226031Sstas } 253226031Sstas 254226031Sstas add_builtin(__gss_krb5_initialize()); 255226031Sstas add_builtin(__gss_spnego_initialize()); 256226031Sstas add_builtin(__gss_ntlm_initialize()); 257226031Sstas 258226031Sstas#ifdef HAVE_DLOPEN 259226031Sstas fp = fopen(_PATH_GSS_MECH, "r"); 260226031Sstas if (!fp) { 261226031Sstas HEIMDAL_MUTEX_unlock(&_gss_mech_mutex); 262226031Sstas return; 263226031Sstas } 264226031Sstas rk_cloexec_file(fp); 265226031Sstas 266226031Sstas while (fgets(buf, sizeof(buf), fp)) { 267226031Sstas _gss_mo_init *mi; 268226031Sstas 269226031Sstas if (*buf == '#') 270226031Sstas continue; 271226031Sstas p = buf; 272226031Sstas name = strsep(&p, "\t\n "); 273226031Sstas if (p) while (isspace((unsigned char)*p)) p++; 274226031Sstas oid = strsep(&p, "\t\n "); 275226031Sstas if (p) while (isspace((unsigned char)*p)) p++; 276226031Sstas lib = strsep(&p, "\t\n "); 277226031Sstas if (p) while (isspace((unsigned char)*p)) p++; 278226031Sstas kobj = strsep(&p, "\t\n "); 279226031Sstas if (!name || !oid || !lib || !kobj) 280226031Sstas continue; 281226031Sstas 282226031Sstas if (_gss_string_to_oid(oid, &mech_oid)) 283226031Sstas continue; 284226031Sstas 285226031Sstas /* 286226031Sstas * Check for duplicates, already loaded mechs. 287226031Sstas */ 288226031Sstas found = 0; 289226031Sstas HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) { 290226031Sstas if (gss_oid_equal(&m->gm_mech.gm_mech_oid, &mech_oid)) { 291226031Sstas found = 1; 292226031Sstas free(mech_oid.elements); 293226031Sstas break; 294226031Sstas } 295226031Sstas } 296226031Sstas if (found) 297226031Sstas continue; 298226031Sstas 299226031Sstas#ifndef RTLD_LOCAL 300226031Sstas#define RTLD_LOCAL 0 301226031Sstas#endif 302226031Sstas 303226031Sstas#ifndef RTLD_GROUP 304226031Sstas#define RTLD_GROUP 0 305226031Sstas#endif 306226031Sstas 307226031Sstas so = dlopen(lib, RTLD_LAZY | RTLD_LOCAL | RTLD_GROUP); 308226031Sstas if (so == NULL) { 309226031Sstas/* fprintf(stderr, "dlopen: %s\n", dlerror()); */ 310226031Sstas goto bad; 311226031Sstas } 312226031Sstas 313226031Sstas m = calloc(1, sizeof(*m)); 314226031Sstas if (m == NULL) 315226031Sstas goto bad; 316226031Sstas 317226031Sstas m->gm_so = so; 318226031Sstas m->gm_mech.gm_mech_oid = mech_oid; 319226031Sstas m->gm_mech.gm_flags = 0; 320226031Sstas m->gm_mech.gm_compat = calloc(1, sizeof(struct gss_mech_compat_desc_struct)); 321226031Sstas if (m->gm_mech.gm_compat == NULL) 322226031Sstas goto bad; 323226031Sstas 324226031Sstas major_status = gss_add_oid_set_member(&minor_status, 325226031Sstas &m->gm_mech.gm_mech_oid, &_gss_mech_oids); 326226031Sstas if (GSS_ERROR(major_status)) 327226031Sstas goto bad; 328226031Sstas 329226031Sstas SYM(acquire_cred); 330226031Sstas SYM(release_cred); 331226031Sstas SYM(init_sec_context); 332226031Sstas SYM(accept_sec_context); 333226031Sstas SYM(process_context_token); 334226031Sstas SYM(delete_sec_context); 335226031Sstas SYM(context_time); 336226031Sstas SYM(get_mic); 337226031Sstas SYM(verify_mic); 338226031Sstas SYM(wrap); 339226031Sstas SYM(unwrap); 340226031Sstas SYM(display_status); 341226031Sstas SYM(indicate_mechs); 342226031Sstas SYM(compare_name); 343226031Sstas SYM(display_name); 344226031Sstas SYM(import_name); 345226031Sstas SYM(export_name); 346226031Sstas SYM(release_name); 347226031Sstas SYM(inquire_cred); 348226031Sstas SYM(inquire_context); 349226031Sstas SYM(wrap_size_limit); 350226031Sstas SYM(add_cred); 351226031Sstas SYM(inquire_cred_by_mech); 352226031Sstas SYM(export_sec_context); 353226031Sstas SYM(import_sec_context); 354226031Sstas SYM(inquire_names_for_mech); 355226031Sstas SYM(inquire_mechs_for_name); 356226031Sstas SYM(canonicalize_name); 357226031Sstas SYM(duplicate_name); 358226031Sstas OPTSYM(inquire_cred_by_oid); 359226031Sstas OPTSYM(inquire_sec_context_by_oid); 360226031Sstas OPTSYM(set_sec_context_option); 361226031Sstas OPTSPISYM(set_cred_option); 362226031Sstas OPTSYM(pseudo_random); 363226031Sstas OPTSYM(wrap_iov); 364226031Sstas OPTSYM(unwrap_iov); 365226031Sstas OPTSYM(wrap_iov_length); 366226031Sstas OPTSYM(store_cred); 367226031Sstas OPTSYM(export_cred); 368226031Sstas OPTSYM(import_cred); 369226031Sstas#if 0 370226031Sstas OPTSYM(acquire_cred_ext); 371226031Sstas OPTSYM(iter_creds); 372226031Sstas OPTSYM(destroy_cred); 373226031Sstas OPTSYM(cred_hold); 374226031Sstas OPTSYM(cred_unhold); 375226031Sstas OPTSYM(cred_label_get); 376226031Sstas OPTSYM(cred_label_set); 377226031Sstas#endif 378226031Sstas OPTSYM(display_name_ext); 379226031Sstas OPTSYM(inquire_name); 380226031Sstas OPTSYM(get_name_attribute); 381226031Sstas OPTSYM(set_name_attribute); 382226031Sstas OPTSYM(delete_name_attribute); 383226031Sstas OPTSYM(export_name_composite); 384226031Sstas OPTSYM(pname_to_uid); 385226031Sstas OPTSPISYM(authorize_localname); 386226031Sstas 387226031Sstas mi = dlsym(so, "gss_mo_init"); 388226031Sstas if (mi != NULL) { 389226031Sstas major_status = mi(&minor_status, &mech_oid, 390226031Sstas &m->gm_mech.gm_mo, &m->gm_mech.gm_mo_num); 391226031Sstas if (GSS_ERROR(major_status)) 392226031Sstas goto bad; 393226031Sstas } else { 394226031Sstas /* API-as-SPI compatibility */ 395226031Sstas COMPATSYM(inquire_saslname_for_mech); 396226031Sstas COMPATSYM(inquire_mech_for_saslname); 397226031Sstas COMPATSYM(inquire_attrs_for_mech); 398226031Sstas COMPATSPISYM(acquire_cred_with_password); 399226031Sstas } 400226031Sstas 401226031Sstas /* pick up the oid sets of names */ 402226031Sstas 403226031Sstas if (m->gm_mech.gm_inquire_names_for_mech) 404226031Sstas (*m->gm_mech.gm_inquire_names_for_mech)(&minor_status, 405226031Sstas &m->gm_mech.gm_mech_oid, &m->gm_name_types); 406226031Sstas 407226031Sstas if (m->gm_name_types == NULL) 408226031Sstas gss_create_empty_oid_set(&minor_status, &m->gm_name_types); 409226031Sstas 410226031Sstas HEIM_SLIST_INSERT_HEAD(&_gss_mechs, m, gm_link); 411226031Sstas continue; 412226031Sstas 413226031Sstas bad: 414226031Sstas if (m != NULL) { 415226031Sstas free(m->gm_mech.gm_compat); 416226031Sstas free(m->gm_mech.gm_mech_oid.elements); 417226031Sstas free(m); 418226031Sstas } 419226031Sstas dlclose(so); 420226031Sstas continue; 421226031Sstas } 422226031Sstas fclose(fp); 423226031Sstas#endif 424226031Sstas HEIMDAL_MUTEX_unlock(&_gss_mech_mutex); 425226031Sstas} 426226031Sstas 427226031Sstasgssapi_mech_interface 428226031Sstas__gss_get_mechanism(gss_const_OID mech) 429226031Sstas{ 430226031Sstas struct _gss_mech_switch *m; 431226031Sstas 432226031Sstas _gss_load_mech(); 433226031Sstas HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) { 434226031Sstas if (gss_oid_equal(&m->gm_mech.gm_mech_oid, mech)) 435226031Sstas return &m->gm_mech; 436226031Sstas } 437226031Sstas return NULL; 438226031Sstas} 439