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
osa_adb_create_db(char * filename,char * lockfilename,int magic)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
osa_adb_destroy_db(char * filename,char * lockfilename,int magic)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
osa_adb_rename_db(char * filefrom,char * lockfrom,char * fileto,char * lockto,int magic)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
osa_adb_init_db(osa_adb_db_t * dbp,char * filename,char * lockfilename,int magic)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
osa_adb_fini_db(osa_adb_db_t db,int magic)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
osa_adb_get_lock(osa_adb_db_t db,int mode)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
osa_adb_release_lock(osa_adb_db_t db)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
osa_adb_open_and_lock(osa_adb_princ_t db,int locktype)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
osa_adb_close_and_unlock(osa_adb_princ_t db)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