diff --git a/include/sasl.h b/include/sasl.h index 5c356e0e..c2ca3cd9 100755 --- a/include/sasl.h +++ b/include/sasl.h @@ -671,6 +671,27 @@ typedef int sasl_canon_user_t(sasl_conn_t *conn, #define SASL_CB_CANON_USER (0x8007) +/* Callback for servers to change channel binding type + * (e.g. "tls-server-end-point" instead of "tls-unique" or "tls-exporter"). + * The callback is used by plugins like SCRAM-SHA-*-PLUS or GS2-KRB5-PLUS + * when the desired binding type of the client does not match the binding + * type set with property SASL_CHANNEL_BINDING. + * A server should check the requested 'cbindingname' of the 'plugin' and + * overwrite the channel binding data with the property SASL_CHANNEL_BINDING + * within the callback function. + * Note that plugins only store a pointer of your channel binding data and + * the memory of your struct sasl_channel_binding_t must be valid during + * the complete authentication process. + * + * plugin -- name of plugin + * cbindingname -- name of desired channel binding type + */ +typedef int sasl_server_channel_binding_t(sasl_conn_t* conn, + void* context, + const char* plugin, + const char* cbindingname); +#define SASL_CB_SERVER_CHANNEL_BINDING (0x8008) + /********************************** * Common Client/server functions * **********************************/ diff --git a/plugins/gs2.c b/plugins/gs2.c index 50cd3f71..f870d2f0 100644 --- a/plugins/gs2.c +++ b/plugins/gs2.c @@ -1025,6 +1025,29 @@ gs2_save_cbindings(context_t *text, return SASL_OK; } +/* + * Request for (alternate) channel binding data + */ +static int +gs2_server_get_channel_binding(sasl_server_params_t* sparams, + const char *mech, + const char *cbindingname) +{ + sasl_server_channel_binding_t* binding = NULL; + void* context = NULL; + /* try to get alternate channel binding data */ + if (_sasl_getcallback(sparams->utils->conn, + SASL_CB_SERVER_CHANNEL_BINDING, + (sasl_callback_ft*)&binding, + &context) == SASL_OK) { + int ret = binding(sparams->utils->conn, + context, + mech, cbindingname); + return ret; + } + return SASL_BADPROT; +} + #define CHECK_REMAIN(n) do { if (remain < (n)) return SASL_BADPROT; } while (0) /* @@ -1073,6 +1096,13 @@ gs2_verify_initial_message(context_t *text, if (ret != SASL_OK) return ret; + if (sparams->cbinding == NULL || + strcmp(sparams->cbinding->name, text->cbindingname) != 0) { + /* Try to get desired channel binding data */ + scram_server_get_channel_binding(sparams, + text->plug.server->mech_name, + text->cbindingname); + } text->gs2_flags |= GS2_CB_FLAG_P; break; case 'n': diff --git a/plugins/scram.c b/plugins/scram.c index 83cf3df5..06a9e2dd 100644 --- a/plugins/scram.c +++ b/plugins/scram.c @@ -1155,7 +1155,27 @@ scram_server_mech_step1(server_context_t *text, } return result; } - + +static int +scram_server_get_channel_binding(sasl_server_params_t* sparams, + const char *mech, + const char *cbindingname) +{ + sasl_server_channel_binding_t* binding = NULL; + void* context = NULL; + /* try to get alternate channel binding data */ + if (_sasl_getcallback(sparams->utils->conn, + SASL_CB_SERVER_CHANNEL_BINDING, + (sasl_callback_ft*)&binding, + &context) == SASL_OK) { + int ret = binding(sparams->utils->conn, + context, + mech, cbindingname); + return ret; + } + return SASL_BADPROT; +} + static int scram_server_mech_step2(server_context_t *text, sasl_server_params_t *sparams, @@ -1281,6 +1301,14 @@ scram_server_mech_step2(server_context_t *text, goto cleanup; } + if (sparams->cbinding == NULL || + strcmp(sparams->cbinding->name, text->cbindingname) != 0) { + /* Try to get desired channel binding data */ + scram_server_get_channel_binding(sparams, + scram_sasl_mech, + text->cbindingname); + } + if (sparams->cbinding == NULL) { sparams->utils->seterror (sparams->utils->conn, 0,