1 /* $NetBSD: db.c,v 1.3 2019/12/15 22:50:49 christos Exp $ */
2
3 /*
4 * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include "hdb_locl.h"
37
38 #if defined(HAVE_DB1)
39
40 #if defined(HAVE_DB_185_H)
41 #include <db_185.h>
42 #elif defined(HAVE_DB_H)
43 #include <db.h>
44 #endif
45
46 typedef struct {
47 HDB hdb; /* generic members */
48 int lock_fd; /* DB-specific */
49 int do_sync; /* DB-specific */
50 } DB1_HDB;
51
52 static krb5_error_code
DB_close(krb5_context context,HDB * db)53 DB_close(krb5_context context, HDB *db)
54 {
55 DB1_HDB *db1 = (DB1_HDB *)db;
56 DB *d = (DB*)db->hdb_db;
57
58 heim_assert(d != 0, "Closing already closed HDB");
59
60 (*d->close)(d);
61 db->hdb_db = 0;
62
63 if (db1->lock_fd >= 0) {
64 close(db1->lock_fd);
65 db1->lock_fd = -1;
66 }
67
68 return 0;
69 }
70
71 static krb5_error_code
DB_destroy(krb5_context context,HDB * db)72 DB_destroy(krb5_context context, HDB *db)
73 {
74 krb5_error_code ret;
75
76 ret = hdb_clear_master_key (context, db);
77 free(db->hdb_name);
78 free(db);
79 return ret;
80 }
81
82 static krb5_error_code
DB_set_sync(krb5_context context,HDB * db,int on)83 DB_set_sync(krb5_context context, HDB *db, int on)
84 {
85 DB1_HDB *db1 = (DB1_HDB *)db;
86 DB *d = (DB*)db->hdb_db;
87 krb5_error_code ret = 0;
88
89 db1->do_sync = on;
90 if (on) {
91 ret = (*d->sync)(d, 0);
92 if (ret == -1) {
93 ret = errno;
94 krb5_set_error_message(context, ret, "Database %s put sync error: %s",
95 db->hdb_name, strerror(ret));
96 }
97 }
98 return ret;
99 }
100
101 static krb5_error_code
DB_lock(krb5_context context,HDB * db,int operation)102 DB_lock(krb5_context context, HDB *db, int operation)
103 {
104
105 return 0;
106 }
107
108 static krb5_error_code
DB_unlock(krb5_context context,HDB * db)109 DB_unlock(krb5_context context, HDB *db)
110 {
111
112 return 0;
113 }
114
115
116 static krb5_error_code
DB_seq(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry,int flag)117 DB_seq(krb5_context context, HDB *db,
118 unsigned flags, hdb_entry_ex *entry, int flag)
119 {
120 DB *d = (DB*)db->hdb_db;
121 DBT key, value;
122 krb5_data key_data, data;
123 int code;
124
125 code = (*d->seq)(d, &key, &value, flag);
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
163 static krb5_error_code
DB_firstkey(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)164 DB_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
170 static krb5_error_code
DB_nextkey(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)171 DB_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
176 static krb5_error_code
DB_rename(krb5_context context,HDB * db,const char * new_name)177 DB_rename(krb5_context context, HDB *db, const char *new_name)
178 {
179 int ret;
180 char *old, *new;
181
182 if (strncmp(new_name, "db:", sizeof("db:") - 1) == 0)
183 new_name += sizeof("db:") - 1;
184 else if (strncmp(new_name, "db1:", sizeof("db1:") - 1) == 0)
185 new_name += sizeof("db1:") - 1;
186 asprintf(&old, "%s.db", db->hdb_name);
187 asprintf(&new, "%s.db", new_name);
188 ret = rename(old, new);
189 free(old);
190 free(new);
191 if(ret)
192 return errno;
193
194 free(db->hdb_name);
195 db->hdb_name = strdup(new_name);
196 return 0;
197 }
198
199 static krb5_error_code
DB__get(krb5_context context,HDB * db,krb5_data key,krb5_data * reply)200 DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
201 {
202 DB *d = (DB*)db->hdb_db;
203 DBT k, v;
204 int code;
205
206 k.data = key.data;
207 k.size = key.length;
208 code = (*d->get)(d, &k, &v, 0);
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
224 static krb5_error_code
DB__put(krb5_context context,HDB * db,int replace,krb5_data key,krb5_data value)225 DB__put(krb5_context context, HDB *db, int replace,
226 krb5_data key, krb5_data value)
227 {
228 DB1_HDB *db1 = (DB1_HDB *)db;
229 DB *d = (DB*)db->hdb_db;
230 DBT k, v;
231 int code;
232
233 k.data = key.data;
234 k.size = key.length;
235 v.data = value.data;
236 v.size = value.length;
237 krb5_clear_error_message(context);
238 code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE);
239 if(code < 0) {
240 code = errno;
241 krb5_set_error_message(context, code, "Database %s put error: %s",
242 db->hdb_name, strerror(code));
243 return code;
244 }
245 if(code == 1) {
246 return HDB_ERR_EXISTS;
247 }
248
249 return db->hdb_set_sync(context, db, db1->do_sync);
250 }
251
252 static krb5_error_code
DB__del(krb5_context context,HDB * db,krb5_data key)253 DB__del(krb5_context context, HDB *db, krb5_data key)
254 {
255 DB1_HDB *db1 = (DB1_HDB *)db;
256 DB *d = (DB*)db->hdb_db;
257 DBT k;
258 krb5_error_code code;
259 k.data = key.data;
260 k.size = key.length;
261 krb5_clear_error_message(context);
262 code = (*d->del)(d, &k, 0);
263 if (code == 1)
264 return HDB_ERR_NOENTRY;
265 if (code < 0) {
266 code = errno;
267 krb5_set_error_message(context, code, "Database %s del error: %s",
268 db->hdb_name, strerror(code));
269 return code;
270 }
271 return db->hdb_set_sync(context, db, db1->do_sync);
272 }
273
274 static DB *
_open_db(char * fn,int flags,int mode,int * fd)275 _open_db(char *fn, int flags, int mode, int *fd)
276 {
277 #ifndef O_EXLOCK
278 int op;
279 int ret;
280
281 *fd = open(fn, flags, mode);
282 if (*fd == -1)
283 return NULL;
284
285 if ((flags & O_ACCMODE) == O_RDONLY)
286 op = LOCK_SH;
287 else
288 op = LOCK_EX;
289
290 ret = flock(*fd, op);
291 if (ret == -1) {
292 int saved_errno;
293
294 saved_errno = errno;
295 close(*fd);
296 errno = saved_errno;
297 return NULL;
298 }
299 #else
300 if ((flags & O_ACCMODE) == O_RDONLY)
301 flags |= O_SHLOCK;
302 else
303 flags |= O_EXLOCK;
304 #endif
305
306 return dbopen(fn, flags, mode, DB_BTREE, NULL);
307 }
308
309 static krb5_error_code
DB_open(krb5_context context,HDB * db,int flags,mode_t mode)310 DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
311 {
312 DB1_HDB *db1 = (DB1_HDB *)db;
313 char *fn;
314 krb5_error_code ret;
315
316 asprintf(&fn, "%s.db", db->hdb_name);
317 if (fn == NULL) {
318 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
319 return ENOMEM;
320 }
321 db->hdb_db = _open_db(fn, flags, mode, &db1->lock_fd);
322 free(fn);
323 /* try to open without .db extension */
324 if(db->hdb_db == NULL && errno == ENOENT)
325 db->hdb_db = _open_db(db->hdb_name, flags, mode, &db1->lock_fd);
326 if(db->hdb_db == NULL) {
327 krb5_set_error_message(context, errno, "dbopen (%s): %s",
328 db->hdb_name, strerror(errno));
329 return errno;
330 }
331 if((flags & O_ACCMODE) == O_RDONLY)
332 ret = hdb_check_db_format(context, db);
333 else
334 ret = hdb_init_db(context, db);
335 if(ret == HDB_ERR_NOENTRY) {
336 krb5_clear_error_message(context);
337 return 0;
338 }
339 if (ret) {
340 DB_close(context, db);
341 krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
342 (flags & O_ACCMODE) == O_RDONLY ?
343 "checking format of" : "initialize",
344 db->hdb_name);
345 }
346 return ret;
347 }
348
349 krb5_error_code
hdb_db1_create(krb5_context context,HDB ** db,const char * filename)350 hdb_db1_create(krb5_context context, HDB **db,
351 const char *filename)
352 {
353 DB1_HDB **db1 = (DB1_HDB **)db;
354 *db = calloc(1, sizeof(**db1)); /* Allocate space for the larger db1 */
355 if (*db == NULL) {
356 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
357 return ENOMEM;
358 }
359
360 (*db)->hdb_db = NULL;
361 (*db)->hdb_name = strdup(filename);
362 if ((*db)->hdb_name == NULL) {
363 free(*db);
364 *db = NULL;
365 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
366 return ENOMEM;
367 }
368 (*db)->hdb_master_key_set = 0;
369 (*db)->hdb_openp = 0;
370 (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL;
371 (*db)->hdb_open = DB_open;
372 (*db)->hdb_close = DB_close;
373 (*db)->hdb_fetch_kvno = _hdb_fetch_kvno;
374 (*db)->hdb_store = _hdb_store;
375 (*db)->hdb_remove = _hdb_remove;
376 (*db)->hdb_firstkey = DB_firstkey;
377 (*db)->hdb_nextkey= DB_nextkey;
378 (*db)->hdb_lock = DB_lock;
379 (*db)->hdb_unlock = DB_unlock;
380 (*db)->hdb_rename = DB_rename;
381 (*db)->hdb__get = DB__get;
382 (*db)->hdb__put = DB__put;
383 (*db)->hdb__del = DB__del;
384 (*db)->hdb_destroy = DB_destroy;
385 (*db)->hdb_set_sync = DB_set_sync;
386
387 (*db1)->lock_fd = -1;
388 (*db1)->do_sync = 1;
389 return 0;
390 }
391
392 #endif /* defined(HAVE_DB1) */
393