pamtest: Capture info and error messages from conversation
authorJakub Hrozek <jakub.hrozek@posteo.se>
Sat, 24 Oct 2015 15:43:30 +0000 (17:43 +0200)
committerAndreas Schneider <asn@samba.org>
Thu, 10 Dec 2015 12:31:19 +0000 (13:31 +0100)
include/libpamtest.h
src/libpamtest.c
src/modules/pam_matrix.c
tests/services/pwrap_pam_opt.in
tests/test_pam_wrapper.c

index b6de8139d19968b60a86387d31f9852eef3179a3..42edfe0e971068928ee0591c7cd9065d7ffd3820 100644 (file)
@@ -60,6 +60,9 @@ struct pamtest_case {
        } case_out;             /* depends on pam_operation, mostly unused */
 };
 
+#define PAMTEST_CASE_INIT 0, 0, { .envlist = NULL }
+#define PAMTEST_CASE_SENTINEL PAMTEST_SENTINEL, 0, PAMTEST_CASE_INIT
+
 enum pamtest_err {
        PAMTEST_ERR_OK,         /* Testcases returns correspond with input */
        PAMTEST_ERR_START,      /* pam_start() failed */
@@ -85,9 +88,17 @@ void pamtest_free_env(char **envlist);
 
 const struct pamtest_case *pamtest_failed_case(struct pamtest_case *test_cases);
 
+struct pamtest_conv_data {
+       const char **in_echo_off;
+       const char **in_echo_on;
+
+       char **out_err;
+       char **out_info;
+};
+
 enum pamtest_err pamtest(const char *service,
                         const char *user,
-                        void *conv_userdata,
+                        struct pamtest_conv_data *conv_data,
                         struct pamtest_case *test_cases);
 
 #endif /* __LIBPAMTEST_H_ */
index 1990a18858757cb5c80e0d4895dcabc43f869504..20a2b28c704be65649a7924e4ad1f0594fba338f 100644 (file)
@@ -22,6 +22,8 @@
 
 #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)
 {
@@ -144,24 +146,43 @@ const struct pamtest_case *pamtest_failed_case(struct pamtest_case *test_cases)
        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;
        }
 
@@ -174,43 +195,78 @@ static int pamtest_simple_conv(int num_msg,
        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);
 }
index 54705f84c74bd2442771fa6b18b6a6f27945d190..fb8c34823b32d3763dc156111d102d6c2a02fef4 100644 (file)
@@ -22,6 +22,9 @@
 #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
  */
@@ -60,6 +63,7 @@ struct pam_matrix_mod_items {
 
 struct pam_matrix_ctx {
        const char *passdb;
+       int flags;
 
        struct pam_lib_items pli;
        struct pam_matrix_mod_items pmi;
@@ -243,6 +247,38 @@ static void pam_matrix_mod_items_free(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.
  *
@@ -344,6 +380,8 @@ static void eval_args(struct pam_matrix_ctx *pe_ctx,
                      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') {
@@ -351,6 +389,9 @@ static void eval_args(struct pam_matrix_ctx *pe_ctx,
                        } 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;
                }
        }
 }
@@ -409,7 +450,7 @@ static int _pam_matrix_auth(struct pam_matrix_ctx *pctx)
        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;
 
@@ -418,6 +459,18 @@ static int pam_matrix_auth(struct pam_matrix_ctx *pctx)
        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;
 }
 
@@ -446,7 +499,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags,
        }
 
        /* 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;
@@ -640,7 +693,7 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags,
                        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,
index 0fff1296d73e8ff65daaba2107e79abdeeeac461..08b58b3cf4fcbc4813a66d218516628b3c125623 100644 (file)
@@ -1 +1 @@
-auth           required        @CMAKE_CURRENT_BINARY_DIR@/../src/pam_matrix.so passdb=@CMAKE_CURRENT_BINARY_DIR@/passdb_ro
+auth           required        @CMAKE_CURRENT_BINARY_DIR@/../src/pam_matrix.so passdb=@CMAKE_CURRENT_BINARY_DIR@/passdb_ro     verbose
index 69d95b62d137ba73656df647b14c98e82c1f02ae..36a68b9952e916528d989e9e72f34983d8648b2e 100644 (file)
 
 #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;
@@ -214,36 +218,44 @@ static int teardown(void **state)
 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);
 }
 
@@ -251,8 +263,8 @@ static void test_pam_acct(void **state)
 {
        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 */
@@ -265,8 +277,8 @@ static void test_pam_acct_err(void **state)
 {
        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 */
@@ -359,11 +371,11 @@ static void test_pam_session(void **state)
        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 */
@@ -386,6 +398,7 @@ static void test_pam_session(void **state)
 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 */
@@ -394,20 +407,24 @@ static void test_pam_chauthtok(void **state)
                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 */
@@ -415,13 +432,16 @@ static void test_pam_chauthtok_prelim_failed(void **state)
                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);
 }
 
@@ -430,10 +450,10 @@ static void test_pam_setcred(void **state)
        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 */
@@ -590,20 +610,62 @@ static void test_pam_strerror(void **state)
 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)
@@ -633,9 +695,9 @@ static void test_get_set(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 */
@@ -718,6 +780,9 @@ int main(void) {
                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),