Home | History | Annotate | Line # | Download | only in hdb
hdb-mdb.c revision 1.1.1.1
      1 /*	$NetBSD: hdb-mdb.c,v 1.1.1.1 2017/01/28 20:46:43 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 - 2006 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * Copyright (c) 2011 - Howard Chu, Symas Corp.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  *
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  *
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * 3. Neither the name of the Institute nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #include "hdb_locl.h"
     38 
     39 #if HAVE_LMDB
     40 
     41 /* LMDB */
     42 
     43 #include <lmdb.h>
     44 
     45 #define	KILO	1024
     46 
     47 typedef struct mdb_info {
     48     MDB_env *e;
     49     MDB_txn *t;
     50     MDB_dbi d;
     51     MDB_cursor *c;
     52 } mdb_info;
     53 
     54 static krb5_error_code
     55 DB_close(krb5_context context, HDB *db)
     56 {
     57     mdb_info *mi = (mdb_info *)db->hdb_db;
     58 
     59     mdb_cursor_close(mi->c);
     60     mdb_txn_abort(mi->t);
     61     mdb_env_close(mi->e);
     62     mi->c = 0;
     63     mi->t = 0;
     64     mi->e = 0;
     65     return 0;
     66 }
     67 
     68 static krb5_error_code
     69 DB_destroy(krb5_context context, HDB *db)
     70 {
     71     krb5_error_code ret;
     72 
     73     ret = hdb_clear_master_key (context, db);
     74     free(db->hdb_name);
     75     free(db->hdb_db);
     76     free(db);
     77     return ret;
     78 }
     79 
     80 static krb5_error_code
     81 DB_lock(krb5_context context, HDB *db, int operation)
     82 {
     83     db->lock_count++;
     84     return 0;
     85 }
     86 
     87 static krb5_error_code
     88 DB_unlock(krb5_context context, HDB *db)
     89 {
     90     if (db->lock_count > 1) {
     91 	db->lock_count--;
     92 	return 0;
     93     }
     94     heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match");
     95     db->lock_count--;
     96     return 0;
     97 }
     98 
     99 
    100 static krb5_error_code
    101 DB_seq(krb5_context context, HDB *db,
    102        unsigned flags, hdb_entry_ex *entry, int flag)
    103 {
    104     mdb_info *mi = db->hdb_db;
    105     MDB_val key, value;
    106     krb5_data key_data, data;
    107     int code;
    108 
    109     key.mv_size = 0;
    110     value.mv_size = 0;
    111     code = mdb_cursor_get(mi->c, &key, &value, flag);
    112     if (code == MDB_NOTFOUND)
    113 	return HDB_ERR_NOENTRY;
    114     if (code)
    115 	return code;
    116 
    117     key_data.data = key.mv_data;
    118     key_data.length = key.mv_size;
    119     data.data = value.mv_data;
    120     data.length = value.mv_size;
    121     memset(entry, 0, sizeof(*entry));
    122     if (hdb_value2entry(context, &data, &entry->entry))
    123 	return DB_seq(context, db, flags, entry, MDB_NEXT);
    124     if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
    125 	code = hdb_unseal_keys (context, db, &entry->entry);
    126 	if (code)
    127 	    hdb_free_entry (context, entry);
    128     }
    129     if (entry->entry.principal == NULL) {
    130 	entry->entry.principal = malloc(sizeof(*entry->entry.principal));
    131 	if (entry->entry.principal == NULL) {
    132 	    hdb_free_entry (context, entry);
    133 	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
    134 	    return ENOMEM;
    135 	} else {
    136 	    hdb_key2principal(context, &key_data, entry->entry.principal);
    137 	}
    138     }
    139     return 0;
    140 }
    141 
    142 
    143 static krb5_error_code
    144 DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
    145 {
    146     mdb_info *mi = db->hdb_db;
    147     int code;
    148 
    149     /* Always start with a fresh cursor to pick up latest DB state */
    150     if (mi->t)
    151 	mdb_txn_abort(mi->t);
    152 
    153     code = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &mi->t);
    154     if (code)
    155 	return code;
    156 
    157     code = mdb_cursor_open(mi->t, mi->d, &mi->c);
    158     if (code)
    159 	return code;
    160 
    161     return DB_seq(context, db, flags, entry, MDB_FIRST);
    162 }
    163 
    164 
    165 static krb5_error_code
    166 DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
    167 {
    168     return DB_seq(context, db, flags, entry, MDB_NEXT);
    169 }
    170 
    171 static krb5_error_code
    172 DB_rename(krb5_context context, HDB *db, const char *new_name)
    173 {
    174     int ret;
    175     char *old, *new;
    176 
    177     if (strncmp(new_name, "mdb:", sizeof("mdb:") - 1) == 0)
    178         new_name += sizeof("mdb:") - 1;
    179     else if (strncmp(new_name, "lmdb:", sizeof("lmdb:") - 1) == 0)
    180         new_name += sizeof("lmdb:") - 1;
    181     if (asprintf(&old, "%s.mdb", db->hdb_name) == -1)
    182 		return ENOMEM;
    183     if (asprintf(&new, "%s.mdb", new_name) == -1) {
    184 		free(old);
    185 		return ENOMEM;
    186     }
    187     ret = rename(old, new);
    188     free(old);
    189     free(new);
    190     if(ret)
    191 	return errno;
    192 
    193     free(db->hdb_name);
    194     db->hdb_name = strdup(new_name);
    195     return 0;
    196 }
    197 
    198 static krb5_error_code
    199 DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
    200 {
    201     mdb_info *mi = (mdb_info*)db->hdb_db;
    202     MDB_txn *txn;
    203     MDB_val k, v;
    204     int code;
    205 
    206     k.mv_data = key.data;
    207     k.mv_size = key.length;
    208 
    209     code = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &txn);
    210     if (code)
    211 	return code;
    212 
    213     code = mdb_get(txn, mi->d, &k, &v);
    214     if (code == 0)
    215 	krb5_data_copy(reply, v.mv_data, v.mv_size);
    216     mdb_txn_abort(txn);
    217     if(code == MDB_NOTFOUND)
    218 	return HDB_ERR_NOENTRY;
    219     return code;
    220 }
    221 
    222 static krb5_error_code
    223 DB__put(krb5_context context, HDB *db, int replace,
    224 	krb5_data key, krb5_data value)
    225 {
    226     mdb_info *mi = (mdb_info*)db->hdb_db;
    227     MDB_txn *txn;
    228     MDB_val k, v;
    229     int code;
    230 
    231     k.mv_data = key.data;
    232     k.mv_size = key.length;
    233     v.mv_data = value.data;
    234     v.mv_size = value.length;
    235 
    236     code = mdb_txn_begin(mi->e, NULL, 0, &txn);
    237     if (code)
    238 	return code;
    239 
    240     code = mdb_put(txn, mi->d, &k, &v, replace ? 0 : MDB_NOOVERWRITE);
    241     if (code)
    242 	mdb_txn_abort(txn);
    243     else
    244 	code = mdb_txn_commit(txn);
    245     if(code == MDB_KEYEXIST)
    246 	return HDB_ERR_EXISTS;
    247     return code;
    248 }
    249 
    250 static krb5_error_code
    251 DB__del(krb5_context context, HDB *db, krb5_data key)
    252 {
    253     mdb_info *mi = (mdb_info*)db->hdb_db;
    254     MDB_txn *txn;
    255     MDB_val k;
    256     krb5_error_code code;
    257 
    258     k.mv_data = key.data;
    259     k.mv_size = key.length;
    260 
    261     code = mdb_txn_begin(mi->e, NULL, 0, &txn);
    262     if (code)
    263 	return code;
    264 
    265     code = mdb_del(txn, mi->d, &k, NULL);
    266     if (code)
    267 	mdb_txn_abort(txn);
    268     else
    269 	code = mdb_txn_commit(txn);
    270     if(code == MDB_NOTFOUND)
    271 	return HDB_ERR_NOENTRY;
    272     return code;
    273 }
    274 
    275 static krb5_error_code
    276 DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
    277 {
    278     mdb_info *mi = (mdb_info *)db->hdb_db;
    279     MDB_txn *txn;
    280     char *fn;
    281     krb5_error_code ret;
    282     int myflags = MDB_NOSUBDIR, tmp;
    283 
    284     if((flags & O_ACCMODE) == O_RDONLY)
    285       myflags |= MDB_RDONLY;
    286 
    287     if (asprintf(&fn, "%s.mdb", db->hdb_name) == -1)
    288 	return krb5_enomem(context);
    289     if (mdb_env_create(&mi->e)) {
    290 	free(fn);
    291 	return krb5_enomem(context);
    292     }
    293 
    294     tmp = krb5_config_get_int_default(context, NULL, 0, "kdc",
    295 	"hdb-mdb-maxreaders", NULL);
    296     if (tmp) {
    297 	ret = mdb_env_set_maxreaders(mi->e, tmp);
    298 	if (ret) {
    299             free(fn);
    300 	    krb5_set_error_message(context, ret, "setting maxreaders on %s: %s",
    301 		db->hdb_name, mdb_strerror(ret));
    302 	    return ret;
    303 	}
    304     }
    305 
    306     tmp = krb5_config_get_int_default(context, NULL, 0, "kdc",
    307 	"hdb-mdb-mapsize", NULL);
    308     if (tmp) {
    309 	size_t maps = tmp;
    310 	maps *= KILO;
    311 	ret = mdb_env_set_mapsize(mi->e, maps);
    312 	if (ret) {
    313             free(fn);
    314 	    krb5_set_error_message(context, ret, "setting mapsize on %s: %s",
    315 		db->hdb_name, mdb_strerror(ret));
    316 	    return ret;
    317 	}
    318     }
    319 
    320     ret = mdb_env_open(mi->e, fn, myflags, mode);
    321     free(fn);
    322     if (ret) {
    323 fail:
    324 	mdb_env_close(mi->e);
    325 	mi->e = 0;
    326 	krb5_set_error_message(context, ret, "opening %s: %s",
    327 			      db->hdb_name, mdb_strerror(ret));
    328 	return ret;
    329     }
    330 
    331     ret = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &txn);
    332     if (ret)
    333 	goto fail;
    334 
    335     ret = mdb_open(txn, NULL, 0, &mi->d);
    336     mdb_txn_abort(txn);
    337     if (ret)
    338 	goto fail;
    339 
    340     if((flags & O_ACCMODE) == O_RDONLY)
    341 	ret = hdb_check_db_format(context, db);
    342     else
    343 	ret = hdb_init_db(context, db);
    344     if(ret == HDB_ERR_NOENTRY)
    345 	return 0;
    346     if (ret) {
    347 	DB_close(context, db);
    348 	krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
    349 			       (flags & O_ACCMODE) == O_RDONLY ?
    350 			       "checking format of" : "initialize",
    351 			       db->hdb_name);
    352     }
    353 
    354     return ret;
    355 }
    356 
    357 krb5_error_code
    358 hdb_mdb_create(krb5_context context, HDB **db,
    359 	      const char *filename)
    360 {
    361     *db = calloc(1, sizeof(**db));
    362     if (*db == NULL) {
    363 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
    364 	return ENOMEM;
    365     }
    366 
    367     (*db)->hdb_db = calloc(1, sizeof(mdb_info));
    368     if ((*db)->hdb_db == NULL) {
    369 	free(*db);
    370 	*db = NULL;
    371 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
    372 	return ENOMEM;
    373     }
    374     (*db)->hdb_name = strdup(filename);
    375     if ((*db)->hdb_name == NULL) {
    376 	free((*db)->hdb_db);
    377 	free(*db);
    378 	*db = NULL;
    379 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
    380 	return ENOMEM;
    381     }
    382     (*db)->hdb_master_key_set = 0;
    383     (*db)->hdb_openp = 0;
    384     (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL;
    385     (*db)->hdb_open  = DB_open;
    386     (*db)->hdb_close = DB_close;
    387     (*db)->hdb_fetch_kvno = _hdb_fetch_kvno;
    388     (*db)->hdb_store = _hdb_store;
    389     (*db)->hdb_remove = _hdb_remove;
    390     (*db)->hdb_firstkey = DB_firstkey;
    391     (*db)->hdb_nextkey= DB_nextkey;
    392     (*db)->hdb_lock = DB_lock;
    393     (*db)->hdb_unlock = DB_unlock;
    394     (*db)->hdb_rename = DB_rename;
    395     (*db)->hdb__get = DB__get;
    396     (*db)->hdb__put = DB__put;
    397     (*db)->hdb__del = DB__del;
    398     (*db)->hdb_destroy = DB_destroy;
    399     return 0;
    400 }
    401 #endif /* HAVE_LMDB */
    402