summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomas Mraz <tmraz@redhat.com>2015-07-17 17:08:43 +0200
committerTomas Mraz <tmraz@redhat.com>2015-07-17 17:08:43 +0200
commitfcf522db281ce23840f028a2ea44039f92c5e144 (patch)
tree61ae0be00aef732856b893478df3ed87cd9c9ba6
parent2feba1307c3bcfc1592b4e4162ebf9967cc096c5 (diff)
downloadlibpwquality-fcf522db281ce23840f028a2ea44039f92c5e144.tar.gz
Add implicit support for <cfgfile>.d/*.conf file parsing.
-rw-r--r--doc/man/pwquality.conf.54
-rw-r--r--src/pwquality.h5
-rw-r--r--src/settings.c69
3 files changed, 77 insertions, 1 deletions
diff --git a/doc/man/pwquality.conf.5 b/doc/man/pwquality.conf.5
index 7315e1c..6d9c80e 100644
--- a/doc/man/pwquality.conf.5
+++ b/doc/man/pwquality.conf.5
@@ -9,6 +9,7 @@
pwquality.conf \- configuration for the libpwquality library
.SH SYNOPSIS
\fB/etc/security/pwquality.conf\fR
+\fB/etc/security/pwquality.conf.d/*.conf\fR
.SH DESCRIPTION
\fBpwquality.conf\fR provides a way to configure the default password
quality requirements for the system passwords. This file is read by the
@@ -19,6 +20,9 @@ The file has a very simple \fIname = value\fR format with possible comments
starting with \fB#\fR character. The whitespace at the beginning of line, end
of line, and around the \fB=\fR sign is ignored.
+The libpwquality library also first reads all \fB*.conf\fR files from the
+\fB/etc/security/pwquality.conf.d\fR directory in ASCII sorted order. The
+values of the same settings are overriden in the order the files are parsed.
.PD
.SH OPTIONS
The possible options in the file are:
diff --git a/src/pwquality.h b/src/pwquality.h
index 2f09e82..32f5f02 100644
--- a/src/pwquality.h
+++ b/src/pwquality.h
@@ -77,7 +77,10 @@ pwquality_free_settings(pwquality_settings_t *pwq);
/* Parse the configuration file (if cfgfile is NULL then the default one).
* If auxerror is not NULL it also possibly returns auxiliary error information
- * that must be passed into pwquality_strerror() function. */
+ * that must be passed into pwquality_strerror() function.
+ * New in 1.3.0: First tries to parse all *.conf configuration files from
+ * <cfgfile>.d directory if it exists. Order of parsing determines what
+ values will be in effect - the latest wins. */
int
pwquality_read_config(pwquality_settings_t *pwq, const char *cfgfile,
void **auxerror);
diff --git a/src/settings.c b/src/settings.c
index a6d6efa..a5f22be 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -12,6 +12,7 @@
#include <limits.h>
#include <ctype.h>
#include <errno.h>
+#include <dirent.h>
#include "pwquality.h"
#include "pwqprivate.h"
@@ -181,15 +182,83 @@ read_config_file(pwquality_settings_t *pwq, const char *cfgfile, void **auxerror
return rv;
}
+static int
+filter_conf(const struct dirent *d)
+{
+ const char *p;
+
+ if ((p = strstr(d->d_name, ".conf")) == NULL)
+ return 0;
+
+ if (p[5] != '\0')
+ return 0;
+
+ return 1;
+}
+
+static int
+comp_func(const struct dirent **a, const struct dirent **b)
+{
+ return strcmp ((*a)->d_name, (*b)->d_name);
+}
+
/* parse the configuration file (if NULL then the default one) */
int
pwquality_read_config(pwquality_settings_t *pwq, const char *cfgfile, void **auxerror)
{
+ char *dirname;
+ struct dirent **namelist;
+ int n;
+ int i;
+ int rv = 0;
+
if (auxerror)
*auxerror = NULL;
if (cfgfile == NULL)
cfgfile = PWQUALITY_DEFAULT_CFGFILE;
+ /* read "*.conf" files from "<cfgfile>.d" directory first */
+
+ if (asprintf(&dirname, "%s.d", cfgfile) < 0)
+ return PWQ_ERROR_MEM_ALLOC;
+
+ /* we do not care about scandir races here so we use scandir */
+ n = scandir(dirname, &namelist, filter_conf, comp_func);
+
+ if (n < 0) {
+ namelist = NULL;
+
+ if (errno == ENOMEM) {
+ free(dirname);
+ return PWQ_ERROR_MEM_ALLOC;
+ } /* other errors are ignored */
+ }
+
+ for (i = 0; i < n; i++) {
+ char *subcfg;
+
+ if (rv) {
+ free(namelist[i]);
+ continue;
+ }
+
+ if (asprintf(&subcfg, "%s/%s", dirname, namelist[i]->d_name) < 0)
+ rv = PWQ_ERROR_MEM_ALLOC;
+ else {
+ rv = read_config_file(pwq, subcfg, auxerror);
+ if (rv == PWQ_ERROR_CFGFILE_OPEN)
+ rv = 0; /* ignore, this one does not modify auxerror */
+ free(subcfg);
+ }
+
+ free(namelist[i]);
+ }
+ free(dirname);
+ free(namelist);
+
+ if (rv)
+ return rv;
+
return read_config_file(pwq, cfgfile, auxerror);
}