root/source4/heimdal/lib/hdb/hdb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hdb_next_enctype2key
  2. hdb_enctype2key
  3. hdb_free_key
  4. hdb_lock
  5. hdb_unlock
  6. hdb_free_entry
  7. hdb_foreach
  8. hdb_check_db_format
  9. hdb_init_db
  10. find_dynamic_method
  11. find_method
  12. hdb_list_builtin
  13. hdb_create

   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 #ifdef HAVE_CONFIG_H
  35 #include "config.h"
  36 #endif
  37 
  38 #include "krb5.h"
  39 #include "krb5_locl.h"
  40 #include "hdb_locl.h"
  41 RCSID("$Id$");
  42 
  43 #ifdef HAVE_DLFCN_H
  44 #include <dlfcn.h>
  45 #endif
  46 
  47 static struct hdb_method methods[] = {
  48 #if HAVE_DB1 || HAVE_DB3
  49     {HDB_INTERFACE_VERSION, "db:",      hdb_db_create},
  50 #endif
  51 #if HAVE_NDBM
  52     {HDB_INTERFACE_VERSION, "ndbm:",    hdb_ndbm_create},
  53 #endif
  54 #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
  55     {HDB_INTERFACE_VERSION, "ldap:",    hdb_ldap_create},
  56     {HDB_INTERFACE_VERSION, "ldapi:",   hdb_ldapi_create},
  57 #endif
  58     {0, NULL,   NULL}
  59 };
  60 
  61 #if HAVE_DB1 || HAVE_DB3
  62 static struct hdb_method dbmetod = {"", hdb_db_create };
  63 #elif defined(HAVE_NDBM)
  64 static struct hdb_method dbmetod = {"", hdb_ndbm_create };
  65 #endif
  66 
  67 
  68 krb5_error_code
  69 hdb_next_enctype2key(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
  70                      const hdb_entry *e,
  71                      krb5_enctype enctype,
  72                      Key **key)
  73 {
  74     Key *k;
  75 
  76     for (k = *key ? (*key) + 1 : e->keys.val;
  77          k < e->keys.val + e->keys.len;
  78          k++)
  79     {
  80         if(k->key.keytype == enctype){
  81             *key = k;
  82             return 0;
  83         }
  84     }
  85     krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
  86                            "No next enctype %d for hdb-entry",
  87                           (int)enctype);
  88     return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
  89 }
  90 
  91 krb5_error_code
  92 hdb_enctype2key(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
  93                 hdb_entry *e,
  94                 krb5_enctype enctype,
  95                 Key **key)
  96 {
  97     *key = NULL;
  98     return hdb_next_enctype2key(context, e, enctype, key);
  99 }
 100 
 101 void
 102 hdb_free_key(Key *key)
     /* [<][>][^][v][top][bottom][index][help] */
 103 {
 104     memset(key->key.keyvalue.data,
 105            0,
 106            key->key.keyvalue.length);
 107     free_Key(key);
 108     free(key);
 109 }
 110 
 111 
 112 krb5_error_code
 113 hdb_lock(int fd, int operation)
     /* [<][>][^][v][top][bottom][index][help] */
 114 {
 115     int i, code = 0;
 116 
 117     for(i = 0; i < 3; i++){
 118         code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
 119         if(code == 0 || errno != EWOULDBLOCK)
 120             break;
 121         sleep(1);
 122     }
 123     if(code == 0)
 124         return 0;
 125     if(errno == EWOULDBLOCK)
 126         return HDB_ERR_DB_INUSE;
 127     return HDB_ERR_CANT_LOCK_DB;
 128 }
 129 
 130 krb5_error_code
 131 hdb_unlock(int fd)
     /* [<][>][^][v][top][bottom][index][help] */
 132 {
 133     int code;
 134     code = flock(fd, LOCK_UN);
 135     if(code)
 136         return 4711 /* XXX */;
 137     return 0;
 138 }
 139 
 140 void
 141 hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
     /* [<][>][^][v][top][bottom][index][help] */
 142 {
 143     int i;
 144 
 145     if (ent->free_entry)
 146         (*ent->free_entry)(context, ent);
 147 
 148     for(i = 0; i < ent->entry.keys.len; ++i) {
 149         Key *k = &ent->entry.keys.val[i];
 150 
 151         memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
 152     }
 153     free_hdb_entry(&ent->entry);
 154 }
 155 
 156 krb5_error_code
 157 hdb_foreach(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 158             HDB *db,
 159             unsigned flags,
 160             hdb_foreach_func_t func,
 161             void *data)
 162 {
 163     krb5_error_code ret;
 164     hdb_entry_ex entry;
 165     ret = db->hdb_firstkey(context, db, flags, &entry);
 166     if (ret == 0)
 167         krb5_clear_error_message(context);
 168     while(ret == 0){
 169         ret = (*func)(context, db, &entry, data);
 170         hdb_free_entry(context, &entry);
 171         if(ret == 0)
 172             ret = db->hdb_nextkey(context, db, flags, &entry);
 173     }
 174     if(ret == HDB_ERR_NOENTRY)
 175         ret = 0;
 176     return ret;
 177 }
 178 
 179 krb5_error_code
 180 hdb_check_db_format(krb5_context context, HDB *db)
     /* [<][>][^][v][top][bottom][index][help] */
 181 {
 182     krb5_data tag;
 183     krb5_data version;
 184     krb5_error_code ret, ret2;
 185     unsigned ver;
 186     int foo;
 187 
 188     ret = db->hdb_lock(context, db, HDB_RLOCK);
 189     if (ret)
 190         return ret;
 191 
 192     tag.data = HDB_DB_FORMAT_ENTRY;
 193     tag.length = strlen(tag.data);
 194     ret = (*db->hdb__get)(context, db, tag, &version);
 195     ret2 = db->hdb_unlock(context, db);
 196     if(ret)
 197         return ret;
 198     if (ret2)
 199         return ret2;
 200     foo = sscanf(version.data, "%u", &ver);
 201     krb5_data_free (&version);
 202     if (foo != 1)
 203         return HDB_ERR_BADVERSION;
 204     if(ver != HDB_DB_FORMAT)
 205         return HDB_ERR_BADVERSION;
 206     return 0;
 207 }
 208 
 209 krb5_error_code
 210 hdb_init_db(krb5_context context, HDB *db)
     /* [<][>][^][v][top][bottom][index][help] */
 211 {
 212     krb5_error_code ret, ret2;
 213     krb5_data tag;
 214     krb5_data version;
 215     char ver[32];
 216 
 217     ret = hdb_check_db_format(context, db);
 218     if(ret != HDB_ERR_NOENTRY)
 219         return ret;
 220 
 221     ret = db->hdb_lock(context, db, HDB_WLOCK);
 222     if (ret)
 223         return ret;
 224 
 225     tag.data = HDB_DB_FORMAT_ENTRY;
 226     tag.length = strlen(tag.data);
 227     snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
 228     version.data = ver;
 229     version.length = strlen(version.data) + 1; /* zero terminated */
 230     ret = (*db->hdb__put)(context, db, 0, tag, version);
 231     ret2 = db->hdb_unlock(context, db);
 232     if (ret) {
 233         if (ret2)
 234             krb5_clear_error_message(context);
 235         return ret;
 236     }
 237     return ret2;
 238 }
 239 
 240 #ifdef HAVE_DLOPEN
 241 
 242  /*
 243  * Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so,
 244  * looking for the hdb_NAME_create symbol.
 245  */
 246 
 247 static const struct hdb_method *
 248 find_dynamic_method (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 249                      const char *filename,
 250                      const char **rest)
 251 {
 252     static struct hdb_method method;
 253     struct hdb_so_method *mso;
 254     char *prefix, *path, *symbol;
 255     const char *p;
 256     void *dl;
 257     size_t len;
 258 
 259     p = strchr(filename, ':');
 260 
 261     /* if no prefix, don't know what module to load, just ignore it */
 262     if (p == NULL)
 263         return NULL;
 264 
 265     len = p - filename;
 266     *rest = filename + len + 1;
 267 
 268     prefix = strndup(filename, len);
 269     if (prefix == NULL)
 270         krb5_errx(context, 1, "out of memory");
 271 
 272     if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1)
 273         krb5_errx(context, 1, "out of memory");
 274 
 275 #ifndef RTLD_NOW
 276 #define RTLD_NOW 0
 277 #endif
 278 #ifndef RTLD_GLOBAL
 279 #define RTLD_GLOBAL 0
 280 #endif
 281 
 282     dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
 283     if (dl == NULL) {
 284         krb5_warnx(context, "error trying to load dynamic module %s: %s\n",
 285                    path, dlerror());
 286         free(prefix);
 287         free(path);
 288         return NULL;
 289     }
 290 
 291     if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1)
 292         krb5_errx(context, 1, "out of memory");
 293         
 294     mso = dlsym(dl, symbol);
 295     if (mso == NULL) {
 296         krb5_warnx(context, "error finding symbol %s in %s: %s\n",
 297                    symbol, path, dlerror());
 298         dlclose(dl);
 299         free(symbol);
 300         free(prefix);
 301         free(path);
 302         return NULL;
 303     }
 304     free(path);
 305     free(symbol);
 306 
 307     if (mso->version != HDB_INTERFACE_VERSION) {
 308         krb5_warnx(context,
 309                    "error wrong version in shared module %s "
 310                    "version: %d should have been %d\n",
 311                    prefix, mso->version, HDB_INTERFACE_VERSION);
 312         dlclose(dl);
 313         free(prefix);
 314         return NULL;
 315     }
 316 
 317     if (mso->create == NULL) {
 318         krb5_errx(context, 1,
 319                   "no entry point function in shared mod %s ",
 320                    prefix);
 321         dlclose(dl);
 322         free(prefix);
 323         return NULL;
 324     }
 325 
 326     method.create = mso->create;
 327     method.prefix = prefix;
 328 
 329     return &method;
 330 }
 331 #endif /* HAVE_DLOPEN */
 332 
 333 /*
 334  * find the relevant method for `filename', returning a pointer to the
 335  * rest in `rest'.
 336  * return NULL if there's no such method.
 337  */
 338 
 339 static const struct hdb_method *
 340 find_method (const char *filename, const char **rest)
     /* [<][>][^][v][top][bottom][index][help] */
 341 {
 342     const struct hdb_method *h;
 343 
 344     for (h = methods; h->prefix != NULL; ++h) {
 345         if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
 346             *rest = filename + strlen(h->prefix);
 347             return h;
 348         }
 349     }
 350 #if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM)
 351     if (strncmp(filename, "/", 1) == 0
 352         || strncmp(filename, "./", 2) == 0
 353         || strncmp(filename, "../", 3) == 0)
 354     {
 355         *rest = filename;
 356         return &dbmetod;
 357     }
 358 #endif
 359 
 360     return NULL;
 361 }
 362 
 363 krb5_error_code
 364 hdb_list_builtin(krb5_context context, char **list)
     /* [<][>][^][v][top][bottom][index][help] */
 365 {
 366     const struct hdb_method *h;
 367     size_t len = 0;
 368     char *buf = NULL;
 369 
 370     for (h = methods; h->prefix != NULL; ++h) {
 371         if (h->prefix[0] == '\0')
 372             continue;
 373         len += strlen(h->prefix) + 2;
 374     }
 375 
 376     len += 1;
 377     buf = malloc(len);
 378     if (buf == NULL) {
 379         krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
 380         return ENOMEM;
 381     }
 382     buf[0] = '\0';
 383 
 384     for (h = methods; h->prefix != NULL; ++h) {
 385         if (h != methods)
 386             strlcat(buf, ", ", len);
 387         strlcat(buf, h->prefix, len);
 388     }
 389     *list = buf;
 390     return 0;
 391 }
 392 
 393 krb5_error_code
 394 hdb_create(krb5_context context, HDB **db, const char *filename)
     /* [<][>][^][v][top][bottom][index][help] */
 395 {
 396     const struct hdb_method *h;
 397     const char *residual;
 398     krb5_error_code ret;
 399     struct krb5_plugin *list = NULL, *e;
 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 
 406     if (h == NULL) {
 407             ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "hdb", &list);
 408             if(ret == 0 && list != NULL) {
 409                     for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
 410                             h = _krb5_plugin_get_symbol(e);
 411                             if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0
 412                                 && h->interface_version == HDB_INTERFACE_VERSION) {
 413                                     residual = filename + strlen(h->prefix);
 414                                     break;
 415                             }
 416                     }
 417                     if (e == NULL) {
 418                             h = NULL;
 419                             _krb5_plugin_free(list);
 420                     }
 421             }
 422     }
 423 
 424 #ifdef HAVE_DLOPEN
 425     if (h == NULL)
 426         h = find_dynamic_method (context, filename, &residual);
 427 #endif
 428     if (h == NULL)
 429         krb5_errx(context, 1, "No database support for %s", filename);
 430     return (*h->create)(context, db, residual);
 431 }

/* [<][>][^][v][top][bottom][index][help] */