1 /* $NetBSD: db3.c,v 1.3 2019/12/15 22:50:49 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997 - 2006 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 #include <fcntl.h> 39 40 #if HAVE_DB3 41 42 #ifdef HAVE_DBHEADER 43 #include <db.h> 44 #elif HAVE_DB6_DB_H 45 #include <db6/db.h> 46 #elif HAVE_DB5_DB_H 47 #include <db5/db.h> 48 #elif HAVE_DB4_DB_H 49 #include <db4/db.h> 50 #elif HAVE_DB3_DB_H 51 #include <db3/db.h> 52 #else 53 #include <db.h> 54 #endif 55 56 typedef struct { 57 HDB hdb; /* generic members */ 58 int lock_fd; /* DB3-specific */ 59 int do_sync; /* DB3-specific */ 60 } DB3_HDB; 61 62 63 static krb5_error_code 64 DB_close(krb5_context context, HDB *db) 65 { 66 DB3_HDB *db3 = (DB3_HDB *)db; 67 DB *d = (DB*)db->hdb_db; 68 DBC *dbcp = (DBC*)db->hdb_dbc; 69 70 heim_assert(d != 0, "Closing already closed HDB"); 71 72 if (dbcp != NULL) 73 dbcp->c_close(dbcp); 74 if (d != NULL) 75 d->close(d, 0); 76 if (db3->lock_fd >= 0) 77 close(db3->lock_fd); 78 79 db3->lock_fd = -1; 80 db->hdb_dbc = 0; 81 db->hdb_db = 0; 82 83 return 0; 84 } 85 86 static krb5_error_code 87 DB_destroy(krb5_context context, HDB *db) 88 { 89 krb5_error_code ret; 90 91 ret = hdb_clear_master_key (context, db); 92 free(db->hdb_name); 93 free(db); 94 return ret; 95 } 96 97 static krb5_error_code 98 DB_set_sync(krb5_context context, HDB *db, int on) 99 { 100 DB3_HDB *db3 = (DB3_HDB *)db; 101 DB *d = (DB*)db->hdb_db; 102 krb5_error_code ret = 0; 103 104 db3->do_sync = on; 105 if (on) { 106 ret = (*d->sync)(d, 0); 107 if (ret) { 108 if (ret == EACCES || ret == ENOSPC || ret == EINVAL) { 109 krb5_set_error_message(context, ret, 110 "Database %s put sync error: %s", 111 db->hdb_name, strerror(ret)); 112 } else { 113 ret = HDB_ERR_UK_SERROR; 114 krb5_set_error_message(context, ret, 115 "Database %s put sync error: unknown (%d)", 116 db->hdb_name, ret); 117 } 118 } 119 } 120 return ret; 121 } 122 123 static krb5_error_code 124 DB_lock(krb5_context context, HDB *db, int operation) 125 { 126 127 return 0; 128 } 129 130 static krb5_error_code 131 DB_unlock(krb5_context context, HDB *db) 132 { 133 134 return 0; 135 } 136 137 138 static krb5_error_code 139 DB_seq(krb5_context context, HDB *db, 140 unsigned flags, hdb_entry_ex *entry, int flag) 141 { 142 DBT key, value; 143 DBC *dbcp = db->hdb_dbc; 144 krb5_data key_data, data; 145 int code; 146 147 memset(&key, 0, sizeof(DBT)); 148 memset(&value, 0, sizeof(DBT)); 149 code = (*dbcp->c_get)(dbcp, &key, &value, flag); 150 if (code == DB_NOTFOUND) 151 return HDB_ERR_NOENTRY; 152 if (code) 153 return code; 154 155 key_data.data = key.data; 156 key_data.length = key.size; 157 data.data = value.data; 158 data.length = value.size; 159 memset(entry, 0, sizeof(*entry)); 160 if (hdb_value2entry(context, &data, &entry->entry)) 161 return DB_seq(context, db, flags, entry, DB_NEXT); 162 if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { 163 code = hdb_unseal_keys (context, db, &entry->entry); 164 if (code) 165 hdb_free_entry (context, entry); 166 } 167 if (entry->entry.principal == NULL) { 168 entry->entry.principal = malloc(sizeof(*entry->entry.principal)); 169 if (entry->entry.principal == NULL) { 170 hdb_free_entry (context, entry); 171 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 172 return ENOMEM; 173 } else { 174 hdb_key2principal(context, &key_data, entry->entry.principal); 175 } 176 } 177 return 0; 178 } 179 180 181 static krb5_error_code 182 DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) 183 { 184 return DB_seq(context, db, flags, entry, DB_FIRST); 185 } 186 187 188 static krb5_error_code 189 DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) 190 { 191 return DB_seq(context, db, flags, entry, DB_NEXT); 192 } 193 194 static krb5_error_code 195 DB_rename(krb5_context context, HDB *db, const char *new_name) 196 { 197 int ret; 198 char *old, *new; 199 200 if (strncmp(new_name, "db:", sizeof("db:") - 1) == 0) 201 new_name += sizeof("db:") - 1; 202 else if (strncmp(new_name, "db3:", sizeof("db3:") - 1) == 0) 203 new_name += sizeof("db3:") - 1; 204 205 ret = asprintf(&old, "%s.db", db->hdb_name); 206 if (ret == -1) 207 return ENOMEM; 208 ret = asprintf(&new, "%s.db", new_name); 209 if (ret == -1) { 210 free(old); 211 return ENOMEM; 212 } 213 ret = rename(old, new); 214 free(old); 215 if(ret) { 216 free(new); 217 return errno; 218 } 219 220 free(db->hdb_name); 221 new[strlen(new) - 3] = '\0'; 222 db->hdb_name = new; 223 return 0; 224 } 225 226 static krb5_error_code 227 DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply) 228 { 229 DB *d = (DB*)db->hdb_db; 230 DBT k, v; 231 int code; 232 233 memset(&k, 0, sizeof(DBT)); 234 memset(&v, 0, sizeof(DBT)); 235 k.data = key.data; 236 k.size = key.length; 237 k.flags = 0; 238 code = (*d->get)(d, NULL, &k, &v, 0); 239 if(code == DB_NOTFOUND) 240 return HDB_ERR_NOENTRY; 241 if(code) 242 return code; 243 244 krb5_data_copy(reply, v.data, v.size); 245 return 0; 246 } 247 248 static krb5_error_code 249 DB__put(krb5_context context, HDB *db, int replace, 250 krb5_data key, krb5_data value) 251 { 252 DB3_HDB *db3 = (DB3_HDB *)db; 253 DB *d = (DB*)db->hdb_db; 254 DBT k, v; 255 int code; 256 257 memset(&k, 0, sizeof(DBT)); 258 memset(&v, 0, sizeof(DBT)); 259 k.data = key.data; 260 k.size = key.length; 261 k.flags = 0; 262 v.data = value.data; 263 v.size = value.length; 264 v.flags = 0; 265 code = (*d->put)(d, NULL, &k, &v, replace ? 0 : DB_NOOVERWRITE); 266 if(code == DB_KEYEXIST) 267 return HDB_ERR_EXISTS; 268 if (code) { 269 /* 270 * Berkeley DB 3 and up have a terrible error reporting 271 * interface... 272 * 273 * DB->err() doesn't output a string. 274 * DB->set_errcall()'s callback function doesn't have a void * 275 * argument that can be used to place the error somewhere. 276 * 277 * The only thing we could do is fopen()/fdopen() a file, set it 278 * with DB->set_errfile(), then call DB->err(), then read the 279 * message from the file, unset it with DB->set_errfile(), close 280 * it and delete it. That's a lot of work... so we don't do it. 281 */ 282 if (code == EACCES || code == ENOSPC || code == EINVAL) { 283 krb5_set_error_message(context, code, 284 "Database %s put error: %s", 285 db->hdb_name, strerror(code)); 286 } else { 287 code = HDB_ERR_UK_SERROR; 288 krb5_set_error_message(context, code, 289 "Database %s put error: unknown (%d)", 290 db->hdb_name, code); 291 } 292 return code; 293 } 294 return db->hdb_set_sync(context, db, db3->do_sync); 295 } 296 297 static krb5_error_code 298 DB__del(krb5_context context, HDB *db, krb5_data key) 299 { 300 DB3_HDB *db3 = (DB3_HDB *)db; 301 DB *d = (DB*)db->hdb_db; 302 DBT k; 303 krb5_error_code code; 304 memset(&k, 0, sizeof(DBT)); 305 k.data = key.data; 306 k.size = key.length; 307 k.flags = 0; 308 code = (*d->del)(d, NULL, &k, 0); 309 if(code == DB_NOTFOUND) 310 return HDB_ERR_NOENTRY; 311 if (code) { 312 if (code == EACCES || code == ENOSPC || code == EINVAL) { 313 krb5_set_error_message(context, code, 314 "Database %s del error: %s", 315 db->hdb_name, strerror(code)); 316 } else { 317 code = HDB_ERR_UK_SERROR; 318 krb5_set_error_message(context, code, 319 "Database %s del error: unknown (%d)", 320 db->hdb_name, code); 321 } 322 return code; 323 } 324 return db->hdb_set_sync(context, db, db3->do_sync); 325 } 326 327 #define RD_CACHE_SZ 0x8000 /* Minimal read cache size */ 328 #define WR_CACHE_SZ 0x8000 /* Minimal write cache size */ 329 330 static int 331 _open_db(DB *d, char *fn, int myflags, int flags, mode_t mode, int *fd) 332 { 333 int ret; 334 int cache_size = (myflags & DB_RDONLY) ? RD_CACHE_SZ : WR_CACHE_SZ; 335 336 *fd = open(fn, flags, mode); 337 338 if (*fd == -1) 339 return errno; 340 341 /* 342 * Without DB_FCNTL_LOCKING, the DB library complains when initializing 343 * a database in an empty file. Since the database is our lock file, 344 * we create it before Berkeley DB does, so a new DB always starts empty. 345 */ 346 myflags |= DB_FCNTL_LOCKING; 347 348 ret = flock(*fd, (myflags&DB_RDONLY) ? LOCK_SH : LOCK_EX); 349 if (ret == -1) { 350 ret = errno; 351 close(*fd); 352 *fd = -1; 353 return ret; 354 } 355 356 d->set_cachesize(d, 0, cache_size, 0); 357 358 #if (DB_VERSION_MAJOR > 4) || ((DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR >= 1)) 359 ret = (*d->open)(d, NULL, fn, NULL, DB_BTREE, myflags, mode); 360 #else 361 ret = (*d->open)(d, fn, NULL, DB_BTREE, myflags, mode); 362 #endif 363 364 if (ret != 0) { 365 close(*fd); 366 *fd = -1; 367 } 368 369 return ret; 370 } 371 372 static krb5_error_code 373 DB_open(krb5_context context, HDB *db, int flags, mode_t mode) 374 { 375 DB3_HDB *db3 = (DB3_HDB *)db; 376 DBC *dbc = NULL; 377 char *fn; 378 krb5_error_code ret; 379 DB *d; 380 int myflags = 0; 381 int aret; 382 383 heim_assert(db->hdb_db == 0, "Opening already open HDB"); 384 385 if (flags & O_CREAT) 386 myflags |= DB_CREATE; 387 388 if (flags & O_EXCL) 389 myflags |= DB_EXCL; 390 391 if((flags & O_ACCMODE) == O_RDONLY) 392 myflags |= DB_RDONLY; 393 394 if (flags & O_TRUNC) 395 myflags |= DB_TRUNCATE; 396 397 aret = asprintf(&fn, "%s.db", db->hdb_name); 398 if (aret == -1) { 399 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 400 return ENOMEM; 401 } 402 403 if (db_create(&d, NULL, 0) != 0) { 404 free(fn); 405 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 406 return ENOMEM; 407 } 408 db->hdb_db = d; 409 410 /* From here on out always DB_close() before returning on error */ 411 412 ret = _open_db(d, fn, myflags, flags, mode, &db3->lock_fd); 413 free(fn); 414 if (ret == ENOENT) { 415 /* try to open without .db extension */ 416 ret = _open_db(d, db->hdb_name, myflags, flags, mode, &db3->lock_fd); 417 } 418 419 if (ret) { 420 DB_close(context, db); 421 krb5_set_error_message(context, ret, "opening %s: %s", 422 db->hdb_name, strerror(ret)); 423 return ret; 424 } 425 426 #ifndef DB_CURSOR_BULK 427 # define DB_CURSOR_BULK 0 /* Missing with DB < 4.8 */ 428 #endif 429 ret = (*d->cursor)(d, NULL, &dbc, DB_CURSOR_BULK); 430 431 if (ret) { 432 DB_close(context, db); 433 krb5_set_error_message(context, ret, "d->cursor: %s", strerror(ret)); 434 return ret; 435 } 436 db->hdb_dbc = dbc; 437 438 if((flags & O_ACCMODE) == O_RDONLY) 439 ret = hdb_check_db_format(context, db); 440 else 441 ret = hdb_init_db(context, db); 442 if(ret == HDB_ERR_NOENTRY) 443 return 0; 444 if (ret) { 445 DB_close(context, db); 446 krb5_set_error_message(context, ret, "hdb_open: failed %s database %s", 447 (flags & O_ACCMODE) == O_RDONLY ? 448 "checking format of" : "initialize", 449 db->hdb_name); 450 } 451 452 return ret; 453 } 454 455 krb5_error_code 456 hdb_db3_create(krb5_context context, HDB **db, 457 const char *filename) 458 { 459 DB3_HDB **db3 = (DB3_HDB **)db; 460 *db3 = calloc(1, sizeof(**db3)); /* Allocate space for the larger db3 */ 461 if (*db == NULL) { 462 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 463 return ENOMEM; 464 } 465 466 (*db)->hdb_db = NULL; 467 (*db)->hdb_name = strdup(filename); 468 if ((*db)->hdb_name == NULL) { 469 free(*db); 470 *db = NULL; 471 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 472 return ENOMEM; 473 } 474 (*db)->hdb_master_key_set = 0; 475 (*db)->hdb_openp = 0; 476 (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL; 477 (*db)->hdb_open = DB_open; 478 (*db)->hdb_close = DB_close; 479 (*db)->hdb_fetch_kvno = _hdb_fetch_kvno; 480 (*db)->hdb_store = _hdb_store; 481 (*db)->hdb_remove = _hdb_remove; 482 (*db)->hdb_firstkey = DB_firstkey; 483 (*db)->hdb_nextkey= DB_nextkey; 484 (*db)->hdb_lock = DB_lock; 485 (*db)->hdb_unlock = DB_unlock; 486 (*db)->hdb_rename = DB_rename; 487 (*db)->hdb__get = DB__get; 488 (*db)->hdb__put = DB__put; 489 (*db)->hdb__del = DB__del; 490 (*db)->hdb_destroy = DB_destroy; 491 (*db)->hdb_set_sync = DB_set_sync; 492 493 (*db3)->lock_fd = -1; 494 return 0; 495 } 496 #endif /* HAVE_DB3 */ 497