xref: /illumos-gate/usr/src/lib/krb5/kdb/kdb5.c (revision 37e2cd25d56b334a2403f2540a0b0a1e6a40bcd1)
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 	/* Solaris Kerberos */
635 	kdb_free_lib_handle(kcontext);
636 	goto clean_n_exit;
637     }
638 
639     status =
640 	dal_handle->lib_handle->vftabl.init_module(kcontext, section, db_args,
641 						   mode);
642     get_errmsg(kcontext, status);
643 
644     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
645 
646     /* Solaris Kerberos */
647     if (status)
648 	kdb_free_lib_handle(kcontext);
649 
650   clean_n_exit:
651     if (section)
652 	free(section);
653     return status;
654 }
655 
656 krb5_error_code
657 krb5_db_inited(krb5_context kcontext)
658 {
659     return !(kcontext && kcontext->db_context &&
660 	     ((kdb5_dal_handle *) kcontext->db_context)->db_context);
661 }
662 
663 krb5_error_code
664 krb5_db_create(krb5_context kcontext, char **db_args)
665 {
666     krb5_error_code status = 0;
667     char   *section = NULL;
668     kdb5_dal_handle *dal_handle;
669 
670     section = kdb_get_conf_section(kcontext);
671     if (section == NULL) {
672 	status = KRB5_KDB_SERVER_INTERNAL_ERR;
673 	krb5_set_error_message (kcontext, status,
674 		gettext("unable to determine configuration section for realm %s\n"),
675 		kcontext->default_realm);
676 	goto clean_n_exit;
677     }
678 
679     if (kcontext->db_context == NULL) {
680 	status = kdb_setup_lib_handle(kcontext);
681 	if (status) {
682 	    goto clean_n_exit;
683 	}
684     }
685 
686     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
687     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
688     if (status) {
689 	goto clean_n_exit;
690     }
691 
692     status =
693 	dal_handle->lib_handle->vftabl.db_create(kcontext, section, db_args);
694     get_errmsg(kcontext, status);
695 
696     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
697 
698   clean_n_exit:
699     if (section)
700 	free(section);
701     return status;
702 }
703 
704 krb5_error_code
705 krb5_db_fini(krb5_context kcontext)
706 {
707     krb5_error_code status = 0;
708     kdb5_dal_handle *dal_handle;
709 
710     if (kcontext->db_context == NULL) {
711 	/* module not loaded. So nothing to be done */
712 	goto clean_n_exit;
713     }
714 
715     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
716     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
717     if (status) {
718 	goto clean_n_exit;
719     }
720 
721     status = dal_handle->lib_handle->vftabl.fini_module(kcontext);
722     get_errmsg(kcontext, status);
723 
724     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
725 
726     if (status) {
727 	goto clean_n_exit;
728     }
729 
730     status = kdb_free_lib_handle(kcontext);
731 
732   clean_n_exit:
733     return status;
734 }
735 
736 krb5_error_code
737 krb5_db_destroy(krb5_context kcontext, char **db_args)
738 {
739     krb5_error_code status = 0;
740     char   *section = NULL;
741     kdb5_dal_handle *dal_handle;
742 
743     section = kdb_get_conf_section(kcontext);
744     if (section == NULL) {
745 	status = KRB5_KDB_SERVER_INTERNAL_ERR;
746 	krb5_set_error_message (kcontext, status,
747 		gettext("unable to determine configuration section for realm %s\n"),
748 		kcontext->default_realm);
749 	goto clean_n_exit;
750     }
751 
752     if (kcontext->db_context == NULL) {
753 	status = kdb_setup_lib_handle(kcontext);
754 	if (status) {
755 	    goto clean_n_exit;
756 	}
757     }
758 
759     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
760     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
761     if (status) {
762 	goto clean_n_exit;
763     }
764 
765     status =
766 	dal_handle->lib_handle->vftabl.db_destroy(kcontext, section, db_args);
767     get_errmsg(kcontext, status);
768     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
769 
770   clean_n_exit:
771     if (section)
772 	free(section);
773     return status;
774 }
775 
776 krb5_error_code
777 krb5_db_get_age(krb5_context kcontext, char *db_name, time_t * t)
778 {
779     krb5_error_code status = 0;
780     kdb5_dal_handle *dal_handle;
781 
782     if (kcontext->db_context == NULL) {
783 	status = kdb_setup_lib_handle(kcontext);
784 	if (status) {
785 	    goto clean_n_exit;
786 	}
787     }
788 
789     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
790     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
791     if (status) {
792 	goto clean_n_exit;
793     }
794 
795     status = dal_handle->lib_handle->vftabl.db_get_age(kcontext, db_name, t);
796     get_errmsg(kcontext, status);
797     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
798 
799   clean_n_exit:
800     return status;
801 }
802 
803 krb5_error_code
804 krb5_db_set_option(krb5_context kcontext, int option, void *value)
805 {
806     krb5_error_code status = 0;
807     kdb5_dal_handle *dal_handle;
808 
809     if (kcontext->db_context == NULL) {
810 	status = kdb_setup_lib_handle(kcontext);
811 	if (status) {
812 	    goto clean_n_exit;
813 	}
814     }
815 
816     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
817     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
818     if (status) {
819 	goto clean_n_exit;
820     }
821 
822     status =
823 	dal_handle->lib_handle->vftabl.db_set_option(kcontext, option, value);
824     get_errmsg(kcontext, status);
825     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
826 
827   clean_n_exit:
828     return status;
829 }
830 
831 krb5_error_code
832 krb5_db_lock(krb5_context kcontext, int lock_mode)
833 {
834     krb5_error_code status = 0;
835     kdb5_dal_handle *dal_handle;
836 
837     if (kcontext->db_context == NULL) {
838 	status = kdb_setup_lib_handle(kcontext);
839 	if (status) {
840 	    goto clean_n_exit;
841 	}
842     }
843 
844     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
845     /* acquire an exclusive lock, ensures no other thread uses this context */
846     status = kdb_lock_lib_lock(dal_handle->lib_handle, TRUE);
847     if (status) {
848 	goto clean_n_exit;
849     }
850 
851     status = dal_handle->lib_handle->vftabl.db_lock(kcontext, lock_mode);
852     get_errmsg(kcontext, status);
853 
854     /* exclusive lock is still held, so no other thread could use this context */
855     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
856 
857   clean_n_exit:
858     return status;
859 }
860 
861 krb5_error_code
862 krb5_db_unlock(krb5_context kcontext)
863 {
864     krb5_error_code status = 0;
865     kdb5_dal_handle *dal_handle;
866 
867     if (kcontext->db_context == NULL) {
868 	status = kdb_setup_lib_handle(kcontext);
869 	if (status) {
870 	    goto clean_n_exit;
871 	}
872     }
873 
874     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
875     /* normal lock acquired and exclusive lock released */
876     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
877     if (status) {
878 	goto clean_n_exit;
879     }
880 
881     status = dal_handle->lib_handle->vftabl.db_unlock(kcontext);
882     get_errmsg(kcontext, status);
883 
884     kdb_unlock_lib_lock(dal_handle->lib_handle, TRUE);
885 
886   clean_n_exit:
887     return status;
888 }
889 
890 krb5_error_code
891 krb5_db_get_principal(krb5_context kcontext,
892 		      krb5_const_principal search_for,
893 		      krb5_db_entry * entries,
894 		      int *nentries, krb5_boolean * more)
895 {
896     krb5_error_code status = 0;
897     kdb5_dal_handle *dal_handle;
898 
899     if (kcontext->db_context == NULL) {
900 	status = kdb_setup_lib_handle(kcontext);
901 	if (status) {
902 	    goto clean_n_exit;
903 	}
904     }
905 
906     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
907     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
908     if (status) {
909 	goto clean_n_exit;
910     }
911 
912     status =
913 	dal_handle->lib_handle->vftabl.db_get_principal(kcontext, search_for,
914 							entries, nentries,
915 							more);
916     get_errmsg(kcontext, status);
917     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
918 
919   clean_n_exit:
920     return status;
921 }
922 
923 krb5_error_code
924 krb5_db_get_principal_nolock(krb5_context kcontext,
925 		      krb5_const_principal search_for,
926 		      krb5_db_entry * entries,
927 		      int *nentries, krb5_boolean * more)
928 {
929     krb5_error_code status = 0;
930     kdb5_dal_handle *dal_handle;
931 
932     if (kcontext->db_context == NULL) {
933 	status = kdb_setup_lib_handle(kcontext);
934 	if (status) {
935 	    goto clean_n_exit;
936 	}
937     }
938 
939     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
940     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
941     if (status) {
942 	goto clean_n_exit;
943     }
944 
945     status =
946 	dal_handle->lib_handle->vftabl.db_get_principal_nolock(kcontext,
947 							search_for,
948 							entries, nentries,
949 							more);
950     get_errmsg(kcontext, status);
951     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
952 
953   clean_n_exit:
954     return status;
955 }
956 
957 krb5_error_code
958 krb5_db_free_principal(krb5_context kcontext, krb5_db_entry * entry, int count)
959 {
960     krb5_error_code status = 0;
961     kdb5_dal_handle *dal_handle;
962 
963     if (kcontext->db_context == NULL) {
964 	status = kdb_setup_lib_handle(kcontext);
965 	if (status) {
966 	    goto clean_n_exit;
967 	}
968     }
969 
970     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
971     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
972     if (status) {
973 	goto clean_n_exit;
974     }
975 
976     status =
977 	dal_handle->lib_handle->vftabl.db_free_principal(kcontext, entry,
978 							 count);
979     get_errmsg(kcontext, status);
980     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
981 
982   clean_n_exit:
983     return status;
984 }
985 
986 krb5_error_code
987 krb5_db_put_principal(krb5_context kcontext,
988 		      krb5_db_entry * entries, int *nentries)
989 {
990     krb5_error_code status = 0;
991     kdb5_dal_handle *dal_handle;
992     char  **db_args = NULL;
993     krb5_tl_data *prev, *curr, *next;
994     int     db_args_size = 0;
995 
996     if (kcontext->db_context == NULL) {
997 	status = kdb_setup_lib_handle(kcontext);
998 	if (status) {
999 	    goto clean_n_exit;
1000 	}
1001     }
1002 
1003     /* Giving db_args as part of tl data causes, db2 to store the
1004        tl_data as such.  To prevent this, tl_data is collated and
1005        passed as a sepearte argument. Currently supports only one
1006        principal.  but passing it as a seperate argument makes it
1007        difficult for kadmin remote to pass arguments to server.  */
1008     prev = NULL, curr = entries->tl_data;
1009     while (curr) {
1010 	if (curr->tl_data_type == KRB5_TL_DB_ARGS) {
1011 	    char  **t;
1012 	    /* Since this is expected to be NULL terminated string and
1013 	       this could come from any client, do a check before
1014 	       passing it to db.  */
1015 	    if (((char *) curr->tl_data_contents)[curr->tl_data_length - 1] !=
1016 		'\0') {
1017 		/* not null terminated. Dangerous input */
1018 		status = EINVAL;
1019 		goto clean_n_exit;
1020 	    }
1021 
1022 	    db_args_size++;
1023 	    t = realloc(db_args, sizeof(char *) * (db_args_size + 1));	/* 1 for NULL */
1024 	    if (t == NULL) {
1025 		status = ENOMEM;
1026 		goto clean_n_exit;
1027 	    }
1028 
1029 	    db_args = t;
1030 	    db_args[db_args_size - 1] = (char *) curr->tl_data_contents;
1031 	    db_args[db_args_size] = NULL;
1032 
1033 	    next = curr->tl_data_next;
1034 	    if (prev == NULL) {
1035 		/* current node is the first in the linked list. remove it */
1036 		entries->tl_data = curr->tl_data_next;
1037 	    } else {
1038 		prev->tl_data_next = curr->tl_data_next;
1039 	    }
1040 	    entries->n_tl_data--;
1041 	    krb5_db_free(kcontext, curr);
1042 
1043 	    /* previous does not change */
1044 	    curr = next;
1045 	} else {
1046 	    prev = curr;
1047 	    curr = curr->tl_data_next;
1048 	}
1049     }
1050 
1051     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1052     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1053     if (status) {
1054 	goto clean_n_exit;
1055     }
1056 
1057     status = dal_handle->lib_handle->vftabl.db_put_principal(kcontext, entries,
1058 							     nentries,
1059 							     db_args);
1060     get_errmsg(kcontext, status);
1061     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1062 
1063   clean_n_exit:
1064     while (db_args_size) {
1065 	if (db_args[db_args_size - 1])
1066 	    krb5_db_free(kcontext, db_args[db_args_size - 1]);
1067 
1068 	db_args_size--;
1069     }
1070 
1071     if (db_args)
1072 	free(db_args);
1073 
1074     return status;
1075 }
1076 
1077 krb5_error_code
1078 krb5_db_delete_principal(krb5_context kcontext,
1079 			 krb5_principal search_for, int *nentries)
1080 {
1081     krb5_error_code status = 0;
1082     kdb5_dal_handle *dal_handle;
1083 
1084     if (kcontext->db_context == NULL) {
1085 	status = kdb_setup_lib_handle(kcontext);
1086 	if (status) {
1087 	    goto clean_n_exit;
1088 	}
1089     }
1090 
1091     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1092     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1093     if (status) {
1094 	goto clean_n_exit;
1095     }
1096 
1097     status =
1098 	dal_handle->lib_handle->vftabl.db_delete_principal(kcontext,
1099 							   search_for,
1100 							   nentries);
1101     get_errmsg(kcontext, status);
1102     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1103 
1104   clean_n_exit:
1105     return status;
1106 }
1107 
1108 krb5_error_code
1109 krb5_db_iterate(krb5_context kcontext,
1110 		char *match_entry,
1111 		int (*func) (krb5_pointer, krb5_db_entry *),
1112 		krb5_pointer func_arg,
1113 		/* Solaris Kerberos: adding support for db_args */
1114 		char **db_args)
1115 {
1116     krb5_error_code status = 0;
1117     kdb5_dal_handle *dal_handle;
1118 
1119     if (kcontext->db_context == NULL) {
1120 	status = kdb_setup_lib_handle(kcontext);
1121 	if (status) {
1122 	    goto clean_n_exit;
1123 	}
1124     }
1125 
1126     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1127     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1128     if (status) {
1129 	goto clean_n_exit;
1130     }
1131 
1132     /* Solaris Kerberos: adding support for db_args */
1133     status = dal_handle->lib_handle->vftabl.db_iterate(kcontext,
1134 						       match_entry,
1135 						       func, func_arg,
1136 						       db_args);
1137     get_errmsg(kcontext, status);
1138     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1139 
1140   clean_n_exit:
1141     return status;
1142 }
1143 
1144 krb5_error_code
1145 krb5_supported_realms(krb5_context kcontext, char **realms)
1146 {
1147     krb5_error_code status = 0;
1148     kdb5_dal_handle *dal_handle;
1149 
1150     if (kcontext->db_context == NULL) {
1151 	status = kdb_setup_lib_handle(kcontext);
1152 	if (status) {
1153 	    goto clean_n_exit;
1154 	}
1155     }
1156 
1157     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1158     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1159     if (status) {
1160 	goto clean_n_exit;
1161     }
1162 
1163     status =
1164 	dal_handle->lib_handle->vftabl.db_supported_realms(kcontext, realms);
1165     get_errmsg(kcontext, status);
1166     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1167 
1168   clean_n_exit:
1169     return status;
1170 }
1171 
1172 krb5_error_code
1173 krb5_free_supported_realms(krb5_context kcontext, char **realms)
1174 {
1175     krb5_error_code status = 0;
1176     kdb5_dal_handle *dal_handle;
1177 
1178     if (kcontext->db_context == NULL) {
1179 	status = kdb_setup_lib_handle(kcontext);
1180 	if (status) {
1181 	    goto clean_n_exit;
1182 	}
1183     }
1184 
1185     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1186     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1187     if (status) {
1188 	goto clean_n_exit;
1189     }
1190 
1191     status =
1192 	dal_handle->lib_handle->vftabl.db_free_supported_realms(kcontext,
1193 								realms);
1194     get_errmsg(kcontext, status);
1195     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1196 
1197   clean_n_exit:
1198     return status;
1199 }
1200 
1201 krb5_error_code
1202 krb5_db_set_master_key_ext(krb5_context kcontext,
1203 			   char *pwd, krb5_keyblock * key)
1204 {
1205     krb5_error_code status = 0;
1206     kdb5_dal_handle *dal_handle;
1207 
1208     if (kcontext->db_context == NULL) {
1209 	status = kdb_setup_lib_handle(kcontext);
1210 	if (status) {
1211 	    goto clean_n_exit;
1212 	}
1213     }
1214 
1215     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1216     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1217     if (status) {
1218 	goto clean_n_exit;
1219     }
1220 
1221     status = dal_handle->lib_handle->vftabl.set_master_key(kcontext, pwd, key);
1222     get_errmsg(kcontext, status);
1223 
1224     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1225 
1226   clean_n_exit:
1227     return status;
1228 }
1229 
1230 krb5_error_code
1231 krb5_db_set_mkey(krb5_context context, krb5_keyblock * key)
1232 {
1233     return krb5_db_set_master_key_ext(context, NULL, key);
1234 }
1235 
1236 krb5_error_code
1237 krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key)
1238 {
1239     krb5_error_code status = 0;
1240     kdb5_dal_handle *dal_handle;
1241 
1242     if (kcontext->db_context == NULL) {
1243 	status = kdb_setup_lib_handle(kcontext);
1244 	if (status) {
1245 	    goto clean_n_exit;
1246 	}
1247     }
1248 
1249     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1250     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1251     if (status) {
1252 	goto clean_n_exit;
1253     }
1254 
1255     /* Lets use temp key and copy it later to avoid memory problems
1256        when freed by the caller.  */
1257     status = dal_handle->lib_handle->vftabl.get_master_key(kcontext, key);
1258     get_errmsg(kcontext, status);
1259     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1260 
1261   clean_n_exit:
1262     return status;
1263 }
1264 
1265 krb5_error_code
1266 krb5_db_store_master_key(krb5_context kcontext,
1267 			 char *db_arg,
1268 			 krb5_principal mname,
1269 			 krb5_keyblock * key, char *master_pwd)
1270 {
1271     krb5_error_code status = 0;
1272     kdb5_dal_handle *dal_handle;
1273 
1274     if (kcontext->db_context == NULL) {
1275 	status = kdb_setup_lib_handle(kcontext);
1276 	if (status) {
1277 	    goto clean_n_exit;
1278 	}
1279     }
1280 
1281     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1282     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1283     if (status) {
1284 	goto clean_n_exit;
1285     }
1286 
1287     status = dal_handle->lib_handle->vftabl.store_master_key(kcontext,
1288 							     db_arg,
1289 							     mname,
1290 							     key, master_pwd);
1291     get_errmsg(kcontext, status);
1292     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1293 
1294   clean_n_exit:
1295     return status;
1296 }
1297 
1298 char   *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1;
1299 char   *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2;
1300 
1301 krb5_error_code
1302 krb5_db_fetch_mkey(krb5_context context,
1303 		   krb5_principal mname,
1304 		   krb5_enctype etype,
1305 		   krb5_boolean fromkeyboard,
1306 		   krb5_boolean twice,
1307 		   char *db_args, krb5_data * salt, krb5_keyblock * key)
1308 {
1309     krb5_error_code retval;
1310     char    password[BUFSIZ];
1311     krb5_data pwd;
1312     unsigned int size = sizeof(password);
1313     int     kvno;
1314     krb5_keyblock tmp_key;
1315 
1316     memset(&tmp_key, 0, sizeof(tmp_key));
1317 
1318     if (fromkeyboard) {
1319 	krb5_data scratch;
1320 
1321 	if ((retval = krb5_read_password(context, krb5_mkey_pwd_prompt1,
1322 					 twice ? krb5_mkey_pwd_prompt2 : 0,
1323 					 password, &size))) {
1324 	    goto clean_n_exit;
1325 	}
1326 
1327 	pwd.data = password;
1328 	pwd.length = size;
1329 	if (!salt) {
1330 	    retval = krb5_principal2salt(context, mname, &scratch);
1331 	    if (retval)
1332 		goto clean_n_exit;
1333 	}
1334 	retval =
1335 	    krb5_c_string_to_key(context, etype, &pwd, salt ? salt : &scratch,
1336 				 key);
1337 
1338 	if (!salt)
1339 	    krb5_xfree(scratch.data);
1340 	memset(password, 0, sizeof(password));	/* erase it */
1341 
1342     } else {
1343 	kdb5_dal_handle *dal_handle;
1344 
1345 	if (context->db_context == NULL) {
1346 	    retval = kdb_setup_lib_handle(context);
1347 	    if (retval) {
1348 		goto clean_n_exit;
1349 	    }
1350 	}
1351 
1352 	dal_handle = (kdb5_dal_handle *) context->db_context;
1353 	retval = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1354 	if (retval) {
1355 	    goto clean_n_exit;
1356 	}
1357 #if 0 /************** Begin IFDEF'ed OUT *******************************/
1358 	/* Orig MIT */
1359 	tmp_key.enctype = key->enctype;
1360 #else
1361 	/* Solaris Kerberos: need to use etype */
1362 	tmp_key.enctype = etype;
1363 #endif /**************** END IFDEF'ed OUT *******************************/
1364 	retval = dal_handle->lib_handle->vftabl.fetch_master_key(context,
1365 								 mname,
1366 								 &tmp_key,
1367 								 &kvno,
1368 								 db_args);
1369 	get_errmsg(context, retval);
1370 	kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1371 
1372 	if (retval) {
1373 	    goto clean_n_exit;
1374 	}
1375 
1376 	key->contents = malloc(tmp_key.length);
1377 	if (key->contents == NULL) {
1378 	    retval = ENOMEM;
1379 	    goto clean_n_exit;
1380 	}
1381 
1382 	key->magic = tmp_key.magic;
1383 	key->enctype = tmp_key.enctype;
1384 	key->length = tmp_key.length;
1385 	memcpy(key->contents, tmp_key.contents, tmp_key.length);
1386     }
1387 
1388   clean_n_exit:
1389     if (tmp_key.contents) {
1390 	memset(tmp_key.contents, 0, tmp_key.length);
1391 	krb5_db_free(context, tmp_key.contents);
1392     }
1393     return retval;
1394 }
1395 
1396 krb5_error_code
1397 krb5_db_verify_master_key(krb5_context kcontext,
1398 			  krb5_principal mprinc, krb5_keyblock * mkey)
1399 {
1400     krb5_error_code status = 0;
1401     kdb5_dal_handle *dal_handle;
1402 
1403     if (kcontext->db_context == NULL) {
1404 	status = kdb_setup_lib_handle(kcontext);
1405 	if (status) {
1406 	    goto clean_n_exit;
1407 	}
1408     }
1409 
1410     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1411     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1412     if (status) {
1413 	goto clean_n_exit;
1414     }
1415 
1416     status = dal_handle->lib_handle->vftabl.verify_master_key(kcontext,
1417 							      mprinc, mkey);
1418     get_errmsg(kcontext, status);
1419     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1420 
1421   clean_n_exit:
1422     return status;
1423 }
1424 
1425 void   *
1426 krb5_db_alloc(krb5_context kcontext, void *ptr, size_t size)
1427 {
1428     krb5_error_code status;
1429     kdb5_dal_handle *dal_handle;
1430     void   *new_ptr = NULL;
1431 
1432     if (kcontext->db_context == NULL) {
1433 	status = kdb_setup_lib_handle(kcontext);
1434 	if (status) {
1435 	    goto clean_n_exit;
1436 	}
1437     }
1438 
1439     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1440 
1441     new_ptr = dal_handle->lib_handle->vftabl.db_alloc(kcontext, ptr, size);
1442 
1443   clean_n_exit:
1444     return new_ptr;
1445 }
1446 
1447 void
1448 krb5_db_free(krb5_context kcontext, void *ptr)
1449 {
1450     krb5_error_code status;
1451     kdb5_dal_handle *dal_handle;
1452 
1453     if (kcontext->db_context == NULL) {
1454 	status = kdb_setup_lib_handle(kcontext);
1455 	if (status) {
1456 	    goto clean_n_exit;
1457 	}
1458     }
1459 
1460     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1461 
1462     dal_handle->lib_handle->vftabl.db_free(kcontext, ptr);
1463 
1464   clean_n_exit:
1465     return;
1466 }
1467 
1468 /* has to be modified */
1469 
1470 krb5_error_code
1471 krb5_dbe_find_enctype(krb5_context kcontext,
1472 		      krb5_db_entry * dbentp,
1473 		      krb5_int32 ktype,
1474 		      krb5_int32 stype,
1475 		      krb5_int32 kvno, krb5_key_data ** kdatap)
1476 {
1477     krb5_int32 start = 0;
1478     return krb5_dbe_search_enctype(kcontext, dbentp, &start, ktype, stype,
1479 				   kvno, kdatap);
1480 }
1481 
1482 krb5_error_code
1483 krb5_dbe_search_enctype(krb5_context kcontext,
1484 			krb5_db_entry * dbentp,
1485 			krb5_int32 * start,
1486 			krb5_int32 ktype,
1487 			krb5_int32 stype,
1488 			krb5_int32 kvno, krb5_key_data ** kdatap)
1489 {
1490     krb5_error_code status = 0;
1491     kdb5_dal_handle *dal_handle;
1492 
1493     if (kcontext->db_context == NULL) {
1494 	status = kdb_setup_lib_handle(kcontext);
1495 	if (status) {
1496 	    goto clean_n_exit;
1497 	}
1498     }
1499 
1500     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1501     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1502     if (status) {
1503 	goto clean_n_exit;
1504     }
1505 
1506     status = dal_handle->lib_handle->vftabl.dbe_search_enctype(kcontext,
1507 							       dbentp,
1508 							       start,
1509 							       ktype,
1510 							       stype,
1511 							       kvno, kdatap);
1512     get_errmsg(kcontext, status);
1513     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1514 
1515   clean_n_exit:
1516     return status;
1517 }
1518 
1519 #define	REALM_SEP_STRING	"@"
1520 
1521 krb5_error_code
1522 krb5_db_setup_mkey_name(krb5_context context,
1523 			const char *keyname,
1524 			const char *realm,
1525 			char **fullname, krb5_principal * principal)
1526 {
1527     krb5_error_code retval;
1528     size_t  keylen;
1529     size_t  rlen = strlen(realm);
1530     char   *fname;
1531 
1532     if (!keyname)
1533 	keyname = KRB5_KDB_M_NAME;	/* XXX external? */
1534 
1535     keylen = strlen(keyname);
1536 
1537     fname = malloc(keylen + rlen + strlen(REALM_SEP_STRING) + 1);
1538     if (!fname)
1539 	return ENOMEM;
1540 
1541     strcpy(fname, keyname);
1542     (void)strcat(fname, REALM_SEP_STRING);
1543     (void)strcat(fname, realm);
1544 
1545     if ((retval = krb5_parse_name(context, fname, principal)))
1546 	return retval;
1547     if (fullname)
1548 	*fullname = fname;
1549     else
1550 	free(fname);
1551     return 0;
1552 }
1553 
1554 krb5_error_code
1555 krb5_dbe_lookup_last_pwd_change(context, entry, stamp)
1556     krb5_context context;
1557     krb5_db_entry *entry;
1558     krb5_timestamp *stamp;
1559 {
1560     krb5_tl_data tl_data;
1561     krb5_error_code code;
1562     krb5_int32 tmp;
1563 
1564     tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
1565 
1566     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
1567 	return (code);
1568 
1569     if (tl_data.tl_data_length != 4) {
1570 	*stamp = 0;
1571 	return (0);
1572     }
1573 
1574     krb5_kdb_decode_int32(tl_data.tl_data_contents, tmp);
1575 
1576     *stamp = (krb5_timestamp) tmp;
1577 
1578     return (0);
1579 }
1580 
1581 /*ARGSUSED*/
1582 krb5_error_code
1583 krb5_dbe_lookup_tl_data(context, entry, ret_tl_data)
1584     krb5_context context;
1585     krb5_db_entry *entry;
1586     krb5_tl_data *ret_tl_data;
1587 {
1588     krb5_tl_data *tl_data;
1589 
1590     for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) {
1591 	if (tl_data->tl_data_type == ret_tl_data->tl_data_type) {
1592 	    *ret_tl_data = *tl_data;
1593 	    return (0);
1594 	}
1595     }
1596 
1597     /* if the requested record isn't found, return zero bytes.
1598      * if it ever means something to have a zero-length tl_data,
1599      * this code and its callers will have to be changed */
1600 
1601     ret_tl_data->tl_data_length = 0;
1602     ret_tl_data->tl_data_contents = NULL;
1603     return (0);
1604 }
1605 
1606 krb5_error_code
1607 krb5_dbe_create_key_data(context, entry)
1608     krb5_context context;
1609     krb5_db_entry *entry;
1610 {
1611     if ((entry->key_data =
1612 	 (krb5_key_data *) krb5_db_alloc(context, entry->key_data,
1613 					 (sizeof(krb5_key_data) *
1614 					  (entry->n_key_data + 1)))) == NULL)
1615 	return (ENOMEM);
1616 
1617     memset(entry->key_data + entry->n_key_data, 0, sizeof(krb5_key_data));
1618     entry->n_key_data++;
1619 
1620     return 0;
1621 }
1622 
1623 krb5_error_code
1624 krb5_dbe_update_mod_princ_data(context, entry, mod_date, mod_princ)
1625     krb5_context context;
1626     krb5_db_entry *entry;
1627     krb5_timestamp mod_date;
1628     krb5_const_principal mod_princ;
1629 {
1630     krb5_tl_data tl_data;
1631 
1632     krb5_error_code retval = 0;
1633     krb5_octet *nextloc = 0;
1634     char   *unparse_mod_princ = 0;
1635     unsigned int unparse_mod_princ_size;
1636 
1637     if ((retval = krb5_unparse_name(context, mod_princ, &unparse_mod_princ)))
1638 	return (retval);
1639 
1640     unparse_mod_princ_size = strlen(unparse_mod_princ) + 1;
1641 
1642     if ((nextloc = (krb5_octet *) malloc(unparse_mod_princ_size + 4))
1643 	== NULL) {
1644 	free(unparse_mod_princ);
1645 	return (ENOMEM);
1646     }
1647 
1648     tl_data.tl_data_type = KRB5_TL_MOD_PRINC;
1649     tl_data.tl_data_length = unparse_mod_princ_size + 4;
1650     tl_data.tl_data_contents = nextloc;
1651 
1652     /* Mod Date */
1653     krb5_kdb_encode_int32(mod_date, nextloc);
1654 
1655     /* Mod Princ */
1656     memcpy(nextloc + 4, unparse_mod_princ, unparse_mod_princ_size);
1657 
1658     retval = krb5_dbe_update_tl_data(context, entry, &tl_data);
1659 
1660     free(unparse_mod_princ);
1661     free(nextloc);
1662 
1663     return (retval);
1664 }
1665 
1666 krb5_error_code
1667 krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ)
1668     krb5_context context;
1669     krb5_db_entry *entry;
1670     krb5_timestamp *mod_time;
1671     krb5_principal *mod_princ;
1672 {
1673     krb5_tl_data tl_data;
1674     krb5_error_code code;
1675 
1676     tl_data.tl_data_type = KRB5_TL_MOD_PRINC;
1677 
1678     if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
1679 	return (code);
1680 
1681     if ((tl_data.tl_data_length < 5) ||
1682 	(tl_data.tl_data_contents[tl_data.tl_data_length - 1] != '\0'))
1683 	return (KRB5_KDB_TRUNCATED_RECORD);
1684 
1685     /* Mod Date */
1686     krb5_kdb_decode_int32(tl_data.tl_data_contents, *mod_time);
1687 
1688     /* Mod Princ */
1689     if ((code = krb5_parse_name(context,
1690 				(const char *) (tl_data.tl_data_contents + 4),
1691 				mod_princ)))
1692 	return (code);
1693 
1694     return (0);
1695 }
1696 
1697 krb5_error_code
1698 krb5_dbe_update_last_pwd_change(context, entry, stamp)
1699     krb5_context context;
1700     krb5_db_entry *entry;
1701     krb5_timestamp stamp;
1702 {
1703     krb5_tl_data tl_data;
1704     krb5_octet buf[4];		/* this is the encoded size of an int32 */
1705 
1706     tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
1707     tl_data.tl_data_length = sizeof(buf);
1708     krb5_kdb_encode_int32((krb5_int32) stamp, buf);
1709     tl_data.tl_data_contents = buf;
1710 
1711     return (krb5_dbe_update_tl_data(context, entry, &tl_data));
1712 }
1713 
1714 krb5_error_code
1715 krb5_dbe_update_tl_data(context, entry, new_tl_data)
1716     krb5_context context;
1717     krb5_db_entry *entry;
1718     krb5_tl_data *new_tl_data;
1719 {
1720     krb5_tl_data *tl_data = NULL;
1721     krb5_octet *tmp;
1722 
1723     /* copy the new data first, so we can fail cleanly if malloc()
1724      * fails */
1725     if ((tmp =
1726 	 (krb5_octet *) krb5_db_alloc(context, NULL,
1727 				      new_tl_data->tl_data_length)) == NULL)
1728 	return (ENOMEM);
1729 
1730     /* Find an existing entry of the specified type and point at
1731      * it, or NULL if not found */
1732 
1733     if (new_tl_data->tl_data_type != KRB5_TL_DB_ARGS) {	/* db_args can be multiple */
1734 	for (tl_data = entry->tl_data; tl_data;
1735 	     tl_data = tl_data->tl_data_next)
1736 	    if (tl_data->tl_data_type == new_tl_data->tl_data_type)
1737 		break;
1738     }
1739 
1740     /* if necessary, chain a new record in the beginning and point at it */
1741 
1742     if (!tl_data) {
1743 	if ((tl_data =
1744 	     (krb5_tl_data *) krb5_db_alloc(context, NULL,
1745 					    sizeof(krb5_tl_data)))
1746 	    == NULL) {
1747 	    free(tmp);
1748 	    return (ENOMEM);
1749 	}
1750 	memset(tl_data, 0, sizeof(krb5_tl_data));
1751 	tl_data->tl_data_next = entry->tl_data;
1752 	entry->tl_data = tl_data;
1753 	entry->n_tl_data++;
1754     }
1755 
1756     /* fill in the record */
1757 
1758     if (tl_data->tl_data_contents)
1759 	krb5_db_free(context, tl_data->tl_data_contents);
1760 
1761     tl_data->tl_data_type = new_tl_data->tl_data_type;
1762     tl_data->tl_data_length = new_tl_data->tl_data_length;
1763     tl_data->tl_data_contents = tmp;
1764     memcpy(tmp, new_tl_data->tl_data_contents, tl_data->tl_data_length);
1765 
1766     return (0);
1767 }
1768 
1769 /* change password functions */
1770 krb5_error_code
1771 krb5_dbe_cpw(krb5_context kcontext,
1772 	     krb5_keyblock * master_key,
1773 	     krb5_key_salt_tuple * ks_tuple,
1774 	     int ks_tuple_count,
1775 	     char *passwd,
1776 	     int new_kvno, krb5_boolean keepold, krb5_db_entry * db_entry)
1777 {
1778     krb5_error_code status = 0;
1779     kdb5_dal_handle *dal_handle;
1780 
1781     if (kcontext->db_context == NULL) {
1782 	status = kdb_setup_lib_handle(kcontext);
1783 	if (status) {
1784 	    goto clean_n_exit;
1785 	}
1786     }
1787 
1788     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1789     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1790     if (status) {
1791 	goto clean_n_exit;
1792     }
1793 
1794     status = dal_handle->lib_handle->vftabl.db_change_pwd(kcontext,
1795 							  master_key,
1796 							  ks_tuple,
1797 							  ks_tuple_count,
1798 							  passwd,
1799 							  new_kvno,
1800 							  keepold, db_entry);
1801     get_errmsg(kcontext, status);
1802     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1803 
1804   clean_n_exit:
1805     return status;
1806 }
1807 
1808 /* policy management functions */
1809 krb5_error_code
1810 krb5_db_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
1811 {
1812     krb5_error_code status = 0;
1813     kdb5_dal_handle *dal_handle;
1814 
1815     if (kcontext->db_context == NULL) {
1816 	status = kdb_setup_lib_handle(kcontext);
1817 	if (status) {
1818 	    goto clean_n_exit;
1819 	}
1820     }
1821 
1822     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1823     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1824     if (status) {
1825 	goto clean_n_exit;
1826     }
1827 
1828     status = dal_handle->lib_handle->vftabl.db_create_policy(kcontext, policy);
1829     get_errmsg(kcontext, status);
1830     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1831 
1832   clean_n_exit:
1833     return status;
1834 }
1835 
1836 krb5_error_code
1837 krb5_db_get_policy(krb5_context kcontext, char *name,
1838 		   osa_policy_ent_t * policy, int *cnt)
1839 {
1840     krb5_error_code status = 0;
1841     kdb5_dal_handle *dal_handle;
1842 
1843     if (kcontext->db_context == NULL) {
1844 	status = kdb_setup_lib_handle(kcontext);
1845 	if (status) {
1846 	    goto clean_n_exit;
1847 	}
1848     }
1849 
1850     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1851     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1852     if (status) {
1853 	goto clean_n_exit;
1854     }
1855 
1856     status =
1857 	dal_handle->lib_handle->vftabl.db_get_policy(kcontext, name, policy,
1858 						     cnt);
1859     get_errmsg(kcontext, status);
1860     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1861 
1862   clean_n_exit:
1863     return status;
1864 }
1865 
1866 krb5_error_code
1867 krb5_db_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
1868 {
1869     krb5_error_code status = 0;
1870     kdb5_dal_handle *dal_handle;
1871 
1872     if (kcontext->db_context == NULL) {
1873 	status = kdb_setup_lib_handle(kcontext);
1874 	if (status) {
1875 	    goto clean_n_exit;
1876 	}
1877     }
1878 
1879     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1880     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1881     if (status) {
1882 	goto clean_n_exit;
1883     }
1884 
1885     status = dal_handle->lib_handle->vftabl.db_put_policy(kcontext, policy);
1886     get_errmsg(kcontext, status);
1887     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1888 
1889   clean_n_exit:
1890     return status;
1891 }
1892 
1893 krb5_error_code
1894 krb5_db_iter_policy(krb5_context kcontext, char *match_entry,
1895 		    osa_adb_iter_policy_func func, void *data)
1896 {
1897     krb5_error_code status = 0;
1898     kdb5_dal_handle *dal_handle;
1899 
1900     if (kcontext->db_context == NULL) {
1901 	status = kdb_setup_lib_handle(kcontext);
1902 	if (status) {
1903 	    goto clean_n_exit;
1904 	}
1905     }
1906 
1907     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1908     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1909     if (status) {
1910 	goto clean_n_exit;
1911     }
1912 
1913     status =
1914 	dal_handle->lib_handle->vftabl.db_iter_policy(kcontext, match_entry,
1915 						      func, data);
1916     get_errmsg(kcontext, status);
1917     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1918 
1919   clean_n_exit:
1920     return status;
1921 }
1922 
1923 krb5_error_code
1924 krb5_db_delete_policy(krb5_context kcontext, char *policy)
1925 {
1926     krb5_error_code status = 0;
1927     kdb5_dal_handle *dal_handle;
1928 
1929     if (kcontext->db_context == NULL) {
1930 	status = kdb_setup_lib_handle(kcontext);
1931 	if (status) {
1932 	    goto clean_n_exit;
1933 	}
1934     }
1935 
1936     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1937     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1938     if (status) {
1939 	goto clean_n_exit;
1940     }
1941 
1942     status = dal_handle->lib_handle->vftabl.db_delete_policy(kcontext, policy);
1943     get_errmsg(kcontext, status);
1944     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1945 
1946   clean_n_exit:
1947     return status;
1948 }
1949 
1950 void
1951 krb5_db_free_policy(krb5_context kcontext, osa_policy_ent_t policy)
1952 {
1953     krb5_error_code status = 0;
1954     kdb5_dal_handle *dal_handle;
1955 
1956     if (kcontext->db_context == NULL) {
1957 	status = kdb_setup_lib_handle(kcontext);
1958 	if (status) {
1959 	    goto clean_n_exit;
1960 	}
1961     }
1962 
1963     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
1964     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
1965     if (status) {
1966 	goto clean_n_exit;
1967     }
1968 
1969     dal_handle->lib_handle->vftabl.db_free_policy(kcontext, policy);
1970     get_errmsg(kcontext, status);
1971     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
1972 
1973   clean_n_exit:
1974     return;
1975 }
1976 
1977 krb5_error_code
1978 krb5_db_promote(krb5_context kcontext, char **db_args)
1979 {
1980     krb5_error_code status = 0;
1981     char   *section = NULL;
1982     kdb5_dal_handle *dal_handle;
1983 
1984     section = kdb_get_conf_section(kcontext);
1985     if (section == NULL) {
1986 	status = KRB5_KDB_SERVER_INTERNAL_ERR;
1987 	krb5_set_error_message (kcontext, status,
1988 		gettext("unable to determine configuration section for realm %s\n"),
1989 		kcontext->default_realm);
1990 	goto clean_n_exit;
1991     }
1992 
1993     if (kcontext->db_context == NULL) {
1994 	status = kdb_setup_lib_handle(kcontext);
1995 	if (status) {
1996 	    goto clean_n_exit;
1997 	}
1998     }
1999 
2000     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
2001     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2002     if (status) {
2003 	goto clean_n_exit;
2004     }
2005 
2006     status =
2007 	dal_handle->lib_handle->vftabl.promote_db(kcontext, section, db_args);
2008     get_errmsg(kcontext, status);
2009     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2010 
2011   clean_n_exit:
2012     if (section)
2013 	free(section);
2014     return status;
2015 }
2016 
2017 /*
2018  * Solaris Kerberos: support for iprop
2019  *
2020  * Not all KDB plugins support iprop.
2021  *
2022  * sets iprop_supported to 1 if iprop supportd, 0 otherwise.
2023  */
2024 krb5_error_code
2025 krb5_db_supports_iprop(krb5_context kcontext, int *iprop_supported)
2026 {
2027     krb5_error_code status = 0;
2028     kdb5_dal_handle *dal_handle;
2029 
2030     if (kcontext->db_context == NULL) {
2031 	status = kdb_setup_lib_handle(kcontext);
2032 	if (status) {
2033 	    goto clean_n_exit;
2034 	}
2035     }
2036 
2037     dal_handle = (kdb5_dal_handle *) kcontext->db_context;
2038     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
2039     if (status) {
2040 	goto clean_n_exit;
2041     }
2042 
2043     *iprop_supported = dal_handle->lib_handle->vftabl.iprop_supported;
2044     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
2045 
2046   clean_n_exit:
2047     return status;
2048 }
2049