]> eyrie.org Git - kerberos/krb5-sync.git/commitdiff
Use a vector for ad_instances
authorRuss Allbery <eagle@eyrie.org>
Wed, 4 Dec 2013 23:29:45 +0000 (15:29 -0800)
committerRuss Allbery <eagle@eyrie.org>
Wed, 4 Dec 2013 23:29:45 +0000 (15:29 -0800)
Rather than doing complicated parsing of the space-separated list
of acceptable instances, add support for vectors and parsing the
configuration option into a vector, which makes the code fairly
simple and straightforward.

Makefile.am
plugin/api.c
plugin/config.c
plugin/internal.h
plugin/vector.c [new file with mode: 0644]

index ee2dcf3ac88e90fdec1370f84ac6fd82f4e09c05..eb90b675f542a61ff1ac3780d4bf26f221a706e6 100644 (file)
@@ -34,7 +34,7 @@ moduledir = $(libdir)/krb5/plugins/kadm5_hook
 module_LTLIBRARIES = plugin/krb5_sync.la
 plugin_krb5_sync_la_SOURCES = plugin/ad.c plugin/api.c plugin/config.c     \
        plugin/error.c plugin/internal.h plugin/heimdal.c plugin/instance.c \
-       plugin/mit.c plugin/queue.c
+       plugin/mit.c plugin/queue.c plugin/vector.c
 plugin_krb5_sync_la_CPPFLAGS = $(KADM5SRV_CPPFLAGS) $(LDAP_CPPFLAGS) \
        $(AM_CPPFLAGS)
 plugin_krb5_sync_la_LDFLAGS = -module -avoid-version $(KADM5SRV_LDFLAGS) \
index a2c5c7f892dfb5c40d3db9bf1de65a4a83d6a861..7aa147224b2103aa10cf812733b41d023ee0ed28 100644 (file)
@@ -54,7 +54,7 @@ sync_init(krb5_context ctx, kadm5_hook_modinfo **result)
     sync_config_string(ctx, "ad_ldap_base", &config->ad_ldap_base);
 
     /* Get allowed instances from krb5.conf. */
-    sync_config_string(ctx, "ad_instances", &config->ad_instances);
+    sync_config_list(ctx, "ad_instances", &config->ad_instances);
 
     /* See if we're propagating an instance to the base account in AD. */
     sync_config_string(ctx, "ad_base_instance", &config->ad_base_instance);
@@ -78,53 +78,33 @@ sync_init(krb5_context ctx, kadm5_hook_modinfo **result)
 void
 sync_close(krb5_context ctx UNUSED, kadm5_hook_modinfo *config)
 {
+    free(config->ad_admin_server);
+    free(config->ad_base_instance);
+    sync_vector_free(config->ad_instances);
     free(config->ad_keytab);
+    free(config->ad_ldap_base);
     free(config->ad_principal);
     free(config->ad_realm);
-    free(config->ad_admin_server);
-    free(config->ad_base_instance);
     free(config->queue_dir);
     free(config);
 }
 
 
 /*
- * Given the list of allowed principals as a space-delimited string and the
- * instance of a principal, returns true if that instance is allowed and false
- * otherwise.
+ * Given the configuration and the instance of a principal, returns true if
+ * that instance is allowed and false otherwise.
  */
 static bool
-instance_allowed(const char *allowed, const char *instance)
+instance_allowed(kadm5_hook_modinfo *config, const char *instance)
 {
-    const char *p, *i, *end;
-    bool checking, okay;
+    size_t i;
 
-    if (allowed == NULL || instance == NULL)
-        return false;
-    i = instance;
-    end = i + strlen(instance);
-    checking = true;
-    okay = false;
-    for (p = allowed; *p != '\0'; p++) {
-        if (*p == ' ') {
-            if (okay && i == end)
-                break;
-            okay = false;
-            checking = true;
-            i = instance;
-        } else if (checking && (i == end || *p != *i)) {
-            okay = false;
-            checking = false;
-            i = instance;
-        } else if (checking && *p == *i) {
-            okay = true;
-            i++;
-        }
-    }
-    if (okay && (*p == '\0' || *p == ' ') && i == end)
-        return true;
-    else
+    if (config->ad_instances == NULL || instance == NULL)
         return false;
+    for (i = 0; i < config->ad_instances->count; i++)
+        if (strcmp(config->ad_instances->strings[i], instance) == 0)
+            return true;
+    return false;
 }
 
 
