Pluggable libheimbase interface for DBs and misc libheimbase enhancements
[Code reviewed by Love Hörnquist Åstrand <lha@kth.se>] Added heim_db_*() entry points for dealing with databases, and make krb5_aname_to_localname() use it. The following enhancements to libheimbase are included: - Add heim_data_t and heim_string_t "reference" variants to avoid memory copies of potentially large data/strings. See heim_data_ref_create() and heim_string_ref_create(). - Added enhancements to heim_array_t to allow their use for queues and stacks, and to improve performance. See heim_array_insert_value(). - Added XPath-like accessors for heim_object_t. See heim_path_get(), heim_path_copy(), heim_path_create(), and heim_path_delete(). These are used extensively in the DB framework's generic composition of ACID support and in the test_base program - Made libheimbase more consistent with Core Foundation naming conventions. See heim_{dict, array}_{get, copy}_value() and heim_path_{get, copy}(). - Added functionality to and fixed bugs in base/json.c: - heim_serialize(); - depth limit for JSON parsing (for DoS protection); - pretty-printing; - JSON compliance (see below); - flag options for parsing and serializing; these are needed because of impedance mismatches between heim_object_t and JSON (e.g., heim_dict_t allows non-string keys, but JSON does not; heimbase supports binary data, while JSON does not). - Added heim_error_enomem(). - Enhanced the test_base program to test new functionality and to use heim_path*() to better test JSON encoding. This includes some fuzz testing of JSON parsing, and running the test under valgrind. - Started to add doxygen documentation for libheimbase (but doc build for libheimbase is still incomplete). Note that there's still some incomplete JSON support: - JSON string quoting is not fully implemented; - libheimbase lacks support for real numbers, while JSON has it -- otherwise libheimbase is a superset of JSON, specifically in that any heim_object_t can be a key for an associative array. The following DB backends are supported natively: - "sorted-text", a binary search of sorted (in C locale), flat text files; - "json", a backend that stores DB contents serialized as JSON (this is intended for configuration-like contents). The DB framework supports: - multiple key/value tables per-DB - ACID transactions The DB framework also natively implements ACID transactions for any DB backends that a) do not provide transactions natively, b) do provide lock/unlock/sync methods (even on Windows). This includes autocommit of DB updates outside transactions. Future DB enhancements may include: - add backends for various DB types (BDB, CDB, MDB, ...); - make libhdb use heim_db_t; - add a command-line tool for interfacing to databases via libheimbase (e.g., to get/set/delete values, create/copy/ backup DBs, inspect history, check integrity); - framework-level transaction logging (with redo and undo logging), for generic incremental replication; - framework-level DB integrity checking. We could store a MAC of the XOR of a hash function applied to {key, value} for every entry in the DB, then use this to check DB integrity incrementally during incremental replication, as well as for the whole DB.
This commit is contained in:
@@ -33,93 +33,16 @@
|
||||
|
||||
#include "krb5_locl.h"
|
||||
#include "an2ln_plugin.h"
|
||||
#include "db_plugin.h"
|
||||
|
||||
/* Default plugin (DB using binary search of sorted text file) follows */
|
||||
static krb5_error_code
|
||||
an2ln_def_plug_init(krb5_context context, void **ctx)
|
||||
{
|
||||
*ctx = NULL;
|
||||
return 0;
|
||||
}
|
||||
static krb5_error_code an2ln_def_plug_init(krb5_context, void **);
|
||||
static void an2ln_def_plug_fini(void *);
|
||||
static krb5_error_code an2ln_def_plug_an2ln(void *, krb5_context, const char *,
|
||||
krb5_const_principal, set_result_f,
|
||||
void *);
|
||||
|
||||
static void
|
||||
an2ln_def_plug_fini(void *ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context,
|
||||
const char *rule,
|
||||
krb5_const_principal aname,
|
||||
set_result_f set_res_f, void *set_res_ctx)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *an2ln_db_fname;
|
||||
const char *ext;
|
||||
bsearch_file_handle bfh = NULL;
|
||||
char *unparsed = NULL;
|
||||
char *value = NULL;
|
||||
|
||||
if (strncmp(rule, "DB:", strlen("DB:") != 0))
|
||||
return KRB5_PLUGIN_NO_HANDLE;
|
||||
|
||||
/*
|
||||
* This plugin implements a binary search of a sorted text file
|
||||
* (sorted in the C locale). We really need to know that the file
|
||||
* is text, so we implement a trivial heuristic: the file name must
|
||||
* end in .txt.
|
||||
*/
|
||||
an2ln_db_fname = &rule[strlen("DB:")];
|
||||
if (!*an2ln_db_fname)
|
||||
return KRB5_PLUGIN_NO_HANDLE;
|
||||
if (strlen(an2ln_db_fname) < (strlen(".txt") + 1))
|
||||
return KRB5_PLUGIN_NO_HANDLE;
|
||||
ext = strrchr(an2ln_db_fname, '.');
|
||||
if (!ext || strcmp(ext, ".txt") != 0)
|
||||
return KRB5_PLUGIN_NO_HANDLE;
|
||||
|
||||
ret = krb5_unparse_name(context, aname, &unparsed);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __bsearch_file_open(an2ln_db_fname, 0, 0, &bfh, NULL);
|
||||
if (ret) {
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Couldn't open aname2lname-text-db", ""));
|
||||
ret = KRB5_PLUGIN_NO_HANDLE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Binary search; file should be sorted (in C locale) */
|
||||
ret = __bsearch_file(bfh, unparsed, &value, NULL, NULL, NULL);
|
||||
if (ret > 0) {
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Couldn't map principal name to username", ""));
|
||||
ret = KRB5_PLUGIN_NO_HANDLE;
|
||||
goto cleanup;
|
||||
} else if (ret < 0) {
|
||||
ret = KRB5_PLUGIN_NO_HANDLE;
|
||||
goto cleanup;
|
||||
} else {
|
||||
/* ret == 0 -> found */
|
||||
if (!value || !*value) {
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Principal mapped to empty username", ""));
|
||||
ret = KRB5_NO_LOCALNAME;
|
||||
goto cleanup;
|
||||
}
|
||||
ret = set_res_f(set_res_ctx, value);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (bfh)
|
||||
__bsearch_file_close(&bfh);
|
||||
free(unparsed);
|
||||
free(value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5plugin_an2ln_ftable an2ln_def_plug = {
|
||||
static krb5plugin_an2ln_ftable an2ln_def_plug = {
|
||||
0,
|
||||
an2ln_def_plug_init,
|
||||
an2ln_def_plug_fini,
|
||||
@@ -425,3 +348,98 @@ krb5_aname_to_localname(krb5_context context,
|
||||
krb5_config_free_strings(rules);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
an2ln_def_plug_init(krb5_context context, void **ctx)
|
||||
{
|
||||
*ctx = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
an2ln_def_plug_fini(void *ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static heim_base_once_t sorted_text_db_init_once = HEIM_BASE_ONCE_INIT;
|
||||
|
||||
static void
|
||||
sorted_text_db_init_f(void *arg)
|
||||
{
|
||||
(void) heim_db_register("sorted-text", NULL, &heim_sorted_text_file_dbtype);
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context,
|
||||
const char *rule,
|
||||
krb5_const_principal aname,
|
||||
set_result_f set_res_f, void *set_res_ctx)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *an2ln_db_fname;
|
||||
heim_db_t dbh = NULL;
|
||||
heim_dict_t db_options;
|
||||
heim_data_t k, v;
|
||||
heim_error_t error;
|
||||
char *unparsed = NULL;
|
||||
char *value = NULL;
|
||||
|
||||
_krb5_load_db_plugins(context);
|
||||
heim_base_once_f(&sorted_text_db_init_once, NULL, sorted_text_db_init_f);
|
||||
|
||||
if (strncmp(rule, "DB:", strlen("DB:") != 0))
|
||||
return KRB5_PLUGIN_NO_HANDLE;
|
||||
|
||||
an2ln_db_fname = &rule[strlen("DB:")];
|
||||
if (!*an2ln_db_fname)
|
||||
return KRB5_PLUGIN_NO_HANDLE;
|
||||
|
||||
ret = krb5_unparse_name(context, aname, &unparsed);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
db_options = heim_dict_create(11);
|
||||
if (db_options != NULL)
|
||||
heim_dict_set_value(db_options, HSTR("read-only"),
|
||||
heim_number_create(1));
|
||||
dbh = heim_db_create(NULL, an2ln_db_fname, db_options, &error);
|
||||
if (dbh == NULL) {
|
||||
krb5_set_error_message(context, heim_error_get_code(error),
|
||||
N_("Couldn't open aname2lname-text-db", ""));
|
||||
ret = KRB5_PLUGIN_NO_HANDLE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Binary search; file should be sorted (in C locale) */
|
||||
k = heim_data_ref_create(unparsed, strlen(unparsed), NULL);
|
||||
if (k == NULL)
|
||||
return krb5_enomem(context);
|
||||
v = heim_db_copy_value(dbh, NULL, k, &error);
|
||||
heim_release(k);
|
||||
if (v == NULL && error != NULL) {
|
||||
krb5_set_error_message(context, heim_error_get_code(error),
|
||||
N_("Lookup in aname2lname-text-db failed", ""));
|
||||
ret = heim_error_get_code(error);
|
||||
goto cleanup;
|
||||
} else if (v == NULL) {
|
||||
ret = KRB5_PLUGIN_NO_HANDLE;
|
||||
goto cleanup;
|
||||
} else {
|
||||
/* found */
|
||||
if (heim_data_get_length(v) == 0) {
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Principal mapped to empty username", ""));
|
||||
ret = KRB5_NO_LOCALNAME;
|
||||
goto cleanup;
|
||||
}
|
||||
ret = set_res_f(set_res_ctx, heim_data_get_ptr(v));
|
||||
heim_release(v);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
heim_release(dbh);
|
||||
free(unparsed);
|
||||
free(value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user