#include "libpamtest.h"
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
static enum pamtest_err run_test_case(pam_handle_t *ph,
struct pamtest_case *tc)
{
return NULL;
}
-struct pamtest_conv_data {
- const char **conv_input;
- size_t conv_index;
+struct pamtest_conv_ctx {
+ struct pamtest_conv_data *data;
+
+ size_t echo_off_idx;
+ size_t echo_on_idx;
+ size_t err_idx;
+ size_t info_idx;
};
+static int add_to_reply(struct pam_response *reply, const char *str)
+{
+ size_t len;
+
+ len = strlen(str) + 1;
+
+ reply->resp = calloc(len, sizeof(char));
+ if (reply->resp == NULL) {
+ return PAM_BUF_ERR;
+ }
+
+ memcpy(reply->resp, str, len);
+ return PAM_SUCCESS;
+}
+
static int pamtest_simple_conv(int num_msg,
const struct pam_message **msgm,
struct pam_response **response,
void *appdata_ptr)
{
int i;
+ int ret;
struct pam_response *reply;
- const char *password;
- size_t pwlen;
- struct pamtest_conv_data *cdata = \
- (struct pamtest_conv_data *) appdata_ptr;
+ const char *prompt;
+ struct pamtest_conv_ctx *cctx = \
+ (struct pamtest_conv_ctx *) appdata_ptr;
- if (cdata == NULL) {
+ if (cctx == NULL) {
return PAM_CONV_ERR;
}
for (i=0; i < num_msg; i++) {
switch (msgm[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
- password = (const char *) \
- cdata->conv_input[cdata->conv_index];
- if (password == NULL) {
+ prompt = (const char *) \
+ cctx->data->in_echo_off[cctx->echo_off_idx];
+ if (prompt == NULL) {
return PAM_CONV_ERR;
}
- pwlen = strlen(password) + 1;
-
- cdata->conv_index++;
+ ret = add_to_reply(&reply[i], prompt);
+ if (ret != PAM_SUCCESS) {
+ /* FIXME - free data? */
+ return ret;
+ }
- reply[i].resp = calloc(pwlen, sizeof(char));
- if (reply[i].resp == NULL) {
- free(reply);
+ cctx->echo_off_idx++;
+ break;
+ case PAM_PROMPT_ECHO_ON:
+ prompt = (const char *) \
+ cctx->data->in_echo_on[cctx->echo_on_idx];
+ if (prompt == NULL) {
return PAM_CONV_ERR;
}
- memcpy(reply[i].resp, password, pwlen);
+
+ ret = add_to_reply(&reply[i], prompt);
+ if (ret != PAM_SUCCESS) {
+ /* FIXME - free data? */
+ return ret;
+ }
+
+ cctx->echo_on_idx++;
+ break;
+ case PAM_ERROR_MSG:
+ if (cctx->data->out_err != NULL) {
+ memcpy(cctx->data->out_err[cctx->err_idx],
+ msgm[i]->msg,
+ MIN(strlen(msgm[i]->msg),
+ PAM_MAX_MSG_SIZE));
+ cctx->err_idx++;
+ }
+ break;
+ case PAM_TEXT_INFO:
+ if (cctx->data->out_info != NULL) {
+ memcpy(cctx->data->out_info[cctx->info_idx],
+ msgm[i]->msg,
+ MIN(strlen(msgm[i]->msg),
+ PAM_MAX_MSG_SIZE));
+ cctx->info_idx++;
+ }
break;
default:
continue;
}
}
- *response = reply;
+ if (response) {
+ *response = reply;
+ }
return PAM_SUCCESS;
}
enum pamtest_err pamtest(const char *service,
const char *user,
- void *conv_userdata,
+ struct pamtest_conv_data *conv_data,
struct pamtest_case *test_cases)
{
- struct pamtest_conv_data cdata;
+ struct pamtest_conv_ctx cctx;
- cdata.conv_input = conv_userdata;
- cdata.conv_index = 0;
+ cctx.data = conv_data;
+ cctx.echo_on_idx = 0;
+ cctx.echo_off_idx = 0;
+ cctx.err_idx = 0;
+ cctx.info_idx = 0;
return pamtest_ex(service, user,
- pamtest_simple_conv, &cdata,
+ pamtest_simple_conv, &cctx,
test_cases);
}
#define PAM_EXAMPLE_AUTH_DATA "pam_matrix:auth_data"
#define PASSDB_KEY "passdb="
+#define VERBOSE_KEY "verbose"
+
+#define PAM_MATRIX_FLG_VERBOSE (1 << 0)
/* Walks over the key until a colon (:) is find
*/
struct pam_matrix_ctx {
const char *passdb;
+ int flags;
struct pam_lib_items pli;
struct pam_matrix_mod_items pmi;
free(pmi->service);
}
+static int pam_matrix_conv(pam_handle_t *pamh,
+ const int msg_style,
+ const char *msg)
+{
+ int ret;
+ const struct pam_conv *conv;
+ const struct pam_message *mesg[1];
+ struct pam_message *pam_msg;
+
+ ret = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+ if (ret != PAM_SUCCESS) {
+ return ret;
+ }
+
+ pam_msg = malloc(sizeof(struct pam_message));
+ if (pam_msg == NULL) {
+ return PAM_BUF_ERR;
+ }
+
+ pam_msg->msg_style = msg_style;
+ pam_msg->msg = msg;
+
+ mesg[0] = (const struct pam_message *) pam_msg;
+ ret = conv->conv(1, mesg, NULL, conv->appdata_ptr);
+ free(pam_msg);
+ if (ret != PAM_SUCCESS) {
+ return ret;
+ }
+
+ return PAM_SUCCESS;
+}
+
/* Read user password. If both prompts are provided, then ask twice and
* assert that both passwords match.
*
int argc,
const char *argv[])
{
+ pe_ctx->flags = 0;
+
for (; argc-- > 0; ++argv) {
if (strncmp(*argv, PASSDB_KEY, strlen(PASSDB_KEY)) == 0) {
if (*(*argv+strlen(PASSDB_KEY)) == '\0') {
} else {
pe_ctx->passdb = *argv+strlen(PASSDB_KEY);
}
+ } else if (strncmp(*argv, VERBOSE_KEY,
+ strlen(VERBOSE_KEY)) == 0) {
+ pe_ctx->flags |= PAM_MATRIX_FLG_VERBOSE;
}
}
}
return rv;
}
-static int pam_matrix_auth(struct pam_matrix_ctx *pctx)
+static int pam_matrix_auth(pam_handle_t *pamh, struct pam_matrix_ctx *pctx)
{
int rv = PAM_AUTH_ERR;
wipe_authtok(pctx->pli.password);
wipe_authtok(pctx->pmi.password);
+ if (pctx->flags & PAM_MATRIX_FLG_VERBOSE) {
+ if (rv == PAM_SUCCESS) {
+ pam_matrix_conv(pamh,
+ PAM_TEXT_INFO,
+ "Authentication succeeded");
+ } else {
+ pam_matrix_conv(pamh,
+ PAM_ERROR_MSG,
+ "Authentication failed");
+ }
+ }
+
return rv;
}
}
/* Auth and get rid of the authtok */
- rv = pam_matrix_auth(&pctx);
+ rv = pam_matrix_auth(pamh, &pctx);
done:
pam_matrix_free(&pctx);
return rv;
goto done;
}
- rv = pam_matrix_auth(&pctx);
+ rv = pam_matrix_auth(pamh, &pctx);
} else if (flags & PAM_UPDATE_AUTHTOK) {
rv = pam_get_item(pamh,
PAM_OLDAUTHTOK,
#include "libpamtest.h"
+#ifndef ZERO_STRUCT
+#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+#endif
+
struct pwrap_test_ctx {
struct pam_conv conv;
pam_handle_t *ph;
static void test_pam_authenticate(void **state)
{
enum pamtest_err perr;
+ struct pamtest_conv_data conv_data;
const char *testuser_authtoks[] = {
"secret",
NULL,
};
struct pamtest_case tests[] = {
- { PAMTEST_AUTHENTICATE, PAM_SUCCESS, 0, 0 },
- { PAMTEST_SENTINEL, 0, 0, 0 },
+ { PAMTEST_AUTHENTICATE, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
};
(void) state; /* unused */
- perr = pamtest("pwrap_pam", "testuser", testuser_authtoks, tests);
+ ZERO_STRUCT(conv_data);
+ conv_data.in_echo_off = testuser_authtoks;
+
+ perr = pamtest("pwrap_pam", "testuser", &conv_data, tests);
assert_int_equal(perr, PAMTEST_ERR_OK);
}
static void test_pam_authenticate_err(void **state)
{
enum pamtest_err perr;
+ struct pamtest_conv_data conv_data;
const char *testuser_authtoks[] = {
"wrong_password",
NULL,
};
struct pamtest_case tests[] = {
- { PAMTEST_AUTHENTICATE, PAM_AUTH_ERR, 0, 0 },
- { PAMTEST_SENTINEL, 0, 0, 0 },
+ { PAMTEST_AUTHENTICATE, PAM_AUTH_ERR, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
};
(void) state; /* unused */
- perr = pamtest("pwrap_pam", "testuser", testuser_authtoks, tests);
+ ZERO_STRUCT(conv_data);
+ conv_data.in_echo_off = testuser_authtoks;
+
+ perr = pamtest("pwrap_pam", "testuser", &conv_data, tests);
assert_int_equal(perr, PAMTEST_ERR_OK);
}
{
enum pamtest_err perr;
struct pamtest_case tests[] = {
- { PAMTEST_ACCOUNT, PAM_SUCCESS, 0, 0 },
- { PAMTEST_SENTINEL, 0, 0, 0 },
+ { PAMTEST_ACCOUNT, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
};
(void) state; /* unused */
{
enum pamtest_err perr;
struct pamtest_case tests[] = {
- { PAMTEST_ACCOUNT, PAM_PERM_DENIED, 0, 0 },
- { PAMTEST_SENTINEL, 0, 0, 0 },
+ { PAMTEST_ACCOUNT, PAM_PERM_DENIED, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
};
(void) state; /* unused */
enum pamtest_err perr;
const char *v;
struct pamtest_case tests[] = {
- { PAMTEST_OPEN_SESSION, PAM_SUCCESS, 0, 0 },
- { PAMTEST_GETENVLIST, PAM_SUCCESS, 0, 0 },
- { PAMTEST_CLOSE_SESSION, PAM_SUCCESS, 0, 0 },
- { PAMTEST_GETENVLIST, PAM_SUCCESS, 0, 0 },
- { PAMTEST_SENTINEL, 0, 0, 0 },
+ { PAMTEST_OPEN_SESSION, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_GETENVLIST, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_CLOSE_SESSION, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_GETENVLIST, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
};
(void) state; /* unused */
static void test_pam_chauthtok(void **state)
{
enum pamtest_err perr;
+ struct pamtest_conv_data conv_data;
const char *testuser_new_authtoks[] = {
"secret", /* old password */
"new_secret", /* new password */
NULL,
};
struct pamtest_case tests[] = {
- { PAMTEST_CHAUTHTOK, PAM_SUCCESS, 0, 0 },
- { PAMTEST_AUTHENTICATE, PAM_SUCCESS, 0, 0 },
- { PAMTEST_SENTINEL, 0, 0, 0 },
+ { PAMTEST_CHAUTHTOK, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_AUTHENTICATE, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
};
(void) state; /* unused */
- perr = pamtest("pwrap_pam", "testuser", testuser_new_authtoks, tests);
+ ZERO_STRUCT(conv_data);
+ conv_data.in_echo_off = testuser_new_authtoks;
+
+ perr = pamtest("pwrap_pam", "testuser", &conv_data, tests);
assert_int_equal(perr, PAMTEST_ERR_OK);
}
static void test_pam_chauthtok_prelim_failed(void **state)
{
enum pamtest_err perr;
+ struct pamtest_conv_data conv_data;
const char *testuser_new_authtoks[] = {
"wrong_secret", /* old password */
"new_secret", /* new password */
NULL,
};
struct pamtest_case tests[] = {
- { PAMTEST_CHAUTHTOK, PAM_AUTH_ERR, 0, 0 },
- { PAMTEST_SENTINEL, 0, 0, 0 },
+ { PAMTEST_CHAUTHTOK, PAM_AUTH_ERR, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
};
(void) state; /* unused */
- perr = pamtest("pwrap_pam", "testuser", testuser_new_authtoks, tests);
+ ZERO_STRUCT(conv_data);
+ conv_data.in_echo_off = testuser_new_authtoks;
+
+ perr = pamtest("pwrap_pam", "testuser", &conv_data, tests);
assert_int_equal(perr, PAMTEST_ERR_OK);
}
enum pamtest_err perr;
const char *v;
struct pamtest_case tests[] = {
- { PAMTEST_GETENVLIST, PAM_SUCCESS, 0, 0 },
- { PAMTEST_SETCRED, PAM_SUCCESS, 0, 0 },
- { PAMTEST_GETENVLIST, PAM_SUCCESS, 0, 0 },
- { PAMTEST_SENTINEL, 0, 0, 0 },
+ { PAMTEST_GETENVLIST, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_SETCRED, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_GETENVLIST, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
};
(void) state; /* unused */
static void test_pam_authenticate_db_opt(void **state)
{
enum pamtest_err perr;
+ struct pamtest_conv_data conv_data;
+ char auth_info_msg[PAM_MAX_MSG_SIZE] = { '\0' };
+ char *info_arr[] = {
+ auth_info_msg,
+ NULL,
+ };
const char *testuser_authtoks[] = {
"secret_ro",
NULL,
};
struct pamtest_case tests[] = {
- { PAMTEST_AUTHENTICATE, PAM_SUCCESS, 0, 0 },
- { PAMTEST_SENTINEL, 0, 0, 0 },
+ { PAMTEST_AUTHENTICATE, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
};
(void) state; /* unused */
- perr = pamtest("pwrap_pam_opt", "testuser_ro",
- testuser_authtoks, tests);
+ ZERO_STRUCT(conv_data);
+
+ conv_data.in_echo_off = testuser_authtoks;
+ conv_data.out_info = info_arr;
+
+ perr = pamtest("pwrap_pam_opt", "testuser_ro", &conv_data, tests);
assert_int_equal(perr, PAMTEST_ERR_OK);
+
+ assert_string_equal(auth_info_msg, "Authentication succeeded");
+}
+
+static void test_pam_authenticate_db_opt_err(void **state)
+{
+ enum pamtest_err perr;
+ struct pamtest_conv_data conv_data;
+ char auth_err_msg[PAM_MAX_MSG_SIZE] = { '\0' };
+ char *err_arr[] = {
+ auth_err_msg,
+ NULL,
+ };
+ const char *testuser_authtoks[] = {
+ "wrong_secret",
+ NULL,
+ };
+ struct pamtest_case tests[] = {
+ { PAMTEST_AUTHENTICATE, PAM_AUTH_ERR, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
+ };
+
+ (void) state; /* unused */
+
+ ZERO_STRUCT(conv_data);
+ conv_data.in_echo_off = testuser_authtoks;
+ conv_data.out_err = err_arr;
+
+ perr = pamtest("pwrap_pam_opt", "testuser_ro", &conv_data, tests);
+ assert_int_equal(perr, PAMTEST_ERR_OK);
+
+ assert_string_equal(auth_err_msg, "Authentication failed");
}
static void test_pam_vsyslog(void **state)
const char *svc;
enum pamtest_err perr;
struct pamtest_case tests[] = {
- { PAMTEST_OPEN_SESSION, PAM_SUCCESS, 0, 0 },
- { PAMTEST_GETENVLIST, PAM_SUCCESS, 0, 0 },
- { PAMTEST_SENTINEL, 0, 0, 0 },
+ { PAMTEST_OPEN_SESSION, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_GETENVLIST, PAM_SUCCESS, PAMTEST_CASE_INIT },
+ { PAMTEST_CASE_SENTINEL },
};
(void) state; /* unused */
cmocka_unit_test_setup_teardown(test_pam_authenticate_db_opt,
setup_ctx_only,
teardown_simple),
+ cmocka_unit_test_setup_teardown(test_pam_authenticate_db_opt_err,
+ setup_ctx_only,
+ teardown_simple),
cmocka_unit_test_setup_teardown(test_pam_vsyslog,
setup_noconv,
teardown),