diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib-sasl/test-sasl-client.c | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/src/lib-sasl/test-sasl-client.c b/src/lib-sasl/test-sasl-client.c new file mode 100644 index 0000000..05b65f2 --- /dev/null +++ b/src/lib-sasl/test-sasl-client.c @@ -0,0 +1,419 @@ +#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 = { + .authid = NULL, +}; + +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; +} |