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_NDBM
3755682Smarkm
3890926Snectar#if defined(HAVE_GDBM_NDBM_H)
3990926Snectar#include <gdbm/ndbm.h>
40233294Sstas#define WRITE_SUPPORT 1
4190926Snectar#elif defined(HAVE_NDBM_H)
4290926Snectar#include <ndbm.h>
4390926Snectar#elif defined(HAVE_DBM_H)
44233294Sstas#define WRITE_SUPPORT 1
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{
56233294Sstas    hdb_clear_master_key (context, db);
57178825Sdfr    free(db->hdb_name);
5855682Smarkm    free(db);
5955682Smarkm    return 0;
6055682Smarkm}
6155682Smarkm
6255682Smarkmstatic krb5_error_code
6355682SmarkmNDBM_lock(krb5_context context, HDB *db, int operation)
6455682Smarkm{
65178825Sdfr    struct ndbm_db *d = db->hdb_db;
6655682Smarkm    return hdb_lock(d->lock_fd, operation);
6755682Smarkm}
6855682Smarkm
6955682Smarkmstatic krb5_error_code
7055682SmarkmNDBM_unlock(krb5_context context, HDB *db)
7155682Smarkm{
72178825Sdfr    struct ndbm_db *d = db->hdb_db;
7355682Smarkm    return hdb_unlock(d->lock_fd);
7455682Smarkm}
7555682Smarkm
7655682Smarkmstatic krb5_error_code
77233294SstasNDBM_seq(krb5_context context, HDB *db,
78178825Sdfr	 unsigned flags, hdb_entry_ex *entry, int first)
7955682Smarkm
8055682Smarkm{
81178825Sdfr    struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
8255682Smarkm    datum key, value;
8355682Smarkm    krb5_data key_data, data;
8472445Sassar    krb5_error_code ret = 0;
8555682Smarkm
8655682Smarkm    if(first)
8755682Smarkm	key = dbm_firstkey(d->db);
8855682Smarkm    else
8955682Smarkm	key = dbm_nextkey(d->db);
9055682Smarkm    if(key.dptr == NULL)
9155682Smarkm	return HDB_ERR_NOENTRY;
9255682Smarkm    key_data.data = key.dptr;
9355682Smarkm    key_data.length = key.dsize;
94178825Sdfr    ret = db->hdb_lock(context, db, HDB_RLOCK);
9555682Smarkm    if(ret) return ret;
9655682Smarkm    value = dbm_fetch(d->db, key);
97178825Sdfr    db->hdb_unlock(context, db);
9855682Smarkm    data.data = value.dptr;
9955682Smarkm    data.length = value.dsize;
100178825Sdfr    memset(entry, 0, sizeof(*entry));
101178825Sdfr    if(hdb_value2entry(context, &data, &entry->entry))
10255682Smarkm	return NDBM_seq(context, db, flags, entry, 0);
103178825Sdfr    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
104178825Sdfr	ret = hdb_unseal_keys (context, db, &entry->entry);
10572445Sassar	if (ret)
10672445Sassar	    hdb_free_entry (context, entry);
10772445Sassar    }
108178825Sdfr    if (ret == 0 && entry->entry.principal == NULL) {
109178825Sdfr	entry->entry.principal = malloc (sizeof(*entry->entry.principal));
110178825Sdfr	if (entry->entry.principal == NULL) {
111233294Sstas	    hdb_free_entry (context, entry);
11272445Sassar	    ret = ENOMEM;
113233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
11472445Sassar	} else {
115178825Sdfr	    hdb_key2principal (context, &key_data, entry->entry.principal);
11672445Sassar	}
11755682Smarkm    }
11872445Sassar    return ret;
11955682Smarkm}
12055682Smarkm
12155682Smarkm
12255682Smarkmstatic krb5_error_code
123178825SdfrNDBM_firstkey(krb5_context context, HDB *db,unsigned flags,hdb_entry_ex *entry)
12455682Smarkm{
12555682Smarkm    return NDBM_seq(context, db, flags, entry, 1);
12655682Smarkm}
12755682Smarkm
12855682Smarkm
12955682Smarkmstatic krb5_error_code
130178825SdfrNDBM_nextkey(krb5_context context, HDB *db, unsigned flags,hdb_entry_ex *entry)
13155682Smarkm{
13255682Smarkm    return NDBM_seq(context, db, flags, entry, 0);
13355682Smarkm}
13455682Smarkm
13555682Smarkmstatic krb5_error_code
136233294Sstasopen_lock_file(krb5_context context, const char *db_name, int *fd)
137233294Sstas{
138233294Sstas    char *lock_file;
139233294Sstas
140233294Sstas    /* lock old and new databases */
141233294Sstas    asprintf(&lock_file, "%s.lock", db_name);
142233294Sstas    if(lock_file == NULL) {
143233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
144233294Sstas	return ENOMEM;
145233294Sstas    }
146233294Sstas
147233294Sstas    *fd = open(lock_file, O_RDWR | O_CREAT, 0600);
148233294Sstas    free(lock_file);
149233294Sstas    if(*fd < 0) {
150233294Sstas	int ret = errno;
151233294Sstas	krb5_set_error_message(context, ret, "open(%s): %s", lock_file,
152233294Sstas			       strerror(ret));
153233294Sstas	return ret;
154233294Sstas    }
155233294Sstas    return 0;
156233294Sstas}
157233294Sstas
158233294Sstas
159233294Sstasstatic krb5_error_code
16055682SmarkmNDBM_rename(krb5_context context, HDB *db, const char *new_name)
16155682Smarkm{
16255682Smarkm    int ret;
16355682Smarkm    char *old_dir, *old_pag, *new_dir, *new_pag;
164233294Sstas    int old_lock_fd, new_lock_fd;
16555682Smarkm
16655682Smarkm    /* lock old and new databases */
167233294Sstas    ret = open_lock_file(context, db->hdb_name, &old_lock_fd);
168233294Sstas    if (ret)
16990926Snectar	return ret;
170233294Sstas
171233294Sstas    ret = hdb_lock(old_lock_fd, HDB_WLOCK);
172233294Sstas    if(ret) {
173233294Sstas	close(old_lock_fd);
174233294Sstas	return ret;
17590926Snectar    }
176233294Sstas
177233294Sstas    ret = open_lock_file(context, new_name, &new_lock_fd);
178233294Sstas    if (ret) {
179233294Sstas	hdb_unlock(old_lock_fd);
180233294Sstas	close(old_lock_fd);
18155682Smarkm	return ret;
18255682Smarkm    }
183233294Sstas
184233294Sstas    ret = hdb_lock(new_lock_fd, HDB_WLOCK);
18555682Smarkm    if(ret) {
186233294Sstas	hdb_unlock(old_lock_fd);
187233294Sstas	close(old_lock_fd);
188233294Sstas	close(new_lock_fd);
18955682Smarkm	return ret;
19055682Smarkm    }
19155682Smarkm
192178825Sdfr    asprintf(&old_dir, "%s.dir", db->hdb_name);
193178825Sdfr    asprintf(&old_pag, "%s.pag", db->hdb_name);
19455682Smarkm    asprintf(&new_dir, "%s.dir", new_name);
19555682Smarkm    asprintf(&new_pag, "%s.pag", new_name);
19655682Smarkm
19755682Smarkm    ret = rename(old_dir, new_dir) || rename(old_pag, new_pag);
198233294Sstas    if (ret) {
199233294Sstas	ret = errno;
200233294Sstas	if (ret == 0)
201233294Sstas	    ret = EPERM;
202233294Sstas	krb5_set_error_message(context, ret, "rename: %s", strerror(ret));
203233294Sstas    }
204233294Sstas
20555682Smarkm    free(old_dir);
20655682Smarkm    free(old_pag);
20755682Smarkm    free(new_dir);
20855682Smarkm    free(new_pag);
20955682Smarkm
210233294Sstas    hdb_unlock(new_lock_fd);
211233294Sstas    hdb_unlock(old_lock_fd);
212233294Sstas    close(new_lock_fd);
213233294Sstas    close(old_lock_fd);
214233294Sstas
215233294Sstas    if(ret)
21690926Snectar	return ret;
21755682Smarkm
218178825Sdfr    free(db->hdb_name);
219178825Sdfr    db->hdb_name = strdup(new_name);
22055682Smarkm    return 0;
22155682Smarkm}
22255682Smarkm
22355682Smarkmstatic krb5_error_code
22455682SmarkmNDBM__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
22555682Smarkm{
226178825Sdfr    struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
22755682Smarkm    datum k, v;
22855682Smarkm    int code;
22955682Smarkm
23055682Smarkm    k.dptr  = key.data;
23155682Smarkm    k.dsize = key.length;
232178825Sdfr    code = db->hdb_lock(context, db, HDB_RLOCK);
23355682Smarkm    if(code)
23455682Smarkm	return code;
23555682Smarkm    v = dbm_fetch(d->db, k);
236178825Sdfr    db->hdb_unlock(context, db);
23755682Smarkm    if(v.dptr == NULL)
23855682Smarkm	return HDB_ERR_NOENTRY;
23955682Smarkm
24055682Smarkm    krb5_data_copy(reply, v.dptr, v.dsize);
24155682Smarkm    return 0;
24255682Smarkm}
24355682Smarkm
24455682Smarkmstatic krb5_error_code
245233294SstasNDBM__put(krb5_context context, HDB *db, int replace,
24655682Smarkm	krb5_data key, krb5_data value)
24755682Smarkm{
248233294Sstas#ifdef WRITE_SUPPORT
249178825Sdfr    struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
25055682Smarkm    datum k, v;
25155682Smarkm    int code;
25255682Smarkm
25355682Smarkm    k.dptr  = key.data;
25455682Smarkm    k.dsize = key.length;
25555682Smarkm    v.dptr  = value.data;
25655682Smarkm    v.dsize = value.length;
25755682Smarkm
258178825Sdfr    code = db->hdb_lock(context, db, HDB_WLOCK);
25955682Smarkm    if(code)
26055682Smarkm	return code;
26155682Smarkm    code = dbm_store(d->db, k, v, replace ? DBM_REPLACE : DBM_INSERT);
262178825Sdfr    db->hdb_unlock(context, db);
26355682Smarkm    if(code == 1)
26455682Smarkm	return HDB_ERR_EXISTS;
26555682Smarkm    if (code < 0)
26655682Smarkm	return code;
26755682Smarkm    return 0;
268233294Sstas#else
269233294Sstas    return HDB_ERR_NO_WRITE_SUPPORT;
270233294Sstas#endif
27155682Smarkm}
27255682Smarkm
27355682Smarkmstatic krb5_error_code
27455682SmarkmNDBM__del(krb5_context context, HDB *db, krb5_data key)
27555682Smarkm{
276178825Sdfr    struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
27755682Smarkm    datum k;
27855682Smarkm    int code;
27955682Smarkm    krb5_error_code ret;
28055682Smarkm
28155682Smarkm    k.dptr = key.data;
28255682Smarkm    k.dsize = key.length;
283178825Sdfr    ret = db->hdb_lock(context, db, HDB_WLOCK);
28455682Smarkm    if(ret) return ret;
28555682Smarkm    code = dbm_delete(d->db, k);
286178825Sdfr    db->hdb_unlock(context, db);
28755682Smarkm    if(code < 0)
28855682Smarkm	return errno;
28955682Smarkm    return 0;
29055682Smarkm}
29155682Smarkm
292178825Sdfr
29355682Smarkmstatic krb5_error_code
294178825SdfrNDBM_close(krb5_context context, HDB *db)
295178825Sdfr{
296178825Sdfr    struct ndbm_db *d = db->hdb_db;
297178825Sdfr    dbm_close(d->db);
298178825Sdfr    close(d->lock_fd);
299178825Sdfr    free(d);
300178825Sdfr    return 0;
301178825Sdfr}
302178825Sdfr
303178825Sdfrstatic krb5_error_code
30455682SmarkmNDBM_open(krb5_context context, HDB *db, int flags, mode_t mode)
30555682Smarkm{
30655682Smarkm    krb5_error_code ret;
30755682Smarkm    struct ndbm_db *d = malloc(sizeof(*d));
30855682Smarkm
30990926Snectar    if(d == NULL) {
310233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
31155682Smarkm	return ENOMEM;
31290926Snectar    }
313233294Sstas
314178825Sdfr    d->db = dbm_open((char*)db->hdb_name, flags, mode);
31555682Smarkm    if(d->db == NULL){
31690926Snectar	ret = errno;
31755682Smarkm	free(d);
318233294Sstas	krb5_set_error_message(context, ret, "dbm_open(%s): %s", db->hdb_name,
319233294Sstas			       strerror(ret));
32090926Snectar	return ret;
32155682Smarkm    }
322233294Sstas
323233294Sstas    ret = open_lock_file(context, db->hdb_name, &d->lock_fd);
324233294Sstas    if (ret) {
32590926Snectar	ret = errno;
32655682Smarkm	dbm_close(d->db);
32755682Smarkm	free(d);
328233294Sstas	krb5_set_error_message(context, ret, "open(lock file): %s",
329233294Sstas			       strerror(ret));
33090926Snectar	return ret;
33155682Smarkm    }
332233294Sstas
333178825Sdfr    db->hdb_db = d;
33455682Smarkm    if((flags & O_ACCMODE) == O_RDONLY)
33555682Smarkm	ret = hdb_check_db_format(context, db);
33655682Smarkm    else
33755682Smarkm	ret = hdb_init_db(context, db);
33855682Smarkm    if(ret == HDB_ERR_NOENTRY)
33955682Smarkm	return 0;
340178825Sdfr    if (ret) {
341178825Sdfr	NDBM_close(context, db);
342233294Sstas	krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
343233294Sstas			       (flags & O_ACCMODE) == O_RDONLY ?
344233294Sstas			       "checking format of" : "initialize",
345233294Sstas			       db->hdb_name);
346178825Sdfr    }
34755682Smarkm    return ret;
34855682Smarkm}
34955682Smarkm
35055682Smarkmkrb5_error_code
351233294Sstashdb_ndbm_create(krb5_context context, HDB **db,
35255682Smarkm		const char *filename)
35355682Smarkm{
354178825Sdfr    *db = calloc(1, sizeof(**db));
35590926Snectar    if (*db == NULL) {
356233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
35755682Smarkm	return ENOMEM;
35890926Snectar    }
35955682Smarkm
360178825Sdfr    (*db)->hdb_db = NULL;
361178825Sdfr    (*db)->hdb_name = strdup(filename);
362178825Sdfr    if ((*db)->hdb_name == NULL) {
36390926Snectar	free(*db);
36490926Snectar	*db = NULL;
365233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
36690926Snectar	return ENOMEM;
36790926Snectar    }
368178825Sdfr    (*db)->hdb_master_key_set = 0;
369178825Sdfr    (*db)->hdb_openp = 0;
370233294Sstas    (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL;
371178825Sdfr    (*db)->hdb_open = NDBM_open;
372178825Sdfr    (*db)->hdb_close = NDBM_close;
373233294Sstas    (*db)->hdb_fetch_kvno = _hdb_fetch_kvno;
374178825Sdfr    (*db)->hdb_store = _hdb_store;
375178825Sdfr    (*db)->hdb_remove = _hdb_remove;
376178825Sdfr    (*db)->hdb_firstkey = NDBM_firstkey;
377178825Sdfr    (*db)->hdb_nextkey= NDBM_nextkey;
378178825Sdfr    (*db)->hdb_lock = NDBM_lock;
379178825Sdfr    (*db)->hdb_unlock = NDBM_unlock;
380178825Sdfr    (*db)->hdb_rename = NDBM_rename;
381178825Sdfr    (*db)->hdb__get = NDBM__get;
382178825Sdfr    (*db)->hdb__put = NDBM__put;
383178825Sdfr    (*db)->hdb__del = NDBM__del;
384178825Sdfr    (*db)->hdb_destroy = NDBM_destroy;
38555682Smarkm    return 0;
38655682Smarkm}
38755682Smarkm
38890926Snectar#endif /* HAVE_NDBM */
389