From 237e57c297708c8009cf2af4833b78abc4e05bbc Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Wed, 21 Sep 2011 16:28:54 +0000 Subject: Add KRB5_TL_STRING_ATTRS and libkdb5 accessors git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25214 dc483132-0cff-0310-8789-dd5450dbe970 --- src/include/kdb.h | 34 +++++++ src/lib/kdb/Makefile.in | 10 +- src/lib/kdb/kdb5.c | 178 ++++++++++++++++++++++++++++++++++ src/lib/kdb/libkdb5.exports | 5 + src/lib/kdb/t_stringattr.c | 94 ++++++++++++++++++ src/lib/krb5/error_tables/kdb5_err.et | 1 + 6 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 src/lib/kdb/t_stringattr.c diff --git a/src/include/kdb.h b/src/include/kdb.h index b666225e6..31f48b151 100644 --- a/src/include/kdb.h +++ b/src/include/kdb.h @@ -145,6 +145,13 @@ typedef struct _krb5_tl_data { krb5_octet * tl_data_contents; } krb5_tl_data; +/* String attributes (currently stored inside tl-data) map C string keys to + * values. They can be set via kadmin and consumed by KDC plugins. */ +typedef struct krb5_string_attr_st { + char *key; + char *value; +} krb5_string_attr; + /* * If this ever changes up the version number and make the arrays be as * big as necessary. @@ -235,6 +242,10 @@ typedef struct __krb5_key_salt_tuple { #define KRB5_TL_ACTKVNO 0x0009 #define KRB5_TL_MKEY_AUX 0x000a +/* String attributes may not always be represented in tl-data. kadmin clients + * must use the modify_strings and get_strings RPCs. */ +#define KRB5_TL_STRING_ATTRS 0x000b + #define KRB5_TL_PAC_LOGON_INFO 0x0100 /* NDR encoded validation info */ #define KRB5_TL_SERVER_REFERRAL 0x0200 /* ASN.1 encoded ServerReferralInfo */ #define KRB5_TL_SVR_REFERRAL_DATA 0x0300 /* ASN.1 encoded PA-SVR-REFERRAL-DATA */ @@ -538,6 +549,23 @@ krb5_dbe_lookup_last_admin_unlock( krb5_context context, krb5_db_entry * entry, krb5_timestamp * stamp); +/* Retrieve the set of string attributes in entry, in no particular order. + * Free *strings_out with krb5_dbe_free_strings when done. */ +krb5_error_code +krb5_dbe_get_strings(krb5_context context, krb5_db_entry *entry, + krb5_string_attr **strings_out, int *count_out); + +/* Retrieve a single string attribute from entry, or NULL if there is no + * attribute for key. Free *value_out with krb5_dbe_free_string when done. */ +krb5_error_code +krb5_dbe_get_string(krb5_context context, krb5_db_entry *entry, + const char *key, char **value_out); + +/* Change or add a string attribute in entry, or delete it if value is NULL. */ +krb5_error_code +krb5_dbe_set_string(krb5_context context, krb5_db_entry *entry, + const char *key, const char *value); + krb5_error_code krb5_dbe_delete_tl_data( krb5_context context, krb5_db_entry * entry, @@ -741,6 +769,12 @@ krb5_dbe_free_mkey_aux_list(krb5_context, krb5_mkey_aux_node *); void krb5_dbe_free_tl_data(krb5_context, krb5_tl_data *); +void +krb5_dbe_free_strings(krb5_context, krb5_string_attr *, int count); + +void +krb5_dbe_free_string(krb5_context, char *); + #define KRB5_KDB_DEF_FLAGS 0 #define KDB_MAX_DB_NAME 128 diff --git a/src/lib/kdb/Makefile.in b/src/lib/kdb/Makefile.in index c49e1b56c..e8f44ae2f 100644 --- a/src/lib/kdb/Makefile.in +++ b/src/lib/kdb/Makefile.in @@ -1,5 +1,6 @@ mydir=lib$(S)kdb BUILDTOP=$(REL)..$(S).. +RUN_SETUP = @KRB5_RUN_ENV@ KRB5_RUN_ENV = @KRB5_RUN_ENV@ KRB5_CONFIG_SETUP = KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf ; export KRB5_CONFIG ; PROG_LIBPATH=-L$(TOPLIBD) @@ -56,12 +57,19 @@ STLIBOBJS= \ all-unix:: all-liblinks install-unix:: install-libs clean-unix:: clean-liblinks clean-libs clean-libobjs - $(RM) adb_err.c adb_err.h + $(RM) adb_err.c adb_err.h t_stringattr.o t_stringattr +check-unix:: t_stringattr + KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf; export KRB5_CONFIG; \ + $(RUN_SETUP) $(VALGRIND) ./t_stringattr generate-files-mac: darwin.exports depend:: adb_err.h +t_stringattr: t_stringattr.o $(KDB5_DEPLIBS) $(KADM_COMM_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o $@ t_stringattr.o $(KDB5_LIBS) $(KADM_COMM_LIBS) \ + $(KRB5_BASE_LIBS) + @lib_frag@ @libobj_frag@ diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c index b82bbcb40..4dfe66151 100644 --- a/src/lib/kdb/kdb5.c +++ b/src/lib/kdb/kdb5.c @@ -162,6 +162,27 @@ krb5_dbe_free_tl_data(krb5_context context, krb5_tl_data *tl_data) } } +void +krb5_dbe_free_strings(krb5_context context, krb5_string_attr *strings, + int count) +{ + int i; + + if (strings == NULL) + return; + for (i = 0; i < count; i++) { + free(strings[i].key); + free(strings[i].value); + } + free(strings); +} + +void +krb5_dbe_free_string(krb5_context context, char *string) +{ + free(string); +} + /* Set *section to the appropriate section to use for a database module's * profile queries. The caller must free the result. */ static krb5_error_code @@ -1960,6 +1981,163 @@ krb5_dbe_update_last_admin_unlock(krb5_context context, krb5_db_entry *entry, return (krb5_dbe_update_tl_data(context, entry, &tl_data)); } +/* + * Prepare to iterate over the string attributes of entry. The returned + * pointers are aliases into entry's tl_data (or into an empty string literal) + * and remain valid until the entry's tl_data is changed. + */ +static krb5_error_code +begin_attrs(krb5_context context, krb5_db_entry *entry, const char **pos_out, + const char **end_out) +{ + krb5_error_code code; + krb5_tl_data tl_data; + + *pos_out = *end_out = NULL; + tl_data.tl_data_type = KRB5_TL_STRING_ATTRS; + code = krb5_dbe_lookup_tl_data(context, entry, &tl_data); + if (code) + return code; + + /* Copy the current mapping to buf, updating key with value if found. */ + *pos_out = (const char *)tl_data.tl_data_contents; + *end_out = *pos_out + tl_data.tl_data_length; + return 0; +} + +/* Find the next key and value pair in *pos and update *pos. */ +static krb5_boolean +next_attr(const char **pos, const char *end, const char **key_out, + const char **val_out) +{ + const char *key, *key_end, *val, *val_end; + + *key_out = *val_out = NULL; + if (*pos == end) + return FALSE; + key = *pos; + key_end = memchr(key, '\0', end - key); + if (key_end == NULL) /* Malformed representation; give up. */ + return FALSE; + val = key_end + 1; + val_end = memchr(val, '\0', end - val); + if (val_end == NULL) /* Malformed representation; give up. */ + return FALSE; + + *key_out = key; + *val_out = val; + *pos = val_end + 1; + return TRUE; +} + +krb5_error_code +krb5_dbe_get_strings(krb5_context context, krb5_db_entry *entry, + krb5_string_attr **strings_out, int *count_out) +{ + krb5_error_code code; + const char *pos, *end, *mapkey, *mapval; + char *key = NULL, *val = NULL; + krb5_string_attr *strings = NULL, *newstrings; + int count = 0; + + *strings_out = NULL; + *count_out = 0; + code = begin_attrs(context, entry, &pos, &end); + if (code) + return code; + + while (next_attr(&pos, end, &mapkey, &mapval)) { + /* Add a copy of mapkey and mapvalue to strings. */ + key = strdup(mapkey); + val = strdup(mapval); + newstrings = realloc(strings, (count + 1) * sizeof(*strings)); + if (key == NULL || val == NULL || newstrings == NULL) { + free(key); + free(val); + krb5_dbe_free_strings(context, strings, count); + return ENOMEM; + } + strings = newstrings; + strings[count].key = key; + strings[count].value = val; + count++; + } + + *strings_out = strings; + *count_out = count; + return 0; +} + +krb5_error_code +krb5_dbe_get_string(krb5_context context, krb5_db_entry *entry, + const char *key, char **value_out) +{ + krb5_error_code code; + const char *pos, *end, *mapkey, *mapval; + + *value_out = NULL; + code = begin_attrs(context, entry, &pos, &end); + if (code) + return code; + while (next_attr(&pos, end, &mapkey, &mapval)) { + if (strcmp(mapkey, key) == 0) { + *value_out = strdup(mapval); + return (*value_out == NULL) ? ENOMEM : 0; + } + } + + return 0; +} + +krb5_error_code +krb5_dbe_set_string(krb5_context context, krb5_db_entry *entry, + const char *key, const char *value) +{ + krb5_error_code code; + const char *pos, *end, *mapkey, *mapval; + struct k5buf buf; + krb5_boolean found = FALSE; + krb5_tl_data tl_data; + ssize_t len; + + /* Copy the current mapping to buf, updating key with value if found. */ + code = begin_attrs(context, entry, &pos, &end); + if (code) + return code; + krb5int_buf_init_dynamic(&buf); + while (next_attr(&pos, end, &mapkey, &mapval)) { + if (strcmp(mapkey, key) == 0) { + if (value != NULL) { + krb5int_buf_add_len(&buf, mapkey, strlen(mapkey) + 1); + krb5int_buf_add_len(&buf, value, strlen(value) + 1); + } + found = TRUE; + } else { + krb5int_buf_add_len(&buf, mapkey, strlen(mapkey) + 1); + krb5int_buf_add_len(&buf, mapval, strlen(mapval) + 1); + } + } + + /* If key wasn't found in the map, add a new entry for it. */ + if (!found && value != NULL) { + krb5int_buf_add_len(&buf, key, strlen(key) + 1); + krb5int_buf_add_len(&buf, value, strlen(value) + 1); + } + + len = krb5int_buf_len(&buf); + if (len == -1) + return ENOMEM; + if (len > 65535) + return KRB5_KDB_STRINGS_TOOLONG; + tl_data.tl_data_type = KRB5_TL_STRING_ATTRS; + tl_data.tl_data_contents = (krb5_octet *)krb5int_buf_data(&buf); + tl_data.tl_data_length = len; + + code = krb5_dbe_update_tl_data(context, entry, &tl_data); + krb5int_free_buf(&buf); + return code; +} + krb5_error_code krb5_dbe_delete_tl_data(krb5_context context, krb5_db_entry *entry, krb5_int16 tl_data_type) diff --git a/src/lib/kdb/libkdb5.exports b/src/lib/kdb/libkdb5.exports index f9f66eba2..3ea179a46 100644 --- a/src/lib/kdb/libkdb5.exports +++ b/src/lib/kdb/libkdb5.exports @@ -43,7 +43,11 @@ krb5_dbe_free_actkvno_list krb5_dbe_free_key_data_contents krb5_dbe_free_mkey_aux_list krb5_dbe_free_key_list +krb5_dbe_free_string +krb5_dbe_free_strings krb5_dbe_get_mkvno +krb5_dbe_get_string +krb5_dbe_get_strings krb5_dbe_lookup_last_admin_unlock krb5_dbe_lookup_last_pwd_change krb5_dbe_lookup_actkvno @@ -52,6 +56,7 @@ krb5_dbe_lookup_mkvno krb5_dbe_lookup_mod_princ_data krb5_dbe_lookup_tl_data krb5_dbe_search_enctype +krb5_dbe_set_string krb5_dbe_update_actkvno krb5_dbe_update_last_admin_unlock krb5_dbe_update_last_pwd_change diff --git a/src/lib/kdb/t_stringattr.c b/src/lib/kdb/t_stringattr.c new file mode 100644 index 000000000..8b943124c --- /dev/null +++ b/src/lib/kdb/t_stringattr.c @@ -0,0 +1,94 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/kdb/t_strings.c - Test program for KDB string attribute functions */ +/* + * Copyright (C) 2011 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include + +/* + * This program exercises the KDB entry APIs for string attributes. It relies + * on some implementation knowledge: + * + * - The APIs will work on a DB entry initialized to all zeros (because it has + * an empty tl_data list). + * + * - Attribute order in krb5_dbe_get_strings matches attribute insertion order. + */ + +int +main() +{ + krb5_db_entry *ent; + krb5_context context; + krb5_string_attr *strings; + char *val; + int count; + + assert(krb5_init_context(&context) == 0); + + /* Start with an empty entry. */ + ent = krb5_db_alloc(context, NULL, sizeof(*ent)); + if (ent == NULL) + return ENOMEM; + memset(ent, 0, sizeof(*ent)); + + /* Check that the entry has no strings to start. */ + assert(krb5_dbe_get_strings(context, ent, &strings, &count) == 0); + assert(strings == NULL && count == 0); + krb5_dbe_free_strings(context, strings, count); + + /* Check that we get a null value querying a specific attribute. */ + assert(krb5_dbe_get_string(context, ent, "foo", &val) == 0); + assert(val == NULL); + + /* Set some attributes one at a time, including a deletion. */ + assert(krb5_dbe_set_string(context, ent, "eggs", "dozen") == 0); + assert(krb5_dbe_set_string(context, ent, "price", "right") == 0); + assert(krb5_dbe_set_string(context, ent, "eggs", NULL) == 0); + assert(krb5_dbe_set_string(context, ent, "time", "flies") == 0); + + /* Query each attribute. */ + assert(krb5_dbe_get_string(context, ent, "price", &val) == 0); + assert(strcmp(val, "right") == 0); + krb5_dbe_free_string(context, val); + assert(krb5_dbe_get_string(context, ent, "time", &val) == 0); + assert(strcmp(val, "flies") == 0); + krb5_dbe_free_string(context, val); + assert(krb5_dbe_get_string(context, ent, "eggs", &val) == 0); + assert(val == NULL); + + /* Query the list of attributes and verify it. */ + assert(krb5_dbe_get_strings(context, ent, &strings, &count) == 0); + assert(count == 2); + assert(strcmp(strings[0].key, "price") == 0); + assert(strcmp(strings[0].value, "right") == 0); + assert(strcmp(strings[1].key, "time") == 0); + assert(strcmp(strings[1].value, "flies") == 0); + krb5_dbe_free_strings(context, strings, count); + + krb5_db_free_principal(context, ent); + krb5_free_context(context); + return 0; +} diff --git a/src/lib/krb5/error_tables/kdb5_err.et b/src/lib/krb5/error_tables/kdb5_err.et index 6fb19d031..1b08ec1a6 100644 --- a/src/lib/krb5/error_tables/kdb5_err.et +++ b/src/lib/krb5/error_tables/kdb5_err.et @@ -84,5 +84,6 @@ ec KRB5_LOG_CORRUPT, "Update log is corrupt" ec KRB5_LOG_ERROR, "Generic update log error" ec KRB5_KDB_DBTYPE_MISMATCH, "Database module does not match KDC version" ec KRB5_KDB_POLICY_REF, "Policy is in use" +ec KRB5_KDB_STRINGS_TOOLONG, "Too much string mapping data" end -- cgit