diff options
Diffstat (limited to 'contrib/pam_modules/pam_passwdqc/passwdqc_parse.c')
-rw-r--r-- | contrib/pam_modules/pam_passwdqc/passwdqc_parse.c | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/contrib/pam_modules/pam_passwdqc/passwdqc_parse.c b/contrib/pam_modules/pam_passwdqc/passwdqc_parse.c new file mode 100644 index 000000000000..1f8165e01df9 --- /dev/null +++ b/contrib/pam_modules/pam_passwdqc/passwdqc_parse.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2000-2003,2005,2016,2020,2021 by Solar Designer + * Copyright (c) 2008,2009 by Dmitry V. Levin + * See LICENSE + */ + +#ifdef _MSC_VER +#define _CRT_NONSTDC_NO_WARNINGS /* we use POSIX function names */ +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include "passwdqc.h" +#include "concat.h" + +static const char *skip_prefix(const char *sample, const char *prefix) +{ + size_t len = strlen(prefix); + + if (strncmp(sample, prefix, len)) + return NULL; + return sample + len; +} + +static int +parse_option(passwdqc_params_t *params, char **reason, const char *option) +{ + const char *err = "Invalid parameter value"; + const char * const err_oom = "Out of memory"; + const char *p; + char *e; + int i, rc = 0; + unsigned long v; + + *reason = NULL; + if ((p = skip_prefix(option, "min="))) { + for (i = 0; i < 5; i++) { + if (!strncmp(p, "disabled", 8)) { + v = INT_MAX; + p += 8; + } else { + v = strtoul(p, &e, 10); + p = e; + } + if (i < 4 && *p++ != ',') + goto parse_error; + if (v > INT_MAX) + goto parse_error; + if (i && (int)v > params->qc.min[i - 1]) + goto parse_error; + params->qc.min[i] = v; + } + if (*p) + goto parse_error; + } else if ((p = skip_prefix(option, "max="))) { + v = strtoul(p, &e, 10); + if (*e || v < 8 || v > INT_MAX) + goto parse_error; + if (v > 10000) + v = 10000; + params->qc.max = v; + } else if ((p = skip_prefix(option, "passphrase="))) { + v = strtoul(p, &e, 10); + if (*e || v > INT_MAX) + goto parse_error; + params->qc.passphrase_words = v; + } else if ((p = skip_prefix(option, "match="))) { + v = strtoul(p, &e, 10); + if (*e || v > INT_MAX) + goto parse_error; + params->qc.match_length = v; + } else if ((p = skip_prefix(option, "similar="))) { + if (!strcmp(p, "permit")) + params->qc.similar_deny = 0; + else if (!strcmp(p, "deny")) + params->qc.similar_deny = 1; + else + goto parse_error; + } else if ((p = skip_prefix(option, "random="))) { + v = strtoul(p, &e, 10); + if (!strcmp(e, ",only")) { + e += 5; + params->qc.min[4] = INT_MAX; + } + if (*e || (v && v < 24) || v > 136) + goto parse_error; + params->qc.random_bits = v; + } else if ((p = skip_prefix(option, "wordlist="))) { + free(params->qc.wordlist); + params->qc.wordlist = NULL; + if (*p && !(params->qc.wordlist = strdup(p))) { + err = err_oom; + goto parse_error; + } + } else if ((p = skip_prefix(option, "denylist="))) { + free(params->qc.denylist); + params->qc.denylist = NULL; + if (*p && !(params->qc.denylist = strdup(p))) { + err = err_oom; + goto parse_error; + } + } else if ((p = skip_prefix(option, "filter="))) { + free(params->qc.filter); + params->qc.filter = NULL; + if (*p && !(params->qc.filter = strdup(p))) { + err = err_oom; + goto parse_error; + } + } else if ((p = skip_prefix(option, "enforce="))) { + params->pam.flags &= ~F_ENFORCE_MASK; + if (!strcmp(p, "users")) + params->pam.flags |= F_ENFORCE_USERS; + else if (!strcmp(p, "everyone")) + params->pam.flags |= F_ENFORCE_EVERYONE; + else if (strcmp(p, "none")) + goto parse_error; + } else if (!strcmp(option, "non-unix")) { + if (params->pam.flags & F_CHECK_OLDAUTHTOK) + goto parse_error; + params->pam.flags |= F_NON_UNIX; + } else if ((p = skip_prefix(option, "retry="))) { + v = strtoul(p, &e, 10); + if (*e || v > INT_MAX) + goto parse_error; + params->pam.retry = v; + } else if ((p = skip_prefix(option, "ask_oldauthtok"))) { + params->pam.flags &= ~F_ASK_OLDAUTHTOK_MASK; + if (params->pam.flags & F_USE_FIRST_PASS) + goto parse_error; + if (!p[0]) + params->pam.flags |= F_ASK_OLDAUTHTOK_PRELIM; + else if (!strcmp(p, "=update")) + params->pam.flags |= F_ASK_OLDAUTHTOK_UPDATE; + else + goto parse_error; + } else if (!strcmp(option, "check_oldauthtok")) { + if (params->pam.flags & F_NON_UNIX) + goto parse_error; + params->pam.flags |= F_CHECK_OLDAUTHTOK; + } else if (!strcmp(option, "use_first_pass")) { + if (params->pam.flags & F_ASK_OLDAUTHTOK_MASK) + goto parse_error; + params->pam.flags |= F_USE_FIRST_PASS | F_USE_AUTHTOK; + } else if (!strcmp(option, "use_authtok")) { + params->pam.flags |= F_USE_AUTHTOK; + } else if (!strcmp(option, "noaudit")) { + params->pam.flags |= F_NO_AUDIT; + } else if ((p = skip_prefix(option, "config="))) { + if ((rc = passwdqc_params_load(params, reason, p))) + goto parse_error; + } else { + err = "Invalid parameter"; + goto parse_error; + } + + return 0; + +parse_error: + passwdqc_params_free(params); + e = concat("Error parsing parameter \"", option, "\": ", + (rc ? (*reason ? *reason : err_oom) : err), NULL); + free(*reason); + *reason = e; + return rc ? rc : -1; +} + +int +passwdqc_params_parse(passwdqc_params_t *params, char **reason, + int argc, const char *const *argv) +{ + int i; + + *reason = NULL; + for (i = 0; i < argc; ++i) { + int rc; + + if ((rc = parse_option(params, reason, argv[i]))) + return rc; + } + return 0; +} + +static const passwdqc_params_t defaults = { + { + {INT_MAX, 24, 11, 8, 7}, /* min */ + 72, /* max */ + 3, /* passphrase_words */ + 4, /* match_length */ + 1, /* similar_deny */ + 47, /* random_bits */ + NULL, /* wordlist */ + NULL, /* denylist */ + NULL /* filter */ + }, + { + F_ENFORCE_EVERYONE, /* flags */ + 3 /* retry */ + } +}; + +void passwdqc_params_reset(passwdqc_params_t *params) +{ + *params = defaults; +} + +void passwdqc_params_free(passwdqc_params_t *params) +{ + free(params->qc.wordlist); + free(params->qc.denylist); + free(params->qc.filter); + passwdqc_params_reset(params); +} |