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