xref: /illumos-gate/usr/src/lib/krb5/kdb/kdb5.c (revision 69c33a7b46881d33b972b85db0dca57ce983f016)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 /*
8  * 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 		/* Solaris Kerberos: adding support for db_args */
1108 		char **db_args)
1109 {
1110     krb5_error_code status = 0;
1111     kdb5_dal_handle *dal_handle;
1112 
1113     if (kcontext->db_context == NULL) {
1114 	status = kdb_setup_lib_handle(kcontext);
1115 	if (status) {
1116 	    goto clean_n_exit;
1117 	}
1118     }
1119 
1120     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1121     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1122     if (status) {
1123 	goto clean_n_exit;
1124     }
1125 
1126     /* Solaris Kerberos: adding support for db_args */
1127     status = dal_handle->lib_handle->vftabl.db_iterate(kcontext,
1128 						       match_entry,
1129 						       func, func_arg,
1130 						       db_args);
1131     get_errmsg(kcontext, status);
1132     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1133 
1134   clean_n_exit:
1135     return status;
1136 }
1137 
1138 krb5_error_code
1139 krb5_supported_realms(krb5_context kcontext, char **realms)
1140 {
1141     krb5_error_code status = 0;
1142     kdb5_dal_handle *dal_handle;
1143 
1144     if (kcontext->db_context == NULL) {
1145 	status = kdb_setup_lib_handle(kcontext);
1146 	if (status) {
1147 	    goto clean_n_exit;
1148 	}
1149     }
1150 
1151     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1152     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1153     if (status) {
1154 	goto clean_n_exit;
1155     }
1156 
1157     status =
1158 	dal_handle->lib_handle->vftabl.db_supported_realms(kcontext, realms);
1159     get_errmsg(kcontext, status);
1160     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1161 
1162   clean_n_exit:
1163     return status;
1164 }
1165 
1166 krb5_error_code
1167 krb5_free_supported_realms(krb5_context kcontext, char **realms)
1168 {
1169     krb5_error_code status = 0;
1170     kdb5_dal_handle *dal_handle;
1171 
1172     if (kcontext->db_context == NULL) {
1173 	status = kdb_setup_lib_handle(kcontext);
1174 	if (status) {
1175 	    goto clean_n_exit;
1176 	}
1177     }
1178 
1179     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1180     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1181     if (status) {
1182 	goto clean_n_exit;
1183     }
1184 
1185     status =
1186 	dal_handle->lib_handle->vftabl.db_free_supported_realms(kcontext,
1187 								realms);
1188     get_errmsg(kcontext, status);
1189     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1190 
1191   clean_n_exit:
1192     return status;
1193 }
1194 
1195 krb5_error_code
1196 krb5_db_set_master_key_ext(krb5_context kcontext,
1197 			   char *pwd, krb5_keyblock * key)
1198 {
1199     krb5_error_code status = 0;
1200     kdb5_dal_handle *dal_handle;
1201 
1202     if (kcontext->db_context == NULL) {
1203 	status = kdb_setup_lib_handle(kcontext);
1204 	if (status) {
1205 	    goto clean_n_exit;
1206 	}
1207     }
1208 
1209     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1210     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1211     if (status) {
1212 	goto clean_n_exit;
1213     }
1214 
1215     status = dal_handle->lib_handle->vftabl.set_master_key(kcontext, pwd, key);
1216     get_errmsg(kcontext, status);
1217 
1218     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1219 
1220   clean_n_exit:
1221     return status;
1222 }
1223 
1224 krb5_error_code
1225 krb5_db_set_mkey(krb5_context context, krb5_keyblock * key)
1226 {
1227     return krb5_db_set_master_key_ext(context, NULL, key);
1228 }
1229 
1230 krb5_error_code
1231 krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key)
1232 {
1233     krb5_error_code status = 0;
1234     kdb5_dal_handle *dal_handle;
1235 
1236     if (kcontext->db_context == NULL) {
1237 	status = kdb_setup_lib_handle(kcontext);
1238 	if (status) {
1239 	    goto clean_n_exit;
1240 	}
1241     }
1242 
1243     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1244     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1245     if (status) {
1246 	goto clean_n_exit;
1247     }
1248 
1249     /* Lets use temp key and copy it later to avoid memory problems
1250        when freed by the caller.  */
1251     status = dal_handle->lib_handle->vftabl.get_master_key(kcontext, key);
1252     get_errmsg(kcontext, status);
1253     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1254 
1255   clean_n_exit:
1256     return status;
1257 }
1258 
1259 krb5_error_code
1260 krb5_db_store_master_key(krb5_context kcontext,
1261 			 char *db_arg,
1262 			 krb5_principal mname,
1263 			 krb5_keyblock * key, char *master_pwd)
1264 {
1265     krb5_error_code status = 0;
1266     kdb5_dal_handle *dal_handle;
1267 
1268     if (kcontext->db_context == NULL) {
1269 	status = kdb_setup_lib_handle(kcontext);
1270 	if (status) {
1271 	    goto clean_n_exit;
1272 	}
1273     }
1274 
1275     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1276     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1277     if (status) {
1278 	goto clean_n_exit;
1279     }
1280 
1281     status = dal_handle->lib_handle->vftabl.store_master_key(kcontext,
1282 							     db_arg,
1283 							     mname,
1284 							     key, master_pwd);
1285     get_errmsg(kcontext, status);
1286     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1287 
1288   clean_n_exit:
1289     return status;
1290 }
1291 
1292 char   *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1;
1293 char   *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2;
1294 
1295 krb5_error_code
1296 krb5_db_fetch_mkey(krb5_context context,
1297 		   krb5_principal mname,
1298 		   krb5_enctype etype,
1299 		   krb5_boolean fromkeyboard,
1300 		   krb5_boolean twice,
1301 		   char *db_args, krb5_data * salt, krb5_keyblock * key)
1302 {
1303     krb5_error_code retval;
1304     char    password[BUFSIZ];
1305     krb5_data pwd;
1306     unsigned int size = sizeof(password);
1307     int     kvno;
1308     krb5_keyblock tmp_key;
1309 
1310     memset(&tmp_key, 0, sizeof(tmp_key));
1311 
1312     if (fromkeyboard) {
1313 	krb5_data scratch;
1314 
1315 	if ((retval = krb5_read_password(context, krb5_mkey_pwd_prompt1,
1316 					 twice ? krb5_mkey_pwd_prompt2 : 0,
1317 					 password, &size))) {
1318 	    goto clean_n_exit;
1319 	}
1320 
1321 	pwd.data = password;
1322 	pwd.length = size;
1323 	if (!salt) {
1324 	    retval = krb5_principal2salt(context, mname, &scratch);
1325 	    if (retval)
1326 		goto clean_n_exit;
1327 	}
1328 	retval =
1329 	    krb5_c_string_to_key(context, etype, &pwd, salt ? salt : &scratch,
1330 				 key);
1331 
1332 	if (!salt)
1333 	    krb5_xfree(scratch.data);
1334 	memset(password, 0, sizeof(password));	/* erase it */
1335 
1336     } else {
1337 	kdb5_dal_handle *dal_handle;
1338 
1339 	if (context->db_context == NULL) {
1340 	    retval = kdb_setup_lib_handle(context);
1341 	    if (retval) {
1342 		goto clean_n_exit;
1343 	    }
1344 	}
1345 
1346 	dal_handle = (kdb5_dal_handle *) context->db_context;
1347 	retval = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1348 	if (retval) {
1349 	    goto clean_n_exit;
1350 	}
1351 #if 0 /************** Begin IFDEF'ed OUT *******************************/
1352 	/* Orig MIT */
1353 	tmp_key.enctype = key->enctype;
1354 #else
1355 	/* Solaris Kerberos: need to use etype */
1356 	tmp_key.enctype = etype;
1357 #endif /**************** END IFDEF'ed OUT *******************************/
1358 	retval = dal_handle->lib_handle->vftabl.fetch_master_key(context,
1359 								 mname,
1360 								 &tmp_key,
1361 								 &kvno,
1362 								 db_args);
1363 	get_errmsg(context, retval);
1364 	kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1365 
1366 	if (retval) {
1367 	    goto clean_n_exit;
1368 	}
1369 
1370 	key->contents = malloc(tmp_key.length);
1371 	if (key->contents == NULL) {
1372 	    retval = ENOMEM;
1373 	    goto clean_n_exit;
1374 	}
1375 
1376 	key->magic = tmp_key.magic;
1377 	key->enctype = tmp_key.enctype;
1378 	key->length = tmp_key.length;
1379 	memcpy(key->contents, tmp_key.contents, tmp_key.length);
1380     }
1381 
1382   clean_n_exit:
1383     if (tmp_key.contents) {
1384 	memset(tmp_key.contents, 0, tmp_key.length);
1385 	krb5_db_free(context, tmp_key.contents);
1386     }
1387     return retval;
1388 }
1389 
1390 krb5_error_code
1391 krb5_db_verify_master_key(krb5_context kcontext,
1392 			  krb5_principal mprinc, krb5_keyblock * mkey)
1393 {
1394     krb5_error_code status = 0;
1395     kdb5_dal_handle *dal_handle;
1396 
1397     if (kcontext->db_context == NULL) {
1398 	status = kdb_setup_lib_handle(kcontext);
1399 	if (status) {
1400 	    goto clean_n_exit;
1401 	}
1402     }
1403 
1404     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1405     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1406     if (status) {
1407 	goto clean_n_exit;
1408     }
1409 
1410     status = dal_handle->lib_handle->vftabl.verify_master_key(kcontext,
1411 							      mprinc, mkey);
1412     get_errmsg(kcontext, status);
1413     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1414 
1415   clean_n_exit:
1416     return status;
1417 }
1418 
1419 void   *
1420 krb5_db_alloc(krb5_context kcontext, void *ptr, size_t size)
1421 {
1422     krb5_error_code status;
1423     kdb5_dal_handle *dal_handle;
1424     void   *new_ptr = NULL;
1425 
1426     if (kcontext->db_context == NULL) {
1427 	status = kdb_setup_lib_handle(kcontext);
1428 	if (status) {
1429 	    goto clean_n_exit;
1430 	}
1431     }
1432 
1433     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1434 
1435     new_ptr = dal_handle->lib_handle->vftabl.db_alloc(kcontext, ptr, size);
1436 
1437   clean_n_exit:
1438     return new_ptr;
1439 }
1440 
1441 void
1442 krb5_db_free(krb5_context kcontext, void *ptr)
1443 {
1444     krb5_error_code status;
1445     kdb5_dal_handle *dal_handle;
1446 
1447     if (kcontext->db_context == NULL) {
1448 	status = kdb_setup_lib_handle(kcontext);
1449 	if (status) {
1450 	    goto clean_n_exit;
1451 	}
1452     }
1453 
1454     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1455 
1456     dal_handle->lib_handle->vftabl.db_free(kcontext, ptr);
1457 
1458   clean_n_exit:
1459     return;
1460 }
1461 
1462 /* has to be modified */
1463 
1464 krb5_error_code
1465 krb5_dbe_find_enctype(krb5_context kcontext,
1466 		      krb5_db_entry * dbentp,
1467 		      krb5_int32 ktype,
1468 		      krb5_int32 stype,
1469 		      krb5_int32 kvno, krb5_key_data ** kdatap)
1470 {
1471     krb5_int32 start = 0;
1472     return krb5_dbe_search_enctype(kcontext, dbentp, &start, ktype, stype,
1473 				   kvno, kdatap);
1474 }
1475 
1476 krb5_error_code
1477 krb5_dbe_search_enctype(krb5_context kcontext,
1478 			krb5_db_entry * dbentp,
1479 			krb5_int32 * start,
1480 			krb5_int32 ktype,
1481 			krb5_int32 stype,
1482 			krb5_int32 kvno, krb5_key_data ** kdatap)
1483 {
1484     krb5_error_code status = 0;
1485     kdb5_dal_handle *dal_handle;
1486 
1487     if (kcontext->db_context == NULL) {
1488 	status = kdb_setup_lib_handle(kcontext);
1489 	if (status) {
1490 	    goto clean_n_exit;
1491 	}
1492     }
1493 
1494     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1495     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1496     if (status) {
1497 	goto clean_n_exit;
1498     }
1499 
1500     status = dal_handle->lib_handle->vftabl.dbe_search_enctype(kcontext,
1501 							       dbentp,
1502 							       start,
1503 							       ktype,
1504 							       stype,
1505 							       kvno, kdatap);
1506     get_errmsg(kcontext, status);
1507     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1508 
1509   clean_n_exit:
1510     return status;
1511 }
1512 
1513 #define	REALM_SEP_STRING	"@"
1514 
1515 krb5_error_code
1516 krb5_db_setup_mkey_name(krb5_context context,
1517 			const char *keyname,
1518 			const char *realm,
1519 			char **fullname, krb5_principal * principal)
1520 {
1521     krb5_error_code retval;
1522     size_t  keylen;
1523     size_t  rlen = strlen(realm);
1524     char   *fname;
1525 
1526     if (!keyname)
1527 	keyname = KRB5_KDB_M_NAME;	/* XXX external? */
1528 
1529     keylen = strlen(keyname);
1530 
1531     fname = malloc(keylen + rlen + strlen(REALM_SEP_STRING) + 1);
1532     if (!fname)
1533 	return ENOMEM;
1534 
1535     strcpy(fname, keyname);
1536     (void)strcat(fname, REALM_SEP_STRING);
1537     (void)strcat(fname, realm);
1538 
1539     if ((retval = krb5_parse_name(context, fname, principal)))
1540 	return retval;
1541     if (fullname)
1542 	*fullname = fname;
1543     else
1544 	free(fname);
1545     return 0;
1546 }
1547 
1548 krb5_error_code
1549 krb5_dbe_lookup_last_pwd_change(context, entry, stamp)
1550     krb5_context context;
1551     krb5_db_entry *entry;
1552     krb5_timestamp *stamp;
1553 {
1554     krb5_tl_data tl_data;
1555     krb5_error_code code;
1556     krb5_int32 tmp;
1557 
1558     tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
1559 
1560     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
1561 	return (code);
1562 
1563     if (tl_data.tl_data_length != 4) {
1564 	*stamp = 0;
1565 	return (0);
1566     }
1567 
1568     krb5_kdb_decode_int32(tl_data.tl_data_contents, tmp);
1569 
1570     *stamp = (krb5_timestamp) tmp;
1571 
1572     return (0);
1573 }
1574 
1575 /*ARGSUSED*/
1576 krb5_error_code
1577 krb5_dbe_lookup_tl_data(context, entry, ret_tl_data)
1578     krb5_context context;
1579     krb5_db_entry *entry;
1580     krb5_tl_data *ret_tl_data;
1581 {
1582     krb5_tl_data *tl_data;
1583 
1584     for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) {
1585 	if (tl_data->tl_data_type == ret_tl_data->tl_data_type) {
1586 	    *ret_tl_data = *tl_data;
1587 	    return (0);
1588 	}
1589     }
1590 
1591     /* if the requested record isn't found, return zero bytes.
1592      * if it ever means something to have a zero-length tl_data,
1593      * this code and its callers will have to be changed */
1594 
1595     ret_tl_data->tl_data_length = 0;
1596     ret_tl_data->tl_data_contents = NULL;
1597     return (0);
1598 }
1599 
1600 krb5_error_code
1601 krb5_dbe_create_key_data(context, entry)
1602     krb5_context context;
1603     krb5_db_entry *entry;
1604 {
1605     if ((entry->key_data =
1606 	 (krb5_key_data *) krb5_db_alloc(context, entry->key_data,
1607 					 (sizeof(krb5_key_data) *
1608 					  (entry->n_key_data + 1)))) == NULL)
1609 	return (ENOMEM);
1610 
1611     memset(entry->key_data + entry->n_key_data, 0, sizeof(krb5_key_data));
1612     entry->n_key_data++;
1613 
1614     return 0;
1615 }
1616 
1617 krb5_error_code
1618 krb5_dbe_update_mod_princ_data(context, entry, mod_date, mod_princ)
1619     krb5_context context;
1620     krb5_db_entry *entry;
1621     krb5_timestamp mod_date;
1622     krb5_const_principal mod_princ;
1623 {
1624     krb5_tl_data tl_data;
1625 
1626     krb5_error_code retval = 0;
1627     krb5_octet *nextloc = 0;
1628     char   *unparse_mod_princ = 0;
1629     unsigned int unparse_mod_princ_size;
1630 
1631     if ((retval = krb5_unparse_name(context, mod_princ, &unparse_mod_princ)))
1632 	return (retval);
1633 
1634     unparse_mod_princ_size = strlen(unparse_mod_princ) + 1;
1635 
1636     if ((nextloc = (krb5_octet *) malloc(unparse_mod_princ_size + 4))
1637 	== NULL) {
1638 	free(unparse_mod_princ);
1639 	return (ENOMEM);
1640     }
1641 
1642     tl_data.tl_data_type = KRB5_TL_MOD_PRINC;
1643     tl_data.tl_data_length = unparse_mod_princ_size + 4;
1644     tl_data.tl_data_contents = nextloc;
1645 
1646     /* Mod Date */
1647     krb5_kdb_encode_int32(mod_date, nextloc);
1648 
1649     /* Mod Princ */
1650     memcpy(nextloc + 4, unparse_mod_princ, unparse_mod_princ_size);
1651 
1652     retval = krb5_dbe_update_tl_data(context, entry, &tl_data);
1653 
1654     free(unparse_mod_princ);
1655     free(nextloc);
1656 
1657     return (retval);
1658 }
1659 
1660 krb5_error_code
1661 krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ)
1662     krb5_context context;
1663     krb5_db_entry *entry;
1664     krb5_timestamp *mod_time;
1665     krb5_principal *mod_princ;
1666 {
1667     krb5_tl_data tl_data;
1668     krb5_error_code code;
1669 
1670     tl_data.tl_data_type = KRB5_TL_MOD_PRINC;
1671 
1672     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
1673 	return (code);
1674 
1675     if ((tl_data.tl_data_length < 5) ||
1676 	(tl_data.tl_data_contents[tl_data.tl_data_length - 1] != '\0'))
1677 	return (KRB5_KDB_TRUNCATED_RECORD);
1678 
1679     /* Mod Date */
1680     krb5_kdb_decode_int32(tl_data.tl_data_contents, *mod_time);
1681 
1682     /* Mod Princ */
1683     if ((code = krb5_parse_name(context,
1684 				(const char *) (tl_data.tl_data_contents + 4),
1685 				mod_princ)))
1686 	return (code);
1687 
1688     return (0);
1689 }
1690 
1691 krb5_error_code
1692 krb5_dbe_update_last_pwd_change(context, entry, stamp)
1693     krb5_context context;
1694     krb5_db_entry *entry;
1695     krb5_timestamp stamp;
1696 {
1697     krb5_tl_data tl_data;
1698     krb5_octet buf[4];		/* this is the encoded size of an int32 */
1699 
1700     tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
1701     tl_data.tl_data_length = sizeof(buf);
1702     krb5_kdb_encode_int32((krb5_int32) stamp, buf);
1703     tl_data.tl_data_contents = buf;
1704 
1705     return (krb5_dbe_update_tl_data(context, entry, &tl_data));
1706 }
1707 
1708 krb5_error_code
1709 krb5_dbe_update_tl_data(context, entry, new_tl_data)
1710     krb5_context context;
1711     krb5_db_entry *entry;
1712     krb5_tl_data *new_tl_data;
1713 {
1714     krb5_tl_data *tl_data = NULL;
1715     krb5_octet *tmp;
1716 
1717     /* copy the new data first, so we can fail cleanly if malloc()
1718      * fails */
1719     if ((tmp =
1720 	 (krb5_octet *) krb5_db_alloc(context, NULL,
1721 				      new_tl_data->tl_data_length)) == NULL)
1722 	return (ENOMEM);
1723 
1724     /* Find an existing entry of the specified type and point at
1725      * it, or NULL if not found */
1726 
1727     if (new_tl_data->tl_data_type != KRB5_TL_DB_ARGS) {	/* db_args can be multiple */
1728 	for (tl_data = entry->tl_data; tl_data;
1729 	     tl_data = tl_data->tl_data_next)
1730 	    if (tl_data->tl_data_type == new_tl_data->tl_data_type)
1731 		break;
1732     }
1733 
1734     /* if necessary, chain a new record in the beginning and point at it */
1735 
1736     if (!tl_data) {
1737 	if ((tl_data =
1738 	     (krb5_tl_data *) krb5_db_alloc(context, NULL,
1739 					    sizeof(krb5_tl_data)))
1740 	    == NULL) {
1741 	    free(tmp);
1742 	    return (ENOMEM);
1743 	}
1744 	memset(tl_data, 0, sizeof(krb5_tl_data));
1745 	tl_data->tl_data_next = entry->tl_data;
1746 	entry->tl_data = tl_data;
1747 	entry->n_tl_data++;
1748     }
1749 
1750     /* fill in the record */
1751 
1752     if (tl_data->tl_data_contents)
1753 	krb5_db_free(context, tl_data->tl_data_contents);
1754 
1755     tl_data->tl_data_type = new_tl_data->tl_data_type;
1756     tl_data->tl_data_length = new_tl_data->tl_data_length;
1757     tl_data->tl_data_contents = tmp;
1758     memcpy(tmp, new_tl_data->tl_data_contents, tl_data->tl_data_length);
1759 
1760     return (0);
1761 }
1762 
1763 /* change password functions */
1764 krb5_error_code
1765 krb5_dbe_cpw(krb5_context kcontext,
1766 	     krb5_keyblock * master_key,
1767 	     krb5_key_salt_tuple * ks_tuple,
1768 	     int ks_tuple_count,
1769 	     char *passwd,
1770 	     int new_kvno, krb5_boolean keepold, krb5_db_entry * db_entry)
1771 {
1772     krb5_error_code status = 0;
1773     kdb5_dal_handle *dal_handle;
1774 
1775     if (kcontext->db_context == NULL) {
1776 	status = kdb_setup_lib_handle(kcontext);
1777 	if (status) {
1778 	    goto clean_n_exit;
1779 	}
1780     }
1781 
1782     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1783     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1784     if (status) {
1785 	goto clean_n_exit;
1786     }
1787 
1788     status = dal_handle->lib_handle->vftabl.db_change_pwd(kcontext,
1789 							  master_key,
1790 							  ks_tuple,
1791 							  ks_tuple_count,
1792 							  passwd,
1793 							  new_kvno,
1794 							  keepold, db_entry);
1795     get_errmsg(kcontext, status);
1796     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1797 
1798   clean_n_exit:
1799     return status;
1800 }
1801 
1802 /* policy management functions */
1803 krb5_error_code
1804 krb5_db_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
1805 {
1806     krb5_error_code status = 0;
1807     kdb5_dal_handle *dal_handle;
1808 
1809     if (kcontext->db_context == NULL) {
1810 	status = kdb_setup_lib_handle(kcontext);
1811 	if (status) {
1812 	    goto clean_n_exit;
1813 	}
1814     }
1815 
1816     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1817     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1818     if (status) {
1819 	goto clean_n_exit;
1820     }
1821 
1822     status = dal_handle->lib_handle->vftabl.db_create_policy(kcontext, policy);
1823     get_errmsg(kcontext, status);
1824     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1825 
1826   clean_n_exit:
1827     return status;
1828 }
1829 
1830 krb5_error_code
1831 krb5_db_get_policy(krb5_context kcontext, char *name,
1832 		   osa_policy_ent_t * policy, int *cnt)
1833 {
1834     krb5_error_code status = 0;
1835     kdb5_dal_handle *dal_handle;
1836 
1837     if (kcontext->db_context == NULL) {
1838 	status = kdb_setup_lib_handle(kcontext);
1839 	if (status) {
1840 	    goto clean_n_exit;
1841 	}
1842     }
1843 
1844     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1845     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1846     if (status) {
1847 	goto clean_n_exit;
1848     }
1849 
1850     status =
1851 	dal_handle->lib_handle->vftabl.db_get_policy(kcontext, name, policy,
1852 						     cnt);
1853     get_errmsg(kcontext, status);
1854     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1855 
1856   clean_n_exit:
1857     return status;
1858 }
1859 
1860 krb5_error_code
1861 krb5_db_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
1862 {
1863     krb5_error_code status = 0;
1864     kdb5_dal_handle *dal_handle;
1865 
1866     if (kcontext->db_context == NULL) {
1867 	status = kdb_setup_lib_handle(kcontext);
1868 	if (status) {
1869 	    goto clean_n_exit;
1870 	}
1871     }
1872 
1873     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1874     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1875     if (status) {
1876 	goto clean_n_exit;
1877     }
1878 
1879     status = dal_handle->lib_handle->vftabl.db_put_policy(kcontext, policy);
1880     get_errmsg(kcontext, status);
1881     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1882 
1883   clean_n_exit:
1884     return status;
1885 }
1886 
1887 krb5_error_code
1888 krb5_db_iter_policy(krb5_context kcontext, char *match_entry,
1889 		    osa_adb_iter_policy_func func, void *data)
1890 {
1891     krb5_error_code status = 0;
1892     kdb5_dal_handle *dal_handle;
1893 
1894     if (kcontext->db_context == NULL) {
1895 	status = kdb_setup_lib_handle(kcontext);
1896 	if (status) {
1897 	    goto clean_n_exit;
1898 	}
1899     }
1900 
1901     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1902     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1903     if (status) {
1904 	goto clean_n_exit;
1905     }
1906 
1907     status =
1908 	dal_handle->lib_handle->vftabl.db_iter_policy(kcontext, match_entry,
1909 						      func, data);
1910     get_errmsg(kcontext, status);
1911     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1912 
1913   clean_n_exit:
1914     return status;
1915 }
1916 
1917 krb5_error_code
1918 krb5_db_delete_policy(krb5_context kcontext, char *policy)
1919 {
1920     krb5_error_code status = 0;
1921     kdb5_dal_handle *dal_handle;
1922 
1923     if (kcontext->db_context == NULL) {
1924 	status = kdb_setup_lib_handle(kcontext);
1925 	if (status) {
1926 	    goto clean_n_exit;
1927 	}
1928     }
1929 
1930     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1931     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1932     if (status) {
1933 	goto clean_n_exit;
1934     }
1935 
1936     status = dal_handle->lib_handle->vftabl.db_delete_policy(kcontext, policy);
1937     get_errmsg(kcontext, status);
1938     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1939 
1940   clean_n_exit:
1941     return status;
1942 }
1943 
1944 void
1945 krb5_db_free_policy(krb5_context kcontext, osa_policy_ent_t policy)
1946 {
1947     krb5_error_code status = 0;
1948     kdb5_dal_handle *dal_handle;
1949 
1950     if (kcontext->db_context == NULL) {
1951 	status = kdb_setup_lib_handle(kcontext);
1952 	if (status) {
1953 	    goto clean_n_exit;
1954 	}
1955     }
1956 
1957     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1958     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1959     if (status) {
1960 	goto clean_n_exit;
1961     }
1962 
1963     dal_handle->lib_handle->vftabl.db_free_policy(kcontext, policy);
1964     get_errmsg(kcontext, status);
1965     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1966 
1967   clean_n_exit:
1968     return;
1969 }
1970 
1971 krb5_error_code
1972 krb5_db_promote(krb5_context kcontext, char **db_args)
1973 {
1974     krb5_error_code status = 0;
1975     char   *section = NULL;
1976     kdb5_dal_handle *dal_handle;
1977 
1978     section = kdb_get_conf_section(kcontext);
1979     if (section == NULL) {
1980 	status = KRB5_KDB_SERVER_INTERNAL_ERR;
1981 	krb5_set_error_message (kcontext, status,
1982 		gettext("unable to determine configuration section for realm %s\n"),
1983 		kcontext->default_realm);
1984 	goto clean_n_exit;
1985     }
1986 
1987     if (kcontext->db_context == NULL) {
1988 	status = kdb_setup_lib_handle(kcontext);
1989 	if (status) {
1990 	    goto clean_n_exit;
1991 	}
1992     }
1993 
1994     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1995     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1996     if (status) {
1997 	goto clean_n_exit;
1998     }
1999 
2000     status =
2001 	dal_handle->lib_handle->vftabl.promote_db(kcontext, section, db_args);
2002     get_errmsg(kcontext, status);
2003     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2004 
2005   clean_n_exit:
2006     if (section)
2007 	free(section);
2008     return status;
2009 }
2010 
2011 /*
2012  * Solaris Kerberos: support for iprop
2013  *
2014  * Not all KDB plugins support iprop.
2015  *
2016  * sets iprop_supported to 1 if iprop supportd, 0 otherwise.
2017  */
2018 krb5_error_code
2019 krb5_db_supports_iprop(krb5_context kcontext, int *iprop_supported)
2020 {
2021     krb5_error_code status = 0;
2022     kdb5_dal_handle *dal_handle;
2023 
2024     if (kcontext->db_context == NULL) {
2025 	status = kdb_setup_lib_handle(kcontext);
2026 	if (status) {
2027 	    goto clean_n_exit;
2028 	}
2029     }
2030 
2031     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
2032     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2033     if (status) {
2034 	goto clean_n_exit;
2035     }
2036 
2037     *iprop_supported = dal_handle->lib_handle->vftabl.iprop_supported;
2038     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2039 
2040   clean_n_exit:
2041     return status;
2042 }
2043