Deleted Added
full compact
3c3
< * Copyright (c) 2004-2007 Dag-Erling Sm��rgrav
---
> * Copyright (c) 2004-2011 Dag-Erling Sm��rgrav
35c35
< * $Id: openpam_configure.c 408 2007-12-21 11:36:24Z des $
---
> * $Id: openpam_configure.c 500 2011-11-22 12:07:03Z des $
37a38,41
> #ifdef HAVE_CONFIG_H
> # include "config.h"
> #endif
>
46a51
> #include "openpam_strlcmp.h"
48,53c53
< const char *_pam_facility_name[PAM_NUM_FACILITIES] = {
< [PAM_ACCOUNT] = "account",
< [PAM_AUTH] = "auth",
< [PAM_PASSWORD] = "password",
< [PAM_SESSION] = "session",
< };
---
> static int openpam_load_chain(pam_handle_t *, const char *, pam_facility_t);
55,61c55,59
< const char *_pam_control_flag_name[PAM_NUM_CONTROL_FLAGS] = {
< [PAM_BINDING] = "binding",
< [PAM_OPTIONAL] = "optional",
< [PAM_REQUIRED] = "required",
< [PAM_REQUISITE] = "requisite",
< [PAM_SUFFICIENT] = "sufficient",
< };
---
> /*
> * Evaluates to non-zero if the argument is a linear whitespace character.
> */
> #define is_lws(ch) \
> (ch == ' ' || ch == '\t')
63c61,66
< static int openpam_load_chain(pam_handle_t *, const char *, pam_facility_t);
---
> /*
> * Evaluates to non-zero if the argument is a printable ASCII character.
> * Assumes that the execution character set is a superset of ASCII.
> */
> #define is_p(ch) \
> (ch >= '!' && ch <= '~')
66,67c69,71
< * Matches a word against the first one in a string.
< * Returns non-zero if they match.
---
> * Returns non-zero if the argument belongs to the POSIX Portable Filename
> * Character Set. Assumes that the execution character set is a superset
> * of ASCII.
68a73,94
> #define is_pfcs(ch) \
> ((ch >= '0' && ch <= '9') || \
> (ch >= 'A' && ch <= 'Z') || \
> (ch >= 'a' && ch <= 'z') || \
> ch == '.' || ch == '_' || ch == '-')
>
> /*
> * Parse the service name.
> *
> * Returns the length of the service name, or 0 if the end of the string
> * was reached or a disallowed non-whitespace character was encountered.
> *
> * If parse_service_name() is successful, it updates *service to point to
> * the first character of the service name and *line to point one
> * character past the end. If it reaches the end of the string, it
> * updates *line to point to the terminating NUL character and leaves
> * *service unmodified. In all other cases, it leaves both *line and
> * *service unmodified.
> *
> * Allowed characters are all characters in the POSIX portable filename
> * character set.
> */
70c96
< match_word(const char *str, const char *word)
---
> parse_service_name(char **line, char **service)
71a98
> char *b, *e;
73,75c100,113
< while (*str && tolower(*str) == tolower(*word))
< ++str, ++word;
< return (*str == ' ' && *word == '\0');
---
> for (b = *line; *b && is_lws(*b); ++b)
> /* nothing */ ;
> if (!*b) {
> *line = b;
> return (0);
> }
> for (e = b; *e && !is_lws(*e); ++e)
> if (!is_pfcs(*e))
> return (0);
> if (e == b)
> return (0);
> *line = e;
> *service = b;
> return (e - b);
79c117,126
< * Return a pointer to the next word (or the final NUL) in a string.
---
> * Parse the facility name.
> *
> * Returns the corresponding pam_facility_t value, or -1 if the end of the
> * string was reached, a disallowed non-whitespace character was
> * encountered, or the first word was not a recognized facility name.
> *
> * If parse_facility_name() is successful, it updates *line to point one
> * character past the end of the facility name. If it reaches the end of
> * the string, it updates *line to point to the terminating NUL character.
> * In all other cases, it leaves *line unmodified.
81,82c128,129
< static const char *
< next_word(const char *str)
---
> static pam_facility_t
> parse_facility_name(char **line)
83a131,132
> char *b, *e;
> int i;
85,91c134,150
< /* skip current word */
< while (*str && *str != ' ')
< ++str;
< /* skip whitespace */
< while (*str == ' ')
< ++str;
< return (str);
---
> for (b = *line; *b && is_lws(*b); ++b)
> /* nothing */ ;
> if (!*b) {
> *line = b;
> return ((pam_facility_t)-1);
> }
> for (e = b; *e && !is_lws(*e); ++e)
> /* nothing */ ;
> if (e == b)
> return ((pam_facility_t)-1);
> for (i = 0; i < PAM_NUM_FACILITIES; ++i)
> if (strlcmp(pam_facility_name[i], b, e - b) == 0)
> break;
> if (i == PAM_NUM_FACILITIES)
> return ((pam_facility_t)-1);
> *line = e;
> return (i);
95c154,158
< * Return a malloc()ed copy of the first word in a string.
---
> * Parse the word "include".
> *
> * If the next word on the line is "include", parse_include() updates
> * *line to point one character past "include" and returns 1. Otherwise,
> * it leaves *line unmodified and returns 0.
96a160,275
> static int
> parse_include(char **line)
> {
> char *b, *e;
>
> for (b = *line; *b && is_lws(*b); ++b)
> /* nothing */ ;
> if (!*b) {
> *line = b;
> return (-1);
> }
> for (e = b; *e && !is_lws(*e); ++e)
> /* nothing */ ;
> if (e == b)
> return (0);
> if (strlcmp("include", b, e - b) != 0)
> return (0);
> *line = e;
> return (1);
> }
>
> /*
> * Parse the control flag.
> *
> * Returns the corresponding pam_control_t value, or -1 if the end of the
> * string was reached, a disallowed non-whitespace character was
> * encountered, or the first word was not a recognized control flag.
> *
> * If parse_control_flag() is successful, it updates *line to point one
> * character past the end of the control flag. If it reaches the end of
> * the string, it updates *line to point to the terminating NUL character.
> * In all other cases, it leaves *line unmodified.
> */
> static pam_control_t
> parse_control_flag(char **line)
> {
> char *b, *e;
> int i;
>
> for (b = *line; *b && is_lws(*b); ++b)
> /* nothing */ ;
> if (!*b) {
> *line = b;
> return ((pam_control_t)-1);
> }
> for (e = b; *e && !is_lws(*e); ++e)
> /* nothing */ ;
> if (e == b)
> return ((pam_control_t)-1);
> for (i = 0; i < PAM_NUM_CONTROL_FLAGS; ++i)
> if (strlcmp(pam_control_flag_name[i], b, e - b) == 0)
> break;
> if (i == PAM_NUM_CONTROL_FLAGS)
> return ((pam_control_t)-1);
> *line = e;
> return (i);
> }
>
> /*
> * Parse a file name.
> *
> * Returns the length of the file name, or 0 if the end of the string was
> * reached or a disallowed non-whitespace character was encountered.
> *
> * If parse_filename() is successful, it updates *filename to point to the
> * first character of the filename and *line to point one character past
> * the end. If it reaches the end of the string, it updates *line to
> * point to the terminating NUL character and leaves *filename unmodified.
> * In all other cases, it leaves both *line and *filename unmodified.
> *
> * Allowed characters are all characters in the POSIX portable filename
> * character set, plus the path separator (forward slash).
> */
> static int
> parse_filename(char **line, char **filename)
> {
> char *b, *e;
>
> for (b = *line; *b && is_lws(*b); ++b)
> /* nothing */ ;
> if (!*b) {
> *line = b;
> return (0);
> }
> for (e = b; *e && !is_lws(*e); ++e)
> if (!is_pfcs(*e) && *e != '/')
> return (0);
> if (e == b)
> return (0);
> *line = e;
> *filename = b;
> return (e - b);
> }
>
> /*
> * Parse an option.
> *
> * Returns a dynamically allocated string containing the next module
> * option, or NULL if the end of the string was reached or a disallowed
> * non-whitespace character was encountered.
> *
> * If parse_option() is successful, it updates *line to point one
> * character past the end of the option. If it reaches the end of the
> * string, it updates *line to point to the terminating NUL character. In
> * all other cases, it leaves *line unmodified.
> *
> * If parse_option() fails to allocate memory, it will return NULL and set
> * errno to a non-zero value.
> *
> * Allowed characters for option names are all characters in the POSIX
> * portable filename character set. Allowed characters for option values
> * are any printable non-whitespace characters. The option value may be
> * quoted in either single or double quotes, in which case space
> * characters and whichever quote character was not used are allowed.
> * Note that the entire value must be quoted, not just part of it.
> */
98c277
< dup_word(const char *str)
---
> parse_option(char **line)
100,101c279,282
< const char *end;
< char *word;
---
> char *nb, *ne, *vb, *ve;
> unsigned char q = 0;
> char *option;
> size_t size;
103c284,285
< for (end = str; *end && *end != ' '; ++end)
---
> errno = 0;
> for (nb = *line; *nb && is_lws(*nb); ++nb)
105c287,288
< if (asprintf(&word, "%.*s", (int)(end - str), str) < 0)
---
> if (!*nb) {
> *line = nb;
107c290,325
< return (word);
---
> }
> for (ne = nb; *ne && !is_lws(*ne) && *ne != '='; ++ne)
> if (!is_pfcs(*ne))
> return (NULL);
> if (ne == nb)
> return (NULL);
> if (*ne == '=') {
> vb = ne + 1;
> if (*vb == '"' || *vb == '\'')
> q = *vb++;
> for (ve = vb;
> *ve && *ve != q && (is_p(*ve) || (q && is_lws(*ve)));
> ++ve)
> /* nothing */ ;
> if (q && *ve != q)
> /* non-printable character or missing endquote */
> return (NULL);
> if (q && *(ve + 1) && !is_lws(*(ve + 1)))
> /* garbage after value */
> return (NULL);
> } else {
> vb = ve = ne;
> }
> size = (ne - nb) + 1;
> if (ve > vb)
> size += (ve - vb) + 1;
> if ((option = malloc(size)) == NULL)
> return (NULL);
> strncpy(option, nb, ne - nb);
> if (ve > vb) {
> option[ne - nb] = '=';
> strncpy(option + (ne - nb) + 1, vb, ve - vb);
> }
> option[size - 1] = '\0';
> *line = q ? ve + 1 : ve;
> return (option);
111c329,333
< * Return the length of the first word in a string.
---
> * Consume trailing whitespace.
> *
> * If there are no non-whitespace characters left on the line, parse_eol()
> * updates *line to point at the terminating NUL character and returns 0.
> * Otherwise, it leaves *line unmodified and returns a non-zero value.
114c336
< wordlen(const char *str)
---
> parse_eol(char **line)
116c338
< int i;
---
> char *p;
118c340
< for (i = 0; str[i] && str[i] != ' '; ++i)
---
> for (p = *line; *p && is_lws(*p); ++p)
120c342,345
< return (i);
---
> if (*p)
> return ((unsigned char)*p);
> *line = p;
> return (0);
129c354
< openpam_read_chain(pam_handle_t *pamh,
---
> openpam_parse_chain(pam_handle_t *pamh,
136,137d360
< const char *p, *q;
< int count, i, lineno, ret;
140c363,365
< char *line, *name;
---
> char *line, *str, *name;
> char *option, **optv;
> int len, lineno, ret;
146c371
< return (0);
---
> return (PAM_SUCCESS);
147a373,376
> if (openpam_check_desc_owner_perms(filename, fileno(f)) != 0) {
> fclose(f);
> return (PAM_SYSTEM_ERR);
> }
149c378,379
< count = lineno = 0;
---
> name = NULL;
> lineno = 0;
151,153c381
< p = line;
<
< /* match service name */
---
> /* get service name if necessary */
155c383,386
< if (!match_word(p, service)) {
---
> if ((len = parse_service_name(&line, &str)) == 0) {
> openpam_log(PAM_LOG_NOTICE,
> "%s(%d): invalid service name (ignored)",
> filename, lineno);
159c390,393
< p = next_word(p);
---
> if (strlcmp(service, str, len) != 0) {
> FREE(line);
> continue;
> }
162,169c396,400
< /* match facility name */
< for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt)
< if (match_word(p, _pam_facility_name[fclt]))
< break;
< if (fclt == PAM_NUM_FACILITIES) {
< openpam_log(PAM_LOG_NOTICE,
< "%s(%d): invalid facility '%.*s' (ignored)",
< filename, lineno, wordlen(p), p);
---
> /* get facility name */
> if ((fclt = parse_facility_name(&line)) == (pam_facility_t)-1) {
> openpam_log(PAM_LOG_ERROR,
> "%s(%d): missing or invalid facility",
> filename, lineno);
176d406
< p = next_word(p);
178,183c408,412
< /* include other chain */
< if (match_word(p, "include")) {
< p = next_word(p);
< if (*next_word(p) != '\0')
< openpam_log(PAM_LOG_NOTICE,
< "%s(%d): garbage at end of 'include' line",
---
> /* check for "include" */
> if (parse_include(&line)) {
> if ((len = parse_service_name(&line, &str)) == 0) {
> openpam_log(PAM_LOG_ERROR,
> "%s(%d): missing or invalid filename",
185c414,416
< if ((name = dup_word(p)) == NULL)
---
> goto fail;
> }
> if ((name = strndup(str, len)) == NULL)
186a418,423
> if (parse_eol(&line) != 0) {
> openpam_log(PAM_LOG_ERROR,
> "%s(%d): garbage at end of line",
> filename, lineno);
> goto fail;
> }
189c426
< if (ret < 0)
---
> if (ret != PAM_SUCCESS)
191d427
< count += ret;
196,198c432,438
< /* allocate new entry */
< if ((this = calloc(1, sizeof *this)) == NULL)
< goto syserr;
---
> /* get control flag */
> if ((ctlf = parse_control_flag(&line)) == (pam_control_t)-1) {
> openpam_log(PAM_LOG_ERROR,
> "%s(%d): missing or invalid control flag",
> filename, lineno);
> goto fail;
> }
200,204c440,441
< /* control flag */
< for (ctlf = 0; ctlf < PAM_NUM_CONTROL_FLAGS; ++ctlf)
< if (match_word(p, _pam_control_flag_name[ctlf]))
< break;
< if (ctlf == PAM_NUM_CONTROL_FLAGS) {
---
> /* get module name */
> if ((len = parse_filename(&line, &str)) == 0) {
206,207c443,444
< "%s(%d): invalid control flag '%.*s'",
< filename, lineno, wordlen(p), p);
---
> "%s(%d): missing or invalid module name",
> filename, lineno);
209a447,452
> if ((name = strndup(str, len)) == NULL)
> goto syserr;
>
> /* allocate new entry */
> if ((this = calloc(1, sizeof *this)) == NULL)
> goto syserr;
212,214c455,468
< /* module name */
< p = next_word(p);
< if (*p == '\0') {
---
> /* get module options */
> if ((this->optv = malloc(sizeof *optv)) == NULL)
> goto syserr;
> this->optc = 0;
> while ((option = parse_option(&line)) != NULL) {
> optv = realloc(this->optv,
> (this->optc + 2) * sizeof *optv);
> if (optv == NULL)
> goto syserr;
> this->optv = optv;
> this->optv[this->optc++] = option;
> }
> this->optv[this->optc] = NULL;
> if (*line != '\0') {
216c470
< "%s(%d): missing module name",
---
> "%s(%d): syntax error in module options",
220,221c474,475
< if ((name = dup_word(p)) == NULL)
< goto syserr;
---
>
> /* load module */
227,241d480
< /* module options */
< p = q = next_word(p);
< while (*q != '\0') {
< ++this->optc;
< q = next_word(q);
< }
< this->optv = calloc(this->optc + 1, sizeof(char *));
< if (this->optv == NULL)
< goto syserr;
< for (i = 0; i < this->optc; ++i) {
< if ((this->optv[i] = dup_word(p)) == NULL)
< goto syserr;
< p = next_word(p);
< }
<
248d486
< ++count;
256,257c494,495
< return (count);
< syserr:
---
> return (PAM_SUCCESS);
> syserr:
259c497,502
< fail:
---
> fail:
> if (this && this->optc) {
> while (this->optc--)
> FREE(this->optv[this->optc]);
> FREE(this->optv);
> }
261a505
> FREE(name);
263c507
< return (-1);
---
> return (PAM_SYSTEM_ERR);
286c530
< int r;
---
> int ret;
293c537
< return (-PAM_BUF_ERR);
---
> return (PAM_BUF_ERR);
295c539
< r = openpam_read_chain(pamh, service, facility,
---
> ret = openpam_parse_chain(pamh, service, facility,
299c543
< r = openpam_read_chain(pamh, service, facility,
---
> ret = openpam_parse_chain(pamh, service, facility,
302,303c546,547
< if (r != 0)
< return (r);
---
> if (ret != PAM_SUCCESS)
> return (ret);
305c549
< return (0);
---
> return (PAM_SUCCESS);
318a563
> const char *p;
320c565,569
< if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) < 0)
---
> for (p = service; *p; ++p)
> if (!is_pfcs(*p))
> return (PAM_SYSTEM_ERR);
>
> if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) != PAM_SUCCESS)
326c575
< if (openpam_load_chain(pamh, PAM_OTHER, fclt) < 0)
---
> if (openpam_load_chain(pamh, PAM_OTHER, fclt) != PAM_SUCCESS)
330c579
< load_err:
---
> load_err: