1/* 2 * Copyright (c) 1997 - 2006 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_DB3 37 38#ifdef HAVE_DBHEADER 39#include <db.h> 40#elif HAVE_DB5_DB_H 41#include <db5/db.h> 42#elif HAVE_DB4_DB_H 43#include <db4/db.h> 44#elif HAVE_DB3_DB_H 45#include <db3/db.h> 46#else 47#include <db.h> 48#endif 49 50static krb5_error_code 51DB_close(krb5_context context, HDB *db) 52{ 53 DB *d = (DB*)db->hdb_db; 54 DBC *dbcp = (DBC*)db->hdb_dbc; 55 56 (*dbcp->c_close)(dbcp); 57 db->hdb_dbc = 0; 58 (*d->close)(d, 0); 59 return 0; 60} 61 62static krb5_error_code 63DB_destroy(krb5_context context, HDB *db) 64{ 65 krb5_error_code ret; 66 67 ret = hdb_clear_master_key (context, db); 68 free(db->hdb_name); 69 free(db); 70 return ret; 71} 72 73static krb5_error_code 74DB_lock(krb5_context context, HDB *db, int operation) 75{ 76 DB *d = (DB*)db->hdb_db; 77 int fd; 78 krb5_error_code ret; 79 80 if (db->lock_count > 1) { 81 db->lock_count++; 82 if (db->lock_count == HDB_WLOCK || db->lock_count == operation) 83 return 0; 84 } 85 86 if ((*d->fd)(d, &fd)) 87 return HDB_ERR_CANT_LOCK_DB; 88 ret = hdb_lock(fd, operation); 89 if (ret) 90 return ret; 91 db->lock_count++; 92 return 0; 93} 94 95static krb5_error_code 96DB_unlock(krb5_context context, HDB *db) 97{ 98 DB *d = (DB*)db->hdb_db; 99 int fd; 100 101 if (db->lock_count > 1) { 102 db->lock_count--; 103 return 0; 104 } 105 heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match"); 106 db->lock_count--; 107 108 if ((*d->fd)(d, &fd)) 109 return HDB_ERR_CANT_LOCK_DB; 110 return hdb_unlock(fd); 111} 112 113 114static krb5_error_code 115DB_seq(krb5_context context, HDB *db, 116 unsigned flags, hdb_entry_ex *entry, int flag) 117{ 118 DBT key, value; 119 DBC *dbcp = db->hdb_dbc; 120 krb5_data key_data, data; 121 int code; 122 123 memset(&key, 0, sizeof(DBT)); 124 memset(&value, 0, sizeof(DBT)); 125 if ((*db->hdb_lock)(context, db, HDB_RLOCK)) 126 return HDB_ERR_DB_INUSE; 127 code = (*dbcp->c_get)(dbcp, &key, &value, flag); 128 (*db->hdb_unlock)(context, db); /* XXX check value */ 129 if (code == DB_NOTFOUND) 130 return HDB_ERR_NOENTRY; 131 if (code) 132 return code; 133 134 key_data.data = key.data; 135 key_data.length = key.size; 136 data.data = value.data; 137 data.length = value.size; 138 memset(entry, 0, sizeof(*entry)); 139 if (hdb_value2entry(context, &data, &entry->entry)) 140 return DB_seq(context, db, flags, entry, DB_NEXT); 141 if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { 142 code = hdb_unseal_keys (context, db, &entry->entry); 143 if (code) 144 hdb_free_entry (context, entry); 145 } 146 if (entry->entry.principal == NULL) { 147 entry->entry.principal = malloc(sizeof(*entry->entry.principal)); 148 if (entry->entry.principal == NULL) { 149 hdb_free_entry (context, entry); 150 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 151 return ENOMEM; 152 } else { 153 hdb_key2principal(context, &key_data, entry->entry.principal); 154 } 155 } 156 return 0; 157} 158 159 160static krb5_error_code 161DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) 162{ 163 return DB_seq(context, db, flags, entry, DB_FIRST); 164} 165 166 167static krb5_error_code 168DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) 169{ 170 return DB_seq(context, db, flags, entry, DB_NEXT); 171} 172 173static krb5_error_code 174DB_rename(krb5_context context, HDB *db, const char *new_name) 175{ 176 int ret; 177 char *old, *new; 178 179 asprintf(&old, "%s.db", db->hdb_name); 180 asprintf(&new, "%s.db", new_name); 181 ret = rename(old, new); 182 free(old); 183 free(new); 184 if(ret) 185 return errno; 186 187 free(db->hdb_name); 188 db->hdb_name = strdup(new_name); 189 return 0; 190} 191 192static krb5_error_code 193DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply) 194{ 195 DB *d = (DB*)db->hdb_db; 196 DBT k, v; 197 int code; 198 199 memset(&k, 0, sizeof(DBT)); 200 memset(&v, 0, sizeof(DBT)); 201 k.data = key.data; 202 k.size = key.length; 203 k.flags = 0; 204 if ((code = (*db->hdb_lock)(context, db, HDB_RLOCK))) 205 return code; 206 code = (*d->get)(d, NULL, &k, &v, 0); 207 (*db->hdb_unlock)(context, db); 208 if(code == DB_NOTFOUND) 209 return HDB_ERR_NOENTRY; 210 if(code) 211 return code; 212 213 krb5_data_copy(reply, v.data, v.size); 214 return 0; 215} 216 217static krb5_error_code 218DB__put(krb5_context context, HDB *db, int replace, 219 krb5_data key, krb5_data value) 220{ 221 DB *d = (DB*)db->hdb_db; 222 DBT k, v; 223 int code; 224 225 memset(&k, 0, sizeof(DBT)); 226 memset(&v, 0, sizeof(DBT)); 227 k.data = key.data; 228 k.size = key.length; 229 k.flags = 0; 230 v.data = value.data; 231 v.size = value.length; 232 v.flags = 0; 233 if ((code = (*db->hdb_lock)(context, db, HDB_WLOCK))) 234 return code; 235 code = (*d->put)(d, NULL, &k, &v, replace ? 0 : DB_NOOVERWRITE); 236 (*db->hdb_unlock)(context, db); 237 if(code == DB_KEYEXIST) 238 return HDB_ERR_EXISTS; 239 if(code) 240 return errno; 241 return 0; 242} 243 244static krb5_error_code 245DB__del(krb5_context context, HDB *db, krb5_data key) 246{ 247 DB *d = (DB*)db->hdb_db; 248 DBT k; 249 krb5_error_code code; 250 memset(&k, 0, sizeof(DBT)); 251 k.data = key.data; 252 k.size = key.length; 253 k.flags = 0; 254 code = (*db->hdb_lock)(context, db, HDB_WLOCK); 255 if(code) 256 return code; 257 code = (*d->del)(d, NULL, &k, 0); 258 (*db->hdb_unlock)(context, db); 259 if(code == DB_NOTFOUND) 260 return HDB_ERR_NOENTRY; 261 if(code) 262 return code; 263 return 0; 264} 265 266static krb5_error_code 267DB_open(krb5_context context, HDB *db, int flags, mode_t mode) 268{ 269 DBC *dbc = NULL; 270 char *fn; 271 krb5_error_code ret; 272 DB *d; 273 int myflags = 0; 274 275 if (flags & O_CREAT) 276 myflags |= DB_CREATE; 277 278 if (flags & O_EXCL) 279 myflags |= DB_EXCL; 280 281 if((flags & O_ACCMODE) == O_RDONLY) 282 myflags |= DB_RDONLY; 283 284 if (flags & O_TRUNC) 285 myflags |= DB_TRUNCATE; 286 287 asprintf(&fn, "%s.db", db->hdb_name); 288 if (fn == NULL) { 289 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 290 return ENOMEM; 291 } 292 if (db_create(&d, NULL, 0) != 0) { 293 free(fn); 294 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 295 return ENOMEM; 296 } 297 db->hdb_db = d; 298 299#if (DB_VERSION_MAJOR >= 4) && (DB_VERSION_MINOR >= 1) 300 ret = (*d->open)(db->hdb_db, NULL, fn, NULL, DB_BTREE, myflags, mode); 301#else 302 ret = (*d->open)(db->hdb_db, fn, NULL, DB_BTREE, myflags, mode); 303#endif 304 305 if (ret == ENOENT) { 306 /* try to open without .db extension */ 307#if (DB_VERSION_MAJOR >= 4) && (DB_VERSION_MINOR >= 1) 308 ret = (*d->open)(db->hdb_db, NULL, db->hdb_name, NULL, DB_BTREE, 309 myflags, mode); 310#else 311 ret = (*d->open)(db->hdb_db, db->hdb_name, NULL, DB_BTREE, 312 myflags, mode); 313#endif 314 } 315 316 if (ret) { 317 free(fn); 318 krb5_set_error_message(context, ret, "opening %s: %s", 319 db->hdb_name, strerror(ret)); 320 return ret; 321 } 322 free(fn); 323 324 ret = (*d->cursor)(d, NULL, &dbc, 0); 325 if (ret) { 326 krb5_set_error_message(context, ret, "d->cursor: %s", strerror(ret)); 327 return ret; 328 } 329 db->hdb_dbc = dbc; 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 return 0; 337 if (ret) { 338 DB_close(context, db); 339 krb5_set_error_message(context, ret, "hdb_open: failed %s database %s", 340 (flags & O_ACCMODE) == O_RDONLY ? 341 "checking format of" : "initialize", 342 db->hdb_name); 343 } 344 345 return ret; 346} 347 348krb5_error_code 349hdb_db_create(krb5_context context, HDB **db, 350 const char *filename) 351{ 352 *db = calloc(1, sizeof(**db)); 353 if (*db == NULL) { 354 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 355 return ENOMEM; 356 } 357 358 (*db)->hdb_db = NULL; 359 (*db)->hdb_name = strdup(filename); 360 if ((*db)->hdb_name == NULL) { 361 free(*db); 362 *db = NULL; 363 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 364 return ENOMEM; 365 } 366 (*db)->hdb_master_key_set = 0; 367 (*db)->hdb_openp = 0; 368 (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL; 369 (*db)->hdb_open = DB_open; 370 (*db)->hdb_close = DB_close; 371 (*db)->hdb_fetch_kvno = _hdb_fetch_kvno; 372 (*db)->hdb_store = _hdb_store; 373 (*db)->hdb_remove = _hdb_remove; 374 (*db)->hdb_firstkey = DB_firstkey; 375 (*db)->hdb_nextkey= DB_nextkey; 376 (*db)->hdb_lock = DB_lock; 377 (*db)->hdb_unlock = DB_unlock; 378 (*db)->hdb_rename = DB_rename; 379 (*db)->hdb__get = DB__get; 380 (*db)->hdb__put = DB__put; 381 (*db)->hdb__del = DB__del; 382 (*db)->hdb_destroy = DB_destroy; 383 return 0; 384} 385#endif /* HAVE_DB3 */ 386