aboutsummaryrefslogtreecommitdiff
path: root/pam-util/vector.c
diff options
context:
space:
mode:
Diffstat (limited to 'pam-util/vector.c')
-rw-r--r--pam-util/vector.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/pam-util/vector.c b/pam-util/vector.c
new file mode 100644
index 000000000000..012a9aef24a3
--- /dev/null
+++ b/pam-util/vector.c
@@ -0,0 +1,289 @@
+/*
+ * 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 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 used by PAM modules are
+ * implemented.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle@eyrie.org>
+ * Copyright 2017-2018 Russ Allbery <eagle@eyrie.org>
+ * Copyright 2010-2011, 2014
+ * The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Copying and distribution of this file, with or without modification, are
+ * permitted in any medium without royalty provided the copyright notice and
+ * this notice are preserved. This file is offered as-is, without any
+ * warranty.
+ *
+ * SPDX-License-Identifier: FSFAP
+ */
+
+#include <config.h>
+#include <portable/system.h>
+
+#include <pam-util/vector.h>
+
+
+/*
+ * Allocate a new, empty vector. Returns NULL if memory allocation fails.
+ */
+struct vector *
+vector_new(void)
+{
+ struct vector *vector;
+
+ vector = calloc(1, sizeof(struct vector));
+ vector->allocated = 1;
+ vector->strings = calloc(1, sizeof(char *));
+ return vector;
+}
+
+
+/*
+ * Allocate a new vector that's a copy of an existing vector. Returns NULL if
+ * memory allocation fails.
+ */
+struct vector *
+vector_copy(const struct vector *old)
+{
+ struct vector *vector;
+ size_t i;
+
+ vector = vector_new();
+ if (!vector_resize(vector, old->count)) {
+ vector_free(vector);
+ return NULL;
+ }
+ vector->count = old->count;
+ for (i = 0; i < old->count; i++) {
+ vector->strings[i] = strdup(old->strings[i]);
+ if (vector->strings[i] == NULL) {
+ vector_free(vector);
+ return NULL;
+ }
+ }
+ return vector;
+}
+
+
+/*
+ * Resize a vector (using reallocarray to resize the table). Return false if
+ * memory allocation fails.
+ */
+bool
+vector_resize(struct vector *vector, size_t size)
+{
+ size_t i;
+ char **strings;
+
+ if (vector->count > size) {
+ for (i = size; i < vector->count; i++)
+ free(vector->strings[i]);
+ vector->count = size;
+ }
+ if (size == 0)
+ size = 1;
+ strings = reallocarray(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
+vector_add(struct vector *vector, const char *string)
+{
+ size_t next = vector->count;
+
+ if (vector->count == vector->allocated)
+ if (!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.
+ */
+void
+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
+vector_free(struct vector *vector)
+{
+ if (vector == NULL)
+ return;
+ 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 *
+vector_reuse(struct vector *vector)
+{
+ if (vector == NULL)
+ return vector_new();
+ else {
+ 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 (*string == '\0')
+ return 0;
+ 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 *
+vector_split_multi(const char *string, const char *seps, struct vector *vector)
+{
+ const char *p, *start;
+ size_t i, count;
+ bool created = false;
+
+ if (vector == NULL)
+ created = true;
+ vector = vector_reuse(vector);
+ if (vector == NULL)
+ return NULL;
+
+ count = split_multi_count(string, seps);
+ if (vector->allocated < count && !vector_resize(vector, count))
+ goto fail;
+
+ 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 (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)
+ vector_free(vector);
+ return NULL;
+}
+
+
+/*
+ * Given a vector and a path to a program, exec that program with the vector
+ * as its arguments. This requires adding a NULL terminator to the vector and
+ * casting it appropriately. Returns 0 on success and -1 on error, like exec
+ * does.
+ */
+int
+vector_exec(const char *path, struct vector *vector)
+{
+ if (vector->allocated == vector->count)
+ if (!vector_resize(vector, vector->count + 1))
+ return -1;
+ vector->strings[vector->count] = NULL;
+ return execv(path, (char *const *) vector->strings);
+}
+
+
+/*
+ * Given a vector, a path to a program, and the environment, exec that program
+ * with the vector as its arguments and the given environment. This requires
+ * adding a NULL terminator to the vector and casting it appropriately.
+ * Returns 0 on success and -1 on error, like exec does.
+ */
+int
+vector_exec_env(const char *path, struct vector *vector,
+ const char *const env[])
+{
+ if (vector->allocated == vector->count)
+ if (!vector_resize(vector, vector->count + 1))
+ return -1;
+ vector->strings[vector->count] = NULL;
+ return execve(path, (char *const *) vector->strings, (char *const *) env);
+}