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
get_db_opt(char * input,char ** opt,char ** val)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
ctx_clear(krb5_db2_context * dbc)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
ctx_get(krb5_context context,krb5_db2_context ** dbc_out)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
configure_context(krb5_context context,char * conf_section,char ** db_args)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
ctx_dbsuffix(krb5_db2_context * dbc,const char * sfx,char ** out)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
ctx_allfiles(krb5_db2_context * dbc,char ** dbname_out,char ** lockname_out,char ** polname_out,char ** plockname_out)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
open_db(krb5_context context,krb5_db2_context * dbc,int flags,int mode,DB ** db_out)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
ctx_unlock(krb5_context context,krb5_db2_context * dbc)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
ctx_lock(krb5_context context,krb5_db2_context * dbc,int lockmode)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
ctx_init(krb5_db2_context * dbc)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
ctx_fini(krb5_db2_context * dbc)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
krb5_db2_fini(krb5_context context)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
check_openable(krb5_context context)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
krb5_db2_get_age(krb5_context context,char * db_name,time_t * age)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
ctx_update_age(krb5_db2_context * dbc)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
krb5_db2_lock(krb5_context context,int lockmode)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
krb5_db2_unlock(krb5_context context)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
destroy_file(char * filename)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
ctx_create_db(krb5_context context,krb5_db2_context * dbc)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
krb5_db2_get_principal(krb5_context context,krb5_const_principal searchfor,unsigned int flags,krb5_db_entry ** entry)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
krb5_db2_put_principal(krb5_context context,krb5_db_entry * entry,char ** db_args)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
krb5_db2_delete_principal(krb5_context context,krb5_const_principal searchfor)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
curs_lock(iter_curs * curs)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
curs_unlock(iter_curs * curs)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
curs_init(iter_curs * curs,krb5_context ctx,krb5_db2_context * dbc,krb5_flags iterflags)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
curs_start(iter_curs * curs)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
curs_save(iter_curs * curs)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
curs_free(iter_curs * curs)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
curs_step(iter_curs * curs)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
curs_run_cb(iter_curs * curs,ctx_iterate_cb func,krb5_pointer func_arg)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
curs_fini(iter_curs * curs)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
ctx_iterate(krb5_context context,krb5_db2_context * dbc,ctx_iterate_cb func,krb5_pointer func_arg,krb5_flags iterflags)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
krb5_db2_iterate(krb5_context context,char * match_expr,ctx_iterate_cb func,krb5_pointer func_arg,krb5_flags iterflags)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
krb5_db2_set_lockmode(krb5_context context,krb5_boolean mode)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
krb5_db2_lib_init()1168 krb5_db2_lib_init()
1169 {
1170 return 0;
1171 }
1172
1173 krb5_error_code
krb5_db2_lib_cleanup()1174 krb5_db2_lib_cleanup()
1175 {
1176 /* right now, no cleanup required */
1177 return 0;
1178 }
1179
1180 krb5_error_code
krb5_db2_open(krb5_context context,char * conf_section,char ** db_args,int mode)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
krb5_db2_create(krb5_context context,char * conf_section,char ** db_args)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
krb5_db2_destroy(krb5_context context,char * conf_section,char ** db_args)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
krb5_db2_create_policy(krb5_context context,osa_policy_ent_t policy)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
krb5_db2_get_policy(krb5_context context,char * name,osa_policy_ent_t * policy)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
krb5_db2_put_policy(krb5_context context,osa_policy_ent_t policy)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
krb5_db2_iter_policy(krb5_context context,char * match_entry,osa_adb_iter_policy_func func,void * data)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
krb5_db2_delete_policy(krb5_context context,char * policy)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
krb5_db2_merge_principal(krb5_context context,krb5_db_entry * src,krb5_db_entry * dst,int * changed)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
krb5_db2_merge_nra_iterator(krb5_pointer ptr,krb5_db_entry * entry)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
ctx_merge_nra(krb5_context context,krb5_db2_context * dbc_temp,krb5_db2_context * dbc_real)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
ctx_promote(krb5_context context,krb5_db2_context * dbc_temp,krb5_db2_context * dbc_real)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
krb5_db2_promote_db(krb5_context context,char * conf_section,char ** db_args)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
krb5_db2_check_policy_as(krb5_context kcontext,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,krb5_timestamp kdc_time,const char ** status,krb5_pa_data *** e_data)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
krb5_db2_audit_as_req(krb5_context kcontext,krb5_kdc_req * request,const krb5_address * local_addr,const krb5_address * remote_addr,krb5_db_entry * client,krb5_db_entry * server,krb5_timestamp authtime,krb5_error_code error_code)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