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