1/* db_berkeley.c--SASL berkeley db interface 2 * Rob Siemborski 3 * Tim Martin 4 * $Id: db_berkeley.c,v 1.11 2011/09/12 08:50:47 mel Exp $ 5 */ 6/* 7 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The name "Carnegie Mellon University" must not be used to 22 * endorse or promote products derived from this software without 23 * prior written permission. For permission or any other legal 24 * details, please contact 25 * Office of Technology Transfer 26 * Carnegie Mellon University 27 * 5000 Forbes Avenue 28 * Pittsburgh, PA 15213-3890 29 * (412) 268-4387, fax: (412) 268-7395 30 * tech-transfer@andrew.cmu.edu 31 * 32 * 4. Redistributions of any form whatsoever must retain the following 33 * acknowledgment: 34 * "This product includes software developed by Computing Services 35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 36 * 37 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 38 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 39 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 40 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 42 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 43 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 44 */ 45 46#include <config.h> 47 48#include <db.h> 49 50#include <sys/stat.h> 51#include <stdlib.h> 52#include <assert.h> 53#include <errno.h> 54#include "sasldb.h" 55 56#define DB_VERSION_FULL ((DB_VERSION_MAJOR << 24) | (DB_VERSION_MINOR << 16) | DB_VERSION_PATCH) 57 58static int db_ok = 0; 59#if defined(KEEP_DB_OPEN) 60static DB * g_db = NULL; 61#endif 62 63/* 64 * Open the database 65 */ 66static int berkeleydb_open(const sasl_utils_t *utils, 67 sasl_conn_t *conn, 68 int rdwr, DB **mbdb) 69{ 70 const char *path = SASL_DB_PATH; 71 int ret; 72 int flags; 73 void *cntxt; 74 sasl_getopt_t *getopt; 75 76#if defined(KEEP_DB_OPEN) 77 if (g_db) { 78 *mbdb = g_db; 79 return SASL_OK; 80 } 81#endif 82 83 if (utils->getcallback(conn, SASL_CB_GETOPT, 84 (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) { 85 const char *p; 86 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK 87 && p != NULL && *p != 0) { 88 path = p; 89 } 90 } 91 92 if (rdwr) flags = DB_CREATE; 93 else flags = DB_RDONLY; 94#if defined(KEEP_DB_OPEN) 95#if defined(DB_THREAD) 96 flags |= DB_THREAD; 97#endif 98#endif 99 100#if DB_VERSION_FULL < 0x03000000 101 ret = db_open(path, DB_HASH, flags, 0660, NULL, NULL, mbdb); 102#else /* DB_VERSION_FULL < 0x03000000 */ 103 ret = db_create(mbdb, NULL, 0); 104 if (ret == 0 && *mbdb != NULL) 105 { 106#if DB_VERSION_FULL >= 0x04010000 107 ret = (*mbdb)->open(*mbdb, NULL, path, NULL, DB_HASH, flags, 0660); 108#else 109 ret = (*mbdb)->open(*mbdb, path, NULL, DB_HASH, flags, 0660); 110#endif 111 if (ret != 0) 112 { 113 (void) (*mbdb)->close(*mbdb, 0); 114 *mbdb = NULL; 115 } 116 } 117#endif /* DB_VERSION_FULL < 0x03000000 */ 118 119 if (ret != 0) { 120 if (rdwr == 0 && ret == ENOENT) { 121 /* File not found and we are only reading the data. 122 Treat as SASL_NOUSER. */ 123 return SASL_NOUSER; 124 } 125 utils->log(conn, SASL_LOG_ERR, 126 "unable to open Berkeley db %s: %s", 127 path, db_strerror(ret)); 128 utils->seterror(conn, SASL_NOLOG, "Unable to open DB"); 129 return SASL_FAIL; 130 } 131 132#if defined(KEEP_DB_OPEN) 133 /* Save the DB handle for later use */ 134 g_db = *mbdb; 135#endif 136 return SASL_OK; 137} 138 139/* 140 * Close the database 141 */ 142static void berkeleydb_close(const sasl_utils_t *utils, DB *mbdb) 143{ 144 int ret; 145 146#if defined(KEEP_DB_OPEN) 147 /* Prevent other threads from reusing the same handle */ 148 /* msdb == g_db */ 149 g_db = NULL; 150#endif 151 152 ret = mbdb->close(mbdb, 0); 153 if (ret!=0) { 154 /* error closing! */ 155 utils->log(NULL, SASL_LOG_ERR, 156 "error closing sasldb: %s", 157 db_strerror(ret)); 158 } 159} 160 161 162/* 163 * Retrieve the secret from the database. 164 * 165 * Return SASL_NOUSER if the entry doesn't exist, 166 * SASL_OK on success. 167 * 168 */ 169int _sasldb_getdata(const sasl_utils_t *utils, 170 sasl_conn_t *context, 171 const char *auth_identity, 172 const char *realm, 173 const char *propName, 174 char *out, const size_t max_out, size_t *out_len) 175{ 176 int result = SASL_OK; 177 char *key; 178 size_t key_len; 179 DBT dbkey, data; 180 DB *mbdb = NULL; 181 182 if(!utils) return SASL_BADPARAM; 183 184 /* check parameters */ 185 if (!auth_identity || !realm || !propName || !out || !max_out) { 186 utils->seterror(context, 0, 187 "Bad parameter in db_berkeley.c: _sasldb_getdata"); 188 return SASL_BADPARAM; 189 } 190 191 if (!db_ok) { 192 utils->seterror(context, 0, 193 "Database not checked"); 194 return SASL_FAIL; 195 } 196 197 /* allocate a key */ 198 result = _sasldb_alloc_key(utils, auth_identity, realm, propName, 199 &key, &key_len); 200 if (result != SASL_OK) { 201 utils->seterror(context, 0, 202 "Could not allocate key in _sasldb_getdata"); 203 return result; 204 } 205 206 /* zero out */ 207 memset(&dbkey, 0, sizeof(dbkey)); 208 memset(&data, 0, sizeof(data)); 209 210 /* open the db */ 211 result = berkeleydb_open(utils, context, 0, &mbdb); 212 if (result != SASL_OK) goto cleanup; 213 214 /* create the key to search for */ 215 dbkey.data = key; 216 dbkey.size = (u_int32_t) key_len; 217 dbkey.flags = DB_DBT_USERMEM; 218 data.flags = DB_DBT_MALLOC; 219 220 /* ask berkeley db for the entry */ 221 result = mbdb->get(mbdb, NULL, &dbkey, &data, 0); 222 223 switch (result) { 224 case 0: 225 /* success */ 226 break; 227 228 case DB_NOTFOUND: 229 result = SASL_NOUSER; 230 utils->seterror(context, SASL_NOLOG, 231 "user: %s@%s property: %s not found in sasldb", 232 auth_identity,realm,propName); 233 goto cleanup; 234 break; 235 default: 236 utils->seterror(context, 0, 237 "error fetching from sasldb: %s", 238 db_strerror(result)); 239 result = SASL_FAIL; 240 goto cleanup; 241 break; 242 } 243 244 if(data.size > max_out + 1) 245 return SASL_BUFOVER; 246 247 if(out_len) *out_len = data.size; 248 memcpy(out, data.data, data.size); 249 out[data.size] = '\0'; 250 251 cleanup: 252 253#if !defined(KEEP_DB_OPEN) 254 if (mbdb != NULL) berkeleydb_close(utils, mbdb); 255#endif 256 257 utils->free(key); 258 utils->free(data.data); 259 260 return result; 261} 262 263/* 264 * Put or delete an entry 265 * 266 * 267 */ 268 269int _sasldb_putdata(const sasl_utils_t *utils, 270 sasl_conn_t *context, 271 const char *authid, 272 const char *realm, 273 const char *propName, 274 const char *data_in, size_t data_len) 275{ 276 int result = SASL_OK; 277 char *key; 278 size_t key_len; 279 DBT dbkey; 280 DB *mbdb = NULL; 281 282 if (!utils) return SASL_BADPARAM; 283 284 if (!authid || !realm || !propName) { 285 utils->seterror(context, 0, 286 "Bad parameter in db_berkeley.c: _sasldb_putdata"); 287 return SASL_BADPARAM; 288 } 289 290 if (!db_ok) { 291 utils->seterror(context, 0, 292 "Database not checked"); 293 return SASL_FAIL; 294 } 295 296 result = _sasldb_alloc_key(utils, authid, realm, propName, 297 &key, &key_len); 298 if (result != SASL_OK) { 299 utils->seterror(context, 0, 300 "Could not allocate key in _sasldb_putdata"); 301 return result; 302 } 303 304 /* open the db */ 305 result=berkeleydb_open(utils, context, 1, &mbdb); 306 if (result!=SASL_OK) goto cleanup; 307 308 /* create the db key */ 309 memset(&dbkey, 0, sizeof(dbkey)); 310 dbkey.data = key; 311 dbkey.size = (u_int32_t) key_len; 312 313 if (data_in) { /* putting secret */ 314 DBT data; 315 316 memset(&data, 0, sizeof(data)); 317 318 data.data = (char *)data_in; 319 if(!data_len) data_len = strlen(data_in); 320 data.size = (u_int32_t) data_len; 321 322 result = mbdb->put(mbdb, NULL, &dbkey, &data, 0); 323 324 if (result != 0) 325 { 326 utils->log(NULL, SASL_LOG_ERR, 327 "error updating sasldb: %s", db_strerror(result)); 328 utils->seterror(context, SASL_NOLOG, 329 "Couldn't update db"); 330 result = SASL_FAIL; 331 goto cleanup; 332 } 333 } else { /* removing secret */ 334 result=mbdb->del(mbdb, NULL, &dbkey, 0); 335 336 if (result != 0) 337 { 338 utils->log(NULL, SASL_LOG_ERR, 339 "error deleting entry from sasldb: %s", db_strerror(result)); 340 utils->seterror(context, SASL_NOLOG, 341 "Couldn't update db"); 342 if (result == DB_NOTFOUND) 343 result = SASL_NOUSER; 344 else 345 result = SASL_FAIL; 346 goto cleanup; 347 } 348 } 349 350 cleanup: 351 352#if !defined(KEEP_DB_OPEN) 353 if (mbdb != NULL) berkeleydb_close(utils, mbdb); 354#endif 355 356 utils->free(key); 357 358 return result; 359} 360 361int _sasl_check_db(const sasl_utils_t *utils, 362 sasl_conn_t *conn) 363{ 364 const char *path = SASL_DB_PATH; 365 int ret; 366 void *cntxt; 367 sasl_getopt_t *getopt; 368 sasl_verifyfile_t *vf; 369 370 if (!utils) return SASL_BADPARAM; 371 372 if (utils->getcallback(conn, SASL_CB_GETOPT, 373 (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) { 374 const char *p; 375 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK 376 && p != NULL && *p != 0) { 377 path = p; 378 } 379 } 380 381 ret = utils->getcallback(conn, SASL_CB_VERIFYFILE, 382 (sasl_callback_ft *)&vf, &cntxt); 383 if (ret != SASL_OK) { 384 utils->seterror(conn, 0, "verifyfile failed"); 385 return ret; 386 } 387 388 ret = vf(cntxt, path, SASL_VRFY_PASSWD); 389 390 if (ret == SASL_OK) { 391 db_ok = 1; 392 } 393 394 if (ret == SASL_OK || ret == SASL_CONTINUE) { 395 return SASL_OK; 396 } else { 397 return ret; 398 } 399} 400 401#if defined(KEEP_DB_OPEN) 402void sasldb_auxprop_free (void *glob_context, 403 const sasl_utils_t *utils) 404{ 405 if (g_db != NULL) berkeleydb_close(utils, g_db); 406} 407#endif 408 409typedef struct berkeleydb_handle 410{ 411 DB *mbdb; 412 DBC *cursor; 413} berkleyhandle_t; 414 415sasldb_handle _sasldb_getkeyhandle(const sasl_utils_t *utils, 416 sasl_conn_t *conn) 417{ 418 int ret; 419 DB *mbdb; 420 berkleyhandle_t *handle; 421 422 if(!utils || !conn) return NULL; 423 424 if(!db_ok) { 425 utils->seterror(conn, 0, "Database not OK in _sasldb_getkeyhandle"); 426 return NULL; 427 } 428 429 ret = berkeleydb_open(utils, conn, 0, &mbdb); 430 431 if (ret != SASL_OK) { 432 return NULL; 433 } 434 435 handle = utils->malloc(sizeof(berkleyhandle_t)); 436 if(!handle) { 437#if !defined(KEEP_DB_OPEN) 438 (void)mbdb->close(mbdb, 0); 439#endif 440 utils->seterror(conn, 0, "Memory error in _sasldb_gethandle"); 441 return NULL; 442 } 443 444 handle->mbdb = mbdb; 445 handle->cursor = NULL; 446 447 return (sasldb_handle)handle; 448} 449 450int _sasldb_getnextkey(const sasl_utils_t *utils __attribute__((unused)), 451 sasldb_handle handle, char *out, 452 const size_t max_out, size_t *out_len) 453{ 454 DB *mbdb; 455 int result; 456 berkleyhandle_t *dbh = (berkleyhandle_t *)handle; 457 DBT key, data; 458 459 if(!utils || !handle || !out || !max_out) 460 return SASL_BADPARAM; 461 462 mbdb = dbh->mbdb; 463 464 memset(&key,0, sizeof(key)); 465 memset(&data,0,sizeof(data)); 466 467 if(!dbh->cursor) { 468 /* make cursor */ 469#if DB_VERSION_FULL < 0x03060000 470 result = mbdb->cursor(mbdb, NULL,&dbh->cursor); 471#else 472 result = mbdb->cursor(mbdb, NULL,&dbh->cursor, 0); 473#endif /* DB_VERSION_FULL < 0x03000000 */ 474 475 if (result!=0) { 476 return SASL_FAIL; 477 } 478 479 /* loop thru */ 480 result = dbh->cursor->c_get(dbh->cursor, &key, &data, 481 DB_FIRST); 482 } else { 483 result = dbh->cursor->c_get(dbh->cursor, &key, &data, 484 DB_NEXT); 485 } 486 487 if (result == DB_NOTFOUND) return SASL_OK; 488 489 if (result != 0) { 490 return SASL_FAIL; 491 } 492 493 if (key.size > max_out) { 494 return SASL_BUFOVER; 495 } 496 497 memcpy(out, key.data, key.size); 498 if (out_len) *out_len = key.size; 499 500 return SASL_CONTINUE; 501} 502 503 504int _sasldb_releasekeyhandle(const sasl_utils_t *utils, 505 sasldb_handle handle) 506{ 507 berkleyhandle_t *dbh = (berkleyhandle_t *)handle; 508 int ret = 0; 509 510 if (!utils || !dbh) return SASL_BADPARAM; 511 512 if (dbh->cursor) { 513 dbh->cursor->c_close(dbh->cursor); 514 } 515 516#if !defined(KEEP_DB_OPEN) 517 /* This is almost the same berkeleydb_close(), except that 518 berkeleydb_close logs a message on error and does not return 519 any error */ 520 if (dbh->mbdb) { 521 ret = dbh->mbdb->close(dbh->mbdb, 0); 522 } 523#endif 524 525 utils->free(dbh); 526 527 if (ret) { 528 return SASL_FAIL; 529 } else { 530 return SASL_OK; 531 } 532} 533