From: Stephan Bosch Date: Sat, 5 Oct 2024 13:01:09 +0000 (+0200) Subject: lib-sasl: Add CRAM-MD5 client support X-Git-Tag: 2.4.2~149 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=60c90255988f96b63b60d7c1af263d4983eddea9;p=thirdparty%2Fdovecot%2Fcore.git lib-sasl: Add CRAM-MD5 client support --- diff --git a/src/lib-sasl/Makefile.am b/src/lib-sasl/Makefile.am index b903a04d5a..525f6b0590 100644 --- a/src/lib-sasl/Makefile.am +++ b/src/lib-sasl/Makefile.am @@ -17,6 +17,7 @@ AM_CPPFLAGS = \ client_mechanisms = \ dsasl-client-mech-anonymous.c \ + dsasl-client-mech-cram-md5.c \ dsasl-client-mech-external.c \ dsasl-client-mech-login.c \ dsasl-client-mech-oauthbearer.c \ diff --git a/src/lib-sasl/dsasl-client-mech-cram-md5.c b/src/lib-sasl/dsasl-client-mech-cram-md5.c new file mode 100644 index 0000000000..3d871692cc --- /dev/null +++ b/src/lib-sasl/dsasl-client-mech-cram-md5.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2024 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "hex-binary.h" +#include "hmac.h" +#include "md5.h" + +#include "dsasl-client-private.h" + +struct cram_md5_dsasl_client { + struct dsasl_client client; + + const char *challenge; +}; + +static enum dsasl_client_result +mech_cram_md5_input(struct dsasl_client *client, + const unsigned char *input, size_t input_len, + const char **error_r) +{ + struct cram_md5_dsasl_client *cclient = + container_of(client, struct cram_md5_dsasl_client, client); + + const unsigned char *p = input, *pend = input + input_len; + + if (p >= pend) { + *error_r = "Server sent empty challenge"; + return DSASL_CLIENT_RESULT_ERR_PROTOCOL; + } + if (*p != '<') { + *error_r = "Server sent invalid challenge begin"; + return DSASL_CLIENT_RESULT_ERR_PROTOCOL; + } + p++; + + for (; (p + 1) < pend; p++) { + if (*p <= 32 || *p == 127 || *p == '>') { + *error_r = "Server sent invalid challenge"; + return DSASL_CLIENT_RESULT_ERR_PROTOCOL; + } + } + + if (p >= pend || *p != '>') { + *error_r = "Server sent invalid challenge end"; + return DSASL_CLIENT_RESULT_ERR_PROTOCOL; + } + p++; + i_assert(p == pend); + + if (input_len < 5) { + *error_r = "Server sent invalid challenge"; + return DSASL_CLIENT_RESULT_ERR_PROTOCOL; + } + + cclient->challenge = p_strndup(client->pool, input, input_len); + return DSASL_CLIENT_RESULT_OK; +} + +static enum dsasl_client_result +mech_cram_md5_output(struct dsasl_client *client, + const unsigned char **output_r, size_t *output_len_r, + const char **error_r) +{ + struct cram_md5_dsasl_client *cclient = + container_of(client, struct cram_md5_dsasl_client, client); + string_t *str; + + if (client->set.authid == NULL) { + *error_r = "authid not set"; + return DSASL_CLIENT_RESULT_ERR_INTERNAL; + } + if (client->password == NULL) { + *error_r = "password not set"; + return DSASL_CLIENT_RESULT_ERR_INTERNAL; + } + + if (cclient->challenge == NULL) { + *output_r = uchar_empty_ptr; + *output_len_r = 0; + return DSASL_CLIENT_RESULT_OK; + } + + struct hmac_context ctx; + unsigned char digest[MD5_RESULTLEN]; + + hmac_init(&ctx, (const unsigned char *)client->password, + strlen(client->password), &hash_method_md5); + hmac_update(&ctx, cclient->challenge, strlen(cclient->challenge)); + hmac_final(&ctx, digest); + + str = str_new(client->pool, 256); + str_append(str, client->set.authid); + str_append_c(str, ' '); + binary_to_hex_append(str, digest, sizeof(digest)); + + *output_r = str_data(str); + *output_len_r = str_len(str); + return DSASL_CLIENT_RESULT_OK; +} + +const struct dsasl_client_mech dsasl_client_mech_cram_md5 = { + .name = SASL_MECH_NAME_CRAM_MD5, + .struct_size = sizeof(struct cram_md5_dsasl_client), + + .input = mech_cram_md5_input, + .output = mech_cram_md5_output, +}; diff --git a/src/lib-sasl/dsasl-client-private.h b/src/lib-sasl/dsasl-client-private.h index 4bdc16cedb..6837fd01ce 100644 --- a/src/lib-sasl/dsasl-client-private.h +++ b/src/lib-sasl/dsasl-client-private.h @@ -43,6 +43,7 @@ struct dsasl_client_mech { }; extern const struct dsasl_client_mech dsasl_client_mech_anonymous; +extern const struct dsasl_client_mech dsasl_client_mech_cram_md5; extern const struct dsasl_client_mech dsasl_client_mech_external; extern const struct dsasl_client_mech dsasl_client_mech_login; extern const struct dsasl_client_mech dsasl_client_mech_oauthbearer; diff --git a/src/lib-sasl/dsasl-client.c b/src/lib-sasl/dsasl-client.c index 983abd1e65..548ff32fbb 100644 --- a/src/lib-sasl/dsasl-client.c +++ b/src/lib-sasl/dsasl-client.c @@ -158,6 +158,7 @@ void dsasl_clients_init(void) dsasl_client_mech_register(&dsasl_client_mech_external); dsasl_client_mech_register(&dsasl_client_mech_plain); dsasl_client_mech_register(&dsasl_client_mech_login); + dsasl_client_mech_register(&dsasl_client_mech_cram_md5); dsasl_client_mech_register(&dsasl_client_mech_oauthbearer); dsasl_client_mech_register(&dsasl_client_mech_xoauth2); dsasl_client_mech_register(&dsasl_client_mech_scram_sha_1); diff --git a/src/lib-sasl/fuzz-sasl-authentication.c b/src/lib-sasl/fuzz-sasl-authentication.c index 3471eb21e6..220f45e58b 100644 --- a/src/lib-sasl/fuzz-sasl-authentication.c +++ b/src/lib-sasl/fuzz-sasl-authentication.c @@ -580,6 +580,7 @@ static void fuzz_sasl_run(struct istream *input) server_inst = sasl_server_instance_create(server, &server_set); sasl_server_mech_register_anonymous(server_inst); + sasl_server_mech_register_cram_md5(server_inst); sasl_server_mech_register_login(server_inst); sasl_server_mech_register_plain(server_inst); sasl_server_mech_register_scram_sha1(server_inst); diff --git a/src/lib-sasl/test-sasl-authentication.c b/src/lib-sasl/test-sasl-authentication.c index 69d6becaf9..5a84afbb86 100644 --- a/src/lib-sasl/test-sasl-authentication.c +++ b/src/lib-sasl/test-sasl-authentication.c @@ -422,6 +422,7 @@ test_sasl_run(const struct test_sasl *test, const char *label, server_inst = sasl_server_instance_create(server, &server_set); sasl_server_mech_register_anonymous(server_inst); + sasl_server_mech_register_cram_md5(server_inst); sasl_server_mech_register_external(server_inst); sasl_server_mech_register_login(server_inst); sasl_server_mech_register_plain(server_inst); @@ -484,6 +485,15 @@ static const struct test_sasl success_tests[] = { .password = "pass", }, }, + /* CRAM-MD5 */ + { + .mech = "CRAM-MD5", + .authid_type = SASL_SERVER_AUTHID_TYPE_USERNAME, + .server = { + .authid = "user", + .password = "pass", + }, + }, /* SCRAM-SHA-1 */ { .mech = "SCRAM-SHA-1", @@ -727,6 +737,31 @@ static const struct test_sasl bad_creds_tests[] = { }, .failure = TRUE, }, + /* CRAM-MD5 */ + { + .mech = "CRAM-MD5", + .authid_type = SASL_SERVER_AUTHID_TYPE_USERNAME, + .server = { + .authid = "user", + .password = "pass", + }, + .client = { + .authid = "userb", + }, + .failure = TRUE, + }, + { + .mech = "CRAM-MD5", + .authid_type = SASL_SERVER_AUTHID_TYPE_USERNAME, + .server = { + .authid = "user", + .password = "pass", + }, + .client = { + .password= "florp", + }, + .failure = TRUE, + }, /* SCRAM-SHA-1 */ { .mech = "SCRAM-SHA-1",