diff --git a/sample/client.c b/sample/client.c index 47e787d7..778d9a6e 100644 --- a/sample/client.c +++ b/sample/client.c @@ -334,7 +334,7 @@ int mysasl_negotiate(FILE *in, FILE *out, sasl_conn_t *conn) void usage(void) { - fprintf(stderr, "usage: client [-c|-C] [-p port] [-s service] [-m mech] host\n"); + fprintf(stderr, "usage: client [-c|-C|-d|-D] [-p port] [-s service] [-m mech] host\n"); exit(EX_USAGE); } @@ -355,16 +355,29 @@ int main(int argc, char *argv[]) int niflags, error; struct sockaddr_storage local_ip, remote_ip; int cb_flag = 0; + int cb_digest = 0; sasl_channel_binding_t cb; - while ((c = getopt(argc, argv, "Ccp:s:m:")) != EOF) { + while ((c = getopt(argc, argv, "CcDdp:s:m:")) != EOF) { switch(c) { case 'C': - cb_flag = 2; /* channel bindings are critical */ + cb_flag = 2; /* 'finish' channel bindings are critical */ + cb_digest = 0; break; case 'c': - cb_flag = 1; /* channel bindings are optional */ + cb_flag = 1; /* 'finish' channel bindings are optional */ + cb_digest = 0; + break; + + case 'D': + cb_flag = 2; /* 'digest' channel bindings are critical */ + cb_digest = 1; + break; + + case 'd': + cb_flag = 1; /* 'digest' bindings are optional */ + cb_digest = 1; break; case 'p': @@ -443,12 +456,23 @@ int main(int argc, char *argv[]) if (r != SASL_OK) saslfail(r, "allocating connection state"); if (cb_flag) { - cb.name = "sasl-sample"; - cb.critical = cb_flag > 1; - cb.data = (unsigned char *) "this is a test of channel binding"; - cb.len = (unsigned int) strlen((const char *) cb.data); - - sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb); + /* see RFC 5929 and RFC 9266 for reference */ + /* see cyrus-imapd for real implementation example */ + const char finish_msg[] = "use SSL_get_(peer)_finished()"; + const char digest_msg[] = "use X509_digest()"; + + cb.critical = cb_flag > 1; + if (cb_digest) { + cb.name = "tls-server-end-point"; + cb.data = (const unsigned char*)digest_msg; + cb.len = sizeof(digest_msg); + } + else { + cb.name = "tls-unique"; /* or "tls-exporter" for TLS 1.3 */ + cb.data = (const unsigned char*)finish_msg; + cb.len = sizeof(finish_msg); + } + sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb); } /* set external properties here diff --git a/sample/server.c b/sample/server.c index 43974765..5c0b7371 100644 --- a/sample/server.c +++ b/sample/server.c @@ -122,6 +122,38 @@ enumerateAttributes(OM_uint32 *minor, int noisy); #endif +typedef int (*my_cb_ft)(void); + +struct my_channel_binding +{ + /* storage for 'tls-unique' or 'tls-exporter' */ + sasl_channel_binding_t finish; + unsigned char finish_data[64]; /* use data[EVP_MAX_MD_SIZE] */ + /* storage for 'tls-server-endpoint' */ + sasl_channel_binding_t digest; + unsigned char digest_data[64]; /* use data[EVP_MAX_MD_SIZE] */ +}; + +/* EXPORTED */ +int my_select_binding(sasl_conn_t* conn, + void* context, + const char* plugin, + const char* cbindingname) +{ + struct my_channel_binding *cb = context; + + if (!conn || !context || !plugin || !cbindingname) { + return SASL_FAIL; + } + + if (cb->digest.name != NULL && + strcmp(cbindingname, cb->digest.name) == 0) { + /* overwrite channel binding with 'tls-server-end-point' data */ + return sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb->digest); + } + return SASL_FAIL; +} + /* create a socket listening on port 'port' */ /* if af is PF_UNSPEC more than one socket may be returned */ /* the returned list is dynamically allocated, so caller needs to free it */ @@ -350,6 +382,12 @@ int main(int argc, char *argv[]) int r, i; sasl_conn_t *conn; int cb_flag = 0; + struct my_channel_binding cb; + + const struct sasl_callback mysasl_cb[] = { + { SASL_CB_SERVER_CHANNEL_BINDING, (my_cb_ft)&my_select_binding, &cb}, + { SASL_CB_LIST_END, NULL, NULL} + }; while ((c = getopt(argc, argv, "Cch:p:s:m:")) != EOF) { switch(c) { @@ -384,7 +422,7 @@ int main(int argc, char *argv[]) } /* initialize the sasl library */ - r = sasl_server_init(NULL, "sample"); + r = sasl_server_init(mysasl_cb, "sample"); if (r != SASL_OK) saslfail(r, "initializing libsasl"); /* get a listening socket */ @@ -408,7 +446,6 @@ int main(int argc, char *argv[]) int nfds, fd = -1; FILE *in, *out; fd_set readfds; - sasl_channel_binding_t cb; FD_ZERO(&readfds); for (i = 1; i <= l[0]; i++) @@ -483,13 +520,26 @@ int main(int argc, char *argv[]) NULL, 0, &conn); if (r != SASL_OK) saslfail(r, "allocating connection state"); - cb.name = "sasl-sample"; - cb.critical = cb_flag > 1; - cb.data = (const unsigned char *) "this is a test of channel binding"; - cb.len = (unsigned int) strlen((const char *) cb.data); - + memset(&cb, 0, sizeof(cb)); if (cb_flag) { - sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb); + /* see RFC 5929 and RFC 9266 for reference */ + /* see cyrus-imapd for real implementation example */ + const char finish_msg[] = "use SSL_get_(peer)_finished()"; + const char digest_msg[] = "use X509_digest()"; + + cb.finish.name = "tls-unique"; /* or "tls-exporter" for TLS 1.3 */ + cb.finish.critical = cb_flag > 1; + cb.finish.data = cb.finish_data; + memcpy(cb.finish_data, finish_msg, sizeof(finish_msg)); + cb.finish.len = sizeof(finish_msg); + sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb.finish); + + /* Prepare alternate channel binding data for callback */ + cb.digest.name = "tls-server-end-point"; + cb.digest.critical = cb_flag > 1; + cb.digest.data = cb.digest_data; + memcpy(cb.digest_data, digest_msg, sizeof(digest_msg)); + cb.digest.len = sizeof(digest_msg); } /* set external properties here