1 /* 2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 8 * 9 * Openvision retains the copyright to derivative works of 10 * this source code. Do *NOT* create a derivative of this 11 * source code before consulting with your legal department. 12 * Do *NOT* integrate *ANY* of this source code into another 13 * product before consulting with your legal department. 14 * 15 * For further information, read the top-level Openvision 16 * copyright which is contained in the top-level MIT Kerberos 17 * copyright. 18 * 19 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 20 * 21 */ 22 23 24 /* 25 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved 26 */ 27 28 #include <sys/file.h> 29 #include <fcntl.h> 30 #include <unistd.h> 31 #include <k5-int.h> 32 #include "policy_db.h" 33 #include <stdlib.h> 34 #include <db.h> 35 36 #define MAX_LOCK_TRIES 5 37 38 struct _locklist { 39 osa_adb_lock_ent lockinfo; 40 struct _locklist *next; 41 }; 42 43 krb5_error_code osa_adb_create_db(char *filename, char *lockfilename, 44 int magic) 45 { 46 int lf; 47 DB *db; 48 BTREEINFO btinfo; 49 50 memset(&btinfo, 0, sizeof(btinfo)); 51 btinfo.flags = 0; 52 btinfo.cachesize = 0; 53 btinfo.psize = 4096; 54 btinfo.lorder = 0; 55 btinfo.minkeypage = 0; 56 btinfo.compare = NULL; 57 btinfo.prefix = NULL; 58 db = dbopen(filename, O_RDWR | O_CREAT | O_EXCL, 0600, DB_BTREE, &btinfo); 59 if (db == NULL) 60 return errno; 61 if (db->close(db) < 0) 62 return errno; 63 64 /* only create the lock file if we successfully created the db */ 65 lf = THREEPARAMOPEN(lockfilename, O_RDWR | O_CREAT | O_EXCL, 0600); 66 if (lf == -1) 67 return errno; 68 (void) close(lf); 69 70 return OSA_ADB_OK; 71 } 72 73 krb5_error_code osa_adb_destroy_db(char *filename, char *lockfilename, 74 int magic) 75 { 76 /* the admin databases do not contain security-critical data */ 77 if (unlink(filename) < 0 || 78 unlink(lockfilename) < 0) 79 return errno; 80 return OSA_ADB_OK; 81 } 82 83 krb5_error_code osa_adb_rename_db(char *filefrom, char *lockfrom, 84 char *fileto, char *lockto, int magic) 85 { 86 osa_adb_db_t fromdb, todb; 87 krb5_error_code ret; 88 89 /* make sure todb exists */ 90 /*LINTED*/ 91 if ((ret = osa_adb_create_db(fileto, lockto, magic)) && 92 ret != EEXIST) 93 return ret; 94 95 if ((ret = osa_adb_init_db(&fromdb, filefrom, lockfrom, magic))) 96 return ret; 97 if ((ret = osa_adb_init_db(&todb, fileto, lockto, magic))) { 98 (void) osa_adb_fini_db(fromdb, magic); 99 return ret; 100 } 101 if ((ret = osa_adb_get_lock(fromdb, KRB5_DB_LOCKMODE_PERMANENT))) { 102 (void) osa_adb_fini_db(fromdb, magic); 103 (void) osa_adb_fini_db(todb, magic); 104 return ret; 105 } 106 if ((ret = osa_adb_get_lock(todb, KRB5_DB_LOCKMODE_PERMANENT))) { 107 (void) osa_adb_fini_db(fromdb, magic); 108 (void) osa_adb_fini_db(todb, magic); 109 return ret; 110 } 111 if ((rename(filefrom, fileto) < 0)) { 112 (void) osa_adb_fini_db(fromdb, magic); 113 (void) osa_adb_fini_db(todb, magic); 114 return errno; 115 } 116 /* 117 * Do not release the lock on fromdb because it is being renamed 118 * out of existence; no one can ever use it again. 119 */ 120 if ((ret = osa_adb_release_lock(todb))) { 121 (void) osa_adb_fini_db(fromdb, magic); 122 (void) osa_adb_fini_db(todb, magic); 123 return ret; 124 } 125 126 (void) osa_adb_fini_db(fromdb, magic); 127 (void) osa_adb_fini_db(todb, magic); 128 return 0; 129 } 130 131 krb5_error_code osa_adb_init_db(osa_adb_db_t *dbp, char *filename, 132 char *lockfilename, int magic) 133 { 134 osa_adb_db_t db; 135 static struct _locklist *locklist = NULL; 136 struct _locklist *lockp; 137 krb5_error_code code; 138 139 if (dbp == NULL || filename == NULL) 140 return EINVAL; 141 142 db = (osa_adb_princ_t) malloc(sizeof(osa_adb_db_ent)); 143 if (db == NULL) 144 return ENOMEM; 145 146 memset(db, 0, sizeof(*db)); 147 db->info.hash = NULL; 148 db->info.bsize = 256; 149 db->info.ffactor = 8; 150 db->info.nelem = 25000; 151 db->info.lorder = 0; 152 153 db->btinfo.flags = 0; 154 db->btinfo.cachesize = 0; 155 db->btinfo.psize = 4096; 156 db->btinfo.lorder = 0; 157 db->btinfo.minkeypage = 0; 158 db->btinfo.compare = NULL; 159 db->btinfo.prefix = NULL; 160 /* 161 * A process is allowed to open the same database multiple times 162 * and access it via different handles. If the handles use 163 * distinct lockinfo structures, things get confused: lock(A), 164 * lock(B), release(B) will result in the kernel unlocking the 165 * lock file but handle A will still think the file is locked. 166 * Therefore, all handles using the same lock file must share a 167 * single lockinfo structure. 168 * 169 * It is not sufficient to have a single lockinfo structure, 170 * however, because a single process may also wish to open 171 * multiple different databases simultaneously, with different 172 * lock files. This code used to use a single static lockinfo 173 * structure, which means that the second database opened used 174 * the first database's lock file. This was Bad. 175 * 176 * We now maintain a linked list of lockinfo structures, keyed by 177 * lockfilename. An entry is added when this function is called 178 * with a new lockfilename, and all subsequent calls with that 179 * lockfilename use the existing entry, updating the refcnt. 180 * When the database is closed with fini_db(), the refcnt is 181 * decremented, and when it is zero the lockinfo structure is 182 * freed and reset. The entry in the linked list, however, is 183 * never removed; it will just be reinitialized the next time 184 * init_db is called with the right lockfilename. 185 */ 186 187 /* find or create the lockinfo structure for lockfilename */ 188 lockp = locklist; 189 while (lockp) { 190 if (strcmp(lockp->lockinfo.filename, lockfilename) == 0) 191 break; 192 else 193 lockp = lockp->next; 194 } 195 if (lockp == NULL) { 196 /* doesn't exist, create it, add to list */ 197 lockp = (struct _locklist *) malloc(sizeof(*lockp)); 198 if (lockp == NULL) { 199 free(db); 200 return ENOMEM; 201 } 202 memset(lockp, 0, sizeof(*lockp)); 203 lockp->next = locklist; 204 locklist = lockp; 205 } 206 207 /* now initialize lockp->lockinfo if necessary */ 208 if (lockp->lockinfo.lockfile == NULL) { 209 if ((code = krb5int_init_context_kdc(&lockp->lockinfo.context))) { 210 free(db); 211 return((krb5_error_code) code); 212 } 213 214 /* 215 * needs be open read/write so that write locking can work with 216 * POSIX systems 217 */ 218 lockp->lockinfo.filename = strdup(lockfilename); 219 if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r+F")) == NULL) { 220 /* 221 * maybe someone took away write permission so we could only 222 * get shared locks? 223 */ 224 if ((lockp->lockinfo.lockfile = fopen(lockfilename, "rF")) 225 == NULL) { 226 free(db); 227 return OSA_ADB_NOLOCKFILE; 228 } 229 } 230 lockp->lockinfo.lockmode = lockp->lockinfo.lockcnt = 0; 231 } 232 233 /* lockp is set, lockinfo is initialized, update the reference count */ 234 db->lock = &lockp->lockinfo; 235 db->lock->refcnt++; 236 237 db->opencnt = 0; 238 db->filename = strdup(filename); 239 db->magic = magic; 240 241 *dbp = db; 242 243 return OSA_ADB_OK; 244 } 245 246 krb5_error_code osa_adb_fini_db(osa_adb_db_t db, int magic) 247 { 248 if (db->magic != magic) 249 return EINVAL; 250 if (db->lock->refcnt == 0) { 251 /* barry says this can't happen */ 252 return OSA_ADB_FAILURE; 253 } else { 254 db->lock->refcnt--; 255 } 256 257 if (db->lock->refcnt == 0) { 258 /* 259 * Don't free db->lock->filename, it is used as a key to 260 * find the lockinfo entry in the linked list. If the 261 * lockfile doesn't exist, we must be closing the database 262 * after trashing it. This has to be allowed, so don't 263 * generate an error. 264 */ 265 if (db->lock->lockmode != KRB5_DB_LOCKMODE_PERMANENT) 266 (void) fclose(db->lock->lockfile); 267 db->lock->lockfile = NULL; 268 krb5_free_context(db->lock->context); 269 } 270 271 db->magic = 0; 272 free(db->filename); 273 free(db); 274 return OSA_ADB_OK; 275 } 276 277 krb5_error_code osa_adb_get_lock(osa_adb_db_t db, int mode) 278 { 279 int tries, gotlock, perm, krb5_mode, ret = 0; 280 281 if (db->lock->lockmode >= mode) { 282 /* No need to upgrade lock, just incr refcnt and return */ 283 db->lock->lockcnt++; 284 return(OSA_ADB_OK); 285 } 286 287 perm = 0; 288 switch (mode) { 289 case KRB5_DB_LOCKMODE_PERMANENT: 290 perm = 1; 291 /* FALLTHROUGH */ 292 case KRB5_DB_LOCKMODE_EXCLUSIVE: 293 krb5_mode = KRB5_LOCKMODE_EXCLUSIVE; 294 break; 295 case KRB5_DB_LOCKMODE_SHARED: 296 krb5_mode = KRB5_LOCKMODE_SHARED; 297 break; 298 default: 299 return(EINVAL); 300 } 301 302 for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) { 303 if ((ret = krb5_lock_file(db->lock->context, 304 fileno(db->lock->lockfile), 305 krb5_mode|KRB5_LOCKMODE_DONTBLOCK)) == 0) { 306 gotlock++; 307 break; 308 } else if (ret == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE) 309 /* tried to exclusive-lock something we don't have */ 310 /* write access to */ 311 return OSA_ADB_NOEXCL_PERM; 312 313 sleep(1); 314 } 315 316 /* test for all the likely "can't get lock" error codes */ 317 if (ret == EACCES || ret == EAGAIN || ret == EWOULDBLOCK) 318 return OSA_ADB_CANTLOCK_DB; 319 else if (ret != 0) 320 return ret; 321 322 /* 323 * If the file no longer exists, someone acquired a permanent 324 * lock. If that process terminates its exclusive lock is lost, 325 * but if we already had the file open we can (probably) lock it 326 * even though it has been unlinked. So we need to insist that 327 * it exist. 328 */ 329 if (access(db->lock->filename, F_OK) < 0) { 330 (void) krb5_lock_file(db->lock->context, 331 fileno(db->lock->lockfile), 332 KRB5_LOCKMODE_UNLOCK); 333 return OSA_ADB_NOLOCKFILE; 334 } 335 336 /* we have the shared/exclusive lock */ 337 338 if (perm) { 339 if (unlink(db->lock->filename) < 0) { 340 /* somehow we can't delete the file, but we already */ 341 /* have the lock, so release it and return */ 342 343 ret = errno; 344 (void) krb5_lock_file(db->lock->context, 345 fileno(db->lock->lockfile), 346 KRB5_LOCKMODE_UNLOCK); 347 348 /* maybe we should return CANTLOCK_DB.. but that would */ 349 /* look just like the db was already locked */ 350 return ret; 351 } 352 353 /* this releases our exclusive lock.. which is okay because */ 354 /* now no one else can get one either */ 355 (void) fclose(db->lock->lockfile); 356 } 357 358 db->lock->lockmode = mode; 359 db->lock->lockcnt++; 360 return OSA_ADB_OK; 361 } 362 363 krb5_error_code osa_adb_release_lock(osa_adb_db_t db) 364 { 365 int ret, fd; 366 367 if (!db->lock->lockcnt) /* lock already unlocked */ 368 return OSA_ADB_NOTLOCKED; 369 370 if (--db->lock->lockcnt == 0) { 371 if (db->lock->lockmode == KRB5_DB_LOCKMODE_PERMANENT) { 372 /* now we need to create the file since it does not exist */ 373 fd = THREEPARAMOPEN(db->lock->filename,O_RDWR | O_CREAT | O_EXCL, 374 0600); 375 if ((db->lock->lockfile = fdopen(fd, "w+F")) == NULL) 376 return OSA_ADB_NOLOCKFILE; 377 } else if ((ret = krb5_lock_file(db->lock->context, 378 fileno(db->lock->lockfile), 379 KRB5_LOCKMODE_UNLOCK))) 380 return ret; 381 382 db->lock->lockmode = 0; 383 } 384 return OSA_ADB_OK; 385 } 386 387 krb5_error_code osa_adb_open_and_lock(osa_adb_princ_t db, int locktype) 388 { 389 int ret; 390 391 ret = osa_adb_get_lock(db, locktype); 392 if (ret != OSA_ADB_OK) 393 return ret; 394 if (db->opencnt) 395 goto open_ok; 396 397 db->db = dbopen(db->filename, O_RDWR, 0600, DB_BTREE, &db->btinfo); 398 if (db->db != NULL) 399 goto open_ok; 400 switch (errno) { 401 #ifdef EFTYPE 402 case EFTYPE: 403 #endif 404 case EINVAL: 405 db->db = dbopen(db->filename, O_RDWR, 0600, DB_HASH, &db->info); 406 if (db->db != NULL) 407 goto open_ok; 408 /* FALLTHROUGH */ 409 default: 410 (void) osa_adb_release_lock(db); 411 if (errno == EINVAL) 412 return OSA_ADB_BAD_DB; 413 return errno; 414 } 415 open_ok: 416 db->opencnt++; 417 return OSA_ADB_OK; 418 } 419 420 krb5_error_code osa_adb_close_and_unlock(osa_adb_princ_t db) 421 { 422 if (--db->opencnt) 423 return osa_adb_release_lock(db); 424 if(db->db != NULL && db->db->close(db->db) == -1) { 425 (void) osa_adb_release_lock(db); 426 return OSA_ADB_FAILURE; 427 } 428 429 db->db = NULL; 430 431 return(osa_adb_release_lock(db)); 432 } 433