aboutsummaryrefslogtreecommitdiff
path: root/libpam_internal
diff options
context:
space:
mode:
Diffstat (limited to 'libpam_internal')
-rw-r--r--libpam_internal/include/pam_econf.h22
-rw-r--r--libpam_internal/include/pam_line.h26
-rw-r--r--libpam_internal/meson.build19
-rw-r--r--libpam_internal/pam_debug.c21
-rw-r--r--libpam_internal/pam_econf.c60
-rw-r--r--libpam_internal/pam_line.c253
6 files changed, 401 insertions, 0 deletions
diff --git a/libpam_internal/include/pam_econf.h b/libpam_internal/include/pam_econf.h
new file mode 100644
index 00000000..ebba659d
--- /dev/null
+++ b/libpam_internal/include/pam_econf.h
@@ -0,0 +1,22 @@
+/* pam_econf.h -- routines to parse configuration files with libeconf */
+
+#ifndef PAM_ECONF_H
+#define PAM_ECONF_H
+
+#ifdef USE_ECONF
+
+#include <libeconf.h>
+
+econf_err pam_econf_readconfig(econf_file **key_file,
+ const char *usr_conf_dir,
+ const char *etc_conf_dir,
+ const char *config_name,
+ const char *config_suffix,
+ const char *delim,
+ const char *comment,
+ bool (*callback)(const char *filename, const void *data),
+ const void *callback_data);
+
+#endif /* USE_ECONF */
+
+#endif /* PAM_ECONF_H */
diff --git a/libpam_internal/include/pam_line.h b/libpam_internal/include/pam_line.h
new file mode 100644
index 00000000..70a5c483
--- /dev/null
+++ b/libpam_internal/include/pam_line.h
@@ -0,0 +1,26 @@
+/* pam_line.h -- routine to parse configuration lines */
+
+#ifndef PAM_LINE_H
+#define PAM_LINE_H
+
+#include "pam_inline.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+struct pam_line_buffer {
+ char *assembled;
+ char *chunk;
+ size_t chunk_size;
+ size_t len;
+ size_t size;
+};
+
+void _pam_line_buffer_clear(struct pam_line_buffer *buffer);
+
+void _pam_line_buffer_init(struct pam_line_buffer *buffer);
+
+int _pam_line_assemble(FILE *f, struct pam_line_buffer *buffer, char repl);
+
+#endif /* PAM_LINE_H */
diff --git a/libpam_internal/meson.build b/libpam_internal/meson.build
new file mode 100644
index 00000000..5d03877a
--- /dev/null
+++ b/libpam_internal/meson.build
@@ -0,0 +1,19 @@
+libpam_internal_src = [
+ 'pam_debug.c',
+ 'pam_econf.c',
+ 'pam_line.c',
+]
+
+libpam_internal_inc = include_directories('include')
+
+libpam_internal = static_library(
+ 'pam_internal',
+ sources: libpam_internal_src,
+ include_directories: [libpam_internal_inc, libpam_inc],
+ dependencies: libeconf,
+)
+
+libpam_internal_dep = declare_dependency(
+ include_directories: [libpam_internal_inc],
+ link_with: [libpam_internal],
+)
diff --git a/libpam_internal/pam_debug.c b/libpam_internal/pam_debug.c
new file mode 100644
index 00000000..e4608f4d
--- /dev/null
+++ b/libpam_internal/pam_debug.c
@@ -0,0 +1,21 @@
+/*
+ * This provides the necessary functions to do debugging in PAM.
+ * Cristian Gafton <gafton@redhat.com>
+ */
+
+#include "config.h"
+
+#ifdef PAM_DEBUG
+
+#include "security/_pam_macros.h"
+
+#undef PAM_MACROS_H
+#undef PAM_NO_HEADER_FUNCTIONS
+#define PAM_DEBUG_C 1
+#include "security/_pam_macros.h"
+
+#else
+
+extern int ISO_C_forbids_an_empty_translation_unit;
+
+#endif /* PAM_DEBUG */
diff --git a/libpam_internal/pam_econf.c b/libpam_internal/pam_econf.c
new file mode 100644
index 00000000..b3b9874d
--- /dev/null
+++ b/libpam_internal/pam_econf.c
@@ -0,0 +1,60 @@
+/* pam_econf.c -- routines to parse configuration files with libeconf */
+
+#include "config.h"
+
+#ifdef USE_ECONF
+
+#include <stdio.h>
+#include <security/_pam_macros.h>
+#include "pam_econf.h"
+
+econf_err pam_econf_readconfig(econf_file **key_file,
+ const char *usr_conf_dir,
+ const char *etc_conf_dir,
+ const char *config_name,
+ const char *config_suffix,
+ const char *delim,
+ const char *comment,
+ bool (*callback)(const char *filename, const void *data),
+ const void *callback_data)
+{
+ econf_err ret = ECONF_SUCCESS;
+ D(("Read configuration from directory %s and %s", etc_conf_dir, usr_conf_dir));
+
+#ifdef HAVE_ECONF_READCONFIG
+
+ char *parsing_dirs = NULL;
+ if (asprintf(&parsing_dirs, "PARSING_DIRS=%s:%s", usr_conf_dir, etc_conf_dir) < 0) {
+ ret = ECONF_NOMEM;
+ parsing_dirs = NULL;
+ }
+ if (ret == ECONF_SUCCESS)
+ ret = econf_newKeyFile_with_options(key_file, parsing_dirs);
+ if (ret == ECONF_SUCCESS)
+ ret = econf_readConfigWithCallback(key_file,
+ NULL,
+ usr_conf_dir,
+ config_name,
+ config_suffix,
+ delim,
+ comment,
+ callback, callback_data);
+ free(parsing_dirs);
+
+#else
+
+ ret = econf_readDirsWithCallback(key_file,
+ usr_conf_dir,
+ etc_conf_dir,
+ config_name,
+ config_suffix,
+ delim,
+ comment,
+ callback, callback_data);
+
+#endif
+
+ return ret;
+}
+
+#endif /* USE_ECONF */
diff --git a/libpam_internal/pam_line.c b/libpam_internal/pam_line.c
new file mode 100644
index 00000000..044b4d73
--- /dev/null
+++ b/libpam_internal/pam_line.c
@@ -0,0 +1,253 @@
+/* pam_line.c -- routine to parse configuration lines */
+
+#include "config.h"
+
+#include "security/_pam_macros.h"
+#include "pam_line.h"
+
+static int _pam_line_buffer_add(struct pam_line_buffer *buffer, char *start,
+ char *end)
+{
+ size_t len = end - start;
+
+ D(("assembled: [%zu/%zu] '%s', adding [%zu] '%s'",
+ buffer->len, buffer->size,
+ buffer->assembled == NULL ? "" : buffer->assembled, len, start));
+
+ if (start == end)
+ return 0;
+
+ if (buffer->assembled == NULL && buffer->chunk == start) {
+ /* no extra allocation needed, just move chunk to assembled */
+ buffer->assembled = buffer->chunk;
+ buffer->len = len;
+ buffer->size = buffer->chunk_size;
+
+ buffer->chunk = NULL;
+ buffer->chunk_size = 0;
+
+ D(("exiting with quick exchange"));
+ return 0;
+ }
+
+ if (buffer->len + len + 1 > buffer->size) {
+ size_t size;
+ char *p;
+
+ size = buffer->len + len + 1;
+ if ((p = realloc(buffer->assembled, size)) == NULL)
+ return -1;
+
+ buffer->assembled = p;
+ buffer->size = size;
+ }
+
+ memcpy(buffer->assembled + buffer->len, start, len);
+ buffer->len += len;
+ buffer->assembled[buffer->len] = '\0';
+
+ D(("exiting"));
+ return 0;
+}
+
+static inline int _pam_line_buffer_add_eol(struct pam_line_buffer *buffer,
+ char *start, char *end)
+{
+ if (buffer->assembled != NULL || (*start != '\0' && *start != '\n'))
+ return _pam_line_buffer_add(buffer, start, end);
+ return 0;
+}
+
+void _pam_line_buffer_clear(struct pam_line_buffer *buffer)
+{
+ pam_overwrite_n(buffer->assembled, buffer->size);
+ _pam_drop(buffer->assembled);
+ pam_overwrite_n(buffer->chunk, buffer->chunk_size);
+ _pam_drop(buffer->chunk);
+ buffer->chunk_size = 0;
+ buffer->len = 0;
+ buffer->size = 0;
+}
+
+void _pam_line_buffer_init(struct pam_line_buffer *buffer)
+{
+ buffer->assembled = NULL;
+ buffer->chunk = NULL;
+ _pam_line_buffer_clear(buffer);
+}
+
+static void _pam_line_buffer_purge(struct pam_line_buffer *buffer)
+{
+ pam_overwrite_n(buffer->chunk, buffer->chunk_size);
+ _pam_drop(buffer->chunk);
+ buffer->chunk_size = 0;
+}
+
+static void _pam_line_buffer_shift(struct pam_line_buffer *buffer)
+{
+ if (buffer->assembled == NULL)
+ return;
+
+ _pam_line_buffer_purge(buffer);
+ buffer->chunk = buffer->assembled;
+ buffer->chunk_size = buffer->size;
+
+ buffer->assembled = NULL;
+ buffer->size = 0;
+ buffer->len = 0;
+}
+
+static inline int _pam_line_buffer_valid(struct pam_line_buffer *buffer)
+{
+ return buffer->assembled != NULL && *buffer->assembled != '\0';
+}
+
+/*
+ * Trim string to relevant parts of a configuration line.
+ *
+ * Preceding whitespaces are skipped and comment (#) marks the end of
+ * configuration line.
+ *
+ * Returns start of configuration line.
+ */
+static inline char *_pam_str_trim(char *str)
+{
+ /* skip leading spaces */
+ str += strspn(str, " \t");
+ /*
+ * we are only interested in characters before the first '#'
+ * character
+ */
+ str[strcspn(str, "#")] = '\0';
+
+ return str;
+}
+
+/*
+ * Remove escaped newline from end of string.
+ *
+ * Configuration lines may span across multiple lines in a file
+ * by ending a line with a backslash (\).
+ *
+ * If an escaped newline is encountered, the backslash will be
+ * replaced with "repl" and the newline itself removed.
+ * Then the variable "end" will point to the new end of line.
+ *
+ * Returns 0 if escaped newline was found and replaced, 1 otherwise.
+ */
+static inline int _pam_str_unescnl(char *start, char **end, char repl)
+{
+ int ret = 1;
+ char *p = *end;
+
+ /*
+ * Check for backslash by scanning back from the end of
+ * the entered line, the '\n' should be included since
+ * normally a line is terminated with this character.
+ */
+ while (p > start && ((*--p == ' ') || (*p == '\t') || (*p == '\n')))
+ ;
+ if (*p == '\\') {
+ *p = repl; /* replace backslash with replacement char */
+ if (repl != '\0') {
+ *++p = '\0'; /* truncate the line here if repl is not NUL */
+ }
+ *end = p;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * Prepare line from file for configuration line parsing.
+ *
+ * A configuration line may span across multiple lines in a file.
+ * Remove comments and skip preceding whitespaces.
+ *
+ * Returns 0 if line spans across multiple lines, 1 if
+ * end of line is encountered.
+ */
+static inline int _pam_str_prepare(char *line, ssize_t len,
+ char **start, char **end, char repl)
+{
+ int ret;
+
+ *start = line;
+ *end = line + len;
+
+ ret = _pam_str_unescnl(*start, end, repl) || strchr(*start, '#') != NULL;
+
+ *start = _pam_str_trim(*start);
+
+ return ret;
+}
+
+/*
+ * This is where we read a line of the PAM config file. The line may be
+ * preceded by lines of comments and also extended with "\\\n"
+ *
+ * The "repl" argument is used as replacement char for the backslash used
+ * in newline escaping, i.e. in "\\\n".
+ *
+ * Returns 0 on EOF, 1 on successful line parsing, or -1 on error.
+ */
+int _pam_line_assemble(FILE *f, struct pam_line_buffer *buffer, char repl)
+{
+ int ret = 0;
+
+ /* loop broken with a 'break' when a non-'\\n' ended line is read */
+
+ D(("called."));
+
+ _pam_line_buffer_shift(buffer);
+
+ for (;;) {
+ char *start, *end;
+ ssize_t n;
+ int eol;
+
+ if ((n = getline(&buffer->chunk, &buffer->chunk_size, f)) == -1) {
+ if (ret) {
+ /* Incomplete read */
+ ret = -1;
+ } else {
+ /* EOF */
+ ret = 0;
+ }
+ break;
+ }
+
+ eol = _pam_str_prepare(buffer->chunk, n, &start, &end, repl);
+
+ if (eol) {
+ if (_pam_line_buffer_add_eol(buffer, start, end)) {
+ ret = -1;
+ break;
+ }
+ if (_pam_line_buffer_valid(buffer)) {
+ /* Successfully parsed a line */
+ ret = 1;
+ break;
+ }
+ /* Start parsing next line */
+ _pam_line_buffer_shift(buffer);
+ ret = 0;
+ } else {
+ /* Configuration line spans across multiple lines in file */
+ if (_pam_line_buffer_add(buffer, start, end)) {
+ ret = -1;
+ break;
+ }
+ /* Keep parsing line */
+ ret = 1;
+ }
+ }
+
+ if (ret == 1)
+ _pam_line_buffer_purge(buffer);
+ else
+ _pam_line_buffer_clear(buffer);
+
+ return ret;
+}