Print this page
2947 initial /etc/pam.d cut
@@ -19,10 +19,12 @@
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2012 Joshua M. Clulow <josh@sysmgr.org>
*/
#include <syslog.h>
#include <dlfcn.h>
#include <sys/types.h>
@@ -78,15 +80,15 @@
static int load_modules(pam_handle_t *, int, char *, pamtab_t *);
static void *open_module(pam_handle_t *, char *);
static int load_function(void *, char *, int (**func)());
/* functions to read and store the pam.conf configuration file */
-static int open_pam_conf(struct pam_fh **, pam_handle_t *, char *);
+static int open_pam_conf(struct pam_fh **, pam_handle_t *, char *, int);
static void close_pam_conf(struct pam_fh *);
-static int read_pam_conf(pam_handle_t *, char *);
+static int read_pam_conf(pam_handle_t *, char *, char *, int);
static int get_pam_conf_entry(struct pam_fh *, pam_handle_t *,
- pamtab_t **);
+ pamtab_t **, char *, int);
static char *read_next_token(char **);
static char *nextline(struct pam_fh *, pam_handle_t *, int *);
static int verify_pam_conf(pamtab_t *, char *);
/* functions to clean up and free memory */
@@ -1008,18 +1010,41 @@
int optional_error = 0;
int required_error = 0;
int success = 0;
pamtab_t *modulep;
int (*sm_func)();
+ char *service;
+ char *service_file;
+ int shardfile = 1;
if (pamh == NULL)
return (PAM_SYSTEM_ERR);
- /* read initial entries from pam.conf */
- if ((err = read_pam_conf(pamh, PAM_CONFIG)) != PAM_SUCCESS) {
+ (void) pam_get_item(pamh, PAM_SERVICE, (void **)&service);
+ if (service == NULL || *service == '\0') {
+ __pam_log(LOG_AUTH | LOG_ERR, "No service name");
+ return (PAM_SYSTEM_ERR);
+ }
+
+ /* read initial entries from /etc/pam.d/<service> */
+ if (asprintf(&service_file, "%s%s", PAM_CONFIG_DIR, service) < 0)
+ return (PAM_SYSTEM_ERR);
+ if ((err = read_pam_conf(pamh, service_file, service, shardfile))
+ != PAM_SUCCESS) {
+ shardfile = 0;
+ pam_trace(PAM_DEBUG_CONF, "run_stack[%d:%s]: can't read "
+ "service-specific conf %s", pamh->include_depth,
+ pam_trace_cname(pamh), modulep->module_path, service_file);
+
+ /* fall back to reading initial entries from pam.conf */
+ if ((err = read_pam_conf(pamh, PAM_CONFIG, service, shardfile))
+ != PAM_SUCCESS) {
return (err);
}
+ }
+ free(service_file);
+ service_file = NULL;
if ((modulep =
pamh->pam_conf_info[pamh->include_depth][type]) == NULL) {
__pam_log(LOG_AUTH | LOG_ERR, "%s no initial module present",
pam_trace_cname(pamh));
@@ -1051,11 +1076,12 @@
pamh->pam_conf_name[PAM_MAX_INCLUDE],
PAM_MAX_INCLUDE);
goto exit_return;
}
if ((err = read_pam_conf(pamh,
- modulep->module_path)) != PAM_SUCCESS) {
+ modulep->module_path, service, shardfile))
+ != PAM_SUCCESS) {
__pam_log(LOG_AUTH | LOG_ERR,
"run_stack[%d:%s]: can't read included "
"conf %s", pamh->include_depth,
pam_trace_cname(pamh),
modulep->module_path);
@@ -1909,16 +1935,18 @@
/*
* open_pam_conf - open the pam.conf config file
*/
static int
-open_pam_conf(struct pam_fh **pam_fh, pam_handle_t *pamh, char *config)
+open_pam_conf(struct pam_fh **pam_fh, pam_handle_t *pamh, char *config,
+ int shardfile)
{
struct stat64 stb;
int fd;
if ((fd = open(config, O_RDONLY)) == -1) {
+ if (!shardfile)
__pam_log(LOG_AUTH | LOG_ALERT,
"open_pam_conf[%d:%s]: open(%s) failed: %s",
pamh->include_depth, pam_trace_cname(pamh), config,
strerror(errno));
return (0);
@@ -1986,40 +2014,34 @@
* read_pam_conf - read in each entry in pam.conf and store info
* under the pam handle.
*/
static int
-read_pam_conf(pam_handle_t *pamh, char *config)
+read_pam_conf(pam_handle_t *pamh, char *config, char *service, int shardfile)
{
struct pam_fh *pam_fh;
pamtab_t *pamentp;
pamtab_t *tpament;
- char *service;
int error;
int i = pamh->include_depth; /* include depth */
+ int j;
/*
* service types:
* error (-1), "auth" (0), "account" (1), "session" (2), "password" (3)
*/
int service_found[PAM_NUM_MODULE_TYPES+1] = {0, 0, 0, 0, 0};
- (void) pam_get_item(pamh, PAM_SERVICE, (void **)&service);
- if (service == NULL || *service == '\0') {
- __pam_log(LOG_AUTH | LOG_ERR, "No service name");
- return (PAM_SYSTEM_ERR);
- }
-
pamh->pam_conf_name[i] = strdup(config);
pam_trace(PAM_DEBUG_CONF, "read_pam_conf[%d:%s](%p) open(%s)",
i, pam_trace_cname(pamh), (void *)pamh, config);
- if (open_pam_conf(&pam_fh, pamh, config) == 0) {
+ if (open_pam_conf(&pam_fh, pamh, config, shardfile) == 0) {
return (PAM_SYSTEM_ERR);
}
while ((error =
- get_pam_conf_entry(pam_fh, pamh, &pamentp)) == PAM_SUCCESS &&
- pamentp) {
+ get_pam_conf_entry(pam_fh, pamh, &pamentp, service, shardfile))
+ == PAM_SUCCESS && pamentp) {
/* See if entry is this service and valid */
if (verify_pam_conf(pamentp, service)) {
pam_trace(PAM_DEBUG_CONF,
"read_pam_conf[%d:%s](%p): bad entry error %s",
@@ -2119,10 +2141,48 @@
} else {
/* irrelevant entry */
free_pamconf(pamentp);
}
}
+
+ /*
+ * If this is a shard file and we have no entries for this
+ * module type (e.g. "account"), then generate a single
+ * "include" rule for the shard file "other" as a fallback.
+ */
+ if (shardfile && strcasecmp(service, "other") != 0) {
+ for (j = 0; j < PAM_NUM_MODULE_TYPES; j++) {
+ if (service_found[j + 1] == 0 &&
+ pamh->pam_conf_info[i][j] == NULL) {
+ pamtab_t *pe = calloc(1, sizeof (pamtab_t));
+
+ pam_trace(PAM_DEBUG_CONF, "read_pam_conf(%p):"
+ "falling back to \"other\" for module "
+ "type \"%s\" in service \"%s\"",
+ (void *)pamh, pam_snames[j], service);
+ if (pe == NULL) {
+ error = PAM_SYSTEM_ERR;
+ __pam_log(LOG_AUTH | LOG_ERR,
+ "calloc: out of memory");
+ goto out;
+ }
+ pe->pam_service = strdup(service);
+ pe->pam_type = j;
+ pe->pam_flag = PAM_INCLUDE;
+ if (asprintf(&pe->module_path, "%s%s",
+ PAM_CONFIG_DIR, "other") < 0) {
+ free(pe);
+ error = PAM_SYSTEM_ERR;
+ __pam_log(LOG_AUTH | LOG_ERR,
+ "asprintf: out of memory");
+ goto out;
+ }
+ pamh->pam_conf_info[i][j] = pe;
+ }
+ }
+ }
+
out:
(void) close_pam_conf(pam_fh);
if (error != PAM_SUCCESS)
free_pam_conf_info(pamh);
return (error);
@@ -2131,11 +2191,12 @@
/*
* get_pam_conf_entry - get a pam.conf entry
*/
static int
-get_pam_conf_entry(struct pam_fh *pam_fh, pam_handle_t *pamh, pamtab_t **pam)
+get_pam_conf_entry(struct pam_fh *pam_fh, pam_handle_t *pamh, pamtab_t **pam,
+ char *service, int shardfile)
{
char *cp, *arg;
int argc;
char *tmp, *tmp_free;
int i;
@@ -2163,21 +2224,32 @@
}
pam_trace(PAM_DEBUG_CONF,
"pam.conf[%s] entry:\t%s", pam_trace_cname(pamh), current_line);
+ if (shardfile) {
+ /*
+ * If this is an /etc/pam.d shard file, then the service name
+ * comes from the file name of the shard.
+ */
+ if (((*pam)->pam_service = strdup(service)) == 0) {
+ __pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
+ goto out;
+ }
+ } else {
/* get service name (e.g. login, su, passwd) */
if ((arg = read_next_token(&cp)) == 0) {
__pam_log(LOG_AUTH | LOG_CRIT,
- "illegal pam.conf[%s] entry: %s: missing SERVICE NAME",
- pam_trace_cname(pamh), current_line);
+ "illegal pam.conf[%s] entry: %s: missing SERVICE "
+ "NAME", pam_trace_cname(pamh), current_line);
goto out;
}
if (((*pam)->pam_service = strdup(arg)) == 0) {
__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
goto out;
}
+ }
/* get module type (e.g. authentication, acct mgmt) */
if ((arg = read_next_token(&cp)) == 0) {
__pam_log(LOG_AUTH | LOG_CRIT,
"illegal pam.conf[%s] entry: %s: missing MODULE TYPE",
@@ -2238,28 +2310,32 @@
pam_trace_cname(pamh), current_line);
error = PAM_SUCCESS; /* success */
goto out;
}
if (arg[0] != '/') {
- size_t len;
+ int ret;
/*
* If module path does not start with "/", then
* prepend PAM_LIB_DIR (/usr/lib/security/).
*/
- /* sizeof (PAM_LIB_DIR) has room for '\0' */
- len = sizeof (PAM_LIB_DIR) + sizeof (PAM_ISA_DIR) + strlen(arg);
- if (((*pam)->module_path = malloc(len)) == NULL) {
- __pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
- goto out;
- }
if ((*pam)->pam_flag & PAM_INCLUDE) {
- (void) snprintf((*pam)->module_path, len, "%s%s",
- PAM_LIB_DIR, arg);
+ /*
+ * If this is an /etc/pam.d shard, we want to get
+ * included files from /etc/pam.d rather than
+ * /usr/lib/security.
+ */
+ ret = asprintf(&(*pam)->module_path, "%s%s",
+ (shardfile ? PAM_CONFIG_DIR : PAM_LIB_DIR), arg);
} else {
- (void) snprintf((*pam)->module_path, len, "%s%s%s",
+ ret = asprintf(&(*pam)->module_path, "%s%s%s",
PAM_LIB_DIR, PAM_ISA_DIR, arg);
}
+ if (ret < 0) {
+ __pam_log(LOG_AUTH | LOG_ERR,
+ "asprintf: out of memory");
+ goto out;
+ }
} else {
/* Full path provided for module */
char *isa;
/* Check for Instruction Set Architecture indicator */