Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / plugins / kdb / db2 / kdb_db2.c
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 nonexistant 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 inconsistant.  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 slave servers; a crash in the middle of an update will be
114  * fixed by the next slave propagation.  A crash in the middle of an
115  * update on the master would be somewhat more serious, but this would
116  * likely be noticed by an administrator, who could fix the problem and
117  * retry 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()
1169 {
1170     return 0;
1171 }
1172
1173 krb5_error_code
1174 krb5_db2_lib_cleanup()
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         return status;
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 void
1318 krb5_db2_free_policy(krb5_context context, osa_policy_ent_t entry)
1319 {
1320     osa_free_policy_ent(entry);
1321 }
1322
1323
1324 /*
1325  * Merge non-replicated attributes from src into dst, setting
1326  * changed to non-zero if dst was changed.
1327  *
1328  * Non-replicated attributes are: last_success, last_failed,
1329  * fail_auth_count, and any negative TL data values.
1330  */
1331 static krb5_error_code
1332 krb5_db2_merge_principal(krb5_context context,
1333                          krb5_db_entry *src,
1334                          krb5_db_entry *dst,
1335                          int *changed)
1336 {
1337     *changed = 0;
1338
1339     if (dst->last_success != src->last_success) {
1340         dst->last_success = src->last_success;
1341         (*changed)++;
1342     }
1343
1344     if (dst->last_failed != src->last_failed) {
1345         dst->last_failed = src->last_failed;
1346         (*changed)++;
1347     }
1348
1349     if (dst->fail_auth_count != src->fail_auth_count) {
1350         dst->fail_auth_count = src->fail_auth_count;
1351         (*changed)++;
1352     }
1353
1354     return 0;
1355 }
1356
1357 struct nra_context {
1358     krb5_context kcontext;
1359     krb5_db2_context *db_context;
1360 };
1361
1362 /*
1363  * Iteration callback merges non-replicated attributes from
1364  * old database.
1365  */
1366 static krb5_error_code
1367 krb5_db2_merge_nra_iterator(krb5_pointer ptr, krb5_db_entry *entry)
1368 {
1369     struct nra_context *nra = (struct nra_context *)ptr;
1370     kdb5_dal_handle *dal_handle = nra->kcontext->dal_handle;
1371     krb5_error_code retval;
1372     int changed;
1373     krb5_db_entry *s_entry;
1374     krb5_db2_context *dst_db;
1375
1376     memset(&s_entry, 0, sizeof(s_entry));
1377
1378     dst_db = dal_handle->db_context;
1379     dal_handle->db_context = nra->db_context;
1380
1381     /* look up the new principal in the old DB */
1382     retval = krb5_db2_get_principal(nra->kcontext, entry->princ, 0, &s_entry);
1383     if (retval != 0) {
1384         /* principal may be newly created, so ignore */
1385         dal_handle->db_context = dst_db;
1386         return 0;
1387     }
1388
1389     /* merge non-replicated attributes from the old entry in */
1390     krb5_db2_merge_principal(nra->kcontext, s_entry, entry, &changed);
1391
1392     dal_handle->db_context = dst_db;
1393
1394     /* if necessary, commit the modified new entry to the new DB */
1395     if (changed) {
1396         retval = krb5_db2_put_principal(nra->kcontext, entry, NULL);
1397     } else {
1398         retval = 0;
1399     }
1400
1401     krb5_db_free_principal(nra->kcontext, s_entry);
1402     return retval;
1403 }
1404
1405 /*
1406  * Merge non-replicated attributes (that is, lockout-related
1407  * attributes and negative TL data types) from the real database
1408  * into the temporary one.
1409  */
1410 static krb5_error_code
1411 ctx_merge_nra(krb5_context context, krb5_db2_context *dbc_temp,
1412               krb5_db2_context *dbc_real)
1413 {
1414     struct nra_context nra;
1415
1416     nra.kcontext = context;
1417     nra.db_context = dbc_real;
1418     return ctx_iterate(context, dbc_temp, krb5_db2_merge_nra_iterator, &nra, 0);
1419 }
1420
1421 /*
1422  * In the filesystem, promote the temporary database described by dbc_temp to
1423  * the real database described by dbc_real.  Both must be exclusively locked.
1424  */
1425 static krb5_error_code
1426 ctx_promote(krb5_context context, krb5_db2_context *dbc_temp,
1427             krb5_db2_context *dbc_real)
1428 {
1429     krb5_error_code retval;
1430     char *tdb = NULL, *tlock = NULL, *tpol = NULL, *tplock = NULL;
1431     char *rdb = NULL, *rlock = NULL, *rpol = NULL, *rplock = NULL;
1432
1433     /* Generate all filenames of interest (including a few we don't need). */
1434     retval = ctx_allfiles(dbc_temp, &tdb, &tlock, &tpol, &tplock);
1435     if (retval)
1436         return retval;
1437     retval = ctx_allfiles(dbc_real, &rdb, &rlock, &rpol, &rplock);
1438     if (retval)
1439         goto cleanup;
1440
1441     /* Rename the principal and policy databases into place. */
1442     if (rename(tdb, rdb)) {
1443         retval = errno;
1444         goto cleanup;
1445     }
1446     if (rename(tpol, rpol)) {
1447         retval = errno;
1448         goto cleanup;
1449     }
1450
1451     ctx_update_age(dbc_real);
1452
1453     /* Release and remove the temporary DB lockfiles. */
1454     (void) unlink(tlock);
1455     (void) unlink(tplock);
1456
1457 cleanup:
1458     free(tdb);
1459     free(tlock);
1460     free(tpol);
1461     free(tplock);
1462     free(rdb);
1463     free(rlock);
1464     free(rpol);
1465     free(rplock);
1466     return retval;
1467 }
1468
1469 krb5_error_code
1470 krb5_db2_promote_db(krb5_context context, char *conf_section, char **db_args)
1471 {
1472     krb5_error_code retval;
1473     krb5_boolean merge_nra = FALSE, real_locked = FALSE;
1474     krb5_db2_context *dbc_temp, *dbc_real = NULL;
1475     char **db_argp;
1476
1477     /* context must be initialized with an exclusively locked temp DB. */
1478     if (!inited(context))
1479         return KRB5_KDB_DBNOTINITED;
1480     dbc_temp = context->dal_handle->db_context;
1481     if (dbc_temp->db_lock_mode != KRB5_LOCKMODE_EXCLUSIVE)
1482         return KRB5_KDB_NOTLOCKED;
1483     if (!dbc_temp->tempdb)
1484         return EINVAL;
1485
1486     /* Check db_args for whether we should merge non-replicated attributes. */
1487     for (db_argp = db_args; *db_argp; db_argp++) {
1488         if (!strcmp(*db_argp, "merge_nra")) {
1489             merge_nra = TRUE;
1490             break;
1491         }
1492     }
1493
1494     /* Make a db2 context for the real DB. */
1495     dbc_real = k5alloc(sizeof(*dbc_real), &retval);
1496     if (dbc_real == NULL)
1497         return retval;
1498     ctx_clear(dbc_real);
1499
1500     /* Try creating the real DB. */
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_create_db(context, dbc_real);
1506     if (retval == EEXIST) {
1507         /* The real database already exists, so open and lock it. */
1508         dbc_real->db_name = strdup(dbc_temp->db_name);
1509         if (dbc_real->db_name == NULL)
1510             goto cleanup;
1511         dbc_real->tempdb = FALSE;
1512         retval = ctx_init(dbc_real);
1513         if (retval)
1514             goto cleanup;
1515         retval = ctx_lock(context, dbc_real, KRB5_DB_LOCKMODE_EXCLUSIVE);
1516         if (retval)
1517             goto cleanup;
1518     } else if (retval)
1519         goto cleanup;
1520     real_locked = TRUE;
1521
1522     if (merge_nra) {
1523         retval = ctx_merge_nra(context, dbc_temp, dbc_real);
1524         if (retval)
1525             goto cleanup;
1526     }
1527
1528     /* Perform filesystem manipulations for the promotion. */
1529     retval = ctx_promote(context, dbc_temp, dbc_real);
1530     if (retval)
1531         goto cleanup;
1532
1533     /* Unlock and finalize context since the temp DB is gone. */
1534     (void) krb5_db2_unlock(context);
1535     krb5_db2_fini(context);
1536
1537 cleanup:
1538     if (real_locked)
1539         (void) ctx_unlock(context, dbc_real);
1540     if (dbc_real)
1541         ctx_fini(dbc_real);
1542     return retval;
1543 }
1544
1545 krb5_error_code
1546 krb5_db2_check_policy_as(krb5_context kcontext, krb5_kdc_req *request,
1547                          krb5_db_entry *client, krb5_db_entry *server,
1548                          krb5_timestamp kdc_time, const char **status,
1549                          krb5_pa_data ***e_data)
1550 {
1551     krb5_error_code retval;
1552
1553     retval = krb5_db2_lockout_check_policy(kcontext, client, kdc_time);
1554     if (retval == KRB5KDC_ERR_CLIENT_REVOKED)
1555         *status = "LOCKED_OUT";
1556     return retval;
1557 }
1558
1559 void
1560 krb5_db2_audit_as_req(krb5_context kcontext, krb5_kdc_req *request,
1561                       krb5_db_entry *client, krb5_db_entry *server,
1562                       krb5_timestamp authtime, krb5_error_code error_code)
1563 {
1564     (void) krb5_db2_lockout_audit(kcontext, client, authtime, error_code);
1565 }