Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/plugins/kdb/db2/kdb_db2.c
34923 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* plugins/kdb/db2/kdb_db2.c */
3
/*
4
* Copyright 1997,2006,2007-2009 by the Massachusetts Institute of Technology.
5
* All Rights Reserved.
6
*
7
* Export of this software from the United States of America may
8
* require a specific license from the United States Government.
9
* It is the responsibility of any person or organization contemplating
10
* export to obtain such a license before exporting.
11
*
12
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13
* distribute this software and its documentation for any purpose and
14
* without fee is hereby granted, provided that the above copyright
15
* notice appear in all copies and that both that copyright notice and
16
* this permission notice appear in supporting documentation, and that
17
* the name of M.I.T. not be used in advertising or publicity pertaining
18
* to distribution of the software without specific, written prior
19
* permission. Furthermore if you modify this software you must label
20
* your software as modified software and not distribute it in such a
21
* fashion that it might be confused with the original M.I.T. software.
22
* M.I.T. makes no representations about the suitability of
23
* this software for any purpose. It is provided "as is" without express
24
* or implied warranty.
25
*
26
*/
27
28
/*
29
* Copyright (C) 1998 by the FundsXpress, INC.
30
*
31
* All rights reserved.
32
*
33
* Export of this software from the United States of America may require
34
* a specific license from the United States Government. It is the
35
* responsibility of any person or organization contemplating export to
36
* obtain such a license before exporting.
37
*
38
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
39
* distribute this software and its documentation for any purpose and
40
* without fee is hereby granted, provided that the above copyright
41
* notice appear in all copies and that both that copyright notice and
42
* this permission notice appear in supporting documentation, and that
43
* the name of FundsXpress. not be used in advertising or publicity pertaining
44
* to distribution of the software without specific, written prior
45
* permission. FundsXpress makes no representations about the suitability of
46
* this software for any purpose. It is provided "as is" without express
47
* or implied warranty.
48
*
49
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
50
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
51
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
52
*/
53
54
#include "k5-int.h"
55
56
#if HAVE_UNISTD_H
57
#include <unistd.h>
58
#endif
59
60
#include <db.h>
61
#include <stdio.h>
62
#include <errno.h>
63
#include <utime.h>
64
#include "kdb5.h"
65
#include "kdb_db2.h"
66
#include "kdb_xdr.h"
67
#include "policy_db.h"
68
69
#define KDB_DB2_DATABASE_NAME "database_name"
70
71
#define SUFFIX_DB ""
72
#define SUFFIX_LOCK ".ok"
73
#define SUFFIX_POLICY ".kadm5"
74
#define SUFFIX_POLICY_LOCK ".kadm5.lock"
75
76
/*
77
* Locking:
78
*
79
* There are two distinct locking protocols used. One is designed to
80
* lock against processes (the admin_server, for one) which make
81
* incremental changes to the database; the other is designed to lock
82
* against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the
83
* entire database in one fell swoop.
84
*
85
* The first locking protocol is implemented using flock() in the
86
* krb_dbl_lock() and krb_dbl_unlock routines.
87
*
88
* The second locking protocol is necessary because DBM "files" are
89
* actually implemented as two separate files, and it is impossible to
90
* atomically rename two files simultaneously. It assumes that the
91
* database is replaced only very infrequently in comparison to the time
92
* needed to do a database read operation.
93
*
94
* A third file is used as a "version" semaphore; the modification
95
* time of this file is the "version number" of the database.
96
* At the start of a read operation, the reader checks the version
97
* number; at the end of the read operation, it checks again. If the
98
* version number changed, or if the semaphore was nonexistent at
99
* either time, the reader sleeps for a second to let things
100
* stabilize, and then tries again; if it does not succeed after
101
* KRB5_DBM_MAX_RETRY attempts, it gives up.
102
*
103
* On update, the semaphore file is deleted (if it exists) before any
104
* update takes place; at the end of the update, it is replaced, with
105
* a version number strictly greater than the version number which
106
* existed at the start of the update.
107
*
108
* If the system crashes in the middle of an update, the semaphore
109
* file is not automatically created on reboot; this is a feature, not
110
* a bug, since the database may be inconsistent. Note that the
111
* absence of a semaphore file does not prevent another _update_ from
112
* taking place later. Database replacements take place automatically
113
* only on replica servers; a crash in the middle of an update will be
114
* fixed by the next propagation. A crash in the middle of an on the
115
* master would be somewhat more serious, but this would likely be
116
* noticed by an administrator, who could fix the problem and retry
117
* the operation.
118
*/
119
120
/* Evaluate to true if the krb5_context c contains an initialized db2
121
* context. */
122
#define inited(c) ((c)->dal_handle->db_context && \
123
((krb5_db2_context *)(c)->dal_handle->db_context)-> \
124
db_inited)
125
126
static krb5_error_code
127
get_db_opt(char *input, char **opt, char **val)
128
{
129
char *pos = strchr(input, '=');
130
if (pos == NULL) {
131
*opt = NULL;
132
*val = strdup(input);
133
if (*val == NULL) {
134
return ENOMEM;
135
}
136
} else {
137
*opt = malloc((pos - input) + 1);
138
*val = strdup(pos + 1);
139
if (!*opt || !*val) {
140
free(*opt);
141
*opt = NULL;
142
free(*val);
143
*val = NULL;
144
return ENOMEM;
145
}
146
memcpy(*opt, input, pos - input);
147
(*opt)[pos - input] = '\0';
148
}
149
return (0);
150
151
}
152
153
/* Restore dbctx to the uninitialized state. */
154
static void
155
ctx_clear(krb5_db2_context *dbc)
156
{
157
/*
158
* Free any dynamically allocated memory. File descriptors and locks
159
* are the caller's problem.
160
*/
161
free(dbc->db_lf_name);
162
free(dbc->db_name);
163
/*
164
* Clear the structure and reset the defaults.
165
*/
166
memset(dbc, 0, sizeof(krb5_db2_context));
167
dbc->db = NULL;
168
dbc->db_lf_name = NULL;
169
dbc->db_lf_file = -1;
170
dbc->db_name = NULL;
171
dbc->db_nb_locks = FALSE;
172
dbc->tempdb = FALSE;
173
}
174
175
/* Set *dbc_out to the db2 database context for context. If one does not
176
* exist, create one in the uninitialized state. */
177
static krb5_error_code
178
ctx_get(krb5_context context, krb5_db2_context **dbc_out)
179
{
180
krb5_db2_context *dbc;
181
kdb5_dal_handle *dal_handle;
182
183
dal_handle = context->dal_handle;
184
185
if (dal_handle->db_context == NULL) {
186
dbc = (krb5_db2_context *) malloc(sizeof(krb5_db2_context));
187
if (dbc == NULL)
188
return ENOMEM;
189
else {
190
memset(dbc, 0, sizeof(krb5_db2_context));
191
ctx_clear(dbc);
192
dal_handle->db_context = dbc;
193
}
194
}
195
*dbc_out = dal_handle->db_context;
196
return 0;
197
}
198
199
/* Using db_args and the profile, initialize the configurable parameters of the
200
* DB context inside context. */
201
static krb5_error_code
202
configure_context(krb5_context context, char *conf_section, char **db_args)
203
{
204
krb5_error_code status;
205
krb5_db2_context *dbc;
206
char **t_ptr, *opt = NULL, *val = NULL, *pval = NULL;
207
profile_t profile = KRB5_DB_GET_PROFILE(context);
208
int bval;
209
210
status = ctx_get(context, &dbc);
211
if (status != 0)
212
return status;
213
214
/* Allow unlockiter to be overridden by command line db_args. */
215
status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
216
KRB5_CONF_UNLOCKITER, FALSE, &bval);
217
if (status != 0)
218
goto cleanup;
219
dbc->unlockiter = bval;
220
221
for (t_ptr = db_args; t_ptr && *t_ptr; t_ptr++) {
222
free(opt);
223
free(val);
224
status = get_db_opt(*t_ptr, &opt, &val);
225
if (opt && !strcmp(opt, "dbname")) {
226
dbc->db_name = strdup(val);
227
if (dbc->db_name == NULL) {
228
status = ENOMEM;
229
goto cleanup;
230
}
231
}
232
else if (!opt && !strcmp(val, "temporary")) {
233
dbc->tempdb = 1;
234
} else if (!opt && !strcmp(val, "merge_nra")) {
235
;
236
} else if (opt && !strcmp(opt, "hash")) {
237
dbc->hashfirst = TRUE;
238
} else if (!opt && !strcmp(val, "unlockiter")) {
239
dbc->unlockiter = TRUE;
240
} else if (!opt && !strcmp(val, "lockiter")) {
241
dbc->unlockiter = FALSE;
242
} else {
243
status = EINVAL;
244
k5_setmsg(context, status,
245
_("Unsupported argument \"%s\" for db2"),
246
opt ? opt : val);
247
goto cleanup;
248
}
249
}
250
251
if (dbc->db_name == NULL) {
252
/* Check for database_name in the db_module section. */
253
status = profile_get_string(profile, KDB_MODULE_SECTION, conf_section,
254
KDB_DB2_DATABASE_NAME, NULL, &pval);
255
if (status == 0 && pval == NULL) {
256
/* For compatibility, check for database_name in the realm. */
257
status = profile_get_string(profile, KDB_REALM_SECTION,
258
KRB5_DB_GET_REALM(context),
259
KDB_DB2_DATABASE_NAME,
260
DEFAULT_KDB_FILE, &pval);
261
}
262
if (status != 0)
263
goto cleanup;
264
dbc->db_name = strdup(pval);
265
}
266
267
status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
268
KRB5_CONF_DISABLE_LAST_SUCCESS, FALSE, &bval);
269
if (status != 0)
270
goto cleanup;
271
dbc->disable_last_success = bval;
272
273
status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
274
KRB5_CONF_DISABLE_LOCKOUT, FALSE, &bval);
275
if (status != 0)
276
goto cleanup;
277
dbc->disable_lockout = bval;
278
279
cleanup:
280
free(opt);
281
free(val);
282
profile_release_string(pval);
283
return status;
284
}
285
286
/*
287
* Set *out to one of the filenames used for the DB described by dbc. sfx
288
* should be one of SUFFIX_DB, SUFFIX_LOCK, SUFFIX_POLICY, or
289
* SUFFIX_POLICY_LOCK.
290
*/
291
static krb5_error_code
292
ctx_dbsuffix(krb5_db2_context *dbc, const char *sfx, char **out)
293
{
294
char *result;
295
const char *tilde;
296
297
*out = NULL;
298
tilde = dbc->tempdb ? "~" : "";
299
if (asprintf(&result, "%s%s%s", dbc->db_name, tilde, sfx) < 0)
300
return ENOMEM;
301
*out = result;
302
return 0;
303
}
304
305
/* Generate all four files corresponding to dbc. */
306
static krb5_error_code
307
ctx_allfiles(krb5_db2_context *dbc, char **dbname_out, char **lockname_out,
308
char **polname_out, char **plockname_out)
309
{
310
char *a = NULL, *b = NULL, *c = NULL, *d = NULL;
311
312
*dbname_out = *lockname_out = *polname_out = *plockname_out = NULL;
313
if (ctx_dbsuffix(dbc, SUFFIX_DB, &a))
314
goto error;
315
if (ctx_dbsuffix(dbc, SUFFIX_LOCK, &b))
316
goto error;
317
if (ctx_dbsuffix(dbc, SUFFIX_POLICY, &c))
318
goto error;
319
if (ctx_dbsuffix(dbc, SUFFIX_POLICY_LOCK, &d))
320
goto error;
321
*dbname_out = a;
322
*lockname_out = b;
323
*polname_out = c;
324
*plockname_out = d;
325
return 0;
326
error:
327
free(a);
328
free(b);
329
free(c);
330
free(d);
331
return ENOMEM;
332
}
333
334
/*
335
* Open the DB2 database described by dbc, using the specified flags and mode,
336
* and return the resulting handle. Try both hash and btree database types;
337
* dbc->hashfirst determines which is attempted first. If dbc->hashfirst
338
* indicated the wrong type, update it to indicate the correct type.
339
*/
340
static krb5_error_code
341
open_db(krb5_context context, krb5_db2_context *dbc, int flags, int mode,
342
DB **db_out)
343
{
344
char *fname = NULL;
345
DB *db;
346
BTREEINFO bti;
347
HASHINFO hashi;
348
bti.flags = 0;
349
bti.cachesize = 0;
350
bti.psize = 4096;
351
bti.lorder = 0;
352
bti.minkeypage = 0;
353
bti.compare = NULL;
354
bti.prefix = NULL;
355
356
*db_out = NULL;
357
358
if (ctx_dbsuffix(dbc, SUFFIX_DB, &fname) != 0)
359
return ENOMEM;
360
361
hashi.bsize = 4096;
362
hashi.cachesize = 0;
363
hashi.ffactor = 40;
364
hashi.hash = NULL;
365
hashi.lorder = 0;
366
hashi.nelem = 1;
367
368
/* Try our best guess at the database type. */
369
db = dbopen(fname, flags, mode,
370
dbc->hashfirst ? DB_HASH : DB_BTREE,
371
dbc->hashfirst ? (void *) &hashi : (void *) &bti);
372
373
if (db == NULL && IS_EFTYPE(errno)) {
374
db = dbopen(fname, flags, mode,
375
dbc->hashfirst ? DB_BTREE : DB_HASH,
376
dbc->hashfirst ? (void *) &bti : (void *) &hashi);
377
/* If that worked, update our guess for next time. */
378
if (db != NULL)
379
dbc->hashfirst = !dbc->hashfirst;
380
}
381
382
/* Don't try unlocked iteration with a hash database. */
383
if (db != NULL && dbc->hashfirst)
384
dbc->unlockiter = FALSE;
385
386
if (db == NULL) {
387
k5_prependmsg(context, errno, _("Cannot open DB2 database '%s'"),
388
fname);
389
}
390
391
*db_out = db;
392
free(fname);
393
return (db == NULL) ? errno : 0;
394
}
395
396
static krb5_error_code
397
ctx_unlock(krb5_context context, krb5_db2_context *dbc)
398
{
399
krb5_error_code retval, retval2;
400
DB *db;
401
402
retval = osa_adb_release_lock(dbc->policy_db);
403
404
if (!dbc->db_locks_held) /* lock already unlocked */
405
return KRB5_KDB_NOTLOCKED;
406
407
db = dbc->db;
408
if (--(dbc->db_locks_held) == 0) {
409
db->close(db);
410
dbc->db = NULL;
411
dbc->db_lock_mode = 0;
412
413
retval2 = krb5_lock_file(context, dbc->db_lf_file,
414
KRB5_LOCKMODE_UNLOCK);
415
if (retval2)
416
return retval2;
417
}
418
419
/* We may be unlocking because osa_adb_get_lock() failed. */
420
if (retval == OSA_ADB_NOTLOCKED)
421
return 0;
422
return retval;
423
}
424
425
static krb5_error_code
426
ctx_lock(krb5_context context, krb5_db2_context *dbc, int lockmode)
427
{
428
krb5_error_code retval;
429
int kmode;
430
431
if (lockmode == KRB5_DB_LOCKMODE_PERMANENT ||
432
lockmode == KRB5_DB_LOCKMODE_EXCLUSIVE)
433
kmode = KRB5_LOCKMODE_EXCLUSIVE;
434
else if (lockmode == KRB5_DB_LOCKMODE_SHARED)
435
kmode = KRB5_LOCKMODE_SHARED;
436
else
437
return EINVAL;
438
439
if (dbc->db_locks_held == 0 || dbc->db_lock_mode < kmode) {
440
/* Acquire or upgrade the lock. */
441
retval = krb5_lock_file(context, dbc->db_lf_file, kmode);
442
/* Check if we tried to lock something not open for write. */
443
if (retval == EBADF && kmode == KRB5_LOCKMODE_EXCLUSIVE)
444
return KRB5_KDB_CANTLOCK_DB;
445
else if (retval == EACCES || retval == EAGAIN || retval == EWOULDBLOCK)
446
return KRB5_KDB_CANTLOCK_DB;
447
else if (retval)
448
return retval;
449
450
/* Open the DB (or re-open it for read/write). */
451
if (dbc->db != NULL)
452
dbc->db->close(dbc->db);
453
retval = open_db(context, dbc,
454
kmode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR,
455
0600, &dbc->db);
456
if (retval) {
457
dbc->db_locks_held = 0;
458
dbc->db_lock_mode = 0;
459
(void) osa_adb_release_lock(dbc->policy_db);
460
(void) krb5_lock_file(context, dbc->db_lf_file,
461
KRB5_LOCKMODE_UNLOCK);
462
return retval;
463
}
464
465
dbc->db_lock_mode = kmode;
466
}
467
dbc->db_locks_held++;
468
469
/* Acquire or upgrade the policy lock. */
470
retval = osa_adb_get_lock(dbc->policy_db, lockmode);
471
if (retval) {
472
(void) ctx_unlock(context, dbc);
473
if (retval == OSA_ADB_NOEXCL_PERM || retval == OSA_ADB_CANTLOCK_DB ||
474
retval == OSA_ADB_NOLOCKFILE)
475
retval = KRB5_KDB_CANTLOCK_DB;
476
}
477
return retval;
478
}
479
480
/* Initialize the lock file and policy database fields of dbc. The db_name and
481
* tempdb fields must already be set. */
482
static krb5_error_code
483
ctx_init(krb5_db2_context *dbc)
484
{
485
krb5_error_code retval;
486
char *polname = NULL, *plockname = NULL;
487
488
retval = ctx_dbsuffix(dbc, SUFFIX_LOCK, &dbc->db_lf_name);
489
if (retval)
490
return retval;
491
492
/*
493
* should be opened read/write so that write locking can work with
494
* POSIX systems
495
*/
496
if ((dbc->db_lf_file = open(dbc->db_lf_name, O_RDWR, 0666)) < 0) {
497
if ((dbc->db_lf_file = open(dbc->db_lf_name, O_RDONLY, 0666)) < 0) {
498
retval = errno;
499
goto cleanup;
500
}
501
}
502
set_cloexec_fd(dbc->db_lf_file);
503
dbc->db_inited++;
504
505
retval = ctx_dbsuffix(dbc, SUFFIX_POLICY, &polname);
506
if (retval)
507
goto cleanup;
508
retval = ctx_dbsuffix(dbc, SUFFIX_POLICY_LOCK, &plockname);
509
if (retval)
510
goto cleanup;
511
retval = osa_adb_init_db(&dbc->policy_db, polname, plockname,
512
OSA_ADB_POLICY_DB_MAGIC);
513
514
cleanup:
515
free(polname);
516
free(plockname);
517
if (retval)
518
ctx_clear(dbc);
519
return retval;
520
}
521
522
static void
523
ctx_fini(krb5_db2_context *dbc)
524
{
525
if (dbc->db_lf_file != -1)
526
(void) close(dbc->db_lf_file);
527
if (dbc->policy_db)
528
(void) osa_adb_fini_db(dbc->policy_db, OSA_ADB_POLICY_DB_MAGIC);
529
ctx_clear(dbc);
530
free(dbc);
531
}
532
533
krb5_error_code
534
krb5_db2_fini(krb5_context context)
535
{
536
if (context->dal_handle->db_context != NULL) {
537
ctx_fini(context->dal_handle->db_context);
538
context->dal_handle->db_context = NULL;
539
}
540
return 0;
541
}
542
543
/* Return successfully if the db2 name set in context can be opened. */
544
static krb5_error_code
545
check_openable(krb5_context context)
546
{
547
krb5_error_code retval;
548
DB *db;
549
krb5_db2_context *dbc;
550
551
dbc = context->dal_handle->db_context;
552
retval = open_db(context, dbc, O_RDONLY, 0, &db);
553
if (retval)
554
return retval;
555
db->close(db);
556
return 0;
557
}
558
559
/*
560
* Return the last modification time of the database.
561
*
562
* Think about using fstat.
563
*/
564
565
krb5_error_code
566
krb5_db2_get_age(krb5_context context, char *db_name, time_t *age)
567
{
568
krb5_db2_context *dbc;
569
struct stat st;
570
571
if (!inited(context))
572
return (KRB5_KDB_DBNOTINITED);
573
dbc = context->dal_handle->db_context;
574
575
if (fstat(dbc->db_lf_file, &st) < 0)
576
*age = -1;
577
else
578
*age = st.st_mtime;
579
return 0;
580
}
581
582
/* Try to update the timestamp on dbc's lockfile. */
583
static void
584
ctx_update_age(krb5_db2_context *dbc)
585
{
586
struct stat st;
587
time_t now;
588
struct utimbuf utbuf;
589
590
now = time((time_t *) NULL);
591
if (fstat(dbc->db_lf_file, &st) != 0)
592
return;
593
if (st.st_mtime >= now) {
594
utbuf.actime = st.st_mtime + 1;
595
utbuf.modtime = st.st_mtime + 1;
596
(void) utime(dbc->db_lf_name, &utbuf);
597
} else
598
(void) utime(dbc->db_lf_name, (struct utimbuf *) NULL);
599
}
600
601
krb5_error_code
602
krb5_db2_lock(krb5_context context, int lockmode)
603
{
604
if (!inited(context))
605
return KRB5_KDB_DBNOTINITED;
606
return ctx_lock(context, context->dal_handle->db_context, lockmode);
607
}
608
609
krb5_error_code
610
krb5_db2_unlock(krb5_context context)
611
{
612
if (!inited(context))
613
return KRB5_KDB_DBNOTINITED;
614
return ctx_unlock(context, context->dal_handle->db_context);
615
}
616
617
/* Zero out and unlink filename. */
618
static krb5_error_code
619
destroy_file(char *filename)
620
{
621
struct stat statb;
622
int dowrite, j, nb, fd, retval;
623
off_t pos;
624
char buf[BUFSIZ], zbuf[BUFSIZ];
625
626
fd = open(filename, O_RDWR, 0);
627
if (fd < 0)
628
return errno;
629
set_cloexec_fd(fd);
630
/* fstat() will probably not fail unless using a remote filesystem
631
* (which is inappropriate for the kerberos database) so this check
632
* is mostly paranoia. */
633
if (fstat(fd, &statb) == -1)
634
goto error;
635
/*
636
* Stroll through the file, reading in BUFSIZ chunks. If everything
637
* is zero, then we're done for that block, otherwise, zero the block.
638
* We would like to just blast through everything, but some DB
639
* implementations make holey files and writing data to the holes
640
* causes actual blocks to be allocated which is no good, since
641
* we're just about to unlink it anyways.
642
*/
643
memset(zbuf, 0, BUFSIZ);
644
pos = 0;
645
while (pos < statb.st_size) {
646
dowrite = 0;
647
nb = read(fd, buf, BUFSIZ);
648
if (nb < 0)
649
goto error;
650
for (j = 0; j < nb; j++) {
651
if (buf[j] != '\0') {
652
dowrite = 1;
653
break;
654
}
655
}
656
/* For signedness */
657
j = nb;
658
if (dowrite) {
659
lseek(fd, pos, SEEK_SET);
660
nb = write(fd, zbuf, j);
661
if (nb < 0)
662
goto error;
663
}
664
pos += nb;
665
}
666
/* ??? Is fsync really needed? I don't know of any non-networked
667
* filesystem which will discard queued writes to disk if a file
668
* is deleted after it is closed. --jfc */
669
#ifndef NOFSYNC
670
fsync(fd);
671
#endif
672
close(fd);
673
674
if (unlink(filename))
675
return errno;
676
return 0;
677
678
error:
679
retval = errno;
680
close(fd);
681
return retval;
682
}
683
684
/* Initialize dbc by locking and creating the DB. If the DB already exists,
685
* clear it out if dbc->tempdb is set; otherwise return EEXIST. */
686
static krb5_error_code
687
ctx_create_db(krb5_context context, krb5_db2_context *dbc)
688
{
689
krb5_error_code retval = 0;
690
char *dbname = NULL, *polname = NULL, *plockname = NULL;
691
692
retval = ctx_allfiles(dbc, &dbname, &dbc->db_lf_name, &polname,
693
&plockname);
694
if (retval)
695
return retval;
696
697
dbc->db_lf_file = open(dbc->db_lf_name, O_CREAT | O_RDWR | O_TRUNC,
698
0600);
699
if (dbc->db_lf_file < 0) {
700
retval = errno;
701
goto cleanup;
702
}
703
retval = krb5_lock_file(context, dbc->db_lf_file, KRB5_LOCKMODE_EXCLUSIVE);
704
if (retval != 0)
705
goto cleanup;
706
set_cloexec_fd(dbc->db_lf_file);
707
dbc->db_lock_mode = KRB5_LOCKMODE_EXCLUSIVE;
708
dbc->db_locks_held = 1;
709
710
if (dbc->tempdb) {
711
/* Temporary DBs are locked for their whole lifetime. Since we have
712
* the lock, any remnant files can be safely destroyed. */
713
(void) destroy_file(dbname);
714
(void) unlink(polname);
715
(void) unlink(plockname);
716
}
717
718
retval = open_db(context, dbc, O_RDWR | O_CREAT | O_EXCL, 0600, &dbc->db);
719
if (retval)
720
goto cleanup;
721
722
/* Create the policy database, initialize a handle to it, and lock it. */
723
retval = osa_adb_create_db(polname, plockname, OSA_ADB_POLICY_DB_MAGIC);
724
if (retval)
725
goto cleanup;
726
retval = osa_adb_init_db(&dbc->policy_db, polname, plockname,
727
OSA_ADB_POLICY_DB_MAGIC);
728
if (retval)
729
goto cleanup;
730
retval = osa_adb_get_lock(dbc->policy_db, KRB5_DB_LOCKMODE_EXCLUSIVE);
731
if (retval)
732
goto cleanup;
733
734
dbc->db_inited = 1;
735
736
cleanup:
737
if (retval) {
738
if (dbc->db != NULL)
739
dbc->db->close(dbc->db);
740
if (dbc->db_locks_held > 0) {
741
(void) krb5_lock_file(context, dbc->db_lf_file,
742
KRB5_LOCKMODE_UNLOCK);
743
}
744
if (dbc->db_lf_file >= 0)
745
close(dbc->db_lf_file);
746
ctx_clear(dbc);
747
}
748
free(dbname);
749
free(polname);
750
free(plockname);
751
return retval;
752
}
753
754
krb5_error_code
755
krb5_db2_get_principal(krb5_context context, krb5_const_principal searchfor,
756
unsigned int flags, krb5_db_entry **entry)
757
{
758
krb5_db2_context *dbc;
759
krb5_error_code retval;
760
DB *db;
761
DBT key, contents;
762
krb5_data keydata, contdata;
763
int dbret;
764
765
*entry = NULL;
766
if (!inited(context))
767
return KRB5_KDB_DBNOTINITED;
768
769
dbc = context->dal_handle->db_context;
770
771
retval = ctx_lock(context, dbc, KRB5_LOCKMODE_SHARED);
772
if (retval)
773
return retval;
774
775
/* XXX deal with wildcard lookups */
776
retval = krb5_encode_princ_dbkey(context, &keydata, searchfor);
777
if (retval)
778
goto cleanup;
779
key.data = keydata.data;
780
key.size = keydata.length;
781
782
db = dbc->db;
783
dbret = (*db->get)(db, &key, &contents, 0);
784
retval = errno;
785
krb5_free_data_contents(context, &keydata);
786
switch (dbret) {
787
case 1:
788
retval = KRB5_KDB_NOENTRY;
789
/* Fall through. */
790
case -1:
791
default:
792
goto cleanup;
793
case 0:
794
contdata.data = contents.data;
795
contdata.length = contents.size;
796
retval = krb5_decode_princ_entry(context, &contdata, entry);
797
break;
798
}
799
800
cleanup:
801
(void) krb5_db2_unlock(context); /* unlock read lock */
802
return retval;
803
}
804
805
krb5_error_code
806
krb5_db2_put_principal(krb5_context context, krb5_db_entry *entry,
807
char **db_args)
808
{
809
int dbret;
810
DB *db;
811
DBT key, contents;
812
krb5_data contdata, keydata;
813
krb5_error_code retval;
814
krb5_db2_context *dbc;
815
816
krb5_clear_error_message (context);
817
if (db_args) {
818
/* DB2 does not support db_args DB arguments for principal */
819
k5_setmsg(context, EINVAL, _("Unsupported argument \"%s\" for db2"),
820
db_args[0]);
821
return EINVAL;
822
}
823
824
if (!inited(context))
825
return KRB5_KDB_DBNOTINITED;
826
827
dbc = context->dal_handle->db_context;
828
if ((retval = ctx_lock(context, dbc, KRB5_LOCKMODE_EXCLUSIVE)))
829
return retval;
830
831
db = dbc->db;
832
833
retval = krb5_encode_princ_entry(context, &contdata, entry);
834
if (retval)
835
goto cleanup;
836
contents.data = contdata.data;
837
contents.size = contdata.length;
838
retval = krb5_encode_princ_dbkey(context, &keydata, entry->princ);
839
if (retval) {
840
krb5_free_data_contents(context, &contdata);
841
goto cleanup;
842
}
843
844
key.data = keydata.data;
845
key.size = keydata.length;
846
dbret = (*db->put)(db, &key, &contents, 0);
847
retval = dbret ? errno : 0;
848
krb5_free_data_contents(context, &keydata);
849
krb5_free_data_contents(context, &contdata);
850
851
cleanup:
852
ctx_update_age(dbc);
853
(void) krb5_db2_unlock(context); /* unlock database */
854
return (retval);
855
}
856
857
krb5_error_code
858
krb5_db2_delete_principal(krb5_context context, krb5_const_principal searchfor)
859
{
860
krb5_error_code retval;
861
krb5_db_entry *entry;
862
krb5_db2_context *dbc;
863
DB *db;
864
DBT key, contents;
865
krb5_data keydata, contdata;
866
int i, dbret;
867
868
if (!inited(context))
869
return KRB5_KDB_DBNOTINITED;
870
871
dbc = context->dal_handle->db_context;
872
if ((retval = ctx_lock(context, dbc, KRB5_LOCKMODE_EXCLUSIVE)))
873
return (retval);
874
875
if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor)))
876
goto cleanup;
877
key.data = keydata.data;
878
key.size = keydata.length;
879
880
db = dbc->db;
881
dbret = (*db->get) (db, &key, &contents, 0);
882
retval = errno;
883
switch (dbret) {
884
case 1:
885
retval = KRB5_KDB_NOENTRY;
886
/* Fall through. */
887
case -1:
888
default:
889
goto cleankey;
890
case 0:
891
;
892
}
893
contdata.data = contents.data;
894
contdata.length = contents.size;
895
retval = krb5_decode_princ_entry(context, &contdata, &entry);
896
if (retval)
897
goto cleankey;
898
899
/* Clear encrypted key contents */
900
for (i = 0; i < entry->n_key_data; i++) {
901
if (entry->key_data[i].key_data_length[0]) {
902
memset(entry->key_data[i].key_data_contents[0], 0,
903
(unsigned) entry->key_data[i].key_data_length[0]);
904
}
905
}
906
907
retval = krb5_encode_princ_entry(context, &contdata, entry);
908
krb5_db_free_principal(context, entry);
909
if (retval)
910
goto cleankey;
911
912
contents.data = contdata.data;
913
contents.size = contdata.length;
914
dbret = (*db->put) (db, &key, &contents, 0);
915
retval = dbret ? errno : 0;
916
krb5_free_data_contents(context, &contdata);
917
if (retval)
918
goto cleankey;
919
dbret = (*db->del) (db, &key, 0);
920
retval = dbret ? errno : 0;
921
cleankey:
922
krb5_free_data_contents(context, &keydata);
923
924
cleanup:
925
ctx_update_age(dbc);
926
(void) krb5_db2_unlock(context); /* unlock write lock */
927
return retval;
928
}
929
930
typedef krb5_error_code (*ctx_iterate_cb)(krb5_pointer, krb5_db_entry *);
931
932
/* Cursor structure for ctx_iterate() */
933
typedef struct iter_curs {
934
DBT key;
935
DBT data;
936
DBT keycopy;
937
unsigned int startflag;
938
unsigned int stepflag;
939
krb5_context ctx;
940
krb5_db2_context *dbc;
941
int lockmode;
942
krb5_boolean islocked;
943
} iter_curs;
944
945
/* Lock DB handle of curs, updating curs->islocked. */
946
static krb5_error_code
947
curs_lock(iter_curs *curs)
948
{
949
krb5_error_code retval;
950
951
retval = ctx_lock(curs->ctx, curs->dbc, curs->lockmode);
952
if (retval)
953
return retval;
954
curs->islocked = TRUE;
955
return 0;
956
}
957
958
/* Unlock DB handle of curs, updating curs->islocked. */
959
static void
960
curs_unlock(iter_curs *curs)
961
{
962
ctx_unlock(curs->ctx, curs->dbc);
963
curs->islocked = FALSE;
964
}
965
966
/* Set up curs and lock DB. */
967
static krb5_error_code
968
curs_init(iter_curs *curs, krb5_context ctx, krb5_db2_context *dbc,
969
krb5_flags iterflags)
970
{
971
int isrecurse = iterflags & KRB5_DB_ITER_RECURSE;
972
unsigned int prevflag = R_PREV;
973
unsigned int nextflag = R_NEXT;
974
975
curs->keycopy.size = 0;
976
curs->keycopy.data = NULL;
977
curs->islocked = FALSE;
978
curs->ctx = ctx;
979
curs->dbc = dbc;
980
981
if (iterflags & KRB5_DB_ITER_WRITE)
982
curs->lockmode = KRB5_LOCKMODE_EXCLUSIVE;
983
else
984
curs->lockmode = KRB5_LOCKMODE_SHARED;
985
986
if (isrecurse) {
987
#ifdef R_RNEXT
988
if (dbc->hashfirst) {
989
k5_setmsg(ctx, EINVAL, _("Recursive iteration is not supported "
990
"for hash databases"));
991
return EINVAL;
992
}
993
prevflag = R_RPREV;
994
nextflag = R_RNEXT;
995
#else
996
k5_setmsg(ctx, EINVAL, _("Recursive iteration not supported "
997
"in this version of libdb"));
998
return EINVAL;
999
#endif
1000
}
1001
if (iterflags & KRB5_DB_ITER_REV) {
1002
curs->startflag = R_LAST;
1003
curs->stepflag = prevflag;
1004
} else {
1005
curs->startflag = R_FIRST;
1006
curs->stepflag = nextflag;
1007
}
1008
return curs_lock(curs);
1009
}
1010
1011
/* Get initial entry. */
1012
static int
1013
curs_start(iter_curs *curs)
1014
{
1015
DB *db = curs->dbc->db;
1016
1017
return db->seq(db, &curs->key, &curs->data, curs->startflag);
1018
}
1019
1020
/* Save iteration state so DB can be unlocked/closed. */
1021
static krb5_error_code
1022
curs_save(iter_curs *curs)
1023
{
1024
if (!curs->dbc->unlockiter)
1025
return 0;
1026
1027
curs->keycopy.data = malloc(curs->key.size);
1028
if (curs->keycopy.data == NULL)
1029
return ENOMEM;
1030
1031
curs->keycopy.size = curs->key.size;
1032
memcpy(curs->keycopy.data, curs->key.data, curs->key.size);
1033
return 0;
1034
}
1035
1036
/* Free allocated cursor resources */
1037
static void
1038
curs_free(iter_curs *curs)
1039
{
1040
free(curs->keycopy.data);
1041
curs->keycopy.size = 0;
1042
curs->keycopy.data = NULL;
1043
}
1044
1045
/* Move one step of iteration (forwards or backwards as requested). Free
1046
* curs->keycopy as a side effect, if needed. */
1047
static int
1048
curs_step(iter_curs *curs)
1049
{
1050
int dbret;
1051
krb5_db2_context *dbc = curs->dbc;
1052
1053
if (dbc->unlockiter) {
1054
/* Reacquire libdb cursor using saved copy of key. */
1055
curs->key = curs->keycopy;
1056
dbret = dbc->db->seq(dbc->db, &curs->key, &curs->data, R_CURSOR);
1057
curs_free(curs);
1058
if (dbret)
1059
return dbret;
1060
}
1061
return dbc->db->seq(dbc->db, &curs->key, &curs->data, curs->stepflag);
1062
}
1063
1064
/* Run one invocation of the callback, unlocking the mutex and possibly the DB
1065
* around the invocation. */
1066
static krb5_error_code
1067
curs_run_cb(iter_curs *curs, ctx_iterate_cb func, krb5_pointer func_arg)
1068
{
1069
krb5_db2_context *dbc = curs->dbc;
1070
krb5_error_code retval, lockerr;
1071
krb5_db_entry *entry;
1072
krb5_context ctx = curs->ctx;
1073
krb5_data contdata;
1074
1075
contdata = make_data(curs->data.data, curs->data.size);
1076
retval = krb5_decode_princ_entry(ctx, &contdata, &entry);
1077
if (retval)
1078
return retval;
1079
/* Save libdb key across possible DB closure. */
1080
retval = curs_save(curs);
1081
if (retval)
1082
return retval;
1083
1084
if (dbc->unlockiter)
1085
curs_unlock(curs);
1086
1087
k5_mutex_unlock(krb5_db2_mutex);
1088
retval = (*func)(func_arg, entry);
1089
krb5_db_free_principal(ctx, entry);
1090
k5_mutex_lock(krb5_db2_mutex);
1091
if (dbc->unlockiter) {
1092
lockerr = curs_lock(curs);
1093
if (lockerr)
1094
return lockerr;
1095
}
1096
return retval;
1097
}
1098
1099
/* Free cursor resources and unlock the DB if needed. */
1100
static void
1101
curs_fini(iter_curs *curs)
1102
{
1103
curs_free(curs);
1104
if (curs->islocked)
1105
curs_unlock(curs);
1106
}
1107
1108
static krb5_error_code
1109
ctx_iterate(krb5_context context, krb5_db2_context *dbc,
1110
ctx_iterate_cb func, krb5_pointer func_arg, krb5_flags iterflags)
1111
{
1112
krb5_error_code retval;
1113
int dbret;
1114
iter_curs curs;
1115
1116
retval = curs_init(&curs, context, dbc, iterflags);
1117
if (retval)
1118
return retval;
1119
dbret = curs_start(&curs);
1120
while (dbret == 0) {
1121
retval = curs_run_cb(&curs, func, func_arg);
1122
if (retval)
1123
goto cleanup;
1124
dbret = curs_step(&curs);
1125
}
1126
switch (dbret) {
1127
case 1:
1128
case 0:
1129
break;
1130
case -1:
1131
default:
1132
retval = errno;
1133
}
1134
cleanup:
1135
curs_fini(&curs);
1136
return retval;
1137
}
1138
1139
krb5_error_code
1140
krb5_db2_iterate(krb5_context context, char *match_expr, ctx_iterate_cb func,
1141
krb5_pointer func_arg, krb5_flags iterflags)
1142
{
1143
if (!inited(context))
1144
return KRB5_KDB_DBNOTINITED;
1145
return ctx_iterate(context, context->dal_handle->db_context, func,
1146
func_arg, iterflags);
1147
}
1148
1149
krb5_boolean
1150
krb5_db2_set_lockmode(krb5_context context, krb5_boolean mode)
1151
{
1152
krb5_boolean old;
1153
krb5_db2_context *dbc;
1154
1155
dbc = context->dal_handle->db_context;
1156
old = mode;
1157
if (dbc) {
1158
old = dbc->db_nb_locks;
1159
dbc->db_nb_locks = mode;
1160
}
1161
return old;
1162
}
1163
1164
/*
1165
* DAL API functions
1166
*/
1167
krb5_error_code
1168
krb5_db2_lib_init(void)
1169
{
1170
return 0;
1171
}
1172
1173
krb5_error_code
1174
krb5_db2_lib_cleanup(void)
1175
{
1176
/* right now, no cleanup required */
1177
return 0;
1178
}
1179
1180
krb5_error_code
1181
krb5_db2_open(krb5_context context, char *conf_section, char **db_args,
1182
int mode)
1183
{
1184
krb5_error_code status = 0;
1185
1186
krb5_clear_error_message(context);
1187
if (inited(context))
1188
return 0;
1189
1190
status = configure_context(context, conf_section, db_args);
1191
if (status != 0)
1192
return status;
1193
1194
status = check_openable(context);
1195
if (status != 0)
1196
return status;
1197
1198
return ctx_init(context->dal_handle->db_context);
1199
}
1200
1201
krb5_error_code
1202
krb5_db2_create(krb5_context context, char *conf_section, char **db_args)
1203
{
1204
krb5_error_code status = 0;
1205
krb5_db2_context *dbc;
1206
1207
krb5_clear_error_message(context);
1208
if (inited(context))
1209
return 0;
1210
1211
status = configure_context(context, conf_section, db_args);
1212
if (status != 0)
1213
return status;
1214
1215
dbc = context->dal_handle->db_context;
1216
status = ctx_create_db(context, dbc);
1217
if (status != 0)
1218
return status;
1219
1220
if (!dbc->tempdb)
1221
krb5_db2_unlock(context);
1222
1223
return 0;
1224
}
1225
1226
krb5_error_code
1227
krb5_db2_destroy(krb5_context context, char *conf_section, char **db_args)
1228
{
1229
krb5_error_code status;
1230
krb5_db2_context *dbc;
1231
char *dbname = NULL, *lockname = NULL, *polname = NULL, *plockname = NULL;
1232
1233
if (inited(context)) {
1234
status = krb5_db2_fini(context);
1235
if (status != 0)
1236
return status;
1237
}
1238
1239
krb5_clear_error_message(context);
1240
status = configure_context(context, conf_section, db_args);
1241
if (status != 0)
1242
return status;
1243
1244
status = check_openable(context);
1245
if (status != 0)
1246
return status;
1247
1248
dbc = context->dal_handle->db_context;
1249
1250
status = ctx_allfiles(dbc, &dbname, &lockname, &polname, &plockname);
1251
if (status)
1252
goto cleanup;
1253
status = destroy_file(dbname);
1254
if (status)
1255
goto cleanup;
1256
status = unlink(lockname);
1257
if (status)
1258
goto cleanup;
1259
status = osa_adb_destroy_db(polname, plockname, OSA_ADB_POLICY_DB_MAGIC);
1260
if (status)
1261
goto cleanup;
1262
1263
status = krb5_db2_fini(context);
1264
1265
cleanup:
1266
free(dbname);
1267
free(lockname);
1268
free(polname);
1269
free(plockname);
1270
return status;
1271
}
1272
1273
/* policy functions */
1274
krb5_error_code
1275
krb5_db2_create_policy(krb5_context context, osa_policy_ent_t policy)
1276
{
1277
krb5_db2_context *dbc = context->dal_handle->db_context;
1278
1279
return osa_adb_create_policy(dbc->policy_db, policy);
1280
}
1281
1282
krb5_error_code
1283
krb5_db2_get_policy(krb5_context context,
1284
char *name, osa_policy_ent_t *policy)
1285
{
1286
krb5_db2_context *dbc = context->dal_handle->db_context;
1287
1288
return osa_adb_get_policy(dbc->policy_db, name, policy);
1289
}
1290
1291
krb5_error_code
1292
krb5_db2_put_policy(krb5_context context, osa_policy_ent_t policy)
1293
{
1294
krb5_db2_context *dbc = context->dal_handle->db_context;
1295
1296
return osa_adb_put_policy(dbc->policy_db, policy);
1297
}
1298
1299
krb5_error_code
1300
krb5_db2_iter_policy(krb5_context context,
1301
char *match_entry,
1302
osa_adb_iter_policy_func func, void *data)
1303
{
1304
krb5_db2_context *dbc = context->dal_handle->db_context;
1305
1306
return osa_adb_iter_policy(dbc->policy_db, func, data);
1307
}
1308
1309
krb5_error_code
1310
krb5_db2_delete_policy(krb5_context context, char *policy)
1311
{
1312
krb5_db2_context *dbc = context->dal_handle->db_context;
1313
1314
return osa_adb_destroy_policy(dbc->policy_db, policy);
1315
}
1316
1317
/*
1318
* Merge non-replicated attributes from src into dst, setting
1319
* changed to non-zero if dst was changed.
1320
*
1321
* Non-replicated attributes are: last_success, last_failed,
1322
* fail_auth_count, and any negative TL data values.
1323
*/
1324
static krb5_error_code
1325
krb5_db2_merge_principal(krb5_context context,
1326
krb5_db_entry *src,
1327
krb5_db_entry *dst,
1328
int *changed)
1329
{
1330
*changed = 0;
1331
1332
if (dst->last_success != src->last_success) {
1333
dst->last_success = src->last_success;
1334
(*changed)++;
1335
}
1336
1337
if (dst->last_failed != src->last_failed) {
1338
dst->last_failed = src->last_failed;
1339
(*changed)++;
1340
}
1341
1342
if (dst->fail_auth_count != src->fail_auth_count) {
1343
dst->fail_auth_count = src->fail_auth_count;
1344
(*changed)++;
1345
}
1346
1347
return 0;
1348
}
1349
1350
struct nra_context {
1351
krb5_context kcontext;
1352
krb5_db2_context *db_context;
1353
};
1354
1355
/*
1356
* Iteration callback merges non-replicated attributes from
1357
* old database.
1358
*/
1359
static krb5_error_code
1360
krb5_db2_merge_nra_iterator(krb5_pointer ptr, krb5_db_entry *entry)
1361
{
1362
struct nra_context *nra = (struct nra_context *)ptr;
1363
kdb5_dal_handle *dal_handle = nra->kcontext->dal_handle;
1364
krb5_error_code retval;
1365
int changed;
1366
krb5_db_entry *s_entry;
1367
krb5_db2_context *dst_db;
1368
1369
memset(&s_entry, 0, sizeof(s_entry));
1370
1371
dst_db = dal_handle->db_context;
1372
dal_handle->db_context = nra->db_context;
1373
1374
/* look up the new principal in the old DB */
1375
retval = krb5_db2_get_principal(nra->kcontext, entry->princ, 0, &s_entry);
1376
if (retval != 0) {
1377
/* principal may be newly created, so ignore */
1378
dal_handle->db_context = dst_db;
1379
return 0;
1380
}
1381
1382
/* merge non-replicated attributes from the old entry in */
1383
krb5_db2_merge_principal(nra->kcontext, s_entry, entry, &changed);
1384
1385
dal_handle->db_context = dst_db;
1386
1387
/* if necessary, commit the modified new entry to the new DB */
1388
if (changed) {
1389
retval = krb5_db2_put_principal(nra->kcontext, entry, NULL);
1390
} else {
1391
retval = 0;
1392
}
1393
1394
krb5_db_free_principal(nra->kcontext, s_entry);
1395
return retval;
1396
}
1397
1398
/*
1399
* Merge non-replicated attributes (that is, lockout-related
1400
* attributes and negative TL data types) from the real database
1401
* into the temporary one.
1402
*/
1403
static krb5_error_code
1404
ctx_merge_nra(krb5_context context, krb5_db2_context *dbc_temp,
1405
krb5_db2_context *dbc_real)
1406
{
1407
struct nra_context nra;
1408
1409
nra.kcontext = context;
1410
nra.db_context = dbc_real;
1411
return ctx_iterate(context, dbc_temp, krb5_db2_merge_nra_iterator, &nra, 0);
1412
}
1413
1414
/*
1415
* In the filesystem, promote the temporary database described by dbc_temp to
1416
* the real database described by dbc_real. Both must be exclusively locked.
1417
*/
1418
static krb5_error_code
1419
ctx_promote(krb5_context context, krb5_db2_context *dbc_temp,
1420
krb5_db2_context *dbc_real)
1421
{
1422
krb5_error_code retval;
1423
char *tdb = NULL, *tlock = NULL, *tpol = NULL, *tplock = NULL;
1424
char *rdb = NULL, *rlock = NULL, *rpol = NULL, *rplock = NULL;
1425
1426
/* Generate all filenames of interest (including a few we don't need). */
1427
retval = ctx_allfiles(dbc_temp, &tdb, &tlock, &tpol, &tplock);
1428
if (retval)
1429
return retval;
1430
retval = ctx_allfiles(dbc_real, &rdb, &rlock, &rpol, &rplock);
1431
if (retval)
1432
goto cleanup;
1433
1434
/* Rename the principal and policy databases into place. */
1435
if (rename(tdb, rdb)) {
1436
retval = errno;
1437
goto cleanup;
1438
}
1439
if (rename(tpol, rpol)) {
1440
retval = errno;
1441
goto cleanup;
1442
}
1443
1444
ctx_update_age(dbc_real);
1445
1446
/* Release and remove the temporary DB lockfiles. */
1447
(void) unlink(tlock);
1448
(void) unlink(tplock);
1449
1450
cleanup:
1451
free(tdb);
1452
free(tlock);
1453
free(tpol);
1454
free(tplock);
1455
free(rdb);
1456
free(rlock);
1457
free(rpol);
1458
free(rplock);
1459
return retval;
1460
}
1461
1462
krb5_error_code
1463
krb5_db2_promote_db(krb5_context context, char *conf_section, char **db_args)
1464
{
1465
krb5_error_code retval;
1466
krb5_boolean merge_nra = FALSE, real_locked = FALSE;
1467
krb5_db2_context *dbc_temp, *dbc_real = NULL;
1468
char **db_argp;
1469
1470
/* context must be initialized with an exclusively locked temp DB. */
1471
if (!inited(context))
1472
return KRB5_KDB_DBNOTINITED;
1473
dbc_temp = context->dal_handle->db_context;
1474
if (dbc_temp->db_lock_mode != KRB5_LOCKMODE_EXCLUSIVE)
1475
return KRB5_KDB_NOTLOCKED;
1476
if (!dbc_temp->tempdb)
1477
return EINVAL;
1478
1479
/* Check db_args for whether we should merge non-replicated attributes. */
1480
for (db_argp = db_args; *db_argp; db_argp++) {
1481
if (!strcmp(*db_argp, "merge_nra")) {
1482
merge_nra = TRUE;
1483
break;
1484
}
1485
}
1486
1487
/* Make a db2 context for the real DB. */
1488
dbc_real = k5alloc(sizeof(*dbc_real), &retval);
1489
if (dbc_real == NULL)
1490
return retval;
1491
ctx_clear(dbc_real);
1492
1493
/* Try creating the real DB. */
1494
dbc_real->db_name = strdup(dbc_temp->db_name);
1495
if (dbc_real->db_name == NULL)
1496
goto cleanup;
1497
dbc_real->tempdb = FALSE;
1498
retval = ctx_create_db(context, dbc_real);
1499
if (retval == EEXIST) {
1500
/* The real database already exists, so open and lock it. */
1501
dbc_real->db_name = strdup(dbc_temp->db_name);
1502
if (dbc_real->db_name == NULL)
1503
goto cleanup;
1504
dbc_real->tempdb = FALSE;
1505
retval = ctx_init(dbc_real);
1506
if (retval)
1507
goto cleanup;
1508
retval = ctx_lock(context, dbc_real, KRB5_DB_LOCKMODE_EXCLUSIVE);
1509
if (retval)
1510
goto cleanup;
1511
} else if (retval)
1512
goto cleanup;
1513
real_locked = TRUE;
1514
1515
if (merge_nra) {
1516
retval = ctx_merge_nra(context, dbc_temp, dbc_real);
1517
if (retval)
1518
goto cleanup;
1519
}
1520
1521
/* Perform filesystem manipulations for the promotion. */
1522
retval = ctx_promote(context, dbc_temp, dbc_real);
1523
if (retval)
1524
goto cleanup;
1525
1526
/* Unlock and finalize context since the temp DB is gone. */
1527
(void) krb5_db2_unlock(context);
1528
krb5_db2_fini(context);
1529
1530
cleanup:
1531
if (real_locked)
1532
(void) ctx_unlock(context, dbc_real);
1533
if (dbc_real)
1534
ctx_fini(dbc_real);
1535
return retval;
1536
}
1537
1538
krb5_error_code
1539
krb5_db2_check_policy_as(krb5_context kcontext, krb5_kdc_req *request,
1540
krb5_db_entry *client, krb5_db_entry *server,
1541
krb5_timestamp kdc_time, const char **status,
1542
krb5_pa_data ***e_data)
1543
{
1544
krb5_error_code retval;
1545
1546
retval = krb5_db2_lockout_check_policy(kcontext, client, kdc_time);
1547
if (retval == KRB5KDC_ERR_CLIENT_REVOKED)
1548
*status = "LOCKED_OUT";
1549
return retval;
1550
}
1551
1552
void
1553
krb5_db2_audit_as_req(krb5_context kcontext, krb5_kdc_req *request,
1554
const krb5_address *local_addr,
1555
const krb5_address *remote_addr, krb5_db_entry *client,
1556
krb5_db_entry *server, krb5_timestamp authtime,
1557
krb5_error_code error_code)
1558
{
1559
(void) krb5_db2_lockout_audit(kcontext, client, authtime, error_code);
1560
}
1561
1562