442c28f27a8fb6a4322048edb9af4a0c96fa47c7
[krb5.git] / src / lib / kdb / kdb5.c
1 /*
2  * Copyright 2006, 2009 by the Massachusetts Institute of Technology.
3  * All Rights Reserved.
4  *
5  * Export of this software from the United States of America may
6  *   require a specific license from the United States Government.
7  *   It is the responsibility of any person or organization contemplating
8  *   export to obtain such a license before exporting.
9  * 
10  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11  * distribute this software and its documentation for any purpose and
12  * without fee is hereby granted, provided that the above copyright
13  * notice appear in all copies and that both that copyright notice and
14  * this permission notice appear in supporting documentation, and that
15  * the name of M.I.T. not be used in advertising or publicity pertaining
16  * to distribution of the software without specific, written prior
17  * permission.  Furthermore if you modify this software you must label
18  * your software as modified software and not distribute it in such a
19  * fashion that it might be confused with the original M.I.T. software.
20  * M.I.T. makes no representations about the suitability of
21  * this software for any purpose.  It is provided "as is" without express
22  * or implied warranty.
23  */
24
25 /*
26  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29
30 /*
31  * This code was based on code donated to MIT by Novell for
32  * distribution under the MIT license.
33  */
34
35 /* 
36  * Include files
37  */
38
39 #include <stdio.h>
40 #include <string.h>
41 #include <k5-int.h>
42 #include <osconf.h>
43 #include "kdb5.h"
44 #include <assert.h>
45 #include "kdb_log.h"
46 #include "kdb5int.h"
47
48 /* Currently DB2 policy related errors are exported from DAL.  But
49    other databases should set_err function to return string.  */
50 #include "adb_err.h"
51
52 /*
53  * Type definitions
54  */
55 #define KRB5_TL_DB_ARGS                 0x7fff
56
57 /*
58  * internal static variable
59  */
60
61 static k5_mutex_t db_lock = K5_MUTEX_PARTIAL_INITIALIZER;
62
63 #ifdef _KDB5_STATIC_LINK
64 #undef _KDB5_DYNAMIC_LINK
65 #else
66 #undef _KDB5_DYNAMIC_LINK
67 /* to avoid redefinition problem */
68 #define _KDB5_DYNAMIC_LINK
69 #endif
70
71 static db_library lib_list;
72
73 /*
74  * Helper Functions
75  */
76
77 MAKE_INIT_FUNCTION(kdb_init_lock_list);
78 MAKE_FINI_FUNCTION(kdb_fini_lock_list);
79
80 int
81 kdb_init_lock_list(void)
82 {
83     return k5_mutex_finish_init(&db_lock);
84 }
85
86 static int
87 kdb_lock_list()
88 {
89     int err;
90     err = CALL_INIT_FUNCTION (kdb_init_lock_list);
91     if (err)
92         return err;
93     return k5_mutex_lock(&db_lock);
94 }
95
96 void
97 kdb_fini_lock_list(void)
98 {
99     if (INITIALIZER_RAN(kdb_init_lock_list))
100         k5_mutex_destroy(&db_lock);
101 }
102
103 static int
104 kdb_unlock_list()
105 {
106     return k5_mutex_unlock(&db_lock);
107 }
108
109 /*
110  * XXX eventually this should be consolidated with krb5_free_key_data_contents
111  * so there is only a single version.
112  */
113 void
114 krb5_dbe_free_key_data_contents(krb5_context context, krb5_key_data *key)
115 {
116     int i, idx;
117
118     idx = (key->key_data_ver == 1 ? 1 : 2);
119     for (i = 0; i < idx; i++) {
120         if (key->key_data_contents[i]) {
121             zap(key->key_data_contents[i], key->key_data_length[i]);
122             free(key->key_data_contents[i]);
123         }
124     }
125     return;
126 }
127
128 void
129 krb5_dbe_free_key_list(krb5_context context, krb5_keylist_node *val)
130 {
131     krb5_keylist_node *temp = val, *prev;
132
133     while (temp != NULL) {
134         prev = temp;
135         temp = temp->next;
136         krb5_free_keyblock_contents(context, &(prev->keyblock));
137         krb5_xfree(prev);
138     }
139 }
140
141 void
142 krb5_dbe_free_actkvno_list(krb5_context context, krb5_actkvno_node *val)
143 {
144     krb5_actkvno_node *temp = val, *prev;
145
146     while (temp != NULL) {
147         prev = temp;
148         temp = temp->next;
149         krb5_xfree(prev);
150     }
151 }
152
153 void
154 krb5_dbe_free_mkey_aux_list(krb5_context context, krb5_mkey_aux_node *val)
155 {
156     krb5_mkey_aux_node *temp = val, *prev;
157
158     while (temp != NULL) {
159         prev = temp;
160         temp = temp->next;
161         krb5_dbe_free_key_data_contents(context, &prev->latest_mkey);
162         krb5_xfree(prev);
163     }
164 }
165
166 void
167 krb5_dbe_free_tl_data(krb5_context context, krb5_tl_data *tl_data)
168 {
169     if (tl_data) {
170         if (tl_data->tl_data_contents)
171             free(tl_data->tl_data_contents);
172         free(tl_data);
173     }
174 }
175
176 #define kdb_init_lib_lock(a) 0
177 #define kdb_destroy_lib_lock(a) (void)0
178 #define kdb_lock_lib_lock(a, b) 0
179 #define kdb_unlock_lib_lock(a, b) (void)0
180
181 /* Caller must free result*/
182
183 static char *
184 kdb_get_conf_section(krb5_context kcontext)
185 {
186     krb5_error_code status = 0;
187     char   *result = NULL;
188     char   *value = NULL;
189
190     if (kcontext->default_realm == NULL)
191         return NULL;
192     /* The profile has to have been initialized.  If the profile was
193        not initialized, expect nothing less than a crash.  */
194     status = profile_get_string(kcontext->profile,
195                                 /* realms */
196                                 KDB_REALM_SECTION,
197                                 kcontext->default_realm,
198                                 /* under the realm name, database_module */
199                                 KDB_MODULE_POINTER,
200                                 /* default value is the realm name itself */
201                                 kcontext->default_realm,
202                                 &value);
203
204     if (status) {
205         /* some problem */
206         result = strdup(kcontext->default_realm);
207         /* let NULL be handled by the caller */
208     } else {
209         result = strdup(value);
210         /* free profile string */
211         profile_release_string(value);
212     }
213
214     return result;
215 }
216
217 static char *
218 kdb_get_library_name(krb5_context kcontext)
219 {
220     krb5_error_code status = 0;
221     char   *result = NULL;
222     char   *value = NULL;
223     char   *lib = NULL;
224
225     status = profile_get_string(kcontext->profile,
226                                 /* realms */
227                                 KDB_REALM_SECTION,
228                                 kcontext->default_realm,
229                                 /* under the realm name, database_module */
230                                 KDB_MODULE_POINTER,
231                                 /* default value is the realm name itself */
232                                 kcontext->default_realm,
233                                 &value);
234     if (status) {
235         goto clean_n_exit;
236     }
237
238 #define DB2_NAME "db2"
239     /* we got the module section. Get the library name from the module */
240     status = profile_get_string(kcontext->profile, KDB_MODULE_SECTION, value,
241                                 KDB_LIB_POINTER,
242                                 /* default to db2 */
243                                 DB2_NAME,
244                                 &lib);
245
246     if (status) {
247         goto clean_n_exit;
248     }
249
250     result = strdup(lib);
251   clean_n_exit:
252     if (value) {
253         /* free profile string */
254         profile_release_string(value);
255     }
256
257     if (lib) {
258         /* free profile string */
259         profile_release_string(lib);
260     }
261     return result;
262 }
263
264 static void
265 kdb_setup_opt_functions(db_library lib)
266 {
267     if (lib->vftabl.set_master_key == NULL) {
268         lib->vftabl.set_master_key = kdb_def_set_mkey;
269     }
270
271     if (lib->vftabl.set_master_key_list == NULL) {
272         lib->vftabl.set_master_key_list = kdb_def_set_mkey_list;
273     }
274
275     if (lib->vftabl.get_master_key == NULL) {
276         lib->vftabl.get_master_key = kdb_def_get_mkey;
277     }
278
279     if (lib->vftabl.get_master_key_list == NULL) {
280         lib->vftabl.get_master_key_list = kdb_def_get_mkey_list;
281     }
282
283     if (lib->vftabl.fetch_master_key == NULL) {
284         lib->vftabl.fetch_master_key = krb5_db_def_fetch_mkey;
285     }
286
287     if (lib->vftabl.verify_master_key == NULL) {
288         lib->vftabl.verify_master_key = krb5_def_verify_master_key;
289     }
290
291     if (lib->vftabl.fetch_master_key_list == NULL) {
292         lib->vftabl.fetch_master_key_list = krb5_def_fetch_mkey_list;
293     }
294
295     if (lib->vftabl.store_master_key_list == NULL) {
296         lib->vftabl.store_master_key_list = krb5_def_store_mkey_list;
297     }
298
299     if (lib->vftabl.dbe_search_enctype == NULL) {
300         lib->vftabl.dbe_search_enctype = krb5_dbe_def_search_enctype;
301     }
302
303     if (lib->vftabl.db_change_pwd == NULL) {
304         lib->vftabl.db_change_pwd = krb5_dbe_def_cpw;
305     }
306
307     if (lib->vftabl.store_master_key == NULL) {
308         lib->vftabl.store_master_key = krb5_def_store_mkey;
309     }
310
311     if (lib->vftabl.promote_db == NULL) {
312         lib->vftabl.promote_db = krb5_def_promote_db;
313     }
314     
315     if (lib->vftabl.dbekd_decrypt_key_data == NULL) {
316         lib->vftabl.dbekd_decrypt_key_data = krb5_dbekd_def_decrypt_key_data;
317     }
318
319     if (lib->vftabl.dbekd_encrypt_key_data == NULL) {
320         lib->vftabl.dbekd_encrypt_key_data = krb5_dbekd_def_encrypt_key_data;
321     }
322 }
323
324 static int kdb_db2_pol_err_loaded = 0;
325 #ifdef _KDB5_STATIC_LINK
326 #define DEF_SYMBOL(a) extern kdb_vftabl krb5_db_vftabl_ ## a
327 #define GET_SYMBOL(a) (krb5_db_vftabl_ ## a)
328 static krb5_error_code
329 kdb_load_library(krb5_context kcontext, char *lib_name, db_library * lib)
330 {
331     krb5_error_code status;
332     void   *vftabl_addr = NULL;
333     char    buf[KRB5_MAX_ERR_STR];
334
335     if (!strcmp("kdb_db2", lib_name) && (kdb_db2_pol_err_loaded == 0)) {
336         initialize_adb_error_table();
337         kdb_db2_pol_err_loaded = 1;
338     }
339
340     *lib = calloc((size_t) 1, sizeof(**lib));
341     if (*lib == NULL) {
342         status = ENOMEM;
343         goto clean_n_exit;
344     }
345
346     status = kdb_init_lib_lock(*lib);
347     if (status) {
348         goto clean_n_exit;
349     }
350
351     strlcpy((*lib)->name, lib_name, sizeof((*lib)->name));
352
353 #if !defined(KDB5_USE_LIB_KDB_DB2) && !defined(KDB5_USE_LIB_TEST)
354 #error No database module defined
355 #endif
356
357 #ifdef KDB5_USE_LIB_KDB_DB2
358     if (strcmp(lib_name, "kdb_db2") == 0) {
359         DEF_SYMBOL(kdb_db2);
360         vftabl_addr = (void *) &GET_SYMBOL(kdb_db2);
361     } else
362 #endif
363 #ifdef KDB5_USE_LIB_TEST
364     if (strcmp(lib_name, "test") == 0) {
365         DEF_SYMBOL(test);
366         vftabl_addr = (void *) &GET_SYMBOL(test);
367     } else
368 #endif
369     {
370         snprintf(buf, sizeof(buf),
371                  "Program not built to support %s database type\n",
372                  lib_name);
373         status = KRB5_KDB_DBTYPE_NOSUP;
374         krb5_db_set_err(kcontext, krb5_err_have_str, status, buf);
375         goto clean_n_exit;
376     }
377
378     memcpy(&(*lib)->vftabl, vftabl_addr, sizeof(kdb_vftabl));
379
380     kdb_setup_opt_functions(*lib);
381
382     if ((status = (*lib)->vftabl.init_library())) {
383         /* ERROR. library not initialized cleanly */
384         snprintf(buf, sizeof(buf),
385                  "%s library initialization failed, error code %ld\n",
386                  lib_name, status);
387         status = KRB5_KDB_DBTYPE_INIT;
388         krb5_db_set_err(kcontext, krb5_err_have_str, status, buf);
389         goto clean_n_exit;
390     }
391
392   clean_n_exit:
393     if (status) {
394         free(*lib), *lib = NULL;
395     }
396     return status;
397 }
398
399 #else /* KDB5_STATIC_LINK*/
400
401 static char *db_dl_location[] = DEFAULT_KDB_LIB_PATH;
402 #define db_dl_n_locations (sizeof(db_dl_location) / sizeof(db_dl_location[0]))
403
404 static krb5_error_code
405 kdb_load_library(krb5_context kcontext, char *lib_name, db_library * lib)
406 {
407     krb5_error_code status = 0;
408     int     ndx;
409     void  **vftabl_addrs = NULL;
410     /* N.B.: If this is "const" but not "static", the Solaris 10
411        native compiler has trouble building the library because of
412        absolute relocations needed in read-only section ".rodata".
413        When it's static, it goes into ".picdata", which is
414        read-write.  */
415     static const char *const dbpath_names[] = {
416         KDB_MODULE_SECTION, KRB5_CONF_DB_MODULE_DIR, NULL,
417     };
418     const char *filebases[2];
419     char **profpath = NULL;
420     char **path = NULL;
421
422     filebases[0] = lib_name;
423     filebases[1] = NULL;
424
425     if (!strcmp(DB2_NAME, lib_name) && (kdb_db2_pol_err_loaded == 0)) {
426         initialize_adb_error_table();
427         kdb_db2_pol_err_loaded = 1;
428     }
429
430     *lib = calloc((size_t) 1, sizeof(**lib));
431     if (*lib == NULL) {
432         status = ENOMEM;
433         goto clean_n_exit;
434     }
435
436     status = kdb_init_lib_lock(*lib);
437     if (status) {
438         goto clean_n_exit;
439     }
440
441     strlcpy((*lib)->name, lib_name, sizeof((*lib)->name));
442
443     /* Fetch the list of directories specified in the config
444        file(s) first.  */
445     status = profile_get_values(kcontext->profile, dbpath_names, &profpath);
446     if (status != 0 && status != PROF_NO_RELATION)
447         goto clean_n_exit;
448     ndx = 0;
449     if (profpath)
450         while (profpath[ndx] != NULL)
451             ndx++;
452
453     path = calloc(ndx + db_dl_n_locations, sizeof (char *));
454     if (path == NULL) {
455         status = ENOMEM;
456         goto clean_n_exit;
457     }
458     if (ndx)
459         memcpy(path, profpath, ndx * sizeof(profpath[0]));
460     memcpy(path + ndx, db_dl_location, db_dl_n_locations * sizeof(char *));
461     status = 0;
462     
463     if ((status = krb5int_open_plugin_dirs ((const char **) path, 
464                                             filebases, 
465                                             &(*lib)->dl_dir_handle, &kcontext->err))) {
466         const char *err_str = krb5_get_error_message(kcontext, status);
467         status = KRB5_KDB_DBTYPE_NOTFOUND;
468         krb5_set_error_message (kcontext, status,
469                                 "Unable to find requested database type: %s", err_str);
470         krb5_free_error_message (kcontext, err_str);
471         goto clean_n_exit;
472     }
473
474     if ((status = krb5int_get_plugin_dir_data (&(*lib)->dl_dir_handle, "kdb_function_table",
475                                                &vftabl_addrs, &kcontext->err))) {
476         const char *err_str = krb5_get_error_message(kcontext, status);
477         status = KRB5_KDB_DBTYPE_INIT;
478         krb5_set_error_message (kcontext, status,
479                                 "plugin symbol 'kdb_function_table' lookup failed: %s", err_str);
480         krb5_free_error_message (kcontext, err_str);
481         goto clean_n_exit;
482     }
483
484     if (vftabl_addrs[0] == NULL) {
485         /* No plugins! */
486         status = KRB5_KDB_DBTYPE_NOTFOUND;
487         krb5_set_error_message (kcontext, status,
488                                 _("Unable to load requested database module '%s': plugin symbol 'kdb_function_table' not found"),
489                                 lib_name);
490         goto clean_n_exit;
491     }
492
493     memcpy(&(*lib)->vftabl, vftabl_addrs[0], sizeof(kdb_vftabl));
494     kdb_setup_opt_functions(*lib);
495     
496     if ((status = (*lib)->vftabl.init_library())) {
497         /* ERROR. library not initialized cleanly */
498         goto clean_n_exit;
499     }    
500     
501 clean_n_exit:
502     if (vftabl_addrs != NULL) { krb5int_free_plugin_dir_data (vftabl_addrs); }
503     /* Both of these DTRT with NULL.  */
504     profile_free_list(profpath);
505     free(path);
506     if (status) {
507         if (*lib) {
508             kdb_destroy_lib_lock(*lib);
509             if (PLUGIN_DIR_OPEN((&(*lib)->dl_dir_handle))) {
510                 krb5int_close_plugin_dirs (&(*lib)->dl_dir_handle);
511             }
512             free(*lib);
513             *lib = NULL;
514         }
515     }
516     return status;
517 }
518
519 #endif /* end of _KDB5_STATIC_LINK */
520
521 static krb5_error_code
522 kdb_find_library(krb5_context kcontext, char *lib_name, db_library * lib)
523 {
524     /* lock here so that no two threads try to do the same at the same time */
525     krb5_error_code status = 0;
526     int     locked = 0;
527     db_library curr_elt, prev_elt = NULL;
528
529     if ((status = kdb_lock_list()) != 0) {
530         goto clean_n_exit;
531     }
532     locked = 1;
533
534     curr_elt = lib_list;
535     while (curr_elt != NULL) {
536         if (strcmp(lib_name, curr_elt->name) == 0) {
537             *lib = curr_elt;
538             goto clean_n_exit;
539         }
540         prev_elt = curr_elt;
541         curr_elt = curr_elt->next;
542     }
543
544     /* module not found. create and add to list */
545     status = kdb_load_library(kcontext, lib_name, lib);
546     if (status) {
547         goto clean_n_exit;
548     }
549
550     if (prev_elt) {
551         /* prev_elt points to the last element in the list */
552         prev_elt->next = *lib;
553         (*lib)->prev = prev_elt;
554     } else {
555         lib_list = *lib;
556     }
557
558   clean_n_exit:
559     if (*lib) {
560         (*lib)->reference_cnt++;
561     }
562
563     if (locked) {
564         kdb_unlock_list();
565     }
566
567     return status;
568 }
569
570 static krb5_error_code
571 kdb_free_library(db_library lib)
572 {
573     krb5_error_code status = 0;
574     int     locked = 0;
575
576     if ((status = kdb_lock_list()) != 0) {
577         goto clean_n_exit;
578     }
579     locked = 1;
580
581     lib->reference_cnt--;
582
583     if (lib->reference_cnt == 0) {
584         status = lib->vftabl.fini_library();
585         if (status) {
586             goto clean_n_exit;
587         }
588
589         /* close the library */
590         if (PLUGIN_DIR_OPEN((&lib->dl_dir_handle))) {
591             krb5int_close_plugin_dirs (&lib->dl_dir_handle);
592         }
593         
594         kdb_destroy_lib_lock(lib);
595
596         if (lib->prev == NULL) {
597             /* first element in the list */
598             lib_list = lib->next;
599         } else {
600             lib->prev->next = lib->next;
601         }
602
603         if (lib->next) {
604             lib->next->prev = lib->prev;
605         }
606         free(lib);
607     }
608
609   clean_n_exit:
610     if (locked) {
611         kdb_unlock_list();
612     }
613
614     return status;
615 }
616
617 static krb5_error_code
618 kdb_setup_lib_handle(krb5_context kcontext)
619 {
620     char   *library = NULL;
621     krb5_error_code status = 0;
622     db_library lib = NULL;
623     kdb5_dal_handle *dal_handle = NULL;
624
625     dal_handle = calloc((size_t) 1, sizeof(kdb5_dal_handle));
626     if (dal_handle == NULL) {
627         status = ENOMEM;
628         goto clean_n_exit;
629     }
630
631     library = kdb_get_library_name(kcontext);
632     if (library == NULL) {
633         status = KRB5_KDB_DBTYPE_NOTFOUND;
634         goto clean_n_exit;
635     }
636
637     status = kdb_find_library(kcontext, library, &lib);
638     if (status) {
639         goto clean_n_exit;
640     }
641
642     dal_handle->lib_handle = lib;
643     kcontext->dal_handle = dal_handle;
644
645   clean_n_exit:
646     free(library);
647
648     if (status) {
649         free(dal_handle);
650         if (lib) {
651             kdb_free_library(lib);
652         }
653     }
654
655     return status;
656 }
657
658 static krb5_error_code
659 kdb_free_lib_handle(krb5_context kcontext)
660 {
661     krb5_error_code status = 0;
662
663     status = kdb_free_library(kcontext->dal_handle->lib_handle);
664     if (status) {
665         goto clean_n_exit;
666     }
667
668     free(kcontext->dal_handle);
669     kcontext->dal_handle = NULL;
670
671   clean_n_exit:
672     return status;
673 }
674
675 static void
676 get_errmsg (krb5_context kcontext, krb5_error_code err_code)
677 {
678     kdb5_dal_handle *dal_handle;
679     const char *e;
680     if (err_code == 0)
681         return;
682     assert(kcontext != NULL);
683     /* Must be called with dal_handle->lib_handle locked!  */
684     assert(kcontext->dal_handle != NULL);
685     dal_handle = kcontext->dal_handle;
686     if (dal_handle->lib_handle->vftabl.errcode_2_string == NULL)
687         return;
688     e = dal_handle->lib_handle->vftabl.errcode_2_string(kcontext, err_code);
689     assert (e != NULL);
690     krb5_set_error_message(kcontext, err_code, "%s", e);
691     if (dal_handle->lib_handle->vftabl.release_errcode_string)
692         dal_handle->lib_handle->vftabl.release_errcode_string(kcontext, e);
693 }
694
695 /*
696  *      External functions... DAL API
697  */
698 krb5_error_code
699 krb5_db_open(krb5_context kcontext, char **db_args, int mode)
700 {
701     krb5_error_code status = 0;
702     char   *section = NULL;
703     kdb5_dal_handle *dal_handle;
704
705     section = kdb_get_conf_section(kcontext);
706     if (section == NULL) {
707         status = KRB5_KDB_SERVER_INTERNAL_ERR;
708         krb5_set_error_message (kcontext, status,
709                 "unable to determine configuration section for realm %s\n",
710                 kcontext->default_realm ? kcontext->default_realm : "[UNSET]");
711         goto clean_n_exit;
712     }
713
714     if (kcontext->dal_handle == NULL) {
715         status = kdb_setup_lib_handle(kcontext);
716         if (status) {
717             goto clean_n_exit;
718         }
719     }
720
721     dal_handle = kcontext->dal_handle;
722     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
723     if (status) {
724         goto clean_n_exit;
725     }
726
727     status =
728         dal_handle->lib_handle->vftabl.init_module(kcontext, section, db_args,
729                                                    mode);
730     get_errmsg(kcontext, status);
731
732     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
733
734   clean_n_exit:
735     if (section)
736         free(section);
737     return status;
738 }
739
740 krb5_error_code
741 krb5_db_inited(krb5_context kcontext)
742 {
743     return !(kcontext && kcontext->dal_handle &&
744              kcontext->dal_handle->db_context);
745 }
746
747 krb5_error_code
748 krb5_db_create(krb5_context kcontext, char **db_args)
749 {
750     krb5_error_code status = 0;
751     char   *section = NULL;
752     kdb5_dal_handle *dal_handle;
753
754     section = kdb_get_conf_section(kcontext);
755     if (section == NULL) {
756         status = KRB5_KDB_SERVER_INTERNAL_ERR;
757         krb5_set_error_message (kcontext, status,
758                 "unable to determine configuration section for realm %s\n",
759                 kcontext->default_realm);
760         goto clean_n_exit;
761     }
762
763     if (kcontext->dal_handle == NULL) {
764         status = kdb_setup_lib_handle(kcontext);
765         if (status) {
766             goto clean_n_exit;
767         }
768     }
769
770     dal_handle = kcontext->dal_handle;
771     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
772     if (status) {
773         goto clean_n_exit;
774     }
775
776     status =
777         dal_handle->lib_handle->vftabl.db_create(kcontext, section, db_args);
778     get_errmsg(kcontext, status);
779
780     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
781
782   clean_n_exit:
783     if (section)
784         free(section);
785     return status;
786 }
787
788 krb5_error_code
789 krb5_db_fini(krb5_context kcontext)
790 {
791     krb5_error_code status = 0;
792     kdb5_dal_handle *dal_handle;
793
794     if (kcontext->dal_handle == NULL) {
795         /* module not loaded. So nothing to be done */
796         goto clean_n_exit;
797     }
798
799     dal_handle = kcontext->dal_handle;
800     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
801     if (status) {
802         goto clean_n_exit;
803     }
804
805     status = dal_handle->lib_handle->vftabl.fini_module(kcontext);
806     get_errmsg(kcontext, status);
807
808     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
809
810     if (status) {
811         goto clean_n_exit;
812     }
813
814     status = kdb_free_lib_handle(kcontext);
815
816   clean_n_exit:
817     return status;
818 }
819
820 krb5_error_code
821 krb5_db_destroy(krb5_context kcontext, char **db_args)
822 {
823     krb5_error_code status = 0;
824     char   *section = NULL;
825     kdb5_dal_handle *dal_handle;
826
827     section = kdb_get_conf_section(kcontext);
828     if (section == NULL) {
829         status = KRB5_KDB_SERVER_INTERNAL_ERR;
830         krb5_set_error_message (kcontext, status,
831                 "unable to determine configuration section for realm %s\n",
832                 kcontext->default_realm);
833         goto clean_n_exit;
834     }
835
836     if (kcontext->dal_handle == NULL) {
837         status = kdb_setup_lib_handle(kcontext);
838         if (status) {
839             goto clean_n_exit;
840         }
841     }
842
843     dal_handle = kcontext->dal_handle;
844     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
845     if (status) {
846         goto clean_n_exit;
847     }
848
849     status =
850         dal_handle->lib_handle->vftabl.db_destroy(kcontext, section, db_args);
851     get_errmsg(kcontext, status);
852     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
853
854   clean_n_exit:
855     if (section)
856         free(section);
857     return status;
858 }
859
860 krb5_error_code
861 krb5_db_get_age(krb5_context kcontext, char *db_name, time_t * t)
862 {
863     krb5_error_code status = 0;
864     kdb5_dal_handle *dal_handle;
865
866     if (kcontext->dal_handle == NULL) {
867         status = kdb_setup_lib_handle(kcontext);
868         if (status) {
869             goto clean_n_exit;
870         }
871     }
872
873     dal_handle = kcontext->dal_handle;
874     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
875     if (status) {
876         goto clean_n_exit;
877     }
878
879     status = dal_handle->lib_handle->vftabl.db_get_age(kcontext, db_name, t);
880     get_errmsg(kcontext, status);
881     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
882
883   clean_n_exit:
884     return status;
885 }
886
887 krb5_error_code
888 krb5_db_set_option(krb5_context kcontext, int option, void *value)
889 {
890     krb5_error_code status = 0;
891     kdb5_dal_handle *dal_handle;
892
893     if (kcontext->dal_handle == NULL) {
894         status = kdb_setup_lib_handle(kcontext);
895         if (status) {
896             goto clean_n_exit;
897         }
898     }
899
900     dal_handle = kcontext->dal_handle;
901     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
902     if (status) {
903         goto clean_n_exit;
904     }
905
906     status =
907         dal_handle->lib_handle->vftabl.db_set_option(kcontext, option, value);
908     get_errmsg(kcontext, status);
909     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
910
911   clean_n_exit:
912     return status;
913 }
914
915 krb5_error_code
916 krb5_db_lock(krb5_context kcontext, int lock_mode)
917 {
918     krb5_error_code status = 0;
919     kdb5_dal_handle *dal_handle;
920
921     if (kcontext->dal_handle == NULL) {
922         status = kdb_setup_lib_handle(kcontext);
923         if (status) {
924             goto clean_n_exit;
925         }
926     }
927
928     dal_handle = kcontext->dal_handle;
929     /* acquire an exclusive lock, ensures no other thread uses this context */
930     status = kdb_lock_lib_lock(dal_handle->lib_handle, TRUE);
931     if (status) {
932         goto clean_n_exit;
933     }
934
935     status = dal_handle->lib_handle->vftabl.db_lock(kcontext, lock_mode);
936     get_errmsg(kcontext, status);
937
938     /* exclusive lock is still held, so no other thread could use this context */
939     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
940
941   clean_n_exit:
942     return status;
943 }
944
945 krb5_error_code
946 krb5_db_unlock(krb5_context kcontext)
947 {
948     krb5_error_code status = 0;
949     kdb5_dal_handle *dal_handle;
950
951     if (kcontext->dal_handle == NULL) {
952         status = kdb_setup_lib_handle(kcontext);
953         if (status) {
954             goto clean_n_exit;
955         }
956     }
957
958     dal_handle = kcontext->dal_handle;
959     /* normal lock acquired and exclusive lock released */
960     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
961     if (status) {
962         goto clean_n_exit;
963     }
964
965     status = dal_handle->lib_handle->vftabl.db_unlock(kcontext);
966     get_errmsg(kcontext, status);
967
968     kdb_unlock_lib_lock(dal_handle->lib_handle, TRUE);
969
970   clean_n_exit:
971     return status;
972 }
973
974 krb5_error_code
975 krb5_db_get_principal(krb5_context kcontext,
976                       krb5_const_principal search_for,
977                       krb5_db_entry * entries,
978                       int *nentries, krb5_boolean * more)
979 {
980     krb5_error_code status = 0;
981     kdb5_dal_handle *dal_handle;
982
983     if (kcontext->dal_handle == NULL) {
984         status = kdb_setup_lib_handle(kcontext);
985         if (status) {
986             goto clean_n_exit;
987         }
988     }
989
990     dal_handle = kcontext->dal_handle;
991     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
992     if (status) {
993         goto clean_n_exit;
994     }
995
996     status =
997         dal_handle->lib_handle->vftabl.db_get_principal(kcontext, search_for, 0,
998                                                         entries, nentries,
999                                                         more);
1000     get_errmsg(kcontext, status);
1001     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1002
1003   clean_n_exit:
1004     return status;
1005 }
1006
1007 krb5_error_code
1008 krb5_db_get_principal_ext(krb5_context kcontext,
1009                           krb5_const_principal search_for,
1010                           unsigned int flags,
1011                           krb5_db_entry * entries,
1012                           int *nentries, krb5_boolean * more)
1013 {
1014     krb5_error_code status = 0;
1015     kdb5_dal_handle *dal_handle;
1016
1017     if (kcontext->dal_handle == NULL) {
1018         status = kdb_setup_lib_handle(kcontext);
1019         if (status) {
1020             goto clean_n_exit;
1021         }
1022     }
1023
1024     dal_handle = kcontext->dal_handle;
1025     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1026     if (status) {
1027         goto clean_n_exit;
1028     }
1029
1030     status =
1031         dal_handle->lib_handle->vftabl.db_get_principal(kcontext, search_for,
1032                                                         flags,
1033                                                         entries, nentries,
1034                                                         more);
1035     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1036
1037   clean_n_exit:
1038     return status;
1039 }
1040
1041 krb5_error_code
1042 krb5_db_free_principal(krb5_context kcontext, krb5_db_entry * entry, int count)
1043 {
1044     krb5_error_code status = 0;
1045     kdb5_dal_handle *dal_handle;
1046
1047     if (kcontext->dal_handle == NULL) {
1048         status = kdb_setup_lib_handle(kcontext);
1049         if (status) {
1050             goto clean_n_exit;
1051         }
1052     }
1053
1054     dal_handle = kcontext->dal_handle;
1055     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1056     if (status) {
1057         goto clean_n_exit;
1058     }
1059
1060     status =
1061         dal_handle->lib_handle->vftabl.db_free_principal(kcontext, entry,
1062                                                          count);
1063     get_errmsg(kcontext, status);
1064     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1065
1066   clean_n_exit:
1067     return status;
1068 }
1069
1070 static void
1071 free_db_args(krb5_context kcontext, char **db_args)
1072 {
1073     int i;
1074     if (db_args) {
1075         /* XXX Is this right?  Or are we borrowing storage from
1076            the caller?  */
1077         for (i = 0; db_args[i]; i++)
1078             krb5_db_free(kcontext, db_args[i]);
1079         free(db_args);
1080     }
1081 }
1082
1083 static krb5_error_code
1084 extract_db_args_from_tl_data(krb5_context kcontext,
1085                              krb5_tl_data **start, krb5_int16 *count,
1086                              char ***db_argsp)
1087 {
1088     char **db_args = NULL;
1089     int db_args_size = 0;
1090     krb5_tl_data *prev, *curr, *next;
1091     krb5_error_code status;
1092
1093     /* Giving db_args as part of tl data causes db2 to store the
1094        tl_data as such.  To prevent this, tl_data is collated and
1095        passed as a separate argument.  Currently supports only one
1096        principal, but passing it as a separate argument makes it
1097        difficult for kadmin remote to pass arguments to server.  */
1098     prev = NULL, curr = *start;
1099     while (curr) {
1100         if (curr->tl_data_type == KRB5_TL_DB_ARGS) {
1101             char  **t;
1102             /* Since this is expected to be NULL terminated string and
1103                this could come from any client, do a check before
1104                passing it to db.  */
1105             if (((char *) curr->tl_data_contents)[curr->tl_data_length - 1] !=
1106                 '\0') {
1107                 /* Not null terminated. Dangerous input.  */
1108                 status = EINVAL;
1109                 goto clean_n_exit;
1110             }
1111
1112             db_args_size++;
1113             t = realloc(db_args, sizeof(char *) * (db_args_size + 1));  /* 1 for NULL */
1114             if (t == NULL) {
1115                 status = ENOMEM;
1116                 goto clean_n_exit;
1117             }
1118
1119             db_args = t;
1120             db_args[db_args_size - 1] = (char *) curr->tl_data_contents;
1121             db_args[db_args_size] = NULL;
1122
1123             next = curr->tl_data_next;
1124             if (prev == NULL) {
1125                 /* current node is the first in the linked list. remove it */
1126                 *start = curr->tl_data_next;
1127             } else {
1128                 prev->tl_data_next = curr->tl_data_next;
1129             }
1130             (*count)--;
1131             krb5_db_free(kcontext, curr);
1132
1133             /* previous does not change */
1134             curr = next;
1135         } else {
1136             prev = curr;
1137             curr = curr->tl_data_next;
1138         }
1139     }
1140     status = 0;
1141 clean_n_exit:
1142     if (status != 0) {
1143         free_db_args(kcontext, db_args);
1144         db_args = NULL;
1145     }
1146     *db_argsp = db_args;
1147     return status;
1148 }
1149
1150 krb5_error_code
1151 krb5int_put_principal_no_log(krb5_context kcontext,
1152                              krb5_db_entry *entries, int *nentries)
1153 {
1154     kdb5_dal_handle *dal_handle;
1155     krb5_error_code status;
1156     char **db_args;
1157
1158     status = extract_db_args_from_tl_data(kcontext, &entries->tl_data,
1159                                           &entries->n_tl_data,
1160                                           &db_args);
1161     if (status)
1162         return status;
1163     assert (kcontext->dal_handle != NULL); /* XXX */
1164     dal_handle = kcontext->dal_handle;
1165     /* XXX Locking?  */
1166     status = dal_handle->lib_handle->vftabl.db_put_principal(kcontext, entries,
1167                                                              nentries,
1168                                                              db_args);
1169     get_errmsg(kcontext, status);
1170     free_db_args(kcontext, db_args);
1171     return status;
1172 }
1173
1174 krb5_error_code
1175 krb5_db_put_principal(krb5_context kcontext,
1176                       krb5_db_entry * entries, int *nentries)
1177 {
1178     krb5_error_code status = 0;
1179     kdb5_dal_handle *dal_handle;
1180     char  **db_args = NULL;
1181     kdb_incr_update_t *upd, *fupd = 0;
1182     char *princ_name = NULL;
1183     kdb_log_context *log_ctx;
1184     int i;
1185     int ulog_locked = 0;
1186
1187     log_ctx = kcontext->kdblog_context;
1188
1189     if (kcontext->dal_handle == NULL) {
1190         status = kdb_setup_lib_handle(kcontext);
1191         if (status) {
1192             goto clean_n_exit;
1193         }
1194     }
1195
1196     status = extract_db_args_from_tl_data(kcontext, &entries->tl_data,
1197                                           &entries->n_tl_data,
1198                                           &db_args);
1199     if (status)
1200         goto clean_n_exit;
1201
1202     dal_handle = kcontext->dal_handle;
1203     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1204     if (status) {
1205         goto clean_n_exit;
1206     }
1207
1208     /*
1209      * We need the lock since ulog_conv_2logentry() does a get
1210      */
1211     if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1212         if (!(upd = (kdb_incr_update_t *)
1213           malloc(sizeof (kdb_incr_update_t)* *nentries))) {
1214             status = errno;
1215             goto err_lock;
1216         }
1217         fupd = upd;
1218
1219         (void) memset(upd, 0, sizeof(kdb_incr_update_t)* *nentries);
1220
1221         if ((status = ulog_conv_2logentry(kcontext, entries, upd, *nentries))) {
1222             goto err_lock;
1223         }
1224     }
1225
1226     status = ulog_lock(kcontext, KRB5_LOCKMODE_EXCLUSIVE);
1227     if (status != 0)
1228         goto err_lock;
1229     ulog_locked = 1;
1230
1231     for (i = 0; i < *nentries; i++) {
1232         /*
1233          * We'll be sharing the same locks as db for logging
1234          */
1235         if (fupd) {
1236                 if ((status = krb5_unparse_name(kcontext, entries->princ,
1237                     &princ_name)))
1238                         goto err_lock;
1239
1240                 upd->kdb_princ_name.utf8str_t_val = princ_name;
1241                 upd->kdb_princ_name.utf8str_t_len = strlen(princ_name);
1242
1243                 if ((status = ulog_add_update(kcontext, upd)) != 0)
1244                         goto err_lock;
1245                 upd++;
1246         }
1247     }
1248
1249     status = dal_handle->lib_handle->vftabl.db_put_principal(kcontext, entries,
1250                                                              nentries,
1251                                                              db_args);
1252     get_errmsg(kcontext, status);
1253     if (status == 0 && fupd) {
1254         upd = fupd;
1255         for (i = 0; i < *nentries; i++) {
1256             (void) ulog_finish_update(kcontext, upd);
1257             upd++;
1258         }
1259     }
1260 err_lock:
1261     if (ulog_locked)
1262         ulog_lock(kcontext, KRB5_LOCKMODE_UNLOCK);
1263
1264     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1265
1266   clean_n_exit:
1267     free_db_args(kcontext, db_args);
1268
1269     if (log_ctx && (log_ctx->iproprole == IPROP_MASTER))
1270         ulog_free_entries(fupd, *nentries);
1271
1272     return status;
1273 }
1274
1275 krb5_error_code
1276 krb5int_delete_principal_no_log(krb5_context kcontext,
1277                                 krb5_principal search_for,
1278                                 int *nentries)
1279 {
1280     kdb5_dal_handle *dal_handle;
1281     krb5_error_code status;
1282
1283     assert (kcontext->dal_handle != NULL); /* XXX */
1284
1285     dal_handle = kcontext->dal_handle;
1286     /* XXX Locking?  */
1287     status = dal_handle->lib_handle->vftabl.db_delete_principal(kcontext,
1288                                                                  search_for,
1289                                                                  nentries);
1290     get_errmsg(kcontext, status);
1291     return status;
1292 }
1293
1294 krb5_error_code
1295 krb5_db_delete_principal(krb5_context kcontext,
1296                          krb5_principal search_for, int *nentries)
1297 {
1298     krb5_error_code status = 0;
1299     kdb5_dal_handle *dal_handle;
1300     kdb_incr_update_t upd;
1301     char *princ_name = NULL;
1302     kdb_log_context *log_ctx;
1303
1304     log_ctx = kcontext->kdblog_context;
1305
1306     if (kcontext->dal_handle == NULL) {
1307         status = kdb_setup_lib_handle(kcontext);
1308         if (status) {
1309             goto clean_n_exit;
1310         }
1311     }
1312
1313     dal_handle = kcontext->dal_handle;
1314     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1315     if (status) {
1316         goto clean_n_exit;
1317     }
1318
1319     status = ulog_lock(kcontext, KRB5_LOCKMODE_EXCLUSIVE);
1320     if (status) {
1321         kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1322         return status;
1323     }
1324
1325     /*
1326      * We'll be sharing the same locks as db for logging
1327      */
1328     if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1329         if ((status = krb5_unparse_name(kcontext, search_for, &princ_name))) {
1330             ulog_lock(kcontext, KRB5_LOCKMODE_UNLOCK);
1331             (void) kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1332             return status;
1333         }
1334
1335         (void) memset(&upd, 0, sizeof (kdb_incr_update_t));
1336
1337         upd.kdb_princ_name.utf8str_t_val = princ_name;
1338         upd.kdb_princ_name.utf8str_t_len = strlen(princ_name);
1339
1340         if ((status = ulog_delete_update(kcontext, &upd)) != 0) {
1341                 ulog_lock(kcontext, KRB5_LOCKMODE_UNLOCK);
1342                 free(princ_name);
1343                 (void) kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1344                 return status;
1345         }
1346
1347         free(princ_name);
1348     }
1349
1350     status = dal_handle->lib_handle->vftabl.db_delete_principal(kcontext,
1351                                                                  search_for,
1352                                                                  nentries);
1353     get_errmsg(kcontext, status);
1354
1355     /*
1356      * We need to commit our update upon success
1357      */
1358     if (!status)
1359         if (log_ctx && (log_ctx->iproprole == IPROP_MASTER))
1360                 (void) ulog_finish_update(kcontext, &upd);
1361
1362     ulog_lock(kcontext, KRB5_LOCKMODE_UNLOCK);
1363     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1364
1365   clean_n_exit:
1366     return status;
1367 }
1368
1369 krb5_error_code
1370 krb5_db_iterate(krb5_context kcontext,
1371                 char *match_entry,
1372                 int (*func) (krb5_pointer, krb5_db_entry *),
1373                 krb5_pointer func_arg)
1374 {
1375     krb5_error_code status = 0;
1376     kdb5_dal_handle *dal_handle;
1377
1378     if (kcontext->dal_handle == NULL) {
1379         status = kdb_setup_lib_handle(kcontext);
1380         if (status) {
1381             goto clean_n_exit;
1382         }
1383     }
1384
1385     dal_handle = kcontext->dal_handle;
1386     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1387     if (status) {
1388         goto clean_n_exit;
1389     }
1390
1391     status = dal_handle->lib_handle->vftabl.db_iterate(kcontext,
1392                                                        match_entry,
1393                                                        func, func_arg);
1394     get_errmsg(kcontext, status);
1395     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1396
1397   clean_n_exit:
1398     return status;
1399 }
1400
1401 krb5_error_code
1402 krb5_supported_realms(krb5_context kcontext, char **realms)
1403 {
1404     krb5_error_code status = 0;
1405     kdb5_dal_handle *dal_handle;
1406
1407     if (kcontext->dal_handle == NULL) {
1408         status = kdb_setup_lib_handle(kcontext);
1409         if (status) {
1410             goto clean_n_exit;
1411         }
1412     }
1413
1414     dal_handle = kcontext->dal_handle;
1415     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1416     if (status) {
1417         goto clean_n_exit;
1418     }
1419
1420     status =
1421         dal_handle->lib_handle->vftabl.db_supported_realms(kcontext, realms);
1422     get_errmsg(kcontext, status);
1423     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1424
1425   clean_n_exit:
1426     return status;
1427 }
1428
1429 krb5_error_code
1430 krb5_free_supported_realms(krb5_context kcontext, char **realms)
1431 {
1432     krb5_error_code status = 0;
1433     kdb5_dal_handle *dal_handle;
1434
1435     if (kcontext->dal_handle == NULL) {
1436         status = kdb_setup_lib_handle(kcontext);
1437         if (status) {
1438             goto clean_n_exit;
1439         }
1440     }
1441
1442     dal_handle = kcontext->dal_handle;
1443     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1444     if (status) {
1445         goto clean_n_exit;
1446     }
1447
1448     status =
1449         dal_handle->lib_handle->vftabl.db_free_supported_realms(kcontext,
1450                                                                 realms);
1451     get_errmsg(kcontext, status);
1452     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1453
1454   clean_n_exit:
1455     return status;
1456 }
1457
1458 krb5_error_code
1459 krb5_db_set_master_key_ext(krb5_context kcontext,
1460                            char *pwd, krb5_keyblock * key)
1461 {
1462     krb5_error_code status = 0;
1463     kdb5_dal_handle *dal_handle;
1464
1465     if (kcontext->dal_handle == NULL) {
1466         status = kdb_setup_lib_handle(kcontext);
1467         if (status) {
1468             goto clean_n_exit;
1469         }
1470     }
1471
1472     dal_handle = kcontext->dal_handle;
1473     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1474     if (status) {
1475         goto clean_n_exit;
1476     }
1477
1478     status = dal_handle->lib_handle->vftabl.set_master_key(kcontext, pwd, key);
1479     get_errmsg(kcontext, status);
1480
1481     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1482
1483   clean_n_exit:
1484     return status;
1485 }
1486
1487 krb5_error_code
1488 krb5_db_set_mkey(krb5_context context, krb5_keyblock * key)
1489 {
1490     return krb5_db_set_master_key_ext(context, NULL, key);
1491 }
1492
1493 krb5_error_code
1494 krb5_db_set_mkey_list(krb5_context kcontext,
1495                       krb5_keylist_node * keylist)
1496 {
1497     krb5_error_code status = 0;
1498     kdb5_dal_handle *dal_handle;
1499
1500     if (kcontext->dal_handle == NULL) {
1501         status = kdb_setup_lib_handle(kcontext);
1502         if (status) {
1503             goto clean_n_exit;
1504         }
1505     }
1506
1507     dal_handle = kcontext->dal_handle;
1508     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1509     if (status) {
1510         goto clean_n_exit;
1511     }
1512
1513     status = dal_handle->lib_handle->vftabl.set_master_key_list(kcontext, keylist);
1514     get_errmsg(kcontext, status);
1515
1516     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1517
1518 clean_n_exit:
1519     return status;
1520 }
1521
1522 krb5_error_code
1523 krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key)
1524 {
1525     krb5_error_code status = 0;
1526     kdb5_dal_handle *dal_handle;
1527
1528     if (kcontext->dal_handle == NULL) {
1529         status = kdb_setup_lib_handle(kcontext);
1530         if (status) {
1531             goto clean_n_exit;
1532         }
1533     }
1534
1535     dal_handle = kcontext->dal_handle;
1536     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1537     if (status) {
1538         goto clean_n_exit;
1539     }
1540
1541     /* Let's use temp key and copy it later to avoid memory problems
1542        when freed by the caller.  */
1543     status = dal_handle->lib_handle->vftabl.get_master_key(kcontext, key);
1544     get_errmsg(kcontext, status);
1545     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1546
1547   clean_n_exit:
1548     return status;
1549 }
1550
1551 krb5_error_code
1552 krb5_db_get_mkey_list(krb5_context kcontext, krb5_keylist_node ** keylist)
1553 {
1554     krb5_error_code status = 0;
1555     kdb5_dal_handle *dal_handle;
1556
1557     if (kcontext->dal_handle == NULL) {
1558         status = kdb_setup_lib_handle(kcontext);
1559         if (status) {
1560             goto clean_n_exit;
1561         }
1562     }
1563
1564     dal_handle = kcontext->dal_handle;
1565     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1566     if (status) {
1567         goto clean_n_exit;
1568     }
1569
1570     /* Let's use temp key and copy it later to avoid memory problems
1571        when freed by the caller.  */
1572     status = dal_handle->lib_handle->vftabl.get_master_key_list(kcontext, keylist);
1573     get_errmsg(kcontext, status);
1574     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1575
1576 clean_n_exit:
1577     return status;
1578 }
1579
1580 krb5_error_code
1581 krb5_db_fetch_mkey_list(krb5_context     context,
1582                    krb5_principal        mname,
1583                    const krb5_keyblock * mkey,
1584                    krb5_kvno             mkvno,
1585                    krb5_keylist_node  **mkey_list)
1586 {
1587     kdb5_dal_handle *dal_handle;
1588     krb5_error_code status = 0;
1589
1590     if (context->dal_handle == NULL) {
1591         status = kdb_setup_lib_handle(context);
1592         if (status) {
1593             goto clean_n_exit;
1594         }
1595     }
1596
1597     dal_handle = context->dal_handle;
1598     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1599     if (status) {
1600         goto clean_n_exit;
1601     }
1602
1603     status = dal_handle->lib_handle->vftabl.fetch_master_key_list(context,
1604         mname,
1605         mkey,
1606         mkvno,
1607         mkey_list);
1608     get_errmsg(context, status);
1609     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1610
1611     if (status) {
1612         goto clean_n_exit;
1613     }
1614
1615 clean_n_exit:
1616     return status;
1617 }
1618
1619 krb5_error_code
1620 krb5_db_free_mkey_list(krb5_context    context,
1621                        krb5_keylist_node  *mkey_list)
1622 {
1623     krb5_keylist_node *cur, *prev;
1624
1625     for (cur = mkey_list; cur != NULL;) {
1626         prev = cur;
1627         cur = cur->next;
1628         krb5_free_keyblock_contents(context, &prev->keyblock);
1629         krb5_xfree(prev);
1630     }
1631
1632     return 0;
1633 }
1634
1635 krb5_error_code
1636 krb5_db_store_master_key(krb5_context kcontext,
1637                          char *keyfile,
1638                          krb5_principal mname,
1639                          krb5_kvno kvno,
1640                          krb5_keyblock * key, char *master_pwd)
1641 {
1642     krb5_error_code status = 0;
1643     kdb5_dal_handle *dal_handle;
1644
1645     if (kcontext->dal_handle == NULL) {
1646         status = kdb_setup_lib_handle(kcontext);
1647         if (status) {
1648             goto clean_n_exit;
1649         }
1650     }
1651
1652     dal_handle = kcontext->dal_handle;
1653     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1654     if (status) {
1655         goto clean_n_exit;
1656     }
1657
1658     status = dal_handle->lib_handle->vftabl.store_master_key(kcontext,
1659                                                              keyfile,
1660                                                              mname,
1661                                                              kvno,
1662                                                              key, master_pwd);
1663     get_errmsg(kcontext, status);
1664     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1665
1666   clean_n_exit:
1667     return status;
1668 }
1669
1670 krb5_error_code
1671 krb5_db_store_master_key_list(krb5_context kcontext,
1672                               char *keyfile,
1673                               krb5_principal mname,
1674                               krb5_keylist_node *keylist,
1675                               char *master_pwd)
1676 {
1677     krb5_error_code status = 0;
1678     kdb5_dal_handle *dal_handle;
1679
1680     if (kcontext->dal_handle == NULL) {
1681         status = kdb_setup_lib_handle(kcontext);
1682         if (status) {
1683             goto clean_n_exit;
1684         }
1685     }
1686
1687     dal_handle = kcontext->dal_handle;
1688     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1689     if (status) {
1690         goto clean_n_exit;
1691     }
1692
1693     status = dal_handle->lib_handle->vftabl.store_master_key_list(kcontext,
1694                                                                   keyfile,
1695                                                                   mname,
1696                                                                   keylist,
1697                                                                   master_pwd);
1698     get_errmsg(kcontext, status);
1699     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1700
1701   clean_n_exit:
1702     return status;
1703 }
1704
1705 char   *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1;
1706 char   *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2;
1707
1708 krb5_error_code
1709 krb5_db_fetch_mkey(krb5_context    context,
1710                    krb5_principal  mname,
1711                    krb5_enctype    etype,
1712                    krb5_boolean    fromkeyboard,
1713                    krb5_boolean    twice,
1714                    char          * db_args,
1715                    krb5_kvno     * kvno,
1716                    krb5_data     * salt,
1717                    krb5_keyblock * key)
1718 {
1719     krb5_error_code retval;
1720     char    password[BUFSIZ];
1721     krb5_data pwd;
1722     unsigned int size = sizeof(password);
1723     krb5_keyblock tmp_key;
1724
1725     memset(&tmp_key, 0, sizeof(tmp_key));
1726
1727     if (fromkeyboard) {
1728         krb5_data scratch;
1729
1730         if ((retval = krb5_read_password(context, krb5_mkey_pwd_prompt1,
1731                                          twice ? krb5_mkey_pwd_prompt2 : 0,
1732                                          password, &size))) {
1733             goto clean_n_exit;
1734         }
1735
1736         pwd.data = password;
1737         pwd.length = size;
1738         if (!salt) {
1739             retval = krb5_principal2salt(context, mname, &scratch);
1740             if (retval)
1741                 goto clean_n_exit;
1742         }
1743         retval =
1744             krb5_c_string_to_key(context, etype, &pwd, salt ? salt : &scratch,
1745                                  key);
1746         /*
1747          * If a kvno pointer was passed in and it dereferences the IGNORE_VNO
1748          * value then it should be assigned the value of the kvno associated
1749          * with the current mkey princ key if that princ entry is available
1750          * otherwise assign 1 which is the default kvno value for the mkey
1751          * princ.
1752          */
1753         if (kvno != NULL && *kvno == IGNORE_VNO) {
1754             int nentries = 1;
1755             krb5_boolean more;
1756             krb5_error_code rc;
1757             krb5_db_entry master_entry;
1758
1759             rc = krb5_db_get_principal(context, mname,
1760                 &master_entry, &nentries, &more);
1761
1762             if (rc == 0 && nentries == 1 && more == FALSE) 
1763                 *kvno = (krb5_kvno) master_entry.key_data->key_data_kvno;
1764             else
1765                 *kvno = 1;
1766
1767             if (rc == 0 && nentries)
1768                 krb5_db_free_principal(context, &master_entry, nentries);
1769         }
1770
1771         if (!salt)
1772             free(scratch.data);
1773         zap(password, sizeof(password));        /* erase it */
1774
1775     } else {
1776         kdb5_dal_handle *dal_handle;
1777
1778         if (context->dal_handle == NULL) {
1779             retval = kdb_setup_lib_handle(context);
1780             if (retval) {
1781                 goto clean_n_exit;
1782             }
1783         }
1784
1785         dal_handle = context->dal_handle;
1786         retval = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1787         if (retval) {
1788             goto clean_n_exit;
1789         }
1790
1791         /* get the enctype from the stash */
1792         tmp_key.enctype = ENCTYPE_UNKNOWN;
1793
1794         retval = dal_handle->lib_handle->vftabl.fetch_master_key(context,
1795                                                                  mname,
1796                                                                  &tmp_key,
1797                                                                  kvno,
1798                                                                  db_args);
1799         get_errmsg(context, retval);
1800         kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1801
1802         if (retval) {
1803             goto clean_n_exit;
1804         }
1805
1806         key->contents = malloc(tmp_key.length);
1807         if (key->contents == NULL) {
1808             retval = ENOMEM;
1809             goto clean_n_exit;
1810         }
1811
1812         key->magic = tmp_key.magic;
1813         key->enctype = tmp_key.enctype;
1814         key->length = tmp_key.length;
1815         memcpy(key->contents, tmp_key.contents, tmp_key.length);
1816     }
1817
1818   clean_n_exit:
1819     if (tmp_key.contents) {
1820         zap(tmp_key.contents, tmp_key.length);
1821         krb5_db_free(context, tmp_key.contents);
1822     }
1823     return retval;
1824 }
1825
1826 krb5_error_code
1827 krb5_db_verify_master_key(krb5_context     kcontext,
1828                           krb5_principal   mprinc,
1829                           krb5_kvno        kvno,
1830                           krb5_keyblock  * mkey)
1831 {
1832     krb5_error_code status = 0;
1833     kdb5_dal_handle *dal_handle;
1834
1835     if (kcontext->dal_handle == NULL) {
1836         status = kdb_setup_lib_handle(kcontext);
1837         if (status) {
1838             goto clean_n_exit;
1839         }
1840     }
1841
1842     dal_handle = kcontext->dal_handle;
1843     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1844     if (status) {
1845         goto clean_n_exit;
1846     }
1847
1848     status = dal_handle->lib_handle->vftabl.verify_master_key(kcontext,
1849                                                               mprinc,
1850                                                               kvno,
1851                                                               mkey);
1852     get_errmsg(kcontext, status);
1853     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1854
1855   clean_n_exit:
1856     return status;
1857 }
1858
1859 krb5_error_code
1860 krb5_dbe_fetch_act_key_list(krb5_context         context,
1861                             krb5_principal       princ,
1862                             krb5_actkvno_node  **act_key_list)
1863 {
1864     krb5_error_code retval = 0;
1865     krb5_db_entry entry;
1866     int nprinc;
1867     krb5_boolean more;
1868
1869     if (act_key_list == NULL)
1870         return (EINVAL);
1871
1872     nprinc = 1;
1873     if ((retval = krb5_db_get_principal(context, princ, &entry,
1874                                         &nprinc, &more))) {
1875         return (retval);
1876     }
1877
1878     if (nprinc != 1) {
1879         if (nprinc) {
1880             krb5_db_free_principal(context, &entry, nprinc);
1881             return (KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
1882         } else {
1883             return(KRB5_KDB_NOMASTERKEY);
1884         }
1885     } else if (more) {
1886         krb5_db_free_principal(context, &entry, nprinc);
1887         return (KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
1888     }
1889
1890     retval = krb5_dbe_lookup_actkvno(context, &entry, act_key_list);
1891
1892     if (*act_key_list == NULL) {
1893         krb5_actkvno_node *tmp_actkvno;
1894         /*
1895          * for mkey princ entries without KRB5_TL_ACTKVNO data provide a default
1896          */
1897
1898         tmp_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
1899         if (tmp_actkvno == NULL)
1900             return (ENOMEM);
1901
1902         memset(tmp_actkvno, 0, sizeof(krb5_actkvno_node));
1903         tmp_actkvno->act_time = 0; /* earliest time possible */
1904         /* use most current key */
1905         tmp_actkvno->act_kvno = entry.key_data[0].key_data_kvno;
1906         *act_key_list = tmp_actkvno;
1907     }
1908
1909     krb5_db_free_principal(context, &entry, nprinc);
1910     return retval;
1911 }
1912
1913 /*
1914  * Locates the "active" mkey used when encrypting a princ's keys.  Note, the
1915  * caller must NOT free the output act_mkey.
1916  */
1917
1918 krb5_error_code
1919 krb5_dbe_find_act_mkey(krb5_context         context,
1920                        krb5_keylist_node  *mkey_list,
1921                        krb5_actkvno_node   *act_mkey_list,
1922                        krb5_kvno           *act_kvno,
1923                        krb5_keyblock      **act_mkey)
1924 {
1925     krb5_kvno tmp_act_kvno;
1926     krb5_error_code retval;
1927     krb5_keylist_node *cur_keyblock = mkey_list;
1928     krb5_actkvno_node   *prev_actkvno, *cur_actkvno;
1929     krb5_timestamp      now;
1930     krb5_boolean        found = FALSE;
1931
1932     if ((retval = krb5_timeofday(context, &now)))
1933         return (retval);
1934
1935     /*
1936      * The list should be sorted in time, early to later so if the first entry
1937      * is later than now, this is a problem.  The fallback in this case is to
1938      * return the earlist activation entry.
1939      */
1940     if (act_mkey_list->act_time > now) {
1941         while (cur_keyblock && cur_keyblock->kvno != act_mkey_list->act_kvno)
1942             cur_keyblock = cur_keyblock->next;
1943         if (cur_keyblock) {
1944             *act_mkey = &cur_keyblock->keyblock;
1945             if (act_kvno != NULL)
1946                 *act_kvno = cur_keyblock->kvno;
1947             return (0);
1948         } else {
1949             return (KRB5_KDB_NOACTMASTERKEY);
1950         }
1951     }
1952
1953     /* find the most current entry <= now */
1954     for (prev_actkvno = cur_actkvno = act_mkey_list; cur_actkvno != NULL;
1955          prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
1956
1957         if (cur_actkvno->act_time == now) {
1958             tmp_act_kvno = cur_actkvno->act_kvno;
1959             found = TRUE;
1960             break;
1961         } else if (cur_actkvno->act_time > now && prev_actkvno->act_time <= now) {
1962             tmp_act_kvno = prev_actkvno->act_kvno;
1963             found = TRUE;
1964             break;
1965         }
1966     }
1967
1968     if (!found) {
1969         /*
1970          * The end of the list was encountered and all entries are < now so use
1971          * the latest entry.
1972          */
1973         if (prev_actkvno->act_time <= now) {
1974             tmp_act_kvno = prev_actkvno->act_kvno;
1975         } else {
1976             /* XXX this shouldn't happen */
1977             return (KRB5_KDB_NOACTMASTERKEY);
1978         }
1979     }
1980
1981     while (cur_keyblock && cur_keyblock->kvno != tmp_act_kvno)
1982         cur_keyblock = cur_keyblock->next;
1983
1984     if (cur_keyblock) {
1985         *act_mkey = &cur_keyblock->keyblock;
1986         if (act_kvno != NULL)
1987             *act_kvno = tmp_act_kvno;
1988         return (0);
1989     } else {
1990         return (KRB5_KDB_NO_MATCHING_KEY);
1991     }
1992 }
1993
1994 /*
1995  * Locates the mkey used to protect a princ's keys.  Note, the caller must not
1996  * free the output key.
1997  */
1998 krb5_error_code
1999 krb5_dbe_find_mkey(krb5_context         context,
2000                    krb5_keylist_node  * mkey_list,
2001                    krb5_db_entry      * entry,
2002                    krb5_keyblock     ** mkey)
2003 {
2004     krb5_kvno mkvno;
2005     krb5_error_code retval;
2006     krb5_keylist_node *cur_keyblock = mkey_list;
2007
2008     retval = krb5_dbe_lookup_mkvno(context, entry, &mkvno);
2009     if (retval)
2010         return (retval);
2011
2012     while (cur_keyblock && cur_keyblock->kvno != mkvno)
2013         cur_keyblock = cur_keyblock->next;
2014
2015     if (cur_keyblock) {
2016         *mkey = &cur_keyblock->keyblock;
2017         return (0);
2018     } else {
2019         return (KRB5_KDB_NO_MATCHING_KEY);
2020     }
2021 }
2022
2023 void   *
2024 krb5_db_alloc(krb5_context kcontext, void *ptr, size_t size)
2025 {
2026     krb5_error_code status;
2027     kdb5_dal_handle *dal_handle;
2028     void   *new_ptr = NULL;
2029
2030     if (kcontext->dal_handle == NULL) {
2031         status = kdb_setup_lib_handle(kcontext);
2032         if (status) {
2033             goto clean_n_exit;
2034         }
2035     }
2036
2037     dal_handle = kcontext->dal_handle;
2038
2039     new_ptr = dal_handle->lib_handle->vftabl.db_alloc(kcontext, ptr, size);
2040
2041   clean_n_exit:
2042     return new_ptr;
2043 }
2044
2045 void
2046 krb5_db_free(krb5_context kcontext, void *ptr)
2047 {
2048     krb5_error_code status;
2049     kdb5_dal_handle *dal_handle;
2050
2051     if (kcontext->dal_handle == NULL) {
2052         status = kdb_setup_lib_handle(kcontext);
2053         if (status) {
2054             goto clean_n_exit;
2055         }
2056     }
2057
2058     dal_handle = kcontext->dal_handle;
2059
2060     dal_handle->lib_handle->vftabl.db_free(kcontext, ptr);
2061
2062   clean_n_exit:
2063     return;
2064 }
2065
2066 /* has to be modified */
2067
2068 krb5_error_code
2069 krb5_dbe_find_enctype(krb5_context kcontext,
2070                       krb5_db_entry * dbentp,
2071                       krb5_int32 ktype,
2072                       krb5_int32 stype,
2073                       krb5_int32 kvno, krb5_key_data ** kdatap)
2074 {
2075     krb5_int32 start = 0;
2076     return krb5_dbe_search_enctype(kcontext, dbentp, &start, ktype, stype,
2077                                    kvno, kdatap);
2078 }
2079
2080 krb5_error_code
2081 krb5_dbe_search_enctype(krb5_context kcontext,
2082                         krb5_db_entry * dbentp,
2083                         krb5_int32 * start,
2084                         krb5_int32 ktype,
2085                         krb5_int32 stype,
2086                         krb5_int32 kvno, krb5_key_data ** kdatap)
2087 {
2088     krb5_error_code status = 0;
2089     kdb5_dal_handle *dal_handle;
2090
2091     if (kcontext->dal_handle == NULL) {
2092         status = kdb_setup_lib_handle(kcontext);
2093         if (status) {
2094             goto clean_n_exit;
2095         }
2096     }
2097
2098     dal_handle = kcontext->dal_handle;
2099     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2100     if (status) {
2101         goto clean_n_exit;
2102     }
2103
2104     status = dal_handle->lib_handle->vftabl.dbe_search_enctype(kcontext,
2105                                                                dbentp,
2106                                                                start,
2107                                                                ktype,
2108                                                                stype,
2109                                                                kvno, kdatap);
2110     get_errmsg(kcontext, status);
2111     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2112
2113   clean_n_exit:
2114     return status;
2115 }
2116
2117 #define REALM_SEP_STRING        "@"
2118
2119 krb5_error_code
2120 krb5_db_setup_mkey_name(krb5_context context,
2121                         const char *keyname,
2122                         const char *realm,
2123                         char **fullname, krb5_principal * principal)
2124 {
2125     krb5_error_code retval;
2126     char   *fname;
2127
2128     if (!keyname)
2129         keyname = KRB5_KDB_M_NAME;      /* XXX external? */
2130
2131     if (asprintf(&fname, "%s%s%s", keyname, REALM_SEP_STRING, realm) < 0)
2132         return ENOMEM;
2133
2134     if ((retval = krb5_parse_name(context, fname, principal)))
2135         return retval;
2136     if (fullname)
2137         *fullname = fname;
2138     else
2139         free(fname);
2140     return 0;
2141 }
2142
2143 krb5_error_code
2144 krb5_dbe_lookup_last_pwd_change(context, entry, stamp)
2145     krb5_context context;
2146     krb5_db_entry *entry;
2147     krb5_timestamp *stamp;
2148 {
2149     krb5_tl_data tl_data;
2150     krb5_error_code code;
2151     krb5_int32 tmp;
2152
2153     tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
2154
2155     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
2156         return (code);
2157
2158     if (tl_data.tl_data_length != 4) {
2159         *stamp = 0;
2160         return (0);
2161     }
2162
2163     krb5_kdb_decode_int32(tl_data.tl_data_contents, tmp);
2164
2165     *stamp = (krb5_timestamp) tmp;
2166
2167     return (0);
2168 }
2169
2170 krb5_error_code
2171 krb5_dbe_lookup_tl_data(context, entry, ret_tl_data)
2172     krb5_context context;
2173     krb5_db_entry *entry;
2174     krb5_tl_data *ret_tl_data;
2175 {
2176     krb5_tl_data *tl_data;
2177
2178     for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) {
2179         if (tl_data->tl_data_type == ret_tl_data->tl_data_type) {
2180             *ret_tl_data = *tl_data;
2181             return (0);
2182         }
2183     }
2184
2185     /*
2186      * If the requested record isn't found, return zero bytes.  If it
2187      * ever means something to have a zero-length tl_data, this code
2188      * and its callers will have to be changed.
2189      */
2190
2191     ret_tl_data->tl_data_length = 0;
2192     ret_tl_data->tl_data_contents = NULL;
2193     return (0);
2194 }
2195
2196 krb5_error_code
2197 krb5_dbe_create_key_data(context, entry)
2198     krb5_context context;
2199     krb5_db_entry *entry;
2200 {
2201     if ((entry->key_data =
2202          (krb5_key_data *) krb5_db_alloc(context, entry->key_data,
2203                                          (sizeof(krb5_key_data) *
2204                                           (entry->n_key_data + 1)))) == NULL)
2205         return (ENOMEM);
2206
2207     memset(entry->key_data + entry->n_key_data, 0, sizeof(krb5_key_data));
2208     entry->n_key_data++;
2209
2210     return 0;
2211 }
2212
2213 krb5_error_code
2214 krb5_dbe_update_mod_princ_data(context, entry, mod_date, mod_princ)
2215     krb5_context context;
2216     krb5_db_entry *entry;
2217     krb5_timestamp mod_date;
2218     krb5_const_principal mod_princ;
2219 {
2220     krb5_tl_data tl_data;
2221
2222     krb5_error_code retval = 0;
2223     krb5_octet *nextloc = 0;
2224     char   *unparse_mod_princ = 0;
2225     unsigned int unparse_mod_princ_size;
2226
2227     if ((retval = krb5_unparse_name(context, mod_princ, &unparse_mod_princ)))
2228         return (retval);
2229
2230     unparse_mod_princ_size = strlen(unparse_mod_princ) + 1;
2231
2232     if ((nextloc = (krb5_octet *) malloc(unparse_mod_princ_size + 4))
2233         == NULL) {
2234         free(unparse_mod_princ);
2235         return (ENOMEM);
2236     }
2237
2238     tl_data.tl_data_type = KRB5_TL_MOD_PRINC;
2239     tl_data.tl_data_length = unparse_mod_princ_size + 4;
2240     tl_data.tl_data_contents = nextloc;
2241
2242     /* Mod Date */
2243     krb5_kdb_encode_int32(mod_date, nextloc);
2244
2245     /* Mod Princ */
2246     memcpy(nextloc + 4, unparse_mod_princ, unparse_mod_princ_size);
2247
2248     retval = krb5_dbe_update_tl_data(context, entry, &tl_data);
2249
2250     free(unparse_mod_princ);
2251     free(nextloc);
2252
2253     return (retval);
2254 }
2255
2256 krb5_error_code
2257 krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ)
2258     krb5_context context;
2259     krb5_db_entry *entry;
2260     krb5_timestamp *mod_time;
2261     krb5_principal *mod_princ;
2262 {
2263     krb5_tl_data tl_data;
2264     krb5_error_code code;
2265
2266     *mod_princ = NULL;
2267     *mod_time = 0;
2268
2269     tl_data.tl_data_type = KRB5_TL_MOD_PRINC;
2270
2271     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
2272         return (code);
2273
2274     if ((tl_data.tl_data_length < 5) ||
2275         (tl_data.tl_data_contents[tl_data.tl_data_length - 1] != '\0'))
2276         return (KRB5_KDB_TRUNCATED_RECORD);
2277
2278     /* Mod Date */
2279     krb5_kdb_decode_int32(tl_data.tl_data_contents, *mod_time);
2280
2281     /* Mod Princ */
2282     if ((code = krb5_parse_name(context,
2283                                 (const char *) (tl_data.tl_data_contents + 4),
2284                                 mod_princ)))
2285         return (code);
2286
2287     return (0);
2288 }
2289
2290 krb5_error_code
2291 krb5_dbe_lookup_mkvno(krb5_context      context,
2292                       krb5_db_entry     *entry,
2293                       krb5_kvno         *mkvno)
2294 {
2295     krb5_tl_data tl_data;
2296     krb5_error_code code;
2297     krb5_int16 tmp;
2298
2299     tl_data.tl_data_type = KRB5_TL_MKVNO;
2300
2301     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
2302         return (code);
2303
2304     if (tl_data.tl_data_length == 0) {
2305         *mkvno = 1; /* default for princs that lack the KRB5_TL_MKVNO data */
2306         return (0);
2307     } else if (tl_data.tl_data_length != 2) {
2308         return (KRB5_KDB_TRUNCATED_RECORD);
2309     }
2310
2311     krb5_kdb_decode_int16(tl_data.tl_data_contents, tmp);
2312     *mkvno = (krb5_kvno) tmp;
2313     return (0);
2314 }
2315
2316 krb5_error_code
2317 krb5_dbe_update_mkvno(krb5_context    context,
2318                       krb5_db_entry * entry,
2319                       krb5_kvno       mkvno)
2320 {
2321     krb5_tl_data tl_data;
2322     krb5_octet buf[2]; /* this is the encoded size of an int16 */
2323     krb5_int16 tmp_kvno = (krb5_int16) mkvno;
2324
2325     tl_data.tl_data_type = KRB5_TL_MKVNO;
2326     tl_data.tl_data_length = sizeof(buf);
2327     krb5_kdb_encode_int16(tmp_kvno, buf);
2328     tl_data.tl_data_contents = buf;
2329
2330     return (krb5_dbe_update_tl_data(context, entry, &tl_data));
2331 }
2332
2333 krb5_error_code
2334 krb5_dbe_lookup_mkey_aux(krb5_context          context,
2335                          krb5_db_entry       * entry,
2336                          krb5_mkey_aux_node ** mkey_aux_data_list)
2337 {
2338     krb5_tl_data tl_data;
2339     krb5_int16 version;
2340     krb5_mkey_aux_node *head_data = NULL, *new_data = NULL,
2341                        *prev_data = NULL;
2342     krb5_octet *curloc; /* current location pointer */
2343     krb5_error_code code;
2344
2345     tl_data.tl_data_type = KRB5_TL_MKEY_AUX;
2346     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
2347         return (code);
2348
2349     if (tl_data.tl_data_contents == NULL) {
2350         *mkey_aux_data_list = NULL;
2351         return (0);
2352     } else {
2353         /* get version to determine how to parse the data */
2354         krb5_kdb_decode_int16(tl_data.tl_data_contents, version);
2355         if (version == 1) {
2356             /* variable size, must be at least 10 bytes */
2357             if (tl_data.tl_data_length < 10)
2358                 return (KRB5_KDB_TRUNCATED_RECORD);
2359
2360             /* curloc points to first tuple entry in the tl_data_contents */
2361             curloc = tl_data.tl_data_contents + sizeof(version);
2362
2363             while (curloc < (tl_data.tl_data_contents + tl_data.tl_data_length)) {
2364
2365                 new_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
2366                 if (new_data == NULL) {
2367                     krb5_dbe_free_mkey_aux_list(context, head_data);
2368                     return (ENOMEM);
2369                 }
2370                 memset(new_data, 0, sizeof(krb5_mkey_aux_node));
2371
2372                 krb5_kdb_decode_int16(curloc, new_data->mkey_kvno);
2373                 curloc += sizeof(krb5_ui_2);
2374                 krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_kvno);
2375                 curloc += sizeof(krb5_ui_2);
2376                 krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_type[0]);
2377                 curloc += sizeof(krb5_ui_2);
2378                 krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_length[0]);
2379                 curloc += sizeof(krb5_ui_2);
2380
2381                 new_data->latest_mkey.key_data_contents[0] = (krb5_octet *)
2382                     malloc(new_data->latest_mkey.key_data_length[0]);
2383
2384                 if (new_data->latest_mkey.key_data_contents[0] == NULL) {
2385                     krb5_dbe_free_mkey_aux_list(context, head_data);
2386                     return (ENOMEM);
2387                 }
2388                 memcpy(new_data->latest_mkey.key_data_contents[0], curloc,
2389                        new_data->latest_mkey.key_data_length[0]);
2390                 curloc += new_data->latest_mkey.key_data_length[0];
2391
2392                 /* always using key data ver 1 for mkeys */
2393                 new_data->latest_mkey.key_data_ver = 1;
2394
2395                 new_data->next = NULL;
2396                 if (prev_data != NULL)
2397                     prev_data->next = new_data;
2398                 else
2399                     head_data = new_data;
2400                 prev_data = new_data;
2401             }
2402         } else {
2403             krb5_set_error_message(context, KRB5_KDB_BAD_VERSION,
2404                                    "Illegal version number for KRB5_TL_MKEY_AUX %d\n",
2405                                    version);
2406             return (KRB5_KDB_BAD_VERSION);
2407         }
2408     }
2409     *mkey_aux_data_list = head_data;
2410     return (0);
2411 }
2412
2413 #if KRB5_TL_MKEY_AUX_VER == 1
2414 krb5_error_code
2415 krb5_dbe_update_mkey_aux(krb5_context         context,
2416                          krb5_db_entry      * entry,
2417                          krb5_mkey_aux_node * mkey_aux_data_list)
2418 {
2419     krb5_tl_data tl_data;
2420     krb5_int16 version, tmp_kvno;
2421     unsigned char *nextloc;
2422     krb5_mkey_aux_node *aux_data_entry;
2423
2424     if (!mkey_aux_data_list) {
2425         /* delete the KRB5_TL_MKEY_AUX from the entry */
2426         krb5_dbe_delete_tl_data(context, entry, KRB5_TL_MKEY_AUX);
2427         return (0);
2428     }
2429
2430     memset(&tl_data, 0, sizeof(tl_data));
2431     tl_data.tl_data_type = KRB5_TL_MKEY_AUX;
2432     /*
2433      * determine out how much space to allocate.  Note key_data_ver not stored
2434      * as this is hard coded to one and is accounted for in
2435      * krb5_dbe_lookup_mkey_aux.
2436      */
2437     tl_data.tl_data_length = sizeof(version); /* version */
2438     for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
2439          aux_data_entry = aux_data_entry->next) {
2440
2441         tl_data.tl_data_length += (sizeof(krb5_ui_2) + /* mkey_kvno */
2442                                    sizeof(krb5_ui_2) + /* latest_mkey kvno */
2443                                    sizeof(krb5_ui_2) + /* latest_mkey enctype */
2444                                    sizeof(krb5_ui_2) + /* latest_mkey length */
2445                                    aux_data_entry->latest_mkey.key_data_length[0]);
2446     }
2447
2448     tl_data.tl_data_contents = (krb5_octet *) malloc(tl_data.tl_data_length);
2449     if (tl_data.tl_data_contents == NULL) {
2450         return (ENOMEM);
2451     }
2452
2453     nextloc = tl_data.tl_data_contents;
2454     version = KRB5_TL_MKEY_AUX_VER;
2455     krb5_kdb_encode_int16(version, nextloc);
2456     nextloc += sizeof(krb5_ui_2);
2457
2458     for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
2459          aux_data_entry = aux_data_entry->next) {
2460
2461         tmp_kvno = (krb5_int16) aux_data_entry->mkey_kvno;
2462         krb5_kdb_encode_int16(tmp_kvno, nextloc);
2463         nextloc += sizeof(krb5_ui_2);
2464
2465         krb5_kdb_encode_int16(aux_data_entry->latest_mkey.key_data_kvno,
2466                               nextloc);
2467         nextloc += sizeof(krb5_ui_2);
2468
2469         krb5_kdb_encode_int16(aux_data_entry->latest_mkey.key_data_type[0],
2470                               nextloc);
2471         nextloc += sizeof(krb5_ui_2);
2472
2473         krb5_kdb_encode_int16(aux_data_entry->latest_mkey.key_data_length[0],
2474                               nextloc);
2475         nextloc += sizeof(krb5_ui_2);
2476
2477         if (aux_data_entry->latest_mkey.key_data_length[0] > 0) {
2478             memcpy(nextloc, aux_data_entry->latest_mkey.key_data_contents[0],
2479                    aux_data_entry->latest_mkey.key_data_length[0]);
2480             nextloc += aux_data_entry->latest_mkey.key_data_length[0];
2481         }
2482     }
2483
2484     return (krb5_dbe_update_tl_data(context, entry, &tl_data));
2485 }
2486 #endif /* KRB5_TL_MKEY_AUX_VER == 1 */
2487
2488 #if KRB5_TL_ACTKVNO_VER == 1
2489 /*
2490  * If version of the KRB5_TL_ACTKVNO data is KRB5_TL_ACTKVNO_VER == 1 then size of
2491  * a actkvno tuple {act_kvno, act_time} entry is:
2492  */
2493 #define ACTKVNO_TUPLE_SIZE (sizeof(krb5_int16) + sizeof(krb5_int32))
2494 #define act_kvno(cp) (cp) /* return pointer to start of act_kvno data */
2495 #define act_time(cp) ((cp) + sizeof(krb5_int16)) /* return pointer to start of act_time data */
2496 #endif
2497
2498 krb5_error_code
2499 krb5_dbe_lookup_actkvno(krb5_context context,
2500                         krb5_db_entry *entry,
2501                         krb5_actkvno_node **actkvno_list)
2502 {
2503     krb5_tl_data tl_data;
2504     krb5_error_code code;
2505     krb5_int16 version, tmp_kvno;
2506     krb5_actkvno_node *head_data = NULL, *new_data = NULL, *prev_data = NULL;
2507     unsigned int num_actkvno, i;
2508     krb5_octet *next_tuple;
2509
2510     memset(&tl_data, 0, sizeof(tl_data));
2511     tl_data.tl_data_type = KRB5_TL_ACTKVNO;
2512
2513     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
2514         return (code);
2515
2516     if (tl_data.tl_data_contents == NULL) {
2517         *actkvno_list = NULL;
2518         return (0);
2519     } else {
2520         /* get version to determine how to parse the data */
2521         krb5_kdb_decode_int16(tl_data.tl_data_contents, version);
2522         if (version == 1) {
2523
2524             /* variable size, must be at least 8 bytes */
2525             if (tl_data.tl_data_length < 8)
2526                 return (KRB5_KDB_TRUNCATED_RECORD);
2527
2528             /*
2529              * Find number of tuple entries, remembering to account for version
2530              * field.
2531              */
2532             num_actkvno = (tl_data.tl_data_length - sizeof(version)) /
2533                           ACTKVNO_TUPLE_SIZE;
2534             prev_data = NULL;
2535             /* next_tuple points to first tuple entry in the tl_data_contents */
2536             next_tuple = tl_data.tl_data_contents + sizeof(version);
2537             for (i = 0; i < num_actkvno; i++) {
2538                 new_data = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
2539                 if (new_data == NULL) {
2540                     krb5_dbe_free_actkvno_list(context, head_data);
2541                     return (ENOMEM);
2542                 }
2543                 memset(new_data, 0, sizeof(krb5_actkvno_node));
2544
2545                 /* using tmp_kvno to avoid type mismatch */
2546                 krb5_kdb_decode_int16(act_kvno(next_tuple), tmp_kvno);
2547                 new_data->act_kvno = (krb5_kvno) tmp_kvno;
2548                 krb5_kdb_decode_int32(act_time(next_tuple), new_data->act_time);
2549
2550                 if (prev_data != NULL)
2551                     prev_data->next = new_data;
2552                 else
2553                     head_data = new_data;
2554                 prev_data = new_data;
2555                 next_tuple += ACTKVNO_TUPLE_SIZE;
2556             }
2557         } else {
2558             krb5_set_error_message (context, KRB5_KDB_BAD_VERSION,
2559                 "Illegal version number for KRB5_TL_ACTKVNO %d\n",
2560                 version);
2561             return (KRB5_KDB_BAD_VERSION);
2562         }
2563     }
2564     *actkvno_list = head_data;
2565     return (0);
2566 }
2567
2568 /*
2569  * Add KRB5_TL_ACTKVNO TL data entries to krb5_db_entry *entry
2570  */
2571 #if KRB5_TL_ACTKVNO_VER == 1
2572 krb5_error_code
2573 krb5_dbe_update_actkvno(krb5_context context,
2574                         krb5_db_entry *entry,
2575                         const krb5_actkvno_node *actkvno_list)
2576 {
2577     krb5_error_code retval = 0;
2578     krb5_int16 version, tmp_kvno;
2579     krb5_tl_data new_tl_data;
2580     unsigned char *nextloc;
2581     const krb5_actkvno_node *cur_actkvno;
2582     krb5_octet *tmpptr;
2583
2584     if (actkvno_list == NULL) {
2585         return (EINVAL);
2586     }
2587
2588     memset(&new_tl_data, 0, sizeof(new_tl_data));
2589     /* allocate initial KRB5_TL_ACTKVNO tl_data entry */
2590     new_tl_data.tl_data_length = sizeof(version);
2591     new_tl_data.tl_data_contents = (krb5_octet *) malloc(new_tl_data.tl_data_length);
2592     if (new_tl_data.tl_data_contents == NULL)
2593         return (ENOMEM);
2594
2595     /* add the current version # for the data format used for KRB5_TL_ACTKVNO */
2596     version = KRB5_TL_ACTKVNO_VER;
2597     krb5_kdb_encode_int16(version, (unsigned char *) new_tl_data.tl_data_contents);
2598
2599     for (cur_actkvno = actkvno_list; cur_actkvno != NULL;
2600          cur_actkvno = cur_actkvno->next) {
2601
2602         new_tl_data.tl_data_length += ACTKVNO_TUPLE_SIZE;
2603         tmpptr = realloc(new_tl_data.tl_data_contents, new_tl_data.tl_data_length);
2604         if (tmpptr == NULL) {
2605             free(new_tl_data.tl_data_contents);
2606             return (ENOMEM);
2607         } else {
2608             new_tl_data.tl_data_contents = tmpptr;
2609         }
2610
2611         /*
2612          * Using realloc so tl_data_contents is required to correctly calculate
2613          * next location to store new tuple.
2614          */
2615         nextloc = new_tl_data.tl_data_contents + new_tl_data.tl_data_length - ACTKVNO_TUPLE_SIZE;
2616         /* using tmp_kvno to avoid type mismatch issues */
2617         tmp_kvno = (krb5_int16) cur_actkvno->act_kvno;
2618         krb5_kdb_encode_int16(tmp_kvno, nextloc);
2619         nextloc += sizeof(krb5_ui_2);
2620         krb5_kdb_encode_int32((krb5_ui_4)cur_actkvno->act_time, nextloc);
2621     }
2622
2623     new_tl_data.tl_data_type = KRB5_TL_ACTKVNO;
2624     retval = krb5_dbe_update_tl_data(context, entry, &new_tl_data);
2625     free(new_tl_data.tl_data_contents);
2626
2627     return (retval);
2628 }
2629 #endif /* KRB5_TL_ACTKVNO_VER == 1 */
2630
2631 krb5_error_code
2632 krb5_dbe_update_last_pwd_change(context, entry, stamp)
2633     krb5_context context;
2634     krb5_db_entry *entry;
2635     krb5_timestamp stamp;
2636 {
2637     krb5_tl_data tl_data;
2638     krb5_octet buf[4];          /* this is the encoded size of an int32 */
2639
2640     tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
2641     tl_data.tl_data_length = sizeof(buf);
2642     krb5_kdb_encode_int32((krb5_int32) stamp, buf);
2643     tl_data.tl_data_contents = buf;
2644
2645     return (krb5_dbe_update_tl_data(context, entry, &tl_data));
2646 }
2647
2648 krb5_error_code
2649 krb5_dbe_delete_tl_data(krb5_context context,
2650                         krb5_db_entry *entry,
2651                         krb5_int16 tl_data_type) 
2652 {
2653     krb5_tl_data *tl_data, *prev_tl_data, *free_tl_data;
2654
2655     /*
2656      * Find existing entries of the specified type and remove them from the
2657      * entry's tl_data list.
2658      */
2659
2660     for (prev_tl_data = tl_data = entry->tl_data; tl_data != NULL;) {
2661         if (tl_data->tl_data_type == tl_data_type) {
2662             if (tl_data == entry->tl_data) {
2663                 /* remove from head */
2664                 entry->tl_data = tl_data->tl_data_next;
2665                 prev_tl_data = entry->tl_data;
2666             } else if (tl_data->tl_data_next == NULL) {
2667                 /* remove from tail */
2668                 prev_tl_data->tl_data_next = NULL;
2669             } else {
2670                 /* remove in between */
2671                 prev_tl_data->tl_data_next = tl_data->tl_data_next;
2672             }
2673             free_tl_data = tl_data;
2674             tl_data = tl_data->tl_data_next;
2675             krb5_dbe_free_tl_data(context, free_tl_data);
2676             entry->n_tl_data--;
2677         } else {
2678             tl_data = tl_data->tl_data_next;
2679             prev_tl_data = tl_data;
2680         }
2681     }
2682
2683     return (0);
2684 }
2685
2686 krb5_error_code
2687 krb5_dbe_update_tl_data(context, entry, new_tl_data)
2688     krb5_context context;
2689     krb5_db_entry *entry;
2690     krb5_tl_data *new_tl_data;
2691 {
2692     krb5_tl_data *tl_data = NULL;
2693     krb5_octet *tmp;
2694
2695     /*
2696      * Copy the new data first, so we can fail cleanly if malloc()
2697      * fails.
2698      */
2699     if ((tmp =
2700          (krb5_octet *) krb5_db_alloc(context, NULL,
2701                                       new_tl_data->tl_data_length)) == NULL)
2702         return (ENOMEM);
2703
2704     /*
2705      * Find an existing entry of the specified type and point at
2706      * it, or NULL if not found.
2707      */
2708
2709     if (new_tl_data->tl_data_type != KRB5_TL_DB_ARGS) { /* db_args can be multiple */
2710         for (tl_data = entry->tl_data; tl_data;
2711              tl_data = tl_data->tl_data_next)
2712             if (tl_data->tl_data_type == new_tl_data->tl_data_type)
2713                 break;
2714     }
2715
2716     /* If necessary, chain a new record in the beginning and point at it.  */
2717
2718     if (!tl_data) {
2719         if ((tl_data =
2720              (krb5_tl_data *) krb5_db_alloc(context, NULL,
2721                                             sizeof(krb5_tl_data)))
2722             == NULL) {
2723             free(tmp);
2724             return (ENOMEM);
2725         }
2726         memset(tl_data, 0, sizeof(krb5_tl_data));
2727         tl_data->tl_data_next = entry->tl_data;
2728         entry->tl_data = tl_data;
2729         entry->n_tl_data++;
2730     }
2731
2732     /* fill in the record */
2733
2734     if (tl_data->tl_data_contents)
2735         krb5_db_free(context, tl_data->tl_data_contents);
2736
2737     tl_data->tl_data_type = new_tl_data->tl_data_type;
2738     tl_data->tl_data_length = new_tl_data->tl_data_length;
2739     tl_data->tl_data_contents = tmp;
2740     memcpy(tmp, new_tl_data->tl_data_contents, tl_data->tl_data_length);
2741
2742     return (0);
2743 }
2744
2745 /* change password functions */
2746 krb5_error_code
2747 krb5_dbe_cpw(krb5_context kcontext,
2748              krb5_keyblock * master_key,
2749              krb5_key_salt_tuple * ks_tuple,
2750              int ks_tuple_count,
2751              char *passwd,
2752              int new_kvno, krb5_boolean keepold, krb5_db_entry * db_entry)
2753 {
2754     krb5_error_code status = 0;
2755     kdb5_dal_handle *dal_handle;
2756
2757     if (kcontext->dal_handle == NULL) {
2758         status = kdb_setup_lib_handle(kcontext);
2759         if (status) {
2760             goto clean_n_exit;
2761         }
2762     }
2763
2764     dal_handle = kcontext->dal_handle;
2765     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2766     if (status) {
2767         goto clean_n_exit;
2768     }
2769
2770     status = dal_handle->lib_handle->vftabl.db_change_pwd(kcontext,
2771                                                           master_key,
2772                                                           ks_tuple,
2773                                                           ks_tuple_count,
2774                                                           passwd,
2775                                                           new_kvno,
2776                                                           keepold, db_entry);
2777     get_errmsg(kcontext, status);
2778     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2779
2780   clean_n_exit:
2781     return status;
2782 }
2783
2784 /* policy management functions */
2785 krb5_error_code
2786 krb5_db_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
2787 {
2788     krb5_error_code status = 0;
2789     kdb5_dal_handle *dal_handle;
2790
2791     if (kcontext->dal_handle == NULL) {
2792         status = kdb_setup_lib_handle(kcontext);
2793         if (status) {
2794             goto clean_n_exit;
2795         }
2796     }
2797
2798     dal_handle = kcontext->dal_handle;
2799     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2800     if (status) {
2801         goto clean_n_exit;
2802     }
2803
2804     status = dal_handle->lib_handle->vftabl.db_create_policy(kcontext, policy);
2805     get_errmsg(kcontext, status);
2806     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2807
2808   clean_n_exit:
2809     return status;
2810 }
2811
2812 krb5_error_code
2813 krb5_db_get_policy(krb5_context kcontext, char *name,
2814                    osa_policy_ent_t * policy, int *cnt)
2815 {
2816     krb5_error_code status = 0;
2817     kdb5_dal_handle *dal_handle;
2818
2819     if (kcontext->dal_handle == NULL) {
2820         status = kdb_setup_lib_handle(kcontext);
2821         if (status) {
2822             goto clean_n_exit;
2823         }
2824     }
2825
2826     dal_handle = kcontext->dal_handle;
2827     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2828     if (status) {
2829         goto clean_n_exit;
2830     }
2831
2832     status =
2833         dal_handle->lib_handle->vftabl.db_get_policy(kcontext, name, policy,
2834                                                      cnt);
2835     get_errmsg(kcontext, status);
2836     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2837
2838   clean_n_exit:
2839     return status;
2840 }
2841
2842 krb5_error_code
2843 krb5_db_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
2844 {
2845     krb5_error_code status = 0;
2846     kdb5_dal_handle *dal_handle;
2847
2848     if (kcontext->dal_handle == NULL) {
2849         status = kdb_setup_lib_handle(kcontext);
2850         if (status) {
2851             goto clean_n_exit;
2852         }
2853     }
2854
2855     dal_handle = kcontext->dal_handle;
2856     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2857     if (status) {
2858         goto clean_n_exit;
2859     }
2860
2861     status = dal_handle->lib_handle->vftabl.db_put_policy(kcontext, policy);
2862     get_errmsg(kcontext, status);
2863     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2864
2865   clean_n_exit:
2866     return status;
2867 }
2868
2869 krb5_error_code
2870 krb5_db_iter_policy(krb5_context kcontext, char *match_entry,
2871                     osa_adb_iter_policy_func func, void *data)
2872 {
2873     krb5_error_code status = 0;
2874     kdb5_dal_handle *dal_handle;
2875
2876     if (kcontext->dal_handle == NULL) {
2877         status = kdb_setup_lib_handle(kcontext);
2878         if (status) {
2879             goto clean_n_exit;
2880         }
2881     }
2882
2883     dal_handle = kcontext->dal_handle;
2884     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2885     if (status) {
2886         goto clean_n_exit;
2887     }
2888
2889     status =
2890         dal_handle->lib_handle->vftabl.db_iter_policy(kcontext, match_entry,
2891                                                       func, data);
2892     get_errmsg(kcontext, status);
2893     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2894
2895   clean_n_exit:
2896     return status;
2897 }
2898
2899 krb5_error_code
2900 krb5_db_delete_policy(krb5_context kcontext, char *policy)
2901 {
2902     krb5_error_code status = 0;
2903     kdb5_dal_handle *dal_handle;
2904
2905     if (kcontext->dal_handle == NULL) {
2906         status = kdb_setup_lib_handle(kcontext);
2907         if (status) {
2908             goto clean_n_exit;
2909         }
2910     }
2911
2912     dal_handle = kcontext->dal_handle;
2913     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2914     if (status) {
2915         goto clean_n_exit;
2916     }
2917
2918     status = dal_handle->lib_handle->vftabl.db_delete_policy(kcontext, policy);
2919     get_errmsg(kcontext, status);
2920     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2921
2922   clean_n_exit:
2923     return status;
2924 }
2925
2926 void
2927 krb5_db_free_policy(krb5_context kcontext, osa_policy_ent_t policy)
2928 {
2929     krb5_error_code status = 0;
2930     kdb5_dal_handle *dal_handle;
2931
2932     if (kcontext->dal_handle == NULL) {
2933         status = kdb_setup_lib_handle(kcontext);
2934         if (status) {
2935             goto clean_n_exit;
2936         }
2937     }
2938
2939     dal_handle = kcontext->dal_handle;
2940     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2941     if (status) {
2942         goto clean_n_exit;
2943     }
2944
2945     dal_handle->lib_handle->vftabl.db_free_policy(kcontext, policy);
2946     get_errmsg(kcontext, status);
2947     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2948
2949   clean_n_exit:
2950     return;
2951 }
2952
2953 krb5_error_code
2954 krb5_db_promote(krb5_context kcontext, char **db_args)
2955 {
2956     krb5_error_code status = 0;
2957     char   *section = NULL;
2958     kdb5_dal_handle *dal_handle;
2959
2960     section = kdb_get_conf_section(kcontext);
2961     if (section == NULL) {
2962         status = KRB5_KDB_SERVER_INTERNAL_ERR;
2963         krb5_set_error_message (kcontext, status,
2964                 "unable to determine configuration section for realm %s\n",
2965                 kcontext->default_realm);
2966         goto clean_n_exit;
2967     }
2968
2969     if (kcontext->dal_handle == NULL) {
2970         status = kdb_setup_lib_handle(kcontext);
2971         if (status) {
2972             goto clean_n_exit;
2973         }
2974     }
2975
2976     dal_handle = kcontext->dal_handle;
2977     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2978     if (status) {
2979         goto clean_n_exit;
2980     }
2981
2982     status =
2983         dal_handle->lib_handle->vftabl.promote_db(kcontext, section, db_args);
2984     get_errmsg(kcontext, status);
2985     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2986
2987   clean_n_exit:
2988     if (section)
2989         free(section);
2990     return status;
2991 }
2992
2993 krb5_error_code
2994 krb5_dbekd_decrypt_key_data( krb5_context         kcontext,
2995                              const krb5_keyblock        * mkey,
2996                              const krb5_key_data        * key_data,
2997                              krb5_keyblock      * dbkey,
2998                              krb5_keysalt       * keysalt)
2999 {
3000     krb5_error_code status = 0;
3001     kdb5_dal_handle *dal_handle;
3002
3003     if (kcontext->dal_handle == NULL) {
3004         status = kdb_setup_lib_handle(kcontext);
3005         if (status) {
3006             goto clean_n_exit;
3007         }
3008     }
3009
3010     dal_handle = kcontext->dal_handle;
3011     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
3012     if (status) {
3013         goto clean_n_exit;
3014     }
3015
3016     status =
3017         dal_handle->lib_handle->vftabl.dbekd_decrypt_key_data(kcontext,
3018             mkey, key_data, dbkey, keysalt);
3019     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
3020
3021   clean_n_exit:
3022     return status;
3023 }
3024
3025 krb5_error_code
3026 krb5_dbekd_encrypt_key_data( krb5_context                 kcontext,
3027                              const krb5_keyblock        * mkey,
3028                              const krb5_keyblock        * dbkey,
3029                              const krb5_keysalt         * keysalt,
3030                              int                          keyver,
3031                              krb5_key_data              * key_data)
3032 {
3033     krb5_error_code status = 0;
3034     kdb5_dal_handle *dal_handle;
3035
3036     if (kcontext->dal_handle == NULL) {
3037         status = kdb_setup_lib_handle(kcontext);
3038         if (status) {
3039             goto clean_n_exit;
3040         }
3041     }
3042
3043     dal_handle = kcontext->dal_handle;
3044     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
3045     if (status) {
3046         goto clean_n_exit;
3047     }
3048
3049     status =
3050         dal_handle->lib_handle->vftabl.dbekd_encrypt_key_data(kcontext,
3051             mkey, dbkey, keysalt, keyver, key_data);
3052     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
3053
3054   clean_n_exit:
3055     return status;
3056 }
3057
3058 krb5_error_code
3059 krb5_db_get_context(krb5_context context, void **db_context)
3060 {
3061     *db_context = KRB5_DB_GET_DB_CONTEXT(context);
3062     if (*db_context == NULL) {
3063         return KRB5_KDB_DBNOTINITED;
3064     }
3065
3066     return 0;
3067 }
3068
3069 krb5_error_code
3070 krb5_db_set_context(krb5_context context, void *db_context)
3071 {
3072     KRB5_DB_GET_DB_CONTEXT(context) = db_context;
3073
3074     return 0;
3075 }
3076
3077 krb5_error_code
3078 krb5_db_invoke(krb5_context kcontext,
3079                unsigned int method,
3080                const krb5_data *req,
3081                krb5_data *rep)
3082 {
3083     krb5_error_code status = 0;
3084     kdb5_dal_handle *dal_handle;
3085
3086     if (kcontext->dal_handle == NULL) {
3087         status = kdb_setup_lib_handle(kcontext);
3088         if (status) {
3089             goto clean_n_exit;
3090         }
3091     }
3092
3093     dal_handle = kcontext->dal_handle;
3094     if (dal_handle->lib_handle->vftabl.db_invoke == NULL) {
3095         status = KRB5_KDB_DBTYPE_NOSUP;
3096         goto clean_n_exit;
3097     }
3098
3099     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
3100     if (status) {
3101         goto clean_n_exit;
3102     }
3103
3104     status =
3105         dal_handle->lib_handle->vftabl.db_invoke(kcontext,
3106                                                  method,
3107                                                  req,
3108                                                  rep);
3109     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
3110
3111   clean_n_exit:
3112     return status;
3113 }
3114