/* * Include files */ #ifdef HAVE_PTHREAD_H #include #endif #include #include #include #include #include #include "kdb5.h" #include /* currently DB2 policy related errors are exported from DAL. But other databases should set_err function to return string */ #include "adb_err.h" /* * Type definitions */ #define KRB5_TL_DB_ARGS 0x7fff /* * internal static variable */ #ifdef HAVE_PTHREAD_H /* static pthread_once_t db_inited = PTHREAD_ONCE_INIT; */ static pthread_mutex_t db_lock = PTHREAD_MUTEX_INITIALIZER; #else /* static int db_inited = 0; */ #endif #ifdef _KDB5_STATIC_LINK #undef _KDB5_DYNAMIC_LINK #else #undef _KDB5_DYNAMIC_LINK /* to avoid redefinition problem */ #define _KDB5_DYNAMIC_LINK #endif static db_library lib_list; /* * Helper Functions */ #ifdef HAVE_PTHREAD_H /* * KNOWN ISSUES with locking: This code does not handle a scenario where a library is thread-safe for different DB contexts, * but not with the same context. It locks the complete DB library. If this is not the scenario, then lock has to be moved from * db_library to kdb5_dal_handle. For now doing a pessimistic locking. * * If any thread does a DB lock, all the other threads are barred from accessing DB using this context (infact library * because of the previous defect). * This is with the assumption that, DB's lock code will take care of excluding other processes/machines from * using the DB. But there could be a scenario where access by some other thread using the same context might corrupt the database. */ static int kdb_lock_list() { return pthread_mutex_lock( &db_lock ); } static int kdb_unlock_list() { return pthread_mutex_unlock( &db_lock ); } static int kdb_init_lib_lock( db_library lib ) { krb5_error_code retval; if( (retval= pthread_mutex_init( &lib->lib_lock, NULL ))) { return retval; } lib->lock_holder = pthread_self(); lib->excl = 0; lib->recursive_cnt = 0; return pthread_cond_init( &lib->unlocked, NULL ); } static int kdb_destroy_lib_lock ( db_library lib ) { krb5_error_code retval; if( (retval = pthread_mutex_destroy( &lib->lib_lock )) ) { return retval; } return pthread_cond_destroy( &lib->unlocked ); } static int kdb_lock_lib_lock( db_library lib, krb5_boolean exclusive ) { /* since, handle locked by one thread should not allow another thread to continue */ krb5_error_code retval = 0; pthread_t myid = pthread_self(); if( (retval = pthread_mutex_lock( &lib->lib_lock )) ) return retval; while( (exclusive && (lib->excl || lib->recursive_cnt)) || (!pthread_equal(lib->lock_holder, myid) && !lib->vftabl.is_thread_safe && lib->recursive_cnt) ) { /* exclusive lock held or some one using lock when exclusive is requested or library not-re-entrent */ if( (retval = pthread_cond_wait(&lib->unlocked, &lib->lib_lock)) ) return retval; } /* exclusive lock and recursive_cnt allow a thread to lock even it already holds a lock */ if( exclusive ) lib->excl++; lib->recursive_cnt++; lib->lock_holder = myid; return pthread_mutex_unlock( &lib->lib_lock ); } static int kdb_unlock_lib_lock( db_library lib, krb5_boolean exclusive ) { krb5_error_code retval = 0; if( (retval = pthread_mutex_lock( &lib->lib_lock )) ) return retval; lib->recursive_cnt--; if( exclusive ) lib->excl--; if( (retval = pthread_cond_broadcast( &lib->unlocked )) ) return retval; return pthread_mutex_unlock( &lib->lib_lock ); } #else /* no PTHREAD */ /* program is not using pthread. So, threads wont be there. No need to lock */ #define kdb_lock_list() 0 #define kdb_unlock_list() 0 #define kdb_init_lib_lock(a) 0 #define kdb_destroy_lib_lock(a) 0 #define kdb_lock_lib_lock(a, b) 0 #define kdb_unlock_lib_lock(a, b) 0 #endif /* end of HAVE_PTHREAD_H */ static char * kdb_get_conf_section( krb5_context kcontext ) { krb5_error_code status = 0; char *result = NULL; char *value = NULL; /* profile has to be initialized. If profile is not initialized, expect nothing less than a crash */ status = profile_get_string( kcontext->profile, KDB_REALM_SECTION, /* realms */ kcontext->default_realm, KDB_MODULE_POINTER, /* under the realm name, database_module */ kcontext->default_realm, /* default value is the realm name itself */ &value ); if( status ) { /* some problem */ result = strdup( kcontext->default_realm ); /* let NULL be handled by the caller */ } else { result = strdup( value ); /* free profile string */ profile_release_string( value ); } return result; } static char * kdb_get_library_name( krb5_context kcontext ) { krb5_error_code status = 0; char *result = NULL; char *value = NULL; char *lib = NULL; status = profile_get_string( kcontext->profile, KDB_REALM_SECTION, /* realms */ kcontext->default_realm, KDB_MODULE_POINTER, /* under the realm name, database_module */ kcontext->default_realm, /* default value is the realm name itself */ &value ); if( status ) { goto clean_n_exit; } /* we got the module section. Get the library name from the module */ status = profile_get_string( kcontext->profile, KDB_MODULE_SECTION, value, KDB_LIB_POINTER, "kdb_db2", /* default to db2 */ &lib ); if( status ) { goto clean_n_exit; } result = strdup( lib ); clean_n_exit: if( value ) { /* free profile string */ profile_release_string( value ); } if( lib ) { /* free profile string */ profile_release_string( lib ); } if( status ) { /* any error default to db2 */ result = strdup( "kdb_db2" ); } return result; } static void kdb_setup_opt_functions ( db_library lib ) { if ( lib->vftabl.set_master_key == NULL ) { lib->vftabl.set_master_key = kdb_def_set_mkey; } if ( lib->vftabl.get_master_key == NULL ) { lib->vftabl.get_master_key = kdb_def_get_mkey; } if( lib->vftabl.fetch_master_key == NULL ) { lib->vftabl.fetch_master_key = krb5_db_def_fetch_mkey; } if( lib->vftabl.verify_master_key == NULL ) { lib->vftabl.verify_master_key = krb5_def_verify_master_key; } if( lib->vftabl.dbe_search_enctype == NULL ) { lib->vftabl.dbe_search_enctype = krb5_dbe_def_search_enctype; } if( lib->vftabl.db_change_pwd == NULL ) { lib->vftabl.db_change_pwd = krb5_dbe_def_cpw; } if( lib->vftabl.store_master_key == NULL ) { lib->vftabl.store_master_key = krb5_def_store_mkey; } } static int kdb_db2_pol_err_loaded = 0; #ifdef _KDB5_STATIC_LINK #define DEF_SYMBOL(a) extern kdb_vftabl krb5_db_vftabl_ ## a #define GET_SYMBOL(a) (krb5_db_vftabl_ ## a) static krb5_error_code kdb_load_library( krb5_context kcontext, char *lib_name, db_library *lib ) { krb5_error_code status; void *vftabl_addr = NULL; char buf[KRB5_MAX_ERR_STR]; if( !strcmp("kdb_db2", lib_name) && (kdb_db2_pol_err_loaded == 0) ) { initialize_adb_error_table(); kdb_db2_pol_err_loaded = 1; } *lib = calloc( (size_t)1, sizeof(**lib)); if( *lib == NULL ) { status = ENOMEM; goto clean_n_exit; } status = kdb_init_lib_lock( *lib ); if( status ) { goto clean_n_exit; } strcpy( (*lib)->name, lib_name ); #if !defined(KDB5_USE_LIB_KDB_DB2) && !defined(KDB5_USE_LIB_TEST) #error No database module defined #endif #ifdef KDB5_USE_LIB_KDB_DB2 if( strcmp( lib_name, "kdb_db2" ) == 0 ) { DEF_SYMBOL(kdb_db2); vftabl_addr = (void*)& GET_SYMBOL(kdb_db2); } else #endif #ifdef KDB5_USE_LIB_TEST if( strcmp( lib_name, "test" ) == 0 ) { DEF_SYMBOL(test); vftabl_addr = (void*) &GET_SYMBOL(test); } else #endif { sprintf( buf, "Program not built to support %s database type\n", lib_name ); status = -1; krb5_db_set_err(kcontext, krb5_err_have_str, status, buf); goto clean_n_exit; } memcpy( &(*lib)->vftabl, vftabl_addr, sizeof(kdb_vftabl)); kdb_setup_opt_functions( *lib ); if( (status=(*lib)->vftabl.init_library(krb5_set_err)) ) { /* ERROR. library not initialized cleanly */ sprintf( buf, "%s library initialization failed, error code %ld\n", lib_name, status ); status = -1; krb5_db_set_err(kcontext, krb5_err_have_str, status, buf); goto clean_n_exit; } clean_n_exit: if( status ) { free(*lib), *lib = NULL; } return status; } #else static char *db_dl_location[] = DEFAULT_KDB_LIB_PATH; static krb5_error_code kdb_load_library( krb5_context kcontext, char *lib_name, db_library *lib ) { krb5_error_code status = 0; char dl_name[1024]; int ndx; void *vftabl_addr; char *err_str = NULL; if( !strcmp("kdb_db2", lib_name) && (kdb_db2_pol_err_loaded == 0) ) { initialize_adb_error_table(); kdb_db2_pol_err_loaded = 1; } *lib = calloc( (size_t)1, sizeof(**lib)); if( *lib == NULL ) { status = ENOMEM; goto clean_n_exit; } status = kdb_init_lib_lock( *lib ); if( status ) { goto clean_n_exit; } strcpy( (*lib)->name, lib_name ); for( ndx = 0; db_dl_location[ndx]; ndx++ ) { sprintf(dl_name, "%s/lib%s.so", db_dl_location[ndx], lib_name); (*lib)->dl_handle = dlopen( dl_name, RTLD_NOW ); if( (*lib)->dl_handle ) { /* found the module */ sprintf( dl_name, "krb5_db_vftabl_%s", lib_name ); dlerror(); vftabl_addr = dlsym( (*lib)->dl_handle, dl_name ); if( vftabl_addr ) { memcpy( &(*lib)->vftabl, vftabl_addr, sizeof(kdb_vftabl)); kdb_setup_opt_functions( *lib ); if( (status = (*lib)->vftabl.init_library(krb5_set_err)) ) { /* ERROR. library not initialized cleanly */ goto clean_n_exit; } } else { status = -1; krb5_set_err(kcontext, krb5_err_have_str, status, dlerror()); goto clean_n_exit; } break; } else { /* set the error. Later if we find everything fine.. we will reset this */ err_str = dlerror(); /* fprintf(stderr, "Error loading library %s\n", t); */ } } if( ! (*lib)->dl_handle ) { /* library not found in the given list. Error str is already set */ status = -1; krb5_set_err(kcontext, krb5_err_have_str, status, err_str); goto clean_n_exit; } clean_n_exit: if( status ) { if( *lib ) { kdb_destroy_lib_lock( *lib ); if( (*lib)->dl_handle ) { dlclose( (*lib)->dl_handle ); } free(*lib); *lib = NULL; } } return status; } #endif /* end of _KDB5_STATIC_LINK */ static krb5_error_code kdb_find_library( krb5_context kcontext, char *lib_name, db_library *lib) { /* lock here so that no two threads try to do the same at the same time */ krb5_error_code status = 0; int locked = 0; db_library curr_elt, prev_elt = NULL; if( (status = kdb_lock_list()) != 0 ) { goto clean_n_exit; } locked = 1; curr_elt = lib_list; while ( curr_elt != NULL ) { if( strcmp( lib_name, curr_elt->name ) == 0 ) { *lib = curr_elt; goto clean_n_exit; } prev_elt = curr_elt; curr_elt = curr_elt->next; } /* module not found. create and add to list */ status = kdb_load_library( kcontext, lib_name, lib); if( status ) { goto clean_n_exit; } if( prev_elt ) { /* prev_elt points to the last element in the list */ prev_elt->next = *lib; (*lib)->prev = prev_elt; } else { lib_list = *lib; } clean_n_exit: if( *lib ) { (*lib)->reference_cnt++; } if( locked ) { kdb_unlock_list(); } return status; } static krb5_error_code kdb_free_library( db_library lib) { krb5_error_code status = 0; int locked = 0; if( (status = kdb_lock_list()) != 0 ) { goto clean_n_exit; } locked = 1; lib->reference_cnt--; if( lib->reference_cnt == 0 ) { status = lib->vftabl.fini_library(); if( status ) { goto clean_n_exit; } /* close the library */ if( lib->dl_handle ) { dlclose( lib->dl_handle ); } kdb_destroy_lib_lock( lib ); if( lib->prev == NULL ) { /* first element in the list */ lib_list = lib->next; } else { lib->prev->next = lib->next; } if( lib->next ) { lib->next->prev = lib->prev; } free(lib); } clean_n_exit: if( locked ) { kdb_unlock_list(); } return status; } static krb5_error_code kdb_setup_lib_handle( krb5_context kcontext ) { char *library = NULL; krb5_error_code status = 0; db_library lib = NULL; kdb5_dal_handle *dal_handle = NULL; dal_handle = calloc( (size_t)1, sizeof(kdb5_dal_handle) ); if( dal_handle == NULL ) { status = ENOMEM; goto clean_n_exit; } library = kdb_get_library_name( kcontext ); if( library == NULL ) { status = -1; goto clean_n_exit; } status = kdb_find_library( kcontext, library, &lib ); if( status ) { goto clean_n_exit; } dal_handle->lib_handle = lib; kcontext->db_context = (void *) dal_handle; clean_n_exit: free(library); if( status ) { free( dal_handle ); if( lib ) { kdb_free_library( lib ); } } return status; } static krb5_error_code kdb_free_lib_handle( krb5_context kcontext ) { krb5_error_code status = 0; status = kdb_free_library( ((kdb5_dal_handle *)kcontext->db_context)->lib_handle ); if( status ) { goto clean_n_exit; } free( kcontext->db_context ); kcontext->db_context = NULL; clean_n_exit: return status; } /* * External functions... DAL API */ void krb5_db_clr_error() { krb5_clr_error(); } krb5_error_code krb5_db_open( krb5_context kcontext, char **db_args, int mode ) { krb5_error_code status = 0; char *section = NULL; kdb5_dal_handle *dal_handle; char buf[KRB5_MAX_ERR_STR]; section = kdb_get_conf_section( kcontext ); if( section == NULL ) { sprintf( buf, "unable to determine configuration section for realm %s\n", kcontext->default_realm ); status = -1; krb5_set_err(kcontext, krb5_err_have_str, status, buf); goto clean_n_exit; } if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.init_module(kcontext, section, db_args, mode ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: if( section ) free(section); return status; } const char * krb5_db_errcode2string ( krb5_context kcontext, long err_code ) { const char *err_str = NULL; krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } err_str = dal_handle->lib_handle->vftabl.errcode_2_string(kcontext, err_code ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return err_str; } krb5_error_code krb5_db_inited ( krb5_context kcontext ) { return !(kcontext && kcontext->db_context && ((kdb5_dal_handle*)kcontext->db_context)->db_context); } krb5_error_code krb5_db_create ( krb5_context kcontext, char **db_args ) { krb5_error_code status = 0; char *section = NULL; kdb5_dal_handle *dal_handle; char buf[KRB5_MAX_ERR_STR]; section = kdb_get_conf_section( kcontext ); if( section == NULL ) { sprintf( buf, "unable to determine configuration section for realm %s\n", kcontext->default_realm ); status = -1; krb5_set_err(kcontext, krb5_err_have_str, status, buf); goto clean_n_exit; } if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_create(kcontext, section, db_args ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: if( section ) free(section); return status; } krb5_error_code krb5_db_fini ( krb5_context kcontext ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { /* module not loaded. So nothing to be done */ goto clean_n_exit; } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.fini_module( kcontext ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = kdb_free_lib_handle( kcontext ); clean_n_exit: return status; } krb5_error_code krb5_db_destroy ( krb5_context kcontext, char **db_args ) { krb5_error_code status = 0; char * section = NULL; kdb5_dal_handle *dal_handle; char buf[KRB5_MAX_ERR_STR]; section = kdb_get_conf_section( kcontext ); if( section == NULL ) { sprintf( buf, "unable to determine configuration section for realm %s\n", kcontext->default_realm ); status = -1; krb5_set_err(kcontext, krb5_err_have_str, status, buf); goto clean_n_exit; } if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_destroy(kcontext, section, db_args ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: if( section ) free(section); return status; } krb5_error_code krb5_db_get_age ( krb5_context kcontext, char *db_name, time_t *t ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_get_age(kcontext, db_name, t ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_set_option ( krb5_context kcontext, int option, void *value ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_set_option(kcontext, option, value ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_lock ( krb5_context kcontext, int lock_mode ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; /* acquire an exclusive lock, ensures no other thread uses this context */ status = kdb_lock_lib_lock( dal_handle->lib_handle, TRUE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_lock(kcontext, lock_mode ); /* exclusive lock is still held, so no other thread could use this context */ kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_unlock ( krb5_context kcontext ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; /* normal lock acquired and exclusive lock released */ status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_unlock( kcontext ); kdb_unlock_lib_lock( dal_handle->lib_handle, TRUE ); clean_n_exit: return status; } krb5_error_code krb5_db_get_principal ( krb5_context kcontext, krb5_const_principal search_for, krb5_db_entry *entries, int *nentries, krb5_boolean *more ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_get_principal(kcontext, search_for, entries, nentries, more); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_free_principal ( krb5_context kcontext, krb5_db_entry *entry, int count ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_free_principal( kcontext, entry, count ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_put_principal ( krb5_context kcontext, krb5_db_entry *entries, int *nentries ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; char **db_args = NULL; krb5_tl_data *prev, *curr, *next; int db_args_size = 0; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } /* Giving db_args as part of tl data causes, db2 to store the tl_data as such. To prevent this, tl_data is collated and passed as a sepearte argument. Currently supports only one principal. but passing it as a seperate argument makes it difficult for kadmin remote to pass arguments to server */ prev=NULL, curr=entries->tl_data; while(curr) { if( curr->tl_data_type == KRB5_TL_DB_ARGS ) { char **t; /* since this is expected to be NULL terminated string and this could come from any client, do a check before passing it to db. */ if( ((char*) curr->tl_data_contents)[curr->tl_data_length-1] != '\0' ) { /* not null terminated. Dangerous input */ status = EINVAL; goto clean_n_exit; } db_args_size++; t = realloc( db_args, sizeof(char*) * (db_args_size+1)); /* 1 for NULL */ if( t == NULL ) { status = ENOMEM; goto clean_n_exit; } db_args = t; db_args[db_args_size-1] = (char*)curr->tl_data_contents; db_args[db_args_size] = NULL; next = curr->tl_data_next; if( prev == NULL ) { /* current node is the first in the linked list. remove it */ entries->tl_data = curr->tl_data_next; } else { prev->tl_data_next = curr->tl_data_next; } entries->n_tl_data--; krb5_db_free( kcontext, curr ); /* previous does not change */ curr = next; } else { prev = curr; curr = curr->tl_data_next; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_put_principal( kcontext, entries, nentries, db_args ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: while( db_args_size ) { if( db_args[db_args_size-1] ) krb5_db_free(kcontext, db_args[db_args_size-1]); db_args_size--; } if( db_args ) free(db_args); return status; } krb5_error_code krb5_db_delete_principal ( krb5_context kcontext, krb5_principal search_for, int *nentries ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_delete_principal( kcontext, search_for, nentries ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_iterate ( krb5_context kcontext, char *match_entry, int (*func) (krb5_pointer, krb5_db_entry *), krb5_pointer func_arg ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_iterate( kcontext, match_entry, func, func_arg ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_supported_realms ( krb5_context kcontext, char **realms ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_supported_realms( kcontext, realms ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_free_supported_realms ( krb5_context kcontext, char **realms ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_free_supported_realms( kcontext, realms ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_set_master_key_ext ( krb5_context kcontext, char *pwd, krb5_keyblock *key ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.set_master_key( kcontext, pwd, key ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_set_mkey ( krb5_context context, krb5_keyblock *key) { return krb5_db_set_master_key_ext ( context, NULL, key ); } krb5_error_code krb5_db_get_mkey ( krb5_context kcontext, krb5_keyblock **key ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } /* lets use temp key and copy it later to avoid memory problems when freed by the caller */ status = dal_handle->lib_handle->vftabl.get_master_key( kcontext, key ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_store_master_key ( krb5_context kcontext, char *db_arg, krb5_principal mname, krb5_keyblock *key, char *master_pwd) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.store_master_key( kcontext, db_arg, mname, key, master_pwd); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } char *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1; char *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2; krb5_error_code krb5_db_fetch_mkey ( krb5_context context, krb5_principal mname, krb5_enctype etype, krb5_boolean fromkeyboard, krb5_boolean twice, char *db_args, krb5_data *salt, krb5_keyblock *key) { krb5_error_code retval; char password[BUFSIZ]; krb5_data pwd; unsigned int size = sizeof(password); int kvno; krb5_keyblock tmp_key; memset( &tmp_key, 0, sizeof(tmp_key) ); if (fromkeyboard) { krb5_data scratch; if ((retval = krb5_read_password(context, krb5_mkey_pwd_prompt1, twice ? krb5_mkey_pwd_prompt2 : 0, password, &size))) { goto clean_n_exit; } pwd.data = password; pwd.length = size; if (!salt) { retval = krb5_principal2salt(context, mname, &scratch); if (retval) goto clean_n_exit; } retval = krb5_c_string_to_key(context, etype, &pwd, salt?salt:&scratch, key); if (!salt) krb5_xfree(scratch.data); memset(password, 0, sizeof(password)); /* erase it */ } else { kdb5_dal_handle *dal_handle; if( context->db_context == NULL ) { retval = kdb_setup_lib_handle( context ); if( retval ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) context->db_context; retval = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( retval ) { goto clean_n_exit; } tmp_key.enctype = key->enctype; retval = dal_handle->lib_handle->vftabl.fetch_master_key( context, mname, &tmp_key, &kvno, db_args); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); if( retval ) { goto clean_n_exit; } key->contents = malloc( tmp_key.length ); if( key->contents == NULL ) { retval = ENOMEM; goto clean_n_exit; } key->magic = tmp_key.magic; key->enctype = tmp_key.enctype; key->length = tmp_key.length; memcpy(key->contents, tmp_key.contents, tmp_key.length); } clean_n_exit: if( tmp_key.contents ) { memset( tmp_key.contents, 0, tmp_key.length ); krb5_db_free( context, tmp_key.contents ); } return retval; } krb5_error_code krb5_db_verify_master_key ( krb5_context kcontext, krb5_principal mprinc, krb5_keyblock *mkey ) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.verify_master_key( kcontext, mprinc, mkey ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } void *krb5_db_alloc( krb5_context kcontext, void *ptr, size_t size ) { krb5_error_code status; kdb5_dal_handle *dal_handle; void *new_ptr = NULL; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; new_ptr = dal_handle->lib_handle->vftabl.db_alloc( kcontext, ptr, size ); clean_n_exit: return new_ptr; } void krb5_db_free( krb5_context kcontext, void *ptr) { krb5_error_code status; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; dal_handle->lib_handle->vftabl.db_free( kcontext, ptr ); clean_n_exit: return; } /* has to be modified */ krb5_error_code krb5_dbe_find_enctype( krb5_context kcontext, krb5_db_entry *dbentp, krb5_int32 ktype, krb5_int32 stype, krb5_int32 kvno, krb5_key_data **kdatap) { krb5_int32 start = 0; return krb5_dbe_search_enctype( kcontext, dbentp, &start, ktype, stype, kvno, kdatap ); } krb5_error_code krb5_dbe_search_enctype ( krb5_context kcontext, krb5_db_entry *dbentp, krb5_int32 *start, krb5_int32 ktype, krb5_int32 stype, krb5_int32 kvno, krb5_key_data **kdatap) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.dbe_search_enctype( kcontext, dbentp, start, ktype, stype, kvno, kdatap ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } #define REALM_SEP_STRING "@" krb5_error_code krb5_db_setup_mkey_name ( krb5_context context, const char *keyname, const char *realm, char **fullname, krb5_principal *principal) { krb5_error_code retval; size_t keylen; size_t rlen = strlen(realm); char *fname; if (!keyname) keyname = KRB5_KDB_M_NAME; /* XXX external? */ keylen = strlen(keyname); fname = malloc(keylen+rlen+strlen(REALM_SEP_STRING)+1); if (!fname) return ENOMEM; strcpy(fname, keyname); strcat(fname, REALM_SEP_STRING); strcat(fname, realm); if ((retval = krb5_parse_name(context, fname, principal))) return retval; if (fullname) *fullname = fname; else free(fname); return 0; } krb5_error_code krb5_dbe_lookup_last_pwd_change(context, entry, stamp) krb5_context context; krb5_db_entry * entry; krb5_timestamp * stamp; { krb5_tl_data tl_data; krb5_error_code code; krb5_int32 tmp; tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE; if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data))) return(code); if (tl_data.tl_data_length != 4) { *stamp = 0; return(0); } krb5_kdb_decode_int32(tl_data.tl_data_contents, tmp); *stamp = (krb5_timestamp) tmp; return(0); } krb5_error_code krb5_dbe_lookup_tl_data(context, entry, ret_tl_data) krb5_context context; krb5_db_entry * entry; krb5_tl_data * ret_tl_data; { krb5_tl_data *tl_data; for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) { if (tl_data->tl_data_type == ret_tl_data->tl_data_type) { *ret_tl_data = *tl_data; return(0); } } /* if the requested record isn't found, return zero bytes. if it ever means something to have a zero-length tl_data, this code and its callers will have to be changed */ ret_tl_data->tl_data_length = 0; ret_tl_data->tl_data_contents = NULL; return(0); } krb5_error_code krb5_dbe_create_key_data(context, entry) krb5_context context; krb5_db_entry * entry; { if ((entry->key_data = (krb5_key_data *) krb5_db_alloc(context, entry->key_data, (sizeof(krb5_key_data)* (entry->n_key_data + 1)))) == NULL) return(ENOMEM); memset(entry->key_data + entry->n_key_data, 0, sizeof(krb5_key_data)); entry->n_key_data++; return 0; } krb5_error_code krb5_dbe_update_mod_princ_data(context, entry, mod_date, mod_princ) krb5_context context; krb5_db_entry * entry; krb5_timestamp mod_date; krb5_const_principal mod_princ; { krb5_tl_data tl_data; krb5_error_code retval = 0; krb5_octet * nextloc = 0; char * unparse_mod_princ = 0; unsigned int unparse_mod_princ_size; if ((retval = krb5_unparse_name(context, mod_princ, &unparse_mod_princ))) return(retval); unparse_mod_princ_size = strlen(unparse_mod_princ) + 1; if ((nextloc = (krb5_octet *) malloc(unparse_mod_princ_size + 4)) == NULL) { free(unparse_mod_princ); return(ENOMEM); } tl_data.tl_data_type = KRB5_TL_MOD_PRINC; tl_data.tl_data_length = unparse_mod_princ_size + 4; tl_data.tl_data_contents = nextloc; /* Mod Date */ krb5_kdb_encode_int32(mod_date, nextloc); /* Mod Princ */ memcpy(nextloc+4, unparse_mod_princ, unparse_mod_princ_size); retval = krb5_dbe_update_tl_data(context, entry, &tl_data); free(unparse_mod_princ); free(nextloc); return(retval); } krb5_error_code krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ) krb5_context context; krb5_db_entry * entry; krb5_timestamp * mod_time; krb5_principal * mod_princ; { krb5_tl_data tl_data; krb5_error_code code; tl_data.tl_data_type = KRB5_TL_MOD_PRINC; if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data))) return(code); if ((tl_data.tl_data_length < 5) || (tl_data.tl_data_contents[tl_data.tl_data_length-1] != '\0')) return(KRB5_KDB_TRUNCATED_RECORD); /* Mod Date */ krb5_kdb_decode_int32(tl_data.tl_data_contents, *mod_time); /* Mod Princ */ if ((code = krb5_parse_name(context, (const char *) (tl_data.tl_data_contents+4), mod_princ))) return(code); return(0); } krb5_error_code krb5_dbe_update_last_pwd_change(context, entry, stamp) krb5_context context; krb5_db_entry * entry; krb5_timestamp stamp; { krb5_tl_data tl_data; krb5_octet buf[4]; /* this is the encoded size of an int32 */ tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE; tl_data.tl_data_length = sizeof(buf); krb5_kdb_encode_int32((krb5_int32) stamp, buf); tl_data.tl_data_contents = buf; return(krb5_dbe_update_tl_data(context, entry, &tl_data)); } krb5_error_code krb5_dbe_update_tl_data(context, entry, new_tl_data) krb5_context context; krb5_db_entry * entry; krb5_tl_data * new_tl_data; { krb5_tl_data * tl_data = NULL; krb5_octet * tmp; /* copy the new data first, so we can fail cleanly if malloc() fails */ if ((tmp = (krb5_octet *) krb5_db_alloc(context, NULL, new_tl_data->tl_data_length)) == NULL) return(ENOMEM); /* Find an existing entry of the specified type and point at it, or NULL if not found */ if( new_tl_data->tl_data_type != KRB5_TL_DB_ARGS ) /* db_args can be multiple */ { for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) if (tl_data->tl_data_type == new_tl_data->tl_data_type) break; } /* if necessary, chain a new record in the beginning and point at it */ if (!tl_data) { if ((tl_data = (krb5_tl_data *) krb5_db_alloc(context, NULL, sizeof(krb5_tl_data))) == NULL) { free(tmp); return(ENOMEM); } memset( tl_data, 0, sizeof(krb5_tl_data)); tl_data->tl_data_next = entry->tl_data; entry->tl_data = tl_data; entry->n_tl_data++; } /* fill in the record */ if (tl_data->tl_data_contents) krb5_db_free(context, tl_data->tl_data_contents); tl_data->tl_data_type = new_tl_data->tl_data_type; tl_data->tl_data_length = new_tl_data->tl_data_length; tl_data->tl_data_contents = tmp; memcpy(tmp, new_tl_data->tl_data_contents, tl_data->tl_data_length); return(0); } /* change password functions */ krb5_error_code krb5_dbe_cpw( krb5_context kcontext, krb5_keyblock * master_key, krb5_key_salt_tuple * ks_tuple, int ks_tuple_count, char * passwd, int new_kvno, krb5_boolean keepold, krb5_db_entry * db_entry) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_change_pwd( kcontext, master_key, ks_tuple, ks_tuple_count, passwd, new_kvno, keepold, db_entry ); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } /* policy management functions */ krb5_error_code krb5_db_create_policy( krb5_context kcontext, osa_policy_ent_t policy) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_create_policy(kcontext, policy); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_get_policy ( krb5_context kcontext, char *name, osa_policy_ent_t *policy, int *cnt) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_get_policy(kcontext, name, policy, cnt); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_put_policy( krb5_context kcontext, osa_policy_ent_t policy) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_put_policy(kcontext, policy); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_iter_policy( krb5_context kcontext, char *match_entry, osa_adb_iter_policy_func func, void *data) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_iter_policy(kcontext, match_entry, func, data); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } krb5_error_code krb5_db_delete_policy( krb5_context kcontext, char *policy) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_delete_policy(kcontext, policy); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return status; } void krb5_db_free_policy( krb5_context kcontext, osa_policy_ent_t policy) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if( kcontext->db_context == NULL ) { status = kdb_setup_lib_handle( kcontext ); if( status ) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock( dal_handle->lib_handle, FALSE ); if( status ) { goto clean_n_exit; } dal_handle->lib_handle->vftabl.db_free_policy(kcontext, policy); kdb_unlock_lib_lock( dal_handle->lib_handle, FALSE ); clean_n_exit: return; }