(root)/
Linux-PAM-1.5.3/
modules/
pam_faillock/
faillock_config.c
       1  /*
       2   * Copyright (c) 2022 Tomas Mraz <tm@t8m.info>
       3   * Copyright (c) 2022 Iker Pedrosa <ipedrosa@redhat.com>
       4   *
       5   * Redistribution and use in source and binary forms, with or without
       6   * modification, are permitted provided that the following conditions
       7   * are met:
       8   * 1. Redistributions of source code must retain the above copyright
       9   *    notice, and the entire permission notice in its entirety,
      10   *    including the disclaimer of warranties.
      11   * 2. Redistributions in binary form must reproduce the above copyright
      12   *    notice, this list of conditions and the following disclaimer in the
      13   *    documentation and/or other materials provided with the distribution.
      14   * 3. The name of the author may not be used to endorse or promote
      15   *    products derived from this software without specific prior
      16   *    written permission.
      17   *
      18   * ALTERNATIVELY, this product may be distributed under the terms of
      19   * the GNU Public License, in which case the provisions of the GPL are
      20   * required INSTEAD OF the above restrictions.  (This clause is
      21   * necessary due to a potential bad interaction between the GPL and
      22   * the restrictions contained in a BSD-style copyright.)
      23   *
      24   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
      25   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      26   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      27   * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
      28   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      29   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      30   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      31   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      32   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      33   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      34   * OF THE POSSIBILITY OF SUCH DAMAGE.
      35   */
      36  
      37  #include "config.h"
      38  
      39  #include <ctype.h>
      40  #include <errno.h>
      41  #include <stdio.h>
      42  #include <stdlib.h>
      43  #include <string.h>
      44  #include <syslog.h>
      45  
      46  #include <security/pam_modules.h>
      47  
      48  #include "faillock_config.h"
      49  #include "faillock.h"
      50  
      51  #define FAILLOCK_DEFAULT_CONF SCONFIGDIR "/faillock.conf"
      52  #ifdef VENDOR_SCONFIGDIR
      53  #define VENDOR_FAILLOCK_DEFAULT_CONF VENDOR_SCONFIGDIR "/faillock.conf"
      54  #endif
      55  
      56  static void PAM_FORMAT((printf, 3, 4)) PAM_NONNULL((3))
      57  config_log(const pam_handle_t *pamh, int priority, const char *fmt, ...)
      58  {
      59  	va_list args;
      60  
      61  	va_start(args, fmt);
      62  	if (pamh) {
      63  		pam_vsyslog(pamh, priority, fmt, args);
      64  	} else {
      65  		char *buf = NULL;
      66  
      67  		if (vasprintf(&buf, fmt, args) < 0) {
      68  			fprintf(stderr, "vasprintf: %m");
      69  			va_end(args);
      70  			return;
      71  		}
      72  		fprintf(stderr, "%s\n", buf);
      73  		free(buf);
      74  	}
      75  	va_end(args);
      76  }
      77  
      78  /* parse a single configuration file */
      79  int
      80  read_config_file(pam_handle_t *pamh, struct options *opts, const char *cfgfile)
      81  {
      82  	char linebuf[FAILLOCK_CONF_MAX_LINELEN+1];
      83  	const char *fname = (cfgfile != NULL) ? cfgfile : FAILLOCK_DEFAULT_CONF;
      84  	FILE *f = fopen(fname, "r");
      85  
      86  #ifdef VENDOR_FAILLOCK_DEFAULT_CONF
      87  	if (f == NULL && errno == ENOENT && cfgfile == NULL) {
      88  		/*
      89  		 * If the default configuration file in /etc does not exist,
      90  		 * try the vendor configuration file as fallback.
      91  		 */
      92  		f = fopen(VENDOR_FAILLOCK_DEFAULT_CONF, "r");
      93  	}
      94  #endif /* VENDOR_FAILLOCK_DEFAULT_CONF */
      95  
      96  	if (f == NULL) {
      97  		/* ignore non-existent default config file */
      98  		if (errno == ENOENT && cfgfile == NULL)
      99  			return PAM_SUCCESS;
     100  		return PAM_SERVICE_ERR;
     101  	}
     102  
     103  	while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
     104  		size_t len;
     105  		char *ptr;
     106  		char *name;
     107  		int eq;
     108  
     109  		len = strlen(linebuf);
     110  		/* len cannot be 0 unless there is a bug in fgets */
     111  		if (len && linebuf[len - 1] != '\n' && !feof(f)) {
     112  			(void) fclose(f);
     113  			return PAM_SERVICE_ERR;
     114  		}
     115  
     116  		if ((ptr=strchr(linebuf, '#')) != NULL) {
     117  			*ptr = '\0';
     118  		} else {
     119  			ptr = linebuf + len;
     120  		}
     121  
     122  		/* drop terminating whitespace including the \n */
     123  		while (ptr > linebuf) {
     124  			if (!isspace(*(ptr-1))) {
     125  				*ptr = '\0';
     126  				break;
     127  			}
     128  			--ptr;
     129  		}
     130  
     131  		/* skip initial whitespace */
     132  		for (ptr = linebuf; isspace(*ptr); ptr++);
     133  		if (*ptr == '\0')
     134  			continue;
     135  
     136  		/* grab the key name */
     137  		eq = 0;
     138  		name = ptr;
     139  		while (*ptr != '\0') {
     140  			if (isspace(*ptr) || *ptr == '=') {
     141  				eq = *ptr == '=';
     142  				*ptr = '\0';
     143  				++ptr;
     144  				break;
     145  			}
     146  			++ptr;
     147  		}
     148  
     149  		/* grab the key value */
     150  		while (*ptr != '\0') {
     151  			if (*ptr != '=' || eq) {
     152  				if (!isspace(*ptr)) {
     153  					break;
     154  				}
     155  			} else {
     156  				eq = 1;
     157  			}
     158  			++ptr;
     159  		}
     160  
     161  		/* set the key:value pair on opts */
     162  		set_conf_opt(pamh, opts, name, ptr);
     163  	}
     164  
     165  	(void)fclose(f);
     166  	return PAM_SUCCESS;
     167  }
     168  
     169  void
     170  set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name,
     171  			 const char *value)
     172  {
     173  	if (strcmp(name, "dir") == 0) {
     174  		if (value[0] != '/') {
     175  			config_log(pamh, LOG_ERR,
     176  					"Tally directory is not absolute path (%s); keeping value",
     177  					value);
     178  		} else {
     179  			free(opts->dir);
     180  			opts->dir = strdup(value);
     181  			if (opts->dir == NULL) {
     182  				opts->fatal_error = 1;
     183  				config_log(pamh, LOG_CRIT, "Error allocating memory: %m");
     184  			}
     185  		}
     186  	}
     187  	else if (strcmp(name, "deny") == 0) {
     188  		if (sscanf(value, "%hu", &opts->deny) != 1) {
     189  			config_log(pamh, LOG_ERR,
     190  				"Bad number supplied for deny argument");
     191  		}
     192  	}
     193  	else if (strcmp(name, "fail_interval") == 0) {
     194  		unsigned int temp;
     195  		if (sscanf(value, "%u", &temp) != 1 ||
     196  			temp > MAX_TIME_INTERVAL) {
     197  			config_log(pamh, LOG_ERR,
     198  				"Bad number supplied for fail_interval argument");
     199  		} else {
     200  			opts->fail_interval = temp;
     201  		}
     202  	}
     203  	else if (strcmp(name, "unlock_time") == 0) {
     204  		unsigned int temp;
     205  
     206  		if (strcmp(value, "never") == 0) {
     207  			opts->unlock_time = 0;
     208  		}
     209  		else if (sscanf(value, "%u", &temp) != 1 ||
     210  			temp > MAX_TIME_INTERVAL) {
     211  			config_log(pamh, LOG_ERR,
     212  				"Bad number supplied for unlock_time argument");
     213  		}
     214  		else {
     215  			opts->unlock_time = temp;
     216  		}
     217  	}
     218  	else if (strcmp(name, "root_unlock_time") == 0) {
     219  		unsigned int temp;
     220  
     221  		if (strcmp(value, "never") == 0) {
     222  			opts->root_unlock_time = 0;
     223  		}
     224  		else if (sscanf(value, "%u", &temp) != 1 ||
     225  			temp > MAX_TIME_INTERVAL) {
     226  			config_log(pamh, LOG_ERR,
     227  				"Bad number supplied for root_unlock_time argument");
     228  		} else {
     229  			opts->root_unlock_time = temp;
     230  		}
     231  	}
     232  	else if (strcmp(name, "admin_group") == 0) {
     233  		free(opts->admin_group);
     234  		opts->admin_group = strdup(value);
     235  		if (opts->admin_group == NULL) {
     236  			opts->fatal_error = 1;
     237  			config_log(pamh, LOG_CRIT, "Error allocating memory: %m");
     238  		}
     239  	}
     240  	else if (strcmp(name, "even_deny_root") == 0) {
     241  		opts->flags |= FAILLOCK_FLAG_DENY_ROOT;
     242  	}
     243  	else if (strcmp(name, "audit") == 0) {
     244  		opts->flags |= FAILLOCK_FLAG_AUDIT;
     245  	}
     246  	else if (strcmp(name, "silent") == 0) {
     247  		opts->flags |= FAILLOCK_FLAG_SILENT;
     248  	}
     249  	else if (strcmp(name, "no_log_info") == 0) {
     250  		opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO;
     251  	}
     252  	else if (strcmp(name, "local_users_only") == 0) {
     253  		opts->flags |= FAILLOCK_FLAG_LOCAL_ONLY;
     254  	}
     255  	else if (strcmp(name, "nodelay") == 0) {
     256  		opts->flags |= FAILLOCK_FLAG_NO_DELAY;
     257  	}
     258  	else {
     259  		config_log(pamh, LOG_ERR, "Unknown option: %s", name);
     260  	}
     261  }
     262  
     263  const char *get_tally_dir(const struct options *opts)
     264  {
     265  	return (opts->dir != NULL) ? opts->dir : FAILLOCK_DEFAULT_TALLYDIR;
     266  }