diff --git a/pam-0.99.8.1-substack.patch b/pam-0.99.8.1-substack.patch
new file mode 100644
index 0000000..912df83
--- /dev/null
+++ b/pam-0.99.8.1-substack.patch
@@ -0,0 +1,784 @@
+Index: libpam/pam_dispatch.c
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_dispatch.c,v
+retrieving revision 1.11
+diff -u -p -r1.11 pam_dispatch.c
+--- libpam/pam_dispatch.c 1 Aug 2006 08:54:57 -0000 1.11
++++ libpam/pam_dispatch.c 12 Oct 2007 16:23:37 -0000
+@@ -34,7 +34,8 @@
+ static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
+ _pam_boolean resumed, int use_cached_chain)
+ {
+- int depth, impression, status, skip_depth;
++ int depth, impression, status, skip_depth, prev_level, stack_level;
++ struct _pam_substack_state *substates = NULL;
+
+ IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR);
+
+@@ -54,27 +55,51 @@ static int _pam_dispatch_aux(pam_handle_
+ skip_depth = pamh->former.depth;
+ status = pamh->former.status;
+ impression = pamh->former.impression;
++ substates = pamh->former.substates;
+ /* forget all that */
+ pamh->former.impression = _PAM_UNDEF;
+ pamh->former.status = PAM_MUST_FAIL_CODE;
+ pamh->former.depth = 0;
++ pamh->former.substates = NULL;
+ } else {
+ skip_depth = 0;
+- impression = _PAM_UNDEF;
+- status = PAM_MUST_FAIL_CODE;
++ substates = malloc(PAM_SUBSTACK_MAX_LEVEL * sizeof(*substates));
++ if (substates == NULL) {
++ pam_syslog(pamh, LOG_CRIT,
++ "_pam_dispatch_aux: no memory for substack states");
++ return PAM_BUF_ERR;
++ }
++ substates[0].impression = impression = _PAM_UNDEF;
++ substates[0].status = status = PAM_MUST_FAIL_CODE;
+ }
+
++ prev_level = 0;
++
+ /* Loop through module logic stack */
+- for (depth=0 ; h != NULL ; h = h->next, ++depth) {
++ for (depth=0 ; h != NULL ; prev_level = stack_level, h = h->next, ++depth) {
+ int retval, cached_retval, action;
+
++ stack_level = h->stack_level;
++
+ /* skip leading modules if they have already returned */
+ if (depth < skip_depth) {
+ continue;
+ }
+
++ /* remember state if we are entering a substack */
++ if (prev_level < stack_level) {
++ substates[stack_level].impression = impression;
++ substates[stack_level].status = status;
++ }
++
+ /* attempt to call the module */
+- if (h->func == NULL) {
++ if (h->handler_type == PAM_HT_MUST_FAIL) {
++ D(("module poorly listed in PAM config; forcing failure"));
++ retval = PAM_MUST_FAIL_CODE;
++ } else if (h->handler_type == PAM_HT_SUBSTACK) {
++ D(("skipping substack handler"));
++ continue;
++ } else if (h->func == NULL) {
+ D(("module function is not defined, indicating failure"));
+ retval = PAM_MODULE_UNKNOWN;
+ } else {
+@@ -83,10 +108,6 @@ static int _pam_dispatch_aux(pam_handle_
+ retval = h->func(pamh, flags, h->argc, h->argv);
+ pamh->mod_name=NULL;
+ D(("module returned: %s", pam_strerror(pamh, retval)));
+- if (h->must_fail) {
+- D(("module poorly listed in PAM config; forcing failure"));
+- retval = PAM_MUST_FAIL_CODE;
+- }
+ }
+
+ /*
+@@ -100,6 +121,7 @@ static int _pam_dispatch_aux(pam_handle_
+ pamh->former.impression = impression;
+ pamh->former.status = status;
+ pamh->former.depth = depth;
++ pamh->former.substates = substates;
+
+ D(("module %d returned PAM_INCOMPLETE", depth));
+ return retval;
+@@ -176,8 +198,8 @@ static int _pam_dispatch_aux(pam_handle_
+ switch (action) {
+ case _PAM_ACTION_RESET:
+
+- impression = _PAM_UNDEF;
+- status = PAM_MUST_FAIL_CODE;
++ impression = substates[stack_level].impression;
++ status = substates[stack_level].status;
+ break;
+
+ case _PAM_ACTION_OK:
+@@ -244,9 +266,13 @@ static int _pam_dispatch_aux(pam_handle_
+ }
+
+ /* this means that we need to skip #action stacked modules */
+- do {
+- h = h->next;
+- } while ( --action > 0 && h != NULL );
++ while (h->next != NULL && h->next->stack_level >= stack_level && action > 0) {
++ do {
++ h = h->next;
++ ++depth;
++ } while (h->next != NULL && h->next->stack_level > stack_level);
++ --action;
++ }
+
+ /* note if we try to skip too many modules action is
+ still non-zero and we snag the next if. */
+@@ -254,14 +280,19 @@ static int _pam_dispatch_aux(pam_handle_
+
+ /* this case is a syntax error: we can't succeed */
+ if (action) {
+- D(("action syntax error"));
++ pam_syslog(pamh, LOG_ERR, "bad jump in stack");
+ impression = _PAM_NEGATIVE;
+ status = PAM_MUST_FAIL_CODE;
+ }
+ }
+- }
+-
++ continue;
++
+ decision_made: /* by getting here we have made a decision */
++ while (h->next != NULL && h->next->stack_level >= stack_level) {
++ h = h->next;
++ ++depth;
++ }
++ }
+
+ /* Sanity check */
+ if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) {
+@@ -269,6 +300,7 @@ decision_made: /* by getting here w
+ status = PAM_MUST_FAIL_CODE;
+ }
+
++ free(substates);
+ /* We have made a decision about the modules executed */
+ return status;
+ }
+Index: libpam/pam_end.c
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_end.c,v
+retrieving revision 1.4
+diff -u -p -r1.4 pam_end.c
+--- libpam/pam_end.c 12 Jan 2006 10:06:49 -0000 1.4
++++ libpam/pam_end.c 12 Oct 2007 16:23:37 -0000
+@@ -71,6 +71,8 @@ int pam_end(pam_handle_t *pamh, int pam_
+ _pam_drop(pamh->pam_conversation);
+ pamh->fail_delay.delay_fn_ptr = NULL;
+
++ _pam_drop(pamh->former.substates);
++
+ /* and finally liberate the memory for the pam_handle structure */
+
+ _pam_drop(pamh);
+Index: libpam/pam_handlers.c
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_handlers.c,v
+retrieving revision 1.24
+diff -u -p -r1.24 pam_handlers.c
+--- libpam/pam_handlers.c 14 Jun 2006 11:41:47 -0000 1.24
++++ libpam/pam_handlers.c 12 Oct 2007 16:23:37 -0000
+@@ -18,7 +18,7 @@
+
+ #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
+@@ -28,7 +28,7 @@ static int _pam_assemble_line(FILE *f, c
+ 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);
+
+@@ -43,6 +43,7 @@ static int _pam_add_handler(pam_handle_t
+ 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 */
+@@ -51,6 +52,7 @@ static int _pam_load_conf_file(pam_handl
+ 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 */
+@@ -68,7 +70,7 @@ static int _pam_parse_conf_file(pam_hand
+ 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;
+@@ -92,6 +94,7 @@ static int _pam_parse_conf_file(pam_hand
+ /* accept "service name" or PAM_DEFAULT_SERVICE modules */
+ 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"
+@@ -105,7 +108,7 @@ static int _pam_parse_conf_file(pam_hand
+ "(%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 */
++ handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */
+ } else if (!strcasecmp("auth", tok)) {
+ module_type = PAM_T_AUTH;
+ } else if (!strcasecmp("session", tok)) {
+@@ -121,9 +124,9 @@ static int _pam_parse_conf_file(pam_hand
+ 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 */
++ 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)",
+@@ -145,7 +148,7 @@ static int _pam_parse_conf_file(pam_hand
+ pam_syslog(pamh, LOG_ERR,
+ "(%s) no control flag supplied", this_service);
+ _pam_set_default_control(actions, _PAM_ACTION_BAD);
+- must_fail = 1;
++ handler_type = PAM_HT_MUST_FAIL;
+ } else if (!strcasecmp("required", tok)) {
+ D(("*PAM_F_REQUIRED*"));
+ actions[PAM_SUCCESS] = _PAM_ACTION_OK;
+@@ -171,6 +174,11 @@ static int _pam_parse_conf_file(pam_hand
+ } 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);
+@@ -180,7 +188,18 @@ static int _pam_parse_conf_file(pam_hand
+
+ 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 */
+@@ -188,7 +207,7 @@ static int _pam_parse_conf_file(pam_hand
+ 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;
+@@ -199,7 +218,7 @@ static int _pam_parse_conf_file(pam_hand
+ 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... */
+@@ -219,7 +238,7 @@ static int _pam_parse_conf_file(pam_hand
+ 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));
+@@ -235,7 +254,7 @@ static int _pam_parse_conf_file(pam_hand
+ }
+ #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) {
+@@ -252,6 +271,7 @@ static int _pam_parse_conf_file(pam_hand
+ 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 */
+@@ -263,6 +283,12 @@ static int _pam_load_conf_file(pam_handl
+
+ 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);
+@@ -280,7 +306,7 @@ static int _pam_load_conf_file(pam_handl
+ 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 */
+@@ -379,7 +405,8 @@ int _pam_init_handlers(pam_handle_t *pam
+ 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 */
+@@ -400,7 +427,7 @@ int _pam_init_handlers(pam_handle_t *pam
+ 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 */
+@@ -419,9 +446,8 @@ int _pam_init_handlers(pam_handle_t *pam
+ 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 */
+@@ -454,7 +480,7 @@ int _pam_init_handlers(pam_handle_t *pam
+ 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 */
+@@ -581,46 +607,19 @@ extract_modulename(const char *mod_path)
+ 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)
+ {
+- 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;
+-
+- D(("called."));
+- IF_NO_PAMH("_pam_add_handler",pamh,PAM_SYSTEM_ERR);
+-
+- /* 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;
+- }
++ struct loaded_module *mod;
+
+- D(("_pam_add_handler: adding type %d, module `%s'",type,mod_path));
+- mod = pamh->handlers.module;
++ D(("_pam_load_module: loading module `%s'", mod_path));
++
++ mod = pamh->handlers.module;
+
+ /* First, ensure the module is loaded */
+ while (x < pamh->handlers.modules_used) {
+@@ -639,9 +638,8 @@ int _pam_add_handler(pam_handle_t *pamh
+ 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;
+@@ -654,10 +652,10 @@ int _pam_add_handler(pam_handle_t *pamh
+ /* 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));
++ 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,
+ "unable to open static handler %s", mod_path);
+@@ -670,15 +668,15 @@ int _pam_add_handler(pam_handle_t *pamh
+ }
+ }
+ #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 {
+@@ -694,9 +692,9 @@ int _pam_add_handler(pam_handle_t *pamh
+ }
+ }
+ 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));
++ 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 {
+@@ -717,7 +715,7 @@ int _pam_add_handler(pam_handle_t *pamh
+
+ /* 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;
+ }
+@@ -726,18 +724,54 @@ int _pam_add_handler(pam_handle_t *pamh
+ 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);
+
+- _pam_drop(mod_full_path);
+- mod_path = NULL; /* no longer needed or trusted */
++ D(("_pam_add_handler: adding type %d, handler_type %d, module `%s'",
++ type, handler_type, mod_path));
+
+- /* Now return error if necessary after trying all possible ways... */
+- if (success != PAM_SUCCESS)
+- return(success);
++ if (handler_type == PAM_HT_MODULE && mod_path != NULL) {
++ if (mod_path[0] == '/') {
++ mod = _pam_load_module(pamh, mod_path);
++ } else if (asprintf(&mod_full_path, "%s%s",
++ DEFAULT_MODULE_PATH, mod_path) >= 0) {
++ mod = _pam_load_module(pamh, mod_full_path);
++ _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;
++ }
++
++ 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 */
+@@ -780,43 +814,43 @@ int _pam_add_handler(pam_handle_t *pamh
+ /* 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);
+ }
+@@ -835,14 +869,15 @@ int _pam_add_handler(pam_handle_t *pamh
+ 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 */
+@@ -857,7 +892,8 @@ int _pam_add_handler(pam_handle_t *pamh
+ 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 */
+@@ -873,7 +909,7 @@ int _pam_add_handler(pam_handle_t *pamh
+ } 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;
+ }
+
+Index: libpam/pam_private.h
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_private.h,v
+retrieving revision 1.19
+diff -u -p -r1.19 pam_private.h
+--- libpam/pam_private.h 24 Jul 2006 15:47:40 -0000 1.19
++++ libpam/pam_private.h 12 Oct 2007 16:23:37 -0000
+@@ -44,7 +44,7 @@
+ #define _PAM_INVALID_RETVAL -1 /* default value for cached_retval */
+
+ struct handler {
+- int must_fail;
++ int handler_type;
+ int (*func)(pam_handle_t *pamh, int flags, int argc, char **argv);
+ int actions[_PAM_RETURN_VALUES];
+ /* set by authenticate, open_session, chauthtok(1st)
+@@ -54,8 +54,13 @@ struct handler {
+ char **argv;
+ struct handler *next;
+ char *mod_name;
++ int stack_level;
+ };
+
++#define PAM_HT_MODULE 0
++#define PAM_HT_MUST_FAIL 1
++#define PAM_HT_SUBSTACK 2
++
+ struct loaded_module {
+ char *name;
+ int type; /* PAM_STATIC_MOD or PAM_DYNAMIC_MOD */
+@@ -76,7 +81,7 @@ struct handlers {
+ };
+
+ struct service {
+- struct loaded_module *module; /* Only used for dynamic loading */
++ struct loaded_module *module; /* Array of modules */
+ int modules_allocated;
+ int modules_used;
+ int handlers_loaded;
+@@ -111,6 +116,12 @@ struct _pam_fail_delay {
+ const void *delay_fn_ptr;
+ };
+
++/* initial state in substack */
++struct _pam_substack_state {
++ int impression;
++ int status;
++};
++
+ struct _pam_former_state {
+ /* this is known and set by _pam_dispatch() */
+ int choice; /* which flavor of module function did we call? */
+@@ -119,6 +130,7 @@ struct _pam_former_state {
+ int depth; /* how deep in the stack were we? */
+ int impression; /* the impression at that time */
+ int status; /* the status before returning incomplete */
++ struct _pam_substack_state *substates; /* array of initial substack states */
+
+ /* state info used by pam_get_user() function */
+ int fail_user;
+@@ -175,6 +187,8 @@ struct pam_handle {
+ #define _PAM_ACTION_UNDEF -6 /* this is treated as an error
+ ( = _PAM_ACTION_BAD) */
+
++#define PAM_SUBSTACK_MAX_LEVEL 16 /* maximum level of substacks */
++
+ /* character tables for parsing config files */
+ extern const char * const _pam_token_actions[-_PAM_ACTION_UNDEF];
+ extern const char * const _pam_token_returns[_PAM_RETURN_VALUES+1];
+Index: libpam/pam_start.c
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_start.c,v
+retrieving revision 1.9
+diff -u -p -r1.9 pam_start.c
+--- libpam/pam_start.c 24 Jul 2006 15:47:40 -0000 1.9
++++ libpam/pam_start.c 12 Oct 2007 16:23:37 -0000
+@@ -88,6 +88,7 @@ int pam_start (
+ (*pamh)->oldauthtok = NULL;
+ (*pamh)->fail_delay.delay_fn_ptr = NULL;
+ (*pamh)->former.choice = PAM_NOT_STACKED;
++ (*pamh)->former.substates = NULL;
+ #ifdef HAVE_LIBAUDIT
+ (*pamh)->audit_state = 0;
+ #endif
+Index: doc/man/pam.conf-syntax.xml
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/doc/man/pam.conf-syntax.xml,v
+retrieving revision 1.4
+retrieving revision 1.5
+diff -u -r1.4 -r1.5
+--- doc/man/pam.conf-syntax.xml 26 Aug 2007 22:44:51 -0000 1.4
++++ doc/man/pam.conf-syntax.xml 19 Oct 2007 17:06:30 -0000 1.5
+@@ -180,6 +180,24 @@
+
+
+
++
++ substack
++
++
++ include all lines of given type from the configuration
++ file specified as an argument to this control. This differs from
++ include in that evaluation of the
++ done and die actions
++ in a substack does not cause skipping the rest of the complete
++ module stack, but only of the substack. Jumps in a substack
++ also can not make evaluation jump out of it, and the whole substack
++ is counted as one module when the jump is done in a parent stack.
++ The reset action will reset the state of a
++ module stack to the state it was in as of beginning of the substack
++ evaluation.
++
++
++
+
+
+
diff --git a/pam.spec b/pam.spec
index ae2d4cf..cd2d790 100644
--- a/pam.spec
+++ b/pam.spec
@@ -11,7 +11,7 @@
Summary: A security tool which provides authentication for applications
Name: pam
Version: 0.99.8.1
-Release: 10%{?dist}
+Release: 11%{?dist}
# The library is BSD licensed with option to relicense as GPLv2+ - this option is redundant
# as the BSD license allows that anyway. pam_timestamp and pam_console modules are GPLv2+,
# pam_rhosts_auth module is BSD with advertising
@@ -44,6 +44,7 @@ Patch44: pam-0.99.7.1-namespace-homedir.patch
Patch45: pam-0.99.8.1-selinux-permit.patch
Patch46: pam-0.99.8.1-succif-in-operator.patch
Patch47: pam-0.99.8.1-xauth-no-free.patch
+Patch48: pam-0.99.8.1-substack.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: cracklib, cracklib-dicts >= 2.8
@@ -115,6 +116,7 @@ popd
%patch45 -p1 -b .permit
%patch46 -p1 -b .in-operator
%patch47 -p1 -b .no-free
+%patch48 -p0 -b .substack
autoreconf
@@ -407,6 +409,9 @@ fi
%doc doc/adg/*.txt doc/adg/html
%changelog
+* Wed Nov 7 2007 Tomas Mraz 0.99.8.1-11
+- add substack support
+
* Tue Sep 25 2007 Tomas Mraz 0.99.8.1-10
- update db4 to 4.6.19 (#274661)