#define BUF_SIZE 1024
#define MODULE_CHUNK 4
-#define UNKNOWN_MODULE_PATH "<*unknown module path*>"
+#define UNKNOWN_MODULE "<*unknown module*>"
#ifndef _PAM_ISA
#define _PAM_ISA "."
#endif
static void _pam_free_handlers_aux(struct handler **hp);
static int _pam_add_handler(pam_handle_t *pamh
- , int must_fail, int other, int type
+ , int must_fail, int other, int stack_level, int type
, int *actions, const char *mod_path
, int argc, char **argv, int argvlen);
static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name
, const char *service /* specific file */
, int module_type /* specific type */
+ , int stack_level /* level of substack */
#ifdef PAM_READ_BOTH_CONFS
, int not_other
#endif /* PAM_READ_BOTH_CONFS */
static int _pam_parse_conf_file(pam_handle_t *pamh, FILE *f
, const char *known_service /* specific file */
, int requested_module_type /* specific type */
+ , int stack_level /* level of substack */
#ifdef PAM_READ_BOTH_CONFS
, int not_other
#endif /* PAM_READ_BOTH_CONFS */
int module_type, actions[_PAM_RETURN_VALUES];
int other; /* set if module is for PAM_DEFAULT_SERVICE */
int res; /* module added successfully? */
- int must_fail=0; /* a badly formatted line must fail when used */
+ int handler_type = PAM_HT_MODULE; /* regular handler from a module */
int argc;
char **argv;
int argvlen;
other = 0;
else
#endif /* PAM_READ_BOTH_CONFS */
- other = !_pam_strCMP(this_service, PAM_DEFAULT_SERVICE);
+ other = !strcasecmp(this_service, PAM_DEFAULT_SERVICE);
/* accept "service name" or PAM_DEFAULT_SERVICE modules */
- if (!_pam_strCMP(this_service, pamh->service_name) || other) {
+ if (!strcasecmp(this_service, pamh->service_name) || other) {
int pam_include = 0;
+ int substack = 0;
/* This is a service we are looking for */
D(("_pam_init_handlers: Found PAM config entry for: %s"
"(%s) empty module type", this_service);
module_type = (requested_module_type != PAM_T_ANY) ?
requested_module_type : PAM_T_AUTH; /* most sensitive */
- must_fail = 1; /* install as normal but fail when dispatched */
- } else if (!_pam_strCMP("auth", tok)) {
- module_type = PAM_T_AUTH;
- } else if (!_pam_strCMP("session", tok)) {
- module_type = PAM_T_SESS;
- } else if (!_pam_strCMP("account", tok)) {
- module_type = PAM_T_ACCT;
- } else if (!_pam_strCMP("password", tok)) {
- module_type = PAM_T_PASS;
+ handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */
} else {
- /* Illegal module type */
- D(("_pam_init_handlers: bad module type: %s", tok));
- pam_syslog(pamh, LOG_ERR, "(%s) illegal module type: %s",
- this_service, tok);
- module_type = (requested_module_type != PAM_T_ANY) ?
- requested_module_type : PAM_T_AUTH; /* most sensitive */
- must_fail = 1; /* install as normal but fail when dispatched */
+ if (tok[0] == '-') { /* do not log module load errors */
+ handler_type = PAM_HT_SILENT_MODULE;
+ ++tok;
+ }
+ if (!strcasecmp("auth", tok)) {
+ module_type = PAM_T_AUTH;
+ } else if (!strcasecmp("session", tok)) {
+ module_type = PAM_T_SESS;
+ } else if (!strcasecmp("account", tok)) {
+ module_type = PAM_T_ACCT;
+ } else if (!strcasecmp("password", tok)) {
+ module_type = PAM_T_PASS;
+ } else {
+ /* Illegal module type */
+ D(("_pam_init_handlers: bad module type: %s", tok));
+ pam_syslog(pamh, LOG_ERR, "(%s) illegal module type: %s",
+ this_service, tok);
+ module_type = (requested_module_type != PAM_T_ANY) ?
+ requested_module_type : PAM_T_AUTH; /* most sensitive */
+ handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */
+ }
}
- D(("Using %s config entry: %s", must_fail?"BAD ":"", tok));
+ D(("Using %s config entry: %s", handler_type?"BAD ":"", tok));
if (requested_module_type != PAM_T_ANY &&
module_type != requested_module_type) {
D(("Skipping config entry: %s (requested=%d, found=%d)",
pam_syslog(pamh, LOG_ERR,
"(%s) no control flag supplied", this_service);
_pam_set_default_control(actions, _PAM_ACTION_BAD);
- must_fail = 1;
- } else if (!_pam_strCMP("required", tok)) {
+ handler_type = PAM_HT_MUST_FAIL;
+ } else if (!strcasecmp("required", tok)) {
D(("*PAM_F_REQUIRED*"));
actions[PAM_SUCCESS] = _PAM_ACTION_OK;
actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
_pam_set_default_control(actions, _PAM_ACTION_BAD);
- } else if (!_pam_strCMP("requisite", tok)) {
+ } else if (!strcasecmp("requisite", tok)) {
D(("*PAM_F_REQUISITE*"));
actions[PAM_SUCCESS] = _PAM_ACTION_OK;
actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
_pam_set_default_control(actions, _PAM_ACTION_DIE);
- } else if (!_pam_strCMP("optional", tok)) {
+ } else if (!strcasecmp("optional", tok)) {
D(("*PAM_F_OPTIONAL*"));
actions[PAM_SUCCESS] = _PAM_ACTION_OK;
actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
_pam_set_default_control(actions, _PAM_ACTION_IGNORE);
- } else if (!_pam_strCMP("sufficient", tok)) {
+ } else if (!strcasecmp("sufficient", tok)) {
D(("*PAM_F_SUFFICIENT*"));
actions[PAM_SUCCESS] = _PAM_ACTION_DONE;
actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_DONE;
_pam_set_default_control(actions, _PAM_ACTION_IGNORE);
- } else if (!_pam_strCMP("include", tok)) {
+ } else if (!strcasecmp("include", tok)) {
D(("*PAM_F_INCLUDE*"));
pam_include = 1;
+ substack = 0;
+ } else if (!strcasecmp("substack", tok)) {
+ D(("*PAM_F_SUBSTACK*"));
+ pam_include = 1;
+ substack = 1;
} else {
D(("will need to parse %s", tok));
_pam_parse_control(actions, tok);
tok = _pam_StrTok(NULL, " \n\t", &nexttok);
if (pam_include) {
- if (_pam_load_conf_file(pamh, tok, this_service, module_type
+ if (substack) {
+ res = _pam_add_handler(pamh, PAM_HT_SUBSTACK, other,
+ stack_level, module_type, actions, tok,
+ 0, NULL, 0);
+ if (res != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "error adding substack %s", tok);
+ D(("failed to load module - aborting"));
+ return PAM_ABORT;
+ }
+ }
+ if (_pam_load_conf_file(pamh, tok, this_service, module_type,
+ stack_level + substack
#ifdef PAM_READ_BOTH_CONFS
, !other
#endif /* PAM_READ_BOTH_CONFS */
continue;
_pam_set_default_control(actions, _PAM_ACTION_BAD);
mod_path = NULL;
- must_fail = 1;
+ handler_type = PAM_HT_MUST_FAIL;
nexttok = NULL;
} else if (tok != NULL) {
mod_path = tok;
pam_syslog(pamh, LOG_ERR,
"(%s) no module name supplied", this_service);
mod_path = NULL;
- must_fail = 1;
+ handler_type = PAM_HT_MUST_FAIL;
}
/* nexttok points to remaining arguments... */
argv = NULL;
}
-#ifdef DEBUG
+#ifdef PAM_DEBUG
{
int y;
D(("CONF%s: %s%s %d %s %d"
- , must_fail?"<*will fail*>":""
+ , handler_type==PAM_HT_MUST_FAIL?"<*will fail*>":""
, this_service, other ? "(backup)":""
, module_type
, mod_path, argc));
}
#endif
- res = _pam_add_handler(pamh, must_fail, other
+ res = _pam_add_handler(pamh, handler_type, other, stack_level
, module_type, actions, mod_path
, argc, argv, argvlen);
if (res != PAM_SUCCESS) {
static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name
, const char *service /* specific file */
, int module_type /* specific type */
+ , int stack_level /* level of substack */
#ifdef PAM_READ_BOTH_CONFS
, int not_other
#endif /* PAM_READ_BOTH_CONFS */
D(("_pam_load_conf_file called"));
+ if (stack_level >= PAM_SUBSTACK_MAX_LEVEL) {
+ D(("maximum level of substacks reached"));
+ pam_syslog(pamh, LOG_ERR, "maximum level of substacks reached");
+ return PAM_ABORT;
+ }
+
if (config_name == NULL) {
D(("no config file supplied"));
pam_syslog(pamh, LOG_ERR, "(%s) no config file supplied", service);
D(("opening %s", config_name));
f = fopen(config_name, "r");
if (f != NULL) {
- retval = _pam_parse_conf_file(pamh, f, service, module_type
+ retval = _pam_parse_conf_file(pamh, f, service, module_type, stack_level
#ifdef PAM_READ_BOTH_CONFS
, not_other
#endif /* PAM_READ_BOTH_CONFS */
f = fopen(filename, "r");
if (f != NULL) {
/* would test magic here? */
- retval = _pam_parse_conf_file(pamh, f, pamh->service_name, PAM_T_ANY
+ retval = _pam_parse_conf_file(pamh, f, pamh->service_name,
+ PAM_T_ANY, 0
#ifdef PAM_READ_BOTH_CONFS
, 0
#endif /* PAM_READ_BOTH_CONFS */
D(("checking %s", PAM_CONFIG));
if ((f = fopen(PAM_CONFIG,"r")) != NULL) {
- retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 1);
+ retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0, 1);
fclose(f);
} else
#endif /* PAM_READ_BOTH_CONFS */
f = fopen(PAM_DEFAULT_SERVICE_FILE, "r");
if (f != NULL) {
/* would test magic here? */
- retval = _pam_parse_conf_file(pamh, f
- , PAM_DEFAULT_SERVICE
- , PAM_T_ANY
+ retval = _pam_parse_conf_file(pamh, f, PAM_DEFAULT_SERVICE,
+ PAM_T_ANY, 0
#ifdef PAM_READ_BOTH_CONFS
, 0
#endif /* PAM_READ_BOTH_CONFS */
return PAM_ABORT;
}
- retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY
+ retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0
#ifdef PAM_READ_BOTH_CONFS
, 0
#endif /* PAM_READ_BOTH_CONFS */
static int _pam_assemble_line(FILE *f, char *buffer, int buf_len)
{
char *p = buffer;
+ char *endp = buffer + buf_len;
char *s, *os;
int used = 0;
D(("called."));
for (;;) {
- if (used >= buf_len) {
+ if (p >= endp) {
/* Overflow */
D(("_pam_assemble_line: overflow"));
return -1;
}
- if (fgets(p, buf_len - used, f) == NULL) {
+ if (fgets(p, endp - p, f) == NULL) {
if (used) {
/* Incomplete read */
return -1;
else
p++;
- if ((retval = strdup (p)) == NULL)
+ if ((retval = _pam_strdup (p)) == NULL)
return NULL;
dot = strrchr (retval, '.');
return retval;
}
-int _pam_add_handler(pam_handle_t *pamh
- , int must_fail, int other, int type
- , int *actions, const char *mod_path
- , int argc, char **argv, int argvlen)
+static struct loaded_module *
+_pam_load_module(pam_handle_t *pamh, const char *mod_path, int handler_type)
{
- struct loaded_module *mod;
int x = 0;
- struct handler **handler_p;
- struct handler **handler_p2;
- struct handlers *the_handlers;
- const char *sym, *sym2;
- char *mod_full_path=NULL;
+ int success;
#ifndef PAM_STATIC
char *mod_full_isa_path=NULL, *isa=NULL;
#endif
- servicefn func, func2;
- int success;
+ struct loaded_module *mod;
- D(("called."));
- IF_NO_PAMH("_pam_add_handler",pamh,PAM_SYSTEM_ERR);
+ D(("_pam_load_module: loading module `%s'", mod_path));
- /* if NULL set to something that can be searched for */
- switch (mod_path != NULL) {
- default:
- if (mod_path[0] == '/') {
- break;
- }
- if (asprintf(&mod_full_path, "%s%s",
- DEFAULT_MODULE_PATH, mod_path) >= 0) {
- mod_path = mod_full_path;
- break;
- }
- mod_full_path = NULL;
- pam_syslog(pamh, LOG_CRIT, "cannot malloc full mod path");
- case 0:
- mod_path = UNKNOWN_MODULE_PATH;
- }
-
- D(("_pam_add_handler: adding type %d, module `%s'",type,mod_path));
- mod = pamh->handlers.module;
+ mod = pamh->handlers.module;
/* First, ensure the module is loaded */
while (x < pamh->handlers.modules_used) {
if (tmp == NULL) {
D(("cannot enlarge module pointer memory"));
pam_syslog(pamh, LOG_ERR,
- "realloc returned NULL in _pam_add_handler");
- _pam_drop(mod_full_path);
- return PAM_ABORT;
+ "realloc returned NULL in _pam_load_module");
+ return NULL;
}
pamh->handlers.module = tmp;
pamh->handlers.modules_allocated += MODULE_CHUNK;
/* Only load static function if function was not found dynamically.
* This code should work even if no dynamic loading is available. */
if (success != PAM_SUCCESS) {
- D(("_pam_add_handler: open static handler %s", mod_path));
- mod->dl_handle = _pam_open_static_handler(mod_path);
+ D(("_pam_load_module: open static handler %s", mod_path));
+ mod->dl_handle = _pam_open_static_handler(pamh, mod_path);
if (mod->dl_handle == NULL) {
- D(("_pam_add_handler: unable to find static handler %s",
+ D(("_pam_load_module: unable to find static handler %s",
mod_path));
- pam_syslog(pamh, LOG_ERR,
+ if (handler_type != PAM_HT_SILENT_MODULE)
+ pam_syslog(pamh, LOG_ERR,
"unable to open static handler %s", mod_path);
/* Didn't find module in dynamic or static..will mark bad */
} else {
}
}
#else
- D(("_pam_add_handler: _pam_dlopen(%s)", mod_path));
+ D(("_pam_load_module: _pam_dlopen(%s)", mod_path));
mod->dl_handle = _pam_dlopen(mod_path);
- D(("_pam_add_handler: _pam_dlopen'ed"));
- D(("_pam_add_handler: dlopen'ed"));
+ D(("_pam_load_module: _pam_dlopen'ed"));
+ D(("_pam_load_module: dlopen'ed"));
if (mod->dl_handle == NULL) {
if (strstr(mod_path, "$ISA")) {
mod_full_isa_path = malloc(strlen(mod_path) + strlen(_PAM_ISA) + 1);
if (mod_full_isa_path == NULL) {
- D(("_pam_handler: couldn't get memory for mod_path"));
+ D(("_pam_load_module: couldn't get memory for mod_path"));
pam_syslog(pamh, LOG_ERR, "no memory for module path");
success = PAM_ABORT;
} else {
}
}
if (mod->dl_handle == NULL) {
- D(("_pam_add_handler: _pam_dlopen(%s) failed", mod_path));
- pam_syslog(pamh, LOG_ERR, "unable to dlopen(%s)", mod_path);
- pam_syslog(pamh, LOG_ERR, "[error: %s]", _pam_dlerror());
+ D(("_pam_load_module: _pam_dlopen(%s) failed", mod_path));
+ if (handler_type != PAM_HT_SILENT_MODULE)
+ pam_syslog(pamh, LOG_ERR, "unable to dlopen(%s): %s", mod_path,
+ _pam_dlerror());
/* Don't abort yet; static code may be able to find function.
* But defaults to abort if nothing found below... */
} else {
mod->dl_handle = NULL;
mod->type = PAM_MT_FAULTY_MOD;
pamh->handlers.modules_used++;
- pam_syslog(pamh, LOG_ERR, "adding faulty module: %s", mod_path);
+ if (handler_type != PAM_HT_SILENT_MODULE)
+ pam_syslog(pamh, LOG_ERR, "adding faulty module: %s", mod_path);
success = PAM_SUCCESS; /* We have successfully added a module */
}
/* indicate its name - later we will search for it by this */
if ((mod->name = _pam_strdup(mod_path)) == NULL) {
- D(("_pam_handler: couldn't get memory for mod_path"));
+ D(("_pam_load_module: couldn't get memory for mod_path"));
pam_syslog(pamh, LOG_ERR, "no memory for module path");
success = PAM_ABORT;
}
mod += x; /* the located module */
success = PAM_SUCCESS;
}
+ return success == PAM_SUCCESS ? mod : NULL;
+}
+
+int _pam_add_handler(pam_handle_t *pamh
+ , int handler_type, int other, int stack_level, int type
+ , int *actions, const char *mod_path
+ , int argc, char **argv, int argvlen)
+{
+ struct loaded_module *mod = NULL;
+ struct handler **handler_p;
+ struct handler **handler_p2;
+ struct handlers *the_handlers;
+ const char *sym, *sym2;
+ char *mod_full_path;
+ servicefn func, func2;
+ int mod_type = PAM_MT_FAULTY_MOD;
+
+ D(("called."));
+ IF_NO_PAMH("_pam_add_handler",pamh,PAM_SYSTEM_ERR);
+
+ D(("_pam_add_handler: adding type %d, handler_type %d, module `%s'",
+ type, handler_type, mod_path));
- _pam_drop(mod_full_path);
- mod_path = NULL; /* no longer needed or trusted */
+ if ((handler_type == PAM_HT_MODULE || handler_type == PAM_HT_SILENT_MODULE) &&
+ mod_path != NULL) {
+ if (mod_path[0] == '/') {
+ mod = _pam_load_module(pamh, mod_path, handler_type);
+ } else if (asprintf(&mod_full_path, "%s%s",
+ DEFAULT_MODULE_PATH, mod_path) >= 0) {
+ mod = _pam_load_module(pamh, mod_full_path, handler_type);
+ _pam_drop(mod_full_path);
+ } else {
+ pam_syslog(pamh, LOG_CRIT, "cannot malloc full mod path");
+ return PAM_ABORT;
+ }
+
+ if (mod == NULL) {
+ /* if we get here with NULL it means allocation error */
+ return PAM_ABORT;
+ }
+
+ mod_type = mod->type;
+ }
- /* Now return error if necessary after trying all possible ways... */
- if (success != PAM_SUCCESS)
- return(success);
+ if (mod_path == NULL)
+ mod_path = UNKNOWN_MODULE;
/*
- * At this point 'mod' points to the stored/loaded module. If its
- * dl_handle is unknown, then we must be able to indicate dispatch
- * failure with 'must_fail'
+ * At this point 'mod' points to the stored/loaded module.
*/
/* Now define the handler(s) based on mod->dlhandle and type */
/* are the modules reliable? */
if (
#ifdef PAM_STATIC
- mod->type != PAM_MT_STATIC_MOD
+ mod_type != PAM_MT_STATIC_MOD
&&
#else
- mod->type != PAM_MT_DYNAMIC_MOD
+ mod_type != PAM_MT_DYNAMIC_MOD
&&
#endif
- mod->type != PAM_MT_FAULTY_MOD
+ mod_type != PAM_MT_FAULTY_MOD
) {
- D(("_pam_add_handlers: illegal module library type; %d", mod->type));
+ D(("_pam_add_handlers: illegal module library type; %d", mod_type));
pam_syslog(pamh, LOG_ERR,
"internal error: module library type not known: %s;%d",
- sym, mod->type);
+ sym, mod_type);
return PAM_ABORT;
}
/* now identify this module's functions - for non-faulty modules */
#ifdef PAM_STATIC
- if ((mod->type == PAM_MT_STATIC_MOD) &&
+ if ((mod_type == PAM_MT_STATIC_MOD) &&
(func = (servicefn)_pam_get_static_sym(mod->dl_handle, sym)) == NULL) {
pam_syslog(pamh, LOG_ERR, "unable to resolve static symbol: %s", sym);
}
#else
- if ((mod->type == PAM_MT_DYNAMIC_MOD) &&
+ if ((mod_type == PAM_MT_DYNAMIC_MOD) &&
!(func = _pam_dlsym(mod->dl_handle, sym)) ) {
pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym);
}
#endif
if (sym2) {
#ifdef PAM_STATIC
- if ((mod->type == PAM_MT_STATIC_MOD) &&
+ if ((mod_type == PAM_MT_STATIC_MOD) &&
(func2 = (servicefn)_pam_get_static_sym(mod->dl_handle, sym2))
== NULL) {
pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym2);
}
#else
- if ((mod->type == PAM_MT_DYNAMIC_MOD) &&
+ if ((mod_type == PAM_MT_DYNAMIC_MOD) &&
!(func2 = _pam_dlsym(mod->dl_handle, sym2)) ) {
pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym2);
}
return (PAM_ABORT);
}
- (*handler_p)->must_fail = must_fail; /* failure forced? */
+ (*handler_p)->handler_type = handler_type;
+ (*handler_p)->stack_level = stack_level;
(*handler_p)->func = func;
memcpy((*handler_p)->actions,actions,sizeof((*handler_p)->actions));
(*handler_p)->cached_retval = _PAM_INVALID_RETVAL;
(*handler_p)->cached_retval_p = &((*handler_p)->cached_retval);
(*handler_p)->argc = argc;
(*handler_p)->argv = argv; /* not a copy */
- (*handler_p)->mod_name = extract_modulename(mod->name);
+ (*handler_p)->mod_name = extract_modulename(mod_path);
(*handler_p)->next = NULL;
/* some of the modules have a second calling function */
return (PAM_ABORT);
}
- (*handler_p2)->must_fail = must_fail; /* failure forced? */
+ (*handler_p2)->handler_type = handler_type;
+ (*handler_p2)->stack_level = stack_level;
(*handler_p2)->func = func2;
memcpy((*handler_p2)->actions,actions,sizeof((*handler_p2)->actions));
(*handler_p2)->cached_retval = _PAM_INVALID_RETVAL; /* ignored */
} else {
(*handler_p2)->argv = NULL; /* no arguments */
}
- (*handler_p2)->mod_name = extract_modulename(mod->name);
+ (*handler_p2)->mod_name = extract_modulename(mod_path);
(*handler_p2)->next = NULL;
}