LCOV - code coverage report
Current view: top level - source4/heimdal/lib/hdb - hdb.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 41 148 27.7 %
Date: 2021-09-23 10:06:22 Functions: 5 13 38.5 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
       3             :  * (Royal Institute of Technology, Stockholm, Sweden).
       4             :  * All rights reserved.
       5             :  *
       6             :  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
       7             :  *
       8             :  * Redistribution and use in source and binary forms, with or without
       9             :  * modification, are permitted provided that the following conditions
      10             :  * are met:
      11             :  *
      12             :  * 1. Redistributions of source code must retain the above copyright
      13             :  *    notice, this list of conditions and the following disclaimer.
      14             :  *
      15             :  * 2. Redistributions in binary form must reproduce the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer in the
      17             :  *    documentation and/or other materials provided with the distribution.
      18             :  *
      19             :  * 3. Neither the name of the Institute nor the names of its contributors
      20             :  *    may be used to endorse or promote products derived from this software
      21             :  *    without specific prior written permission.
      22             :  *
      23             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      24             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      25             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      26             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      27             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      28             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      29             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      30             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      31             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      32             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      33             :  * SUCH DAMAGE.
      34             :  */
      35             : 
      36             : #include "krb5_locl.h"
      37             : #include "hdb_locl.h"
      38             : 
      39             : #ifdef HAVE_DLFCN_H
      40             : #include <dlfcn.h>
      41             : #endif
      42             : 
      43             : /*! @mainpage Heimdal database backend library
      44             :  *
      45             :  * @section intro Introduction
      46             :  *
      47             :  * Heimdal libhdb library provides the backend support for Heimdal kdc
      48             :  * and kadmind. Its here where plugins for diffrent database engines
      49             :  * can be pluged in and extend support for here Heimdal get the
      50             :  * principal and policy data from.
      51             :  *
      52             :  * Example of Heimdal backend are:
      53             :  * - Berkeley DB 1.85
      54             :  * - Berkeley DB 3.0
      55             :  * - Berkeley DB 4.0
      56             :  * - New Berkeley DB
      57             :  * - LDAP
      58             :  *
      59             :  *
      60             :  * The project web page: http://www.h5l.org/
      61             :  *
      62             :  */
      63             : 
      64             : const int hdb_interface_version = HDB_INTERFACE_VERSION;
      65             : 
      66             : static struct hdb_method methods[] = {
      67             : #if HAVE_DB1 || HAVE_DB3
      68             :     { HDB_INTERFACE_VERSION, "db:",   hdb_db_create},
      69             : #endif
      70             : #if HAVE_DB1
      71             :     { HDB_INTERFACE_VERSION, "mit-db:",       hdb_mdb_create},
      72             : #endif
      73             : #if HAVE_NDBM
      74             :     { HDB_INTERFACE_VERSION, "ndbm:", hdb_ndbm_create},
      75             : #endif
      76             :     { HDB_INTERFACE_VERSION, "keytab:",       hdb_keytab_create},
      77             : #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
      78             :     { HDB_INTERFACE_VERSION, "ldap:", hdb_ldap_create},
      79             :     { HDB_INTERFACE_VERSION, "ldapi:",        hdb_ldapi_create},
      80             : #endif
      81             : #ifdef HAVE_SQLITE3
      82             :     { HDB_INTERFACE_VERSION, "sqlite:", hdb_sqlite_create},
      83             : #endif
      84             :     {0, NULL,   NULL}
      85             : };
      86             : 
      87             : #if HAVE_DB1 || HAVE_DB3
      88             : static struct hdb_method dbmetod =
      89             :     { HDB_INTERFACE_VERSION, "", hdb_db_create };
      90             : #elif defined(HAVE_NDBM)
      91             : static struct hdb_method dbmetod =
      92             :     { HDB_INTERFACE_VERSION, "", hdb_ndbm_create };
      93             : #endif
      94             : 
      95             : 
      96             : krb5_error_code
      97      535132 : hdb_next_enctype2key(krb5_context context,
      98             :                      const hdb_entry *e,
      99             :                      krb5_enctype enctype,
     100             :                      Key **key)
     101             : {
     102             :     Key *k;
     103             : 
     104     1286141 :     for (k = *key ? (*key) + 1 : e->keys.val;
     105      751009 :          k < e->keys.val + e->keys.len;
     106      215877 :          k++)
     107             :     {
     108      636906 :         if(k->key.keytype == enctype){
     109      421029 :             *key = k;
     110      421029 :             return 0;
     111             :         }
     112             :     }
     113      114103 :     krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
     114             :                            "No next enctype %d for hdb-entry",
     115             :                           (int)enctype);
     116      114103 :     return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
     117             : }
     118             : 
     119             : krb5_error_code
     120      356984 : hdb_enctype2key(krb5_context context,
     121             :                 hdb_entry *e,
     122             :                 krb5_enctype enctype,
     123             :                 Key **key)
     124             : {
     125      356984 :     *key = NULL;
     126      356984 :     return hdb_next_enctype2key(context, e, enctype, key);
     127             : }
     128             : 
     129             : void
     130           0 : hdb_free_key(Key *key)
     131             : {
     132           0 :     memset(key->key.keyvalue.data,
     133             :            0,
     134             :            key->key.keyvalue.length);
     135           0 :     free_Key(key);
     136           0 :     free(key);
     137           0 : }
     138             : 
     139             : 
     140             : krb5_error_code
     141           0 : hdb_lock(int fd, int operation)
     142             : {
     143           0 :     int i, code = 0;
     144             : 
     145           0 :     for(i = 0; i < 3; i++){
     146           0 :         code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
     147           0 :         if(code == 0 || errno != EWOULDBLOCK)
     148             :             break;
     149           0 :         sleep(1);
     150             :     }
     151           0 :     if(code == 0)
     152           0 :         return 0;
     153           0 :     if(errno == EWOULDBLOCK)
     154           0 :         return HDB_ERR_DB_INUSE;
     155           0 :     return HDB_ERR_CANT_LOCK_DB;
     156             : }
     157             : 
     158             : krb5_error_code
     159           0 : hdb_unlock(int fd)
     160             : {
     161             :     int code;
     162           0 :     code = flock(fd, LOCK_UN);
     163           0 :     if(code)
     164           0 :         return 4711 /* XXX */;
     165           0 :     return 0;
     166             : }
     167             : 
     168             : void
     169      244004 : hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
     170             : {
     171             :     Key *k;
     172             :     size_t i;
     173             : 
     174      244004 :     if (ent->free_entry)
     175      243721 :         (*ent->free_entry)(context, ent);
     176             : 
     177      921248 :     for(i = 0; i < ent->entry.keys.len; i++) {
     178      683370 :         k = &ent->entry.keys.val[i];
     179             : 
     180      701674 :         memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
     181             :     }
     182      244004 :     free_hdb_entry(&ent->entry);
     183      244004 : }
     184             : 
     185             : krb5_error_code
     186           0 : hdb_foreach(krb5_context context,
     187             :             HDB *db,
     188             :             unsigned flags,
     189             :             hdb_foreach_func_t func,
     190             :             void *data)
     191             : {
     192             :     krb5_error_code ret;
     193             :     hdb_entry_ex entry;
     194           0 :     ret = db->hdb_firstkey(context, db, flags, &entry);
     195           0 :     if (ret == 0)
     196           0 :         krb5_clear_error_message(context);
     197           0 :     while(ret == 0){
     198           0 :         ret = (*func)(context, db, &entry, data);
     199           0 :         hdb_free_entry(context, &entry);
     200           0 :         if(ret == 0)
     201           0 :             ret = db->hdb_nextkey(context, db, flags, &entry);
     202             :     }
     203           0 :     if(ret == HDB_ERR_NOENTRY)
     204           0 :         ret = 0;
     205           0 :     return ret;
     206             : }
     207             : 
     208             : krb5_error_code
     209           0 : hdb_check_db_format(krb5_context context, HDB *db)
     210             : {
     211             :     krb5_data tag;
     212             :     krb5_data version;
     213             :     krb5_error_code ret, ret2;
     214             :     unsigned ver;
     215             :     int foo;
     216             : 
     217           0 :     ret = db->hdb_lock(context, db, HDB_RLOCK);
     218           0 :     if (ret)
     219           0 :         return ret;
     220             : 
     221           0 :     tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
     222           0 :     tag.length = strlen(tag.data);
     223           0 :     ret = (*db->hdb__get)(context, db, tag, &version);
     224           0 :     ret2 = db->hdb_unlock(context, db);
     225           0 :     if(ret)
     226           0 :         return ret;
     227           0 :     if (ret2)
     228           0 :         return ret2;
     229           0 :     foo = sscanf(version.data, "%u", &ver);
     230           0 :     krb5_data_free (&version);
     231           0 :     if (foo != 1)
     232           0 :         return HDB_ERR_BADVERSION;
     233           0 :     if(ver != HDB_DB_FORMAT)
     234           0 :         return HDB_ERR_BADVERSION;
     235           0 :     return 0;
     236             : }
     237             : 
     238             : krb5_error_code
     239           0 : hdb_init_db(krb5_context context, HDB *db)
     240             : {
     241             :     krb5_error_code ret, ret2;
     242             :     krb5_data tag;
     243             :     krb5_data version;
     244             :     char ver[32];
     245             : 
     246           0 :     ret = hdb_check_db_format(context, db);
     247           0 :     if(ret != HDB_ERR_NOENTRY)
     248           0 :         return ret;
     249             : 
     250           0 :     ret = db->hdb_lock(context, db, HDB_WLOCK);
     251           0 :     if (ret)
     252           0 :         return ret;
     253             : 
     254           0 :     tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
     255           0 :     tag.length = strlen(tag.data);
     256           0 :     snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
     257           0 :     version.data = ver;
     258           0 :     version.length = strlen(version.data) + 1; /* zero terminated */
     259           0 :     ret = (*db->hdb__put)(context, db, 0, tag, version);
     260           0 :     ret2 = db->hdb_unlock(context, db);
     261           0 :     if (ret) {
     262           0 :         if (ret2)
     263           0 :             krb5_clear_error_message(context);
     264           0 :         return ret;
     265             :     }
     266           0 :     return ret2;
     267             : }
     268             : 
     269             : #ifdef HAVE_DLOPEN
     270             : 
     271             :  /*
     272             :  * Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so,
     273             :  * looking for the hdb_NAME_create symbol.
     274             :  */
     275             : 
     276             : static const struct hdb_method *
     277             : find_dynamic_method (krb5_context context,
     278             :                      const char *filename,
     279             :                      const char **rest)
     280             : {
     281             :     static struct hdb_method method;
     282             :     struct hdb_so_method *mso;
     283             :     char *prefix, *path, *symbol;
     284             :     const char *p;
     285             :     void *dl;
     286             :     size_t len;
     287             : 
     288             :     p = strchr(filename, ':');
     289             : 
     290             :     /* if no prefix, don't know what module to load, just ignore it */
     291             :     if (p == NULL)
     292             :         return NULL;
     293             : 
     294             :     len = p - filename;
     295             :     *rest = filename + len + 1;
     296             : 
     297             :     prefix = malloc(len + 1);
     298             :     if (prefix == NULL)
     299             :         krb5_errx(context, 1, "out of memory");
     300             :     strlcpy(prefix, filename, len + 1);
     301             : 
     302             :     if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1)
     303             :         krb5_errx(context, 1, "out of memory");
     304             : 
     305             : #ifndef RTLD_NOW
     306             : #define RTLD_NOW 0
     307             : #endif
     308             : #ifndef RTLD_GLOBAL
     309             : #define RTLD_GLOBAL 0
     310             : #endif
     311             : 
     312             :     dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
     313             :     if (dl == NULL) {
     314             :         krb5_warnx(context, "error trying to load dynamic module %s: %s\n",
     315             :                    path, dlerror());
     316             :         free(prefix);
     317             :         free(path);
     318             :         return NULL;
     319             :     }
     320             : 
     321             :     if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1)
     322             :         krb5_errx(context, 1, "out of memory");
     323             : 
     324             :     mso = (struct hdb_so_method *) dlsym(dl, symbol);
     325             :     if (mso == NULL) {
     326             :         krb5_warnx(context, "error finding symbol %s in %s: %s\n",
     327             :                    symbol, path, dlerror());
     328             :         dlclose(dl);
     329             :         free(symbol);
     330             :         free(prefix);
     331             :         free(path);
     332             :         return NULL;
     333             :     }
     334             :     free(path);
     335             :     free(symbol);
     336             : 
     337             :     if (mso->version != HDB_INTERFACE_VERSION) {
     338             :         krb5_warnx(context,
     339             :                    "error wrong version in shared module %s "
     340             :                    "version: %d should have been %d\n",
     341             :                    prefix, mso->version, HDB_INTERFACE_VERSION);
     342             :         dlclose(dl);
     343             :         free(prefix);
     344             :         return NULL;
     345             :     }
     346             : 
     347             :     if (mso->create == NULL) {
     348             :         krb5_errx(context, 1,
     349             :                   "no entry point function in shared mod %s ",
     350             :                    prefix);
     351             :         dlclose(dl);
     352             :         free(prefix);
     353             :         return NULL;
     354             :     }
     355             : 
     356             :     method.create = mso->create;
     357             :     method.prefix = prefix;
     358             : 
     359             :     return &method;
     360             : }
     361             : #endif /* HAVE_DLOPEN */
     362             : 
     363             : /*
     364             :  * find the relevant method for `filename', returning a pointer to the
     365             :  * rest in `rest'.
     366             :  * return NULL if there's no such method.
     367             :  */
     368             : 
     369             : static const struct hdb_method *
     370          34 : find_method (const char *filename, const char **rest)
     371             : {
     372             :     const struct hdb_method *h;
     373             : 
     374          68 :     for (h = methods; h->prefix != NULL; ++h) {
     375          34 :         if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
     376           0 :             *rest = filename + strlen(h->prefix);
     377           0 :             return h;
     378             :         }
     379             :     }
     380             : #if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM)
     381             :     if (strncmp(filename, "/", 1) == 0
     382             :         || strncmp(filename, "./", 2) == 0
     383             :         || strncmp(filename, "../", 3) == 0)
     384             :     {
     385             :         *rest = filename;
     386             :         return &dbmetod;
     387             :     }
     388             : #endif
     389             : 
     390          34 :     return NULL;
     391             : }
     392             : 
     393             : krb5_error_code
     394           0 : hdb_list_builtin(krb5_context context, char **list)
     395             : {
     396             :     const struct hdb_method *h;
     397           0 :     size_t len = 0;
     398           0 :     char *buf = NULL;
     399             : 
     400           0 :     for (h = methods; h->prefix != NULL; ++h) {
     401           0 :         if (h->prefix[0] == '\0')
     402           0 :             continue;
     403           0 :         len += strlen(h->prefix) + 2;
     404             :     }
     405             : 
     406           0 :     len += 1;
     407           0 :     buf = malloc(len);
     408           0 :     if (buf == NULL) {
     409           0 :         krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
     410           0 :         return ENOMEM;
     411             :     }
     412           0 :     buf[0] = '\0';
     413             : 
     414           0 :     for (h = methods; h->prefix != NULL; ++h) {
     415           0 :         if (h != methods)
     416           0 :             strlcat(buf, ", ", len);
     417           0 :         strlcat(buf, h->prefix, len);
     418             :     }
     419           0 :     *list = buf;
     420           0 :     return 0;
     421             : }
     422             : 
     423             : krb5_error_code
     424           0 : _hdb_keytab2hdb_entry(krb5_context context,
     425             :                       const krb5_keytab_entry *ktentry,
     426             :                       hdb_entry_ex *entry)
     427             : {
     428           0 :     entry->entry.kvno = ktentry->vno;
     429           0 :     entry->entry.created_by.time = ktentry->timestamp;
     430             : 
     431           0 :     entry->entry.keys.val = calloc(1, sizeof(entry->entry.keys.val[0]));
     432           0 :     if (entry->entry.keys.val == NULL)
     433           0 :         return ENOMEM;
     434           0 :     entry->entry.keys.len = 1;
     435             : 
     436           0 :     entry->entry.keys.val[0].mkvno = NULL;
     437           0 :     entry->entry.keys.val[0].salt = NULL;
     438             : 
     439           0 :     return krb5_copy_keyblock_contents(context,
     440             :                                        &ktentry->keyblock,
     441           0 :                                        &entry->entry.keys.val[0].key);
     442             : }
     443             : 
     444             : /**
     445             :  * Create a handle for a Kerberos database
     446             :  *
     447             :  * Create a handle for a Kerberos database backend specified by a
     448             :  * filename.  Doesn't create a file if its doesn't exists, you have to
     449             :  * use O_CREAT to tell the backend to create the file.
     450             :  */
     451             : 
     452             : krb5_error_code
     453          34 : hdb_create(krb5_context context, HDB **db, const char *filename)
     454             : {
     455             :     const struct hdb_method *h;
     456             :     const char *residual;
     457             :     krb5_error_code ret;
     458          34 :     struct krb5_plugin *list = NULL, *e;
     459             : 
     460          34 :     if(filename == NULL)
     461           0 :         filename = HDB_DEFAULT_DB;
     462          34 :     krb5_add_et_list(context, initialize_hdb_error_table_r);
     463          34 :     h = find_method (filename, &residual);
     464             : 
     465          34 :     if (h == NULL) {
     466          34 :             ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "hdb", &list);
     467          34 :             if(ret == 0 && list != NULL) {
     468          34 :                     for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
     469          34 :                             h = _krb5_plugin_get_symbol(e);
     470          34 :                             if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0
     471          34 :                                 && h->interface_version == HDB_INTERFACE_VERSION) {
     472          34 :                                     residual = filename + strlen(h->prefix);
     473          34 :                                     break;
     474             :                             }
     475             :                     }
     476          34 :                     if (e == NULL) {
     477           0 :                             h = NULL;
     478           0 :                             _krb5_plugin_free(list);
     479             :                     }
     480             :             }
     481             :     }
     482             : 
     483             : #ifdef HAVE_DLOPEN
     484             :     if (h == NULL)
     485             :         h = find_dynamic_method (context, filename, &residual);
     486             : #endif
     487          34 :     if (h == NULL)
     488           0 :         krb5_errx(context, 1, "No database support for %s", filename);
     489          34 :     return (*h->create)(context, db, residual);
     490             : }

Generated by: LCOV version 1.13