xref: /titanic_51/usr/src/lib/krb5/plugins/kdb/db2/kdb_db2.c (revision fad1d7055df96950837a53ae555fcf0d85aa4d19)
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  
81  static char *gen_dbsuffix(char *, char *);
82  
83  static krb5_error_code krb5_db2_db_start_update(krb5_context);
84  static krb5_error_code krb5_db2_db_end_update(krb5_context);
85  
86  static krb5_error_code krb5_db2_db_set_name(krb5_context, char *, int);
87  
88  krb5_error_code krb5_db2_db_lock(krb5_context, int);
89  
90  static krb5_error_code krb5_db2_db_set_hashfirst(krb5_context, int);
91  
92  /*
93   * Solaris Kerberos
94   * Extra error handling
95   */
96  char errbuf[1024];
97  static void krb5_db2_prepend_err_str(krb5_context , const char *,
98      krb5_error_code, krb5_error_code);
99  
100  static 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  
155  static krb5_error_code
156  krb5_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   */
181  static void
182  k5db2_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  
201  static krb5_error_code
202  k5db2_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  
226  static char *
227  gen_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  
244  static DB *
245  k5db2_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  
300  static krb5_error_code
301  krb5_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  
318  krb5_error_code
319  krb5_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   */
390  krb5_error_code
391  krb5_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   */
428  krb5_error_code
429  krb5_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  
443  krb5_error_code
444  krb5_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  
468  static krb5_error_code
469  krb5_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  
508  krb5_error_code
509  krb5_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  
535  static krb5_error_code
536  krb5_db2_db_start_update(krb5_context context)
537  {
538      return 0;
539  }
540  
541  static krb5_error_code
542  krb5_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  
598  krb5_error_code
599  krb5_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  
711  krb5_error_code
712  krb5_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   */
747  krb5_error_code
748  krb5_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   */
821  static krb5_error_code
822  destroy_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   */
911  krb5_error_code
912  krb5_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  
957  krb5_error_code
958  krb5_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   */
1029  krb5_error_code
1030  krb5_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  
1049  krb5_error_code
1050  krb5_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  
1165  err_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  
1180  krb5_error_code
1181  krb5_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  
1301  krb5_error_code
1302  krb5_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  
1380  krb5_error_code
1381  krb5_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  
1417  krb5_boolean
1418  krb5_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   */
1436  krb5_error_code
1437  krb5_db2_lib_init()
1438  {
1439      return 0;
1440  }
1441  
1442  krb5_error_code
1443  krb5_db2_lib_cleanup()
1444  {
1445      /* right now, no cleanup required */
1446      return 0;
1447  }
1448  
1449  krb5_error_code
1450  krb5_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  
1547  krb5_error_code
1548  krb5_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  
1649  krb5_error_code
1650  krb5_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  
1723  krb5_error_code
1724  krb5_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  
1730  krb5_error_code
1731  krb5_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  
1763  void   *
1764  krb5_db2_alloc(krb5_context kcontext, void *ptr, size_t size)
1765  {
1766      return realloc(ptr, size);
1767  }
1768  
1769  void
1770  krb5_db2_free(krb5_context kcontext, void *ptr)
1771  {
1772      free(ptr);
1773  }
1774  
1775  /* policy functions */
1776  krb5_error_code
1777  krb5_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  
1788  krb5_error_code
1789  krb5_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  
1801  krb5_error_code
1802  krb5_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  
1813  krb5_error_code
1814  krb5_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  
1827  krb5_error_code
1828  krb5_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  
1839  void
1840  krb5_db2_free_policy(krb5_context kcontext, osa_policy_ent_t entry)
1841  {
1842      osa_free_policy_ent(entry);
1843  }
1844  
1845  
1846  /* */
1847  
1848  krb5_error_code
1849  krb5_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  
1876  clean_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   */
1895  krb5_error_code
1896  krb5_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);
1994  errfromok:
1995      free_dbsuffix(fromok);
1996  errout:
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  
2012  const char *
2013  krb5_db2_errcode_2_string(krb5_context kcontext, long err_code)
2014  {
2015      return krb5_get_error_message(kcontext, err_code);
2016  }
2017  
2018  void
2019  krb5_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   */
2029  static void
2030  krb5_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