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