diff options
-rw-r--r-- | libpam/Makefile.am | 5 | ||||
-rw-r--r-- | libpam/include/pam_assemble_line.h | 259 | ||||
-rw-r--r-- | libpam/pam_handlers.c | 253 |
3 files changed, 264 insertions, 253 deletions
diff --git a/libpam/Makefile.am b/libpam/Makefile.am index 0d2fb6f3..5ed045a6 100644 --- a/libpam/Makefile.am +++ b/libpam/Makefile.am @@ -18,8 +18,9 @@ include_HEADERS = include/security/_pam_compat.h \ include/security/pam_ext.h include/security/pam_modutil.h noinst_HEADERS = pam_prelude.h pam_private.h pam_tokens.h \ - pam_modutil_private.h include/pam_cc_compat.h \ - include/pam_inline.h include/test_assert.h + pam_modutil_private.h include/pam_assemble_line.h \ + include/pam_cc_compat.h include/pam_inline.h \ + include/test_assert.h libpam_la_LDFLAGS = -no-undefined -version-info 85:1:85 libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) $(ECONF_LIBS) @LIBDL@ @LTLIBINTL@ diff --git a/libpam/include/pam_assemble_line.h b/libpam/include/pam_assemble_line.h new file mode 100644 index 00000000..a460b9e1 --- /dev/null +++ b/libpam/include/pam_assemble_line.h @@ -0,0 +1,259 @@ +/* pam_assemble_line.h -- routine to parse configuration lines */ + +#ifndef PAM_ASSEMBLE_LINE_H +#define PAM_ASSEMBLE_LINE_H + +#include "pam_inline.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +struct line_buffer { + char *assembled; + char *chunk; + size_t chunk_size; + size_t len; + size_t size; +}; + +static int _pam_buffer_add(struct 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_buffer_add_eol(struct line_buffer *buffer, + char *start, char *end) +{ + if (buffer->assembled != NULL || (*start != '\0' && *start != '\n')) + return _pam_buffer_add(buffer, start, end); + return 0; +} + +static void _pam_buffer_clear(struct line_buffer *buffer) +{ + _pam_drop(buffer->assembled); + _pam_drop(buffer->chunk); + buffer->chunk_size = 0; + buffer->len = 0; + buffer->size = 0; +} + +static void _pam_buffer_init(struct line_buffer *buffer) +{ + buffer->assembled = NULL; + buffer->chunk = NULL; + _pam_buffer_clear(buffer); +} + +static void _pam_buffer_purge(struct line_buffer *buffer) +{ + _pam_drop(buffer->chunk); + buffer->chunk_size = 0; +} + +static void _pam_buffer_shift(struct line_buffer *buffer) +{ + if (buffer->assembled == NULL) + return; + + _pam_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_buffer_valid(struct 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 a blank ' ' 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) +{ + 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++ = ' '; /* replace backslash with ' ' */ + *p = '\0'; /* truncate the line here */ + *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) +{ + int ret; + + *start = line; + *end = line + len; + + ret = _pam_str_unescnl(*start, end) || 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" + * + * Returns 0 on EOF, 1 on successful line parsing, or -1 on error. + */ + +static int _pam_assemble_line(FILE *f, struct line_buffer *buffer) +{ + int ret = 0; + + /* loop broken with a 'break' when a non-'\\n' ended line is read */ + + D(("called.")); + + _pam_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); + + if (eol) { + if (_pam_buffer_add_eol(buffer, start, end)) { + ret = -1; + break; + } + if (_pam_buffer_valid(buffer)) { + /* Successfully parsed a line */ + ret = 1; + break; + } + /* Start parsing next line */ + _pam_buffer_shift(buffer); + ret = 0; + } else { + /* Configuration line spans across multiple lines in file */ + if (_pam_buffer_add(buffer, start, end)) { + ret = -1; + break; + } + /* Keep parsing line */ + ret = 1; + } + } + + if (ret == 1) + _pam_buffer_purge(buffer); + else + _pam_buffer_clear(buffer); + + return ret; +} + +#endif /* PAM_ASSEMBLE_LINE_H */ diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c index 88e12253..28f82d73 100644 --- a/libpam/pam_handlers.c +++ b/libpam/pam_handlers.c @@ -18,24 +18,14 @@ #include <fcntl.h> #include <unistd.h> +#include "pam_assemble_line.h" + #define MODULE_CHUNK 4 #define UNKNOWN_MODULE "<*unknown module*>" #ifndef _PAM_ISA #define _PAM_ISA "." #endif -struct line_buffer { - char *assembled; - char *chunk; - size_t chunk_size; - size_t len; - size_t size; -}; - -static void _pam_buffer_init(struct line_buffer *buffer); - -static int _pam_assemble_line(FILE *f, struct line_buffer *buf); - static void _pam_free_handlers_aux(struct handler **hp); static int _pam_add_handler(pam_handle_t *pamh @@ -584,245 +574,6 @@ int _pam_init_handlers(pam_handle_t *pamh) return PAM_SUCCESS; } -static int _pam_buffer_add(struct 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_buffer_add_eol(struct line_buffer *buffer, - char *start, char *end) -{ - if (buffer->assembled != NULL || (*start != '\0' && *start != '\n')) - return _pam_buffer_add(buffer, start, end); - return 0; -} - -static void _pam_buffer_clear(struct line_buffer *buffer) -{ - _pam_drop(buffer->assembled); - _pam_drop(buffer->chunk); - buffer->chunk_size = 0; - buffer->len = 0; - buffer->size = 0; -} - -static void _pam_buffer_init(struct line_buffer *buffer) -{ - buffer->assembled = NULL; - buffer->chunk = NULL; - _pam_buffer_clear(buffer); -} - -static void _pam_buffer_purge(struct line_buffer *buffer) -{ - _pam_drop(buffer->chunk); - buffer->chunk_size = 0; -} - -static void _pam_buffer_shift(struct line_buffer *buffer) -{ - if (buffer->assembled == NULL) - return; - - _pam_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_buffer_valid(struct 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 a blank ' ' 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) -{ - 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++ = ' '; /* replace backslash with ' ' */ - *p = '\0'; /* truncate the line here */ - *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) -{ - int ret; - - *start = line; - *end = line + len; - - ret = _pam_str_unescnl(*start, end) || 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" - * - * Returns 0 on EOF, 1 on successful line parsing, or -1 on error. - */ - -static int _pam_assemble_line(FILE *f, struct line_buffer *buffer) -{ - int ret = 0; - - /* loop broken with a 'break' when a non-'\\n' ended line is read */ - - D(("called.")); - - _pam_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); - - if (eol) { - if (_pam_buffer_add_eol(buffer, start, end)) { - ret = -1; - break; - } - if (_pam_buffer_valid(buffer)) { - /* Successfully parsed a line */ - ret = 1; - break; - } - /* Start parsing next line */ - _pam_buffer_shift(buffer); - ret = 0; - } else { - /* Configuration line spans across multiple lines in file */ - if (_pam_buffer_add(buffer, start, end)) { - ret = -1; - break; - } - /* Keep parsing line */ - ret = 1; - } - } - - if (ret == 1) - _pam_buffer_purge(buffer); - else - _pam_buffer_clear(buffer); - - return ret; -} - static char * extract_modulename(const char *mod_path) { |