openpam_configure.c [plain text]
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <security/pam_appl.h>
#include "openpam_impl.h"
const char *_pam_facility_name[PAM_NUM_FACILITIES] = {
[PAM_ACCOUNT] = "account",
[PAM_AUTH] = "auth",
[PAM_PASSWORD] = "password",
[PAM_SESSION] = "session",
};
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",
};
static int openpam_load_chain(pam_handle_t *, const char *, pam_facility_t);
static int
match_word(const char *str, const char *word)
{
while (*str && tolower(*str) == tolower(*word))
++str, ++word;
return (*str == ' ' && *word == '\0');
}
static const char *
next_word(const char *str)
{
while (*str && *str != ' ')
++str;
while (*str == ' ')
++str;
return (str);
}
static char *
dup_word(const char *str)
{
const char *end;
char *word;
for (end = str; *end && *end != ' '; ++end)
;
if (asprintf(&word, "%.*s", (int)(end - str), str) < 0)
return (NULL);
return (word);
}
static int
wordlen(const char *str)
{
int i;
for (i = 0; str[i] && str[i] != ' '; ++i)
;
return (i);
}
typedef enum { pam_conf_style, pam_d_style } openpam_style_t;
static int
openpam_read_chain(pam_handle_t *pamh,
const char *service,
pam_facility_t facility,
const char *filename,
openpam_style_t style)
{
pam_chain_t *this, **next;
const char *p, *q;
int count, i, lineno, ret;
pam_facility_t fclt;
pam_control_t ctlf;
char *line, *name;
FILE *f;
if ((f = fopen(filename, "r")) == NULL) {
openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_NOTICE,
"%s: %m", filename);
return (0);
}
this = NULL;
count = lineno = 0;
while ((line = openpam_readline(f, &lineno, NULL)) != NULL) {
p = line;
if (style == pam_conf_style) {
if (!match_word(p, service)) {
FREE(line);
continue;
}
p = next_word(p);
}
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);
goto fail;
}
if (facility != fclt && facility != PAM_FACILITY_ANY) {
FREE(line);
continue;
}
p = next_word(p);
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",
filename, lineno);
if ((name = dup_word(p)) == NULL)
goto syserr;
ret = openpam_load_chain(pamh, name, fclt);
FREE(name);
if (ret < 0)
goto fail;
count += ret;
FREE(line);
continue;
}
if ((this = calloc(1, sizeof *this)) == NULL)
goto syserr;
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) {
openpam_log(PAM_LOG_ERROR,
"%s(%d): invalid control flag '%.*s'",
filename, lineno, wordlen(p), p);
goto fail;
}
this->flag = ctlf;
p = next_word(p);
if (*p == '\0') {
openpam_log(PAM_LOG_ERROR,
"%s(%d): missing module name",
filename, lineno);
goto fail;
}
if ((name = dup_word(p)) == NULL)
goto syserr;
this->module = openpam_load_module(name);
FREE(name);
if (this->module == NULL)
goto fail;
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);
}
for (next = &pamh->chains[fclt]; *next != NULL;
next = &(*next)->next)
;
*next = this;
this = NULL;
++count;
FREE(line);
}
if (!feof(f))
goto syserr;
fclose(f);
return (count);
syserr:
openpam_log(PAM_LOG_ERROR, "%s: %m", filename);
fail:
FREE(this);
FREE(line);
fclose(f);
return (-1);
}
static const char *openpam_policy_path[] = {
"/etc/pam.d/",
"/etc/pam.conf",
"/usr/local/etc/pam.d/",
"/usr/local/etc/pam.conf",
NULL
};
static int
openpam_load_chain(pam_handle_t *pamh,
const char *service,
pam_facility_t facility)
{
const char **path;
char *filename;
size_t len;
int r;
for (path = openpam_policy_path; *path != NULL; ++path) {
len = strlen(*path);
if ((*path)[len - 1] == '/') {
if (asprintf(&filename, "%s%s", *path, service) < 0) {
openpam_log(PAM_LOG_ERROR, "asprintf(): %m");
return (-PAM_BUF_ERR);
}
r = openpam_read_chain(pamh, service, facility,
filename, pam_d_style);
FREE(filename);
} else {
r = openpam_read_chain(pamh, service, facility,
*path, pam_conf_style);
}
if (r != 0)
return (r);
}
return (0);
}
int
openpam_configure(pam_handle_t *pamh,
const char *service)
{
pam_facility_t fclt;
if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) < 0)
goto load_err;
for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt) {
if (pamh->chains[fclt] != NULL)
continue;
if (openpam_load_chain(pamh, PAM_OTHER, fclt) < 0)
goto load_err;
}
return (PAM_SUCCESS);
load_err:
openpam_clear_chains(pamh->chains);
return (PAM_SYSTEM_ERR);
}