1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * lib/kdb/kdb_db2.c
10  *
11  * Copyright 1997,2006 by the Massachusetts Institute of Technology.
12  * All Rights Reserved.
13  *
14  * Export of this software from the United States of America may
15  *   require a specific license from the United States Government.
16  *   It is the responsibility of any person or organization contemplating
17  *   export to obtain such a license before exporting.
18  *
19  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
20  * distribute this software and its documentation for any purpose and
21  * without fee is hereby granted, provided that the above copyright
22  * notice appear in all copies and that both that copyright notice and
23  * this permission notice appear in supporting documentation, and that
24  * the name of M.I.T. not be used in advertising or publicity pertaining
25  * to distribution of the software without specific, written prior
26  * permission.  Furthermore if you modify this software you must label
27  * your software as modified software and not distribute it in such a
28  * fashion that it might be confused with the original M.I.T. software.
29  * M.I.T. makes no representations about the suitability of
30  * this software for any purpose.  It is provided "as is" without express
31  * or implied warranty.
32  *
33  */
34 
35 /*
36  * Copyright (C) 1998 by the FundsXpress, INC.
37  *
38  * All rights reserved.
39  *
40  * Export of this software from the United States of America may require
41  * a specific license from the United States Government.  It is the
42  * responsibility of any person or organization contemplating export to
43  * obtain such a license before exporting.
44  *
45  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
46  * distribute this software and its documentation for any purpose and
47  * without fee is hereby granted, provided that the above copyright
48  * notice appear in all copies and that both that copyright notice and
49  * this permission notice appear in supporting documentation, and that
50  * the name of FundsXpress. not be used in advertising or publicity pertaining
51  * to distribution of the software without specific, written prior
52  * permission.  FundsXpress makes no representations about the suitability of
53  * this software for any purpose.  It is provided "as is" without express
54  * or implied warranty.
55  *
56  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
57  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
58  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
59  */
60 
61 #include "k5-int.h"
62 #include <kdb_log.h>
63 
64 #if HAVE_UNISTD_H
65 #include <unistd.h>
66 #endif
67 
68 #include <db.h>
69 #include <stdio.h>
70 #include <errno.h>
71 #include <utime.h>
72 #include <kdb5.h>
73 #include "kdb_db2.h"
74 #include "kdb_xdr.h"
75 #include "policy_db.h"
76 #include <libintl.h>
77 
78 #define KDB_DB2_DATABASE_NAME "database_name"
79 
80 #include "kdb_db2.h"
81 
82 static char *gen_dbsuffix(char *, char *);
83 
84 static krb5_error_code krb5_db2_db_start_update(krb5_context);
85 static krb5_error_code krb5_db2_db_end_update(krb5_context);
86 
87 static krb5_error_code krb5_db2_db_set_name(krb5_context, char *, int);
88 
89 krb5_error_code krb5_db2_db_lock(krb5_context, int);
90 
91 static krb5_error_code krb5_db2_db_set_hashfirst(krb5_context, int);
92 
93 static char default_db_name[] = DEFAULT_KDB_FILE;
94 
95 /*
96  * Locking:
97  *
98  * There are two distinct locking protocols used.  One is designed to
99  * lock against processes (the admin_server, for one) which make
100  * incremental changes to the database; the other is designed to lock
101  * against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the
102  * entire database in one fell swoop.
103  *
104  * The first locking protocol is implemented using flock() in the
105  * krb_dbl_lock() and krb_dbl_unlock routines.
106  *
107  * The second locking protocol is necessary because DBM "files" are
108  * actually implemented as two separate files, and it is impossible to
109  * atomically rename two files simultaneously.  It assumes that the
110  * database is replaced only very infrequently in comparison to the time
111  * needed to do a database read operation.
112  *
113  * A third file is used as a "version" semaphore; the modification
114  * time of this file is the "version number" of the database.
115  * At the start of a read operation, the reader checks the version
116  * number; at the end of the read operation, it checks again.  If the
117  * version number changed, or if the semaphore was nonexistant at
118  * either time, the reader sleeps for a second to let things
119  * stabilize, and then tries again; if it does not succeed after
120  * KRB5_DBM_MAX_RETRY attempts, it gives up.
121  *
122  * On update, the semaphore file is deleted (if it exists) before any
123  * update takes place; at the end of the update, it is replaced, with
124  * a version number strictly greater than the version number which
125  * existed at the start of the update.
126  *
127  * If the system crashes in the middle of an update, the semaphore
128  * file is not automatically created on reboot; this is a feature, not
129  * a bug, since the database may be inconsistant.  Note that the
130  * absence of a semaphore file does not prevent another _update_ from
131  * taking place later.  Database replacements take place automatically
132  * only on slave servers; a crash in the middle of an update will be
133  * fixed by the next slave propagation.  A crash in the middle of an
134  * update on the master would be somewhat more serious, but this would
135  * likely be noticed by an administrator, who could fix the problem and
136  * retry the operation.
137  */
138 
139 #define free_dbsuffix(name) free(name)
140 
141 /*
142  * Routines to deal with context.
143  */
144 #define	k5db2_inited(c)	(c && c->db_context \
145 			 && ((kdb5_dal_handle*)c->db_context)->db_context \
146                          && ((krb5_db2_context *) ((kdb5_dal_handle*)c->db_context)->db_context)->db_inited)
147 
148 static krb5_error_code
149 krb5_db2_get_db_opt(char *input, char **opt, char **val)
150 {
151     char   *pos = strchr(input, '=');
152     if (pos == NULL) {
153 	*opt = NULL;
154 	*val = strdup(input);
155 	if (*val == NULL) {
156 	    return ENOMEM;
157 	}
158     } else {
159 	*opt = malloc((pos - input) + 1);
160 	*val = strdup(pos + 1);
161 	if (!*opt || !*val) {
162 	    return ENOMEM;
163 	}
164 	memcpy(*opt, input, pos - input);
165 	(*opt)[pos - input] = '\0';
166     }
167     return (0);
168 
169 }
170 
171 /*
172  * Restore the default context.
173  */
174 static void
175 k5db2_clear_context(krb5_db2_context *dbctx)
176 {
177     /*
178      * Free any dynamically allocated memory.  File descriptors and locks
179      * are the caller's problem.
180      */
181     if (dbctx->db_lf_name)
182 	free(dbctx->db_lf_name);
183     if (dbctx->db_name && (dbctx->db_name != default_db_name))
184 	free(dbctx->db_name);
185     /*
186      * Clear the structure and reset the defaults.
187      */
188     memset((char *) dbctx, 0, sizeof(krb5_db2_context));
189     dbctx->db_name = default_db_name;
190     dbctx->db_nb_locks = FALSE;
191     dbctx->tempdb = FALSE;
192 }
193 
194 static krb5_error_code
195 k5db2_init_context(krb5_context context)
196 {
197     krb5_db2_context *db_ctx;
198     kdb5_dal_handle *dal_handle;
199 
200     dal_handle = (kdb5_dal_handle *) context->db_context;
201 
202     if (dal_handle->db_context == NULL) {
203 	db_ctx = (krb5_db2_context *) malloc(sizeof(krb5_db2_context));
204 	if (db_ctx == NULL)
205 	    return ENOMEM;
206 	else {
207 	    memset((char *) db_ctx, 0, sizeof(krb5_db2_context));
208 	    k5db2_clear_context((krb5_db2_context *) db_ctx);
209 	    dal_handle->db_context = (void *) db_ctx;
210 	}
211     }
212     return (0);
213 }
214 
215 /*
216  * Utility routine: generate name of database file.
217  */
218 
219 static char *
220 gen_dbsuffix(char *db_name, char *sfx)
221 {
222     char   *dbsuffix;
223 
224     if (sfx == NULL)
225 	return ((char *) NULL);
226 
227     dbsuffix = malloc(strlen(db_name) + strlen(sfx) + 1);
228     if (!dbsuffix)
229 	return (0);
230     /*LINTED*/
231     (void) strcpy(dbsuffix, db_name);
232     /*LINTED*/
233     (void) strcat(dbsuffix, sfx);
234     return dbsuffix;
235 }
236 
237 static DB *
238 k5db2_dbopen(krb5_db2_context *dbc, char *fname, int flags, int mode, int tempdb)
239 {
240     DB     *db;
241     BTREEINFO bti;
242     HASHINFO hashi;
243     bti.flags = 0;
244     bti.cachesize = 0;
245     bti.psize = 4096;
246     bti.lorder = 0;
247     bti.minkeypage = 0;
248     bti.compare = NULL;
249     bti.prefix = NULL;
250 
251     if (tempdb) {
252 	fname = gen_dbsuffix(fname, "~");
253     } else {
254 	fname = strdup(fname);
255     }
256     if (fname == NULL)
257     {
258 	errno = ENOMEM;
259 	return NULL;
260     }
261 
262 
263     hashi.bsize = 4096;
264     hashi.cachesize = 0;
265     hashi.ffactor = 40;
266     hashi.hash = NULL;
267     hashi.lorder = 0;
268     hashi.nelem = 1;
269 
270     db = dbopen(fname, flags, mode,
271 		dbc->hashfirst ? DB_HASH : DB_BTREE,
272 		dbc->hashfirst ? (void *) &hashi : (void *) &bti);
273     if (db != NULL) {
274 	free(fname);
275 	return db;
276     }
277     switch (errno) {
278 #ifdef EFTYPE
279     case EFTYPE:
280 #endif
281     case EINVAL:
282 	db = dbopen(fname, flags, mode,
283 		    dbc->hashfirst ? DB_BTREE : DB_HASH,
284 		    dbc->hashfirst ? (void *) &bti : (void *) &hashi);
285 	if (db != NULL)
286 	    dbc->hashfirst = !dbc->hashfirst;
287     default:
288 	free(fname);
289 	return db;
290     }
291 }
292 
293 static krb5_error_code
294 krb5_db2_db_set_hashfirst(krb5_context context, int hashfirst)
295 {
296     krb5_db2_context *dbc;
297     kdb5_dal_handle *dal_handle;
298 
299     if (k5db2_inited(context))
300 	return KRB5_KDB_DBNOTINITED;
301     dal_handle = (kdb5_dal_handle *) context->db_context;
302     dbc = (krb5_db2_context *) dal_handle->db_context;
303     dbc->hashfirst = hashfirst;
304     return 0;
305 }
306 
307 /*
308  * initialization for data base routines.
309  */
310 
311 krb5_error_code
312 krb5_db2_db_init(krb5_context context)
313 {
314     char   *filename = NULL;
315     krb5_db2_context *db_ctx;
316     krb5_error_code retval;
317     kdb5_dal_handle *dal_handle;
318     char    policy_db_name[1024], policy_lock_name[1024];
319 
320     if (k5db2_inited(context))
321 	return 0;
322 
323     /* Check for presence of our context, if not present, allocate one. */
324     if ((retval = k5db2_init_context(context)))
325 	return (retval);
326 
327     dal_handle = (kdb5_dal_handle *) context->db_context;
328     db_ctx = dal_handle->db_context;
329     db_ctx->db = NULL;
330 
331     if (!(filename = gen_dbsuffix(db_ctx->db_name, db_ctx->tempdb
332 				  ?KDB2_TEMP_LOCK_EXT:KDB2_LOCK_EXT)))
333 	return ENOMEM;
334     db_ctx->db_lf_name = filename;	/* so it gets freed by clear_context */
335 
336     /*
337      * should be opened read/write so that write locking can work with
338      * POSIX systems
339      */
340     if ((db_ctx->db_lf_file = open(filename, O_RDWR, 0666)) < 0) {
341 	if ((db_ctx->db_lf_file = open(filename, O_RDONLY, 0666)) < 0) {
342 	    retval = errno;
343 	    goto err_out;
344 	}
345     }
346     db_ctx->db_inited++;
347 
348     if ((retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time)))
349 	goto err_out;
350 
351     sprintf(policy_db_name, db_ctx->tempdb ? "%s~.kadm5" : "%s.kadm5",
352 	    db_ctx->db_name);
353     sprintf(policy_lock_name, "%s.lock", policy_db_name);
354 
355     if ((retval = osa_adb_init_db(&db_ctx->policy_db, policy_db_name,
356 				  policy_lock_name, OSA_ADB_POLICY_DB_MAGIC)))
357     {
358 	goto err_out;
359     }
360     return 0;
361 
362   err_out:
363     db_ctx->db = NULL;
364     k5db2_clear_context(db_ctx);
365     return (retval);
366 }
367 
368 /*
369  * gracefully shut down database--must be called by ANY program that does
370  * a krb5_db2_db_init
371  */
372 krb5_error_code
373 krb5_db2_db_fini(krb5_context context)
374 {
375     krb5_error_code retval = 0;
376     krb5_db2_context *db_ctx;
377     kdb5_dal_handle *dal_handle;
378 
379     dal_handle = (kdb5_dal_handle *) context->db_context;
380     if (dal_handle == NULL) {
381 	return 0;
382     }
383 
384     db_ctx = (krb5_db2_context *) dal_handle->db_context;
385 
386     if (k5db2_inited(context)) {
387 	if (close(db_ctx->db_lf_file))
388 	    retval = errno;
389 	else
390 	    retval = 0;
391     }
392     if (db_ctx) {
393 	if (db_ctx->policy_db) {
394 	    retval =
395 		osa_adb_fini_db(db_ctx->policy_db, OSA_ADB_POLICY_DB_MAGIC);
396 	    if (retval)
397 		return retval;
398 	}
399 
400 	k5db2_clear_context(db_ctx);
401 	/*      free(dal_handle->db_context); */
402 	dal_handle->db_context = NULL;
403     }
404     return retval;
405 }
406 
407 /*
408  * Set/Get the master key associated with the database
409  */
410 krb5_error_code
411 krb5_db2_db_set_mkey(krb5_context context, krb5_keyblock *key)
412 {
413     krb5_db2_context *db_ctx;
414     kdb5_dal_handle *dal_handle;
415 
416     if (!k5db2_inited(context))
417 	return (KRB5_KDB_DBNOTINITED);
418 
419     dal_handle = (kdb5_dal_handle *) context->db_context;
420     db_ctx = dal_handle->db_context;
421     db_ctx->db_master_key = key;
422     return 0;
423 }
424 
425 krb5_error_code
426 krb5_db2_db_get_mkey(krb5_context context, krb5_keyblock **key)
427 {
428     krb5_db2_context *db_ctx;
429     kdb5_dal_handle *dal_handle;
430 
431     if (!k5db2_inited(context))
432 	return (KRB5_KDB_DBNOTINITED);
433 
434     dal_handle = (kdb5_dal_handle *) context->db_context;
435     db_ctx = dal_handle->db_context;
436     *key = db_ctx->db_master_key;
437 
438     return 0;
439 }
440 
441 /*
442  * Set the "name" of the current database to some alternate value.
443  *
444  * Passing a null pointer as "name" will set back to the default.
445  * If the alternate database doesn't exist, nothing is changed.
446  *
447  * XXX rethink this
448  */
449 
450 static krb5_error_code
451 krb5_db2_db_set_name(krb5_context context, char *name, int tempdb)
452 {
453     DB     *db;
454     krb5_db2_context *db_ctx;
455     krb5_error_code kret;
456     kdb5_dal_handle *dal_handle;
457 
458     if (k5db2_inited(context))
459 	return KRB5_KDB_DBINITED;
460 
461     /* Check for presence of our context, if not present, allocate one. */
462     if ((kret = k5db2_init_context(context)))
463 	return (kret);
464 
465     if (name == NULL)
466 	name = default_db_name;
467 
468     dal_handle = (kdb5_dal_handle *) context->db_context;
469     db_ctx = dal_handle->db_context;
470     db_ctx->tempdb = tempdb;
471     db = k5db2_dbopen(db_ctx, name, O_RDONLY, 0, tempdb);
472     if (db == NULL)
473 	return errno;
474 
475     db_ctx->db_name = strdup(name);
476     if (db_ctx->db_name == NULL) {
477 	(*db->close) (db);
478 	return ENOMEM;
479     }
480     (*db->close) (db);
481     return 0;
482 }
483 
484 /*
485  * Return the last modification time of the database.
486  *
487  * Think about using fstat.
488  */
489 
490 krb5_error_code
491 krb5_db2_db_get_age(krb5_context context, char *db_name, time_t *age)
492 {
493     krb5_db2_context *db_ctx;
494     kdb5_dal_handle *dal_handle;
495     struct stat st;
496 
497     if (!k5db2_inited(context))
498 	return (KRB5_KDB_DBNOTINITED);
499     dal_handle = (kdb5_dal_handle *) context->db_context;
500     db_ctx = (krb5_db2_context *) dal_handle->db_context;
501 
502     if (fstat(db_ctx->db_lf_file, &st) < 0)
503 	*age = -1;
504     else
505 	*age = st.st_mtime;
506     return 0;
507 }
508 
509 /*
510  * Remove the semaphore file; indicates that database is currently
511  * under renovation.
512  *
513  * This is only for use when moving the database out from underneath
514  * the server (for example, during slave updates).
515  */
516 
517 static krb5_error_code
518 krb5_db2_db_start_update(krb5_context context)
519 {
520     return 0;
521 }
522 
523 static krb5_error_code
524 krb5_db2_db_end_update(krb5_context context)
525 {
526     krb5_error_code retval;
527     krb5_db2_context *db_ctx;
528     kdb5_dal_handle *dal_handle;
529     struct stat st;
530     time_t  now;
531     struct utimbuf utbuf;
532 
533     if (!k5db2_inited(context))
534 	return (KRB5_KDB_DBNOTINITED);
535 
536     retval = 0;
537     dal_handle = (kdb5_dal_handle *) context->db_context;
538     db_ctx = dal_handle->db_context;
539     now = time((time_t *) NULL);
540     if (fstat(db_ctx->db_lf_file, &st) == 0) {
541 	if (st.st_mtime >= now) {
542 	    utbuf.actime = st.st_mtime + 1;
543 	    utbuf.modtime = st.st_mtime + 1;
544 	    if (utime(db_ctx->db_lf_name, &utbuf))
545 		retval = errno;
546 	} else {
547 	    if (utime(db_ctx->db_lf_name, (struct utimbuf *) NULL))
548 		retval = errno;
549 	}
550     } else
551 	retval = errno;
552     if (!retval) {
553 	if (fstat(db_ctx->db_lf_file, &st) == 0)
554 	    db_ctx->db_lf_time = st.st_mtime;
555 	else
556 	    retval = errno;
557     }
558     return (retval);
559 }
560 
561 #define MAX_LOCK_TRIES 5
562 
563 krb5_error_code
564 krb5_db2_db_lock(krb5_context context, int in_mode)
565 {
566     krb5_db2_context *db_ctx;
567     int     krb5_lock_mode;
568     DB     *db;
569     krb5_error_code retval;
570     time_t  mod_time;
571     kdb5_dal_handle *dal_handle;
572     int     mode, gotlock, tries;
573 
574     switch (in_mode) {
575     case KRB5_DB_LOCKMODE_PERMANENT:
576 	mode = KRB5_DB_LOCKMODE_EXCLUSIVE;
577 	break;
578     case KRB5_DB_LOCKMODE_EXCLUSIVE:
579 	mode = KRB5_LOCKMODE_EXCLUSIVE;
580 	break;
581 
582     case KRB5_DB_LOCKMODE_SHARED:
583 	mode = KRB5_LOCKMODE_SHARED;
584 	break;
585     default:
586 	return EINVAL;
587     }
588 
589     if (!k5db2_inited(context))
590 	return KRB5_KDB_DBNOTINITED;
591 
592     dal_handle = (kdb5_dal_handle *) context->db_context;
593     db_ctx = (krb5_db2_context *) dal_handle->db_context;
594     if (db_ctx->db_locks_held && (db_ctx->db_lock_mode >= mode)) {
595 	/* No need to upgrade lock, just return */
596 	db_ctx->db_locks_held++;
597 	goto policy_lock;
598     }
599 
600     if ((mode != KRB5_LOCKMODE_SHARED) && (mode != KRB5_LOCKMODE_EXCLUSIVE))
601 	return KRB5_KDB_BADLOCKMODE;
602 
603     krb5_lock_mode = mode | KRB5_LOCKMODE_DONTBLOCK;
604     for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) {
605 	retval = krb5_lock_file(context, db_ctx->db_lf_file, krb5_lock_mode);
606 	if (retval == 0) {
607 	    gotlock++;
608 	    break;
609 	} else if (retval == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE)
610 	    /* tried to exclusive-lock something we don't have */
611 	    /* write access to */
612 	    return KRB5_KDB_CANTLOCK_DB;
613 	sleep(1);
614     }
615     if (retval == EACCES)
616 	return KRB5_KDB_CANTLOCK_DB;
617     else if (retval == EAGAIN || retval == EWOULDBLOCK)
618 	return OSA_ADB_CANTLOCK_DB;
619     else if (retval != 0)
620 	return retval;
621 
622     if ((retval = krb5_db2_db_get_age(context, NULL, &mod_time)))
623 	goto lock_error;
624 
625     db = k5db2_dbopen(db_ctx, db_ctx->db_name,
626 		      mode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, 0600, db_ctx->tempdb);
627     if (db) {
628 	db_ctx->db_lf_time = mod_time;
629 	db_ctx->db = db;
630     } else {
631 	retval = errno;
632 	db_ctx->db = NULL;
633 	goto lock_error;
634     }
635 
636     db_ctx->db_lock_mode = mode;
637     db_ctx->db_locks_held++;
638 
639   policy_lock:
640     if ((retval = osa_adb_get_lock(db_ctx->policy_db, in_mode))) {
641 	krb5_db2_db_unlock(context);
642     }
643     return retval;
644 
645   lock_error:;
646     db_ctx->db_lock_mode = 0;
647     db_ctx->db_locks_held = 0;
648     krb5_db2_db_unlock(context);
649     return retval;
650 }
651 
652 krb5_error_code
653 krb5_db2_db_unlock(krb5_context context)
654 {
655     krb5_db2_context *db_ctx;
656     kdb5_dal_handle *dal_handle;
657     DB     *db;
658     krb5_error_code retval;
659 
660     if (!k5db2_inited(context))
661 	return KRB5_KDB_DBNOTINITED;
662 
663     dal_handle = (kdb5_dal_handle *) context->db_context;
664     db_ctx = (krb5_db2_context *) dal_handle->db_context;
665 
666     if ((retval = osa_adb_release_lock(db_ctx->policy_db))) {
667 	return retval;
668     }
669 
670     if (!db_ctx->db_locks_held)	/* lock already unlocked */
671 	return KRB5_KDB_NOTLOCKED;
672     db = db_ctx->db;
673     if (--(db_ctx->db_locks_held) == 0) {
674 	(*db->close) (db);
675 	db_ctx->db = NULL;
676 
677 	retval = krb5_lock_file(context, db_ctx->db_lf_file,
678 				KRB5_LOCKMODE_UNLOCK);
679 	db_ctx->db_lock_mode = 0;
680 	return (retval);
681     }
682     return 0;
683 }
684 
685 /*
686  * Create the database, assuming it's not there.
687  */
688 krb5_error_code
689 krb5_db2_db_create(krb5_context context, char *db_name, krb5_int32 flags)
690 {
691     register krb5_error_code retval = 0;
692     kdb5_dal_handle *dal_handle;
693     char   *okname;
694     char   *db_name2 = NULL;
695     int     fd;
696     krb5_db2_context *db_ctx;
697     DB     *db;
698     char    policy_db_name[1024], policy_lock_name[1024];
699 
700     if ((retval = k5db2_init_context(context)))
701 	return (retval);
702 
703     dal_handle = (kdb5_dal_handle *) context->db_context;
704     db_ctx = (krb5_db2_context *) dal_handle->db_context;
705     switch (flags) {
706     case KRB5_KDB_CREATE_HASH:
707 	if ((retval = krb5_db2_db_set_hashfirst(context, TRUE)))
708 	    return retval;
709 	break;
710     case KRB5_KDB_CREATE_BTREE:
711     case 0:
712 	if ((retval = krb5_db2_db_set_hashfirst(context, FALSE)))
713 	    return retval;
714 	break;
715     default:
716 	return KRB5_KDB_BAD_CREATEFLAGS;
717     }
718     db = k5db2_dbopen(db_ctx, db_name, O_RDWR | O_CREAT | O_EXCL, 0600, db_ctx->tempdb);
719     if (db == NULL)
720 	retval = errno;
721     else
722 	(*db->close) (db);
723     if (retval == 0) {
724 
725 	db_name2 = db_ctx->tempdb ? gen_dbsuffix(db_name, "~") : strdup(db_name);
726 	if (db_name2 == NULL)
727 	    return ENOMEM;
728 	okname = gen_dbsuffix(db_name2, KDB2_LOCK_EXT);
729 	if (!okname)
730 	    retval = ENOMEM;
731 	else {
732 	    fd = open(okname, O_CREAT | O_RDWR | O_TRUNC, 0600);
733 	    if (fd < 0)
734 		retval = errno;
735 	    else
736 		close(fd);
737 	    free_dbsuffix(okname);
738 	}
739     }
740 
741     sprintf(policy_db_name, "%s.kadm5", db_name2);
742     sprintf(policy_lock_name, "%s.lock", policy_db_name);
743 
744     retval = osa_adb_create_db(policy_db_name,
745 			       policy_lock_name, OSA_ADB_POLICY_DB_MAGIC);
746     free(db_name2);
747     return retval;
748 }
749 
750 /*
751  * Destroy the database.  Zero's out all of the files, just to be sure.
752  */
753 static krb5_error_code
754 destroy_file_suffix(char *dbname, char *suffix)
755 {
756     char   *filename;
757     struct stat statb;
758     int     nb, fd;
759     unsigned int j;
760     off_t   pos;
761     char    buf[BUFSIZ];
762     char    zbuf[BUFSIZ];
763     int     dowrite;
764 
765     filename = gen_dbsuffix(dbname, suffix);
766     if (filename == 0)
767 	return ENOMEM;
768     if ((fd = open(filename, O_RDWR, 0)) < 0) {
769 	free(filename);
770 	return errno;
771     }
772     /* fstat() will probably not fail unless using a remote filesystem
773      * (which is inappropriate for the kerberos database) so this check
774      * is mostly paranoia.  */
775     if (fstat(fd, &statb) == -1) {
776 	int     retval = errno;
777 	free(filename);
778 	return retval;
779     }
780     /*
781      * Stroll through the file, reading in BUFSIZ chunks.  If everything
782      * is zero, then we're done for that block, otherwise, zero the block.
783      * We would like to just blast through everything, but some DB
784      * implementations make holey files and writing data to the holes
785      * causes actual blocks to be allocated which is no good, since
786      * we're just about to unlink it anyways.
787      */
788     memset(zbuf, 0, BUFSIZ);
789     pos = 0;
790     while (pos < statb.st_size) {
791 	dowrite = 0;
792 	nb = read(fd, buf, BUFSIZ);
793 	if (nb < 0) {
794 	    int     retval = errno;
795 	    free(filename);
796 	    return retval;
797 	}
798 	for (j = 0; j < nb; j++) {
799 	    if (buf[j] != '\0') {
800 		dowrite = 1;
801 		break;
802 	    }
803 	}
804 	/* For signedness */
805 	j = nb;
806 	if (dowrite) {
807 	    lseek(fd, pos, SEEK_SET);
808 	    nb = write(fd, zbuf, j);
809 	    if (nb < 0) {
810 		int     retval = errno;
811 		free(filename);
812 		return retval;
813 	    }
814 	}
815 	pos += nb;
816     }
817     /* ??? Is fsync really needed?  I don't know of any non-networked
818      * filesystem which will discard queued writes to disk if a file
819      * is deleted after it is closed.  --jfc */
820 #ifndef NOFSYNC
821     fsync(fd);
822 #endif
823     close(fd);
824 
825     if (unlink(filename)) {
826 	free(filename);
827 	return (errno);
828     }
829     free(filename);
830     return (0);
831 }
832 
833 /*
834  * Since the destroy operation happens outside the init/fini bracket, we
835  * have some tomfoolery to undergo here.  If we're operating under no
836  * database context, then we initialize with the default.  If the caller
837  * wishes a different context (e.g. different dispatch table), it's their
838  * responsibility to call kdb5_db_set_dbops() before this call.  That will
839  * set up the right dispatch table values (e.g. name extensions).
840  *
841  * Not quite valid due to ripping out of dbops...
842  */
843 krb5_error_code
844 krb5_db2_db_destroy(krb5_context context, char *dbname)
845 {
846     krb5_error_code retval1, retval2;
847     krb5_boolean tmpcontext;
848     char    policy_db_name[1024], policy_lock_name[1024];
849 
850     tmpcontext = 0;
851     if (!context->db_context
852 	|| !((kdb5_dal_handle *) context->db_context)->db_context) {
853 	tmpcontext = 1;
854 	if ((retval1 = k5db2_init_context(context)))
855 	    return (retval1);
856     }
857 
858     retval1 = retval2 = 0;
859     retval1 = destroy_file_suffix(dbname, "");
860     retval2 = destroy_file_suffix(dbname, KDB2_LOCK_EXT);
861 
862     if (tmpcontext) {
863 	k5db2_clear_context((krb5_db2_context *) ((kdb5_dal_handle *) context->
864 						  db_context)->db_context);
865 	free(((kdb5_dal_handle *) context->db_context)->db_context);
866 	((kdb5_dal_handle *) context->db_context)->db_context = NULL;
867     }
868 
869     if (retval1 || retval2)
870 	return (retval1 ? retval1 : retval2);
871 
872 
873     assert (strlen(dbname) + strlen("%s.kadm5") < sizeof(policy_db_name));
874     sprintf(policy_db_name, "%s.kadm5", dbname);
875     /* XXX finish this */
876     sprintf(policy_lock_name, "%s.lock", policy_db_name);
877 
878     retval1 = osa_adb_destroy_db(policy_db_name,
879 				 policy_lock_name, OSA_ADB_POLICY_DB_MAGIC);
880 
881     return retval1;
882 }
883 
884 /*
885  * look up a principal in the data base.
886  * returns number of entries found, and whether there were
887  * more than requested.
888  */
889 
890 krb5_error_code
891 krb5_db2_db_get_principal(krb5_context context,
892 			  krb5_const_principal searchfor,
893 			  krb5_db_entry *entries, /* filled in */
894 			  int *nentries, /* how much room/how many found */
895 			  krb5_boolean *more) /* are there more? */
896 {
897     krb5_db2_context *db_ctx;
898     krb5_error_code retval;
899     DB     *db;
900     DBT     key, contents;
901     krb5_data keydata, contdata;
902     int     trynum, dbret;
903     kdb5_dal_handle *dal_handle;
904 
905     *more = FALSE;
906     *nentries = 0;
907 
908     if (!k5db2_inited(context))
909 	return KRB5_KDB_DBNOTINITED;
910 
911     dal_handle = (kdb5_dal_handle *) context->db_context;
912     db_ctx = (krb5_db2_context *) dal_handle->db_context;
913 
914     for (trynum = 0; trynum < KRB5_DB2_MAX_RETRY; trynum++) {
915 	if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED))) {
916 	    if (db_ctx->db_nb_locks)
917 		return (retval);
918 	    sleep(1);
919 	    continue;
920 	}
921 	break;
922     }
923     if (trynum == KRB5_DB2_MAX_RETRY)
924 	return KRB5_KDB_DB_INUSE;
925 
926     /* XXX deal with wildcard lookups */
927     retval = krb5_encode_princ_dbkey(context, &keydata, searchfor);
928     if (retval)
929 	goto cleanup;
930     key.data = keydata.data;
931     key.size = keydata.length;
932 
933     db = db_ctx->db;
934     dbret = (*db->get) (db, &key, &contents, 0);
935     retval = errno;
936     krb5_free_data_contents(context, &keydata);
937     switch (dbret) {
938     case 1:
939 	retval = 0;
940     /*LINTED*/
941     case -1:
942     default:
943 	*nentries = 0;
944 	goto cleanup;
945     case 0:
946 	contdata.data = contents.data;
947 	contdata.length = contents.size;
948 	retval = krb5_decode_princ_contents(context, &contdata, entries);
949 	if (!retval)
950 	    *nentries = 1;
951 	break;
952     }
953 
954   cleanup:
955     (void) krb5_db2_db_unlock(context);	/* unlock read lock */
956     return retval;
957 }
958 
959 /*
960   Free stuff returned by krb5_db2_db_get_principal.
961  */
962 krb5_error_code
963 krb5_db2_db_free_principal(krb5_context context, krb5_db_entry *entries,
964 			   int nentries)
965 {
966     register int i;
967     for (i = 0; i < nentries; i++)
968 	krb5_dbe_free_contents(context, &entries[i]);
969     return 0;
970 }
971 
972 /*
973   Stores the *"nentries" entry structures pointed to by "entries" in the
974   database.
975 
976   *"nentries" is updated upon return to reflect the number of records
977   acutally stored; the first *"nstored" records will have been stored in the
978   database (even if an error occurs).
979 
980  */
981 
982 krb5_error_code
983 krb5_db2_db_put_principal(krb5_context context,
984 			  krb5_db_entry *entries,
985 			  int *nentries, /* number of entry structs to update */
986 			  char **db_args)
987 {
988     int     i, n, dbret;
989     DB     *db;
990     DBT     key, contents;
991     krb5_data contdata, keydata;
992     krb5_error_code retval;
993     krb5_db2_context *db_ctx;
994     kdb5_dal_handle *dal_handle;
995     kdb_incr_update_t *upd, *fupd;
996     char *princ_name = NULL;
997     kdb_log_context *log_ctx;
998 
999     krb5_clear_error_message (context);
1000     if (db_args) {
1001 	/* DB2 does not support db_args DB arguments for principal */
1002 	krb5_set_error_message(context, EINVAL,
1003 			       gettext("Unsupported argument \"%s\" for db2"),
1004 			       db_args[0]);
1005 	return EINVAL;
1006     }
1007 
1008     log_ctx = context->kdblog_context;
1009 
1010     n = *nentries;
1011     *nentries = 0;
1012     if (!k5db2_inited(context))
1013 	return KRB5_KDB_DBNOTINITED;
1014 
1015     dal_handle = (kdb5_dal_handle *) context->db_context;
1016     db_ctx = (krb5_db2_context *) dal_handle->db_context;
1017     if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
1018 	return retval;
1019 
1020     /*
1021      * Solaris Kerberos: We need the lock since ulog_conv_2logentry() does a get
1022      */
1023     if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1024 	if (!(upd = (kdb_incr_update_t *)
1025 	  malloc(sizeof (kdb_incr_update_t)*n))) {
1026 	    retval = errno;
1027 	    goto err_lock;
1028 	}
1029 	fupd = upd;
1030 
1031 	(void) memset(upd, 0, sizeof(kdb_incr_update_t)*n);
1032 
1033         if ((retval = ulog_conv_2logentry(context, entries, upd, n))) {
1034 	    goto err_lock;
1035 	}
1036     }
1037 
1038     db = db_ctx->db;
1039     if ((retval = krb5_db2_db_start_update(context))) {
1040 	(void) krb5_db2_db_unlock(context);
1041 	goto err_lock;
1042     }
1043 
1044     /* for each one, stuff temps, and do replace/append */
1045     for (i = 0; i < n; i++) {
1046 	/*
1047 	 * Solaris Kerberos: We'll be sharing the same locks as db for logging
1048 	 */
1049         if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1050 		if ((retval = krb5_unparse_name(context, entries->princ,
1051 		    &princ_name)))
1052 			goto err_lock;
1053 
1054 		upd->kdb_princ_name.utf8str_t_val = princ_name;
1055 		upd->kdb_princ_name.utf8str_t_len = strlen(princ_name);
1056 
1057                 if (retval = ulog_add_update(context, upd))
1058 			goto err_lock;
1059         }
1060 
1061 	retval = krb5_encode_princ_contents(context, &contdata, entries);
1062 	if (retval)
1063 	    break;
1064 	contents.data = contdata.data;
1065 	contents.size = contdata.length;
1066 	retval = krb5_encode_princ_dbkey(context, &keydata, entries->princ);
1067 	if (retval) {
1068 	    krb5_free_data_contents(context, &contdata);
1069 	    break;
1070 	}
1071 
1072 	key.data = keydata.data;
1073 	key.size = keydata.length;
1074 	dbret = (*db->put) (db, &key, &contents, 0);
1075 	retval = dbret ? errno : 0;
1076 	krb5_free_data_contents(context, &keydata);
1077 	krb5_free_data_contents(context, &contdata);
1078 	if (retval)
1079 	    break;
1080 	else if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1081 	    /*
1082 	     * We need to make sure the db record is synced before we mark
1083 	     * it as committed via finish_update.
1084 	     */
1085 	    dbret = (*db->sync)(db, 0);
1086 	    if (dbret) {
1087 		retval = errno;
1088 		goto err_lock;
1089 	    }
1090 	    (void) ulog_finish_update(context, upd);
1091 	    upd++;
1092 	}
1093 	entries++;		/* bump to next struct */
1094     }
1095 
1096     (void) krb5_db2_db_end_update(context);
1097 
1098 err_lock:
1099     (void) krb5_db2_db_unlock(context);	/* unlock database */
1100 
1101     if (log_ctx && (log_ctx->iproprole == IPROP_MASTER))
1102         ulog_free_entries(fupd, n);
1103 
1104     *nentries = i;
1105     return (retval);
1106 }
1107 
1108 /*
1109  * delete a principal from the data base.
1110  * returns number of entries removed
1111  */
1112 
1113 krb5_error_code
1114 krb5_db2_db_delete_principal(krb5_context context,
1115 			     krb5_const_principal searchfor,
1116 			     int *nentries) /* how many found & deleted */
1117 {
1118     krb5_error_code retval;
1119     krb5_db_entry entry;
1120     krb5_db2_context *db_ctx;
1121     DB     *db;
1122     DBT     key, contents;
1123     krb5_data keydata, contdata;
1124     int     i, dbret;
1125     kdb5_dal_handle *dal_handle;
1126     kdb_incr_update_t upd;
1127     char *princ_name = NULL;
1128     kdb_log_context *log_ctx;
1129 
1130     log_ctx = context->kdblog_context;
1131 
1132     if (!k5db2_inited(context))
1133 	return KRB5_KDB_DBNOTINITED;
1134 
1135     dal_handle = (kdb5_dal_handle *) context->db_context;
1136     db_ctx = (krb5_db2_context *) dal_handle->db_context;
1137     if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
1138 	return (retval);
1139 
1140     if ((retval = krb5_db2_db_start_update(context))) {
1141 	(void) krb5_db2_db_unlock(context);	/* unlock write lock */
1142 	return (retval);
1143     }
1144 
1145     if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor)))
1146 	goto cleanup;
1147     key.data = keydata.data;
1148     key.size = keydata.length;
1149 
1150     db = db_ctx->db;
1151     dbret = (*db->get) (db, &key, &contents, 0);
1152     retval = errno;
1153     switch (dbret) {
1154     case 1:
1155 	retval = KRB5_KDB_NOENTRY;
1156     /*LINTED*/
1157     case -1:
1158     default:
1159 	*nentries = 0;
1160 	goto cleankey;
1161     case 0:
1162 	;
1163     }
1164     /*
1165      * Solaris Kerberos: We'll be sharing the same locks as db for logging
1166      */
1167     if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1168 	if ((retval = krb5_unparse_name(context, searchfor, &princ_name))) {
1169 		(void) krb5_db2_db_unlock(context);
1170 		return retval;
1171 	}
1172 
1173 	(void) memset(&upd, 0, sizeof (kdb_incr_update_t));
1174 
1175 	upd.kdb_princ_name.utf8str_t_val = princ_name;
1176 	upd.kdb_princ_name.utf8str_t_len = strlen(princ_name);
1177 
1178 	if (retval = ulog_delete_update(context, &upd)) {
1179 		free(princ_name);
1180 		(void) krb5_db2_db_unlock(context);
1181 		return retval;
1182 	}
1183 
1184 	free(princ_name);
1185     }
1186 
1187     memset((char *) &entry, 0, sizeof(entry));
1188     contdata.data = contents.data;
1189     contdata.length = contents.size;
1190     retval = krb5_decode_princ_contents(context, &contdata, &entry);
1191     if (retval)
1192 	goto cleankey;
1193     *nentries = 1;
1194 
1195     /* Clear encrypted key contents */
1196     for (i = 0; i < entry.n_key_data; i++) {
1197 	if (entry.key_data[i].key_data_length[0]) {
1198 	    memset((char *) entry.key_data[i].key_data_contents[0], 0,
1199 		   (unsigned) entry.key_data[i].key_data_length[0]);
1200 	}
1201     }
1202 
1203     retval = krb5_encode_princ_contents(context, &contdata, &entry);
1204     krb5_dbe_free_contents(context, &entry);
1205     if (retval)
1206 	goto cleankey;
1207 
1208     contents.data = contdata.data;
1209     contents.size = contdata.length;
1210     dbret = (*db->put) (db, &key, &contents, 0);
1211     retval = dbret ? errno : 0;
1212     krb5_free_data_contents(context, &contdata);
1213     if (retval)
1214 	goto cleankey;
1215     dbret = (*db->del) (db, &key, 0);
1216     retval = dbret ? errno : 0;
1217 
1218     /*
1219      * We need to commit our update upon success
1220      */
1221     if (!retval)
1222 	if (log_ctx && (log_ctx->iproprole == IPROP_MASTER))
1223 		(void) ulog_finish_update(context, &upd);
1224 
1225   cleankey:
1226     krb5_free_data_contents(context, &keydata);
1227 
1228   cleanup:
1229     (void) krb5_db2_db_end_update(context);
1230     (void) krb5_db2_db_unlock(context);	/* unlock write lock */
1231     return retval;
1232 }
1233 
1234 krb5_error_code
1235 krb5_db2_db_iterate_ext(krb5_context context,
1236 			krb5_error_code(*func) (krb5_pointer, krb5_db_entry *),
1237 			krb5_pointer func_arg,
1238 			int backwards, int recursive)
1239 {
1240     krb5_db2_context *db_ctx;
1241     DB     *db;
1242     DBT     key, contents;
1243     krb5_data contdata;
1244     krb5_db_entry entries;
1245     krb5_error_code retval;
1246     kdb5_dal_handle *dal_handle;
1247     int     dbret;
1248     void   *cookie;
1249 
1250     cookie = NULL;
1251     if (!k5db2_inited(context))
1252 	return KRB5_KDB_DBNOTINITED;
1253 
1254     dal_handle = (kdb5_dal_handle *) context->db_context;
1255     db_ctx = (krb5_db2_context *) dal_handle->db_context;
1256     retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED);
1257 
1258     if (retval)
1259 	return retval;
1260 
1261     db = db_ctx->db;
1262     if (recursive && db->type != DB_BTREE) {
1263 	(void) krb5_db2_db_unlock(context);
1264 	return KRB5_KDB_UK_RERROR;	/* Not optimal, but close enough. */
1265     }
1266 
1267     if (!recursive) {
1268 	dbret = (*db->seq) (db, &key, &contents, backwards ? R_LAST : R_FIRST);
1269     } else {
1270 #ifdef HAVE_BT_RSEQ
1271 	dbret = bt_rseq(db, &key, &contents, &cookie,
1272 			backwards ? R_LAST : R_FIRST);
1273 #else
1274 	(void) krb5_db2_db_unlock(context);
1275 	return KRB5_KDB_UK_RERROR;	/* Not optimal, but close enough. */
1276 #endif
1277     }
1278     while (dbret == 0) {
1279 	contdata.data = contents.data;
1280 	contdata.length = contents.size;
1281 	retval = krb5_decode_princ_contents(context, &contdata, &entries);
1282 	if (retval)
1283 	    break;
1284 	retval = (*func) (func_arg, &entries);
1285 	krb5_dbe_free_contents(context, &entries);
1286 	if (retval)
1287 	    break;
1288 	if (!recursive) {
1289 	    dbret = (*db->seq) (db, &key, &contents,
1290 				backwards ? R_PREV : R_NEXT);
1291 	} else {
1292 #ifdef HAVE_BT_RSEQ
1293 	    dbret = bt_rseq(db, &key, &contents, &cookie,
1294 			    backwards ? R_PREV : R_NEXT);
1295 #else
1296 	    (void) krb5_db2_db_unlock(context);
1297 	    return KRB5_KDB_UK_RERROR;	/* Not optimal, but close enough. */
1298 #endif
1299 	}
1300     }
1301     switch (dbret) {
1302     case 1:
1303     case 0:
1304 	break;
1305     case -1:
1306     default:
1307 	retval = errno;
1308     }
1309     (void) krb5_db2_db_unlock(context);
1310     return retval;
1311 }
1312 
1313 krb5_error_code
1314 krb5_db2_db_iterate(krb5_context context,
1315 		    char *match_expr,
1316 		    krb5_error_code(*func) (krb5_pointer, krb5_db_entry *),
1317 		    krb5_pointer func_arg)
1318 {
1319     return krb5_db2_db_iterate_ext(context, func, func_arg, 0, 0);
1320 }
1321 
1322 krb5_boolean
1323 krb5_db2_db_set_lockmode(krb5_context context, krb5_boolean mode)
1324 {
1325     krb5_boolean old;
1326     krb5_db2_context *db_ctx;
1327     kdb5_dal_handle *dal_handle;
1328 
1329     dal_handle = (kdb5_dal_handle *) context->db_context;
1330     old = mode;
1331     if (dal_handle && (db_ctx = (krb5_db2_context *) dal_handle->db_context)) {
1332 	old = db_ctx->db_nb_locks;
1333 	db_ctx->db_nb_locks = mode;
1334     }
1335     return old;
1336 }
1337 
1338 /*
1339  *     DAL API functions
1340  */
1341 krb5_error_code
1342 krb5_db2_lib_init()
1343 {
1344     return 0;
1345 }
1346 
1347 krb5_error_code
1348 krb5_db2_lib_cleanup()
1349 {
1350     /* right now, no cleanup required */
1351     return 0;
1352 }
1353 
1354 krb5_error_code
1355 krb5_db2_open(krb5_context kcontext,
1356 	      char *conf_section, char **db_args, int mode)
1357 {
1358     krb5_error_code status = 0;
1359     char  **t_ptr = db_args;
1360     int     db_name_set = 0, tempdb=0;
1361     char *dbname = NULL;
1362 
1363     krb5_clear_error_message (kcontext);
1364 
1365     if (k5db2_inited(kcontext))
1366 	return 0;
1367 
1368     while (t_ptr && *t_ptr) {
1369 	char   *opt = NULL, *val = NULL;
1370 
1371 	krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1372 	if (opt && !strcmp(opt, "dbname")) {
1373 	    if (dbname) free(dbname);
1374 	    dbname = strdup(val);
1375 	}
1376 	else if (!opt && !strcmp(val, "temporary") ) {
1377 	    tempdb = 1;
1378 	}
1379 	/* ignore hash argument. Might have been passed from create */
1380 	else if (!opt || strcmp(opt, "hash")) {
1381 	    krb5_set_error_message(kcontext, EINVAL,
1382 				   gettext("Unsupported argument \"%s\" for db2"),
1383 				   opt ? opt : val);
1384 	    free(opt);
1385 	    free(val);
1386 	    return EINVAL;
1387 	}
1388 
1389 	free(opt);
1390 	free(val);
1391 	t_ptr++;
1392     }
1393 
1394     if(dbname) {
1395 	status = krb5_db2_db_set_name(kcontext, dbname, tempdb);
1396 	free(dbname);
1397 	if (status) {
1398 	    goto clean_n_exit;
1399 	}
1400 	db_name_set = 1;
1401     }
1402     if (!db_name_set) {
1403 	char   *value = NULL;
1404 	status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME,	/* under given conf section */
1405 				    NULL, &value);
1406 
1407 	if (value == NULL) {
1408 	    /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */
1409 	    status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME,	/* under given realm */
1410 					default_db_name, &value);
1411 	    if (status) {
1412 		goto clean_n_exit;
1413 	    }
1414 	}
1415 
1416 	status = krb5_db2_db_set_name(kcontext, value, tempdb);
1417 	profile_release_string(value);
1418 	if (status) {
1419 	    goto clean_n_exit;
1420 	}
1421 
1422     }
1423 
1424     status = krb5_db2_db_init(kcontext);
1425 
1426   clean_n_exit:
1427     return status;
1428 }
1429 
1430 krb5_error_code
1431 krb5_db2_create(krb5_context kcontext, char *conf_section, char **db_args)
1432 {
1433     krb5_error_code status = 0;
1434     char  **t_ptr = db_args;
1435     int     db_name_set = 0, tempdb=0;
1436     krb5_int32 flags = KRB5_KDB_CREATE_BTREE;
1437     char   *db_name = NULL;
1438 
1439     krb5_clear_error_message (kcontext);
1440 
1441     if (k5db2_inited(kcontext))
1442 	return 0;
1443 
1444     while (t_ptr && *t_ptr) {
1445 	char   *opt = NULL, *val = NULL;
1446 
1447 	krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1448 	if (opt && !strcmp(opt, "dbname")) {
1449 	    db_name = strdup(val);
1450 	    if (db_name == NULL)
1451 		return ENOMEM;
1452 	}
1453 	else if (!opt && !strcmp(val, "temporary")) {
1454 	    tempdb = 1;
1455 	}
1456 	else if (opt && !strcmp(opt, "hash")) {
1457 	    flags = KRB5_KDB_CREATE_HASH;
1458 	} else {
1459 	    krb5_set_error_message(kcontext, EINVAL,
1460 				   gettext("Unsupported argument \"%s\" for db2"),
1461 				   opt ? opt : val);
1462 	    free(opt);
1463 	    free(val);
1464 	    return EINVAL;
1465 	}
1466 
1467 	free(opt);
1468 	free(val);
1469 	t_ptr++;
1470     }
1471     if (db_name) {
1472 	    status = krb5_db2_db_set_name(kcontext, db_name, tempdb);
1473 	    if (!status) {
1474 		status = EEXIST;
1475 		goto clean_n_exit;
1476 	    }
1477 	    db_name_set = 1;
1478     }
1479     if (!db_name_set) {
1480 	char   *value = NULL;
1481 	status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext),
1482 				    KDB_MODULE_SECTION, conf_section,
1483 				    /* under given conf section */
1484 				    KDB_DB2_DATABASE_NAME, NULL, &value);
1485 
1486 	if (value == NULL) {
1487 	    /* Special case for db2.  We might actually be looking at
1488 	     * old type config file where database is specified as
1489 	     * part of realm.  */
1490 	    status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext),
1491 					KDB_REALM_SECTION,
1492 					KRB5_DB_GET_REALM(kcontext),
1493 					/* under given realm */
1494 					KDB_DB2_DATABASE_NAME,
1495 					default_db_name, &value);
1496 	    if (status) {
1497 		goto clean_n_exit;
1498 	    }
1499 	}
1500 
1501 	db_name = strdup(value);
1502 	/* Solaris Kerberos: for safety */
1503 	if (db_name == NULL) {
1504 	    status = ENOMEM;
1505 	    goto clean_n_exit;
1506 	}
1507 	status = krb5_db2_db_set_name(kcontext, value, tempdb);
1508 	profile_release_string(value);
1509 	if (!status) {
1510 	    status = EEXIST;
1511 	    goto clean_n_exit;
1512 	}
1513 
1514     }
1515 
1516     status = krb5_db2_db_create(kcontext, db_name, flags);
1517     if (status)
1518 	goto clean_n_exit;
1519     /* db2 has a problem of needing to close and open the database again. This removes that need */
1520     status = krb5_db2_db_fini(kcontext);
1521     if (status)
1522 	goto clean_n_exit;
1523 
1524     status = krb5_db2_open(kcontext, conf_section, db_args, KRB5_KDB_OPEN_RW);
1525 
1526   clean_n_exit:
1527     if (db_name)
1528 	free(db_name);
1529     return status;
1530 }
1531 
1532 krb5_error_code
1533 krb5_db2_destroy(krb5_context kcontext, char *conf_section, char **db_args)
1534 {
1535     krb5_error_code status = 0;
1536     char  **t_ptr = db_args;
1537     int     db_name_set = 0, tempdb=0;
1538     char   *db_name = NULL;
1539 
1540     while (t_ptr && *t_ptr) {
1541 	char   *opt = NULL, *val = NULL;
1542 
1543 	krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1544 	if (opt && !strcmp(opt, "dbname")) {
1545 	    db_name = strdup(val);
1546 	    if (db_name == NULL)
1547 		return ENOMEM;
1548 	}
1549 	else if (!opt && !strcmp(val, "temporary")) {
1550 	    tempdb = 1;
1551 	}
1552 	/* ignore hash argument. Might have been passed from create */
1553 	else if (!opt || strcmp(opt, "hash")) {
1554 	    free(opt);
1555 	    free(val);
1556 	    return EINVAL;
1557 	}
1558 
1559 	free(opt);
1560 	free(val);
1561 	t_ptr++;
1562     }
1563 
1564     if (db_name) {
1565 	status = krb5_db2_db_set_name(kcontext, db_name, tempdb);
1566 	if (status) {
1567 	    goto clean_n_exit;
1568 	}
1569 	db_name_set = 1;
1570     }
1571     if (!db_name_set) {
1572 	char   *value = NULL;
1573 	status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME,	/* under given conf section */
1574 				    NULL, &value);
1575 
1576 	if (value == NULL) {
1577 	    /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */
1578 	    status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME,	/* under given realm */
1579 					default_db_name, &value);
1580 	    if (status) {
1581 		goto clean_n_exit;
1582 	    }
1583 	}
1584 
1585 	db_name = strdup(value);
1586 	if (db_name == NULL) {
1587 	    status = ENOMEM;
1588 	    goto clean_n_exit;
1589 	}
1590 	status = krb5_db2_db_set_name(kcontext, value, tempdb);
1591 	profile_release_string(value);
1592 	if (status) {
1593 	    goto clean_n_exit;
1594 	}
1595 
1596     }
1597 
1598     status = krb5_db2_db_destroy(kcontext, db_name);
1599 
1600   clean_n_exit:
1601     if (db_name)
1602 	free(db_name);
1603     return status;
1604 }
1605 
1606 krb5_error_code
1607 krb5_db2_set_master_key_ext(krb5_context kcontext,
1608 			    char *pwd, krb5_keyblock * key)
1609 {
1610     return krb5_db2_db_set_mkey(kcontext, key);
1611 }
1612 
1613 krb5_error_code
1614 krb5_db2_db_set_option(krb5_context kcontext, int option, void *value)
1615 {
1616     krb5_error_code status = 0;
1617     krb5_boolean oldval;
1618     krb5_db2_context *db_ctx;
1619     kdb5_dal_handle *dal_handle;
1620 
1621         if (!k5db2_inited(kcontext))
1622 	return KRB5_KDB_DBNOTINITED;
1623 
1624     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1625     db_ctx = (krb5_db2_context *) dal_handle->db_context;
1626 
1627 
1628     switch (option) {
1629     case KRB5_KDB_OPT_SET_DB_NAME:
1630 	status = krb5_db2_db_set_name(kcontext, (char *) value, db_ctx->tempdb);
1631 	break;
1632 
1633     case KRB5_KDB_OPT_SET_LOCK_MODE:
1634 	oldval = krb5_db2_db_set_lockmode(kcontext, *((krb5_boolean *) value));
1635 	*((krb5_boolean *) value) = oldval;
1636 	break;
1637 
1638     default:
1639 	status = -1;		/* TBD */
1640 	break;
1641     }
1642 
1643     return status;
1644 }
1645 
1646 void   *
1647 krb5_db2_alloc(krb5_context kcontext, void *ptr, size_t size)
1648 {
1649     return realloc(ptr, size);
1650 }
1651 
1652 void
1653 krb5_db2_free(krb5_context kcontext, void *ptr)
1654 {
1655     free(ptr);
1656 }
1657 
1658 /* policy functions */
1659 krb5_error_code
1660 krb5_db2_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
1661 {
1662     kdb5_dal_handle *dal_handle;
1663     krb5_db2_context *dbc;
1664 
1665     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1666     dbc = (krb5_db2_context *) dal_handle->db_context;
1667 
1668     return osa_adb_create_policy(dbc->policy_db, policy);
1669 }
1670 
1671 krb5_error_code
1672 krb5_db2_get_policy(krb5_context kcontext,
1673 		    char *name, osa_policy_ent_t * policy, int *cnt)
1674 {
1675     kdb5_dal_handle *dal_handle;
1676     krb5_db2_context *dbc;
1677 
1678     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1679     dbc = (krb5_db2_context *) dal_handle->db_context;
1680 
1681     return osa_adb_get_policy(dbc->policy_db, name, policy, cnt);
1682 }
1683 
1684 krb5_error_code
1685 krb5_db2_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
1686 {
1687     kdb5_dal_handle *dal_handle;
1688     krb5_db2_context *dbc;
1689 
1690     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1691     dbc = (krb5_db2_context *) dal_handle->db_context;
1692 
1693     return osa_adb_put_policy(dbc->policy_db, policy);
1694 }
1695 
1696 krb5_error_code
1697 krb5_db2_iter_policy(krb5_context kcontext,
1698 		     char *match_entry,
1699 		     osa_adb_iter_policy_func func, void *data)
1700 {
1701     kdb5_dal_handle *dal_handle;
1702     krb5_db2_context *dbc;
1703 
1704     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1705     dbc = (krb5_db2_context *) dal_handle->db_context;
1706 
1707     return osa_adb_iter_policy(dbc->policy_db, func, data);
1708 }
1709 
1710 krb5_error_code
1711 krb5_db2_delete_policy(krb5_context kcontext, char *policy)
1712 {
1713     kdb5_dal_handle *dal_handle;
1714     krb5_db2_context *dbc;
1715 
1716     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1717     dbc = (krb5_db2_context *) dal_handle->db_context;
1718 
1719     return osa_adb_destroy_policy(dbc->policy_db, policy);
1720 }
1721 
1722 void
1723 krb5_db2_free_policy(krb5_context kcontext, osa_policy_ent_t entry)
1724 {
1725     osa_free_policy_ent(entry);
1726 }
1727 
1728 
1729 /* */
1730 
1731 krb5_error_code
1732 krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args)
1733 {
1734     krb5_error_code status = 0;
1735     char *db_name = NULL;
1736     char *temp_db_name = NULL;
1737 
1738     krb5_clear_error_message (kcontext);
1739 
1740     {
1741 	kdb5_dal_handle *dal_handle = kcontext->db_context;
1742 	krb5_db2_context *db_ctx = dal_handle->db_context;
1743 	db_name = strdup(db_ctx->db_name);
1744 	if (db_name == NULL) {
1745 	    status = ENOMEM;
1746 	    goto clean_n_exit;
1747 	}
1748     }
1749 
1750     assert(kcontext->db_context != NULL);
1751     temp_db_name = gen_dbsuffix(db_name, "~");
1752     if (temp_db_name == NULL) {
1753 	status = ENOMEM;
1754 	goto clean_n_exit;
1755     }
1756 
1757     status = krb5_db2_db_rename (kcontext, temp_db_name, db_name);
1758 
1759 clean_n_exit:
1760     if (db_name)
1761 	free(db_name);
1762     if (temp_db_name)
1763 	free(temp_db_name);
1764     return status;
1765 }
1766 
1767 /* Retrieved from pre-DAL code base.  */
1768 /*
1769  * "Atomically" rename the database in a way that locks out read
1770  * access in the middle of the rename.
1771  *
1772  * Not perfect; if we crash in the middle of an update, we don't
1773  * necessarily know to complete the transaction the rename, but...
1774  *
1775  * Since the rename operation happens outside the init/fini bracket, we
1776  * have to go through the same stuff that we went through up in db_destroy.
1777  */
1778 krb5_error_code
1779 krb5_db2_db_rename(context, from, to)
1780     krb5_context context;
1781     char *from;
1782     char *to;
1783 {
1784     char *fromok;
1785     krb5_error_code retval;
1786     krb5_db2_context *s_context, *db_ctx;
1787     kdb5_dal_handle *dal_handle = context->db_context;
1788 
1789     s_context = dal_handle->db_context;
1790     dal_handle->db_context = NULL;
1791     if ((retval = k5db2_init_context(context)))
1792 	return retval;
1793     db_ctx = (krb5_db2_context *) dal_handle->db_context;
1794 
1795     /*
1796      * Create the database if it does not already exist; the
1797      * files must exist because krb5_db2_db_lock, called below,
1798      * will fail otherwise.
1799      */
1800     {
1801 	struct stat statbuf;
1802 
1803 	if (stat(to, &statbuf) == -1) {
1804 	    if (errno == ENOENT) {
1805 		retval = krb5_db2_db_create(context, to,
1806 					    KRB5_KDB_CREATE_BTREE);
1807 		if (retval)
1808 		    goto errout;
1809 	    }
1810 	    else {
1811 		/*
1812 		 * XXX assuming we should bail if there is some other stat error
1813 		 */
1814 		retval = errno;
1815 		goto errout;
1816 	    }
1817 	}
1818     }
1819     /*
1820      * Set the database to the target, so that other processes sharing
1821      * the target will stop their activity, and notice the new database.
1822      */
1823     retval = krb5_db2_db_set_name(context, to, 0);
1824     if (retval)
1825 	goto errout;
1826 
1827     retval = krb5_db2_db_init(context);
1828     if (retval)
1829 	goto errout;
1830 
1831     /* XXX WAF this needs to be redone (not lock safe)!!! */
1832     {
1833 	/* Ugly brute force hack.
1834 
1835 	   Should be going through nice friendly helper routines for
1836 	   this, but it's a mess of jumbled so-called interfaces right
1837 	   now.  */
1838 	char    policy[2048], new_policy[2048];
1839 	assert (strlen(db_ctx->db_name) < 2000);
1840 	/*LINTED*/
1841 	sprintf(policy, "%s.kadm5", db_ctx->db_name);
1842 	/*LINTED*/
1843 	sprintf(new_policy, "%s~.kadm5", db_ctx->db_name);
1844 	if (0 != rename(new_policy, policy)) {
1845 	    retval = errno;
1846 	    goto errout;
1847 	}
1848 	strcat(new_policy, ".lock");
1849 	(void) unlink(new_policy);
1850     }
1851 
1852     retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time);
1853     if (retval)
1854 	goto errout;
1855 
1856     fromok = gen_dbsuffix(from, KDB2_LOCK_EXT);
1857     if (fromok == NULL) {
1858 	retval = ENOMEM;
1859 	goto errout;
1860     }
1861 
1862     if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
1863 	goto errfromok;
1864 
1865     if ((retval = krb5_db2_db_start_update(context)))
1866 	goto errfromok;
1867 
1868     if (rename(from, to)) {
1869 	retval = errno;
1870 	goto errfromok;
1871     }
1872     if (unlink(fromok)) {
1873 	retval = errno;
1874 	goto errfromok;
1875     }
1876     retval = krb5_db2_db_end_update(context);
1877 errfromok:
1878     free_dbsuffix(fromok);
1879 errout:
1880     if (dal_handle->db_context) {
1881 	if (db_ctx->db_lf_file >= 0) {
1882 	    krb5_db2_db_unlock(context);
1883 	    close(db_ctx->db_lf_file);
1884 	}
1885 	k5db2_clear_context((krb5_db2_context *) dal_handle->db_context);
1886 	free(dal_handle->db_context);
1887     }
1888 
1889     dal_handle->db_context = s_context;
1890     (void) krb5_db2_db_unlock(context);	/* unlock saved context db */
1891 
1892     return retval;
1893 }
1894