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