@@ -185,7 +165,7 @@ principal_allowed(kadm5_hook_modinfo *config, krb5_context ctx,
         const char *instance;
 
         instance = krb5_principal_get_comp_string(ctx, principal, 1);
-        if (!instance_allowed(config->ad_instances, instance)) {
+        if (!instance_allowed(config, instance)) {
             code = krb5_unparse_name(ctx, principal, &display);
             if (code != 0)
                 display = NULL;
index 6ef8e1c250c29ac68c2760629f89ec522adf7194..2b4e0a128ddcdd9b084229e25b9e009da9cd36d2 100644 (file)
@@ -127,6 +127,40 @@ sync_config_boolean(krb5_context ctx, const char *opt, bool *result)
 }
 
 
+/*
+ * Load a list option from Kerberos appdefaults.  Takes the Kerberos context,
+ * the option, and the result location.  The option is read as a string and
+ * the split on spaces and tabs into a list.
+ *
+ * This requires an annoying workaround because one cannot specify a default
+ * value of NULL with MIT Kerberos, since MIT Kerberos unconditionally calls
+ * strdup on the default value.  There's also no way to determine if memory
+ * allocation failed while parsing or while setting the default value.
+ */
+krb5_error_code
+sync_config_list(krb5_context ctx, const char *opt, struct vector **result)
+{
+    realm_type realm;
+    char *value = NULL;
+
+    /* Obtain the string from [appdefaults]. */
+    realm = default_realm(ctx);
+    krb5_appdefault_string(ctx, "krb5-sync", realm, opt, "", &value);
+    free_default_realm(ctx, realm);
+
+    /* If we got something back, store it in result. */
+    if (value != NULL) {
+        if (value[0] != '\0') {
+            *result = sync_vector_split_multi(value, " \t", *result);
+            if (*result == NULL)
+                return sync_error_system(ctx, "cannot allocate memory");
+        }
+        krb5_free_string(ctx, value);
+    }
+    return 0;
+}
+
+
 /*
  * Load a string option from Kerberos appdefaults.  Takes the Kerberos
  * context, the option, and the result location.
index ce94a1ec96ae099e3793b0211d1dad123bc2b2e0..d0b57401c969a7d7e0bb440cce0520592a834e06 100644 (file)
 typedef struct kadm5_hook_modinfo_st kadm5_hook_modinfo;
 #endif
 
+/* Used to store a list of strings, managed by the sync_vector_* functions. */
+struct vector {
+    size_t count;
+    size_t allocated;
+    char **strings;
+};
+
 /*
  * Local configuration information for the module.  This contains all the
  * parameters that are read from the krb5-sync sub-section of the appdefaults
@@ -36,7 +43,7 @@ typedef struct kadm5_hook_modinfo_st kadm5_hook_modinfo;
 struct kadm5_hook_modinfo_st {
     char *ad_admin_server;
     char *ad_base_instance;
-    char *ad_instances;
+    struct vector *ad_instances;
     char *ad_keytab;
     char *ad_ldap_base;
     char *ad_principal;
@@ -91,6 +98,32 @@ krb5_error_code sync_queue_write(kadm5_hook_modinfo *, krb5_context,
                                  krb5_principal, const char *operation,
                                  const char *password);
 
+/*
+ * Manage vectors, which are counted lists of strings.  The functions that
+ * return a boolean return false if memory allocation fails.
+ */
+struct vector *sync_vector_new(void)
+    __attribute__((__malloc__));
+bool sync_vector_add(struct vector *, const char *string)
+    __attribute__((__nonnull__));
+void sync_vector_free(struct vector *)
+    __attribute__((__nonnull__));
+
+/*
+ * vector_split_multi splits on a set of characters.  If the vector argument
+ * is NULL, a new vector is allocated; otherwise, the provided one is reused.
+ * Returns NULL on memory allocation failure, after which the provided vector
+ * may have been modified to only have partial results.
+ *
+ * Empty strings will yield zero-length vectors.  Adjacent delimiters are
+ * treated as a single delimiter by vector_split_multi.  Any leading or
+ * trailing delimiters are ignored, so this function will never create
+ * zero-length strings (similar to the behavior of strtok).
+ */
+struct vector *sync_vector_split_multi(const char *string, const char *seps,
+                                       struct vector *)
+    __attribute__((__nonnull__(1, 2)));
+
 /*
  * Obtain configuration settings from krb5.conf.  These are wrappers around
  * the krb5_appdefault_* APIs that handle setting the section name, obtaining
@@ -99,6 +132,8 @@ krb5_error_code sync_queue_write(kadm5_hook_modinfo *, krb5_context,
  */
 void sync_config_boolean(krb5_context, const char *, bool *)
     __attribute__((__nonnull__));
+krb5_error_code sync_config_list(krb5_context, const char *, struct vector **)
+    __attribute__((__nonnull__));
 void sync_config_string(krb5_context, const char *, char **)
     __attribute__((__nonnull__));
 
diff --git a/plugin/vector.c b/plugin/vector.c
new file mode 100644 (file)
index 0000000..69ed8b3
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Vector handling (counted lists of char *'s).
+ *
+ * A vector is a table for handling a list of strings with less overhead than
+ * linked list.  The intention is for vectors, once allocated, to be reused;
+ * this saves on memory allocations once the array of char *'s reaches a
+ * stable size.
+ *
+ * This is based on the from rra-c-util util/vector.c library, but that
+ * library uses xmalloc routines to exit the program if memory allocation
+ * fails.  This is a modified version of the vector library that instead
+ * returns false on failure to allocate memory, allowing the caller to do
+ * appropriate recovery.
+ *
+ * Vectors require list of strings, not arbitrary binary data, and cannot
+ * handle data elements containing nul characters.
+ *
+ * Only the portions of the vector library needed by this module is
+ * implemented.
+ *
+ * Written by Russ Allbery <eagle@eyrie.org>
+ * Copyright 2013
+ *     The Board of Trustees of the Leland Stanford Junior University
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#include <config.h>
+#include <portable/system.h>
+
+#include <plugin/internal.h>
+
+
+/*
+ * Allocate a new, empty vector.  Returns NULL if memory allocation fails.
+ */
+struct vector *
+sync_vector_new(void)
+{
+    struct vector *vector;
+
+    vector = calloc(1, sizeof(struct vector));
+    if (vector == NULL)
+        return NULL;
+    return vector;
+}
+
+
+/*
+ * Resize a vector (using realloc to resize the table).  Return false if
+ * memory allocation fails.
+ */
+static bool
+sync_vector_resize(struct vector *vector, size_t size)
+{
+    size_t i;
+    char **strings;
+
+    /* If we're shrinking the vector, free the excess strings. */
+    if (vector->count > size) {
+        for (i = size; i < vector->count; i++)
+            free(vector->strings[i]);
+        vector->count = size;
+    }
+
+    /* If resizing to zero, free all storage.  Otherwise, realloc. */
+    if (size == 0) {
+        free(vector->strings);
+        vector->strings = NULL;
+    } else {
+        strings = realloc(vector->strings, size * sizeof(char *));
+        if (strings == NULL)
+            return false;
+        vector->strings = strings;
+    }
+    vector->allocated = size;
+    return true;
+}
+
+
+/*
+ * Add a new string to the vector, resizing the vector as necessary.  The
+ * vector is resized an element at a time; if a lot of resizes are expected,
+ * vector_resize should be called explicitly with a more suitable size.
+ * Return false if memory allocation fails.
+ */
+bool
+sync_vector_add(struct vector *vector, const char *string)
+{
+    size_t next = vector->count;
+
+    if (vector->count == vector->allocated)
+        if (!sync_vector_resize(vector, vector->allocated + 1))
+            return false;
+    vector->strings[next] = strdup(string);
+    if (vector->strings[next] == NULL)
+        return false;
+    vector->count++;
+    return true;
+}
+
+
+/*
+ * Empty a vector but keep the allocated memory for the pointer table.
+ */
+static void
+sync_vector_clear(struct vector *vector)
+{
+    size_t i;
+
+    for (i = 0; i < vector->count; i++)
+        if (vector->strings[i] != NULL)
+            free(vector->strings[i]);
+    vector->count = 0;
+}
+
+
+/*
+ * Free a vector completely.
+ */
+void
+sync_vector_free(struct vector *vector)
+{
+    if (vector == NULL)
+        return;
+    sync_vector_clear(vector);
+    free(vector->strings);
+    free(vector);
+}
+
+
+/*
+ * Given a vector that we may be reusing, clear it out.  If the first argument
+ * is NULL, allocate a new vector.  Used by vector_split*.  Returns NULL if
+ * memory allocation fails.
+ */
+static struct vector *
+sync_vector_reuse(struct vector *vector)
+{
+    if (vector == NULL)
+        return sync_vector_new();
+    else {
+        sync_vector_clear(vector);
+        return vector;
+    }
+}
+
+
+/*
+ * Given a string and a set of separators expressed as a string, count the
+ * number of strings that it will split into when splitting on those
+ * separators.
+ */
+static size_t
+split_multi_count(const char *string, const char *seps)
+{
+    const char *p;
+    size_t count;
+
+    /* If the string is empty, the count of components is zero. */
+    if (*string == '\0')
+        return 0;
+
+    /* Otherwise, walk the string looking for non-consecutive separators. */
+    for (count = 1, p = string + 1; *p != '\0'; p++)
+        if (strchr(seps, *p) != NULL && strchr(seps, p[-1]) == NULL)
+            count++;
+
+    /*
+     * If the string ends in separators, we've overestimated the number of
+     * strings by one.
+     */
+    if (strchr(seps, p[-1]) != NULL)
+        count--;
+    return count;
+}
+
+
+/*
+ * Given a string, split it at any of the provided separators to form a
+ * vector, copying each string segment.  If the third argument isn't NULL,
+ * reuse that vector; otherwise, allocate a new one.  Any number of
+ * consecutive separators are considered a single separator.  Returns NULL on
+ * memory allocation failure, after which the provided vector may only have
+ * partial results.
+ */
+struct vector *
+sync_vector_split_multi(const char *string, const char *seps,
+                        struct vector *vector)
+{
+    const char *p, *start;
+    size_t i, count;
+    bool created = false;
+
+    /* Set up the vector we'll use to store the results. */
+    if (vector == NULL)
+        created = true;
+    vector = sync_vector_reuse(vector);
+    if (vector == NULL)
+        return NULL;
+
+    /* Count how big a vector we need and resize accordingly. */
+    count = split_multi_count(string, seps);
+    if (vector->allocated < count && !sync_vector_resize(vector, count))
+        goto fail;
+
+    /* Now, walk the string and build the components. */
+    vector->count = 0;
+    for (start = string, p = string, i = 0; *p != '\0'; p++)
+        if (strchr(seps, *p) != NULL) {
+            if (start != p) {
+                vector->strings[i] = strndup(start, (size_t) (p - start));
+                if (vector->strings[i] == NULL)
+                    goto fail;
+                i++;
+                vector->count++;
+            }
+            start = p + 1;
+        }
+
+    /* If there is anything left in the string, we have one more component. */
+    if (start != p) {
+        vector->strings[i] = strndup(start, (size_t) (p - start));
+        if (vector->strings[i] == NULL)
+            goto fail;
+        vector->count++;
+    }
+    return vector;
+
+fail:
+    if (created)
+        sync_vector_free(vector);
+    return NULL;
+}