#include "sendf.h"
#include "http_negotiate.h"
#include "vauth/vauth.h"
+#include "vtls/vtls.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
neg_ctx->sslContext = conn->sslContext;
#endif
+ /* Check if the connection is using SSL and get the channel binding data */
+#ifdef HAVE_GSSAPI
+ if(conn->handler->flags & PROTOPT_SSL) {
+ Curl_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE);
+ result = Curl_ssl_get_channel_binding(
+ data, FIRSTSOCKET, &neg_ctx->channel_binding_data);
+ if(result) {
+ Curl_http_auth_cleanup_negotiate(conn);
+ return result;
+ }
+ }
+#endif
/* Initialize the security context and decode our challenge */
result = Curl_auth_decode_spnego_message(data, userp, passwdp, service,
host, header, neg_ctx);
+#ifdef HAVE_GSSAPI
+ Curl_dyn_free(&neg_ctx->channel_binding_data);
+#endif
+
if(result)
Curl_http_auth_cleanup_negotiate(conn);
gss_ctx_id_t context;
gss_name_t spn;
gss_buffer_desc output_token;
+ struct dynbuf channel_binding_data;
#else
#ifdef USE_WINDOWS_SSPI
#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ gss_channel_bindings_t chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
+ struct gss_channel_bindings_struct chan;
(void) user;
(void) password;
input_token.length = chlglen;
}
+ /* Set channel binding data if available */
+ if(nego->channel_binding_data.leng > 0) {
+ memset(&chan, 0, sizeof(struct gss_channel_bindings_struct));
+ chan.application_data.length = nego->channel_binding_data.leng;
+ chan.application_data.value = nego->channel_binding_data.bufr;
+ chan_bindings = &chan;
+ }
+
/* Generate our challenge-response message */
major_status = Curl_gss_init_sec_context(data,
&minor_status,
&nego->context,
nego->spn,
&Curl_spnego_mech_oid,
- GSS_C_NO_CHANNEL_BINDINGS,
+ chan_bindings,
&input_token,
&output_token,
TRUE,
NULL, /* disassociate_connection */
bearssl_recv, /* recv decrypted data */
bearssl_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif /* USE_BEARSSL */
NULL, /* disassociate_connection */
gtls_recv, /* recv decrypted data */
gtls_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif /* USE_GNUTLS */
NULL, /* disassociate_connection */
mbed_recv, /* recv decrypted data */
mbed_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif /* USE_MBEDTLS */
return nread;
}
+static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
+ struct dynbuf *binding)
+{
+ /* required for X509_get_signature_nid support */
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+ X509 *cert;
+ int algo_nid;
+ const EVP_MD *algo_type;
+ const char *algo_name;
+ unsigned int length;
+ unsigned char buf[EVP_MAX_MD_SIZE];
+
+ const char prefix[] = "tls-server-end-point:";
+ struct connectdata *conn = data->conn;
+ struct Curl_cfilter *cf = conn->cfilter[sockindex];
+ struct ossl_ctx *octx = NULL;
+
+ do {
+ const struct Curl_cftype *cft = cf->cft;
+ struct ssl_connect_data *connssl = cf->ctx;
+
+ if(cft->name && !strcmp(cft->name, "SSL")) {
+ octx = (struct ossl_ctx *)connssl->backend;
+ break;
+ }
+
+ if(cf->next)
+ cf = cf->next;
+
+ } while(cf->next);
+
+ if(!octx) {
+ failf(data,
+ "Failed to find SSL backend for endpoint");
+ return CURLE_SSL_ENGINE_INITFAILED;
+ }
+
+ cert = SSL_get1_peer_certificate(octx->ssl);
+ if(!cert) {
+ /* No server certificate, don't do channel binding */
+ return CURLE_OK;
+ }
+
+ if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) {
+ failf(data,
+ "Unable to find digest NID for certificate signature algorithm");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ /* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */
+ if(algo_nid == NID_md5 || algo_nid == NID_sha1) {
+ algo_type = EVP_sha256();
+ }
+ else {
+ algo_type = EVP_get_digestbynid(algo_nid);
+ if(!algo_type) {
+ algo_name = OBJ_nid2sn(algo_nid);
+ failf(data, "Could not find digest algorithm %s (NID %d)",
+ algo_name ? algo_name : "(null)", algo_nid);
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+ }
+
+ if(!X509_digest(cert, algo_type, buf, &length)) {
+ failf(data, "X509_digest() failed");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ /* Append "tls-server-end-point:" */
+ if(Curl_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
+ return CURLE_OUT_OF_MEMORY;
+ /* Append digest */
+ if(Curl_dyn_addn(binding, buf, length))
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+#else
+ /* No X509_get_signature_nid support */
+ (void)data; /* unused */
+ (void)sockindex; /* unused */
+ (void)binding; /* unused */
+ return CURLE_OK;
+#endif
+}
+
static size_t ossl_version(char *buffer, size_t size)
{
#ifdef LIBRESSL_VERSION_NUMBER
NULL, /* remote of data from this connection */
ossl_recv, /* recv decrypted data */
ossl_send, /* send data to encrypt */
+ ossl_get_channel_binding /* get_channel_binding */
};
#endif /* USE_OPENSSL */
NULL, /* disassociate_connection */
cr_recv, /* recv decrypted data */
cr_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif /* USE_RUSTLS */
NULL, /* disassociate_connection */
schannel_recv, /* recv decrypted data */
schannel_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif /* USE_SCHANNEL */
NULL, /* disassociate_connection */
sectransp_recv, /* recv decrypted data */
sectransp_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#ifdef __GNUC__
return CURLE_OK;
}
+CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
+ struct dynbuf *binding)
+{
+ if(Curl_ssl->get_channel_binding)
+ return Curl_ssl->get_channel_binding(data, sockindex, binding);
+ return CURLE_OK;
+}
+
void Curl_ssl_close_all(struct Curl_easy *data)
{
/* kill the session ID cache if not shared */
NULL, /* disassociate_connection */
multissl_recv_plain, /* recv decrypted data */
multissl_send_plain, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
const struct Curl_ssl *Curl_ssl =
bool Curl_ssl_false_start(struct Curl_easy *data);
+/* The maximum size of the SSL channel binding is 85 bytes, as defined in
+ * RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes long,
+ * and SHA-512 is the longest supported hash algorithm, with a digest length of
+ * 64 bytes.
+ * The maximum size of the channel binding is therefore 21 + 64 = 85 bytes.
+ */
+#define SSL_CB_MAX_SIZE 85
+
+/* Return the tls-server-end-point channel binding, including the
+ * 'tls-server-end-point:' prefix.
+ * If successful, the data is written to the dynbuf, and CURLE_OK is returned.
+ * The dynbuf MUST HAVE a minimum toobig size of SSL_CB_MAX_SIZE.
+ * If the dynbuf is too small, CURLE_OUT_OF_MEMORY is returned.
+ * If channel binding is not supported, binding stays empty and CURLE_OK is
+ * returned.
+ */
+CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
+ struct dynbuf *binding);
+
#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *mem, size_t len, CURLcode *code);
+ CURLcode (*get_channel_binding)(struct Curl_easy *data, int sockindex,
+ struct dynbuf *binding);
+
};
extern const struct Curl_ssl *Curl_ssl;
NULL, /* disassociate_connection */
wolfssl_recv, /* recv decrypted data */
wolfssl_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif