diff --git a/src/auth.c b/src/auth.c index d5f9c4a2..25c2e21d 100644 --- a/src/auth.c +++ b/src/auth.c @@ -83,7 +83,8 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *conn, static int _handle_scram_challenge(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata); -static char *_make_scram_init_msg(xmpp_conn_t *conn); +struct scram_user_data; +static int _make_scram_init_msg(struct scram_user_data *scram); static int _handle_missing_features_sasl(xmpp_conn_t *conn, void *userdata); static int _handle_missing_bind(xmpp_conn_t *conn, void *userdata); @@ -243,21 +244,24 @@ _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) if (text == NULL) continue; - if (strcasecmp(text, "PLAIN") == 0) + if (strcasecmp(text, "PLAIN") == 0) { conn->sasl_support |= SASL_MASK_PLAIN; - else if (strcasecmp(text, "EXTERNAL") == 0 && - (conn->tls_client_cert || conn->tls_client_key)) + } else if (strcasecmp(text, "EXTERNAL") == 0 && + (conn->tls_client_cert || conn->tls_client_key)) { conn->sasl_support |= SASL_MASK_EXTERNAL; - else if (strcasecmp(text, "DIGEST-MD5") == 0) + } else if (strcasecmp(text, "DIGEST-MD5") == 0) { conn->sasl_support |= SASL_MASK_DIGESTMD5; - else if (strcasecmp(text, "SCRAM-SHA-1") == 0) - conn->sasl_support |= SASL_MASK_SCRAMSHA1; - else if (strcasecmp(text, "SCRAM-SHA-256") == 0) - conn->sasl_support |= SASL_MASK_SCRAMSHA256; - else if (strcasecmp(text, "SCRAM-SHA-512") == 0) - conn->sasl_support |= SASL_MASK_SCRAMSHA512; - else if (strcasecmp(text, "ANONYMOUS") == 0) + } else if (strcasecmp(text, "ANONYMOUS") == 0) { conn->sasl_support |= SASL_MASK_ANONYMOUS; + } else { + size_t n; + for (n = 0; n < scram_algs_num; ++n) { + if (strcasecmp(text, scram_algs[n]->scram_name) == 0) { + conn->sasl_support |= scram_algs[n]->mask; + break; + } + } + } strophe_free(conn->ctx, text); } @@ -439,7 +443,11 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *conn, } struct scram_user_data { + xmpp_conn_t *conn; + int sasl_plus; char *scram_init; + char *channel_binding; + const char *first_bare; const struct hash_alg *alg; }; @@ -471,8 +479,9 @@ static int _handle_scram_challenge(xmpp_conn_t *conn, if (!challenge) goto err; - response = sasl_scram(conn->ctx, scram_ctx->alg, challenge, - scram_ctx->scram_init, conn->jid, conn->pass); + response = + sasl_scram(conn->ctx, scram_ctx->alg, scram_ctx->channel_binding, + challenge, scram_ctx->first_bare, conn->jid, conn->pass); strophe_free(conn->ctx, challenge); if (!response) goto err; @@ -506,7 +515,8 @@ static int _handle_scram_challenge(xmpp_conn_t *conn, */ rc = _handle_sasl_result(conn, stanza, (void *)scram_ctx->alg->scram_name); - strophe_free(conn->ctx, scram_ctx->scram_init); + strophe_free_and_null(conn->ctx, scram_ctx->channel_binding); + strophe_free_and_null(conn->ctx, scram_ctx->scram_init); strophe_free(conn->ctx, scram_ctx); } @@ -517,33 +527,103 @@ static int _handle_scram_challenge(xmpp_conn_t *conn, err_free_response: strophe_free(conn->ctx, response); err: - strophe_free(conn->ctx, scram_ctx->scram_init); + strophe_free_and_null(conn->ctx, scram_ctx->channel_binding); + strophe_free_and_null(conn->ctx, scram_ctx->scram_init); strophe_free(conn->ctx, scram_ctx); disconnect_mem_error(conn); return 0; } -static char *_make_scram_init_msg(xmpp_conn_t *conn) +static int _make_scram_init_msg(struct scram_user_data *scram) { + xmpp_conn_t *conn = scram->conn; xmpp_ctx_t *ctx = conn->ctx; - size_t message_len; - char *node; - char *message; - char nonce[32]; + const void *binding_data; + const char *binding_type; + char *node, *message; + size_t message_len, binding_type_len = 0, binding_data_len; + int l, is_secured = xmpp_conn_is_secured(conn); + /* This buffer must be able to hold: + * "p=<10 bytes binding type>,,<36 bytes binding data>" + * + alignment */ + char buf[56]; + + if (scram->sasl_plus) { + if (!is_secured) { + strophe_error( + ctx, "xmpp", + "SASL: Server requested a -PLUS variant to authenticate, " + "but the connection is not secured. This is an error on " + "the server side we can't do anything about."); + return -1; + } + if (tls_init_channel_binding(conn->tls, &binding_type, + &binding_type_len)) { + return -1; + } + /* directly account for the '=' char in 'p=' */ + binding_type_len += 1; + } node = xmpp_jid_node(ctx, conn->jid); if (!node) { - return NULL; + return -1; } - xmpp_rand_nonce(ctx->rand, nonce, sizeof(nonce)); - message_len = strlen(node) + strlen(nonce) + 8 + 1; + /* 32 bytes nonce is enough */ + xmpp_rand_nonce(ctx->rand, buf, 33); + message_len = strlen(node) + strlen(buf) + 8 + binding_type_len + 1; message = strophe_alloc(ctx, message_len); - if (message) { - strophe_snprintf(message, message_len, "n,,n=%s,r=%s", node, nonce); + if (!message) { + goto err_node; } + /* increase length to account for 'y,,', 'n,,' or 'p,,'. + * In the 'p' case the '=' sign has already been accounted for above. + */ + binding_type_len += 3; + if (scram->sasl_plus) { + l = strophe_snprintf(message, message_len, "p=%s,,n=%s,r=%s", + binding_type, node, buf); + } else { + l = strophe_snprintf(message, message_len, "%c,,n=%s,r=%s", + is_secured ? 'y' : 'n', node, buf); + } + if (l < 0 || (size_t)l >= message_len) { + goto err_msg; + } + if (binding_type_len > sizeof(buf)) { + goto err_msg; + } + /* Make `first_bare` point to the 'n' of 'n=' of the + * client-first-message */ + scram->first_bare = message + binding_type_len; + memcpy(buf, message, binding_type_len); + if (scram->sasl_plus) { + binding_data = + tls_get_channel_binding_data(conn->tls, &binding_data_len); + if (!binding_data) { + goto err_msg; + } + if (binding_data_len > sizeof(buf) - binding_type_len) { + strophe_error(ctx, "xmpp", "Channel binding data is too long (%zu)", + binding_data_len); + goto err_msg; + } + memcpy(&buf[binding_type_len], binding_data, binding_data_len); + binding_type_len += binding_data_len; + } + scram->channel_binding = + xmpp_base64_encode(ctx, (void *)buf, binding_type_len); + memset(buf, 0, binding_type_len); strophe_free(ctx, node); + scram->scram_init = message; + + return 0; - return message; +err_msg: + strophe_free(ctx, message); +err_node: + strophe_free(ctx, node); + return -1; } static xmpp_stanza_t *_make_starttls(xmpp_conn_t *conn) @@ -636,7 +716,7 @@ static void _auth(xmpp_conn_t *conn) return; } - if (anonjid && conn->sasl_support & SASL_MASK_ANONYMOUS) { + if (anonjid && (conn->sasl_support & SASL_MASK_ANONYMOUS)) { /* some crap here */ auth = _make_sasl_auth(conn, "ANONYMOUS"); if (!auth) { @@ -702,22 +782,26 @@ static void _auth(xmpp_conn_t *conn) "Password hasn't been set, and SASL ANONYMOUS unsupported."); xmpp_disconnect(conn); } else if (conn->sasl_support & SASL_MASK_SCRAM) { + size_t n; scram_ctx = strophe_alloc(conn->ctx, sizeof(*scram_ctx)); - if (conn->sasl_support & SASL_MASK_SCRAMSHA512) - scram_ctx->alg = &scram_sha512; - else if (conn->sasl_support & SASL_MASK_SCRAMSHA256) - scram_ctx->alg = &scram_sha256; - else if (conn->sasl_support & SASL_MASK_SCRAMSHA1) - scram_ctx->alg = &scram_sha1; + memset(scram_ctx, 0, sizeof(*scram_ctx)); + for (n = 0; n < scram_algs_num; ++n) { + if (conn->sasl_support & scram_algs[n]->mask) { + scram_ctx->alg = scram_algs[n]; + break; + } + } + auth = _make_sasl_auth(conn, scram_ctx->alg->scram_name); if (!auth) { disconnect_mem_error(conn); return; } - /* don't free scram_init on success */ - scram_ctx->scram_init = _make_scram_init_msg(conn); - if (!scram_ctx->scram_init) { + scram_ctx->conn = conn; + scram_ctx->sasl_plus = + scram_ctx->alg->mask & SASL_MASK_SCRAM_PLUS ? 1 : 0; + if (_make_scram_init_msg(scram_ctx)) { strophe_free(conn->ctx, scram_ctx); xmpp_stanza_release(auth); disconnect_mem_error(conn); @@ -753,7 +837,7 @@ static void _auth(xmpp_conn_t *conn) send_stanza(conn, auth, XMPP_QUEUE_STROPHE); - /* SASL SCRAM-SHA-1 was tried, unset flag */ + /* SASL algorithm was tried, unset flag */ conn->sasl_support &= ~scram_ctx->alg->mask; } else if (conn->sasl_support & SASL_MASK_DIGESTMD5) { auth = _make_sasl_auth(conn, "DIGEST-MD5"); diff --git a/src/common.h b/src/common.h index 32f2f3ae..3e521ced 100644 --- a/src/common.h +++ b/src/common.h @@ -167,9 +167,14 @@ struct _xmpp_send_queue_t { #define SASL_MASK_SCRAMSHA256 (1 << 4) #define SASL_MASK_SCRAMSHA512 (1 << 5) #define SASL_MASK_EXTERNAL (1 << 6) +#define SASL_MASK_SCRAMSHA1_PLUS (1 << 7) +#define SASL_MASK_SCRAMSHA256_PLUS (1 << 8) -#define SASL_MASK_SCRAM \ +#define SASL_MASK_SCRAM_PLUS \ + (SASL_MASK_SCRAMSHA1_PLUS | SASL_MASK_SCRAMSHA256_PLUS) +#define SASL_MASK_SCRAM_WEAK \ (SASL_MASK_SCRAMSHA1 | SASL_MASK_SCRAMSHA256 | SASL_MASK_SCRAMSHA512) +#define SASL_MASK_SCRAM (SASL_MASK_SCRAM_PLUS | SASL_MASK_SCRAM_WEAK) enum { XMPP_PORT_CLIENT = 5222, diff --git a/src/sasl.c b/src/sasl.c index 74a6b432..a280a7fd 100644 --- a/src/sasl.c +++ b/src/sasl.c @@ -375,6 +375,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, /** generate auth response string for the SASL SCRAM mechanism */ char *sasl_scram(xmpp_ctx_t *ctx, const struct hash_alg *alg, + const char *channel_binding, const char *challenge, const char *first_bare, const char *jid, @@ -398,6 +399,7 @@ char *sasl_scram(xmpp_ctx_t *ctx, char *result = NULL; size_t response_len; size_t auth_len; + int l; UNUSED(jid); @@ -428,37 +430,44 @@ char *sasl_scram(xmpp_ctx_t *ctx, } ival = strtol(i, &saveptr, 10); - auth_len = 10 + strlen(r) + strlen(first_bare) + strlen(challenge); + /* "c=," + r + ",p=" + sign_b64 + '\0' */ + response_len = 3 + strlen(channel_binding) + strlen(r) + 3 + + ((alg->digest_size + 2) / 3 * 4) + 1; + response = strophe_alloc(ctx, response_len); + if (!response) { + goto out_sval; + } + + auth_len = 3 + response_len + strlen(first_bare) + strlen(challenge); auth = strophe_alloc(ctx, auth_len); if (!auth) { - goto out_sval; + goto out_response; } - /* "c=biws," + r + ",p=" + sign_b64 + '\0' */ - response_len = 7 + strlen(r) + 3 + ((alg->digest_size + 2) / 3 * 4) + 1; - response = strophe_alloc(ctx, response_len); - if (!response) { + l = strophe_snprintf(response, response_len, "c=%s,%s", channel_binding, r); + if (l < 0 || (size_t)l >= response_len) { + goto out_auth; + } + l = strophe_snprintf(auth, auth_len, "%s,%s,%s", first_bare, challenge, + response); + if (l < 0 || (size_t)l >= auth_len) { goto out_auth; } - - strophe_snprintf(response, response_len, "c=biws,%s", r); - strophe_snprintf(auth, auth_len, "%s,%s,%s", first_bare + 3, challenge, - response); SCRAM_ClientKey(alg, (uint8_t *)password, strlen(password), (uint8_t *)sval, sval_len, (uint32_t)ival, key); SCRAM_ClientSignature(alg, key, (uint8_t *)auth, strlen(auth), sign); - SCRAM_ClientProof(alg, sign, key, sign); + SCRAM_ClientProof(alg, key, sign, sign); sign_b64 = xmpp_base64_encode(ctx, sign, alg->digest_size); if (!sign_b64) { - goto out_response; + goto out_auth; } /* Check for buffer overflow */ if (strlen(response) + strlen(sign_b64) + 3 + 1 > response_len) { strophe_free(ctx, sign_b64); - goto out_response; + goto out_auth; } strcat(response, ",p="); strcat(response, sign_b64); @@ -467,14 +476,14 @@ char *sasl_scram(xmpp_ctx_t *ctx, response_b64 = xmpp_base64_encode(ctx, (unsigned char *)response, strlen(response)); if (!response_b64) { - goto out_response; + goto out_auth; } result = response_b64; -out_response: - strophe_free(ctx, response); out_auth: strophe_free(ctx, auth); +out_response: + strophe_free(ctx, response); out_sval: strophe_free(ctx, sval); out: diff --git a/src/sasl.h b/src/sasl.h index bc5eb4ec..aa52e0c9 100644 --- a/src/sasl.h +++ b/src/sasl.h @@ -29,6 +29,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *password); char *sasl_scram(xmpp_ctx_t *ctx, const struct hash_alg *alg, + const char *channel_binding, const char *challenge, const char *first_bare, const char *jid, diff --git a/src/scram.c b/src/scram.c index 89380636..568ac5d6 100644 --- a/src/scram.c +++ b/src/scram.c @@ -42,6 +42,15 @@ const struct hash_alg scram_sha1 = { (void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update, (void (*)(void *, uint8_t *))crypto_SHA1_Final}; +const struct hash_alg scram_sha1_plus = { + "SCRAM-SHA-1-PLUS", + SASL_MASK_SCRAMSHA1_PLUS, + SHA1_DIGEST_SIZE, + (void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1, + (void (*)(void *))crypto_SHA1_Init, + (void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update, + (void (*)(void *, uint8_t *))crypto_SHA1_Final}; + const struct hash_alg scram_sha256 = { "SCRAM-SHA-256", SASL_MASK_SCRAMSHA256, @@ -51,6 +60,15 @@ const struct hash_alg scram_sha256 = { (void (*)(void *, const uint8_t *, size_t))sha256_process, (void (*)(void *, uint8_t *))sha256_done}; +const struct hash_alg scram_sha256_plus = { + "SCRAM-SHA-256-PLUS", + SASL_MASK_SCRAMSHA256_PLUS, + SHA256_DIGEST_SIZE, + (void (*)(const uint8_t *, size_t, uint8_t *))sha256_hash, + (void (*)(void *))sha256_init, + (void (*)(void *, const uint8_t *, size_t))sha256_process, + (void (*)(void *, uint8_t *))sha256_done}; + const struct hash_alg scram_sha512 = { "SCRAM-SHA-512", SASL_MASK_SCRAMSHA512, @@ -60,6 +78,32 @@ const struct hash_alg scram_sha512 = { (void (*)(void *, const uint8_t *, size_t))sha512_process, (void (*)(void *, uint8_t *))sha512_done}; +/* The order of this list defines the order in which the SCRAM algorithms are + * tried if the server supports them. + * Their order is derived from + * https://datatracker.ietf.org/doc/html/draft-ietf-kitten-password-storage + */ +const struct hash_alg *scram_algs[] = { + /* *1 */ + &scram_sha256_plus, + /* *1 */ + &scram_sha1_plus, + /* *1 */ + &scram_sha512, + /* *1 */ + &scram_sha256, + /* *1 */ + &scram_sha1, +}; +/* *1 - I want to use clang-format here, but by default it will put multiple + * elements per line if there's no comment. Currently clang-format also doesn't + * have an option to enforce it to behave like that, besides by putting comments + * between each element (or I couldn't find a way to do it). In order to prevent + * clang-format from re-formatting the array I added the comments above and + * wrote this lengthy description. + */ +const size_t scram_algs_num = sizeof(scram_algs) / sizeof(scram_algs[0]); + union common_hash_ctx { SHA1_CTX sha1; sha256_context sha256; diff --git a/src/scram.h b/src/scram.h index 59b79a29..e7ba5c3f 100644 --- a/src/scram.h +++ b/src/scram.h @@ -34,9 +34,8 @@ struct hash_alg { void (*final)(void *, uint8_t *); }; -extern const struct hash_alg scram_sha1; -extern const struct hash_alg scram_sha256; -extern const struct hash_alg scram_sha512; +extern const struct hash_alg *scram_algs[]; +extern const size_t scram_algs_num; void SCRAM_ClientKey(const struct hash_alg *alg, const uint8_t *password, diff --git a/src/tls.h b/src/tls.h index 9745572e..338b2421 100644 --- a/src/tls.h +++ b/src/tls.h @@ -45,6 +45,10 @@ unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn); xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn); int tls_set_credentials(tls_t *tls, const char *cafilename); +int tls_init_channel_binding(tls_t *tls, + const char **binding_prefix, + size_t *binding_prefix_len); +const void *tls_get_channel_binding_data(tls_t *tls, size_t *size); int tls_start(tls_t *tls); int tls_stop(tls_t *tls); diff --git a/src/tls_dummy.c b/src/tls_dummy.c index 4b7b2642..edd8e469 100644 --- a/src/tls_dummy.c +++ b/src/tls_dummy.c @@ -75,6 +75,23 @@ int tls_set_credentials(tls_t *tls, const char *cafilename) return -1; } +int tls_init_channel_binding(tls_t *tls, + const char **binding_prefix, + size_t *binding_prefix_len) +{ + UNUSED(tls); + UNUSED(binding_prefix); + UNUSED(binding_prefix_len); + return -1; +} + +const void *tls_get_channel_binding_data(tls_t *tls, size_t *size) +{ + UNUSED(tls); + UNUSED(size); + return NULL; +} + int tls_start(tls_t *tls) { UNUSED(tls); diff --git a/src/tls_gnutls.c b/src/tls_gnutls.c index c78783a4..0e24bc53 100644 --- a/src/tls_gnutls.c +++ b/src/tls_gnutls.c @@ -25,12 +25,26 @@ #include "tls.h" #include "sock.h" +#if GNUTLS_VERSION_NUMBER < 0x030702 +/* This is a very dirty workaround to make this file compile on older GnuTLS + * We simply define our own value for the later introduced + * `GNUTLS_CB_TLS_EXPORTER` which makes it compile, but it will fail on runtime + * when the combination of the following options happen: + * * GnuTLS < 3.7.2 + * * TLS 1.3 + * * A SCRAM PLUS variant to authenticate + * We will think about fixing this once a user pops up who has that problem. + */ +#define GNUTLS_CB_TLS_EXPORTER 3 +#endif + struct _tls { xmpp_ctx_t *ctx; /* do we need this? */ xmpp_conn_t *conn; gnutls_session_t session; gnutls_certificate_credentials_t cred; gnutls_x509_crt_t client_cert; + gnutls_datum_t channel_binding; int lasterror; }; @@ -507,8 +521,8 @@ tls_t *tls_new(xmpp_conn_t *conn) void tls_free(tls_t *tls) { - if (tls->client_cert) - gnutls_x509_crt_deinit(tls->client_cert); + gnutls_free(tls->channel_binding.data); + gnutls_x509_crt_deinit(tls->client_cert); gnutls_deinit(tls->session); gnutls_certificate_free_credentials(tls->cred); strophe_free(tls->ctx, tls); @@ -555,6 +569,55 @@ int tls_set_credentials(tls_t *tls, const char *cafilename) return err == GNUTLS_E_SUCCESS; } +int tls_init_channel_binding(tls_t *tls, + const char **binding_prefix, + size_t *binding_prefix_len) +{ + gnutls_channel_binding_t binding_type; + gnutls_protocol_t tls_version = gnutls_protocol_get_version(tls->session); + + switch (tls_version) { + case GNUTLS_SSL3: + case GNUTLS_TLS1_0: + case GNUTLS_TLS1_1: + case GNUTLS_TLS1_2: + *binding_prefix = "tls-unique"; + *binding_prefix_len = strlen("tls-unique"); + binding_type = GNUTLS_CB_TLS_UNIQUE; + break; + case GNUTLS_TLS1_3: + *binding_prefix = "tls-exporter"; + *binding_prefix_len = strlen("tls-exporter"); + binding_type = GNUTLS_CB_TLS_EXPORTER; + break; + default: + strophe_error(tls->ctx, "tls", "Unsupported TLS Version: %s", + gnutls_protocol_get_name(tls_version)); + return -1; + } + + if (tls->channel_binding.data) { + gnutls_free(tls->channel_binding.data); + tls->channel_binding.data = NULL; + } + int ret = gnutls_session_channel_binding(tls->session, binding_type, + &tls->channel_binding); + if (ret) { + strophe_error(tls->ctx, "tls", "could not get channel binding: %s", + gnutls_strerror(ret)); + } + return ret; +} + +const void *tls_get_channel_binding_data(tls_t *tls, size_t *size) +{ + if (!tls->channel_binding.data || !tls->channel_binding.size) { + strophe_error(tls->ctx, "tls", "No channel binding data available"); + } + *size = tls->channel_binding.size; + return tls->channel_binding.data; +} + int tls_start(tls_t *tls) { sock_set_blocking(tls->conn->sock); diff --git a/src/tls_openssl.c b/src/tls_openssl.c index 929e5f01..f82eefb0 100644 --- a/src/tls_openssl.c +++ b/src/tls_openssl.c @@ -107,6 +107,8 @@ struct _tls { SSL_CTX *ssl_ctx; SSL *ssl; X509 *client_cert; + void *channel_binding_data; + size_t channel_binding_size; int lasterror; }; @@ -715,6 +717,7 @@ tls_t *tls_new(xmpp_conn_t *conn) void tls_free(tls_t *tls) { + strophe_free(tls->ctx, tls->channel_binding_data); SSL_free(tls->ssl); X509_free(tls->client_cert); SSL_CTX_free(tls->ssl_ctx); @@ -741,6 +744,65 @@ int tls_set_credentials(tls_t *tls, const char *cafilename) return -1; } +int tls_init_channel_binding(tls_t *tls, + const char **binding_prefix, + size_t *binding_prefix_len) +{ + const char *label = NULL; + size_t labellen = 0; + + switch (SSL_version(tls->ssl)) { + case SSL3_VERSION: + *binding_prefix = "tls-unique"; + *binding_prefix_len = strlen("tls-unique"); + tls->channel_binding_size = 36; + break; + case TLS1_VERSION: + case TLS1_1_VERSION: + case TLS1_2_VERSION: + *binding_prefix = "tls-unique"; + *binding_prefix_len = strlen("tls-unique"); + tls->channel_binding_size = 12; + break; +#ifdef TLS1_3_VERSION + case TLS1_3_VERSION: + label = "EXPORTER-Channel-Binding"; + labellen = 24; + *binding_prefix = "tls-exporter"; + *binding_prefix_len = strlen("tls-exporter"); + tls->channel_binding_size = 32; + break; +#endif + default: + strophe_error(tls->ctx, "tls", "Unsupported TLS Version: %s", + SSL_get_version(tls->ssl)); + return -1; + } + + strophe_free_and_null(tls->ctx, tls->channel_binding_data); + tls->channel_binding_data = + strophe_alloc(tls->ctx, tls->channel_binding_size); + if (!tls->channel_binding_data) + return -1; + + if (SSL_export_keying_material(tls->ssl, tls->channel_binding_data, + tls->channel_binding_size, label, labellen, + NULL, 0, 0) != 1) { + strophe_error(tls->ctx, "tls", "Could not get channel binding data"); + return -1; + } + return 0; +} + +const void *tls_get_channel_binding_data(tls_t *tls, size_t *size) +{ + if (!tls->channel_binding_data || !tls->channel_binding_size) { + strophe_error(tls->ctx, "tls", "No channel binding data available"); + } + *size = tls->channel_binding_size; + return tls->channel_binding_data; +} + int tls_start(tls_t *tls) { int error; diff --git a/src/tls_schannel.c b/src/tls_schannel.c index 911a502f..f40c9e95 100644 --- a/src/tls_schannel.c +++ b/src/tls_schannel.c @@ -237,6 +237,23 @@ int tls_set_credentials(tls_t *tls, const char *cafilename) return -1; } +int tls_init_channel_binding(tls_t *tls, + const char **binding_prefix, + size_t *binding_prefix_len) +{ + UNUSED(tls); + UNUSED(binding_prefix); + UNUSED(binding_prefix_len); + return -1; +} + +const void *tls_get_channel_binding_data(tls_t *tls, size_t *size) +{ + UNUSED(tls); + UNUSED(size); + return NULL; +} + int tls_start(tls_t *tls) { ULONG ctxtreq = 0, ctxtattr = 0;