diff --git a/include/re_http.h b/include/re_http.h index 2789442cd..7155b0197 100644 --- a/include/re_http.h +++ b/include/re_http.h @@ -125,6 +125,9 @@ typedef void (http_conn_h)(struct tcp_conn *tc, struct tls_conn *sc, void *arg); int http_client_alloc(struct http_cli **clip, struct dnsc *dnsc); +int http_client_add_ca(struct http_cli *cli, const char *tls_ca); +int http_client_set_tls_hostname(struct http_cli *cli, + const struct pl *hostname); int http_request(struct http_req **reqp, struct http_cli *cli, const char *met, const char *uri, http_resp_h *resph, http_data_h *datah, void *arg, const char *fmt, ...); diff --git a/include/re_tls.h b/include/re_tls.h index dfd2900dc..13924bdf0 100644 --- a/include/re_tls.h +++ b/include/re_tls.h @@ -49,6 +49,9 @@ int tls_fingerprint(const struct tls *tls, enum tls_fingerprint type, int tls_peer_fingerprint(const struct tls_conn *tc, enum tls_fingerprint type, uint8_t *md, size_t size); int tls_peer_common_name(const struct tls_conn *tc, char *cn, size_t size); +int tls_peer_set_verify_host(struct tls_conn *tc, const char *hostname); +int tls_set_verify_purpose(struct tls *tls, const char *purpose); +int tls_set_hostname(char *tls_hostname, const struct pl *hostname); int tls_peer_verify(const struct tls_conn *tc); int tls_srtp_keyinfo(const struct tls_conn *tc, enum srtp_suite *suite, uint8_t *cli_key, size_t cli_key_size, diff --git a/src/http/client.c b/src/http/client.c index 39afa39cb..8116eef9e 100644 --- a/src/http/client.c +++ b/src/http/client.c @@ -35,6 +35,7 @@ struct http_cli { struct hash *ht_conn; struct dnsc *dnsc; struct tls *tls; + char *tls_hostname; }; struct conn; @@ -97,6 +98,7 @@ static void cli_destructor(void *arg) mem_deref(cli->ht_conn); mem_deref(cli->dnsc); mem_deref(cli->tls); + mem_deref(cli->tls_hostname); } @@ -442,6 +444,17 @@ static int conn_connect(struct http_req *req) err = tls_start_tcp(&conn->sc, req->cli->tls, conn->tc, 0); if (err) goto out; + + if (req->cli->tls_hostname) + err = tls_peer_set_verify_host(conn->sc, + req->cli->tls_hostname); + + if (err) + goto out; + + err = tls_set_servername(conn->sc, req->host); + if (err) + goto out; } #endif @@ -683,11 +696,15 @@ int http_client_alloc(struct http_cli **clip, struct dnsc *dnsc) #ifdef USE_TLS err = tls_alloc(&cli->tls, TLS_METHOD_SSLV23, NULL, NULL); + if (err) + goto out; + + err = tls_set_verify_purpose(cli->tls, "sslserver"); + if (err) + goto out; #else err = 0; #endif - if (err) - goto out; cli->dnsc = mem_ref(dnsc); @@ -699,3 +716,40 @@ int http_client_alloc(struct http_cli **clip, struct dnsc *dnsc) return err; } + + +#ifdef USE_TLS +/** + * Add trusted CA certificates + * + * @param cli HTTP client + * @param capath Path to CA certificates + * + * @return 0 if success, otherwise errorcode + */ +int http_client_add_ca(struct http_cli *cli, const char *tls_ca) +{ + if (!cli || !tls_ca) + return EINVAL; + + return tls_add_ca(cli->tls, tls_ca); +} + + +/** + * Set verify host name + * + * @param cli HTTP client + * @param hostname String for alternative name validation. + * + * @return 0 if success, otherwise errorcode + */ +int http_client_set_tls_hostname(struct http_cli *cli, + const struct pl *hostname) +{ + if (!cli || !hostname) + return EINVAL; + + return tls_set_hostname(cli->tls_hostname, hostname); +} +#endif diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index 662fe99ab..60c026480 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -229,6 +229,90 @@ int tls_add_ca(struct tls *tls, const char *cafile) } +/** + * Set SSL verification of the certificate purpose + * + * @param tls TLS Context + * @param purpose Certificate purpose as string + * + * @return int 0 if success, errorcode otherwise + */ +int tls_set_verify_purpose(struct tls *tls, const char *purpose) +{ + int err; + int i; + X509_PURPOSE *xptmp; + + if (!tls || !purpose) + return EINVAL; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + i = X509_PURPOSE_get_by_sname(purpose); +#else + i = X509_PURPOSE_get_by_sname((char *) purpose); +#endif + + if (i < 0) + return EINVAL; + + /* purpose index -> purpose object */ + /* purpose object -> purpose value */ + xptmp = X509_PURPOSE_get0(i); + i = X509_PURPOSE_get_id(xptmp); + err = SSL_CTX_set_purpose(tls->ctx, i); + + return err == 1 ? 0 : EINVAL; +} + + +/** + * Set SSL verification of hostname + * + * @param tc TLS Connection + * @param hostname Certificate hostname + * + * @return int 0 if success, errorcode otherwise + */ +int tls_peer_set_verify_host(struct tls_conn *tc, const char *hostname) +{ + int err = 0; + + if (!tc) + return EINVAL; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + err = SSL_set1_host(tc->ssl, hostname); +#else + DEBUG_WARNING("verify hostname needs openssl version 1.1.0\n"); + return ENOSYS; +#endif + + return err == 1 ? 0 : EINVAL; +} + + +/** + * Convert string hostname to pl hostname + * + * @param tls_hostname Certificate hostname as string + * @param hostname Certificate hostname as pl + * + * @return int 0 if success, errorcode otherwise + */ +int tls_set_hostname(char *tls_hostname, const struct pl *hostname) +{ + if (!tls_hostname || !hostname) + return EINVAL; + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + DEBUG_WARNING("verify hostname needs openssl version 1.1.0\n"); + return ENOSYS; +#endif + + return pl_strdup(&tls_hostname, hostname); +} + + /** * Generate and set selfsigned certificate on TLS context *