1 /* 2 * Copyright (c) 1997 - 2007 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "hdb_locl.h" 35 36 RCSID("$Id: hdb.c 20214 2007-02-09 21:51:10Z lha $"); 37 38 #ifdef HAVE_DLFCN_H 39 #include <dlfcn.h> 40 #endif 41 42 struct hdb_method { 43 const char *prefix; 44 krb5_error_code (*create)(krb5_context, HDB **, const char *filename); 45 }; 46 47 static struct hdb_method methods[] = { 48 #if HAVE_DB1 || HAVE_DB3 49 {"db:", hdb_db_create}, 50 #endif 51 #if HAVE_NDBM 52 {"ndbm:", hdb_ndbm_create}, 53 #endif 54 #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE) 55 {"ldap:", hdb_ldap_create}, 56 {"ldapi:", hdb_ldapi_create}, 57 #endif 58 #ifdef HAVE_LDB /* Used for integrated samba build */ 59 {"ldb:", hdb_ldb_create}, 60 #endif 61 {NULL, NULL} 62 }; 63 64 #if HAVE_DB1 || HAVE_DB3 65 static struct hdb_method dbmetod = {"", hdb_db_create }; 66 #elif defined(HAVE_NDBM) 67 static struct hdb_method dbmetod = {"", hdb_ndbm_create }; 68 #endif 69 70 71 krb5_error_code 72 hdb_next_enctype2key(krb5_context context, 73 const hdb_entry *e, 74 krb5_enctype enctype, 75 Key **key) 76 { 77 Key *k; 78 79 for (k = *key ? (*key) + 1 : e->keys.val; 80 k < e->keys.val + e->keys.len; 81 k++) 82 { 83 if(k->key.keytype == enctype){ 84 *key = k; 85 return 0; 86 } 87 } 88 krb5_set_error_string(context, "No next enctype %d for hdb-entry", 89 (int)enctype); 90 return KRB5_PROG_ETYPE_NOSUPP; /* XXX */ 91 } 92 93 krb5_error_code 94 hdb_enctype2key(krb5_context context, 95 hdb_entry *e, 96 krb5_enctype enctype, 97 Key **key) 98 { 99 *key = NULL; 100 return hdb_next_enctype2key(context, e, enctype, key); 101 } 102 103 void 104 hdb_free_key(Key *key) 105 { 106 memset(key->key.keyvalue.data, 107 0, 108 key->key.keyvalue.length); 109 free_Key(key); 110 free(key); 111 } 112 113 114 krb5_error_code 115 hdb_lock(int fd, int operation) 116 { 117 int i, code = 0; 118 119 for(i = 0; i < 3; i++){ 120 code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB); 121 if(code == 0 || errno != EWOULDBLOCK) 122 break; 123 sleep(1); 124 } 125 if(code == 0) 126 return 0; 127 if(errno == EWOULDBLOCK) 128 return HDB_ERR_DB_INUSE; 129 return HDB_ERR_CANT_LOCK_DB; 130 } 131 132 krb5_error_code 133 hdb_unlock(int fd) 134 { 135 int code; 136 code = flock(fd, LOCK_UN); 137 if(code) 138 return 4711 /* XXX */; 139 return 0; 140 } 141 142 void 143 hdb_free_entry(krb5_context context, hdb_entry_ex *ent) 144 { 145 int i; 146 147 if (ent->free_entry) 148 (*ent->free_entry)(context, ent); 149 150 for(i = 0; i < ent->entry.keys.len; ++i) { 151 Key *k = &ent->entry.keys.val[i]; 152 153 memset (k->key.keyvalue.data, 0, k->key.keyvalue.length); 154 } 155 free_hdb_entry(&ent->entry); 156 } 157 158 krb5_error_code 159 hdb_foreach(krb5_context context, 160 HDB *db, 161 unsigned flags, 162 hdb_foreach_func_t func, 163 void *data) 164 { 165 krb5_error_code ret; 166 hdb_entry_ex entry; 167 ret = db->hdb_firstkey(context, db, flags, &entry); 168 if (ret == 0) 169 krb5_clear_error_string(context); 170 while(ret == 0){ 171 ret = (*func)(context, db, &entry, data); 172 hdb_free_entry(context, &entry); 173 if(ret == 0) 174 ret = db->hdb_nextkey(context, db, flags, &entry); 175 } 176 if(ret == HDB_ERR_NOENTRY) 177 ret = 0; 178 return ret; 179 } 180 181 krb5_error_code 182 hdb_check_db_format(krb5_context context, HDB *db) 183 { 184 krb5_data tag; 185 krb5_data version; 186 krb5_error_code ret, ret2; 187 unsigned ver; 188 int foo; 189 190 ret = db->hdb_lock(context, db, HDB_RLOCK); 191 if (ret) 192 return ret; 193 194 tag.data = HDB_DB_FORMAT_ENTRY; 195 tag.length = strlen(tag.data); 196 ret = (*db->hdb__get)(context, db, tag, &version); 197 ret2 = db->hdb_unlock(context, db); 198 if(ret) 199 return ret; 200 if (ret2) 201 return ret2; 202 foo = sscanf(version.data, "%u", &ver); 203 krb5_data_free (&version); 204 if (foo != 1) 205 return HDB_ERR_BADVERSION; 206 if(ver != HDB_DB_FORMAT) 207 return HDB_ERR_BADVERSION; 208 return 0; 209 } 210 211 krb5_error_code 212 hdb_init_db(krb5_context context, HDB *db) 213 { 214 krb5_error_code ret, ret2; 215 krb5_data tag; 216 krb5_data version; 217 char ver[32]; 218 219 ret = hdb_check_db_format(context, db); 220 if(ret != HDB_ERR_NOENTRY) 221 return ret; 222 223 ret = db->hdb_lock(context, db, HDB_WLOCK); 224 if (ret) 225 return ret; 226 227 tag.data = HDB_DB_FORMAT_ENTRY; 228 tag.length = strlen(tag.data); 229 snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT); 230 version.data = ver; 231 version.length = strlen(version.data) + 1; /* zero terminated */ 232 ret = (*db->hdb__put)(context, db, 0, tag, version); 233 ret2 = db->hdb_unlock(context, db); 234 if (ret) { 235 if (ret2) 236 krb5_clear_error_string(context); 237 return ret; 238 } 239 return ret2; 240 } 241 242 #ifdef HAVE_DLOPEN 243 244 /* 245 * Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so, 246 * looking for the hdb_NAME_create symbol. 247 */ 248 249 static const struct hdb_method * 250 find_dynamic_method (krb5_context context, 251 const char *filename, 252 const char **rest) 253 { 254 static struct hdb_method method; 255 struct hdb_so_method *mso; 256 char *prefix, *path, *symbol; 257 const char *p; 258 void *dl; 259 size_t len; 260 261 p = strchr(filename, ':'); 262 263 /* if no prefix, don't know what module to load, just ignore it */ 264 if (p == NULL) 265 return NULL; 266 267 len = p - filename; 268 *rest = filename + len + 1; 269 270 prefix = strndup(filename, len); 271 if (prefix == NULL) 272 krb5_errx(context, 1, "out of memory"); 273 274 if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1) 275 krb5_errx(context, 1, "out of memory"); 276 277 #ifndef RTLD_NOW 278 #define RTLD_NOW 0 279 #endif 280 #ifndef RTLD_GLOBAL 281 #define RTLD_GLOBAL 0 282 #endif 283 284 dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL); 285 if (dl == NULL) { 286 krb5_warnx(context, "error trying to load dynamic module %s: %s\n", 287 path, dlerror()); 288 free(prefix); 289 free(path); 290 return NULL; 291 } 292 293 if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1) 294 krb5_errx(context, 1, "out of memory"); 295 296 mso = dlsym(dl, symbol); 297 if (mso == NULL) { 298 krb5_warnx(context, "error finding symbol %s in %s: %s\n", 299 symbol, path, dlerror()); 300 dlclose(dl); 301 free(symbol); 302 free(prefix); 303 free(path); 304 return NULL; 305 } 306 free(path); 307 free(symbol); 308 309 if (mso->version != HDB_INTERFACE_VERSION) { 310 krb5_warnx(context, 311 "error wrong version in shared module %s " 312 "version: %d should have been %d\n", 313 prefix, mso->version, HDB_INTERFACE_VERSION); 314 dlclose(dl); 315 free(prefix); 316 return NULL; 317 } 318 319 if (mso->create == NULL) { 320 krb5_errx(context, 1, 321 "no entry point function in shared mod %s ", 322 prefix); 323 dlclose(dl); 324 free(prefix); 325 return NULL; 326 } 327 328 method.create = mso->create; 329 method.prefix = prefix; 330 331 return &method; 332 } 333 #endif /* HAVE_DLOPEN */ 334 335 /* 336 * find the relevant method for `filename', returning a pointer to the 337 * rest in `rest'. 338 * return NULL if there's no such method. 339 */ 340 341 static const struct hdb_method * 342 find_method (const char *filename, const char **rest) 343 { 344 const struct hdb_method *h; 345 346 for (h = methods; h->prefix != NULL; ++h) { 347 if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) { 348 *rest = filename + strlen(h->prefix); 349 return h; 350 } 351 } 352 #if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM) 353 if (strncmp(filename, "/", 1) == 0 354 || strncmp(filename, "./", 2) == 0 355 || strncmp(filename, "../", 3) == 0) 356 { 357 *rest = filename; 358 return &dbmetod; 359 } 360 #endif 361 362 return NULL; 363 } 364 365 krb5_error_code 366 hdb_list_builtin(krb5_context context, char **list) 367 { 368 const struct hdb_method *h; 369 size_t len = 0; 370 char *buf = NULL; 371 372 for (h = methods; h->prefix != NULL; ++h) { 373 if (h->prefix[0] == '\0') 374 continue; 375 len += strlen(h->prefix) + 2; 376 } 377 378 len += 1; 379 buf = malloc(len); 380 if (buf == NULL) { 381 krb5_set_error_string(context, "malloc: out of memory"); 382 return ENOMEM; 383 } 384 buf[0] = '\0'; 385 386 for (h = methods; h->prefix != NULL; ++h) { 387 if (h != methods) 388 strlcat(buf, ", ", len); 389 strlcat(buf, h->prefix, len); 390 } 391 *list = buf; 392 return 0; 393 } 394 395 krb5_error_code 396 hdb_create(krb5_context context, HDB **db, const char *filename) 397 { 398 const struct hdb_method *h; 399 const char *residual; 400 401 if(filename == NULL) 402 filename = HDB_DEFAULT_DB; 403 krb5_add_et_list(context, initialize_hdb_error_table_r); 404 h = find_method (filename, &residual); 405 #ifdef HAVE_DLOPEN 406 if (h == NULL) 407 h = find_dynamic_method (context, filename, &residual); 408 #endif 409 if (h == NULL) 410 krb5_errx(context, 1, "No database support for %s", filename); 411 return (*h->create)(context, db, residual); 412 } 413