summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog52
-rw-r--r--NEWS1
-rw-r--r--doc/man/pam.conf-syntax.xml18
-rw-r--r--libpam/pam_dispatch.c66
-rw-r--r--libpam/pam_end.c2
-rw-r--r--libpam/pam_handlers.c210
-rw-r--r--libpam/pam_private.h18
-rw-r--r--libpam/pam_start.c1
-rw-r--r--xtests/.cvsignore2
-rw-r--r--xtests/Makefile.am16
-rw-r--r--xtests/group.conf2
-rwxr-xr-xxtests/run-xtests.sh6
-rw-r--r--xtests/tst-pam_access1.c2
-rwxr-xr-xxtests/tst-pam_access1.sh6
-rw-r--r--xtests/tst-pam_access2.c2
-rwxr-xr-xxtests/tst-pam_access2.sh6
-rw-r--r--xtests/tst-pam_access4.c2
-rwxr-xr-xxtests/tst-pam_access4.sh4
-rw-r--r--xtests/tst-pam_authfail.c96
-rw-r--r--xtests/tst-pam_authfail.pamd5
-rw-r--r--xtests/tst-pam_authsucceed.c96
-rw-r--r--xtests/tst-pam_authsucceed.pamd5
-rw-r--r--xtests/tst-pam_group1.c6
-rwxr-xr-xxtests/tst-pam_group1.sh4
-rw-r--r--xtests/tst-pam_substack1.pamd5
-rwxr-xr-xxtests/tst-pam_substack1.sh3
-rw-r--r--xtests/tst-pam_substack1a.pamd2
-rw-r--r--xtests/tst-pam_substack2.pamd6
-rwxr-xr-xxtests/tst-pam_substack2.sh3
-rw-r--r--xtests/tst-pam_substack2a.pamd2
-rw-r--r--xtests/tst-pam_substack3.pamd5
-rwxr-xr-xxtests/tst-pam_substack3.sh3
-rw-r--r--xtests/tst-pam_substack3a.pamd3
-rw-r--r--xtests/tst-pam_substack4.pamd5
-rwxr-xr-xxtests/tst-pam_substack4.sh3
-rw-r--r--xtests/tst-pam_substack4a.pamd4
-rw-r--r--xtests/tst-pam_substack5.pamd4
-rwxr-xr-xxtests/tst-pam_substack5.sh3
-rw-r--r--xtests/tst-pam_substack5a.pamd3
39 files changed, 554 insertions, 128 deletions
diff --git a/ChangeLog b/ChangeLog
index 49379e6e..93ccb080 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,55 @@
+2007-10-19 Tomas Mraz <t8m@centrum.cz>
+
+ * xtests/tst-pam_access1.c: Use different name for user and group.
+ * xtests/tst-pam_access1.sh: Likewise.
+ * xtests/tst-pam_access2.c: Likewise.
+ * xtests/tst-pam_access2.sh: Likewise.
+ * xtests/tst-pam_access4.c: Likewise.
+ * xtests/tst-pam_access4.sh: Likewise.
+ * xtests/group.conf: Likewise.
+ * xtests/tst-pam_group1.c: Likewise.
+ * xtests/tst-pam_group1.sh: Likewise.
+
+ * libpam/pam_dispatch.c (_pam_dispatch_aux): Save states for substacks,
+ record substack level, skip over virtual substack modules, implement
+ evaluation of done, die, reset and jumps in substacks. Also fixes
+ too far jumps in substacks.
+ * libpam/pam_end.c (pam_end): Drop substack evaluation states.
+ * libpam/pam_handlers.c (_pam_parse_conf_file): Add substack level
+ parameter, instead of must_fail use handler_type needed for virtual
+ substack modules.
+ (_pam_load_conf_file): Add substack level parameter.
+ (_pam_init_handlers): Substack level parameter added to
+ _pam_parse_conf_file() calls.
+ (_pam_load_module): New function.
+ (_pam_add_handler): Refactor code into the _pam_load_module(). Add
+ support for virtual substack modules.
+ * libpam/pam_private.h: Rename must_fail to handler_type, add stack_level
+ to struct handler. Define handler type constants. Add struct
+ for substack evaluation states. Define constant for maximum
+ substack level. Add substack states pointer to former state struct.
+ * libpam/pam_start.c (pam_start): Initialize pointer to substack states.
+ * doc/man/pam.conf-syntax.xml: Document substack control.
+ * xtests/Makefile.am: Add new tests for substack evaluation.
+ * xtests/run_xtests.sh: Support multiple .pamd files in a test.
+ * xtests/tst-pam_authfail.pamd: New tests for substack evaluation.
+ * xtests/tst-pam_authsucceed.pamd: Likewise.
+ * xtests/tst-pam_substack1.pamd: Likewise.
+ * xtests/tst-pam_substack1a.pamd: Likewise.
+ * xtests/tst-pam_substack1.sh: Likewise.
+ * xtests/tst-pam_substack2.pamd: Likewise.
+ * xtests/tst-pam_substack2a.pamd: Likewise.
+ * xtests/tst-pam_substack2.sh: Likewise.
+ * xtests/tst-pam_substack3.pamd: Likewise.
+ * xtests/tst-pam_substack3a.pamd: Likewise.
+ * xtests/tst-pam_substack3.sh: Likewise.
+ * xtests/tst-pam_substack4.pamd: Likewise.
+ * xtests/tst-pam_substack4a.pamd: Likewise.
+ * xtests/tst-pam_substack4.sh: Likewise.
+ * xtests/tst-pam_substack5.pamd: Likewise.
+ * xtests/tst-pam_substack5a.pamd: Likewise.
+ * xtests/tst-pam_substack5.sh: Likewise.
+
2007-10-18 Tomas Mraz <t8m@centrum.cz>
* xtests/tst-pam_dispatch4.c: Fix comment about the test.
* xtests/tst-pam_dispatch4.pamd: Improve the testcase.
diff --git a/NEWS b/NEWS
index 2b14fec9..acc6d0f2 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,6 @@
Linux-PAM NEWS -- history of user-visible changes.
+* New substack directive in config file syntax.
Release 0.99.9.0
* misc_conv no longer blocks SIGINT; applications that don't want
diff --git a/doc/man/pam.conf-syntax.xml b/doc/man/pam.conf-syntax.xml
index f098a26a..1460c6f6 100644
--- a/doc/man/pam.conf-syntax.xml
+++ b/doc/man/pam.conf-syntax.xml
@@ -180,6 +180,24 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>substack</term>
+ <listitem>
+ <para>
+ include all lines of given type from the configuration
+ file specified as an argument to this control. This differs from
+ <emphasis>include</emphasis> in that evaluation of the
+ <emphasis>done</emphasis> and <emphasis>die</emphasis> 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 <emphasis>reset</emphasis> action will reset the state of a
+ module stack to the state it was in as of beginning of the substack
+ evaluation.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
<para>
diff --git a/libpam/pam_dispatch.c b/libpam/pam_dispatch.c
index ab032d74..fa4e5ed4 100644
--- a/libpam/pam_dispatch.c
+++ b/libpam/pam_dispatch.c
@@ -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_t *pamh, int flags, struct handler *h,
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_t *pamh, int flags, struct handler *h,
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_t *pamh, int flags, struct handler *h,
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_t *pamh, int flags, struct handler *h,
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_t *pamh, int flags, struct handler *h,
}
/* 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_t *pamh, int flags, struct handler *h,
/* 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 we have made a decision */
status = PAM_MUST_FAIL_CODE;
}
+ free(substates);
/* We have made a decision about the modules executed */
return status;
}
diff --git a/libpam/pam_end.c b/libpam/pam_end.c
index e46dd0c4..de1c26ed 100644
--- a/libpam/pam_end.c
+++ b/libpam/pam_end.c
@@ -71,6 +71,8 @@ int pam_end(pam_handle_t *pamh, int pam_status)
_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);
diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c
index 87d781d2..11508145 100644
--- a/libpam/pam_handlers.c
+++ b/libpam/pam_handlers.c
@@ -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, char *buf, int buf_len);
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 *pamh
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_handle_t *pamh, const char *config_name
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_handle_t *pamh, FILE *f
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_handle_t *pamh, FILE *f
/* 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_handle_t *pamh, FILE *f
"(%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_handle_t *pamh, FILE *f
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_handle_t *pamh, FILE *f
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_handle_t *pamh, FILE *f
} 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_handle_t *pamh, FILE *f
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_handle_t *pamh, FILE *f
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_handle_t *pamh, FILE *f
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_handle_t *pamh, FILE *f
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_handle_t *pamh, FILE *f
}
#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_handle_t *pamh, FILE *f
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_handle_t *pamh, const char *config_name
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_handle_t *pamh, const char *config_name
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 *pamh)
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 *pamh)
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 *pamh)
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 *pamh)
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;
}
diff --git a/libpam/pam_private.h b/libpam/pam_private.h
index 8b7d9146..bf32ad44 100644
--- a/libpam/pam_private.h
+++ b/libpam/pam_private.h
@@ -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];
diff --git a/libpam/pam_start.c b/libpam/pam_start.c
index 6f6df6b1..7b0d3aa4 100644
--- a/libpam/pam_start.c
+++ b/libpam/pam_start.c
@@ -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
diff --git a/xtests/.cvsignore b/xtests/.cvsignore
index 00c9ad07..530ce890 100644
--- a/xtests/.cvsignore
+++ b/xtests/.cvsignore
@@ -19,3 +19,5 @@ tst-pam_unix2
tst-pam_unix3
tst-pam_succeed_if1
tst-pam_group1
+tst-pam_authfail
+tst-pam_authsucceed
diff --git a/xtests/Makefile.am b/xtests/Makefile.am
index 8b6dbd25..62e32643 100644
--- a/xtests/Makefile.am
+++ b/xtests/Makefile.am
@@ -21,7 +21,13 @@ EXTRA_DIST = run-xtests.sh tst-pam_dispatch1.pamd tst-pam_dispatch2.pamd \
tst-pam_access4.pamd tst-pam_access4.sh \
limits.conf tst-pam_limits1.pamd tst-pam_limits1.sh \
tst-pam_succeed_if1.pamd tst-pam_succeed_if1.sh \
- group.conf tst-pam_group1.pamd tst-pam_group1.sh
+ group.conf tst-pam_group1.pamd tst-pam_group1.sh \
+ tst-pam_authfail.pamd tst-pam_authsucceed.pamd \
+ tst-pam_substack1.pamd tst-pam_substack1a.pamd tst-pam_substack1.sh \
+ tst-pam_substack2.pamd tst-pam_substack2a.pamd tst-pam_substack2.sh \
+ tst-pam_substack3.pamd tst-pam_substack3a.pamd tst-pam_substack3.sh \
+ tst-pam_substack4.pamd tst-pam_substack4a.pamd tst-pam_substack4.sh \
+ tst-pam_substack5.pamd tst-pam_substack5a.pamd tst-pam_substack5.sh
XTESTS = tst-pam_dispatch1 tst-pam_dispatch2 tst-pam_dispatch3 \
tst-pam_dispatch4 tst-pam_dispatch5 \
@@ -29,9 +35,13 @@ XTESTS = tst-pam_dispatch1 tst-pam_dispatch2 tst-pam_dispatch3 \
tst-pam_unix1 tst-pam_unix2 tst-pam_unix3 \
tst-pam_access1 tst-pam_access2 tst-pam_access3 \
tst-pam_access4 tst-pam_limits1 tst-pam_succeed_if1 \
- tst-pam_group1
+ tst-pam_group1 tst-pam_authfail tst-pam_authsucceed
+
+NOSRCTESTS = tst-pam_substack1 tst-pam_substack2 tst-pam_substack3 \
+ tst-pam_substack4 tst-pam_substack5
+
noinst_PROGRAMS = $(XTESTS)
xtests: $(XTESTS) run-xtests.sh
- "$(srcdir)"/run-xtests.sh "$(srcdir)" ${XTESTS}
+ "$(srcdir)"/run-xtests.sh "$(srcdir)" ${XTESTS} ${NOSRCTESTS}
diff --git a/xtests/group.conf b/xtests/group.conf
index bcfe3755..04fe3ef7 100644
--- a/xtests/group.conf
+++ b/xtests/group.conf
@@ -1,3 +1,3 @@
-tst-pam_group1;tty1;tstpamgrp;Al0000-2400;tstpamgrp
+tst-pam_group1;tty1;tstpamgrp;Al0000-2400;tstpamgrpg
diff --git a/xtests/run-xtests.sh b/xtests/run-xtests.sh
index 53dbdf06..4e981858 100755
--- a/xtests/run-xtests.sh
+++ b/xtests/run-xtests.sh
@@ -24,7 +24,9 @@ install -m 644 "${SRCDIR}"/group.conf /etc/security/group.conf
cp /etc/security/limits.conf /etc/security/limits.conf-pam-xtests
install -m 644 "${SRCDIR}"/limits.conf /etc/security/limits.conf
for testname in $XTESTS ; do
- install -m 644 "${SRCDIR}"/$testname.pamd /etc/pam.d/$testname
+ for cfg in "${SRCDIR}"/$testname*.pamd ; do
+ install -m 644 $cfg /etc/pam.d/$(basename $cfg .pamd)
+ done
if test -x "${SRCDIR}"/$testname.sh ; then
"${SRCDIR}"/$testname.sh > /dev/null
else
@@ -42,7 +44,7 @@ for testname in $XTESTS ; do
pass=`expr $pass + 1`
fi
all=`expr $all + 1`
- rm -f /etc/pam.d/$testname
+ rm -f /etc/pam.d/$testname*
done
mv /etc/security/access.conf-pam-xtests /etc/security/access.conf
mv /etc/security/group.conf-pam-xtests /etc/security/group.conf
diff --git a/xtests/tst-pam_access1.c b/xtests/tst-pam_access1.c
index 06b65f0c..457b95d4 100644
--- a/xtests/tst-pam_access1.c
+++ b/xtests/tst-pam_access1.c
@@ -87,7 +87,7 @@ int
main(int argc, char *argv[])
{
pam_handle_t *pamh = NULL;
- const char *user="tstpamaccess";
+ const char *user="tstpamaccess1";
int retval;
int debug = 0;
diff --git a/xtests/tst-pam_access1.sh b/xtests/tst-pam_access1.sh
index 48d8cb3e..180d2563 100755
--- a/xtests/tst-pam_access1.sh
+++ b/xtests/tst-pam_access1.sh
@@ -1,9 +1,9 @@
#!/bin/bash
-/usr/sbin/groupadd -p '!!' tstpamaccess
-/usr/sbin/useradd -G tstpamaccess -p '!!' tstpamaccess
+/usr/sbin/groupadd tstpamaccess
+/usr/sbin/useradd -G tstpamaccess -p '!!' tstpamaccess1
./tst-pam_access1
RET=$?
-/usr/sbin/userdel -r tstpamaccess 2> /dev/null
+/usr/sbin/userdel -r tstpamaccess1 2> /dev/null
/usr/sbin/groupdel tstpamaccess 2> /dev/null
exit $RET
diff --git a/xtests/tst-pam_access2.c b/xtests/tst-pam_access2.c
index 194d07d7..b6e18812 100644
--- a/xtests/tst-pam_access2.c
+++ b/xtests/tst-pam_access2.c
@@ -87,7 +87,7 @@ int
main(int argc, char *argv[])
{
pam_handle_t *pamh = NULL;
- const char *user="tstpamaccess";
+ const char *user="tstpamaccess2";
int retval;
int debug = 0;
diff --git a/xtests/tst-pam_access2.sh b/xtests/tst-pam_access2.sh
index c1b3c992..0a302759 100755
--- a/xtests/tst-pam_access2.sh
+++ b/xtests/tst-pam_access2.sh
@@ -1,9 +1,9 @@
#!/bin/bash
-/usr/sbin/groupadd -p '!!' tstpamaccess
-/usr/sbin/useradd -p '!!' tstpamaccess
+/usr/sbin/groupadd tstpamaccess
+/usr/sbin/useradd -p '!!' tstpamaccess2
./tst-pam_access2
RET=$?
-/usr/sbin/userdel -r tstpamaccess 2> /dev/null
+/usr/sbin/userdel -r tstpamaccess2 2> /dev/null
/usr/sbin/groupdel tstpamaccess 2> /dev/null
exit $RET
diff --git a/xtests/tst-pam_access4.c b/xtests/tst-pam_access4.c
index 1e53a364..acdb46f1 100644
--- a/xtests/tst-pam_access4.c
+++ b/xtests/tst-pam_access4.c
@@ -87,7 +87,7 @@ int
main(int argc, char *argv[])
{
pam_handle_t *pamh = NULL;
- const char *user="tstpamaccess";
+ const char *user="tstpamaccess4";
int retval;
int debug = 0;
diff --git a/xtests/tst-pam_access4.sh b/xtests/tst-pam_access4.sh
index 58bf260d..61e7b448 100755
--- a/xtests/tst-pam_access4.sh
+++ b/xtests/tst-pam_access4.sh
@@ -1,7 +1,7 @@
#!/bin/bash
-/usr/sbin/useradd -p '!!' tstpamaccess
+/usr/sbin/useradd -p '!!' tstpamaccess4
./tst-pam_access4
RET=$?
-/usr/sbin/userdel -r tstpamaccess 2> /dev/null
+/usr/sbin/userdel -r tstpamaccess4 2> /dev/null
exit $RET
diff --git a/xtests/tst-pam_authfail.c b/xtests/tst-pam_authfail.c
new file mode 100644
index 00000000..afdbd6a4
--- /dev/null
+++ b/xtests/tst-pam_authfail.c
@@ -0,0 +1,96 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+
+static struct pam_conv conv = {
+ misc_conv,
+ NULL
+};
+
+
+/* Check that auth stack fails. */
+
+int
+main(int argc, char *argv[])
+{
+ pam_handle_t *pamh=NULL;
+ const char *user="nobody";
+ const char *stack="tst-pam_authfail";
+ int retval;
+ int debug = 0;
+
+ if (argc > 2) {
+ stack = argv[2];
+ }
+
+ if (argc > 1) {
+ if (strcmp (argv[1], "-d") == 0)
+ debug = 1;
+ else
+ stack = argv[1];
+ }
+
+
+ retval = pam_start(stack, user, &conv, &pamh);
+ if (retval != PAM_SUCCESS)
+ {
+ if (debug)
+ fprintf (stderr, "test3: pam_start returned %d\n", retval);
+ return 1;
+ }
+
+ retval = pam_authenticate(pamh, 0);
+ if (retval == PAM_SUCCESS)
+ {
+ if (debug)
+ fprintf (stderr, "test3: pam_authenticate returned %d\n", retval);
+ return 1;
+ }
+
+ retval = pam_end(pamh,retval);
+ if (retval != PAM_SUCCESS)
+ {
+ if (debug)
+ fprintf (stderr, "test3: pam_end returned %d\n", retval);
+ return 1;
+ }
+ return 0;
+}
diff --git a/xtests/tst-pam_authfail.pamd b/xtests/tst-pam_authfail.pamd
new file mode 100644
index 00000000..8ff1a40f
--- /dev/null
+++ b/xtests/tst-pam_authfail.pamd
@@ -0,0 +1,5 @@
+#%PAM-1.0
+# test that successful sufficient module cannot affect stack
+# after failed required module
+auth required pam_debug.so auth=perm_denied
+auth sufficient pam_debug.so auth=success
diff --git a/xtests/tst-pam_authsucceed.c b/xtests/tst-pam_authsucceed.c
new file mode 100644
index 00000000..8666f3f7
--- /dev/null
+++ b/xtests/tst-pam_authsucceed.c
@@ -0,0 +1,96 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+
+static struct pam_conv conv = {
+ misc_conv,
+ NULL
+};
+
+
+/* Check that auth stack succeeds. */
+
+int
+main(int argc, char *argv[])
+{
+ pam_handle_t *pamh=NULL;
+ const char *user="nobody";
+ const char *stack="tst-pam_authsucceed";
+ int retval;
+ int debug = 0;
+
+ if (argc > 2) {
+ stack = argv[2];
+ }
+
+ if (argc > 1) {
+ if (strcmp (argv[1], "-d") == 0)
+ debug = 1;
+ else
+ stack = argv[1];
+ }
+
+
+ retval = pam_start(stack, user, &conv, &pamh);
+ if (retval != PAM_SUCCESS)
+ {
+ if (debug)
+ fprintf (stderr, "test3: pam_start returned %d\n", retval);
+ return 1;
+ }
+
+ retval = pam_authenticate(pamh, 0);
+ if (retval != PAM_SUCCESS)
+ {
+ if (debug)
+ fprintf (stderr, "test3: pam_authenticate returned %d\n", retval);
+ return 1;
+ }
+
+ retval = pam_end(pamh,retval);
+ if (retval != PAM_SUCCESS)
+ {
+ if (debug)
+ fprintf (stderr, "test3: pam_end returned %d\n", retval);
+ return 1;
+ }
+ return 0;
+}
diff --git a/xtests/tst-pam_authsucceed.pamd b/xtests/tst-pam_authsucceed.pamd
new file mode 100644
index 00000000..abaa1eff
--- /dev/null
+++ b/xtests/tst-pam_authsucceed.pamd
@@ -0,0 +1,5 @@
+#%PAM-1.0
+# test that failed sufficient module cannot affect stack
+# with following successful required module
+auth sufficient pam_debug.so auth=auth_err
+auth required pam_debug.so auth=success
diff --git a/xtests/tst-pam_group1.c b/xtests/tst-pam_group1.c
index e4e3ca48..f5a04bc0 100644
--- a/xtests/tst-pam_group1.c
+++ b/xtests/tst-pam_group1.c
@@ -36,10 +36,10 @@
Check the following line in group.conf:
- tst-pam_group1;*;tstpamgrp;Al0000-2400;tstpamgrp
+ tst-pam_group1;*;tstpamgrp;Al0000-2400;tstpamgrpg
- pam_group should add group tstpamgrp to user tstpamgrp, but not
+ pam_group should add group tstpamgrpg to user tstpamgrp, but not
to tstpamgrp2.
*/
@@ -193,7 +193,7 @@ main(int argc, char *argv[])
if (argc > 1 && strcmp (argv[1], "-d") == 0)
debug = 1;
- grp = getgrnam ("tstpamgrp");
+ grp = getgrnam ("tstpamgrpg");
if (grp == NULL)
return 1;
grpid = grp->gr_gid;
diff --git a/xtests/tst-pam_group1.sh b/xtests/tst-pam_group1.sh
index 2d38a6ad..b76377f5 100755
--- a/xtests/tst-pam_group1.sh
+++ b/xtests/tst-pam_group1.sh
@@ -1,11 +1,11 @@
#!/bin/bash
-/usr/sbin/groupadd -p '!!' tstpamgrp
+/usr/sbin/groupadd tstpamgrpg
/usr/sbin/useradd -p '!!' tstpamgrp
/usr/sbin/useradd -p '!!' tstpamgrp2
./tst-pam_group1
RET=$?
/usr/sbin/userdel -r tstpamgrp 2> /dev/null
/usr/sbin/userdel -r tstpamgrp2 2> /dev/null
-/usr/sbin/groupdel tstpamgrp 2> /dev/null
+/usr/sbin/groupdel tstpamgrpg 2> /dev/null
exit $RET
diff --git a/xtests/tst-pam_substack1.pamd b/xtests/tst-pam_substack1.pamd
new file mode 100644
index 00000000..6eab233e
--- /dev/null
+++ b/xtests/tst-pam_substack1.pamd
@@ -0,0 +1,5 @@
+#%PAM-1.0
+# Even if the substack succeeds with sufficient
+# the whole stack should fail.
+auth substack tst-pam_substack1a
+auth required pam_debug.so auth=auth_err
diff --git a/xtests/tst-pam_substack1.sh b/xtests/tst-pam_substack1.sh
new file mode 100755
index 00000000..52601755
--- /dev/null
+++ b/xtests/tst-pam_substack1.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exec ./tst-pam_authfail tst-pam_substack1
diff --git a/xtests/tst-pam_substack1a.pamd b/xtests/tst-pam_substack1a.pamd
new file mode 100644
index 00000000..51c8c8fd
--- /dev/null
+++ b/xtests/tst-pam_substack1a.pamd
@@ -0,0 +1,2 @@
+#%PAM-1.0
+auth sufficient pam_debug.so auth=success
diff --git a/xtests/tst-pam_substack2.pamd b/xtests/tst-pam_substack2.pamd
new file mode 100644
index 00000000..618e2986
--- /dev/null
+++ b/xtests/tst-pam_substack2.pamd
@@ -0,0 +1,6 @@
+#%PAM-1.0
+# Even if the substack fails with requisite
+# the whole stack should succeed.
+auth substack tst-pam_substack2a
+auth [success=reset] pam_permit.so
+auth required pam_debug.so auth=success
diff --git a/xtests/tst-pam_substack2.sh b/xtests/tst-pam_substack2.sh
new file mode 100755
index 00000000..c02f597e
--- /dev/null
+++ b/xtests/tst-pam_substack2.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exec ./tst-pam_authsucceed tst-pam_substack2
diff --git a/xtests/tst-pam_substack2a.pamd b/xtests/tst-pam_substack2a.pamd
new file mode 100644
index 00000000..db853542
--- /dev/null
+++ b/xtests/tst-pam_substack2a.pamd
@@ -0,0 +1,2 @@
+#%PAM-1.0
+auth requisite pam_debug.so auth=auth_err
diff --git a/xtests/tst-pam_substack3.pamd b/xtests/tst-pam_substack3.pamd
new file mode 100644
index 00000000..4fc6016c
--- /dev/null
+++ b/xtests/tst-pam_substack3.pamd
@@ -0,0 +1,5 @@
+#%PAM-1.0
+# Reset in the substack resets to state as of it was
+# in the beginning of substack evaluation
+auth required pam_permit.so
+auth substack tst-pam_substack3a
diff --git a/xtests/tst-pam_substack3.sh b/xtests/tst-pam_substack3.sh
new file mode 100755
index 00000000..0e572aae
--- /dev/null
+++ b/xtests/tst-pam_substack3.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exec ./tst-pam_authsucceed tst-pam_substack3
diff --git a/xtests/tst-pam_substack3a.pamd b/xtests/tst-pam_substack3a.pamd
new file mode 100644
index 00000000..a2ae915c
--- /dev/null
+++ b/xtests/tst-pam_substack3a.pamd
@@ -0,0 +1,3 @@
+#%PAM-1.0
+auth required pam_debug.so auth=auth_err
+auth [success=reset] pam_permit.so
diff --git a/xtests/tst-pam_substack4.pamd b/xtests/tst-pam_substack4.pamd
new file mode 100644
index 00000000..f0017c75
--- /dev/null
+++ b/xtests/tst-pam_substack4.pamd
@@ -0,0 +1,5 @@
+#%PAM-1.0
+# Substack is counted as one module in jumps
+auth [success=1] pam_permit.so
+auth substack tst-pam_substack4a
+auth required pam_permit.so
diff --git a/xtests/tst-pam_substack4.sh b/xtests/tst-pam_substack4.sh
new file mode 100755
index 00000000..a3ef08a7
--- /dev/null
+++ b/xtests/tst-pam_substack4.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exec ./tst-pam_authsucceed tst-pam_substack4
diff --git a/xtests/tst-pam_substack4a.pamd b/xtests/tst-pam_substack4a.pamd
new file mode 100644
index 00000000..3b91c1ba
--- /dev/null
+++ b/xtests/tst-pam_substack4a.pamd
@@ -0,0 +1,4 @@
+#%PAM-1.0
+auth required pam_debug.so auth=auth_err
+auth required pam_debug.so auth=auth_err
+auth required pam_debug.so auth=auth_err
diff --git a/xtests/tst-pam_substack5.pamd b/xtests/tst-pam_substack5.pamd
new file mode 100644
index 00000000..04f07aeb
--- /dev/null
+++ b/xtests/tst-pam_substack5.pamd
@@ -0,0 +1,4 @@
+#%PAM-1.0
+# Requisite terminates substack
+auth required pam_permit.so
+auth substack tst-pam_substack5a
diff --git a/xtests/tst-pam_substack5.sh b/xtests/tst-pam_substack5.sh
new file mode 100755
index 00000000..e2714fda
--- /dev/null
+++ b/xtests/tst-pam_substack5.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exec ./tst-pam_authfail tst-pam_substack5
diff --git a/xtests/tst-pam_substack5a.pamd b/xtests/tst-pam_substack5a.pamd
new file mode 100644
index 00000000..a6850f40
--- /dev/null
+++ b/xtests/tst-pam_substack5a.pamd
@@ -0,0 +1,3 @@
+#%PAM-1.0
+auth requisite pam_debug.so auth=auth_err
+auth [success=reset] pam_permit.so