1/*
2 * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "hdb_locl.h"
35
36#if HAVE_DB1
37
38#if defined(HAVE_DB_185_H)
39#include <db_185.h>
40#elif defined(HAVE_DB_H)
41#include <db.h>
42#endif
43
44static krb5_error_code
45DB_close(krb5_context context, HDB *db)
46{
47    DB *d = (DB*)db->hdb_db;
48    (*d->close)(d);
49    return 0;
50}
51
52static krb5_error_code
53DB_destroy(krb5_context context, HDB *db)
54{
55    krb5_error_code ret;
56
57    ret = hdb_clear_master_key (context, db);
58    free(db->hdb_name);
59    free(db);
60    return ret;
61}
62
63static krb5_error_code
64DB_lock(krb5_context context, HDB *db, int operation)
65{
66    DB *d = (DB*)db->hdb_db;
67    int fd = (*d->fd)(d);
68    krb5_error_code ret;
69
70    if (db->lock_count > 0) {
71	db->lock_count++;
72	if (db->lock_type == HDB_WLOCK || db->lock_type == operation)
73	    return 0;
74    }
75
76    if(fd < 0) {
77	krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
78			       "Can't lock database: %s", db->hdb_name);
79	return HDB_ERR_CANT_LOCK_DB;
80    }
81    ret = hdb_lock(fd, operation);
82    if (ret)
83	return ret;
84    db->lock_count++;
85    return 0;
86}
87
88static krb5_error_code
89DB_unlock(krb5_context context, HDB *db)
90{
91    DB *d = (DB*)db->hdb_db;
92    int fd = (*d->fd)(d);
93
94    if (db->lock_count > 1) {
95	db->lock_count--;
96	return 0;
97    }
98    heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match");
99    db->lock_count--;
100
101    if(fd < 0) {
102	krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
103			       "Can't unlock database: %s", db->hdb_name);
104	return HDB_ERR_CANT_LOCK_DB;
105    }
106    return hdb_unlock(fd);
107}
108
109
110static krb5_error_code
111DB_seq(krb5_context context, HDB *db,
112       unsigned flags, hdb_entry_ex *entry, int flag)
113{
114    DB *d = (DB*)db->hdb_db;
115    DBT key, value;
116    krb5_data key_data, data;
117    int code;
118
119    code = db->hdb_lock(context, db, HDB_RLOCK);
120    if(code == -1) {
121	krb5_set_error_message(context, HDB_ERR_DB_INUSE, "Database %s in use", db->hdb_name);
122	return HDB_ERR_DB_INUSE;
123    }
124    code = (*d->seq)(d, &key, &value, flag);
125    db->hdb_unlock(context, db); /* XXX check value */
126    if(code == -1) {
127	code = errno;
128	krb5_set_error_message(context, code, "Database %s seq error: %s",
129			       db->hdb_name, strerror(code));
130	return code;
131    }
132    if(code == 1) {
133	krb5_clear_error_message(context);
134	return HDB_ERR_NOENTRY;
135    }
136
137    key_data.data = key.data;
138    key_data.length = key.size;
139    data.data = value.data;
140    data.length = value.size;
141    memset(entry, 0, sizeof(*entry));
142    if (hdb_value2entry(context, &data, &entry->entry))
143	return DB_seq(context, db, flags, entry, R_NEXT);
144    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
145	code = hdb_unseal_keys (context, db, &entry->entry);
146	if (code)
147	    hdb_free_entry (context, entry);
148    }
149    if (code == 0 && entry->entry.principal == NULL) {
150	entry->entry.principal = malloc(sizeof(*entry->entry.principal));
151	if (entry->entry.principal == NULL) {
152	    code = ENOMEM;
153	    krb5_set_error_message(context, code, "malloc: out of memory");
154	    hdb_free_entry (context, entry);
155	} else {
156	    hdb_key2principal(context, &key_data, entry->entry.principal);
157	}
158    }
159    return code;
160}
161
162
163static krb5_error_code
164DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
165{
166    return DB_seq(context, db, flags, entry, R_FIRST);
167}
168
169
170static krb5_error_code
171DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
172{
173    return DB_seq(context, db, flags, entry, R_NEXT);
174}
175
176static krb5_error_code
177DB_rename(krb5_context context, HDB *db, const char *new_name)
178{
179    int ret;
180    char *old, *new;
181
182    asprintf(&old, "%s.db", db->hdb_name);
183    asprintf(&new, "%s.db", new_name);
184    ret = rename(old, new);
185    free(old);
186    free(new);
187    if(ret)
188	return errno;
189
190    free(db->hdb_name);
191    db->hdb_name = strdup(new_name);
192    return 0;
193}
194
195static krb5_error_code
196DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
197{
198    DB *d = (DB*)db->hdb_db;
199    DBT k, v;
200    int code;
201
202    k.data = key.data;
203    k.size = key.length;
204    code = db->hdb_lock(context, db, HDB_RLOCK);
205    if(code)
206	return code;
207    code = (*d->get)(d, &k, &v, 0);
208    db->hdb_unlock(context, db);
209    if(code < 0) {
210	code = errno;
211	krb5_set_error_message(context, code, "Database %s get error: %s",
212			       db->hdb_name, strerror(code));
213	return code;
214    }
215    if(code == 1) {
216	krb5_clear_error_message(context);
217	return HDB_ERR_NOENTRY;
218    }
219
220    krb5_data_copy(reply, v.data, v.size);
221    return 0;
222}
223
224static krb5_error_code
225DB__put(krb5_context context, HDB *db, int replace,
226	krb5_data key, krb5_data value)
227{
228    DB *d = (DB*)db->hdb_db;
229    DBT k, v;
230    int code;
231
232    k.data = key.data;
233    k.size = key.length;
234    v.data = value.data;
235    v.size = value.length;
236    code = db->hdb_lock(context, db, HDB_WLOCK);
237    if(code)
238	return code;
239    code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE);
240    db->hdb_unlock(context, db);
241    if(code < 0) {
242	code = errno;
243	krb5_set_error_message(context, code, "Database %s put error: %s",
244			       db->hdb_name, strerror(code));
245	return code;
246    }
247    if(code == 1) {
248	krb5_clear_error_message(context);
249	return HDB_ERR_EXISTS;
250    }
251    return 0;
252}
253
254static krb5_error_code
255DB__del(krb5_context context, HDB *db, krb5_data key)
256{
257    DB *d = (DB*)db->hdb_db;
258    DBT k;
259    krb5_error_code code;
260    k.data = key.data;
261    k.size = key.length;
262    code = db->hdb_lock(context, db, HDB_WLOCK);
263    if(code)
264	return code;
265    code = (*d->del)(d, &k, 0);
266    db->hdb_unlock(context, db);
267    if(code == 1) {
268	code = errno;
269	krb5_set_error_message(context, code, "Database %s put error: %s",
270			       db->hdb_name, strerror(code));
271	return code;
272    }
273    if(code < 0)
274	return errno;
275    return 0;
276}
277
278static krb5_error_code
279DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
280{
281    char *fn;
282    krb5_error_code ret;
283
284    asprintf(&fn, "%s.db", db->hdb_name);
285    if (fn == NULL) {
286	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
287	return ENOMEM;
288    }
289    db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
290    free(fn);
291    /* try to open without .db extension */
292    if(db->hdb_db == NULL && errno == ENOENT)
293	db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL);
294    if(db->hdb_db == NULL) {
295	ret = errno;
296	krb5_set_error_message(context, ret, "dbopen (%s): %s",
297			      db->hdb_name, strerror(ret));
298	return ret;
299    }
300    if((flags & O_ACCMODE) == O_RDONLY)
301	ret = hdb_check_db_format(context, db);
302    else
303	ret = hdb_init_db(context, db);
304    if(ret == HDB_ERR_NOENTRY) {
305	krb5_clear_error_message(context);
306	return 0;
307    }
308    if (ret) {
309	DB_close(context, db);
310	krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
311			      (flags & O_ACCMODE) == O_RDONLY ?
312			      "checking format of" : "initialize",
313			      db->hdb_name);
314    }
315    return ret;
316}
317
318krb5_error_code
319hdb_db_create(krb5_context context, HDB **db,
320	      const char *filename)
321{
322    *db = calloc(1, sizeof(**db));
323    if (*db == NULL) {
324	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
325	return ENOMEM;
326    }
327
328    (*db)->hdb_db = NULL;
329    (*db)->hdb_name = strdup(filename);
330    if ((*db)->hdb_name == NULL) {
331	free(*db);
332	*db = NULL;
333	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
334	return ENOMEM;
335    }
336    (*db)->hdb_master_key_set = 0;
337    (*db)->hdb_openp = 0;
338    (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL;
339    (*db)->hdb_open = DB_open;
340    (*db)->hdb_close = DB_close;
341    (*db)->hdb_fetch_kvno = _hdb_fetch_kvno;
342    (*db)->hdb_store = _hdb_store;
343    (*db)->hdb_remove = _hdb_remove;
344    (*db)->hdb_firstkey = DB_firstkey;
345    (*db)->hdb_nextkey= DB_nextkey;
346    (*db)->hdb_lock = DB_lock;
347    (*db)->hdb_unlock = DB_unlock;
348    (*db)->hdb_rename = DB_rename;
349    (*db)->hdb__get = DB__get;
350    (*db)->hdb__put = DB__put;
351    (*db)->hdb__del = DB__del;
352    (*db)->hdb_destroy = DB_destroy;
353    return 0;
354}
355
356#endif /* HAVE_DB1 */
357