diff options
author | Iker Pedrosa <ipedrosa@redhat.com> | 2022-04-19 16:15:52 +0200 |
---|---|---|
committer | Iker Pedrosa <ipedrosa@redhat.com> | 2022-05-24 13:20:18 +0200 |
commit | 9bcbe96d9e82a23d983c0618178a8dc25596ac2d (patch) | |
tree | cc345a4a31828c7fdd5c84c82d9467ed78d40f09 | |
parent | d3b73b6cd818f4fd9c923822592eccbe8ecdd121 (diff) | |
download | pam-9bcbe96d9e82a23d983c0618178a8dc25596ac2d.tar.gz pam-9bcbe96d9e82a23d983c0618178a8dc25596ac2d.tar.bz2 pam-9bcbe96d9e82a23d983c0618178a8dc25596ac2d.zip |
pam_faillock: move config to its own file
The configuration load can be reused by faillock.
* modules/pam_faillock/faillock_config.c: Move configuration loading
functions (read_config_file and set_conf_opt) to this file.
* modules/pam_faillock/faillock_config.h: Move configuration loading
macros and structures.
* modules/pam_faillock/Makefile.am: Add faillock_config.
* modules/pam_faillock/faillock.h: Remove configuration loading macros.
* modules/pam_faillock/pam_faillock.c: Remove configuration loading
functions, macros and structures.
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
-rw-r--r-- | modules/pam_faillock/Makefile.am | 4 | ||||
-rw-r--r-- | modules/pam_faillock/faillock.h | 4 | ||||
-rw-r--r-- | modules/pam_faillock/faillock_config.c | 233 | ||||
-rw-r--r-- | modules/pam_faillock/faillock_config.h | 85 | ||||
-rw-r--r-- | modules/pam_faillock/pam_faillock.c | 226 |
5 files changed, 322 insertions, 230 deletions
diff --git a/modules/pam_faillock/Makefile.am b/modules/pam_faillock/Makefile.am index 16d9f8bc..a0f0efa8 100644 --- a/modules/pam_faillock/Makefile.am +++ b/modules/pam_faillock/Makefile.am @@ -20,7 +20,7 @@ TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS) securelibdir = $(SECUREDIR) secureconfdir = $(SCONFIGDIR) -noinst_HEADERS = faillock.h +noinst_HEADERS = faillock.h faillock_config.h AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \ $(WARN_CFLAGS) @@ -44,7 +44,7 @@ dist_secureconf_DATA = faillock.conf securelib_LTLIBRARIES = pam_faillock.la sbin_PROGRAMS = faillock -pam_faillock_la_SOURCES = pam_faillock.c faillock.c +pam_faillock_la_SOURCES = pam_faillock.c faillock.c faillock_config.c faillock_SOURCES = main.c faillock.c if ENABLE_REGENERATE_MAN diff --git a/modules/pam_faillock/faillock.h b/modules/pam_faillock/faillock.h index c3f157ef..0ea0ffba 100644 --- a/modules/pam_faillock/faillock.h +++ b/modules/pam_faillock/faillock.h @@ -67,10 +67,6 @@ struct tally_data { }; #define FAILLOCK_DEFAULT_TALLYDIR "/var/run/faillock" -#define FAILLOCK_DEFAULT_CONF SCONFIGDIR "/faillock.conf" -#ifdef VENDOR_SCONFIGDIR -#define VENDOR_FAILLOCK_DEFAULT_CONF VENDOR_SCONFIGDIR "/faillock.conf" -#endif int open_tally(const char *dir, const char *user, uid_t uid, int create); int read_tally(int fd, struct tally_data *tallies); diff --git a/modules/pam_faillock/faillock_config.c b/modules/pam_faillock/faillock_config.c new file mode 100644 index 00000000..8740b826 --- /dev/null +++ b/modules/pam_faillock/faillock_config.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2022 Tomas Mraz <tm@t8m.info> + * Copyright (c) 2022 Iker Pedrosa <ipedrosa@redhat.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include <security/pam_modules.h> + +#include "faillock_config.h" + +#define FAILLOCK_DEFAULT_CONF SCONFIGDIR "/faillock.conf" +#ifdef VENDOR_SCONFIGDIR +#define VENDOR_FAILLOCK_DEFAULT_CONF VENDOR_SCONFIGDIR "/faillock.conf" +#endif + +/* parse a single configuration file */ +int +read_config_file(pam_handle_t *pamh, struct options *opts, const char *cfgfile) +{ + char linebuf[FAILLOCK_CONF_MAX_LINELEN+1]; + const char *fname = (cfgfile != NULL) ? cfgfile : FAILLOCK_DEFAULT_CONF; + FILE *f = fopen(fname, "r"); + +#ifdef VENDOR_FAILLOCK_DEFAULT_CONF + if (f == NULL && errno == ENOENT && cfgfile == NULL) { + /* + * If the default configuration file in /etc does not exist, + * try the vendor configuration file as fallback. + */ + f = fopen(VENDOR_FAILLOCK_DEFAULT_CONF, "r"); + } +#endif /* VENDOR_FAILLOCK_DEFAULT_CONF */ + + if (f == NULL) { + /* ignore non-existent default config file */ + if (errno == ENOENT && cfgfile == NULL) + return PAM_SUCCESS; + return PAM_SERVICE_ERR; + } + + while (fgets(linebuf, sizeof(linebuf), f) != NULL) { + size_t len; + char *ptr; + char *name; + int eq; + + len = strlen(linebuf); + /* len cannot be 0 unless there is a bug in fgets */ + if (len && linebuf[len - 1] != '\n' && !feof(f)) { + (void) fclose(f); + return PAM_SERVICE_ERR; + } + + if ((ptr=strchr(linebuf, '#')) != NULL) { + *ptr = '\0'; + } else { + ptr = linebuf + len; + } + + /* drop terminating whitespace including the \n */ + while (ptr > linebuf) { + if (!isspace(*(ptr-1))) { + *ptr = '\0'; + break; + } + --ptr; + } + + /* skip initial whitespace */ + for (ptr = linebuf; isspace(*ptr); ptr++); + if (*ptr == '\0') + continue; + + /* grab the key name */ + eq = 0; + name = ptr; + while (*ptr != '\0') { + if (isspace(*ptr) || *ptr == '=') { + eq = *ptr == '='; + *ptr = '\0'; + ++ptr; + break; + } + ++ptr; + } + + /* grab the key value */ + while (*ptr != '\0') { + if (*ptr != '=' || eq) { + if (!isspace(*ptr)) { + break; + } + } else { + eq = 1; + } + ++ptr; + } + + /* set the key:value pair on opts */ + set_conf_opt(pamh, opts, name, ptr); + } + + (void)fclose(f); + return PAM_SUCCESS; +} + +void +set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, + const char *value) +{ + if (strcmp(name, "dir") == 0) { + if (value[0] != '/') { + pam_syslog(pamh, LOG_ERR, + "Tally directory is not absolute path (%s); keeping default", value); + } else { + free(opts->dir); + opts->dir = strdup(value); + } + } + else if (strcmp(name, "deny") == 0) { + if (sscanf(value, "%hu", &opts->deny) != 1) { + pam_syslog(pamh, LOG_ERR, + "Bad number supplied for deny argument"); + } + } + else if (strcmp(name, "fail_interval") == 0) { + unsigned int temp; + if (sscanf(value, "%u", &temp) != 1 || + temp > MAX_TIME_INTERVAL) { + pam_syslog(pamh, LOG_ERR, + "Bad number supplied for fail_interval argument"); + } else { + opts->fail_interval = temp; + } + } + else if (strcmp(name, "unlock_time") == 0) { + unsigned int temp; + + if (strcmp(value, "never") == 0) { + opts->unlock_time = 0; + } + else if (sscanf(value, "%u", &temp) != 1 || + temp > MAX_TIME_INTERVAL) { + pam_syslog(pamh, LOG_ERR, + "Bad number supplied for unlock_time argument"); + } + else { + opts->unlock_time = temp; + } + } + else if (strcmp(name, "root_unlock_time") == 0) { + unsigned int temp; + + if (strcmp(value, "never") == 0) { + opts->root_unlock_time = 0; + } + else if (sscanf(value, "%u", &temp) != 1 || + temp > MAX_TIME_INTERVAL) { + pam_syslog(pamh, LOG_ERR, + "Bad number supplied for root_unlock_time argument"); + } else { + opts->root_unlock_time = temp; + } + } + else if (strcmp(name, "admin_group") == 0) { + free(opts->admin_group); + opts->admin_group = strdup(value); + if (opts->admin_group == NULL) { + opts->fatal_error = 1; + pam_syslog(pamh, LOG_CRIT, "Error allocating memory: %m"); + } + } + else if (strcmp(name, "even_deny_root") == 0) { + opts->flags |= FAILLOCK_FLAG_DENY_ROOT; + } + else if (strcmp(name, "audit") == 0) { + opts->flags |= FAILLOCK_FLAG_AUDIT; + } + else if (strcmp(name, "silent") == 0) { + opts->flags |= FAILLOCK_FLAG_SILENT; + } + else if (strcmp(name, "no_log_info") == 0) { + opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO; + } + else if (strcmp(name, "local_users_only") == 0) { + opts->flags |= FAILLOCK_FLAG_LOCAL_ONLY; + } + else if (strcmp(name, "nodelay") == 0) { + opts->flags |= FAILLOCK_FLAG_NO_DELAY; + } + else { + pam_syslog(pamh, LOG_ERR, "Unknown option: %s", name); + } +} diff --git a/modules/pam_faillock/faillock_config.h b/modules/pam_faillock/faillock_config.h new file mode 100644 index 00000000..81aa6654 --- /dev/null +++ b/modules/pam_faillock/faillock_config.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 Tomas Mraz <tm@t8m.info> + * Copyright (c) 2022 Iker Pedrosa <ipedrosa@redhat.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * faillock_config.h - load configuration options from file + * + */ + +#ifndef _FAILLOCK_CONFIG_H +#define _FAILLOCK_CONFIG_H + +#include <limits.h> +#include <stdint.h> +#include <sys/types.h> + +#include <security/pam_ext.h> + +#define FAILLOCK_FLAG_DENY_ROOT 0x1 +#define FAILLOCK_FLAG_AUDIT 0x2 +#define FAILLOCK_FLAG_SILENT 0x4 +#define FAILLOCK_FLAG_NO_LOG_INFO 0x8 +#define FAILLOCK_FLAG_UNLOCKED 0x10 +#define FAILLOCK_FLAG_LOCAL_ONLY 0x20 +#define FAILLOCK_FLAG_NO_DELAY 0x40 + +#define FAILLOCK_CONF_MAX_LINELEN 1023 +#define MAX_TIME_INTERVAL 604800 /* 7 days */ + +struct options { + unsigned int action; + unsigned int flags; + unsigned short deny; + unsigned int fail_interval; + unsigned int unlock_time; + unsigned int root_unlock_time; + char *dir; + const char *user; + char *admin_group; + int failures; + uint64_t latest_time; + uid_t uid; + int is_admin; + uint64_t now; + int fatal_error; +}; + +int read_config_file(pam_handle_t *pamh, struct options *opts, + const char *cfgfile); +void set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, + const char *value); + +#endif /* _FAILLOCK_CONFIG_H */ diff --git a/modules/pam_faillock/pam_faillock.c b/modules/pam_faillock/pam_faillock.c index 932d4281..0a5b6b62 100644 --- a/modules/pam_faillock/pam_faillock.c +++ b/modules/pam_faillock/pam_faillock.c @@ -38,7 +38,6 @@ #include <stdio.h> #include <string.h> #include <unistd.h> -#include <stdint.h> #include <stdlib.h> #include <errno.h> #include <time.h> @@ -56,55 +55,12 @@ #include "pam_inline.h" #include "faillock.h" +#include "faillock_config.h" #define FAILLOCK_ACTION_PREAUTH 0 #define FAILLOCK_ACTION_AUTHSUCC 1 #define FAILLOCK_ACTION_AUTHFAIL 2 -#define FAILLOCK_FLAG_DENY_ROOT 0x1 -#define FAILLOCK_FLAG_AUDIT 0x2 -#define FAILLOCK_FLAG_SILENT 0x4 -#define FAILLOCK_FLAG_NO_LOG_INFO 0x8 -#define FAILLOCK_FLAG_UNLOCKED 0x10 -#define FAILLOCK_FLAG_LOCAL_ONLY 0x20 -#define FAILLOCK_FLAG_NO_DELAY 0x40 - -#define MAX_TIME_INTERVAL 604800 /* 7 days */ -#define FAILLOCK_CONF_MAX_LINELEN 1023 - -static const char default_faillock_conf[] = FAILLOCK_DEFAULT_CONF; - -struct options { - unsigned int action; - unsigned int flags; - unsigned short deny; - unsigned int fail_interval; - unsigned int unlock_time; - unsigned int root_unlock_time; - char *dir; - const char *user; - char *admin_group; - int failures; - uint64_t latest_time; - uid_t uid; - int is_admin; - uint64_t now; - int fatal_error; -}; - -static int read_config_file( - pam_handle_t *pamh, - struct options *opts, - const char *cfgfile -); - -static void set_conf_opt( - pam_handle_t *pamh, - struct options *opts, - const char *name, - const char *value -); - static int args_parse(pam_handle_t *pamh, int argc, const char **argv, int flags, struct options *opts) @@ -112,7 +68,7 @@ args_parse(pam_handle_t *pamh, int argc, const char **argv, int i; int config_arg_index = -1; int rv; - const char *conf = default_faillock_conf; + const char *conf = NULL; memset(opts, 0, sizeof(*opts)); @@ -184,184 +140,6 @@ args_parse(pam_handle_t *pamh, int argc, const char **argv, return PAM_SUCCESS; } -/* parse a single configuration file */ -static int -read_config_file(pam_handle_t *pamh, struct options *opts, const char *cfgfile) -{ - FILE *f; - char linebuf[FAILLOCK_CONF_MAX_LINELEN+1]; - - f = fopen(cfgfile, "r"); -#ifdef VENDOR_FAILLOCK_DEFAULT_CONF - if (f == NULL && errno == ENOENT && cfgfile == default_faillock_conf) { - /* - * If the default configuration file in /etc does not exist, - * try the vendor configuration file as fallback. - */ - f = fopen(VENDOR_FAILLOCK_DEFAULT_CONF, "r"); - } -#endif - if (f == NULL) { - /* ignore non-existent default config file */ - if (errno == ENOENT && cfgfile == default_faillock_conf) - return PAM_SUCCESS; - return PAM_SERVICE_ERR; - } - - while (fgets(linebuf, sizeof(linebuf), f) != NULL) { - size_t len; - char *ptr; - char *name; - int eq; - - len = strlen(linebuf); - /* len cannot be 0 unless there is a bug in fgets */ - if (len && linebuf[len - 1] != '\n' && !feof(f)) { - (void) fclose(f); - return PAM_SERVICE_ERR; - } - - if ((ptr=strchr(linebuf, '#')) != NULL) { - *ptr = '\0'; - } else { - ptr = linebuf + len; - } - - /* drop terminating whitespace including the \n */ - while (ptr > linebuf) { - if (!isspace(*(ptr-1))) { - *ptr = '\0'; - break; - } - --ptr; - } - - /* skip initial whitespace */ - for (ptr = linebuf; isspace(*ptr); ptr++); - if (*ptr == '\0') - continue; - - /* grab the key name */ - eq = 0; - name = ptr; - while (*ptr != '\0') { - if (isspace(*ptr) || *ptr == '=') { - eq = *ptr == '='; - *ptr = '\0'; - ++ptr; - break; - } - ++ptr; - } - - /* grab the key value */ - while (*ptr != '\0') { - if (*ptr != '=' || eq) { - if (!isspace(*ptr)) { - break; - } - } else { - eq = 1; - } - ++ptr; - } - - /* set the key:value pair on opts */ - set_conf_opt(pamh, opts, name, ptr); - } - - (void)fclose(f); - return PAM_SUCCESS; -} - -static void -set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, const char *value) -{ - if (strcmp(name, "dir") == 0) { - if (value[0] != '/') { - pam_syslog(pamh, LOG_ERR, - "Tally directory is not absolute path (%s); keeping default", value); - } else { - free(opts->dir); - opts->dir = strdup(value); - } - } - else if (strcmp(name, "deny") == 0) { - if (sscanf(value, "%hu", &opts->deny) != 1) { - pam_syslog(pamh, LOG_ERR, - "Bad number supplied for deny argument"); - } - } - else if (strcmp(name, "fail_interval") == 0) { - unsigned int temp; - if (sscanf(value, "%u", &temp) != 1 || - temp > MAX_TIME_INTERVAL) { - pam_syslog(pamh, LOG_ERR, - "Bad number supplied for fail_interval argument"); - } else { - opts->fail_interval = temp; - } - } - else if (strcmp(name, "unlock_time") == 0) { - unsigned int temp; - - if (strcmp(value, "never") == 0) { - opts->unlock_time = 0; - } - else if (sscanf(value, "%u", &temp) != 1 || - temp > MAX_TIME_INTERVAL) { - pam_syslog(pamh, LOG_ERR, - "Bad number supplied for unlock_time argument"); - } - else { - opts->unlock_time = temp; - } - } - else if (strcmp(name, "root_unlock_time") == 0) { - unsigned int temp; - - if (strcmp(value, "never") == 0) { - opts->root_unlock_time = 0; - } - else if (sscanf(value, "%u", &temp) != 1 || - temp > MAX_TIME_INTERVAL) { - pam_syslog(pamh, LOG_ERR, - "Bad number supplied for root_unlock_time argument"); - } else { - opts->root_unlock_time = temp; - } - } - else if (strcmp(name, "admin_group") == 0) { - free(opts->admin_group); - opts->admin_group = strdup(value); - if (opts->admin_group == NULL) { - opts->fatal_error = 1; - pam_syslog(pamh, LOG_CRIT, "Error allocating memory: %m"); - } - } - else if (strcmp(name, "even_deny_root") == 0) { - opts->flags |= FAILLOCK_FLAG_DENY_ROOT; - } - else if (strcmp(name, "audit") == 0) { - opts->flags |= FAILLOCK_FLAG_AUDIT; - } - else if (strcmp(name, "silent") == 0) { - opts->flags |= FAILLOCK_FLAG_SILENT; - } - else if (strcmp(name, "no_log_info") == 0) { - opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO; - } - else if (strcmp(name, "local_users_only") == 0) { - opts->flags |= FAILLOCK_FLAG_LOCAL_ONLY; - } - else if (strcmp(name, "nodelay") == 0) { - opts->flags |= FAILLOCK_FLAG_NO_DELAY; - } - else { - pam_syslog(pamh, LOG_ERR, "Unknown option: %s", name); - } -} - static int check_local_user (pam_handle_t *pamh, const char *user) { |