155682Smarkm/*
272445Sassar * Copyright (c) 1997 - 2001 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "hdb_locl.h"
3555682Smarkm
36178825SdfrRCSID("$Id: ndbm.c 16395 2005-12-13 11:54:10Z lha $");
3755682Smarkm
3890926Snectar#if HAVE_NDBM
3955682Smarkm
4090926Snectar#if defined(HAVE_GDBM_NDBM_H)
4190926Snectar#include <gdbm/ndbm.h>
4290926Snectar#elif defined(HAVE_NDBM_H)
4390926Snectar#include <ndbm.h>
4490926Snectar#elif defined(HAVE_DBM_H)
4590926Snectar#include <dbm.h>
4690926Snectar#endif
4790926Snectar
4855682Smarkmstruct ndbm_db {
4955682Smarkm    DBM *db;
5055682Smarkm    int lock_fd;
5155682Smarkm};
5255682Smarkm
5355682Smarkmstatic krb5_error_code
5455682SmarkmNDBM_destroy(krb5_context context, HDB *db)
5555682Smarkm{
5655682Smarkm    krb5_error_code ret;
5755682Smarkm
5855682Smarkm    ret = hdb_clear_master_key (context, db);
59178825Sdfr    free(db->hdb_name);
6055682Smarkm    free(db);
6155682Smarkm    return 0;
6255682Smarkm}
6355682Smarkm
6455682Smarkmstatic krb5_error_code
6555682SmarkmNDBM_lock(krb5_context context, HDB *db, int operation)
6655682Smarkm{
67178825Sdfr    struct ndbm_db *d = db->hdb_db;
6855682Smarkm    return hdb_lock(d->lock_fd, operation);
6955682Smarkm}
7055682Smarkm
7155682Smarkmstatic krb5_error_code
7255682SmarkmNDBM_unlock(krb5_context context, HDB *db)
7355682Smarkm{
74178825Sdfr    struct ndbm_db *d = db->hdb_db;
7555682Smarkm    return hdb_unlock(d->lock_fd);
7655682Smarkm}
7755682Smarkm
7855682Smarkmstatic krb5_error_code
7955682SmarkmNDBM_seq(krb5_context context, HDB *db,
80178825Sdfr	 unsigned flags, hdb_entry_ex *entry, int first)
8155682Smarkm
8255682Smarkm{
83178825Sdfr    struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
8455682Smarkm    datum key, value;
8555682Smarkm    krb5_data key_data, data;
8672445Sassar    krb5_error_code ret = 0;
8755682Smarkm
8855682Smarkm    if(first)
8955682Smarkm	key = dbm_firstkey(d->db);
9055682Smarkm    else
9155682Smarkm	key = dbm_nextkey(d->db);
9255682Smarkm    if(key.dptr == NULL)
9355682Smarkm	return HDB_ERR_NOENTRY;
9455682Smarkm    key_data.data = key.dptr;
9555682Smarkm    key_data.length = key.dsize;
96178825Sdfr    ret = db->hdb_lock(context, db, HDB_RLOCK);
9755682Smarkm    if(ret) return ret;
9855682Smarkm    value = dbm_fetch(d->db, key);
99178825Sdfr    db->hdb_unlock(context, db);
10055682Smarkm    data.data = value.dptr;
10155682Smarkm    data.length = value.dsize;
102178825Sdfr    memset(entry, 0, sizeof(*entry));
103178825Sdfr    if(hdb_value2entry(context, &data, &entry->entry))
10455682Smarkm	return NDBM_seq(context, db, flags, entry, 0);
105178825Sdfr    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
106178825Sdfr	ret = hdb_unseal_keys (context, db, &entry->entry);
10772445Sassar	if (ret)
10872445Sassar	    hdb_free_entry (context, entry);
10972445Sassar    }
110178825Sdfr    if (ret == 0 && entry->entry.principal == NULL) {
111178825Sdfr	entry->entry.principal = malloc (sizeof(*entry->entry.principal));
112178825Sdfr	if (entry->entry.principal == NULL) {
11372445Sassar	    ret = ENOMEM;
11472445Sassar	    hdb_free_entry (context, entry);
11590926Snectar	    krb5_set_error_string(context, "malloc: out of memory");
11672445Sassar	} else {
117178825Sdfr	    hdb_key2principal (context, &key_data, entry->entry.principal);
11872445Sassar	}
11955682Smarkm    }
12072445Sassar    return ret;
12155682Smarkm}
12255682Smarkm
12355682Smarkm
12455682Smarkmstatic krb5_error_code
125178825SdfrNDBM_firstkey(krb5_context context, HDB *db,unsigned flags,hdb_entry_ex *entry)
12655682Smarkm{
12755682Smarkm    return NDBM_seq(context, db, flags, entry, 1);
12855682Smarkm}
12955682Smarkm
13055682Smarkm
13155682Smarkmstatic krb5_error_code
132178825SdfrNDBM_nextkey(krb5_context context, HDB *db, unsigned flags,hdb_entry_ex *entry)
13355682Smarkm{
13455682Smarkm    return NDBM_seq(context, db, flags, entry, 0);
13555682Smarkm}
13655682Smarkm
13755682Smarkmstatic krb5_error_code
13855682SmarkmNDBM_rename(krb5_context context, HDB *db, const char *new_name)
13955682Smarkm{
14055682Smarkm    /* XXX this function will break */
141178825Sdfr    struct ndbm_db *d = db->hdb_db;
14255682Smarkm
14355682Smarkm    int ret;
14455682Smarkm    char *old_dir, *old_pag, *new_dir, *new_pag;
14555682Smarkm    char *new_lock;
14655682Smarkm    int lock_fd;
14755682Smarkm
14855682Smarkm    /* lock old and new databases */
149178825Sdfr    ret = db->hdb_lock(context, db, HDB_WLOCK);
15090926Snectar    if(ret)
15190926Snectar	return ret;
15255682Smarkm    asprintf(&new_lock, "%s.lock", new_name);
15390926Snectar    if(new_lock == NULL) {
154178825Sdfr	db->hdb_unlock(context, db);
15590926Snectar	krb5_set_error_string(context, "malloc: out of memory");
15690926Snectar	return ENOMEM;
15790926Snectar    }
15855682Smarkm    lock_fd = open(new_lock, O_RDWR | O_CREAT, 0600);
15955682Smarkm    if(lock_fd < 0) {
16055682Smarkm	ret = errno;
161178825Sdfr	db->hdb_unlock(context, db);
16290926Snectar	krb5_set_error_string(context, "open(%s): %s", new_lock,
16390926Snectar			      strerror(ret));
16490926Snectar	free(new_lock);
16555682Smarkm	return ret;
16655682Smarkm    }
16790926Snectar    free(new_lock);
16855682Smarkm    ret = hdb_lock(lock_fd, HDB_WLOCK);
16955682Smarkm    if(ret) {
170178825Sdfr	db->hdb_unlock(context, db);
17155682Smarkm	close(lock_fd);
17255682Smarkm	return ret;
17355682Smarkm    }
17455682Smarkm
175178825Sdfr    asprintf(&old_dir, "%s.dir", db->hdb_name);
176178825Sdfr    asprintf(&old_pag, "%s.pag", db->hdb_name);
17755682Smarkm    asprintf(&new_dir, "%s.dir", new_name);
17855682Smarkm    asprintf(&new_pag, "%s.pag", new_name);
17955682Smarkm
18055682Smarkm    ret = rename(old_dir, new_dir) || rename(old_pag, new_pag);
18155682Smarkm    free(old_dir);
18255682Smarkm    free(old_pag);
18355682Smarkm    free(new_dir);
18455682Smarkm    free(new_pag);
18555682Smarkm    hdb_unlock(lock_fd);
186178825Sdfr    db->hdb_unlock(context, db);
18755682Smarkm
18855682Smarkm    if(ret) {
18990926Snectar	ret = errno;
19055682Smarkm	close(lock_fd);
19190926Snectar	krb5_set_error_string(context, "rename: %s", strerror(ret));
19290926Snectar	return ret;
19355682Smarkm    }
19455682Smarkm
19555682Smarkm    close(d->lock_fd);
19655682Smarkm    d->lock_fd = lock_fd;
19755682Smarkm
198178825Sdfr    free(db->hdb_name);
199178825Sdfr    db->hdb_name = strdup(new_name);
20055682Smarkm    return 0;
20155682Smarkm}
20255682Smarkm
20355682Smarkmstatic krb5_error_code
20455682SmarkmNDBM__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
20555682Smarkm{
206178825Sdfr    struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
20755682Smarkm    datum k, v;
20855682Smarkm    int code;
20955682Smarkm
21055682Smarkm    k.dptr  = key.data;
21155682Smarkm    k.dsize = key.length;
212178825Sdfr    code = db->hdb_lock(context, db, HDB_RLOCK);
21355682Smarkm    if(code)
21455682Smarkm	return code;
21555682Smarkm    v = dbm_fetch(d->db, k);
216178825Sdfr    db->hdb_unlock(context, db);
21755682Smarkm    if(v.dptr == NULL)
21855682Smarkm	return HDB_ERR_NOENTRY;
21955682Smarkm
22055682Smarkm    krb5_data_copy(reply, v.dptr, v.dsize);
22155682Smarkm    return 0;
22255682Smarkm}
22355682Smarkm
22455682Smarkmstatic krb5_error_code
22555682SmarkmNDBM__put(krb5_context context, HDB *db, int replace,
22655682Smarkm	krb5_data key, krb5_data value)
22755682Smarkm{
228178825Sdfr    struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
22955682Smarkm    datum k, v;
23055682Smarkm    int code;
23155682Smarkm
23255682Smarkm    k.dptr  = key.data;
23355682Smarkm    k.dsize = key.length;
23455682Smarkm    v.dptr  = value.data;
23555682Smarkm    v.dsize = value.length;
23655682Smarkm
237178825Sdfr    code = db->hdb_lock(context, db, HDB_WLOCK);
23855682Smarkm    if(code)
23955682Smarkm	return code;
24055682Smarkm    code = dbm_store(d->db, k, v, replace ? DBM_REPLACE : DBM_INSERT);
241178825Sdfr    db->hdb_unlock(context, db);
24255682Smarkm    if(code == 1)
24355682Smarkm	return HDB_ERR_EXISTS;
24455682Smarkm    if (code < 0)
24555682Smarkm	return code;
24655682Smarkm    return 0;
24755682Smarkm}
24855682Smarkm
24955682Smarkmstatic krb5_error_code
25055682SmarkmNDBM__del(krb5_context context, HDB *db, krb5_data key)
25155682Smarkm{
252178825Sdfr    struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
25355682Smarkm    datum k;
25455682Smarkm    int code;
25555682Smarkm    krb5_error_code ret;
25655682Smarkm
25755682Smarkm    k.dptr = key.data;
25855682Smarkm    k.dsize = key.length;
259178825Sdfr    ret = db->hdb_lock(context, db, HDB_WLOCK);
26055682Smarkm    if(ret) return ret;
26155682Smarkm    code = dbm_delete(d->db, k);
262178825Sdfr    db->hdb_unlock(context, db);
26355682Smarkm    if(code < 0)
26455682Smarkm	return errno;
26555682Smarkm    return 0;
26655682Smarkm}
26755682Smarkm
268178825Sdfr
26955682Smarkmstatic krb5_error_code
270178825SdfrNDBM_close(krb5_context context, HDB *db)
271178825Sdfr{
272178825Sdfr    struct ndbm_db *d = db->hdb_db;
273178825Sdfr    dbm_close(d->db);
274178825Sdfr    close(d->lock_fd);
275178825Sdfr    free(d);
276178825Sdfr    return 0;
277178825Sdfr}
278178825Sdfr
279178825Sdfrstatic krb5_error_code
28055682SmarkmNDBM_open(krb5_context context, HDB *db, int flags, mode_t mode)
28155682Smarkm{
28255682Smarkm    krb5_error_code ret;
28355682Smarkm    struct ndbm_db *d = malloc(sizeof(*d));
28455682Smarkm    char *lock_file;
28555682Smarkm
28690926Snectar    if(d == NULL) {
28790926Snectar	krb5_set_error_string(context, "malloc: out of memory");
28855682Smarkm	return ENOMEM;
28990926Snectar    }
290178825Sdfr    asprintf(&lock_file, "%s.lock", (char*)db->hdb_name);
29155682Smarkm    if(lock_file == NULL) {
29255682Smarkm	free(d);
29390926Snectar	krb5_set_error_string(context, "malloc: out of memory");
29455682Smarkm	return ENOMEM;
29555682Smarkm    }
296178825Sdfr    d->db = dbm_open((char*)db->hdb_name, flags, mode);
29755682Smarkm    if(d->db == NULL){
29890926Snectar	ret = errno;
29955682Smarkm	free(d);
30055682Smarkm	free(lock_file);
301178825Sdfr	krb5_set_error_string(context, "dbm_open(%s): %s", db->hdb_name,
30290926Snectar			      strerror(ret));
30390926Snectar	return ret;
30455682Smarkm    }
30555682Smarkm    d->lock_fd = open(lock_file, O_RDWR | O_CREAT, 0600);
30655682Smarkm    if(d->lock_fd < 0){
30790926Snectar	ret = errno;
30855682Smarkm	dbm_close(d->db);
30955682Smarkm	free(d);
31090926Snectar	krb5_set_error_string(context, "open(%s): %s", lock_file,
31190926Snectar			      strerror(ret));
31290926Snectar	free(lock_file);
31390926Snectar	return ret;
31455682Smarkm    }
31590926Snectar    free(lock_file);
316178825Sdfr    db->hdb_db = d;
31755682Smarkm    if((flags & O_ACCMODE) == O_RDONLY)
31855682Smarkm	ret = hdb_check_db_format(context, db);
31955682Smarkm    else
32055682Smarkm	ret = hdb_init_db(context, db);
32155682Smarkm    if(ret == HDB_ERR_NOENTRY)
32255682Smarkm	return 0;
323178825Sdfr    if (ret) {
324178825Sdfr	NDBM_close(context, db);
325178825Sdfr	krb5_set_error_string(context, "hdb_open: failed %s database %s",
326178825Sdfr			      (flags & O_ACCMODE) == O_RDONLY ?
327178825Sdfr			      "checking format of" : "initialize",
328178825Sdfr			      db->hdb_name);
329178825Sdfr    }
33055682Smarkm    return ret;
33155682Smarkm}
33255682Smarkm
33355682Smarkmkrb5_error_code
33455682Smarkmhdb_ndbm_create(krb5_context context, HDB **db,
33555682Smarkm		const char *filename)
33655682Smarkm{
337178825Sdfr    *db = calloc(1, sizeof(**db));
33890926Snectar    if (*db == NULL) {
33990926Snectar	krb5_set_error_string(context, "malloc: out of memory");
34055682Smarkm	return ENOMEM;
34190926Snectar    }
34255682Smarkm
343178825Sdfr    (*db)->hdb_db = NULL;
344178825Sdfr    (*db)->hdb_name = strdup(filename);
345178825Sdfr    if ((*db)->hdb_name == NULL) {
34690926Snectar	krb5_set_error_string(context, "malloc: out of memory");
34790926Snectar	free(*db);
34890926Snectar	*db = NULL;
34990926Snectar	return ENOMEM;
35090926Snectar    }
351178825Sdfr    (*db)->hdb_master_key_set = 0;
352178825Sdfr    (*db)->hdb_openp = 0;
353178825Sdfr    (*db)->hdb_open = NDBM_open;
354178825Sdfr    (*db)->hdb_close = NDBM_close;
355178825Sdfr    (*db)->hdb_fetch = _hdb_fetch;
356178825Sdfr    (*db)->hdb_store = _hdb_store;
357178825Sdfr    (*db)->hdb_remove = _hdb_remove;
358178825Sdfr    (*db)->hdb_firstkey = NDBM_firstkey;
359178825Sdfr    (*db)->hdb_nextkey= NDBM_nextkey;
360178825Sdfr    (*db)->hdb_lock = NDBM_lock;
361178825Sdfr    (*db)->hdb_unlock = NDBM_unlock;
362178825Sdfr    (*db)->hdb_rename = NDBM_rename;
363178825Sdfr    (*db)->hdb__get = NDBM__get;
364178825Sdfr    (*db)->hdb__put = NDBM__put;
365178825Sdfr    (*db)->hdb__del = NDBM__del;
366178825Sdfr    (*db)->hdb_destroy = NDBM_destroy;
36755682Smarkm    return 0;
36855682Smarkm}
36955682Smarkm
37090926Snectar#endif /* HAVE_NDBM */
371