xref: /onnv-gate/usr/src/lib/krb5/plugins/kdb/db2/adb_openclose.c (revision 4960:a4746a82a247)
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