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
62static 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
72static db_library lib_list;
73
74/*
75 * Helper Functions
76 */
77
78MAKE_INIT_FUNCTION(kdb_init_lock_list);
79MAKE_FINI_FUNCTION(kdb_fini_lock_list);
80
81int
82kdb_init_lock_list(void)
83{
84    return k5_mutex_finish_init(&db_lock);
85}
86
87static int
88kdb_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
97void
98kdb_fini_lock_list(void)
99{
100    if (INITIALIZER_RAN(kdb_init_lock_list))
101	k5_mutex_destroy(&db_lock);
102}
103
104static int
105kdb_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
117static char *
118kdb_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
151static char *
152kdb_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
198static void
199kdb_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
234static 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)
238static krb5_error_code
239kdb_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
309static char *db_dl_location[] = DEFAULT_KDB_LIB_PATH;
310#define db_dl_n_locations (sizeof(db_dl_location) / sizeof(db_dl_location[0]))
311
312static krb5_error_code
313kdb_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
409clean_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
429static krb5_error_code
430kdb_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
478static krb5_error_code
479kdb_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
525static krb5_error_code
526kdb_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
566static krb5_error_code
567kdb_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
585static void
586get_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 */
608krb5_error_code
609krb5_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
656krb5_error_code
657krb5_db_inited(krb5_context kcontext)
658{
659    return !(kcontext && kcontext->db_context &&
660	     ((kdb5_dal_handle *) kcontext->db_context)->db_context);
661}
662
663krb5_error_code
664krb5_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
704krb5_error_code
705krb5_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
736krb5_error_code
737krb5_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
776krb5_error_code
777krb5_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
803krb5_error_code
804krb5_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
831krb5_error_code
832krb5_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
861krb5_error_code
862krb5_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
890krb5_error_code
891krb5_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
923krb5_error_code
924krb5_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
957krb5_error_code
958krb5_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
986krb5_error_code
987krb5_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
1077krb5_error_code
1078krb5_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
1108krb5_error_code
1109krb5_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
1144krb5_error_code
1145krb5_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
1172krb5_error_code
1173krb5_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
1201krb5_error_code
1202krb5_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
1230krb5_error_code
1231krb5_db_set_mkey(krb5_context context, krb5_keyblock * key)
1232{
1233    return krb5_db_set_master_key_ext(context, NULL, key);
1234}
1235
1236krb5_error_code
1237krb5_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
1265krb5_error_code
1266krb5_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
1298char   *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1;
1299char   *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2;
1300
1301krb5_error_code
1302krb5_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
1396krb5_error_code
1397krb5_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
1425void   *
1426krb5_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
1447void
1448krb5_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
1470krb5_error_code
1471krb5_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
1482krb5_error_code
1483krb5_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
1521krb5_error_code
1522krb5_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
1554krb5_error_code
1555krb5_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*/
1582krb5_error_code
1583krb5_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
1606krb5_error_code
1607krb5_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
1623krb5_error_code
1624krb5_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
1666krb5_error_code
1667krb5_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
1697krb5_error_code
1698krb5_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
1714krb5_error_code
1715krb5_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 */
1770krb5_error_code
1771krb5_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 */
1809krb5_error_code
1810krb5_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
1836krb5_error_code
1837krb5_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
1866krb5_error_code
1867krb5_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
1893krb5_error_code
1894krb5_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
1923krb5_error_code
1924krb5_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
1950void
1951krb5_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
1977krb5_error_code
1978krb5_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 */
2024krb5_error_code
2025krb5_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