diff options
Diffstat (limited to 'libpam_internal')
-rw-r--r-- | libpam_internal/include/pam_econf.h | 22 | ||||
-rw-r--r-- | libpam_internal/include/pam_line.h | 26 | ||||
-rw-r--r-- | libpam_internal/meson.build | 19 | ||||
-rw-r--r-- | libpam_internal/pam_debug.c | 21 | ||||
-rw-r--r-- | libpam_internal/pam_econf.c | 60 | ||||
-rw-r--r-- | libpam_internal/pam_line.c | 253 |
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; +} |