xref: /illumos-gate/usr/src/lib/krb5/plugins/kdb/db2/kdb_db2.c (revision b5d991cd75971f6bf7a801452ede3fe1667ddfde)
1 /*
2  * Copyright 2008 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, char **db_args)
1318 {
1319     char  **t_ptr = db_args;
1320     int backwards = 0, recursive = 0;
1321 
1322     while (t_ptr && *t_ptr) {
1323 	char   *opt = NULL, *val = NULL;
1324 
1325 	krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1326 
1327 	/* Solaris Kerberos: adding support for -rev/recurse flags */
1328 	if (val && !strcmp(val, "rev"))
1329 	    backwards = 1;
1330 	else if (val && !strcmp(val, "recurse"))
1331 	    recursive = 1;
1332 	else {
1333 	    krb5_set_error_message(context, EINVAL,
1334 				   gettext("Unsupported argument \"%s\" for db2"),
1335 				   val);
1336 	    free(opt);
1337 	    free(val);
1338 	    return EINVAL;
1339 	}
1340 
1341 	free(opt);
1342 	free(val);
1343 	t_ptr++;
1344     }
1345 
1346     /* Solaris Kerberos: adding support for -rev/recurse flags */
1347     return krb5_db2_db_iterate_ext(context, func, func_arg, backwards, recursive);
1348 }
1349 
1350 krb5_boolean
1351 krb5_db2_db_set_lockmode(krb5_context context, krb5_boolean mode)
1352 {
1353     krb5_boolean old;
1354     krb5_db2_context *db_ctx;
1355     kdb5_dal_handle *dal_handle;
1356 
1357     dal_handle = (kdb5_dal_handle *) context->db_context;
1358     old = mode;
1359     if (dal_handle && (db_ctx = (krb5_db2_context *) dal_handle->db_context)) {
1360 	old = db_ctx->db_nb_locks;
1361 	db_ctx->db_nb_locks = mode;
1362     }
1363     return old;
1364 }
1365 
1366 /*
1367  *     DAL API functions
1368  */
1369 krb5_error_code
1370 krb5_db2_lib_init()
1371 {
1372     return 0;
1373 }
1374 
1375 krb5_error_code
1376 krb5_db2_lib_cleanup()
1377 {
1378     /* right now, no cleanup required */
1379     return 0;
1380 }
1381 
1382 krb5_error_code
1383 krb5_db2_open(krb5_context kcontext,
1384 	      char *conf_section, char **db_args, int mode)
1385 {
1386     krb5_error_code status = 0;
1387     char  **t_ptr = db_args;
1388     int     db_name_set = 0, tempdb=0;
1389     char *dbname = NULL;
1390 
1391     krb5_clear_error_message (kcontext);
1392 
1393     if (k5db2_inited(kcontext))
1394 	return 0;
1395 
1396     while (t_ptr && *t_ptr) {
1397 	char   *opt = NULL, *val = NULL;
1398 
1399 	krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1400 	if (opt && !strcmp(opt, "dbname")) {
1401 	    if (dbname) free(dbname);
1402 	    dbname = strdup(val);
1403 	}
1404 	else if (!opt && !strcmp(val, "temporary") ) {
1405 	    tempdb = 1;
1406 	}
1407 	/* ignore hash argument. Might have been passed from create */
1408 	else if (!opt || strcmp(opt, "hash")) {
1409 	    krb5_set_error_message(kcontext, EINVAL,
1410 				   gettext("Unsupported argument \"%s\" for db2"),
1411 				   opt ? opt : val);
1412 	    free(opt);
1413 	    free(val);
1414 	    return EINVAL;
1415 	}
1416 
1417 	free(opt);
1418 	free(val);
1419 	t_ptr++;
1420     }
1421 
1422     if(dbname) {
1423 	status = krb5_db2_db_set_name(kcontext, dbname, tempdb);
1424 	free(dbname);
1425 	if (status) {
1426 	    goto clean_n_exit;
1427 	}
1428 	db_name_set = 1;
1429     }
1430     if (!db_name_set) {
1431 	char   *value = NULL;
1432 	status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME,	/* under given conf section */
1433 				    NULL, &value);
1434 
1435 	if (value == NULL) {
1436 	    /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */
1437 	    status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME,	/* under given realm */
1438 					default_db_name, &value);
1439 	    if (status) {
1440 		goto clean_n_exit;
1441 	    }
1442 	}
1443 
1444 	status = krb5_db2_db_set_name(kcontext, value, tempdb);
1445 	profile_release_string(value);
1446 	if (status) {
1447 	    goto clean_n_exit;
1448 	}
1449 
1450     }
1451 
1452     status = krb5_db2_db_init(kcontext);
1453 
1454   clean_n_exit:
1455     return status;
1456 }
1457 
1458 krb5_error_code
1459 krb5_db2_create(krb5_context kcontext, char *conf_section, char **db_args)
1460 {
1461     krb5_error_code status = 0;
1462     char  **t_ptr = db_args;
1463     int     db_name_set = 0, tempdb=0;
1464     krb5_int32 flags = KRB5_KDB_CREATE_BTREE;
1465     char   *db_name = NULL;
1466 
1467     krb5_clear_error_message (kcontext);
1468 
1469     if (k5db2_inited(kcontext))
1470 	return 0;
1471 
1472     while (t_ptr && *t_ptr) {
1473 	char   *opt = NULL, *val = NULL;
1474 
1475 	krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1476 	if (opt && !strcmp(opt, "dbname")) {
1477 	    db_name = strdup(val);
1478 	    if (db_name == NULL)
1479 		return ENOMEM;
1480 	}
1481 	else if (!opt && !strcmp(val, "temporary")) {
1482 	    tempdb = 1;
1483 	}
1484 	else if (opt && !strcmp(opt, "hash")) {
1485 	    flags = KRB5_KDB_CREATE_HASH;
1486 	} else {
1487 	    krb5_set_error_message(kcontext, EINVAL,
1488 				   gettext("Unsupported argument \"%s\" for db2"),
1489 				   opt ? opt : val);
1490 	    free(opt);
1491 	    free(val);
1492 	    return EINVAL;
1493 	}
1494 
1495 	free(opt);
1496 	free(val);
1497 	t_ptr++;
1498     }
1499     if (db_name) {
1500 	    status = krb5_db2_db_set_name(kcontext, db_name, tempdb);
1501 	    if (!status) {
1502 		status = EEXIST;
1503 		goto clean_n_exit;
1504 	    }
1505 	    db_name_set = 1;
1506     }
1507     if (!db_name_set) {
1508 	char   *value = NULL;
1509 	status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext),
1510 				    KDB_MODULE_SECTION, conf_section,
1511 				    /* under given conf section */
1512 				    KDB_DB2_DATABASE_NAME, NULL, &value);
1513 
1514 	if (value == NULL) {
1515 	    /* Special case for db2.  We might actually be looking at
1516 	     * old type config file where database is specified as
1517 	     * part of realm.  */
1518 	    status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext),
1519 					KDB_REALM_SECTION,
1520 					KRB5_DB_GET_REALM(kcontext),
1521 					/* under given realm */
1522 					KDB_DB2_DATABASE_NAME,
1523 					default_db_name, &value);
1524 	    if (status) {
1525 		goto clean_n_exit;
1526 	    }
1527 	}
1528 
1529 	db_name = strdup(value);
1530 	/* Solaris Kerberos: for safety */
1531 	if (db_name == NULL) {
1532 	    status = ENOMEM;
1533 	    goto clean_n_exit;
1534 	}
1535 	status = krb5_db2_db_set_name(kcontext, value, tempdb);
1536 	profile_release_string(value);
1537 	if (!status) {
1538 	    status = EEXIST;
1539 	    goto clean_n_exit;
1540 	}
1541 
1542     }
1543 
1544     status = krb5_db2_db_create(kcontext, db_name, flags);
1545     if (status)
1546 	goto clean_n_exit;
1547     /* db2 has a problem of needing to close and open the database again. This removes that need */
1548     status = krb5_db2_db_fini(kcontext);
1549     if (status)
1550 	goto clean_n_exit;
1551 
1552     status = krb5_db2_open(kcontext, conf_section, db_args, KRB5_KDB_OPEN_RW);
1553 
1554   clean_n_exit:
1555     if (db_name)
1556 	free(db_name);
1557     return status;
1558 }
1559 
1560 krb5_error_code
1561 krb5_db2_destroy(krb5_context kcontext, char *conf_section, char **db_args)
1562 {
1563     krb5_error_code status = 0;
1564     char  **t_ptr = db_args;
1565     int     db_name_set = 0, tempdb=0;
1566     char   *db_name = NULL;
1567 
1568     while (t_ptr && *t_ptr) {
1569 	char   *opt = NULL, *val = NULL;
1570 
1571 	krb5_db2_get_db_opt(*t_ptr, &opt, &val);
1572 	if (opt && !strcmp(opt, "dbname")) {
1573 	    db_name = strdup(val);
1574 	    if (db_name == NULL)
1575 		return ENOMEM;
1576 	}
1577 	else if (!opt && !strcmp(val, "temporary")) {
1578 	    tempdb = 1;
1579 	}
1580 	/* ignore hash argument. Might have been passed from create */
1581 	else if (!opt || strcmp(opt, "hash")) {
1582 	    free(opt);
1583 	    free(val);
1584 	    return EINVAL;
1585 	}
1586 
1587 	free(opt);
1588 	free(val);
1589 	t_ptr++;
1590     }
1591 
1592     if (db_name) {
1593 	status = krb5_db2_db_set_name(kcontext, db_name, tempdb);
1594 	if (status) {
1595 	    goto clean_n_exit;
1596 	}
1597 	db_name_set = 1;
1598     }
1599     if (!db_name_set) {
1600 	char   *value = NULL;
1601 	status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME,	/* under given conf section */
1602 				    NULL, &value);
1603 
1604 	if (value == NULL) {
1605 	    /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */
1606 	    status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME,	/* under given realm */
1607 					default_db_name, &value);
1608 	    if (status) {
1609 		goto clean_n_exit;
1610 	    }
1611 	}
1612 
1613 	db_name = strdup(value);
1614 	if (db_name == NULL) {
1615 	    status = ENOMEM;
1616 	    goto clean_n_exit;
1617 	}
1618 	status = krb5_db2_db_set_name(kcontext, value, tempdb);
1619 	profile_release_string(value);
1620 	if (status) {
1621 	    goto clean_n_exit;
1622 	}
1623 
1624     }
1625 
1626     status = krb5_db2_db_destroy(kcontext, db_name);
1627 
1628   clean_n_exit:
1629     if (db_name)
1630 	free(db_name);
1631     return status;
1632 }
1633 
1634 krb5_error_code
1635 krb5_db2_set_master_key_ext(krb5_context kcontext,
1636 			    char *pwd, krb5_keyblock * key)
1637 {
1638     return krb5_db2_db_set_mkey(kcontext, key);
1639 }
1640 
1641 krb5_error_code
1642 krb5_db2_db_set_option(krb5_context kcontext, int option, void *value)
1643 {
1644     krb5_error_code status = 0;
1645     krb5_boolean oldval;
1646     krb5_db2_context *db_ctx;
1647     kdb5_dal_handle *dal_handle;
1648 
1649         if (!k5db2_inited(kcontext))
1650 	return KRB5_KDB_DBNOTINITED;
1651 
1652     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1653     db_ctx = (krb5_db2_context *) dal_handle->db_context;
1654 
1655 
1656     switch (option) {
1657     case KRB5_KDB_OPT_SET_DB_NAME:
1658 	status = krb5_db2_db_set_name(kcontext, (char *) value, db_ctx->tempdb);
1659 	break;
1660 
1661     case KRB5_KDB_OPT_SET_LOCK_MODE:
1662 	oldval = krb5_db2_db_set_lockmode(kcontext, *((krb5_boolean *) value));
1663 	*((krb5_boolean *) value) = oldval;
1664 	break;
1665 
1666     default:
1667 	status = -1;		/* TBD */
1668 	break;
1669     }
1670 
1671     return status;
1672 }
1673 
1674 void   *
1675 krb5_db2_alloc(krb5_context kcontext, void *ptr, size_t size)
1676 {
1677     return realloc(ptr, size);
1678 }
1679 
1680 void
1681 krb5_db2_free(krb5_context kcontext, void *ptr)
1682 {
1683     free(ptr);
1684 }
1685 
1686 /* policy functions */
1687 krb5_error_code
1688 krb5_db2_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
1689 {
1690     kdb5_dal_handle *dal_handle;
1691     krb5_db2_context *dbc;
1692 
1693     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1694     dbc = (krb5_db2_context *) dal_handle->db_context;
1695 
1696     return osa_adb_create_policy(dbc->policy_db, policy);
1697 }
1698 
1699 krb5_error_code
1700 krb5_db2_get_policy(krb5_context kcontext,
1701 		    char *name, osa_policy_ent_t * policy, int *cnt)
1702 {
1703     kdb5_dal_handle *dal_handle;
1704     krb5_db2_context *dbc;
1705 
1706     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1707     dbc = (krb5_db2_context *) dal_handle->db_context;
1708 
1709     return osa_adb_get_policy(dbc->policy_db, name, policy, cnt);
1710 }
1711 
1712 krb5_error_code
1713 krb5_db2_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
1714 {
1715     kdb5_dal_handle *dal_handle;
1716     krb5_db2_context *dbc;
1717 
1718     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1719     dbc = (krb5_db2_context *) dal_handle->db_context;
1720 
1721     return osa_adb_put_policy(dbc->policy_db, policy);
1722 }
1723 
1724 krb5_error_code
1725 krb5_db2_iter_policy(krb5_context kcontext,
1726 		     char *match_entry,
1727 		     osa_adb_iter_policy_func func, void *data)
1728 {
1729     kdb5_dal_handle *dal_handle;
1730     krb5_db2_context *dbc;
1731 
1732     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1733     dbc = (krb5_db2_context *) dal_handle->db_context;
1734 
1735     return osa_adb_iter_policy(dbc->policy_db, func, data);
1736 }
1737 
1738 krb5_error_code
1739 krb5_db2_delete_policy(krb5_context kcontext, char *policy)
1740 {
1741     kdb5_dal_handle *dal_handle;
1742     krb5_db2_context *dbc;
1743 
1744     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1745     dbc = (krb5_db2_context *) dal_handle->db_context;
1746 
1747     return osa_adb_destroy_policy(dbc->policy_db, policy);
1748 }
1749 
1750 void
1751 krb5_db2_free_policy(krb5_context kcontext, osa_policy_ent_t entry)
1752 {
1753     osa_free_policy_ent(entry);
1754 }
1755 
1756 
1757 /* */
1758 
1759 krb5_error_code
1760 krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args)
1761 {
1762     krb5_error_code status = 0;
1763     char *db_name = NULL;
1764     char *temp_db_name = NULL;
1765 
1766     krb5_clear_error_message (kcontext);
1767 
1768     {
1769 	kdb5_dal_handle *dal_handle = kcontext->db_context;
1770 	krb5_db2_context *db_ctx = dal_handle->db_context;
1771 	db_name = strdup(db_ctx->db_name);
1772 	if (db_name == NULL) {
1773 	    status = ENOMEM;
1774 	    goto clean_n_exit;
1775 	}
1776     }
1777 
1778     assert(kcontext->db_context != NULL);
1779     temp_db_name = gen_dbsuffix(db_name, "~");
1780     if (temp_db_name == NULL) {
1781 	status = ENOMEM;
1782 	goto clean_n_exit;
1783     }
1784 
1785     status = krb5_db2_db_rename (kcontext, temp_db_name, db_name);
1786 
1787 clean_n_exit:
1788     if (db_name)
1789 	free(db_name);
1790     if (temp_db_name)
1791 	free(temp_db_name);
1792     return status;
1793 }
1794 
1795 /* Retrieved from pre-DAL code base.  */
1796 /*
1797  * "Atomically" rename the database in a way that locks out read
1798  * access in the middle of the rename.
1799  *
1800  * Not perfect; if we crash in the middle of an update, we don't
1801  * necessarily know to complete the transaction the rename, but...
1802  *
1803  * Since the rename operation happens outside the init/fini bracket, we
1804  * have to go through the same stuff that we went through up in db_destroy.
1805  */
1806 krb5_error_code
1807 krb5_db2_db_rename(context, from, to)
1808     krb5_context context;
1809     char *from;
1810     char *to;
1811 {
1812     char *fromok;
1813     krb5_error_code retval;
1814     krb5_db2_context *s_context, *db_ctx;
1815     kdb5_dal_handle *dal_handle = context->db_context;
1816 
1817     s_context = dal_handle->db_context;
1818     dal_handle->db_context = NULL;
1819     if ((retval = k5db2_init_context(context)))
1820 	return retval;
1821     db_ctx = (krb5_db2_context *) dal_handle->db_context;
1822 
1823     /*
1824      * Create the database if it does not already exist; the
1825      * files must exist because krb5_db2_db_lock, called below,
1826      * will fail otherwise.
1827      */
1828     {
1829 	struct stat statbuf;
1830 
1831 	if (stat(to, &statbuf) == -1) {
1832 	    if (errno == ENOENT) {
1833 		retval = krb5_db2_db_create(context, to,
1834 					    KRB5_KDB_CREATE_BTREE);
1835 		if (retval)
1836 		    goto errout;
1837 	    }
1838 	    else {
1839 		/*
1840 		 * XXX assuming we should bail if there is some other stat error
1841 		 */
1842 		retval = errno;
1843 		goto errout;
1844 	    }
1845 	}
1846     }
1847     /*
1848      * Set the database to the target, so that other processes sharing
1849      * the target will stop their activity, and notice the new database.
1850      */
1851     retval = krb5_db2_db_set_name(context, to, 0);
1852     if (retval)
1853 	goto errout;
1854 
1855     retval = krb5_db2_db_init(context);
1856     if (retval)
1857 	goto errout;
1858 
1859     /* XXX WAF this needs to be redone (not lock safe)!!! */
1860     {
1861 	/* Ugly brute force hack.
1862 
1863 	   Should be going through nice friendly helper routines for
1864 	   this, but it's a mess of jumbled so-called interfaces right
1865 	   now.  */
1866 	char    policy[2048], new_policy[2048];
1867 	assert (strlen(db_ctx->db_name) < 2000);
1868 	/*LINTED*/
1869 	sprintf(policy, "%s.kadm5", db_ctx->db_name);
1870 	/*LINTED*/
1871 	sprintf(new_policy, "%s~.kadm5", db_ctx->db_name);
1872 	if (0 != rename(new_policy, policy)) {
1873 	    retval = errno;
1874 	    goto errout;
1875 	}
1876 	strcat(new_policy, ".lock");
1877 	(void) unlink(new_policy);
1878     }
1879 
1880     retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time);
1881     if (retval)
1882 	goto errout;
1883 
1884     fromok = gen_dbsuffix(from, KDB2_LOCK_EXT);
1885     if (fromok == NULL) {
1886 	retval = ENOMEM;
1887 	goto errout;
1888     }
1889 
1890     if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
1891 	goto errfromok;
1892 
1893     if ((retval = krb5_db2_db_start_update(context)))
1894 	goto errfromok;
1895 
1896     if (rename(from, to)) {
1897 	retval = errno;
1898 	goto errfromok;
1899     }
1900     if (unlink(fromok)) {
1901 	retval = errno;
1902 	goto errfromok;
1903     }
1904     retval = krb5_db2_db_end_update(context);
1905 errfromok:
1906     free_dbsuffix(fromok);
1907 errout:
1908     if (dal_handle->db_context) {
1909 	if (db_ctx->db_lf_file >= 0) {
1910 	    krb5_db2_db_unlock(context);
1911 	    close(db_ctx->db_lf_file);
1912 	}
1913 	k5db2_clear_context((krb5_db2_context *) dal_handle->db_context);
1914 	free(dal_handle->db_context);
1915     }
1916 
1917     dal_handle->db_context = s_context;
1918     (void) krb5_db2_db_unlock(context);	/* unlock saved context db */
1919 
1920     return retval;
1921 }
1922