]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-sasl: Add unit tests
authorAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 11 Aug 2021 06:48:06 +0000 (09:48 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 13 Aug 2021 06:46:44 +0000 (09:46 +0300)
src/lib-sasl/Makefile.am
src/lib-sasl/test-sasl-client.c [new file with mode: 0644]

index ac428a1a54776e50c916488a44aff9e30c2268f8..5017bb9e784156beef220580f0c20fd7500dffa0 100644 (file)
@@ -1,7 +1,8 @@
 noinst_LTLIBRARIES = libsasl.la
 
 AM_CPPFLAGS = \
-       -I$(top_srcdir)/src/lib
+       -I$(top_srcdir)/src/lib \
+       -I$(top_srcdir)/src/lib-test
 
 libsasl_la_SOURCES = \
        mech-external.c \
@@ -16,3 +17,24 @@ headers = \
 
 pkginc_libdir=$(pkgincludedir)
 pkginc_lib_HEADERS = $(headers)
+
+test_programs = \
+       test-sasl-client
+
+noinst_PROGRAMS = $(test_programs)
+
+test_libs = \
+       $(noinst_LTLIBRARIES) \
+       ../lib-test/libtest.la \
+       ../lib/liblib.la
+
+test_deps = $(test_libs)
+
+test_sasl_client_SOURCES = test-sasl-client.c
+test_sasl_client_LDADD = $(test_libs)
+test_sasl_client_DEPENDENCIES = $(test_deps)
+
+check-local:
+       for bin in $(test_programs); do \
+         if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
+       done
diff --git a/src/lib-sasl/test-sasl-client.c b/src/lib-sasl/test-sasl-client.c
new file mode 100644 (file)
index 0000000..2052570
--- /dev/null
@@ -0,0 +1,418 @@
+#include "lib.h"
+#include "str.h"
+#include "strfuncs.h"
+#include "test-common.h"
+#include "dsasl-client.h"
+
+static const struct dsasl_client_settings sasl_empty_set = {
+};
+
+static const struct dsasl_client_settings sasl_no_password_set = {
+       .authid = "testuser",
+};
+
+static const struct dsasl_client_settings sasl_set = {
+       .authid = "testuser",
+       .password = "testpassword"
+};
+
+static const struct dsasl_client_settings sasl_master_set = {
+       .authzid = "testuser",
+       .authid = "masteruser",
+       .password = "masterpassword"
+};
+
+static void test_sasl_client_login(void)
+{
+       const char *error;
+       test_begin("sasl client LOGIN");
+       const struct dsasl_client_mech *mech = dsasl_client_mech_find("login");
+       i_assert(mech != NULL);
+       struct dsasl_client *client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       string_t *input = t_str_new(64);
+       string_t *output_s = t_str_new(64);
+       const unsigned char *output;
+       size_t olen;
+
+       /* parameters shouldn't work (test here for completeness) */
+       test_assert(dsasl_client_set_parameter(client, "parameter", "value", &error) == 0);
+
+       /* Any input is valid */
+       str_append(input, "Username:");
+       test_assert(dsasl_client_input(client, input->data, input->used, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       /* see what we got */
+       str_append_data(output_s, output, olen);
+       test_assert_strcmp(str_c(output_s), "testuser");
+
+       str_truncate(input, 0);
+       str_truncate(output_s, 0);
+
+       str_append(input, "Password:");
+       test_assert(dsasl_client_input(client, input->data, input->used, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       str_append_data(output_s, output, olen);
+       test_assert_strcmp(str_c(output_s), "testpassword");
+
+       /* there should be no results to collect, we can reuse error here */
+       test_assert(dsasl_client_get_result(client, "parameter", &error, &error) == 0);
+
+       dsasl_client_free(&client);
+       test_assert(client == NULL);
+
+       /* server sends input after password */
+       client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == -1);
+       test_assert_strcmp(error, "Server didn't finish authentication");
+
+       dsasl_client_free(&client);
+
+       /* missing username & password */
+       client = dsasl_client_new(mech, &sasl_empty_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == -1);
+       test_assert_strcmp(error, "authid not set");
+       dsasl_client_free(&client);
+
+       /* missing password */
+       client = dsasl_client_new(mech, &sasl_no_password_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == -1);
+       test_assert_strcmp(error, "password not set");
+       dsasl_client_free(&client);
+
+       /* unexpected NUL byte in input */
+       client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       str_truncate(input, 0);
+       str_append_data(input, "unexpected\0", 11);
+       test_assert(dsasl_client_input(client, input->data, input->used, &error) == -1);
+       test_assert_strcmp(error, "Unexpected NUL in input data");
+       dsasl_client_free(&client);
+
+       test_end();
+}
+
+static void test_sasl_client_plain(void)
+{
+       const char *error;
+       test_begin("sasl client PLAIN");
+       const struct dsasl_client_mech *mech = dsasl_client_mech_find("plain");
+       i_assert(mech != NULL);
+       struct dsasl_client *client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       const unsigned char *output;
+       size_t olen;
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       const unsigned char expected[] = "\0testuser\0testpassword";
+       /* there is no NUL byte at the end */
+       test_assert(olen == sizeof(expected) - 1);
+       test_assert(memcmp(output, expected, I_MIN(sizeof(expected) - 1, olen)) == 0);
+
+       dsasl_client_free(&client);
+       test_assert(client == NULL);
+
+       client = dsasl_client_new(mech, &sasl_master_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       const unsigned char expected_master[] =
+               "testuser\0masteruser\0masterpassword";
+       /* there is no NUL byte at the end */
+       test_assert(olen == sizeof(expected_master) - 1);
+       test_assert(memcmp(output, expected_master,
+                          I_MIN(sizeof(expected_master) - 1, olen)) == 0);
+
+       dsasl_client_free(&client);
+
+       /* unexpected initial response */
+       const unsigned char input[] = "ir";
+       client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+       test_assert(dsasl_client_input(client, input, sizeof(input)-1, &error) == -1);
+       test_assert_strcmp(error, "Server sent non-empty initial response");
+
+       dsasl_client_free(&client);
+
+       /* server sends input after response */
+       client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == -1);
+       test_assert_strcmp(error, "Server didn't finish authentication");
+
+       dsasl_client_free(&client);
+
+       /* missing username & password */
+       client = dsasl_client_new(mech, &sasl_empty_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == -1);
+       test_assert_strcmp(error, "authid not set");
+       dsasl_client_free(&client);
+
+       /* missing password */
+       client = dsasl_client_new(mech, &sasl_no_password_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == -1);
+       test_assert_strcmp(error, "password not set");
+       dsasl_client_free(&client);
+
+       /* unexpected NUL byte in input */
+       client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       const unsigned char input2[] = "unexpected\0";
+       test_assert(dsasl_client_input(client, input2, sizeof(input2), &error) == -1);
+       test_assert_strcmp(error, "Unexpected NUL in input data");
+       dsasl_client_free(&client);
+
+       test_end();
+}
+
+static void test_sasl_client_external(void)
+{
+       const char *error;
+       test_begin("sasl client EXTERNAL");
+       const struct dsasl_client_mech *mech = dsasl_client_mech_find("external");
+       i_assert(mech != NULL);
+       struct dsasl_client *client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       const unsigned char *output;
+       size_t olen;
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       const unsigned char expected[] = "testuser";
+       /* there is no NUL byte at the end */
+       test_assert(olen == sizeof(expected) - 1);
+       test_assert(memcmp(output, expected, I_MIN(sizeof(expected) - 1, olen)) == 0);
+
+       dsasl_client_free(&client);
+       test_assert(client == NULL);
+
+       client = dsasl_client_new(mech, &sasl_master_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       const unsigned char expected_master[] = "testuser";
+       /* there is no NUL byte at the end */
+       test_assert(olen == sizeof(expected_master) - 1);
+       test_assert(memcmp(output, expected_master,
+                          I_MIN(sizeof(expected_master) - 1, olen)) == 0);
+
+       dsasl_client_free(&client);
+
+       /* unexpected NUL byte in input */
+       client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       const unsigned char input2[] = "unexpected\0";
+       test_assert(dsasl_client_input(client, input2, sizeof(input2), &error) == -1);
+       test_assert_strcmp(error, "Unexpected NUL in input data");
+       dsasl_client_free(&client);
+
+       test_end();
+}
+
+static void test_sasl_client_oauthbearer(void)
+{
+       const char *error;
+       const char *value;
+       test_begin("sasl client OAUTHBEARER");
+       const struct dsasl_client_mech *mech = dsasl_client_mech_find("oauthbearer");
+       i_assert(mech != NULL);
+       struct dsasl_client *client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       string_t *input = t_str_new(64);
+       const unsigned char *output;
+       size_t olen;
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+
+       const unsigned char expected[] = "n,a=testuser,\1"
+               "auth=Bearer testpassword\1\1";
+       /* there is no NUL byte at the end */
+       test_assert(olen == sizeof(expected) - 1);
+       test_assert(memcmp(output, expected, I_MIN(sizeof(expected) - 1, olen)) == 0);
+       test_assert(dsasl_client_get_result(client, "status", &value, &error) == 1);
+       test_assert_strcmp(value, "");
+
+       dsasl_client_free(&client);
+       test_assert(client == NULL);
+
+       /* with host & port set */
+       client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_set_parameter(client, "host", "example.com", &error) == 1);
+       test_assert(dsasl_client_set_parameter(client, "port", "imap", &error) == -1);
+       test_assert_strcmp(error, "Invalid port value");
+       test_assert(dsasl_client_set_parameter(client, "port", "143", &error) == 1);
+       test_assert(dsasl_client_set_parameter(client, "unknown", "value", &error) == 0);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+
+       const unsigned char expected_h_p[] = "n,a=testuser,\1"
+               "host=example.com\1"
+               "port=143\1"
+               "auth=Bearer testpassword\1\1";
+       /* there is no NUL byte at the end */
+       test_assert(olen == sizeof(expected_h_p) - 1);
+       test_assert(memcmp(output, expected_h_p,
+                          I_MIN(sizeof(expected_h_p) - 1, olen)) == 0);
+
+       dsasl_client_free(&client);
+       test_assert(client == NULL);
+
+       client = dsasl_client_new(mech, &sasl_set);
+       /* test error response */
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       str_append(input, "{\"status\":\"401\",\"schemes\":\"bearer\",\"scope\":\"mail\"}");
+       test_assert(dsasl_client_input(client, input->data, input->used, &error) == -1);
+       test_assert_strcmp(error, "Failed to authenticate: 401");
+       test_assert(dsasl_client_get_result(client, "status", &value, &error) == 1);
+       test_assert_strcmp(value, "401");
+
+       dsasl_client_free(&client);
+
+       /* missing username & password */
+       client = dsasl_client_new(mech, &sasl_empty_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == -1);
+       test_assert_strcmp(error, "authid not set");
+       dsasl_client_free(&client);
+
+       /* missing password */
+       client = dsasl_client_new(mech, &sasl_no_password_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == -1);
+       test_assert_strcmp(error, "password not set");
+       dsasl_client_free(&client);
+
+       /* unexpected NUL byte in input */
+       client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       const unsigned char input2[] = "unexpected\0";
+       test_assert(dsasl_client_input(client, input2, sizeof(input2), &error) == -1);
+       test_assert_strcmp(error, "Unexpected NUL in input data");
+       dsasl_client_free(&client);
+
+       test_end();
+}
+
+static void test_sasl_client_xoauth2(void)
+{
+       const char *error;
+       test_begin("sasl client XOAUTH2");
+       const struct dsasl_client_mech *mech = dsasl_client_mech_find("xoauth2");
+       i_assert(mech != NULL);
+       struct dsasl_client *client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       string_t *input = t_str_new(64);
+       const unsigned char *output;
+       size_t olen;
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+
+       const unsigned char expected[] = "user=testuser\1auth=Bearer testpassword\1\1";
+       /* there is no NUL byte at the end */
+       test_assert(olen == sizeof(expected) - 1);
+       test_assert(memcmp(output, expected, I_MIN(sizeof(expected) - 1, olen)) == 0);
+
+       dsasl_client_free(&client);
+       test_assert(client == NULL);
+
+       client = dsasl_client_new(mech, &sasl_set);
+       /* test error response */
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == 0);
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       str_append(input, "{\"status\":\"401\",\"schemes\":\"bearer\",\"scope\":\"mail\"}");
+       test_assert(dsasl_client_input(client, input->data, input->used, &error) == -1);
+       test_assert_strcmp(error, "Failed to authenticate: 401");
+
+       dsasl_client_free(&client);
+
+       /* missing username & password */
+       client = dsasl_client_new(mech, &sasl_empty_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == -1);
+       test_assert_strcmp(error, "authid not set");
+       dsasl_client_free(&client);
+
+       /* missing password */
+       client = dsasl_client_new(mech, &sasl_no_password_set);
+       i_assert(client != NULL);
+
+       test_assert(dsasl_client_input(client, uchar_empty_ptr, 0, &error) == 0);
+       test_assert(dsasl_client_output(client, &output, &olen, &error) == -1);
+       test_assert_strcmp(error, "password not set");
+       dsasl_client_free(&client);
+
+       /* unexpected NUL byte in input */
+       client = dsasl_client_new(mech, &sasl_set);
+       i_assert(client != NULL);
+
+       const unsigned char input2[] = "unexpected\0";
+       test_assert(dsasl_client_input(client, input2, sizeof(input2), &error) == -1);
+       test_assert_strcmp(error, "Unexpected NUL in input data");
+       dsasl_client_free(&client);
+
+       test_end();
+}
+
+int main(void)
+{
+       static void (*const test_functions[])(void) = {
+               test_sasl_client_login,
+               test_sasl_client_plain,
+               test_sasl_client_external,
+               test_sasl_client_oauthbearer,
+               test_sasl_client_xoauth2,
+               NULL
+       };
+       dsasl_clients_init();
+       int ret = test_run(test_functions);
+       dsasl_clients_deinit();
+       return ret;
+}