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