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