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:
@@ -53,7 +53,7 @@ LDADD = libkrb5.la \
|
||||
$(LIB_hcrypto) \
|
||||
$(top_builddir)/lib/asn1/libasn1.la \
|
||||
$(top_builddir)/lib/wind/libwind.la \
|
||||
$(LIB_roken)
|
||||
$(LIB_heimbase) $(LIB_roken)
|
||||
|
||||
if PKINIT
|
||||
LIB_pkinit = ../hx509/libhx509.la
|
||||
@@ -142,6 +142,8 @@ dist_libkrb5_la_SOURCES = \
|
||||
crypto-rand.c \
|
||||
doxygen.c \
|
||||
data.c \
|
||||
db_plugin.c \
|
||||
db_plugin.h \
|
||||
deprecated.c \
|
||||
digest.c \
|
||||
eai_to_heim_errno.c \
|
||||
@@ -341,7 +343,7 @@ nodist_include_HEADERS = krb5_err.h heim_err.h k524_err.h
|
||||
|
||||
# XXX use nobase_include_HEADERS = krb5/locate_plugin.h
|
||||
krb5dir = $(includedir)/krb5
|
||||
krb5_HEADERS = locate_plugin.h send_to_kdc_plugin.h ccache_plugin.h an2ln_plugin.h
|
||||
krb5_HEADERS = locate_plugin.h send_to_kdc_plugin.h ccache_plugin.h an2ln_plugin.h db_plugin.h
|
||||
|
||||
build_HEADERZ = \
|
||||
$(krb5_HEADERS) \
|
||||
|
@@ -66,6 +66,7 @@ libkrb5_OBJS = \
|
||||
$(OBJ)\crypto-pk.obj \
|
||||
$(OBJ)\crypto-rand.obj \
|
||||
$(OBJ)\data.obj \
|
||||
$(OBJ)\db_plugin.obj \
|
||||
$(OBJ)\deprecated.obj \
|
||||
$(OBJ)\digest.obj \
|
||||
$(OBJ)\dll.obj \
|
||||
@@ -329,8 +330,13 @@ $(OBJ)\k524_err.c $(OBJ)\k524_err.h: k524_err.et
|
||||
#----------------------------------------------------------------------
|
||||
# libkrb5
|
||||
|
||||
$(LIBKRB5): $(libkrb5_OBJS) $(libkrb5_gen_OBJS)
|
||||
$(LIBCON)
|
||||
$(LIBKRB5): $(libkrb5_OBJS) $(libkrb5_gen_OBJS)
|
||||
$(LIBCON_C) -OUT:$@ $(LIBHEIMBASE) @<<
|
||||
$(libkrb5_OBJS: =
|
||||
)
|
||||
$(libkrb5_gen_OBJS: =
|
||||
)
|
||||
<<
|
||||
|
||||
all:: $(LIBKRB5)
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
31
lib/krb5/db_plugin.c
Normal file
31
lib/krb5/db_plugin.c
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
*/
|
||||
|
||||
#include "krb5_locl.h"
|
||||
#include "db_plugin.h"
|
||||
|
||||
/* Default plugin (DB using binary search of sorted text file) follows */
|
||||
static heim_base_once_t db_plugins_once = HEIM_BASE_ONCE_INIT;
|
||||
|
||||
static krb5_error_code KRB5_LIB_CALL
|
||||
db_plugins_plcallback(krb5_context context, const void *plug, void *plugctx,
|
||||
void *userctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
db_plugins_init(void *arg)
|
||||
{
|
||||
krb5_context context = arg;
|
||||
(void)_krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_DB,
|
||||
KRB5_PLUGIN_DB_VERSION_0, 0, NULL,
|
||||
db_plugins_plcallback);
|
||||
}
|
||||
|
||||
void
|
||||
_krb5_load_db_plugins(krb5_context context)
|
||||
{
|
||||
heim_base_once_f(&db_plugins_once, context, db_plugins_init);
|
||||
}
|
||||
|
68
lib/krb5/db_plugin.h
Normal file
68
lib/krb5/db_plugin.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Secure Endpoints Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef HEIMDAL_KRB5_DB_PLUGIN_H
|
||||
#define HEIMDAL_KRB5_DB_PLUGIN_H 1
|
||||
|
||||
#define KRB5_PLUGIN_DB "krb5_db_plug"
|
||||
#define KRB5_PLUGIN_DB_VERSION_0 0
|
||||
|
||||
/** @struct krb5plugin_db_ftable_desc
|
||||
*
|
||||
* @brief Description of the krb5 DB plugin facility.
|
||||
*
|
||||
* The krb5_aname_to_lname(3) function's DB rule is pluggable. The
|
||||
* plugin is named KRB5_PLUGIN_DB ("krb5_db_plug"), with a single minor
|
||||
* version, KRB5_PLUGIN_DB_VERSION_0 (0).
|
||||
*
|
||||
* The plugin consists of a data symbol referencing a structure of type
|
||||
* krb5plugin_db_ftable_desc, with three fields:
|
||||
*
|
||||
* @param init Plugin initialization function (see krb5-plugin(7))
|
||||
*
|
||||
* @param minor_version The plugin minor version number (0)
|
||||
*
|
||||
* @param fini Plugin finalization function
|
||||
*
|
||||
* The init entry point is expected to call heim_db_register(). The
|
||||
* fini entry point is expected to do nothing.
|
||||
*
|
||||
* @ingroup krb5_support
|
||||
*/
|
||||
typedef struct krb5plugin_db_ftable_desc {
|
||||
int minor_version;
|
||||
krb5_error_code (*init)(krb5_context, void **);
|
||||
void (*fini)(void *);
|
||||
} krb5plugin_db_ftable;
|
||||
|
||||
#endif /* HEIMDAL_KRB5_DB_PLUGIN_H */
|
||||
|
@@ -41,6 +41,7 @@
|
||||
.In krb5.h
|
||||
.In krb5/an2ln_plugin.h
|
||||
.In krb5/ccache_plugin.h
|
||||
.In krb5/db_plugin.h
|
||||
.In krb5/kuserok_plugin.h
|
||||
.In krb5/locate_plugin.h
|
||||
.In krb5/send_to_kdc_plugin.h
|
||||
@@ -48,8 +49,8 @@
|
||||
Heimdal has a plugin interface. Plugins may be statically linked into
|
||||
Heimdal and registered via the
|
||||
.Xr krb5_plugin_register 3
|
||||
function, or they may be loaded from shared objects present in the
|
||||
Heimdal plugins directories.
|
||||
function, or they may be dynamically loaded from shared objects present
|
||||
in the Heimdal plugins directories.
|
||||
.Pp
|
||||
Plugins consist of a C struct whose struct name is given in the
|
||||
associated header file, such as, for example,
|
||||
@@ -57,7 +58,7 @@ associated header file, such as, for example,
|
||||
and a pointer to which is either registered via
|
||||
.Xr krb5_plugin_register 3
|
||||
or found in a shared object via a symbol lookup for the symbol name
|
||||
defined in the associated header file (e.g., "kuserok-plugin" for the
|
||||
defined in the associated header file (e.g., "kuserok" for the
|
||||
plugin for
|
||||
.Xr krb5_kuserok 3
|
||||
).
|
||||
@@ -81,16 +82,26 @@ be output through the init function's second argument.
|
||||
plugin's context to be destroyed, and returning void.
|
||||
.El
|
||||
.Pp
|
||||
Each plugin type must add one or more fields to this struct following
|
||||
the above three. Plugins are typically invoked in no particular order until
|
||||
one succeeds or fails, or all return a special return value such as
|
||||
KRB5_PLUGIN_NO_HANDLE to indicate that the plugin was not applicable. Most
|
||||
plugin types obtain deterministic plugin behavior in spite of the
|
||||
non-deterministic invokation order by, for example, invoking all plugins for
|
||||
each "rule" and passing the rule to each plugin with the expectation that just
|
||||
one plugin will match any given rule.
|
||||
Each plugin type must add zero or more fields to this struct following
|
||||
the above three. Plugins are typically invoked in no particular order
|
||||
until one succeeds or fails, or all return a special return value such
|
||||
as KRB5_PLUGIN_NO_HANDLE to indicate that the plugin was not applicable.
|
||||
Most plugin types obtain deterministic plugin behavior in spite of the
|
||||
non-deterministic invokation order by, for example, invoking all plugins
|
||||
for each "rule" and passing the rule to each plugin with the expectation
|
||||
that just one plugin will match any given rule.
|
||||
.Pp
|
||||
The krb5-kuserok plugin adds a single field to its struct: a pointer to
|
||||
There is a database plugin system intended for many of the uses of
|
||||
databases in Heimdal. The plugin is expected to call
|
||||
.Xr heim_db_register 3
|
||||
from its
|
||||
.Va init
|
||||
entry point to register a DB type. The DB plugin's
|
||||
.Va fini
|
||||
function must do nothing, and the plugin must not provide any other
|
||||
entry points.
|
||||
.Pp
|
||||
The krb5_kuserok plugin adds a single field to its struct: a pointer to
|
||||
a function that implements kuserok functionality with the following
|
||||
form:
|
||||
.Bd -literal -offset indent
|
||||
|
@@ -94,6 +94,7 @@ libroken_la_OBJS = \
|
||||
$(OBJ)\strerror_r.obj \
|
||||
$(OBJ)\strlcat.obj \
|
||||
$(OBJ)\strlcpy.obj \
|
||||
$(OBJ)\strndup.obj \
|
||||
$(OBJ)\strpool.obj \
|
||||
$(OBJ)\strptime.obj \
|
||||
$(OBJ)\strsep.obj \
|
||||
|
Reference in New Issue
Block a user