1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/db2/kdb_db2.c */
4 * Copyright 1997,2006,2007-2009 by the Massachusetts Institute of Technology.
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.
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.
29 * Copyright (C) 1998 by the FundsXpress, INC.
31 * All rights reserved.
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.
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.
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.
67 #include "policy_db.h"
69 #define KDB_DB2_DATABASE_NAME "database_name"
72 #define SUFFIX_LOCK ".ok"
73 #define SUFFIX_POLICY ".kadm5"
74 #define SUFFIX_POLICY_LOCK ".kadm5.lock"
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.
85 * The first locking protocol is implemented using flock() in the
86 * krb_dbl_lock() and krb_dbl_unlock routines.
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.
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.
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.
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.
120 /* Evaluate to true if the krb5_context c contains an initialized db2
122 #define inited(c) ((c)->dal_handle->db_context && \
123 ((krb5_db2_context *)(c)->dal_handle->db_context)-> \
126 static krb5_error_code
127 get_db_opt(char *input, char **opt, char **val)
129 char *pos = strchr(input, '=');
132 *val = strdup(input);
137 *opt = malloc((pos - input) + 1);
138 *val = strdup(pos + 1);
139 if (!*opt || !*val) {
146 memcpy(*opt, input, pos - input);
147 (*opt)[pos - input] = '\0';
153 /* Restore dbctx to the uninitialized state. */
155 ctx_clear(krb5_db2_context *dbc)
158 * Free any dynamically allocated memory. File descriptors and locks
159 * are the caller's problem.
161 free(dbc->db_lf_name);
164 * Clear the structure and reset the defaults.
166 memset(dbc, 0, sizeof(krb5_db2_context));
168 dbc->db_lf_name = NULL;
169 dbc->db_lf_file = -1;
171 dbc->db_nb_locks = FALSE;
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)
180 krb5_db2_context *dbc;
181 kdb5_dal_handle *dal_handle;
183 dal_handle = context->dal_handle;
185 if (dal_handle->db_context == NULL) {
186 dbc = (krb5_db2_context *) malloc(sizeof(krb5_db2_context));
190 memset(dbc, 0, sizeof(krb5_db2_context));
192 dal_handle->db_context = dbc;
195 *dbc_out = dal_handle->db_context;
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)
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);
210 status = ctx_get(context, &dbc);
214 for (t_ptr = db_args; t_ptr && *t_ptr; t_ptr++) {
217 status = get_db_opt(*t_ptr, &opt, &val);
218 if (opt && !strcmp(opt, "dbname")) {
219 dbc->db_name = strdup(val);
220 if (dbc->db_name == NULL) {
225 else if (!opt && !strcmp(val, "temporary")) {
227 } else if (!opt && !strcmp(val, "merge_nra")) {
229 } else if (opt && !strcmp(opt, "hash")) {
230 dbc->hashfirst = TRUE;
233 krb5_set_error_message(context, status,
234 _("Unsupported argument \"%s\" for db2"),
240 if (dbc->db_name == NULL) {
241 /* Check for database_name in the db_module section. */
242 status = profile_get_string(profile, KDB_MODULE_SECTION, conf_section,
243 KDB_DB2_DATABASE_NAME, NULL, &pval);
244 if (status == 0 && pval == NULL) {
245 /* For compatibility, check for database_name in the realm. */
246 status = profile_get_string(profile, KDB_REALM_SECTION,
247 KRB5_DB_GET_REALM(context),
248 KDB_DB2_DATABASE_NAME,
249 DEFAULT_KDB_FILE, &pval);
253 dbc->db_name = strdup(pval);
256 status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
257 KRB5_CONF_DISABLE_LAST_SUCCESS, FALSE, &bval);
260 dbc->disable_last_success = bval;
262 status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
263 KRB5_CONF_DISABLE_LOCKOUT, FALSE, &bval);
266 dbc->disable_lockout = bval;
271 profile_release_string(pval);
276 * Set *out to one of the filenames used for the DB described by dbc. sfx
277 * should be one of SUFFIX_DB, SUFFIX_LOCK, SUFFIX_POLICY, or
278 * SUFFIX_POLICY_LOCK.
280 static krb5_error_code
281 ctx_dbsuffix(krb5_db2_context *dbc, const char *sfx, char **out)
287 tilde = dbc->tempdb ? "~" : "";
288 if (asprintf(&result, "%s%s%s", dbc->db_name, tilde, sfx) < 0)
294 /* Generate all four files corresponding to dbc. */
295 static krb5_error_code
296 ctx_allfiles(krb5_db2_context *dbc, char **dbname_out, char **lockname_out,
297 char **polname_out, char **plockname_out)
299 char *a = NULL, *b = NULL, *c = NULL, *d = NULL;
301 *dbname_out = *lockname_out = *polname_out = *plockname_out = NULL;
302 if (ctx_dbsuffix(dbc, SUFFIX_DB, &a))
304 if (ctx_dbsuffix(dbc, SUFFIX_LOCK, &b))
306 if (ctx_dbsuffix(dbc, SUFFIX_POLICY, &c))
308 if (ctx_dbsuffix(dbc, SUFFIX_POLICY_LOCK, &d))
324 * Open the DB2 database described by dbc, using the specified flags and mode,
325 * and return the resulting handle. Try both hash and btree database types;
326 * dbc->hashfirst determines which is attempted first. If dbc->hashfirst
327 * indicated the wrong type, update it to indicate the correct type.
330 open_db(krb5_db2_context *dbc, int flags, int mode)
344 if (ctx_dbsuffix(dbc, SUFFIX_DB, &fname) != 0) {
356 /* Try our best guess at the database type. */
357 db = dbopen(fname, flags, mode,
358 dbc->hashfirst ? DB_HASH : DB_BTREE,
359 dbc->hashfirst ? (void *) &hashi : (void *) &bti);
363 /* If that was wrong, retry with the other type. */
369 db = dbopen(fname, flags, mode,
370 dbc->hashfirst ? DB_BTREE : DB_HASH,
371 dbc->hashfirst ? (void *) &bti : (void *) &hashi);
372 /* If that worked, update our guess for next time. */
374 dbc->hashfirst = !dbc->hashfirst;
383 static krb5_error_code
384 ctx_unlock(krb5_context context, krb5_db2_context *dbc)
386 krb5_error_code retval;
389 retval = osa_adb_release_lock(dbc->policy_db);
393 if (!dbc->db_locks_held) /* lock already unlocked */
394 return KRB5_KDB_NOTLOCKED;
397 if (--(dbc->db_locks_held) == 0) {
400 dbc->db_lock_mode = 0;
402 retval = krb5_lock_file(context, dbc->db_lf_file,
403 KRB5_LOCKMODE_UNLOCK);
408 #define MAX_LOCK_TRIES 5
410 static krb5_error_code
411 ctx_lock(krb5_context context, krb5_db2_context *dbc, int lockmode)
413 krb5_error_code retval;
416 if (lockmode == KRB5_DB_LOCKMODE_PERMANENT ||
417 lockmode == KRB5_DB_LOCKMODE_EXCLUSIVE)
418 kmode = KRB5_LOCKMODE_EXCLUSIVE;
419 else if (lockmode == KRB5_DB_LOCKMODE_SHARED)
420 kmode = KRB5_LOCKMODE_SHARED;
424 if (dbc->db_locks_held == 0 || dbc->db_lock_mode < kmode) {
425 /* Acquire or upgrade the lock. */
426 for (tries = 0; tries < MAX_LOCK_TRIES; tries++) {
427 retval = krb5_lock_file(context, dbc->db_lf_file,
428 kmode | KRB5_LOCKMODE_DONTBLOCK);
431 if (retval == EBADF && kmode == KRB5_LOCKMODE_EXCLUSIVE)
432 /* Tried to lock something we don't have write access to. */
433 return KRB5_KDB_CANTLOCK_DB;
436 if (retval == EACCES)
437 return KRB5_KDB_CANTLOCK_DB;
438 else if (retval == EAGAIN || retval == EWOULDBLOCK)
439 return OSA_ADB_CANTLOCK_DB;
443 /* Open the DB (or re-open it for read/write). */
445 dbc->db->close(dbc->db);
446 dbc->db = open_db(dbc,
447 kmode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR,
449 if (dbc->db == NULL) {
451 dbc->db_locks_held = 0;
452 dbc->db_lock_mode = 0;
453 (void) osa_adb_release_lock(dbc->policy_db);
454 (void) krb5_lock_file(context, dbc->db_lf_file,
455 KRB5_LOCKMODE_UNLOCK);
459 dbc->db_lock_mode = kmode;
461 dbc->db_locks_held++;
463 /* Acquire or upgrade the policy lock. */
464 retval = osa_adb_get_lock(dbc->policy_db, lockmode);
466 (void) ctx_unlock(context, dbc);
470 /* Initialize the lock file and policy database fields of dbc. The db_name and
471 * tempdb fields must already be set. */
472 static krb5_error_code
473 ctx_init(krb5_db2_context *dbc)
475 krb5_error_code retval;
476 char *polname = NULL, *plockname = NULL;
478 retval = ctx_dbsuffix(dbc, SUFFIX_LOCK, &dbc->db_lf_name);
483 * should be opened read/write so that write locking can work with
486 if ((dbc->db_lf_file = open(dbc->db_lf_name, O_RDWR, 0666)) < 0) {
487 if ((dbc->db_lf_file = open(dbc->db_lf_name, O_RDONLY, 0666)) < 0) {
492 set_cloexec_fd(dbc->db_lf_file);
495 retval = ctx_dbsuffix(dbc, SUFFIX_POLICY, &polname);
498 retval = ctx_dbsuffix(dbc, SUFFIX_POLICY_LOCK, &plockname);
501 retval = osa_adb_init_db(&dbc->policy_db, polname, plockname,
502 OSA_ADB_POLICY_DB_MAGIC);
513 ctx_fini(krb5_db2_context *dbc)
515 if (dbc->db_lf_file != -1)
516 (void) close(dbc->db_lf_file);
518 (void) osa_adb_fini_db(dbc->policy_db, OSA_ADB_POLICY_DB_MAGIC);
524 krb5_db2_fini(krb5_context context)
526 if (context->dal_handle->db_context != NULL) {
527 ctx_fini(context->dal_handle->db_context);
528 context->dal_handle->db_context = NULL;
533 /* Return successfully if the db2 name set in context can be opened. */
534 static krb5_error_code
535 check_openable(krb5_context context)
538 krb5_db2_context *dbc;
540 dbc = context->dal_handle->db_context;
541 db = open_db(dbc, O_RDONLY, 0);
549 * Return the last modification time of the database.
551 * Think about using fstat.
555 krb5_db2_get_age(krb5_context context, char *db_name, time_t *age)
557 krb5_db2_context *dbc;
560 if (!inited(context))
561 return (KRB5_KDB_DBNOTINITED);
562 dbc = context->dal_handle->db_context;
564 if (fstat(dbc->db_lf_file, &st) < 0)
571 /* Try to update the timestamp on dbc's lockfile. */
573 ctx_update_age(krb5_db2_context *dbc)
577 struct utimbuf utbuf;
579 now = time((time_t *) NULL);
580 if (fstat(dbc->db_lf_file, &st) != 0)
582 if (st.st_mtime >= now) {
583 utbuf.actime = st.st_mtime + 1;
584 utbuf.modtime = st.st_mtime + 1;
585 (void) utime(dbc->db_lf_name, &utbuf);
587 (void) utime(dbc->db_lf_name, (struct utimbuf *) NULL);
591 krb5_db2_lock(krb5_context context, int lockmode)
593 if (!inited(context))
594 return KRB5_KDB_DBNOTINITED;
595 return ctx_lock(context, context->dal_handle->db_context, lockmode);
599 krb5_db2_unlock(krb5_context context)
601 if (!inited(context))
602 return KRB5_KDB_DBNOTINITED;
603 return ctx_unlock(context, context->dal_handle->db_context);
606 /* Zero out and unlink filename. */
607 static krb5_error_code
608 destroy_file(char *filename)
611 int dowrite, j, nb, fd, retval;
613 char buf[BUFSIZ], zbuf[BUFSIZ];
615 fd = open(filename, O_RDWR, 0);
619 /* fstat() will probably not fail unless using a remote filesystem
620 * (which is inappropriate for the kerberos database) so this check
621 * is mostly paranoia. */
622 if (fstat(fd, &statb) == -1)
625 * Stroll through the file, reading in BUFSIZ chunks. If everything
626 * is zero, then we're done for that block, otherwise, zero the block.
627 * We would like to just blast through everything, but some DB
628 * implementations make holey files and writing data to the holes
629 * causes actual blocks to be allocated which is no good, since
630 * we're just about to unlink it anyways.
632 memset(zbuf, 0, BUFSIZ);
634 while (pos < statb.st_size) {
636 nb = read(fd, buf, BUFSIZ);
639 for (j = 0; j < nb; j++) {
640 if (buf[j] != '\0') {
648 lseek(fd, pos, SEEK_SET);
649 nb = write(fd, zbuf, j);
655 /* ??? Is fsync really needed? I don't know of any non-networked
656 * filesystem which will discard queued writes to disk if a file
657 * is deleted after it is closed. --jfc */
663 if (unlink(filename))
673 /* Initialize dbc by locking and creating the DB. If the DB already exists,
674 * clear it out if dbc->tempdb is set; otherwise return EEXIST. */
675 static krb5_error_code
676 ctx_create_db(krb5_context context, krb5_db2_context *dbc)
678 krb5_error_code retval = 0;
679 char *dbname = NULL, *polname = NULL, *plockname = NULL;
681 retval = ctx_allfiles(dbc, &dbname, &dbc->db_lf_name, &polname,
686 dbc->db_lf_file = open(dbc->db_lf_name, O_CREAT | O_RDWR | O_TRUNC,
688 if (dbc->db_lf_file < 0) {
692 retval = krb5_lock_file(context, dbc->db_lf_file,
693 KRB5_LOCKMODE_EXCLUSIVE | KRB5_LOCKMODE_DONTBLOCK);
696 set_cloexec_fd(dbc->db_lf_file);
697 dbc->db_lock_mode = KRB5_LOCKMODE_EXCLUSIVE;
698 dbc->db_locks_held = 1;
701 /* Temporary DBs are locked for their whole lifetime. Since we have
702 * the lock, any remnant files can be safely destroyed. */
703 (void) destroy_file(dbname);
704 (void) unlink(polname);
705 (void) unlink(plockname);
708 dbc->db = open_db(dbc, O_RDWR | O_CREAT | O_EXCL, 0600);
709 if (dbc->db == NULL) {
714 /* Create the policy database, initialize a handle to it, and lock it. */
715 retval = osa_adb_create_db(polname, plockname, OSA_ADB_POLICY_DB_MAGIC);
718 retval = osa_adb_init_db(&dbc->policy_db, polname, plockname,
719 OSA_ADB_POLICY_DB_MAGIC);
722 retval = osa_adb_get_lock(dbc->policy_db, KRB5_DB_LOCKMODE_EXCLUSIVE);
731 dbc->db->close(dbc->db);
732 if (dbc->db_locks_held > 0) {
733 (void) krb5_lock_file(context, dbc->db_lf_file,
734 KRB5_LOCKMODE_UNLOCK);
736 if (dbc->db_lf_file >= 0)
737 close(dbc->db_lf_file);
747 krb5_db2_get_principal(krb5_context context, krb5_const_principal searchfor,
748 unsigned int flags, krb5_db_entry **entry)
750 krb5_db2_context *dbc;
751 krb5_error_code retval;
754 krb5_data keydata, contdata;
758 if (!inited(context))
759 return KRB5_KDB_DBNOTINITED;
761 dbc = context->dal_handle->db_context;
763 for (trynum = 0; trynum < KRB5_DB2_MAX_RETRY; trynum++) {
764 if ((retval = ctx_lock(context, dbc, KRB5_LOCKMODE_SHARED))) {
765 if (dbc->db_nb_locks)
772 if (trynum == KRB5_DB2_MAX_RETRY)
773 return KRB5_KDB_DB_INUSE;
775 /* XXX deal with wildcard lookups */
776 retval = krb5_encode_princ_dbkey(context, &keydata, searchfor);
779 key.data = keydata.data;
780 key.size = keydata.length;
783 dbret = (*db->get)(db, &key, &contents, 0);
785 krb5_free_data_contents(context, &keydata);
788 retval = KRB5_KDB_NOENTRY;
794 contdata.data = contents.data;
795 contdata.length = contents.size;
796 retval = krb5_decode_princ_entry(context, &contdata, entry);
801 (void) krb5_db2_unlock(context); /* unlock read lock */
805 /* Free an entry returned by krb5_db2_get_principal. */
807 krb5_db2_free_principal(krb5_context context, krb5_db_entry *entry)
809 krb5_dbe_free(context, entry);
813 krb5_db2_put_principal(krb5_context context, krb5_db_entry *entry,
819 krb5_data contdata, keydata;
820 krb5_error_code retval;
821 krb5_db2_context *dbc;
823 krb5_clear_error_message (context);
825 /* DB2 does not support db_args DB arguments for principal */
826 krb5_set_error_message(context, EINVAL,
827 _("Unsupported argument \"%s\" for db2"),
832 if (!inited(context))
833 return KRB5_KDB_DBNOTINITED;
835 dbc = context->dal_handle->db_context;
836 if ((retval = ctx_lock(context, dbc, KRB5_LOCKMODE_EXCLUSIVE)))
841 retval = krb5_encode_princ_entry(context, &contdata, entry);
844 contents.data = contdata.data;
845 contents.size = contdata.length;
846 retval = krb5_encode_princ_dbkey(context, &keydata, entry->princ);
848 krb5_free_data_contents(context, &contdata);
852 key.data = keydata.data;
853 key.size = keydata.length;
854 dbret = (*db->put)(db, &key, &contents, 0);
855 retval = dbret ? errno : 0;
856 krb5_free_data_contents(context, &keydata);
857 krb5_free_data_contents(context, &contdata);
861 (void) krb5_db2_unlock(context); /* unlock database */
866 krb5_db2_delete_principal(krb5_context context, krb5_const_principal searchfor)
868 krb5_error_code retval;
869 krb5_db_entry *entry;
870 krb5_db2_context *dbc;
873 krb5_data keydata, contdata;
876 if (!inited(context))
877 return KRB5_KDB_DBNOTINITED;
879 dbc = context->dal_handle->db_context;
880 if ((retval = ctx_lock(context, dbc, KRB5_LOCKMODE_EXCLUSIVE)))
883 if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor)))
885 key.data = keydata.data;
886 key.size = keydata.length;
889 dbret = (*db->get) (db, &key, &contents, 0);
893 retval = KRB5_KDB_NOENTRY;
901 contdata.data = contents.data;
902 contdata.length = contents.size;
903 retval = krb5_decode_princ_entry(context, &contdata, &entry);
907 /* Clear encrypted key contents */
908 for (i = 0; i < entry->n_key_data; i++) {
909 if (entry->key_data[i].key_data_length[0]) {
910 memset(entry->key_data[i].key_data_contents[0], 0,
911 (unsigned) entry->key_data[i].key_data_length[0]);
915 retval = krb5_encode_princ_entry(context, &contdata, entry);
916 krb5_dbe_free(context, entry);
920 contents.data = contdata.data;
921 contents.size = contdata.length;
922 dbret = (*db->put) (db, &key, &contents, 0);
923 retval = dbret ? errno : 0;
924 krb5_free_data_contents(context, &contdata);
927 dbret = (*db->del) (db, &key, 0);
928 retval = dbret ? errno : 0;
930 krb5_free_data_contents(context, &keydata);
934 (void) krb5_db2_unlock(context); /* unlock write lock */
938 static krb5_error_code
939 ctx_iterate(krb5_context context, krb5_db2_context *dbc,
940 krb5_error_code (*func)(krb5_pointer, krb5_db_entry *),
941 krb5_pointer func_arg)
945 krb5_db_entry *entry;
946 krb5_error_code retval, retval2;
949 retval = ctx_lock(context, dbc, KRB5_LOCKMODE_SHARED);
953 dbret = dbc->db->seq(dbc->db, &key, &contents, R_FIRST);
955 contdata.data = contents.data;
956 contdata.length = contents.size;
957 retval = krb5_decode_princ_entry(context, &contdata, &entry);
960 retval = k5_mutex_unlock(krb5_db2_mutex);
963 retval = (*func)(func_arg, entry);
964 krb5_dbe_free(context, entry);
965 retval2 = k5_mutex_lock(krb5_db2_mutex);
966 /* Note: If re-locking fails, the wrapper in db2_exp.c will
967 still try to unlock it again. That would be a bug. Fix
968 when integrating the locking better. */
975 dbret = dbc->db->seq(dbc->db, &key, &contents, R_NEXT);
985 (void) ctx_unlock(context, dbc);
990 krb5_db2_iterate(krb5_context context, char *match_expr,
991 krb5_error_code(*func) (krb5_pointer, krb5_db_entry *),
992 krb5_pointer func_arg)
994 if (!inited(context))
995 return KRB5_KDB_DBNOTINITED;
996 return ctx_iterate(context, context->dal_handle->db_context, func,
1001 krb5_db2_set_lockmode(krb5_context context, krb5_boolean mode)
1004 krb5_db2_context *dbc;
1006 dbc = context->dal_handle->db_context;
1009 old = dbc->db_nb_locks;
1010 dbc->db_nb_locks = mode;
1025 krb5_db2_lib_cleanup()
1027 /* right now, no cleanup required */
1032 krb5_db2_open(krb5_context context, char *conf_section, char **db_args,
1035 krb5_error_code status = 0;
1037 krb5_clear_error_message(context);
1038 if (inited(context))
1041 status = configure_context(context, conf_section, db_args);
1045 status = check_openable(context);
1049 return ctx_init(context->dal_handle->db_context);
1053 krb5_db2_create(krb5_context context, char *conf_section, char **db_args)
1055 krb5_error_code status = 0;
1056 krb5_db2_context *dbc;
1058 krb5_clear_error_message(context);
1059 if (inited(context))
1062 status = configure_context(context, conf_section, db_args);
1066 dbc = context->dal_handle->db_context;
1067 status = ctx_create_db(context, dbc);
1072 krb5_db2_unlock(context);
1078 krb5_db2_destroy(krb5_context context, char *conf_section, char **db_args)
1080 krb5_error_code status;
1081 krb5_db2_context *dbc;
1082 char *dbname = NULL, *lockname = NULL, *polname = NULL, *plockname = NULL;
1084 if (inited(context)) {
1085 status = krb5_db2_fini(context);
1090 krb5_clear_error_message(context);
1091 status = configure_context(context, conf_section, db_args);
1095 status = check_openable(context);
1099 dbc = context->dal_handle->db_context;
1101 status = ctx_allfiles(dbc, &dbname, &lockname, &polname, &plockname);
1104 status = destroy_file(dbname);
1107 status = unlink(lockname);
1110 status = osa_adb_destroy_db(polname, plockname, OSA_ADB_POLICY_DB_MAGIC);
1114 status = krb5_db2_fini(context);
1125 krb5_db2_alloc(krb5_context context, void *ptr, size_t size)
1127 return realloc(ptr, size);
1131 krb5_db2_free(krb5_context context, void *ptr)
1136 /* policy functions */
1138 krb5_db2_create_policy(krb5_context context, osa_policy_ent_t policy)
1140 krb5_db2_context *dbc = context->dal_handle->db_context;
1142 return osa_adb_create_policy(dbc->policy_db, policy);
1146 krb5_db2_get_policy(krb5_context context,
1147 char *name, osa_policy_ent_t *policy)
1149 krb5_db2_context *dbc = context->dal_handle->db_context;
1151 return osa_adb_get_policy(dbc->policy_db, name, policy);
1155 krb5_db2_put_policy(krb5_context context, osa_policy_ent_t policy)
1157 krb5_db2_context *dbc = context->dal_handle->db_context;
1159 return osa_adb_put_policy(dbc->policy_db, policy);
1163 krb5_db2_iter_policy(krb5_context context,
1165 osa_adb_iter_policy_func func, void *data)
1167 krb5_db2_context *dbc = context->dal_handle->db_context;
1169 return osa_adb_iter_policy(dbc->policy_db, func, data);
1173 krb5_db2_delete_policy(krb5_context context, char *policy)
1175 krb5_db2_context *dbc = context->dal_handle->db_context;
1177 return osa_adb_destroy_policy(dbc->policy_db, policy);
1181 krb5_db2_free_policy(krb5_context context, osa_policy_ent_t entry)
1183 osa_free_policy_ent(entry);
1188 * Merge non-replicated attributes from src into dst, setting
1189 * changed to non-zero if dst was changed.
1191 * Non-replicated attributes are: last_success, last_failed,
1192 * fail_auth_count, and any negative TL data values.
1194 static krb5_error_code
1195 krb5_db2_merge_principal(krb5_context context,
1202 if (dst->last_success != src->last_success) {
1203 dst->last_success = src->last_success;
1207 if (dst->last_failed != src->last_failed) {
1208 dst->last_failed = src->last_failed;
1212 if (dst->fail_auth_count != src->fail_auth_count) {
1213 dst->fail_auth_count = src->fail_auth_count;
1220 struct nra_context {
1221 krb5_context kcontext;
1222 krb5_db2_context *db_context;
1226 * Iteration callback merges non-replicated attributes from
1229 static krb5_error_code
1230 krb5_db2_merge_nra_iterator(krb5_pointer ptr, krb5_db_entry *entry)
1232 struct nra_context *nra = (struct nra_context *)ptr;
1233 kdb5_dal_handle *dal_handle = nra->kcontext->dal_handle;
1234 krb5_error_code retval;
1236 krb5_db_entry *s_entry;
1237 krb5_db2_context *dst_db;
1239 memset(&s_entry, 0, sizeof(s_entry));
1241 dst_db = dal_handle->db_context;
1242 dal_handle->db_context = nra->db_context;
1244 /* look up the new principal in the old DB */
1245 retval = krb5_db2_get_principal(nra->kcontext, entry->princ, 0, &s_entry);
1247 /* principal may be newly created, so ignore */
1248 dal_handle->db_context = dst_db;
1252 /* merge non-replicated attributes from the old entry in */
1253 krb5_db2_merge_principal(nra->kcontext, s_entry, entry, &changed);
1255 dal_handle->db_context = dst_db;
1257 /* if necessary, commit the modified new entry to the new DB */
1259 retval = krb5_db2_put_principal(nra->kcontext, entry, NULL);
1268 * Merge non-replicated attributes (that is, lockout-related
1269 * attributes and negative TL data types) from the real database
1270 * into the temporary one.
1272 static krb5_error_code
1273 ctx_merge_nra(krb5_context context, krb5_db2_context *dbc_temp,
1274 krb5_db2_context *dbc_real)
1276 struct nra_context nra;
1278 nra.kcontext = context;
1279 nra.db_context = dbc_real;
1280 return ctx_iterate(context, dbc_temp, krb5_db2_merge_nra_iterator, &nra);
1284 * In the filesystem, promote the temporary database described by dbc_temp to
1285 * the real database described by dbc_real. Both must be exclusively locked.
1287 static krb5_error_code
1288 ctx_promote(krb5_context context, krb5_db2_context *dbc_temp,
1289 krb5_db2_context *dbc_real)
1291 krb5_error_code retval;
1292 char *tdb = NULL, *tlock = NULL, *tpol = NULL, *tplock = NULL;
1293 char *rdb = NULL, *rlock = NULL, *rpol = NULL, *rplock = NULL;
1295 /* Generate all filenames of interest (including a few we don't need). */
1296 retval = ctx_allfiles(dbc_temp, &tdb, &tlock, &tpol, &tplock);
1299 retval = ctx_allfiles(dbc_real, &rdb, &rlock, &rpol, &rplock);
1303 /* Rename the principal and policy databases into place. */
1304 if (rename(tdb, rdb)) {
1308 if (rename(tpol, rpol)) {
1313 ctx_update_age(dbc_real);
1315 /* Release and remove the temporary DB lockfiles. */
1316 (void) unlink(tlock);
1317 (void) unlink(tplock);
1332 krb5_db2_promote_db(krb5_context context, char *conf_section, char **db_args)
1334 krb5_error_code retval;
1335 krb5_boolean merge_nra = FALSE, real_locked = FALSE;
1336 krb5_db2_context *dbc_temp, *dbc_real = NULL;
1339 /* context must be initialized with an exclusively locked temp DB. */
1340 if (!inited(context))
1341 return KRB5_KDB_DBNOTINITED;
1342 dbc_temp = context->dal_handle->db_context;
1343 if (dbc_temp->db_lock_mode != KRB5_LOCKMODE_EXCLUSIVE)
1344 return KRB5_KDB_NOTLOCKED;
1345 if (!dbc_temp->tempdb)
1348 /* Check db_args for whether we should merge non-replicated attributes. */
1349 for (db_argp = db_args; *db_argp; db_argp++) {
1350 if (!strcmp(*db_argp, "merge_nra")) {
1356 /* Make a db2 context for the real DB. */
1357 dbc_real = k5alloc(sizeof(*dbc_real), &retval);
1358 if (dbc_real == NULL)
1360 ctx_clear(dbc_real);
1362 /* Try creating the real DB. */
1363 dbc_real->db_name = strdup(dbc_temp->db_name);
1364 if (dbc_real->db_name == NULL)
1366 dbc_real->tempdb = FALSE;
1367 retval = ctx_create_db(context, dbc_real);
1368 if (retval == EEXIST) {
1369 /* The real database already exists, so open and lock it. */
1370 dbc_real->db_name = strdup(dbc_temp->db_name);
1371 if (dbc_real->db_name == NULL)
1373 dbc_real->tempdb = FALSE;
1374 retval = ctx_init(dbc_real);
1377 retval = ctx_lock(context, dbc_real, KRB5_DB_LOCKMODE_EXCLUSIVE);
1385 retval = ctx_merge_nra(context, dbc_temp, dbc_real);
1390 /* Perform filesystem manipulations for the promotion. */
1391 retval = ctx_promote(context, dbc_temp, dbc_real);
1395 /* Unlock and finalize context since the temp DB is gone. */
1396 (void) krb5_db2_unlock(context);
1397 krb5_db2_fini(context);
1401 (void) ctx_unlock(context, dbc_real);
1408 krb5_db2_check_policy_as(krb5_context kcontext, krb5_kdc_req *request,
1409 krb5_db_entry *client, krb5_db_entry *server,
1410 krb5_timestamp kdc_time, const char **status,
1411 krb5_pa_data ***e_data)
1413 krb5_error_code retval;
1415 retval = krb5_db2_lockout_check_policy(kcontext, client, kdc_time);
1416 if (retval == KRB5KDC_ERR_CLIENT_REVOKED)
1417 *status = "LOCKED_OUT";
1422 krb5_db2_audit_as_req(krb5_context kcontext, krb5_kdc_req *request,
1423 krb5_db_entry *client, krb5_db_entry *server,
1424 krb5_timestamp authtime, krb5_error_code error_code)
1426 (void) krb5_db2_lockout_audit(kcontext, client, authtime, error_code);