xref: /titanic_44/usr/src/lib/krb5/kdb/kdb5.c (revision b6131d8fb9d81bf7aaa169dfc3f2f24f68825b18)
1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 /*
8  * Copyright 2006 by the Massachusetts Institute of Technology.
9  * All Rights Reserved.
10  *
11  * Export of this software from the United States of America may
12  *   require a specific license from the United States Government.
13  *   It is the responsibility of any person or organization contemplating
14  *   export to obtain such a license before exporting.
15  *
16  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
17  * distribute this software and its documentation for any purpose and
18  * without fee is hereby granted, provided that the above copyright
19  * notice appear in all copies and that both that copyright notice and
20  * this permission notice appear in supporting documentation, and that
21  * the name of M.I.T. not be used in advertising or publicity pertaining
22  * to distribution of the software without specific, written prior
23  * permission.  Furthermore if you modify this software you must label
24  * your software as modified software and not distribute it in such a
25  * fashion that it might be confused with the original M.I.T. software.
26  * M.I.T. makes no representations about the suitability of
27  * this software for any purpose.  It is provided "as is" without express
28  * or implied warranty.
29  */
30 
31 /*
32  * This code was based on code donated to MIT by Novell for
33  * distribution under the MIT license.
34  */
35 
36 /*
37  * Include files
38  */
39 
40 #include <stdio.h>
41 #include <string.h>
42 #include <k5-int.h>
43 #include <osconf.h>
44 #include "kdb5.h"
45 #include <assert.h>
46 #include "k5-platform.h"
47 #include <libintl.h>
48 
49 /* Currently DB2 policy related errors are exported from DAL.  But
50    other databases should set_err function to return string.  */
51 #include "adb_err.h"
52 
53 /*
54  * Type definitions
55  */
56 #define KRB5_TL_DB_ARGS                 0x7fff
57 
58 /*
59  * internal static variable
60  */
61 
62 static k5_mutex_t db_lock = K5_MUTEX_PARTIAL_INITIALIZER;
63 
64 #ifdef _KDB5_STATIC_LINK
65 #undef _KDB5_DYNAMIC_LINK
66 #else
67 #undef _KDB5_DYNAMIC_LINK
68 /* to avoid redefinition problem */
69 #define _KDB5_DYNAMIC_LINK
70 #endif
71 
72 static db_library lib_list;
73 
74 /*
75  * Helper Functions
76  */
77 
78 MAKE_INIT_FUNCTION(kdb_init_lock_list);
79 MAKE_FINI_FUNCTION(kdb_fini_lock_list);
80 
81 int
82 kdb_init_lock_list(void)
83 {
84     return k5_mutex_finish_init(&db_lock);
85 }
86 
87 static int
88 kdb_lock_list()
89 {
90     int err;
91     err = CALL_INIT_FUNCTION (kdb_init_lock_list);
92     if (err)
93 	return err;
94     return k5_mutex_lock(&db_lock);
95 }
96 
97 void
98 kdb_fini_lock_list(void)
99 {
100     if (INITIALIZER_RAN(kdb_init_lock_list))
101 	k5_mutex_destroy(&db_lock);
102 }
103 
104 static int
105 kdb_unlock_list()
106 {
107     return k5_mutex_unlock(&db_lock);
108 }
109 
110 #define kdb_init_lib_lock(a) 0
111 #define kdb_destroy_lib_lock(a) (void)0
112 #define kdb_lock_lib_lock(a, b) 0
113 #define kdb_unlock_lib_lock(a, b) (void)0
114 
115 /* Caller must free result*/
116 
117 static char *
118 kdb_get_conf_section(krb5_context kcontext)
119 {
120     krb5_error_code status = 0;
121     char   *result = NULL;
122     char   *value = NULL;
123 
124     if (kcontext->default_realm == NULL)
125 	return NULL;
126     /* The profile has to have been initialized.  If the profile was
127        not initialized, expect nothing less than a crash.  */
128     status = profile_get_string(kcontext->profile,
129 				/* realms */
130 				KDB_REALM_SECTION,
131 				kcontext->default_realm,
132 				/* under the realm name, database_module */
133 				KDB_MODULE_POINTER,
134 				/* default value is the realm name itself */
135 				kcontext->default_realm,
136 				&value);
137 
138     if (status) {
139 	/* some problem */
140 	result = strdup(kcontext->default_realm);
141 	/* let NULL be handled by the caller */
142     } else {
143 	result = strdup(value);
144 	/* free profile string */
145 	profile_release_string(value);
146     }
147 
148     return result;
149 }
150 
151 static char *
152 kdb_get_library_name(krb5_context kcontext)
153 {
154     krb5_error_code status = 0;
155     char   *result = NULL;
156     char   *value = NULL;
157     char   *lib = NULL;
158 
159     status = profile_get_string(kcontext->profile,
160 				/* realms */
161 				KDB_REALM_SECTION,
162 				kcontext->default_realm,
163 				/* under the realm name, database_module */
164 				KDB_MODULE_POINTER,
165 				/* default value is the realm name itself */
166 				kcontext->default_realm,
167 				&value);
168     if (status) {
169 	goto clean_n_exit;
170     }
171 
172 #define DB2_NAME "db2"
173     /* we got the module section. Get the library name from the module */
174     status = profile_get_string(kcontext->profile, KDB_MODULE_SECTION, value,
175 				KDB_LIB_POINTER,
176 				/* default to db2 */
177 				DB2_NAME,
178 				&lib);
179 
180     if (status) {
181 	goto clean_n_exit;
182     }
183 
184     result = strdup(lib);
185   clean_n_exit:
186     if (value) {
187 	/* free profile string */
188 	profile_release_string(value);
189     }
190 
191     if (lib) {
192 	/* free profile string */
193 	profile_release_string(lib);
194     }
195     return result;
196 }
197 
198 static void
199 kdb_setup_opt_functions(db_library lib)
200 {
201     if (lib->vftabl.set_master_key == NULL) {
202 	lib->vftabl.set_master_key = kdb_def_set_mkey;
203     }
204 
205     if (lib->vftabl.get_master_key == NULL) {
206 	lib->vftabl.get_master_key = kdb_def_get_mkey;
207     }
208 
209     if (lib->vftabl.fetch_master_key == NULL) {
210 	lib->vftabl.fetch_master_key = krb5_db_def_fetch_mkey;
211     }
212 
213     if (lib->vftabl.verify_master_key == NULL) {
214 	lib->vftabl.verify_master_key = krb5_def_verify_master_key;
215     }
216 
217     if (lib->vftabl.dbe_search_enctype == NULL) {
218 	lib->vftabl.dbe_search_enctype = krb5_dbe_def_search_enctype;
219     }
220 
221     if (lib->vftabl.db_change_pwd == NULL) {
222 	lib->vftabl.db_change_pwd = krb5_dbe_def_cpw;
223     }
224 
225     if (lib->vftabl.store_master_key == NULL) {
226 	lib->vftabl.store_master_key = krb5_def_store_mkey;
227     }
228 
229     if (lib->vftabl.promote_db == NULL) {
230 	lib->vftabl.promote_db = krb5_def_promote_db;
231     }
232 }
233 
234 static int kdb_db2_pol_err_loaded = 0;
235 #ifdef _KDB5_STATIC_LINK
236 #define DEF_SYMBOL(a) extern kdb_vftabl krb5_db_vftabl_ ## a
237 #define GET_SYMBOL(a) (krb5_db_vftabl_ ## a)
238 static krb5_error_code
239 kdb_load_library(krb5_context kcontext, char *lib_name, db_library * lib)
240 {
241     krb5_error_code status;
242     void   *vftabl_addr = NULL;
243     char    buf[KRB5_MAX_ERR_STR];
244 
245     if (!strcmp("kdb_db2", lib_name) && (kdb_db2_pol_err_loaded == 0)) {
246 	initialize_adb_error_table();
247 	kdb_db2_pol_err_loaded = 1;
248     }
249 
250     *lib = calloc((size_t) 1, sizeof(**lib));
251     if (*lib == NULL) {
252 	status = ENOMEM;
253 	goto clean_n_exit;
254     }
255 
256     status = kdb_init_lib_lock(*lib);
257     if (status) {
258 	goto clean_n_exit;
259     }
260 
261     strcpy((*lib)->name, lib_name);
262 
263 #if !defined(KDB5_USE_LIB_KDB_DB2) && !defined(KDB5_USE_LIB_TEST)
264 #error No database module defined
265 #endif
266 
267 #ifdef KDB5_USE_LIB_KDB_DB2
268     if (strcmp(lib_name, "kdb_db2") == 0) {
269 	DEF_SYMBOL(kdb_db2);
270 	vftabl_addr = (void *) &GET_SYMBOL(kdb_db2);
271     } else
272 #endif
273 #ifdef KDB5_USE_LIB_TEST
274     if (strcmp(lib_name, "test") == 0) {
275 	DEF_SYMBOL(test);
276 	vftabl_addr = (void *) &GET_SYMBOL(test);
277     } else
278 #endif
279     {
280 	snprintf(buf, sizeof(buf), gettext("Program not built to support %s database type\n"),
281 		lib_name);
282 	status = KRB5_KDB_DBTYPE_NOSUP;
283 	krb5_db_set_err(kcontext, krb5_err_have_str, status, buf);
284 	goto clean_n_exit;
285     }
286 
287     memcpy(&(*lib)->vftabl, vftabl_addr, sizeof(kdb_vftabl));
288 
289     kdb_setup_opt_functions(*lib);
290 
291     if ((status = (*lib)->vftabl.init_library())) {
292 	/* ERROR. library not initialized cleanly */
293 	snprintf(buf, sizeof(buf), gettext("%s library initialization failed, error code %ld\n"),
294 		lib_name, status);
295 	status = KRB5_KDB_DBTYPE_INIT;
296 	krb5_db_set_err(kcontext, krb5_err_have_str, status, buf);
297 	goto clean_n_exit;
298     }
299 
300   clean_n_exit:
301     if (status) {
302 	free(*lib), *lib = NULL;
303     }
304     return status;
305 }
306 
307 #else /* KDB5_STATIC_LINK*/
308 
309 static char *db_dl_location[] = DEFAULT_KDB_LIB_PATH;
310 #define db_dl_n_locations (sizeof(db_dl_location) / sizeof(db_dl_location[0]))
311 
312 static krb5_error_code
313 kdb_load_library(krb5_context kcontext, char *lib_name, db_library * lib)
314 {
315     krb5_error_code status = 0;
316     int     ndx;
317     void  **vftabl_addrs = NULL;
318     /* N.B.: If this is "const" but not "static", the Solaris 10
319        native compiler has trouble building the library because of
320        absolute relocations needed in read-only section ".rodata".
321        When it's static, it goes into ".picdata", which is
322        read-write.  */
323     static const char *const dbpath_names[] = {
324 	KDB_MODULE_SECTION, "db_module_dir", NULL,
325     };
326     const char *filebases[2];
327     char **profpath = NULL;
328     char **path = NULL;
329 
330     filebases[0] = lib_name;
331     filebases[1] = NULL;
332 
333     if (!strcmp(DB2_NAME, lib_name) && (kdb_db2_pol_err_loaded == 0)) {
334 	initialize_adb_error_table();
335 	kdb_db2_pol_err_loaded = 1;
336     }
337 
338     *lib = calloc((size_t) 1, sizeof(**lib));
339     if (*lib == NULL) {
340 	status = ENOMEM;
341 	goto clean_n_exit;
342     }
343 
344     status = kdb_init_lib_lock(*lib);
345     if (status) {
346 	goto clean_n_exit;
347     }
348 
349     strcpy((*lib)->name, lib_name);
350 
351     /* Fetch the list of directories specified in the config
352        file(s) first.  */
353     status = profile_get_values(kcontext->profile, dbpath_names, &profpath);
354     if (status != 0 && status != PROF_NO_RELATION)
355 	goto clean_n_exit;
356     ndx = 0;
357     if (profpath)
358 	while (profpath[ndx] != NULL)
359 	    ndx++;
360 
361     path = calloc(ndx + db_dl_n_locations, sizeof (char *));
362     if (path == NULL) {
363 	status = errno;
364 	goto clean_n_exit;
365     }
366     if (ndx)
367 	memcpy(path, profpath, ndx * sizeof(profpath[0]));
368     memcpy(path + ndx, db_dl_location, db_dl_n_locations * sizeof(char *));
369     status = 0;
370 
371     if ((status = krb5int_open_plugin_dirs ((const char **) path,
372                                             filebases,
373                                             &(*lib)->dl_dir_handle, &kcontext->err))) {
374         const char *err_str = krb5_get_error_message(kcontext, status);
375 	status = KRB5_KDB_DBTYPE_NOTFOUND;
376 	krb5_set_error_message (kcontext, status,
377 				gettext("Unable to find requested database type: %s"), err_str);
378 	krb5_free_error_message (kcontext, err_str);
379 	goto clean_n_exit;
380     }
381 
382     if ((status = krb5int_get_plugin_dir_data (&(*lib)->dl_dir_handle, "kdb_function_table",
383                                                &vftabl_addrs, &kcontext->err))) {
384         const char *err_str = krb5_get_error_message(kcontext, status);
385         status = KRB5_KDB_DBTYPE_INIT;
386         krb5_set_error_message (kcontext, status,
387                                 gettext("plugin symbol 'kdb_function_table' lookup failed: %s"), err_str);
388         krb5_free_error_message (kcontext, err_str);
389 	goto clean_n_exit;
390     }
391 
392     if (vftabl_addrs[0] == NULL) {
393 	/* No plugins! */
394 	status = KRB5_KDB_DBTYPE_NOTFOUND;
395 	krb5_set_error_message (kcontext, status,
396 				gettext("Unable to load requested database module '%s': plugin symbol 'kdb_function_table' not found"),
397 				lib_name);
398 	goto clean_n_exit;
399     }
400 
401     memcpy(&(*lib)->vftabl, vftabl_addrs[0], sizeof(kdb_vftabl));
402     kdb_setup_opt_functions(*lib);
403 
404     if ((status = (*lib)->vftabl.init_library())) {
405         /* ERROR. library not initialized cleanly */
406         goto clean_n_exit;
407     }
408 
409 clean_n_exit:
410     if (vftabl_addrs != NULL) { krb5int_free_plugin_dir_data (vftabl_addrs); }
411     /* Both of these DTRT with NULL.  */
412     profile_free_list(profpath);
413     free(path);
414     if (status) {
415         if (*lib) {
416 	    kdb_destroy_lib_lock(*lib);
417             if (PLUGIN_DIR_OPEN((&(*lib)->dl_dir_handle))) {
418                 krb5int_close_plugin_dirs (&(*lib)->dl_dir_handle);
419             }
420 	    free(*lib);
421 	    *lib = NULL;
422 	}
423     }
424     return status;
425 }
426 
427 #endif /* end of _KDB5_STATIC_LINK */
428 
429 static krb5_error_code
430 kdb_find_library(krb5_context kcontext, char *lib_name, db_library * lib)
431 {
432     /* lock here so that no two threads try to do the same at the same time */
433     krb5_error_code status = 0;
434     int     locked = 0;
435     db_library curr_elt, prev_elt = NULL;
436 
437     if ((status = kdb_lock_list()) != 0) {
438 	goto clean_n_exit;
439     }
440     locked = 1;
441 
442     curr_elt = lib_list;
443     while (curr_elt != NULL) {
444 	if (strcmp(lib_name, curr_elt->name) == 0) {
445 	    *lib = curr_elt;
446 	    goto clean_n_exit;
447 	}
448 	prev_elt = curr_elt;
449 	curr_elt = curr_elt->next;
450     }
451 
452     /* module not found. create and add to list */
453     status = kdb_load_library(kcontext, lib_name, lib);
454     if (status) {
455 	goto clean_n_exit;
456     }
457 
458     if (prev_elt) {
459 	/* prev_elt points to the last element in the list */
460 	prev_elt->next = *lib;
461 	(*lib)->prev = prev_elt;
462     } else {
463 	lib_list = *lib;
464     }
465 
466   clean_n_exit:
467     if (*lib) {
468 	(*lib)->reference_cnt++;
469     }
470 
471     if (locked) {
472 	(void)kdb_unlock_list();
473     }
474 
475     return status;
476 }
477 
478 static krb5_error_code
479 kdb_free_library(db_library lib)
480 {
481     krb5_error_code status = 0;
482     int     locked = 0;
483 
484     if ((status = kdb_lock_list()) != 0) {
485 	goto clean_n_exit;
486     }
487     locked = 1;
488 
489     lib->reference_cnt--;
490 
491     if (lib->reference_cnt == 0) {
492 	status = lib->vftabl.fini_library();
493 	if (status) {
494 	    goto clean_n_exit;
495 	}
496 
497 	/* close the library */
498         if (PLUGIN_DIR_OPEN((&lib->dl_dir_handle))) {
499             krb5int_close_plugin_dirs (&lib->dl_dir_handle);
500         }
501 
502 	kdb_destroy_lib_lock(lib);
503 
504 	if (lib->prev == NULL) {
505 	    /* first element in the list */
506 	    lib_list = lib->next;
507 	} else {
508 	    lib->prev->next = lib->next;
509 	}
510 
511 	if (lib->next) {
512 	    lib->next->prev = lib->prev;
513 	}
514 	free(lib);
515     }
516 
517   clean_n_exit:
518     if (locked) {
519 	(void)kdb_unlock_list();
520     }
521 
522     return status;
523 }
524 
525 static krb5_error_code
526 kdb_setup_lib_handle(krb5_context kcontext)
527 {
528     char   *library = NULL;
529     krb5_error_code status = 0;
530     db_library lib = NULL;
531     kdb5_dal_handle *dal_handle = NULL;
532 
533     dal_handle = calloc((size_t) 1, sizeof(kdb5_dal_handle));
534     if (dal_handle == NULL) {
535 	status = ENOMEM;
536 	goto clean_n_exit;
537     }
538 
539     library = kdb_get_library_name(kcontext);
540     if (library == NULL) {
541 	status = KRB5_KDB_DBTYPE_NOTFOUND;
542 	goto clean_n_exit;
543     }
544 
545     status = kdb_find_library(kcontext, library, &lib);
546     if (status) {
547 	goto clean_n_exit;
548     }
549 
550     dal_handle->lib_handle = lib;
551     kcontext->db_context = (void *) dal_handle;
552 
553   clean_n_exit:
554     free(library);
555 
556     if (status) {
557 	free(dal_handle);
558 	if (lib) {
559 	    (void)kdb_free_library(lib);
560 	}
561     }
562 
563     return status;
564 }
565 
566 static krb5_error_code
567 kdb_free_lib_handle(krb5_context kcontext)
568 {
569     krb5_error_code status = 0;
570 
571     status =
572 	kdb_free_library(((kdb5_dal_handle *) kcontext->db_context)->
573 			 lib_handle);
574     if (status) {
575 	goto clean_n_exit;
576     }
577 
578     free(kcontext->db_context);
579     kcontext->db_context = NULL;
580 
581   clean_n_exit:
582     return status;
583 }
584 
585 static void
586 get_errmsg (krb5_context kcontext, krb5_error_code err_code)
587 {
588     kdb5_dal_handle *dal_handle;
589     const char *e;
590     if (err_code == 0)
591 	return;
592     assert(kcontext != NULL);
593     /* Must be called with dal_handle->lib_handle locked!  */
594     assert(kcontext->db_context != NULL);
595     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
596     if (dal_handle->lib_handle->vftabl.errcode_2_string == NULL)
597 	return;
598     e = dal_handle->lib_handle->vftabl.errcode_2_string(kcontext, err_code);
599     assert (e != NULL);
600     krb5_set_error_message(kcontext, err_code, "%s", e);
601     if (dal_handle->lib_handle->vftabl.release_errcode_string)
602 	dal_handle->lib_handle->vftabl.release_errcode_string(kcontext, e);
603 }
604 
605 /*
606  *      External functions... DAL API
607  */
608 krb5_error_code
609 krb5_db_open(krb5_context kcontext, char **db_args, int mode)
610 {
611     krb5_error_code status = 0;
612     char   *section = NULL;
613     kdb5_dal_handle *dal_handle;
614 
615     section = kdb_get_conf_section(kcontext);
616     if (section == NULL) {
617 	status = KRB5_KDB_SERVER_INTERNAL_ERR;
618 	krb5_set_error_message (kcontext, status,
619 		gettext("unable to determine configuration section for realm %s\n"),
620 		kcontext->default_realm ? kcontext->default_realm : "[UNSET]");
621 	goto clean_n_exit;
622     }
623 
624     if (kcontext->db_context == NULL) {
625 	status = kdb_setup_lib_handle(kcontext);
626 	if (status) {
627 	    goto clean_n_exit;
628 	}
629     }
630 
631     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
632     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
633     if (status) {
634 	goto clean_n_exit;
635     }
636 
637     status =
638 	dal_handle->lib_handle->vftabl.init_module(kcontext, section, db_args,
639 						   mode);
640     get_errmsg(kcontext, status);
641 
642     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
643 
644   clean_n_exit:
645     if (section)
646 	free(section);
647     return status;
648 }
649 
650 krb5_error_code
651 krb5_db_inited(krb5_context kcontext)
652 {
653     return !(kcontext && kcontext->db_context &&
654 	     ((kdb5_dal_handle *) kcontext->db_context)->db_context);
655 }
656 
657 krb5_error_code
658 krb5_db_create(krb5_context kcontext, char **db_args)
659 {
660     krb5_error_code status = 0;
661     char   *section = NULL;
662     kdb5_dal_handle *dal_handle;
663 
664     section = kdb_get_conf_section(kcontext);
665     if (section == NULL) {
666 	status = KRB5_KDB_SERVER_INTERNAL_ERR;
667 	krb5_set_error_message (kcontext, status,
668 		gettext("unable to determine configuration section for realm %s\n"),
669 		kcontext->default_realm);
670 	goto clean_n_exit;
671     }
672 
673     if (kcontext->db_context == NULL) {
674 	status = kdb_setup_lib_handle(kcontext);
675 	if (status) {
676 	    goto clean_n_exit;
677 	}
678     }
679 
680     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
681     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
682     if (status) {
683 	goto clean_n_exit;
684     }
685 
686     status =
687 	dal_handle->lib_handle->vftabl.db_create(kcontext, section, db_args);
688     get_errmsg(kcontext, status);
689 
690     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
691 
692   clean_n_exit:
693     if (section)
694 	free(section);
695     return status;
696 }
697 
698 krb5_error_code
699 krb5_db_fini(krb5_context kcontext)
700 {
701     krb5_error_code status = 0;
702     kdb5_dal_handle *dal_handle;
703 
704     if (kcontext->db_context == NULL) {
705 	/* module not loaded. So nothing to be done */
706 	goto clean_n_exit;
707     }
708 
709     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
710     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
711     if (status) {
712 	goto clean_n_exit;
713     }
714 
715     status = dal_handle->lib_handle->vftabl.fini_module(kcontext);
716     get_errmsg(kcontext, status);
717 
718     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
719 
720     if (status) {
721 	goto clean_n_exit;
722     }
723 
724     status = kdb_free_lib_handle(kcontext);
725 
726   clean_n_exit:
727     return status;
728 }
729 
730 krb5_error_code
731 krb5_db_destroy(krb5_context kcontext, char **db_args)
732 {
733     krb5_error_code status = 0;
734     char   *section = NULL;
735     kdb5_dal_handle *dal_handle;
736 
737     section = kdb_get_conf_section(kcontext);
738     if (section == NULL) {
739 	status = KRB5_KDB_SERVER_INTERNAL_ERR;
740 	krb5_set_error_message (kcontext, status,
741 		gettext("unable to determine configuration section for realm %s\n"),
742 		kcontext->default_realm);
743 	goto clean_n_exit;
744     }
745 
746     if (kcontext->db_context == NULL) {
747 	status = kdb_setup_lib_handle(kcontext);
748 	if (status) {
749 	    goto clean_n_exit;
750 	}
751     }
752 
753     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
754     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
755     if (status) {
756 	goto clean_n_exit;
757     }
758 
759     status =
760 	dal_handle->lib_handle->vftabl.db_destroy(kcontext, section, db_args);
761     get_errmsg(kcontext, status);
762     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
763 
764   clean_n_exit:
765     if (section)
766 	free(section);
767     return status;
768 }
769 
770 krb5_error_code
771 krb5_db_get_age(krb5_context kcontext, char *db_name, time_t * t)
772 {
773     krb5_error_code status = 0;
774     kdb5_dal_handle *dal_handle;
775 
776     if (kcontext->db_context == NULL) {
777 	status = kdb_setup_lib_handle(kcontext);
778 	if (status) {
779 	    goto clean_n_exit;
780 	}
781     }
782 
783     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
784     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
785     if (status) {
786 	goto clean_n_exit;
787     }
788 
789     status = dal_handle->lib_handle->vftabl.db_get_age(kcontext, db_name, t);
790     get_errmsg(kcontext, status);
791     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
792 
793   clean_n_exit:
794     return status;
795 }
796 
797 krb5_error_code
798 krb5_db_set_option(krb5_context kcontext, int option, void *value)
799 {
800     krb5_error_code status = 0;
801     kdb5_dal_handle *dal_handle;
802 
803     if (kcontext->db_context == NULL) {
804 	status = kdb_setup_lib_handle(kcontext);
805 	if (status) {
806 	    goto clean_n_exit;
807 	}
808     }
809 
810     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
811     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
812     if (status) {
813 	goto clean_n_exit;
814     }
815 
816     status =
817 	dal_handle->lib_handle->vftabl.db_set_option(kcontext, option, value);
818     get_errmsg(kcontext, status);
819     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
820 
821   clean_n_exit:
822     return status;
823 }
824 
825 krb5_error_code
826 krb5_db_lock(krb5_context kcontext, int lock_mode)
827 {
828     krb5_error_code status = 0;
829     kdb5_dal_handle *dal_handle;
830 
831     if (kcontext->db_context == NULL) {
832 	status = kdb_setup_lib_handle(kcontext);
833 	if (status) {
834 	    goto clean_n_exit;
835 	}
836     }
837 
838     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
839     /* acquire an exclusive lock, ensures no other thread uses this context */
840     status = kdb_lock_lib_lock(dal_handle->lib_handle, TRUE);
841     if (status) {
842 	goto clean_n_exit;
843     }
844 
845     status = dal_handle->lib_handle->vftabl.db_lock(kcontext, lock_mode);
846     get_errmsg(kcontext, status);
847 
848     /* exclusive lock is still held, so no other thread could use this context */
849     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
850 
851   clean_n_exit:
852     return status;
853 }
854 
855 krb5_error_code
856 krb5_db_unlock(krb5_context kcontext)
857 {
858     krb5_error_code status = 0;
859     kdb5_dal_handle *dal_handle;
860 
861     if (kcontext->db_context == NULL) {
862 	status = kdb_setup_lib_handle(kcontext);
863 	if (status) {
864 	    goto clean_n_exit;
865 	}
866     }
867 
868     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
869     /* normal lock acquired and exclusive lock released */
870     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
871     if (status) {
872 	goto clean_n_exit;
873     }
874 
875     status = dal_handle->lib_handle->vftabl.db_unlock(kcontext);
876     get_errmsg(kcontext, status);
877 
878     kdb_unlock_lib_lock(dal_handle->lib_handle, TRUE);
879 
880   clean_n_exit:
881     return status;
882 }
883 
884 krb5_error_code
885 krb5_db_get_principal(krb5_context kcontext,
886 		      krb5_const_principal search_for,
887 		      krb5_db_entry * entries,
888 		      int *nentries, krb5_boolean * more)
889 {
890     krb5_error_code status = 0;
891     kdb5_dal_handle *dal_handle;
892 
893     if (kcontext->db_context == NULL) {
894 	status = kdb_setup_lib_handle(kcontext);
895 	if (status) {
896 	    goto clean_n_exit;
897 	}
898     }
899 
900     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
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_get_principal(kcontext, search_for,
908 							entries, nentries,
909 							more);
910     get_errmsg(kcontext, status);
911     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
912 
913   clean_n_exit:
914     return status;
915 }
916 
917 krb5_error_code
918 krb5_db_get_principal_nolock(krb5_context kcontext,
919 		      krb5_const_principal search_for,
920 		      krb5_db_entry * entries,
921 		      int *nentries, krb5_boolean * more)
922 {
923     krb5_error_code status = 0;
924     kdb5_dal_handle *dal_handle;
925 
926     if (kcontext->db_context == NULL) {
927 	status = kdb_setup_lib_handle(kcontext);
928 	if (status) {
929 	    goto clean_n_exit;
930 	}
931     }
932 
933     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
934     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
935     if (status) {
936 	goto clean_n_exit;
937     }
938 
939     status =
940 	dal_handle->lib_handle->vftabl.db_get_principal_nolock(kcontext,
941 							search_for,
942 							entries, nentries,
943 							more);
944     get_errmsg(kcontext, status);
945     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
946 
947   clean_n_exit:
948     return status;
949 }
950 
951 krb5_error_code
952 krb5_db_free_principal(krb5_context kcontext, krb5_db_entry * entry, int count)
953 {
954     krb5_error_code status = 0;
955     kdb5_dal_handle *dal_handle;
956 
957     if (kcontext->db_context == NULL) {
958 	status = kdb_setup_lib_handle(kcontext);
959 	if (status) {
960 	    goto clean_n_exit;
961 	}
962     }
963 
964     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
965     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
966     if (status) {
967 	goto clean_n_exit;
968     }
969 
970     status =
971 	dal_handle->lib_handle->vftabl.db_free_principal(kcontext, entry,
972 							 count);
973     get_errmsg(kcontext, status);
974     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
975 
976   clean_n_exit:
977     return status;
978 }
979 
980 krb5_error_code
981 krb5_db_put_principal(krb5_context kcontext,
982 		      krb5_db_entry * entries, int *nentries)
983 {
984     krb5_error_code status = 0;
985     kdb5_dal_handle *dal_handle;
986     char  **db_args = NULL;
987     krb5_tl_data *prev, *curr, *next;
988     int     db_args_size = 0;
989 
990     if (kcontext->db_context == NULL) {
991 	status = kdb_setup_lib_handle(kcontext);
992 	if (status) {
993 	    goto clean_n_exit;
994 	}
995     }
996 
997     /* Giving db_args as part of tl data causes, db2 to store the
998        tl_data as such.  To prevent this, tl_data is collated and
999        passed as a sepearte argument. Currently supports only one
1000        principal.  but passing it as a seperate argument makes it
1001        difficult for kadmin remote to pass arguments to server.  */
1002     prev = NULL, curr = entries->tl_data;
1003     while (curr) {
1004 	if (curr->tl_data_type == KRB5_TL_DB_ARGS) {
1005 	    char  **t;
1006 	    /* Since this is expected to be NULL terminated string and
1007 	       this could come from any client, do a check before
1008 	       passing it to db.  */
1009 	    if (((char *) curr->tl_data_contents)[curr->tl_data_length - 1] !=
1010 		'\0') {
1011 		/* not null terminated. Dangerous input */
1012 		status = EINVAL;
1013 		goto clean_n_exit;
1014 	    }
1015 
1016 	    db_args_size++;
1017 	    t = realloc(db_args, sizeof(char *) * (db_args_size + 1));	/* 1 for NULL */
1018 	    if (t == NULL) {
1019 		status = ENOMEM;
1020 		goto clean_n_exit;
1021 	    }
1022 
1023 	    db_args = t;
1024 	    db_args[db_args_size - 1] = (char *) curr->tl_data_contents;
1025 	    db_args[db_args_size] = NULL;
1026 
1027 	    next = curr->tl_data_next;
1028 	    if (prev == NULL) {
1029 		/* current node is the first in the linked list. remove it */
1030 		entries->tl_data = curr->tl_data_next;
1031 	    } else {
1032 		prev->tl_data_next = curr->tl_data_next;
1033 	    }
1034 	    entries->n_tl_data--;
1035 	    krb5_db_free(kcontext, curr);
1036 
1037 	    /* previous does not change */
1038 	    curr = next;
1039 	} else {
1040 	    prev = curr;
1041 	    curr = curr->tl_data_next;
1042 	}
1043     }
1044 
1045     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1046     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1047     if (status) {
1048 	goto clean_n_exit;
1049     }
1050 
1051     status = dal_handle->lib_handle->vftabl.db_put_principal(kcontext, entries,
1052 							     nentries,
1053 							     db_args);
1054     get_errmsg(kcontext, status);
1055     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1056 
1057   clean_n_exit:
1058     while (db_args_size) {
1059 	if (db_args[db_args_size - 1])
1060 	    krb5_db_free(kcontext, db_args[db_args_size - 1]);
1061 
1062 	db_args_size--;
1063     }
1064 
1065     if (db_args)
1066 	free(db_args);
1067 
1068     return status;
1069 }
1070 
1071 krb5_error_code
1072 krb5_db_delete_principal(krb5_context kcontext,
1073 			 krb5_principal search_for, int *nentries)
1074 {
1075     krb5_error_code status = 0;
1076     kdb5_dal_handle *dal_handle;
1077 
1078     if (kcontext->db_context == NULL) {
1079 	status = kdb_setup_lib_handle(kcontext);
1080 	if (status) {
1081 	    goto clean_n_exit;
1082 	}
1083     }
1084 
1085     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1086     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1087     if (status) {
1088 	goto clean_n_exit;
1089     }
1090 
1091     status =
1092 	dal_handle->lib_handle->vftabl.db_delete_principal(kcontext,
1093 							   search_for,
1094 							   nentries);
1095     get_errmsg(kcontext, status);
1096     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1097 
1098   clean_n_exit:
1099     return status;
1100 }
1101 
1102 krb5_error_code
1103 krb5_db_iterate(krb5_context kcontext,
1104 		char *match_entry,
1105 		int (*func) (krb5_pointer, krb5_db_entry *),
1106 		krb5_pointer func_arg)
1107 {
1108     krb5_error_code status = 0;
1109     kdb5_dal_handle *dal_handle;
1110 
1111     if (kcontext->db_context == NULL) {
1112 	status = kdb_setup_lib_handle(kcontext);
1113 	if (status) {
1114 	    goto clean_n_exit;
1115 	}
1116     }
1117 
1118     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1119     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1120     if (status) {
1121 	goto clean_n_exit;
1122     }
1123 
1124     status = dal_handle->lib_handle->vftabl.db_iterate(kcontext,
1125 						       match_entry,
1126 						       func, func_arg);
1127     get_errmsg(kcontext, status);
1128     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1129 
1130   clean_n_exit:
1131     return status;
1132 }
1133 
1134 krb5_error_code
1135 krb5_supported_realms(krb5_context kcontext, char **realms)
1136 {
1137     krb5_error_code status = 0;
1138     kdb5_dal_handle *dal_handle;
1139 
1140     if (kcontext->db_context == NULL) {
1141 	status = kdb_setup_lib_handle(kcontext);
1142 	if (status) {
1143 	    goto clean_n_exit;
1144 	}
1145     }
1146 
1147     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1148     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1149     if (status) {
1150 	goto clean_n_exit;
1151     }
1152 
1153     status =
1154 	dal_handle->lib_handle->vftabl.db_supported_realms(kcontext, realms);
1155     get_errmsg(kcontext, status);
1156     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1157 
1158   clean_n_exit:
1159     return status;
1160 }
1161 
1162 krb5_error_code
1163 krb5_free_supported_realms(krb5_context kcontext, char **realms)
1164 {
1165     krb5_error_code status = 0;
1166     kdb5_dal_handle *dal_handle;
1167 
1168     if (kcontext->db_context == NULL) {
1169 	status = kdb_setup_lib_handle(kcontext);
1170 	if (status) {
1171 	    goto clean_n_exit;
1172 	}
1173     }
1174 
1175     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1176     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1177     if (status) {
1178 	goto clean_n_exit;
1179     }
1180 
1181     status =
1182 	dal_handle->lib_handle->vftabl.db_free_supported_realms(kcontext,
1183 								realms);
1184     get_errmsg(kcontext, status);
1185     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1186 
1187   clean_n_exit:
1188     return status;
1189 }
1190 
1191 krb5_error_code
1192 krb5_db_set_master_key_ext(krb5_context kcontext,
1193 			   char *pwd, krb5_keyblock * key)
1194 {
1195     krb5_error_code status = 0;
1196     kdb5_dal_handle *dal_handle;
1197 
1198     if (kcontext->db_context == NULL) {
1199 	status = kdb_setup_lib_handle(kcontext);
1200 	if (status) {
1201 	    goto clean_n_exit;
1202 	}
1203     }
1204 
1205     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1206     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1207     if (status) {
1208 	goto clean_n_exit;
1209     }
1210 
1211     status = dal_handle->lib_handle->vftabl.set_master_key(kcontext, pwd, key);
1212     get_errmsg(kcontext, status);
1213 
1214     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1215 
1216   clean_n_exit:
1217     return status;
1218 }
1219 
1220 krb5_error_code
1221 krb5_db_set_mkey(krb5_context context, krb5_keyblock * key)
1222 {
1223     return krb5_db_set_master_key_ext(context, NULL, key);
1224 }
1225 
1226 krb5_error_code
1227 krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key)
1228 {
1229     krb5_error_code status = 0;
1230     kdb5_dal_handle *dal_handle;
1231 
1232     if (kcontext->db_context == NULL) {
1233 	status = kdb_setup_lib_handle(kcontext);
1234 	if (status) {
1235 	    goto clean_n_exit;
1236 	}
1237     }
1238 
1239     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1240     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1241     if (status) {
1242 	goto clean_n_exit;
1243     }
1244 
1245     /* Lets use temp key and copy it later to avoid memory problems
1246        when freed by the caller.  */
1247     status = dal_handle->lib_handle->vftabl.get_master_key(kcontext, key);
1248     get_errmsg(kcontext, status);
1249     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1250 
1251   clean_n_exit:
1252     return status;
1253 }
1254 
1255 krb5_error_code
1256 krb5_db_store_master_key(krb5_context kcontext,
1257 			 char *db_arg,
1258 			 krb5_principal mname,
1259 			 krb5_keyblock * key, char *master_pwd)
1260 {
1261     krb5_error_code status = 0;
1262     kdb5_dal_handle *dal_handle;
1263 
1264     if (kcontext->db_context == NULL) {
1265 	status = kdb_setup_lib_handle(kcontext);
1266 	if (status) {
1267 	    goto clean_n_exit;
1268 	}
1269     }
1270 
1271     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1272     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1273     if (status) {
1274 	goto clean_n_exit;
1275     }
1276 
1277     status = dal_handle->lib_handle->vftabl.store_master_key(kcontext,
1278 							     db_arg,
1279 							     mname,
1280 							     key, master_pwd);
1281     get_errmsg(kcontext, status);
1282     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1283 
1284   clean_n_exit:
1285     return status;
1286 }
1287 
1288 char   *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1;
1289 char   *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2;
1290 
1291 krb5_error_code
1292 krb5_db_fetch_mkey(krb5_context context,
1293 		   krb5_principal mname,
1294 		   krb5_enctype etype,
1295 		   krb5_boolean fromkeyboard,
1296 		   krb5_boolean twice,
1297 		   char *db_args, krb5_data * salt, krb5_keyblock * key)
1298 {
1299     krb5_error_code retval;
1300     char    password[BUFSIZ];
1301     krb5_data pwd;
1302     unsigned int size = sizeof(password);
1303     int     kvno;
1304     krb5_keyblock tmp_key;
1305 
1306     memset(&tmp_key, 0, sizeof(tmp_key));
1307 
1308     if (fromkeyboard) {
1309 	krb5_data scratch;
1310 
1311 	if ((retval = krb5_read_password(context, krb5_mkey_pwd_prompt1,
1312 					 twice ? krb5_mkey_pwd_prompt2 : 0,
1313 					 password, &size))) {
1314 	    goto clean_n_exit;
1315 	}
1316 
1317 	pwd.data = password;
1318 	pwd.length = size;
1319 	if (!salt) {
1320 	    retval = krb5_principal2salt(context, mname, &scratch);
1321 	    if (retval)
1322 		goto clean_n_exit;
1323 	}
1324 	retval =
1325 	    krb5_c_string_to_key(context, etype, &pwd, salt ? salt : &scratch,
1326 				 key);
1327 
1328 	if (!salt)
1329 	    krb5_xfree(scratch.data);
1330 	memset(password, 0, sizeof(password));	/* erase it */
1331 
1332     } else {
1333 	kdb5_dal_handle *dal_handle;
1334 
1335 	if (context->db_context == NULL) {
1336 	    retval = kdb_setup_lib_handle(context);
1337 	    if (retval) {
1338 		goto clean_n_exit;
1339 	    }
1340 	}
1341 
1342 	dal_handle = (kdb5_dal_handle *) context->db_context;
1343 	retval = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1344 	if (retval) {
1345 	    goto clean_n_exit;
1346 	}
1347 #if 0 /************** Begin IFDEF'ed OUT *******************************/
1348 	/* Orig MIT */
1349 	tmp_key.enctype = key->enctype;
1350 #else
1351 	/* Solaris Kerberos: need to use etype */
1352 	tmp_key.enctype = etype;
1353 #endif /**************** END IFDEF'ed OUT *******************************/
1354 	retval = dal_handle->lib_handle->vftabl.fetch_master_key(context,
1355 								 mname,
1356 								 &tmp_key,
1357 								 &kvno,
1358 								 db_args);
1359 	get_errmsg(context, retval);
1360 	kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1361 
1362 	if (retval) {
1363 	    goto clean_n_exit;
1364 	}
1365 
1366 	key->contents = malloc(tmp_key.length);
1367 	if (key->contents == NULL) {
1368 	    retval = ENOMEM;
1369 	    goto clean_n_exit;
1370 	}
1371 
1372 	key->magic = tmp_key.magic;
1373 	key->enctype = tmp_key.enctype;
1374 	key->length = tmp_key.length;
1375 	memcpy(key->contents, tmp_key.contents, tmp_key.length);
1376     }
1377 
1378   clean_n_exit:
1379     if (tmp_key.contents) {
1380 	memset(tmp_key.contents, 0, tmp_key.length);
1381 	krb5_db_free(context, tmp_key.contents);
1382     }
1383     return retval;
1384 }
1385 
1386 krb5_error_code
1387 krb5_db_verify_master_key(krb5_context kcontext,
1388 			  krb5_principal mprinc, krb5_keyblock * mkey)
1389 {
1390     krb5_error_code status = 0;
1391     kdb5_dal_handle *dal_handle;
1392 
1393     if (kcontext->db_context == NULL) {
1394 	status = kdb_setup_lib_handle(kcontext);
1395 	if (status) {
1396 	    goto clean_n_exit;
1397 	}
1398     }
1399 
1400     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1401     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1402     if (status) {
1403 	goto clean_n_exit;
1404     }
1405 
1406     status = dal_handle->lib_handle->vftabl.verify_master_key(kcontext,
1407 							      mprinc, mkey);
1408     get_errmsg(kcontext, status);
1409     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1410 
1411   clean_n_exit:
1412     return status;
1413 }
1414 
1415 void   *
1416 krb5_db_alloc(krb5_context kcontext, void *ptr, size_t size)
1417 {
1418     krb5_error_code status;
1419     kdb5_dal_handle *dal_handle;
1420     void   *new_ptr = NULL;
1421 
1422     if (kcontext->db_context == NULL) {
1423 	status = kdb_setup_lib_handle(kcontext);
1424 	if (status) {
1425 	    goto clean_n_exit;
1426 	}
1427     }
1428 
1429     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1430 
1431     new_ptr = dal_handle->lib_handle->vftabl.db_alloc(kcontext, ptr, size);
1432 
1433   clean_n_exit:
1434     return new_ptr;
1435 }
1436 
1437 void
1438 krb5_db_free(krb5_context kcontext, void *ptr)
1439 {
1440     krb5_error_code status;
1441     kdb5_dal_handle *dal_handle;
1442 
1443     if (kcontext->db_context == NULL) {
1444 	status = kdb_setup_lib_handle(kcontext);
1445 	if (status) {
1446 	    goto clean_n_exit;
1447 	}
1448     }
1449 
1450     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1451 
1452     dal_handle->lib_handle->vftabl.db_free(kcontext, ptr);
1453 
1454   clean_n_exit:
1455     return;
1456 }
1457 
1458 /* has to be modified */
1459 
1460 krb5_error_code
1461 krb5_dbe_find_enctype(krb5_context kcontext,
1462 		      krb5_db_entry * dbentp,
1463 		      krb5_int32 ktype,
1464 		      krb5_int32 stype,
1465 		      krb5_int32 kvno, krb5_key_data ** kdatap)
1466 {
1467     krb5_int32 start = 0;
1468     return krb5_dbe_search_enctype(kcontext, dbentp, &start, ktype, stype,
1469 				   kvno, kdatap);
1470 }
1471 
1472 krb5_error_code
1473 krb5_dbe_search_enctype(krb5_context kcontext,
1474 			krb5_db_entry * dbentp,
1475 			krb5_int32 * start,
1476 			krb5_int32 ktype,
1477 			krb5_int32 stype,
1478 			krb5_int32 kvno, krb5_key_data ** kdatap)
1479 {
1480     krb5_error_code status = 0;
1481     kdb5_dal_handle *dal_handle;
1482 
1483     if (kcontext->db_context == NULL) {
1484 	status = kdb_setup_lib_handle(kcontext);
1485 	if (status) {
1486 	    goto clean_n_exit;
1487 	}
1488     }
1489 
1490     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1491     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1492     if (status) {
1493 	goto clean_n_exit;
1494     }
1495 
1496     status = dal_handle->lib_handle->vftabl.dbe_search_enctype(kcontext,
1497 							       dbentp,
1498 							       start,
1499 							       ktype,
1500 							       stype,
1501 							       kvno, kdatap);
1502     get_errmsg(kcontext, status);
1503     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1504 
1505   clean_n_exit:
1506     return status;
1507 }
1508 
1509 #define	REALM_SEP_STRING	"@"
1510 
1511 krb5_error_code
1512 krb5_db_setup_mkey_name(krb5_context context,
1513 			const char *keyname,
1514 			const char *realm,
1515 			char **fullname, krb5_principal * principal)
1516 {
1517     krb5_error_code retval;
1518     size_t  keylen;
1519     size_t  rlen = strlen(realm);
1520     char   *fname;
1521 
1522     if (!keyname)
1523 	keyname = KRB5_KDB_M_NAME;	/* XXX external? */
1524 
1525     keylen = strlen(keyname);
1526 
1527     fname = malloc(keylen + rlen + strlen(REALM_SEP_STRING) + 1);
1528     if (!fname)
1529 	return ENOMEM;
1530 
1531     strcpy(fname, keyname);
1532     (void)strcat(fname, REALM_SEP_STRING);
1533     (void)strcat(fname, realm);
1534 
1535     if ((retval = krb5_parse_name(context, fname, principal)))
1536 	return retval;
1537     if (fullname)
1538 	*fullname = fname;
1539     else
1540 	free(fname);
1541     return 0;
1542 }
1543 
1544 krb5_error_code
1545 krb5_dbe_lookup_last_pwd_change(context, entry, stamp)
1546     krb5_context context;
1547     krb5_db_entry *entry;
1548     krb5_timestamp *stamp;
1549 {
1550     krb5_tl_data tl_data;
1551     krb5_error_code code;
1552     krb5_int32 tmp;
1553 
1554     tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
1555 
1556     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
1557 	return (code);
1558 
1559     if (tl_data.tl_data_length != 4) {
1560 	*stamp = 0;
1561 	return (0);
1562     }
1563 
1564     krb5_kdb_decode_int32(tl_data.tl_data_contents, tmp);
1565 
1566     *stamp = (krb5_timestamp) tmp;
1567 
1568     return (0);
1569 }
1570 
1571 /*ARGSUSED*/
1572 krb5_error_code
1573 krb5_dbe_lookup_tl_data(context, entry, ret_tl_data)
1574     krb5_context context;
1575     krb5_db_entry *entry;
1576     krb5_tl_data *ret_tl_data;
1577 {
1578     krb5_tl_data *tl_data;
1579 
1580     for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) {
1581 	if (tl_data->tl_data_type == ret_tl_data->tl_data_type) {
1582 	    *ret_tl_data = *tl_data;
1583 	    return (0);
1584 	}
1585     }
1586 
1587     /* if the requested record isn't found, return zero bytes.
1588      * if it ever means something to have a zero-length tl_data,
1589      * this code and its callers will have to be changed */
1590 
1591     ret_tl_data->tl_data_length = 0;
1592     ret_tl_data->tl_data_contents = NULL;
1593     return (0);
1594 }
1595 
1596 krb5_error_code
1597 krb5_dbe_create_key_data(context, entry)
1598     krb5_context context;
1599     krb5_db_entry *entry;
1600 {
1601     if ((entry->key_data =
1602 	 (krb5_key_data *) krb5_db_alloc(context, entry->key_data,
1603 					 (sizeof(krb5_key_data) *
1604 					  (entry->n_key_data + 1)))) == NULL)
1605 	return (ENOMEM);
1606 
1607     memset(entry->key_data + entry->n_key_data, 0, sizeof(krb5_key_data));
1608     entry->n_key_data++;
1609 
1610     return 0;
1611 }
1612 
1613 krb5_error_code
1614 krb5_dbe_update_mod_princ_data(context, entry, mod_date, mod_princ)
1615     krb5_context context;
1616     krb5_db_entry *entry;
1617     krb5_timestamp mod_date;
1618     krb5_const_principal mod_princ;
1619 {
1620     krb5_tl_data tl_data;
1621 
1622     krb5_error_code retval = 0;
1623     krb5_octet *nextloc = 0;
1624     char   *unparse_mod_princ = 0;
1625     unsigned int unparse_mod_princ_size;
1626 
1627     if ((retval = krb5_unparse_name(context, mod_princ, &unparse_mod_princ)))
1628 	return (retval);
1629 
1630     unparse_mod_princ_size = strlen(unparse_mod_princ) + 1;
1631 
1632     if ((nextloc = (krb5_octet *) malloc(unparse_mod_princ_size + 4))
1633 	== NULL) {
1634 	free(unparse_mod_princ);
1635 	return (ENOMEM);
1636     }
1637 
1638     tl_data.tl_data_type = KRB5_TL_MOD_PRINC;
1639     tl_data.tl_data_length = unparse_mod_princ_size + 4;
1640     tl_data.tl_data_contents = nextloc;
1641 
1642     /* Mod Date */
1643     krb5_kdb_encode_int32(mod_date, nextloc);
1644 
1645     /* Mod Princ */
1646     memcpy(nextloc + 4, unparse_mod_princ, unparse_mod_princ_size);
1647 
1648     retval = krb5_dbe_update_tl_data(context, entry, &tl_data);
1649 
1650     free(unparse_mod_princ);
1651     free(nextloc);
1652 
1653     return (retval);
1654 }
1655 
1656 krb5_error_code
1657 krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ)
1658     krb5_context context;
1659     krb5_db_entry *entry;
1660     krb5_timestamp *mod_time;
1661     krb5_principal *mod_princ;
1662 {
1663     krb5_tl_data tl_data;
1664     krb5_error_code code;
1665 
1666     tl_data.tl_data_type = KRB5_TL_MOD_PRINC;
1667 
1668     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
1669 	return (code);
1670 
1671     if ((tl_data.tl_data_length < 5) ||
1672 	(tl_data.tl_data_contents[tl_data.tl_data_length - 1] != '\0'))
1673 	return (KRB5_KDB_TRUNCATED_RECORD);
1674 
1675     /* Mod Date */
1676     krb5_kdb_decode_int32(tl_data.tl_data_contents, *mod_time);
1677 
1678     /* Mod Princ */
1679     if ((code = krb5_parse_name(context,
1680 				(const char *) (tl_data.tl_data_contents + 4),
1681 				mod_princ)))
1682 	return (code);
1683 
1684     return (0);
1685 }
1686 
1687 krb5_error_code
1688 krb5_dbe_update_last_pwd_change(context, entry, stamp)
1689     krb5_context context;
1690     krb5_db_entry *entry;
1691     krb5_timestamp stamp;
1692 {
1693     krb5_tl_data tl_data;
1694     krb5_octet buf[4];		/* this is the encoded size of an int32 */
1695 
1696     tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
1697     tl_data.tl_data_length = sizeof(buf);
1698     krb5_kdb_encode_int32((krb5_int32) stamp, buf);
1699     tl_data.tl_data_contents = buf;
1700 
1701     return (krb5_dbe_update_tl_data(context, entry, &tl_data));
1702 }
1703 
1704 krb5_error_code
1705 krb5_dbe_update_tl_data(context, entry, new_tl_data)
1706     krb5_context context;
1707     krb5_db_entry *entry;
1708     krb5_tl_data *new_tl_data;
1709 {
1710     krb5_tl_data *tl_data = NULL;
1711     krb5_octet *tmp;
1712 
1713     /* copy the new data first, so we can fail cleanly if malloc()
1714      * fails */
1715     if ((tmp =
1716 	 (krb5_octet *) krb5_db_alloc(context, NULL,
1717 				      new_tl_data->tl_data_length)) == NULL)
1718 	return (ENOMEM);
1719 
1720     /* Find an existing entry of the specified type and point at
1721      * it, or NULL if not found */
1722 
1723     if (new_tl_data->tl_data_type != KRB5_TL_DB_ARGS) {	/* db_args can be multiple */
1724 	for (tl_data = entry->tl_data; tl_data;
1725 	     tl_data = tl_data->tl_data_next)
1726 	    if (tl_data->tl_data_type == new_tl_data->tl_data_type)
1727 		break;
1728     }
1729 
1730     /* if necessary, chain a new record in the beginning and point at it */
1731 
1732     if (!tl_data) {
1733 	if ((tl_data =
1734 	     (krb5_tl_data *) krb5_db_alloc(context, NULL,
1735 					    sizeof(krb5_tl_data)))
1736 	    == NULL) {
1737 	    free(tmp);
1738 	    return (ENOMEM);
1739 	}
1740 	memset(tl_data, 0, sizeof(krb5_tl_data));
1741 	tl_data->tl_data_next = entry->tl_data;
1742 	entry->tl_data = tl_data;
1743 	entry->n_tl_data++;
1744     }
1745 
1746     /* fill in the record */
1747 
1748     if (tl_data->tl_data_contents)
1749 	krb5_db_free(context, tl_data->tl_data_contents);
1750 
1751     tl_data->tl_data_type = new_tl_data->tl_data_type;
1752     tl_data->tl_data_length = new_tl_data->tl_data_length;
1753     tl_data->tl_data_contents = tmp;
1754     memcpy(tmp, new_tl_data->tl_data_contents, tl_data->tl_data_length);
1755 
1756     return (0);
1757 }
1758 
1759 /* change password functions */
1760 krb5_error_code
1761 krb5_dbe_cpw(krb5_context kcontext,
1762 	     krb5_keyblock * master_key,
1763 	     krb5_key_salt_tuple * ks_tuple,
1764 	     int ks_tuple_count,
1765 	     char *passwd,
1766 	     int new_kvno, krb5_boolean keepold, krb5_db_entry * db_entry)
1767 {
1768     krb5_error_code status = 0;
1769     kdb5_dal_handle *dal_handle;
1770 
1771     if (kcontext->db_context == NULL) {
1772 	status = kdb_setup_lib_handle(kcontext);
1773 	if (status) {
1774 	    goto clean_n_exit;
1775 	}
1776     }
1777 
1778     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1779     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1780     if (status) {
1781 	goto clean_n_exit;
1782     }
1783 
1784     status = dal_handle->lib_handle->vftabl.db_change_pwd(kcontext,
1785 							  master_key,
1786 							  ks_tuple,
1787 							  ks_tuple_count,
1788 							  passwd,
1789 							  new_kvno,
1790 							  keepold, db_entry);
1791     get_errmsg(kcontext, status);
1792     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1793 
1794   clean_n_exit:
1795     return status;
1796 }
1797 
1798 /* policy management functions */
1799 krb5_error_code
1800 krb5_db_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
1801 {
1802     krb5_error_code status = 0;
1803     kdb5_dal_handle *dal_handle;
1804 
1805     if (kcontext->db_context == NULL) {
1806 	status = kdb_setup_lib_handle(kcontext);
1807 	if (status) {
1808 	    goto clean_n_exit;
1809 	}
1810     }
1811 
1812     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1813     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1814     if (status) {
1815 	goto clean_n_exit;
1816     }
1817 
1818     status = dal_handle->lib_handle->vftabl.db_create_policy(kcontext, policy);
1819     get_errmsg(kcontext, status);
1820     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1821 
1822   clean_n_exit:
1823     return status;
1824 }
1825 
1826 krb5_error_code
1827 krb5_db_get_policy(krb5_context kcontext, char *name,
1828 		   osa_policy_ent_t * policy, int *cnt)
1829 {
1830     krb5_error_code status = 0;
1831     kdb5_dal_handle *dal_handle;
1832 
1833     if (kcontext->db_context == NULL) {
1834 	status = kdb_setup_lib_handle(kcontext);
1835 	if (status) {
1836 	    goto clean_n_exit;
1837 	}
1838     }
1839 
1840     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1841     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1842     if (status) {
1843 	goto clean_n_exit;
1844     }
1845 
1846     status =
1847 	dal_handle->lib_handle->vftabl.db_get_policy(kcontext, name, policy,
1848 						     cnt);
1849     get_errmsg(kcontext, status);
1850     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1851 
1852   clean_n_exit:
1853     return status;
1854 }
1855 
1856 krb5_error_code
1857 krb5_db_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
1858 {
1859     krb5_error_code status = 0;
1860     kdb5_dal_handle *dal_handle;
1861 
1862     if (kcontext->db_context == NULL) {
1863 	status = kdb_setup_lib_handle(kcontext);
1864 	if (status) {
1865 	    goto clean_n_exit;
1866 	}
1867     }
1868 
1869     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1870     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1871     if (status) {
1872 	goto clean_n_exit;
1873     }
1874 
1875     status = dal_handle->lib_handle->vftabl.db_put_policy(kcontext, policy);
1876     get_errmsg(kcontext, status);
1877     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1878 
1879   clean_n_exit:
1880     return status;
1881 }
1882 
1883 krb5_error_code
1884 krb5_db_iter_policy(krb5_context kcontext, char *match_entry,
1885 		    osa_adb_iter_policy_func func, void *data)
1886 {
1887     krb5_error_code status = 0;
1888     kdb5_dal_handle *dal_handle;
1889 
1890     if (kcontext->db_context == NULL) {
1891 	status = kdb_setup_lib_handle(kcontext);
1892 	if (status) {
1893 	    goto clean_n_exit;
1894 	}
1895     }
1896 
1897     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1898     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1899     if (status) {
1900 	goto clean_n_exit;
1901     }
1902 
1903     status =
1904 	dal_handle->lib_handle->vftabl.db_iter_policy(kcontext, match_entry,
1905 						      func, data);
1906     get_errmsg(kcontext, status);
1907     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1908 
1909   clean_n_exit:
1910     return status;
1911 }
1912 
1913 krb5_error_code
1914 krb5_db_delete_policy(krb5_context kcontext, char *policy)
1915 {
1916     krb5_error_code status = 0;
1917     kdb5_dal_handle *dal_handle;
1918 
1919     if (kcontext->db_context == NULL) {
1920 	status = kdb_setup_lib_handle(kcontext);
1921 	if (status) {
1922 	    goto clean_n_exit;
1923 	}
1924     }
1925 
1926     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1927     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1928     if (status) {
1929 	goto clean_n_exit;
1930     }
1931 
1932     status = dal_handle->lib_handle->vftabl.db_delete_policy(kcontext, policy);
1933     get_errmsg(kcontext, status);
1934     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1935 
1936   clean_n_exit:
1937     return status;
1938 }
1939 
1940 void
1941 krb5_db_free_policy(krb5_context kcontext, osa_policy_ent_t policy)
1942 {
1943     krb5_error_code status = 0;
1944     kdb5_dal_handle *dal_handle;
1945 
1946     if (kcontext->db_context == NULL) {
1947 	status = kdb_setup_lib_handle(kcontext);
1948 	if (status) {
1949 	    goto clean_n_exit;
1950 	}
1951     }
1952 
1953     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1954     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1955     if (status) {
1956 	goto clean_n_exit;
1957     }
1958 
1959     dal_handle->lib_handle->vftabl.db_free_policy(kcontext, policy);
1960     get_errmsg(kcontext, status);
1961     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1962 
1963   clean_n_exit:
1964     return;
1965 }
1966 
1967 krb5_error_code
1968 krb5_db_promote(krb5_context kcontext, char **db_args)
1969 {
1970     krb5_error_code status = 0;
1971     char   *section = NULL;
1972     kdb5_dal_handle *dal_handle;
1973 
1974     section = kdb_get_conf_section(kcontext);
1975     if (section == NULL) {
1976 	status = KRB5_KDB_SERVER_INTERNAL_ERR;
1977 	krb5_set_error_message (kcontext, status,
1978 		gettext("unable to determine configuration section for realm %s\n"),
1979 		kcontext->default_realm);
1980 	goto clean_n_exit;
1981     }
1982 
1983     if (kcontext->db_context == NULL) {
1984 	status = kdb_setup_lib_handle(kcontext);
1985 	if (status) {
1986 	    goto clean_n_exit;
1987 	}
1988     }
1989 
1990     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1991     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1992     if (status) {
1993 	goto clean_n_exit;
1994     }
1995 
1996     status =
1997 	dal_handle->lib_handle->vftabl.promote_db(kcontext, section, db_args);
1998     get_errmsg(kcontext, status);
1999     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2000 
2001   clean_n_exit:
2002     if (section)
2003 	free(section);
2004     return status;
2005 }
2006 
2007 /*
2008  * Solaris Kerberos: support for iprop
2009  *
2010  * Not all KDB plugins support iprop.
2011  *
2012  * sets iprop_supported to 1 if iprop supportd, 0 otherwise.
2013  */
2014 krb5_error_code
2015 krb5_db_supports_iprop(krb5_context kcontext, int *iprop_supported)
2016 {
2017     krb5_error_code status = 0;
2018     kdb5_dal_handle *dal_handle;
2019 
2020     if (kcontext->db_context == NULL) {
2021 	status = kdb_setup_lib_handle(kcontext);
2022 	if (status) {
2023 	    goto clean_n_exit;
2024 	}
2025     }
2026 
2027     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
2028     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2029     if (status) {
2030 	goto clean_n_exit;
2031     }
2032 
2033     *iprop_supported = dal_handle->lib_handle->vftabl.iprop_supported;
2034     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2035 
2036   clean_n_exit:
2037     return status;
2038 }
2039