155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2001 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "hdb_locl.h"
3555682Smarkm
3690926Snectar#if HAVE_DB1
3755682Smarkm
3890926Snectar#if defined(HAVE_DB_185_H)
3990926Snectar#include <db_185.h>
4090926Snectar#elif defined(HAVE_DB_H)
4190926Snectar#include <db.h>
4290926Snectar#endif
4390926Snectar
4455682Smarkmstatic krb5_error_code
4555682SmarkmDB_close(krb5_context context, HDB *db)
4655682Smarkm{
47178825Sdfr    DB *d = (DB*)db->hdb_db;
48178825Sdfr    (*d->close)(d);
4955682Smarkm    return 0;
5055682Smarkm}
5155682Smarkm
5255682Smarkmstatic krb5_error_code
5355682SmarkmDB_destroy(krb5_context context, HDB *db)
5455682Smarkm{
5555682Smarkm    krb5_error_code ret;
5655682Smarkm
5755682Smarkm    ret = hdb_clear_master_key (context, db);
58178825Sdfr    free(db->hdb_name);
5955682Smarkm    free(db);
6055682Smarkm    return ret;
6155682Smarkm}
6255682Smarkm
6355682Smarkmstatic krb5_error_code
6455682SmarkmDB_lock(krb5_context context, HDB *db, int operation)
6555682Smarkm{
66178825Sdfr    DB *d = (DB*)db->hdb_db;
6755682Smarkm    int fd = (*d->fd)(d);
68178825Sdfr    if(fd < 0) {
69233294Sstas	krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
70233294Sstas			       "Can't lock database: %s", db->hdb_name);
7155682Smarkm	return HDB_ERR_CANT_LOCK_DB;
72178825Sdfr    }
7355682Smarkm    return hdb_lock(fd, operation);
7455682Smarkm}
7555682Smarkm
7655682Smarkmstatic krb5_error_code
7755682SmarkmDB_unlock(krb5_context context, HDB *db)
7855682Smarkm{
79178825Sdfr    DB *d = (DB*)db->hdb_db;
8055682Smarkm    int fd = (*d->fd)(d);
81178825Sdfr    if(fd < 0) {
82233294Sstas	krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
83233294Sstas			       "Can't unlock database: %s", db->hdb_name);
8455682Smarkm	return HDB_ERR_CANT_LOCK_DB;
85178825Sdfr    }
8655682Smarkm    return hdb_unlock(fd);
8755682Smarkm}
8855682Smarkm
8955682Smarkm
9055682Smarkmstatic krb5_error_code
9155682SmarkmDB_seq(krb5_context context, HDB *db,
92178825Sdfr       unsigned flags, hdb_entry_ex *entry, int flag)
9355682Smarkm{
94178825Sdfr    DB *d = (DB*)db->hdb_db;
9555682Smarkm    DBT key, value;
9655682Smarkm    krb5_data key_data, data;
9755682Smarkm    int code;
9855682Smarkm
99178825Sdfr    code = db->hdb_lock(context, db, HDB_RLOCK);
100178825Sdfr    if(code == -1) {
101233294Sstas	krb5_set_error_message(context, HDB_ERR_DB_INUSE, "Database %s in use", db->hdb_name);
10255682Smarkm	return HDB_ERR_DB_INUSE;
103178825Sdfr    }
104178825Sdfr    code = (*d->seq)(d, &key, &value, flag);
105178825Sdfr    db->hdb_unlock(context, db); /* XXX check value */
106178825Sdfr    if(code == -1) {
107178825Sdfr	code = errno;
108233294Sstas	krb5_set_error_message(context, code, "Database %s seq error: %s",
109233294Sstas			       db->hdb_name, strerror(code));
110178825Sdfr	return code;
111178825Sdfr    }
112178825Sdfr    if(code == 1) {
113233294Sstas	krb5_clear_error_message(context);
11455682Smarkm	return HDB_ERR_NOENTRY;
115178825Sdfr    }
11655682Smarkm
11755682Smarkm    key_data.data = key.data;
11855682Smarkm    key_data.length = key.size;
11955682Smarkm    data.data = value.data;
12055682Smarkm    data.length = value.size;
121178825Sdfr    memset(entry, 0, sizeof(*entry));
122178825Sdfr    if (hdb_value2entry(context, &data, &entry->entry))
12355682Smarkm	return DB_seq(context, db, flags, entry, R_NEXT);
124178825Sdfr    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
125178825Sdfr	code = hdb_unseal_keys (context, db, &entry->entry);
12672445Sassar	if (code)
12772445Sassar	    hdb_free_entry (context, entry);
12872445Sassar    }
129178825Sdfr    if (code == 0 && entry->entry.principal == NULL) {
130178825Sdfr	entry->entry.principal = malloc(sizeof(*entry->entry.principal));
131178825Sdfr	if (entry->entry.principal == NULL) {
13272445Sassar	    code = ENOMEM;
133233294Sstas	    krb5_set_error_message(context, code, "malloc: out of memory");
13472445Sassar	    hdb_free_entry (context, entry);
13572445Sassar	} else {
136178825Sdfr	    hdb_key2principal(context, &key_data, entry->entry.principal);
13772445Sassar	}
13855682Smarkm    }
13972445Sassar    return code;
14055682Smarkm}
14155682Smarkm
14255682Smarkm
14355682Smarkmstatic krb5_error_code
144178825SdfrDB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
14555682Smarkm{
14655682Smarkm    return DB_seq(context, db, flags, entry, R_FIRST);
14755682Smarkm}
14855682Smarkm
14955682Smarkm
15055682Smarkmstatic krb5_error_code
151178825SdfrDB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
15255682Smarkm{
15355682Smarkm    return DB_seq(context, db, flags, entry, R_NEXT);
15455682Smarkm}
15555682Smarkm
15655682Smarkmstatic krb5_error_code
15755682SmarkmDB_rename(krb5_context context, HDB *db, const char *new_name)
15855682Smarkm{
15955682Smarkm    int ret;
16055682Smarkm    char *old, *new;
16155682Smarkm
162178825Sdfr    asprintf(&old, "%s.db", db->hdb_name);
16355682Smarkm    asprintf(&new, "%s.db", new_name);
16455682Smarkm    ret = rename(old, new);
16555682Smarkm    free(old);
16655682Smarkm    free(new);
16755682Smarkm    if(ret)
16855682Smarkm	return errno;
169233294Sstas
170178825Sdfr    free(db->hdb_name);
171178825Sdfr    db->hdb_name = strdup(new_name);
17255682Smarkm    return 0;
17355682Smarkm}
17455682Smarkm
17555682Smarkmstatic krb5_error_code
17655682SmarkmDB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
17755682Smarkm{
178178825Sdfr    DB *d = (DB*)db->hdb_db;
17955682Smarkm    DBT k, v;
18055682Smarkm    int code;
18155682Smarkm
18255682Smarkm    k.data = key.data;
18355682Smarkm    k.size = key.length;
184178825Sdfr    code = db->hdb_lock(context, db, HDB_RLOCK);
18555682Smarkm    if(code)
18655682Smarkm	return code;
187178825Sdfr    code = (*d->get)(d, &k, &v, 0);
188178825Sdfr    db->hdb_unlock(context, db);
189178825Sdfr    if(code < 0) {
190178825Sdfr	code = errno;
191233294Sstas	krb5_set_error_message(context, code, "Database %s get error: %s",
192233294Sstas			       db->hdb_name, strerror(code));
193178825Sdfr	return code;
194178825Sdfr    }
195178825Sdfr    if(code == 1) {
196233294Sstas	krb5_clear_error_message(context);
19755682Smarkm	return HDB_ERR_NOENTRY;
198178825Sdfr    }
199233294Sstas
20055682Smarkm    krb5_data_copy(reply, v.data, v.size);
20155682Smarkm    return 0;
20255682Smarkm}
20355682Smarkm
20455682Smarkmstatic krb5_error_code
205233294SstasDB__put(krb5_context context, HDB *db, int replace,
20655682Smarkm	krb5_data key, krb5_data value)
20755682Smarkm{
208178825Sdfr    DB *d = (DB*)db->hdb_db;
20955682Smarkm    DBT k, v;
21055682Smarkm    int code;
21155682Smarkm
21255682Smarkm    k.data = key.data;
21355682Smarkm    k.size = key.length;
21455682Smarkm    v.data = value.data;
21555682Smarkm    v.size = value.length;
216178825Sdfr    code = db->hdb_lock(context, db, HDB_WLOCK);
21755682Smarkm    if(code)
21855682Smarkm	return code;
219178825Sdfr    code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE);
220178825Sdfr    db->hdb_unlock(context, db);
221178825Sdfr    if(code < 0) {
222178825Sdfr	code = errno;
223233294Sstas	krb5_set_error_message(context, code, "Database %s put error: %s",
224233294Sstas			       db->hdb_name, strerror(code));
225178825Sdfr	return code;
226178825Sdfr    }
227178825Sdfr    if(code == 1) {
228233294Sstas	krb5_clear_error_message(context);
22955682Smarkm	return HDB_ERR_EXISTS;
230178825Sdfr    }
23155682Smarkm    return 0;
23255682Smarkm}
23355682Smarkm
23455682Smarkmstatic krb5_error_code
23555682SmarkmDB__del(krb5_context context, HDB *db, krb5_data key)
23655682Smarkm{
237178825Sdfr    DB *d = (DB*)db->hdb_db;
23855682Smarkm    DBT k;
23955682Smarkm    krb5_error_code code;
24055682Smarkm    k.data = key.data;
24155682Smarkm    k.size = key.length;
242178825Sdfr    code = db->hdb_lock(context, db, HDB_WLOCK);
24355682Smarkm    if(code)
24455682Smarkm	return code;
245178825Sdfr    code = (*d->del)(d, &k, 0);
246178825Sdfr    db->hdb_unlock(context, db);
247178825Sdfr    if(code == 1) {
248178825Sdfr	code = errno;
249233294Sstas	krb5_set_error_message(context, code, "Database %s put error: %s",
250233294Sstas			       db->hdb_name, strerror(code));
251178825Sdfr	return code;
252178825Sdfr    }
25355682Smarkm    if(code < 0)
25455682Smarkm	return errno;
25555682Smarkm    return 0;
25655682Smarkm}
25755682Smarkm
25855682Smarkmstatic krb5_error_code
25955682SmarkmDB_open(krb5_context context, HDB *db, int flags, mode_t mode)
26055682Smarkm{
26155682Smarkm    char *fn;
26255682Smarkm    krb5_error_code ret;
26355682Smarkm
264178825Sdfr    asprintf(&fn, "%s.db", db->hdb_name);
26590926Snectar    if (fn == NULL) {
266233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
26755682Smarkm	return ENOMEM;
26890926Snectar    }
269178825Sdfr    db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
27055682Smarkm    free(fn);
27155682Smarkm    /* try to open without .db extension */
272178825Sdfr    if(db->hdb_db == NULL && errno == ENOENT)
273178825Sdfr	db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL);
274178825Sdfr    if(db->hdb_db == NULL) {
27590926Snectar	ret = errno;
276233294Sstas	krb5_set_error_message(context, ret, "dbopen (%s): %s",
277178825Sdfr			      db->hdb_name, strerror(ret));
27890926Snectar	return ret;
27990926Snectar    }
28055682Smarkm    if((flags & O_ACCMODE) == O_RDONLY)
28155682Smarkm	ret = hdb_check_db_format(context, db);
28255682Smarkm    else
28355682Smarkm	ret = hdb_init_db(context, db);
28490926Snectar    if(ret == HDB_ERR_NOENTRY) {
285233294Sstas	krb5_clear_error_message(context);
28655682Smarkm	return 0;
28790926Snectar    }
288178825Sdfr    if (ret) {
289178825Sdfr	DB_close(context, db);
290233294Sstas	krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
291233294Sstas			      (flags & O_ACCMODE) == O_RDONLY ?
292233294Sstas			      "checking format of" : "initialize",
293178825Sdfr			      db->hdb_name);
294178825Sdfr    }
29555682Smarkm    return ret;
29655682Smarkm}
29755682Smarkm
29855682Smarkmkrb5_error_code
299233294Sstashdb_db_create(krb5_context context, HDB **db,
30055682Smarkm	      const char *filename)
30155682Smarkm{
302178825Sdfr    *db = calloc(1, sizeof(**db));
30390926Snectar    if (*db == NULL) {
304233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
30555682Smarkm	return ENOMEM;
30690926Snectar    }
30755682Smarkm
308178825Sdfr    (*db)->hdb_db = NULL;
309178825Sdfr    (*db)->hdb_name = strdup(filename);
310178825Sdfr    if ((*db)->hdb_name == NULL) {
31190926Snectar	free(*db);
31290926Snectar	*db = NULL;
313233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
31490926Snectar	return ENOMEM;
31590926Snectar    }
316178825Sdfr    (*db)->hdb_master_key_set = 0;
317178825Sdfr    (*db)->hdb_openp = 0;
318233294Sstas    (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL;
319178825Sdfr    (*db)->hdb_open = DB_open;
320178825Sdfr    (*db)->hdb_close = DB_close;
321233294Sstas    (*db)->hdb_fetch_kvno = _hdb_fetch_kvno;
322178825Sdfr    (*db)->hdb_store = _hdb_store;
323178825Sdfr    (*db)->hdb_remove = _hdb_remove;
324178825Sdfr    (*db)->hdb_firstkey = DB_firstkey;
325178825Sdfr    (*db)->hdb_nextkey= DB_nextkey;
326178825Sdfr    (*db)->hdb_lock = DB_lock;
327178825Sdfr    (*db)->hdb_unlock = DB_unlock;
328178825Sdfr    (*db)->hdb_rename = DB_rename;
329178825Sdfr    (*db)->hdb__get = DB__get;
330178825Sdfr    (*db)->hdb__put = DB__put;
331178825Sdfr    (*db)->hdb__del = DB__del;
332178825Sdfr    (*db)->hdb_destroy = DB_destroy;
33355682Smarkm    return 0;
33455682Smarkm}
33555682Smarkm
33690926Snectar#endif /* HAVE_DB1 */
337