Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/plugins/kdb/db2/adb_openclose.c
34914 views
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
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
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
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
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
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
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
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
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
357