172445Sassar/*
2178825Sdfr * Copyright (c) 1997 - 2006 Kungliga Tekniska H�gskolan
372445Sassar * (Royal Institute of Technology, Stockholm, Sweden).
472445Sassar * All rights reserved.
572445Sassar *
672445Sassar * Redistribution and use in source and binary forms, with or without
772445Sassar * modification, are permitted provided that the following conditions
872445Sassar * are met:
972445Sassar *
1072445Sassar * 1. Redistributions of source code must retain the above copyright
1172445Sassar *    notice, this list of conditions and the following disclaimer.
1272445Sassar *
1372445Sassar * 2. Redistributions in binary form must reproduce the above copyright
1472445Sassar *    notice, this list of conditions and the following disclaimer in the
1572445Sassar *    documentation and/or other materials provided with the distribution.
1672445Sassar *
1772445Sassar * 3. Neither the name of the Institute nor the names of its contributors
1872445Sassar *    may be used to endorse or promote products derived from this software
1972445Sassar *    without specific prior written permission.
2072445Sassar *
2172445Sassar * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2272445Sassar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2372445Sassar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2472445Sassar * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2572445Sassar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2672445Sassar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2772445Sassar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2872445Sassar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2972445Sassar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3072445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3172445Sassar * SUCH DAMAGE.
3272445Sassar */
3372445Sassar
3472445Sassar#include "hdb_locl.h"
3572445Sassar
36178825SdfrRCSID("$Id: db3.c 21610 2007-07-17 07:10:45Z lha $");
3772445Sassar
3890926Snectar#if HAVE_DB3
3990926Snectar
40127808Snectar#ifdef HAVE_DB4_DB_H
41127808Snectar#include <db4/db.h>
42127808Snectar#elif defined(HAVE_DB3_DB_H)
43127808Snectar#include <db3/db.h>
44127808Snectar#else
4590926Snectar#include <db.h>
46127808Snectar#endif
4790926Snectar
4872445Sassarstatic krb5_error_code
4972445SassarDB_close(krb5_context context, HDB *db)
5072445Sassar{
51178825Sdfr    DB *d = (DB*)db->hdb_db;
52178825Sdfr    DBC *dbcp = (DBC*)db->hdb_dbc;
5372445Sassar
54178825Sdfr    (*dbcp->c_close)(dbcp);
55178825Sdfr    db->hdb_dbc = 0;
56178825Sdfr    (*d->close)(d, 0);
5772445Sassar    return 0;
5872445Sassar}
5972445Sassar
6072445Sassarstatic krb5_error_code
6172445SassarDB_destroy(krb5_context context, HDB *db)
6272445Sassar{
6372445Sassar    krb5_error_code ret;
6472445Sassar
6572445Sassar    ret = hdb_clear_master_key (context, db);
66178825Sdfr    free(db->hdb_name);
6772445Sassar    free(db);
6872445Sassar    return ret;
6972445Sassar}
7072445Sassar
7172445Sassarstatic krb5_error_code
7272445SassarDB_lock(krb5_context context, HDB *db, int operation)
7372445Sassar{
74178825Sdfr    DB *d = (DB*)db->hdb_db;
7572445Sassar    int fd;
7672445Sassar    if ((*d->fd)(d, &fd))
7772445Sassar	return HDB_ERR_CANT_LOCK_DB;
7872445Sassar    return hdb_lock(fd, operation);
7972445Sassar}
8072445Sassar
8172445Sassarstatic krb5_error_code
8272445SassarDB_unlock(krb5_context context, HDB *db)
8372445Sassar{
84178825Sdfr    DB *d = (DB*)db->hdb_db;
8572445Sassar    int fd;
8672445Sassar    if ((*d->fd)(d, &fd))
8772445Sassar	return HDB_ERR_CANT_LOCK_DB;
8872445Sassar    return hdb_unlock(fd);
8972445Sassar}
9072445Sassar
9172445Sassar
9272445Sassarstatic krb5_error_code
9372445SassarDB_seq(krb5_context context, HDB *db,
94178825Sdfr       unsigned flags, hdb_entry_ex *entry, int flag)
9572445Sassar{
9672445Sassar    DBT key, value;
97178825Sdfr    DBC *dbcp = db->hdb_dbc;
9872445Sassar    krb5_data key_data, data;
9972445Sassar    int code;
10072445Sassar
10172445Sassar    memset(&key, 0, sizeof(DBT));
10272445Sassar    memset(&value, 0, sizeof(DBT));
103178825Sdfr    if ((*db->hdb_lock)(context, db, HDB_RLOCK))
10472445Sassar	return HDB_ERR_DB_INUSE;
105178825Sdfr    code = (*dbcp->c_get)(dbcp, &key, &value, flag);
106178825Sdfr    (*db->hdb_unlock)(context, db); /* XXX check value */
10772445Sassar    if (code == DB_NOTFOUND)
10872445Sassar	return HDB_ERR_NOENTRY;
10972445Sassar    if (code)
11072445Sassar	return code;
11172445Sassar
11272445Sassar    key_data.data = key.data;
11372445Sassar    key_data.length = key.size;
11472445Sassar    data.data = value.data;
11572445Sassar    data.length = value.size;
116178825Sdfr    memset(entry, 0, sizeof(*entry));
117178825Sdfr    if (hdb_value2entry(context, &data, &entry->entry))
11872445Sassar	return DB_seq(context, db, flags, entry, DB_NEXT);
119178825Sdfr    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
120178825Sdfr	code = hdb_unseal_keys (context, db, &entry->entry);
12172445Sassar	if (code)
12272445Sassar	    hdb_free_entry (context, entry);
12372445Sassar    }
124178825Sdfr    if (entry->entry.principal == NULL) {
125178825Sdfr	entry->entry.principal = malloc(sizeof(*entry->entry.principal));
126178825Sdfr	if (entry->entry.principal == NULL) {
12772445Sassar	    hdb_free_entry (context, entry);
12890926Snectar	    krb5_set_error_string(context, "malloc: out of memory");
12990926Snectar	    return ENOMEM;
13072445Sassar	} else {
131178825Sdfr	    hdb_key2principal(context, &key_data, entry->entry.principal);
13272445Sassar	}
13372445Sassar    }
13472445Sassar    return 0;
13572445Sassar}
13672445Sassar
13772445Sassar
13872445Sassarstatic krb5_error_code
139178825SdfrDB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
14072445Sassar{
14172445Sassar    return DB_seq(context, db, flags, entry, DB_FIRST);
14272445Sassar}
14372445Sassar
14472445Sassar
14572445Sassarstatic krb5_error_code
146178825SdfrDB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
14772445Sassar{
14872445Sassar    return DB_seq(context, db, flags, entry, DB_NEXT);
14972445Sassar}
15072445Sassar
15172445Sassarstatic krb5_error_code
15272445SassarDB_rename(krb5_context context, HDB *db, const char *new_name)
15372445Sassar{
15472445Sassar    int ret;
15572445Sassar    char *old, *new;
15672445Sassar
157178825Sdfr    asprintf(&old, "%s.db", db->hdb_name);
15872445Sassar    asprintf(&new, "%s.db", new_name);
15972445Sassar    ret = rename(old, new);
16072445Sassar    free(old);
16172445Sassar    free(new);
16272445Sassar    if(ret)
16372445Sassar	return errno;
16472445Sassar
165178825Sdfr    free(db->hdb_name);
166178825Sdfr    db->hdb_name = strdup(new_name);
16772445Sassar    return 0;
16872445Sassar}
16972445Sassar
17072445Sassarstatic krb5_error_code
17172445SassarDB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
17272445Sassar{
173178825Sdfr    DB *d = (DB*)db->hdb_db;
17472445Sassar    DBT k, v;
17572445Sassar    int code;
17672445Sassar
17772445Sassar    memset(&k, 0, sizeof(DBT));
17872445Sassar    memset(&v, 0, sizeof(DBT));
17972445Sassar    k.data = key.data;
18072445Sassar    k.size = key.length;
18172445Sassar    k.flags = 0;
182178825Sdfr    if ((code = (*db->hdb_lock)(context, db, HDB_RLOCK)))
18372445Sassar	return code;
184178825Sdfr    code = (*d->get)(d, NULL, &k, &v, 0);
185178825Sdfr    (*db->hdb_unlock)(context, db);
18672445Sassar    if(code == DB_NOTFOUND)
18772445Sassar	return HDB_ERR_NOENTRY;
18872445Sassar    if(code)
18972445Sassar	return code;
19072445Sassar
19172445Sassar    krb5_data_copy(reply, v.data, v.size);
19272445Sassar    return 0;
19372445Sassar}
19472445Sassar
19572445Sassarstatic krb5_error_code
19672445SassarDB__put(krb5_context context, HDB *db, int replace,
19772445Sassar	krb5_data key, krb5_data value)
19872445Sassar{
199178825Sdfr    DB *d = (DB*)db->hdb_db;
20072445Sassar    DBT k, v;
20172445Sassar    int code;
20272445Sassar
20372445Sassar    memset(&k, 0, sizeof(DBT));
20472445Sassar    memset(&v, 0, sizeof(DBT));
20572445Sassar    k.data = key.data;
20672445Sassar    k.size = key.length;
20772445Sassar    k.flags = 0;
20872445Sassar    v.data = value.data;
20972445Sassar    v.size = value.length;
21072445Sassar    v.flags = 0;
211178825Sdfr    if ((code = (*db->hdb_lock)(context, db, HDB_WLOCK)))
21272445Sassar	return code;
213178825Sdfr    code = (*d->put)(d, NULL, &k, &v, replace ? 0 : DB_NOOVERWRITE);
214178825Sdfr    (*db->hdb_unlock)(context, db);
21572445Sassar    if(code == DB_KEYEXIST)
21672445Sassar	return HDB_ERR_EXISTS;
21772445Sassar    if(code)
21872445Sassar	return errno;
21972445Sassar    return 0;
22072445Sassar}
22172445Sassar
22272445Sassarstatic krb5_error_code
22372445SassarDB__del(krb5_context context, HDB *db, krb5_data key)
22472445Sassar{
225178825Sdfr    DB *d = (DB*)db->hdb_db;
22672445Sassar    DBT k;
22772445Sassar    krb5_error_code code;
22872445Sassar    memset(&k, 0, sizeof(DBT));
22972445Sassar    k.data = key.data;
23072445Sassar    k.size = key.length;
23172445Sassar    k.flags = 0;
232178825Sdfr    code = (*db->hdb_lock)(context, db, HDB_WLOCK);
23372445Sassar    if(code)
23472445Sassar	return code;
235178825Sdfr    code = (*d->del)(d, NULL, &k, 0);
236178825Sdfr    (*db->hdb_unlock)(context, db);
23772445Sassar    if(code == DB_NOTFOUND)
23872445Sassar	return HDB_ERR_NOENTRY;
23972445Sassar    if(code)
24072445Sassar	return code;
24172445Sassar    return 0;
24272445Sassar}
24372445Sassar
24472445Sassarstatic krb5_error_code
24572445SassarDB_open(krb5_context context, HDB *db, int flags, mode_t mode)
24672445Sassar{
247178825Sdfr    DBC *dbc = NULL;
24872445Sassar    char *fn;
24972445Sassar    krb5_error_code ret;
25072445Sassar    DB *d;
25172445Sassar    int myflags = 0;
25272445Sassar
25372445Sassar    if (flags & O_CREAT)
25472445Sassar      myflags |= DB_CREATE;
25572445Sassar
25672445Sassar    if (flags & O_EXCL)
25772445Sassar      myflags |= DB_EXCL;
25872445Sassar
259178825Sdfr    if((flags & O_ACCMODE) == O_RDONLY)
26072445Sassar      myflags |= DB_RDONLY;
26172445Sassar
26272445Sassar    if (flags & O_TRUNC)
26372445Sassar      myflags |= DB_TRUNCATE;
26472445Sassar
265178825Sdfr    asprintf(&fn, "%s.db", db->hdb_name);
26690926Snectar    if (fn == NULL) {
26790926Snectar	krb5_set_error_string(context, "malloc: out of memory");
26872445Sassar	return ENOMEM;
26990926Snectar    }
27072445Sassar    db_create(&d, NULL, 0);
271178825Sdfr    db->hdb_db = d;
272178825Sdfr
273178825Sdfr#if (DB_VERSION_MAJOR >= 4) && (DB_VERSION_MINOR >= 1)
274178825Sdfr    ret = (*d->open)(db->hdb_db, NULL, fn, NULL, DB_BTREE, myflags, mode);
275127808Snectar#else
276178825Sdfr    ret = (*d->open)(db->hdb_db, fn, NULL, DB_BTREE, myflags, mode);
277127808Snectar#endif
278178825Sdfr
279178825Sdfr    if (ret == ENOENT) {
28072445Sassar	/* try to open without .db extension */
281178825Sdfr#if (DB_VERSION_MAJOR >= 4) && (DB_VERSION_MINOR >= 1)
282178825Sdfr	ret = (*d->open)(db->hdb_db, NULL, db->hdb_name, NULL, DB_BTREE,
283178825Sdfr			 myflags, mode);
284127808Snectar#else
285178825Sdfr	ret = (*d->open)(db->hdb_db, db->hdb_name, NULL, DB_BTREE,
286178825Sdfr			 myflags, mode);
287127808Snectar#endif
28872445Sassar    }
289178825Sdfr
290178825Sdfr    if (ret) {
291178825Sdfr	free(fn);
292178825Sdfr	krb5_set_error_string(context, "opening %s: %s",
293178825Sdfr			      db->hdb_name, strerror(ret));
294178825Sdfr	return ret;
295178825Sdfr    }
29672445Sassar    free(fn);
29772445Sassar
298178825Sdfr    ret = (*d->cursor)(d, NULL, &dbc, 0);
29990926Snectar    if (ret) {
30090926Snectar	krb5_set_error_string(context, "d->cursor: %s", strerror(ret));
30172445Sassar        return ret;
30290926Snectar    }
303178825Sdfr    db->hdb_dbc = dbc;
30472445Sassar
30572445Sassar    if((flags & O_ACCMODE) == O_RDONLY)
30672445Sassar	ret = hdb_check_db_format(context, db);
30772445Sassar    else
30872445Sassar	ret = hdb_init_db(context, db);
30972445Sassar    if(ret == HDB_ERR_NOENTRY)
31072445Sassar	return 0;
311178825Sdfr    if (ret) {
312178825Sdfr	DB_close(context, db);
313178825Sdfr	krb5_set_error_string(context, "hdb_open: failed %s database %s",
314178825Sdfr			      (flags & O_ACCMODE) == O_RDONLY ?
315178825Sdfr			      "checking format of" : "initialize",
316178825Sdfr			      db->hdb_name);
317178825Sdfr    }
318178825Sdfr
31972445Sassar    return ret;
32072445Sassar}
32172445Sassar
32272445Sassarkrb5_error_code
32372445Sassarhdb_db_create(krb5_context context, HDB **db,
32472445Sassar	      const char *filename)
32572445Sassar{
326178825Sdfr    *db = calloc(1, sizeof(**db));
32790926Snectar    if (*db == NULL) {
32890926Snectar	krb5_set_error_string(context, "malloc: out of memory");
32972445Sassar	return ENOMEM;
33090926Snectar    }
33172445Sassar
332178825Sdfr    (*db)->hdb_db = NULL;
333178825Sdfr    (*db)->hdb_name = strdup(filename);
334178825Sdfr    if ((*db)->hdb_name == NULL) {
33590926Snectar	krb5_set_error_string(context, "malloc: out of memory");
33690926Snectar	free(*db);
33790926Snectar	*db = NULL;
33890926Snectar	return ENOMEM;
33990926Snectar    }
340178825Sdfr    (*db)->hdb_master_key_set = 0;
341178825Sdfr    (*db)->hdb_openp = 0;
342178825Sdfr    (*db)->hdb_open  = DB_open;
343178825Sdfr    (*db)->hdb_close = DB_close;
344178825Sdfr    (*db)->hdb_fetch = _hdb_fetch;
345178825Sdfr    (*db)->hdb_store = _hdb_store;
346178825Sdfr    (*db)->hdb_remove = _hdb_remove;
347178825Sdfr    (*db)->hdb_firstkey = DB_firstkey;
348178825Sdfr    (*db)->hdb_nextkey= DB_nextkey;
349178825Sdfr    (*db)->hdb_lock = DB_lock;
350178825Sdfr    (*db)->hdb_unlock = DB_unlock;
351178825Sdfr    (*db)->hdb_rename = DB_rename;
352178825Sdfr    (*db)->hdb__get = DB__get;
353178825Sdfr    (*db)->hdb__put = DB__put;
354178825Sdfr    (*db)->hdb__del = DB__del;
355178825Sdfr    (*db)->hdb_destroy = DB_destroy;
35672445Sassar    return 0;
35772445Sassar}
35890926Snectar#endif /* HAVE_DB3 */
359