#include "randgen.h"
#include "safe-memset.h"
-#include "auth-scram.h"
#include "auth-scram-client.h"
/* c-nonce length */
static string_t *auth_scram_get_client_first(struct auth_scram_client *client)
{
+ const char *cbind_type = client->set.cbind_type;
+ enum auth_scram_cbind_server_support cbind_support =
+ client->set.cbind_support;
const char *authzid_enc, *username_enc, *gs2_header, *cfm_bare;
string_t *str;
size_t cfm_bare_offset;
username_enc = auth_scram_escape_username(client->set.authid);
str = t_str_new(256);
- str_append(str, "n,"); /* Channel binding not supported */
+ if (cbind_type == NULL) {
+ /* Channel binding not supported by client */
+ str_append(str, "n,");
+ } else if (cbind_support == AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE) {
+ /* Channel binding not supported by server */
+ str_append(str, "y,");
+ } else {
+ str_append(str, "p=");
+ str_append(str, cbind_type);
+ str_append_c(str, ',');
+ }
if (*authzid_enc != '\0') {
str_append(str, "a=");
str_append(str, authzid_enc);
static string_t *auth_scram_get_client_final(struct auth_scram_client *client)
{
const struct hash_method *hmethod = client->set.hash_method;
+ const buffer_t *cbind_data = client->set.cbind_data;
unsigned char salted_password[hmethod->digest_size];
unsigned char client_key[hmethod->digest_size];
unsigned char stored_key[hmethod->digest_size];
unsigned char client_proof[hmethod->digest_size];
unsigned char server_key[hmethod->digest_size];
struct hmac_context ctx;
- const char *cbind_input;
+ const void *cbind_input;
+ size_t cbind_input_size;
string_t *auth_message, *str;
unsigned int k;
s-nonce = printable
*/
- cbind_input = client->gs2_header;
+ if (client->gs2_header[0] != 'p') {
+ i_assert(cbind_data == NULL);
+ cbind_input = client->gs2_header;
+ cbind_input_size = strlen(client->gs2_header);
+ } else {
+ size_t gs2_header_len = strlen(client->gs2_header);
+ buffer_t *cbind_buf;
+ i_assert(cbind_data != NULL);
+ cbind_buf = t_buffer_create(gs2_header_len + cbind_data->used);
+ buffer_append(cbind_buf, client->gs2_header, gs2_header_len);
+ buffer_append_buf(cbind_buf, cbind_data, 0, SIZE_MAX);
+ cbind_input = cbind_buf->data;
+ cbind_input_size = cbind_buf->used;
+ }
str = t_str_new(256);
str_append(str, "c=");
- base64_encode(cbind_input, strlen(cbind_input), str);
+ base64_encode(cbind_input, cbind_input_size, str);
str_append(str, ",r=");
str_append(str, client->nonce);
#include "str.h"
#include "hmac.h"
#include "randgen.h"
+#include "buffer.h"
#include "hash-method.h"
#include "sha1.h"
#include "sha2.h"
#include "auth-scram-server.h"
#include "auth-scram-client.h"
+// FIXME: channel binding tests
+
struct backend_context {
pool_t pool;
const char *username;
const char *login_username;
+ const char *cbind_type;
+ buffer_t *cbind_data;
+
enum auth_scram_server_error expect_error;
unsigned int test_id;
};
return TRUE;
}
+static void
+test_auth_start_channel_binding(struct auth_scram_server *asserver,
+ const char *type)
+{
+ struct backend_context *bctx =
+ container_of(asserver, struct backend_context, asserver);
+
+ test_assert_strcmp(bctx->cbind_type, type);
+}
+
+static int
+test_auth_accept_channel_binding(struct auth_scram_server *asserver,
+ buffer_t **data_r)
+{
+ struct backend_context *bctx =
+ container_of(asserver, struct backend_context, asserver);
+
+ *data_r = bctx->cbind_data;
+ return 0;
+}
+
static int
test_auth_credentials_lookup(struct auth_scram_server *asserver,
struct auth_scram_key_data *key_data)
static const struct auth_scram_server_backend backend = {
.set_username = test_auth_set_username,
.set_login_username = test_auth_set_login_username,
+ .start_channel_binding = test_auth_start_channel_binding,
+ .accept_channel_binding = test_auth_accept_channel_binding,
.credentials_lookup = test_auth_credentials_lookup,
};
case 10:
output = "y,,n=user,q=frop";
break;
+ case 20:
+ output = "y,,n=frop,r=2342141234123";
+ break;
+ case 21:
+ output = "n,,n=frop,r=0980923401388";
+ break;
default:
auth_scram_client_output(&bctx->asclient, output_r,
output_len_r);
static void
test_auth_success_one(const struct hash_method *hmethod, const char *authid,
- const char *authzid, const char *password)
+ const char *authzid, const char *password,
+ const char *cbind_type)
{
struct backend_context *bctx;
pool_t pool;
bctx->password = password;
bctx->iterate_count = 4096;
+ if (cbind_type != NULL) {
+ buffer_t *cbind_buf = t_buffer_create(64);
+ unsigned char *cbind_data =
+ buffer_append_space_unsafe(cbind_buf, cbind_buf->used);
+
+ random_fill(cbind_data, cbind_buf->used);
+ bctx->cbind_type = cbind_type;
+ bctx->cbind_data = cbind_buf;
+ }
+
struct auth_scram_client_settings client_set;
i_zero(&client_set);
client_set.authzid = authzid;
client_set.password = password;
+ if (cbind_type != NULL) {
+ client_set.cbind_support =
+ AUTH_SCRAM_CBIND_SERVER_SUPPORT_REQUIRED;
+ client_set.cbind_type = cbind_type;
+ client_set.cbind_data = bctx->cbind_data;
+ }
+
auth_scram_client_init(&bctx->asclient, pool, &client_set);
struct auth_scram_server_settings server_set;
i_zero(&server_set);
server_set.hash_method = hmethod;
+ if (cbind_type != NULL) {
+ server_set.cbind_support =
+ AUTH_SCRAM_CBIND_SERVER_SUPPORT_REQUIRED;
+ }
+
auth_scram_server_init(&bctx->asserver, pool, &server_set, &backend);
while (!test_has_failed()) {
static void test_auth_success(void)
{
test_begin("auth success sha1");
- test_auth_success_one(&hash_method_sha1, "user", NULL, "frop");
+ test_auth_success_one(&hash_method_sha1, "user", NULL, "frop", NULL);
test_end();
test_begin("auth success sha1 master");
- test_auth_success_one(&hash_method_sha1, "master", "user", "frop");
+ test_auth_success_one(&hash_method_sha1, "master", "user", "frop",
+ NULL);
test_end();
test_begin("auth success sha256");
- test_auth_success_one(&hash_method_sha256, "user", NULL, "frop");
+ test_auth_success_one(&hash_method_sha256, "user", NULL, "frop", NULL);
test_end();
test_begin("auth success sha256 master");
- test_auth_success_one(&hash_method_sha256, "master", "user", "frop");
+ test_auth_success_one(&hash_method_sha256, "master", "user", "frop",
+ NULL);
test_end();
test_begin("auth success sha1 ','");
- test_auth_success_one(&hash_method_sha1, "u,er", NULL, "frop");
+ test_auth_success_one(&hash_method_sha1, "u,er", NULL, "frop", NULL);
test_end();
test_begin("auth success sha1 master ','");
- test_auth_success_one(&hash_method_sha1, "m,ster", ",ser", "frop");
+ test_auth_success_one(&hash_method_sha1, "m,ster", ",ser", "frop",
+ NULL);
test_end();
test_begin("auth success sha1 '='");
- test_auth_success_one(&hash_method_sha1, "u=er", NULL, "frop");
+ test_auth_success_one(&hash_method_sha1, "u=er", NULL, "frop", NULL);
test_end();
test_begin("auth success sha1 master '='");
- test_auth_success_one(&hash_method_sha1, "m=ster", "=ser", "frop");
+ test_auth_success_one(&hash_method_sha1, "m=ster", "=ser", "frop",
+ NULL);
+ test_end();
+
+ test_begin("auth success sha1 cbind");
+ test_auth_success_one(&hash_method_sha1, "user", NULL, "frop",
+ "tls-unique");
+ test_end();
+
+ test_begin("auth success sha1 master cbind");
+ test_auth_success_one(&hash_method_sha1, "master", "user", "frop",
+ "tls-unique");
+ test_end();
+
+ test_begin("auth success sha256 cbind");
+ test_auth_success_one(&hash_method_sha1, "user", NULL, "frop",
+ "tls-unique");
+ test_end();
+
+ test_begin("auth success sha256 master cbind");
+ test_auth_success_one(&hash_method_sha1, "master", "user", "frop",
+ "tls-unique");
test_end();
}
static void
test_auth_server_error_one(const struct hash_method *hmethod,
+ enum auth_scram_cbind_server_support cbind_support,
enum auth_scram_server_error expect_error,
unsigned int test_id)
{
i_zero(&server_set);
server_set.hash_method = hmethod;
+ server_set.cbind_support = cbind_support;
auth_scram_server_init(&bctx->asserver, pool, &server_set, &backend);
for (i = 0; i <= 19; i++) {
test_begin("auth server error sha1 - protocol violation");
test_auth_server_error_one(
- &hash_method_sha1,
+ &hash_method_sha1, AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE,
AUTH_SCRAM_SERVER_ERROR_PROTOCOL_VIOLATION, i);
test_end();
}
test_begin("auth server error sha1 - bad username");
test_auth_server_error_one(
- &hash_method_sha1,
+ &hash_method_sha1, AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE,
AUTH_SCRAM_SERVER_ERROR_BAD_USERNAME, 0);
test_end();
test_begin("auth server error sha256 - bad login username");
test_auth_server_error_one(
- &hash_method_sha256,
+ &hash_method_sha256, AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE,
AUTH_SCRAM_SERVER_ERROR_BAD_LOGIN_USERNAME, 0);
test_end();
test_begin("auth server error sha1 - lookup failed");
test_auth_server_error_one(
- &hash_method_sha1,
+ &hash_method_sha1, AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE,
AUTH_SCRAM_SERVER_ERROR_LOOKUP_FAILED, 0);
test_end();
test_begin("auth server error sha256 - lookup failed");
test_auth_server_error_one(
- &hash_method_sha256,
+ &hash_method_sha256, AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE,
AUTH_SCRAM_SERVER_ERROR_LOOKUP_FAILED, 0);
test_end();
test_begin("auth server error sha1 - password mismatch");
test_auth_server_error_one(
- &hash_method_sha1,
+ &hash_method_sha1, AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE,
AUTH_SCRAM_SERVER_ERROR_VERIFICATION_FAILED, 0);
test_end();
test_begin("auth server error sha256 - password mismatch");
test_auth_server_error_one(
- &hash_method_sha256,
+ &hash_method_sha256, AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE,
AUTH_SCRAM_SERVER_ERROR_VERIFICATION_FAILED, 0);
test_end();
+
+ test_begin("auth server error sha1 - channel bind downgrade attack");
+ test_auth_server_error_one(
+ &hash_method_sha1, AUTH_SCRAM_CBIND_SERVER_SUPPORT_AVAILABLE,
+ AUTH_SCRAM_SERVER_ERROR_PROTOCOL_VIOLATION, 20);
+ test_end();
+
+ test_begin("auth server error sha1 - channel bind required");
+ test_auth_server_error_one(
+ &hash_method_sha1, AUTH_SCRAM_CBIND_SERVER_SUPPORT_REQUIRED,
+ AUTH_SCRAM_SERVER_ERROR_PROTOCOL_VIOLATION, 21);
+ test_end();
}
int main(void)