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