]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: test-mech - Rewrote test
authorAki Tuomi <aki.tuomi@open-xchange.com>
Thu, 7 May 2020 10:57:14 +0000 (13:57 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Mon, 11 May 2020 08:44:30 +0000 (11:44 +0300)
src/auth/test-mech.c

index 6921d2135b8a8a8d552bbaf84013534a99fedb19..487fa52c42718a9ca2ce320d5889b2adccbc6d3c 100644 (file)
 #include "otp.h"
 #include "mech-otp-skey-common.h"
 #include "settings-parser.h"
+#include "password-scheme.h"
 #include "test-common.h"
+#include "test-auth.h"
+#include "auth-token.h"
 
 #include <unistd.h>
 #include <time.h>
@@ -33,32 +36,50 @@ extern const struct mech_module mech_scram_sha256;
 extern const struct mech_module mech_xoauth2;
 
 static struct auth_settings set;
-static const char *challenge = NULL;
+static struct mechanisms_register *mech_reg;
+
+struct test_case {
+       const struct mech_module *mech;
+       const unsigned char *in;
+       size_t len;
+       const char *username;
+       const char *expect_error;
+       bool success;
+       bool set_username_before_test;
+       bool set_cert_username;
+};
 
 static void
-verify_plain_continue_mock_callback(struct auth_request *request ATTR_UNUSED,
-                                   verify_plain_callback_t *callback ATTR_UNUSED)
+verify_plain_continue_mock_callback(struct auth_request *request,
+                                   verify_plain_callback_t *callback)
 {
-       request->failed = FALSE;
+       request->passdb_success = TRUE;
+       callback(PASSDB_RESULT_OK, request);
 }
 
 static void
-request_handler_reply_mock_callback(struct auth_request *request ATTR_UNUSED,
-                                   enum auth_client_result result ATTR_UNUSED,
+request_handler_reply_mock_callback(struct auth_request *request,
+                                   enum auth_client_result result,
                                    const void *auth_reply ATTR_UNUSED,
                                    size_t reply_size ATTR_UNUSED)
 {
-       if (request->user != NULL && request->mech != &mech_external) {
+       request->failed = result != AUTH_CLIENT_RESULT_SUCCESS;
+
+       if (request->passdb_result == PASSDB_RESULT_OK)
                request->failed = FALSE;
+       else if (request->mech == &mech_otp) {
+               if (null_strcmp(request->user, "otp_phase_2") == 0)
+                       request->failed = FALSE;
+       } else if (request->mech == &mech_oauthbearer) {
        }
 };
 
 static void
-request_handler_reply_continue_mock_callback(struct auth_request *request ATTR_UNUSED,
-                                            const void *reply ATTR_UNUSED,
-                                            size_t reply_size ATTR_UNUSED)
+request_handler_reply_continue_mock_callback(struct auth_request *request,
+                                            const void *reply,
+                                            size_t reply_size)
 {
-       challenge = p_strndup(request->pool, reply, reply_size);
+       request->context = p_strndup(request->pool, reply, reply_size);
 }
 
 static void
@@ -69,14 +90,26 @@ auth_client_request_mock_callback(const char *reply ATTR_UNUSED,
 
 static void test_mechs_init(void)
 {
+       const char *const services[] = {NULL};
        process_start_time = time(NULL);
 
        /* Copy default settings */
        set = *(struct auth_settings *) auth_setting_parser_info.defaults;
        global_auth_settings = &set;
+       global_auth_settings->base_dir = ".";
        memset((&set)->username_chars_map, 1, sizeof((&set)->username_chars_map));
        set.username_format = "";
 
+       t_array_init(&set.passdbs, 2);
+       struct auth_passdb_settings *mock_set = t_new(struct auth_passdb_settings, 1);
+       *mock_set = mock_passdb_set;
+       array_push_back(&set.passdbs, &mock_set);
+       mock_set = t_new(struct auth_passdb_settings, 1);
+       *mock_set = mock_passdb_set;
+       mock_set->master = TRUE;
+       array_push_back(&set.passdbs, &mock_set);
+       t_array_init(&set.userdbs, 1);
+
        /* Disable stats */
        set.stats = FALSE;
 
@@ -85,31 +118,48 @@ static void test_mechs_init(void)
        /* For tests of mech-anonymous. */
        set.anonymous_username = "anonuser";
 
-       i_array_init(&auths, 1);
-       struct auth *auth = t_new(struct auth, 1);
-       array_push_back(&auths, &auth);
+       mech_init(global_auth_settings);
+       mech_reg = mech_register_init(global_auth_settings);
+       passdbs_init();
+       userdbs_init();
+       passdb_mock_mod_init();
+       password_schemes_init();
+
+       auths_preinit(&set, pool_datastack_create(), mech_reg, services);
+       auths_init();
+       auth_token_init();
 }
 
-static void test_mech_prepare_request(struct auth_request *request,
+static void test_mech_prepare_request(struct auth_request **request_r,
+                                     const struct mech_module *mech,
                                      struct auth_request_handler *handler,
                                      unsigned int running_test,
-                                     const char *username)
+                                     const struct test_case *test_case)
 {
+       global_auth_settings->ssl_username_from_cert = test_case->set_cert_username;
+       struct auth *auth = auth_default_service();
+
+       struct auth_request *request = auth_request_new(mech,  NULL);
        request->handler = handler;
        request->id = running_test+1;
        request->mech_password = NULL;
        request->state = AUTH_REQUEST_STATE_NEW;
        request->set = global_auth_settings;
        request->connect_uid = running_test;
+       request->passdb = auth->passdbs;
+       request->userdb = auth->userdbs;
        handler->refcount = 1;
 
        auth_fields_add(request->extra_fields, "nodelay", "", 0);
        auth_request_ref(request);
        auth_request_state_count[AUTH_REQUEST_STATE_NEW] = 1;
-       challenge = NULL;
 
-       if (username != NULL)
-               request->user = p_strdup(request->pool, username);
+       if (test_case->set_username_before_test || test_case->set_cert_username)
+               request->user = p_strdup(request->pool, test_case->username);
+       if (test_case->set_cert_username)
+               request->cert_username = TRUE;
+
+       *request_r = request;
 }
 
 static void test_mech_handle_challenge(struct auth_request *request,
@@ -118,6 +168,9 @@ static void test_mech_handle_challenge(struct auth_request *request,
                                       unsigned int running_test,
                                       bool expected_success)
 {
+       string_t *out = t_str_new(16);
+       str_append_data(out, in, in_len);
+       const char *challenge = request->context;
        if (request->mech == &mech_login) {
                /* We do not care about any specific password just give
                 * the username input as password also in case it's wanted. */
@@ -125,12 +178,15 @@ static void test_mech_handle_challenge(struct auth_request *request,
                        test_assert_strcmp_idx(challenge, "Password:", running_test);
                else
                        test_assert_strcmp_idx(challenge, "Username:", running_test);
+       } else if (request->mech == &mech_cram_md5 && *in != '\0') {
+               str_truncate(out, 0);
+               str_append(out, "testuser b913a602c7eda7a495b4e6e7334d3890");
        } else if (request->mech == &mech_digest_md5) {
                struct digest_auth_request *digest_request =
                        (struct digest_auth_request *) request;
                digest_request->nonce = "OA6MG9tEQGm2hh";
        }
-       auth_request_continue(request, in, in_len);
+       auth_request_continue(request, out->data, out->used);
 }
 
 static inline const unsigned char *
@@ -154,119 +210,116 @@ static void test_mechs(void)
                .verify_plain_continue_callback = verify_plain_continue_mock_callback,
        };
 
-       static struct {
-               const struct mech_module *mech;
-               const unsigned char *in;
-               size_t len;
-               const char *username;
-               const char *expect_error;
-               bool success;
-               bool set_username;
-       } tests[] = {
+       static struct test_case tests[] = {
                /* Expected to be successful */
-               {&mech_anonymous, UCHAR_LEN("\0any \0 bad \0 content"), "anonuser", NULL, TRUE, FALSE},
-               {&mech_apop, NULL, 0, "testuser", "All password databases were skipped", TRUE, FALSE},
-               {&mech_cram_md5, UCHAR_LEN("testuser mockresponse"), "testuser", "All password databases were skipped", TRUE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("username=\"testuser@example.com\",realm=\"example.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"OA6MHXh6VqTrRk\",nc=00000001,digest-uriresponse=d388dad90d4bbd760a152321f2143af7,qop=\"auth\""), "testuser@example.com", "All password databases were skipped",TRUE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("username=\"testuser@example.com\",realm=\"example.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"OA6MHXh6VqTrRk\",nc=00000001,digest-uriresponse=d388dad90d4bbd760a152321f2143af7,qop=\"auth\",authzid=\"masteruser\""), "testuser@example.com", NULL, TRUE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("username=\"test\xc3\xbaser@example.com\",realm=\"example.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"OA6MHXh6VqTrRk\",nc=00000001,digest-uriresponse=d388dad90d4bbd760a152321f2143af7,qop=\"auth\",authzid=\"masteruser\""), "test\xc3\xbaser@example.com", NULL, TRUE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("username=\"test\xc3\xbaser@example.com\",realm=\"example.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"OA6MHXh6VqTrRk\",charset=\"utf-8\",cipher=unsupported,nc=00000001,digest-uri=imap/server.com,response=d388dad90d4bbd760a152321f2143af7,qop=\"auth\",authzid=\"masteruser\""), "test\xc3\xbaser@example.com", NULL, TRUE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("username=\"testuser\",realm=\"example.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"OA6MHXh6VqTrRk\",charset=\"utf-8\",cipher=unsupported,nc=00000001,digest-uri=imap/server.com,response=d388dad90d4bbd760a152321f2143af7,qop=\"auth\",authzid=\"masteruser\""), "testuser@example.com", NULL, TRUE, FALSE},
-               {&mech_external, UCHAR_LEN(""), "testuser", NULL, TRUE, TRUE},
-               {&mech_dovecot_token, UCHAR_LEN("service\0pid\0testuser\0session_id\0deadbeef"), "testuser", NULL, TRUE, FALSE},
-               {&mech_login, UCHAR_LEN("testuser"), "testuser", NULL, TRUE, FALSE},
-               {&mech_plain, UCHAR_LEN("\0testuser\0testpass"), "testuser", NULL, TRUE, FALSE},
-               {&mech_plain, UCHAR_LEN("normaluser\0masteruser\0masterpass"), "masteruser", NULL, TRUE, FALSE},
-               {&mech_plain, UCHAR_LEN("normaluser\0normaluser\0masterpass"), "normaluser", NULL, TRUE, FALSE},
-               {&mech_otp, UCHAR_LEN("somebody\0testuser"), "testuser", "All password databases were skipped", TRUE, FALSE},
-               {&mech_otp, UCHAR_LEN("hex:5Bf0 75d9 959d 036f"), "otp_phase_2", NULL, TRUE, TRUE},
-               {&mech_otp, UCHAR_LEN("word:BOND FOGY DRAB NE RISE MART"), "otp_phase_2", NULL, TRUE, TRUE},
-               {&mech_otp, UCHAR_LEN("init-hex:f6bd 6b33 89b8 7203:md5 499 ke6118:23d1 b253 5ae0 2b7e"), "otp_phase_2", NULL, TRUE, TRUE},
-               {&mech_otp, UCHAR_LEN("init-word:END KERN BALM NICK EROS WAVY:md5 499 ke1235:BABY FAIN OILY NIL TIDY DADE"), "otp_phase2", NULL , TRUE, TRUE},
-               {&mech_oauthbearer, UCHAR_LEN("n,a=testuser,p=cHJvb2Y=,f=nonstandart\x01host=server\x01port=143\x01""auth=Bearer vF9dft4qmTc2Nvb3RlckBhbHRhdmlzdGEuY29tCg==\x01\x01"), "testuser", NULL, TRUE, FALSE},
-               {&mech_scram_sha1, UCHAR_LEN("n,,n=testuser,r=rOprNGfwEbeRWgbNEkqO"), "testuser", "All password databases were skipped", TRUE, FALSE},
-               {&mech_scram_sha256, UCHAR_LEN("n,,n=testuser,r=rOprNGfwEbeRWgbNEkqO"), "testuser",  "All password databases were skipped", TRUE, FALSE},
-               {&mech_xoauth2, UCHAR_LEN("user=testuser\x01""auth=Bearer vF9dft4qmTc2Nvb3RlckBhdHRhdmlzdGEuY29tCg==\x01\x01"), "testuser", NULL, TRUE, FALSE},
+               {&mech_anonymous, UCHAR_LEN("\0any \0 bad \0 content"), "anonuser", NULL, TRUE, FALSE, FALSE},
+               {&mech_apop, NULL, 0, "testuser", NULL, TRUE, FALSE, FALSE},
+               {&mech_cram_md5, UCHAR_LEN("testuser b913a602c7eda7a495b4e6e7334d3890"), "testuser", NULL, TRUE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("username=\"testuser@example.com\",realm=\"example.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"OA6MHXh6VqTrRk\",nc=00000001,digest-uriresponse=d388dad90d4bbd760a152321f2143af7,qop=\"auth\""), "testuser@example.com", NULL,TRUE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("username=\"testuser@example.com\",realm=\"example.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"OA6MHXh6VqTrRk\",nc=00000001,digest-uriresponse=d388dad90d4bbd760a152321f2143af7,qop=\"auth\",authzid=\"masteruser\""), "testuser@example.com", NULL, TRUE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("username=\"test\xc3\xbaser@example.com\",realm=\"example.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"OA6MHXh6VqTrRk\",nc=00000001,digest-uriresponse=d388dad90d4bbd760a152321f2143af7,qop=\"auth\",authzid=\"masteruser\""), "test\xc3\xbaser@example.com", NULL, TRUE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("username=\"test\xc3\xbaser@example.com\",realm=\"example.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"OA6MHXh6VqTrRk\",charset=\"utf-8\",cipher=unsupported,nc=00000001,digest-uri=imap/server.com,response=d388dad90d4bbd760a152321f2143af7,qop=\"auth\",authzid=\"masteruser\""), "test\xc3\xbaser@example.com", NULL, TRUE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("username=\"testuser\",realm=\"example.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"OA6MHXh6VqTrRk\",charset=\"utf-8\",cipher=unsupported,nc=00000001,digest-uri=imap/server.com,response=d388dad90d4bbd760a152321f2143af7,qop=\"auth\",authzid=\"masteruser\""), "testuser@example.com", NULL, TRUE, FALSE, FALSE},
+               {&mech_external, UCHAR_LEN(""), "testuser", NULL, TRUE, TRUE, TRUE},
+               {&mech_dovecot_token, NULL, 0, "testuser", NULL, TRUE, FALSE, FALSE},
+               {&mech_login, UCHAR_LEN("testuser"), "testuser", NULL, TRUE, FALSE, FALSE},
+               {&mech_plain, UCHAR_LEN("\0testuser\0testpass"), "testuser", NULL, TRUE, FALSE, FALSE},
+               {&mech_plain, UCHAR_LEN("normaluser\0masteruser\0masterpass"), "masteruser", NULL, TRUE, FALSE, FALSE},
+               {&mech_plain, UCHAR_LEN("normaluser\0normaluser\0masterpass"), "normaluser", NULL, TRUE, FALSE, FALSE},
+               {&mech_otp, UCHAR_LEN("hex:5Bf0 75d9 959d 036f"), "otp_phase_2", NULL, TRUE, TRUE, FALSE},
+               {&mech_otp, UCHAR_LEN("word:BOND FOGY DRAB NE RISE MART"), "otp_phase_2", NULL, TRUE, TRUE, FALSE},
+               {&mech_otp, UCHAR_LEN("init-hex:f6bd 6b33 89b8 7203:md5 499 ke6118:23d1 b253 5ae0 2b7e"), "otp_phase_2", NULL, TRUE, TRUE, FALSE},
+               {&mech_otp, UCHAR_LEN("init-word:END KERN BALM NICK EROS WAVY:md5 499 ke1235:BABY FAIN OILY NIL TIDY DADE"), "otp_phase_2", NULL , TRUE, TRUE, FALSE},
+               {&mech_oauthbearer, UCHAR_LEN("n,a=testuser,p=cHJvb2Y=,f=nonstandart\x01host=server\x01port=143\x01""auth=Bearer vF9dft4qmTc2Nvb3RlckBhbHRhdmlzdGEuY29tCg==\x01\x01"), "testuser", NULL, FALSE, TRUE, FALSE},
+               {&mech_scram_sha1, UCHAR_LEN("n,,n=testuser,r=rOprNGfwEbeRWgbNEkqO"), "testuser", NULL, TRUE, FALSE, FALSE},
+               {&mech_scram_sha256, UCHAR_LEN("n,,n=testuser,r=rOprNGfwEbeRWgbNEkqO"), "testuser",  NULL, TRUE, FALSE, FALSE},
+               {&mech_xoauth2, UCHAR_LEN("user=testuser\x01""auth=Bearer vF9dft4qmTc2Nvb3RlckBhdHRhdmlzdGEuY29tCg==\x01\x01"), "testuser", NULL, TRUE, FALSE, FALSE},
 
                /* Below tests are expected to fail */
                /* Empty input tests*/
-               {&mech_apop, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
-               {&mech_cram_md5, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
-               {&mech_dovecot_token, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
-               {&mech_external, UCHAR_LEN(""), "testuser", NULL, FALSE, TRUE},
-               {&mech_external, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
-               {&mech_login, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
-               {&mech_otp, UCHAR_LEN(""), NULL, "invalid input", FALSE, FALSE},
-               {&mech_otp, UCHAR_LEN(""), "testuser", "unsupported response type", FALSE, TRUE},
-               {&mech_plain, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
-               {&mech_oauthbearer, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
-               {&mech_xoauth2, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
-               {&mech_scram_sha1, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
-               {&mech_scram_sha256, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE},
+               {&mech_apop, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_cram_md5, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_dovecot_token, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_external, UCHAR_LEN(""), "testuser", NULL, FALSE, TRUE, FALSE},
+               {&mech_external, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_login, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_otp, UCHAR_LEN(""), NULL, "invalid input", FALSE, FALSE, FALSE},
+               {&mech_otp, UCHAR_LEN(""), "testuser", "invalid input", FALSE, FALSE, FALSE},
+               {&mech_plain, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_oauthbearer, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_xoauth2, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_scram_sha1, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_scram_sha256, UCHAR_LEN(""), NULL, NULL, FALSE, FALSE, FALSE},
 
                /* Bad input tests*/
-               {&mech_apop, UCHAR_LEN("1.1.1\0test\0user\0response"), NULL, NULL, FALSE, FALSE},
-               {&mech_apop, UCHAR_LEN("1.1.1\0testuser\0tooshort"), NULL, NULL, FALSE, FALSE},
-               {&mech_apop, UCHAR_LEN("1.1.1\0testuser\0responseoflen16-"), NULL, NULL, FALSE, FALSE},
-               {&mech_apop, UCHAR_LEN("1.1.1"), NULL, NULL, FALSE, FALSE},
-               {&mech_cram_md5, UCHAR_LEN("testuser\0response"), NULL, NULL, FALSE, FALSE},
+               {&mech_apop, UCHAR_LEN("1.1.1\0test\0user\0response"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_apop, UCHAR_LEN("1.1.1\0testuser\0tooshort"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_apop, UCHAR_LEN("1.1.1\0testuser\0responseoflen16-"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_apop, UCHAR_LEN("1.1.1"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_otp, UCHAR_LEN("somebody\0testuser"), "testuser", "otp(testuser): unsupported response type", FALSE, TRUE, FALSE},
+               {&mech_cram_md5, UCHAR_LEN("testuser\0response"), "testuser", NULL, FALSE, FALSE, FALSE},
 
                /* Covering most of the digest md5 parsing */
-               {&mech_digest_md5, UCHAR_LEN("username=\"testuser@example.com\",realm=\"example.com\",cnonce=\"OA6MHXh6VqTrRk\",response=d388dad90d4bbd760a152321f2143af7,qop=\"auth\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("realm=\"example.com\",cnonce=\"OA6MHXh6VqTrRk\",nonce=\"OA6MG9tEQGm2hh\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("username=\"testuser@example.com\",realm=\"example.com\", nonce=\"OA6MG9tEQGm2hh\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("qop=\"auth-int\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("qop=\"auth-int\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("qop=\"auth-conf\",\"cipher=rc4\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("cnonce=\"OA6MHXh6VqTrRk\",cnonce=\"OA6MHXh6VqTrRk\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("cnonce=\"\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("nonce=\"not matching\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("nc=00000001,nc=00000002"), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("nc=NAN"), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("nc=00000002"), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("cipher=unsupported"), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("digest-uri="), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("username=\"\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("username=\"a\",username=\"b\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("response=broken"), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("maxbuf=32,maxbuf=1024"), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("maxbuf=broken"), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("authzid=\"somebody\",authzid=\"else\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("authzid=\"\""), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("charset=unsupported"), NULL, NULL, FALSE, FALSE},
-               {&mech_digest_md5, UCHAR_LEN("qop=unsupported"), NULL, NULL, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("username=\"testuser@example.com\",realm=\"example.com\",cnonce=\"OA6MHXh6VqTrRk\",response=d388dad90d4bbd760a152321f2143af7,qop=\"auth\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("realm=\"example.com\",cnonce=\"OA6MHXh6VqTrRk\",nonce=\"OA6MG9tEQGm2hh\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("username=\"testuser@example.com\",realm=\"example.com\", nonce=\"OA6MG9tEQGm2hh\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("qop=\"auth-int\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("qop=\"auth-int\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("qop=\"auth-conf\",\"cipher=rc4\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("cnonce=\"OA6MHXh6VqTrRk\",cnonce=\"OA6MHXh6VqTrRk\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("cnonce=\"\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("nonce=\"not matching\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("nc=00000001,nc=00000002"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("nc=NAN"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("nc=00000002"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("cipher=unsupported"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("digest-uri="), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("username=\"\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("username=\"a\",username=\"b\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("response=broken"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("maxbuf=32,maxbuf=1024"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("maxbuf=broken"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("authzid=\"somebody\",authzid=\"else\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("authzid=\"\""), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("charset=unsupported"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_digest_md5, UCHAR_LEN("qop=unsupported"), NULL, NULL, FALSE, FALSE, FALSE},
 
                /* Too much nuls */
-               {&mech_dovecot_token, UCHAR_LEN("service\0pid\0fail\0se\0ssion_id\0deadbeef"), NULL , NULL, FALSE, FALSE},
-               {&mech_login, UCHAR_LEN("test user\0user"), NULL, NULL, TRUE, FALSE},
-               {&mech_oauthbearer, UCHAR_LEN("n,a==testuser,\x01""auth=Bearer token\x01\x01"), NULL, NULL, FALSE, FALSE},
-               {&mech_oauthbearer, UCHAR_LEN("n,a=testuser,f=non-standard\x01""auth=Bearer token\x01\x01"), "testuser", NULL, FALSE, FALSE},
-               {&mech_oauthbearer, UCHAR_LEN("n,a=testuser\x01""auth=token\x01\x01"), "testuser", NULL, FALSE, FALSE},
-               {&mech_xoauth2, UCHAR_LEN("testuser\x01auth=Bearer token\x01\x01"), NULL, NULL, FALSE, FALSE},
+               {&mech_dovecot_token, UCHAR_LEN("service\0pid\0fail\0se\0ssion_id\0deadbeef"), NULL , NULL, FALSE, FALSE, FALSE},
+               {&mech_login, UCHAR_LEN("test user\0user"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_oauthbearer, UCHAR_LEN("n,a==testuser,\x01""auth=Bearer token\x01\x01"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_oauthbearer, UCHAR_LEN("n,a=testuser,f=non-standard\x01""auth=Bearer token\x01\x01"), "testuser", NULL, FALSE, FALSE, FALSE},
+               {&mech_oauthbearer, UCHAR_LEN("n,a=testuser\x01""auth=token\x01\x01"), "testuser", NULL, FALSE, FALSE, FALSE},
+               {&mech_xoauth2, UCHAR_LEN("testuser\x01auth=Bearer token\x01\x01"), NULL, NULL, FALSE, FALSE, FALSE},
                /* does not start with [B|b]earer */
-               {&mech_xoauth2, UCHAR_LEN("user=testuser\x01""auth=token\x01\x01"), "testuser", NULL, FALSE, FALSE},
+               {&mech_xoauth2, UCHAR_LEN("user=testuser\x01""auth=token\x01\x01"), "testuser", NULL, FALSE, FALSE, FALSE},
                /* Too much nuls */
-               {&mech_plain, UCHAR_LEN("\0fa\0il\0ing\0withthis"), NULL, NULL, FALSE, FALSE},
-               {&mech_plain, UCHAR_LEN("failingwiththis"), NULL, NULL, FALSE, FALSE},
-               {&mech_plain, UCHAR_LEN("failing\0withthis"), NULL, NULL, FALSE, FALSE},
-               {&mech_otp, UCHAR_LEN("someb\0ody\0testuser"), NULL, "invalid input", FALSE, FALSE},
+               {&mech_plain, UCHAR_LEN("\0fa\0il\0ing\0withthis"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_plain, UCHAR_LEN("failingwiththis"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_plain, UCHAR_LEN("failing\0withthis"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_otp, UCHAR_LEN("someb\0ody\0testuser"), NULL, "invalid input", FALSE, FALSE, FALSE},
                /* phase 2 */
-               {&mech_otp, UCHAR_LEN("someb\0ody\0testuser"), "testuser", NULL, FALSE, TRUE},
-               {&mech_scram_sha1, UCHAR_LEN("c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts="), NULL, NULL, FALSE, FALSE},
-               {&mech_scram_sha1, UCHAR_LEN("iws0X8v3Bz2T0CJGbJQyF0X+HI4Ts=,,,,"), NULL, NULL, FALSE, FALSE},
-               {&mech_scram_sha1, UCHAR_LEN("n,a=masteruser,,"), NULL, NULL, FALSE, FALSE},
-               {&mech_scram_sha1, UCHAR_LEN("n,a==masteruser,,"), NULL, NULL, FALSE, FALSE},
-               {&mech_scram_sha1, UCHAR_LEN("n,,m=testuser,,"), NULL, NULL, FALSE, FALSE},
-               {&mech_scram_sha1, UCHAR_LEN("broken\0input"), NULL, NULL, FALSE, FALSE},
-               {&mech_scram_sha256, UCHAR_LEN("broken\0input"), NULL, NULL, FALSE, FALSE},
+               {&mech_otp, UCHAR_LEN("someb\0ody\0testuser"), "testuser", "otp(testuser): unsupported response type", FALSE, TRUE, FALSE},
+               {&mech_scram_sha1, UCHAR_LEN("c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts="), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_scram_sha1, UCHAR_LEN("iws0X8v3Bz2T0CJGbJQyF0X+HI4Ts=,,,,"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_scram_sha1, UCHAR_LEN("n,a=masteruser,,"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_scram_sha1, UCHAR_LEN("n,a==masteruser,,"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_scram_sha1, UCHAR_LEN("n,,m=testuser,,"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_scram_sha1, UCHAR_LEN("broken\0input"), NULL, NULL, FALSE, FALSE, FALSE},
+               {&mech_scram_sha256, UCHAR_LEN("broken\0input"), NULL, NULL, FALSE, FALSE, FALSE},
        };
 
        test_mechs_init();
 
+       string_t *d_token = t_str_new(32);
+       str_append_data(d_token, UCHAR_LEN("service\0pid\0testuser\0session\0"));
+       str_append(d_token, auth_token_get("service","pid","testuser","session"));
+
        for (unsigned int running_test = 0; running_test < N_ELEMENTS(tests);
             running_test++) T_BEGIN {
-               const struct mech_module *mech = tests[running_test].mech;
+               struct test_case *test_case = &tests[running_test];
+               const struct mech_module *mech = test_case->mech;
                struct auth_request *request;
                const char *testname = t_strdup_printf("auth mech %s %d/%lu",
                                                       mech->mech_name,
@@ -274,38 +327,57 @@ static void test_mechs(void)
                                                       N_ELEMENTS(tests));
                test_begin(testname);
 
-               request = auth_request_new(mech,  NULL);
-               test_mech_prepare_request(request, &handler, running_test,
-                                         tests[running_test].set_username ?
-                                         tests[running_test].username : NULL);
+               test_mech_prepare_request(&request, mech, &handler, running_test,
+                                         test_case);
 
-               if (mech == &mech_apop && tests[running_test].in == NULL)
-                       tests[running_test].in =
+               if (mech == &mech_apop && test_case->in == NULL)
+                       test_case->in =
                                test_mech_construct_apop_challenge(request->connect_uid,
-                                                                  &tests[running_test].len);
+                                                                  &test_case->len);
+               if (mech == &mech_dovecot_token && test_case->in == NULL) {
+                       test_case->in = d_token->data;
+                       test_case->len = d_token->used;
+               }
 
-               if (tests[running_test].expect_error != NULL)
-                       test_expect_error_string(tests[running_test].expect_error);
+               if (test_case->expect_error != NULL)
+                       test_expect_error_string(test_case->expect_error);
 
                request->state = AUTH_REQUEST_STATE_NEW;
-               request->initial_response = tests[running_test].in;
-               request->initial_response_len = tests[running_test].len;
+               request->initial_response = test_case->in;
+               request->initial_response_len = test_case->len;
                auth_request_initial(request);
 
+               const char *challenge = request->context;
+
                if (challenge != NULL) {
-                       test_mech_handle_challenge(request, tests[running_test].in,
-                                                  tests[running_test].len,
+                       test_mech_handle_challenge(request, test_case->in,
+                                                  test_case->len,
                                                   running_test,
-                                                  tests[running_test].success);
+                                                  test_case->success);
                }
 
-               if (!tests[running_test].set_username)
-                       test_assert_strcmp_idx(tests[running_test].username, request->user,
+               const char *username = request->user;
+
+               if (request->master_user != NULL)
+                       username = request->master_user;
+
+               if (!test_case->set_username_before_test && test_case->success) {
+                       /* If the username was set by the test logic, do not
+                        * compare it as it does not give any additional
+                        * information */
+                       test_assert_strcmp_idx(test_case->username, username,
                                               running_test);
-               else if (!tests[running_test].set_username && !tests[running_test].success)
-                       test_assert_idx(request->user == NULL, running_test);
+               } else if (!test_case->set_username_before_test && !test_case->success) {
+                       /* If the username is not set by the testlogic and we
+                        * expect failure, verify that the mechanism failed by
+                        * checking that the username is not set */
+                       test_assert_idx(username == NULL, running_test);
+               }
+
+               if (test_case->success)
+                       test_assert_idx(request->failed == FALSE, running_test);
                else
-                       test_assert_idx(!request->failed, running_test);
+                       test_assert_idx(request->failed == TRUE, running_test);
 
                event_unref(&request->event);
                event_unref(&request->mech_event);
@@ -314,7 +386,12 @@ static void test_mechs(void)
                test_end();
        } T_END;
        mech_otp_deinit();
-       array_free(&auths);
+       auths_deinit();
+       auth_token_deinit();
+       password_schemes_deinit();
+       passdb_mock_mod_deinit();
+       passdbs_deinit();
+       event_unref(&auth_event);
 }
 
 int main(void)