mirror of
https://github.com/eclipse-mosquitto/mosquitto.git
synced 2026-05-21 12:24:29 +02:00
Replace mosquitto__cmp_hostname_wildcard with openssl equivalent
This commit is contained in:
parent
82256e14b0
commit
d61d25d488
|
|
@ -81,6 +81,8 @@ Broker:
|
|||
- Add support for systemd watchdog.
|
||||
- Remove support for TLS v1.1.
|
||||
- Allow seconds when defining persistent_client_expiration.
|
||||
- Use openssl provided function for x509 certificate hostname verification,
|
||||
rather than own function.
|
||||
|
||||
Plugins / plugin interface:
|
||||
- Add persist-sqlite plugin.
|
||||
|
|
@ -182,6 +184,8 @@ Client library:
|
|||
- Add `mosquitto_ext_auth_continue()` function to continue an MQTT v5 extended
|
||||
authentication.
|
||||
- Remove support for TLS v1.1.
|
||||
- Use openssl provided function for x509 certificate hostname verification,
|
||||
rather than own function.
|
||||
|
||||
Clients:
|
||||
- Add `-W` timeout support to Windows.
|
||||
|
|
|
|||
|
|
@ -908,6 +908,9 @@ int net__socket_connect_step3(struct mosquitto *mosq, const char *host)
|
|||
net__socket_close(mosq);
|
||||
return MOSQ_ERR_TLS;
|
||||
}
|
||||
if(tls__set_verify_hostname(mosq, host)){
|
||||
return MOSQ_ERR_TLS;
|
||||
}
|
||||
|
||||
if(net__socket_connect_tls(mosq)){
|
||||
net__socket_close(mosq);
|
||||
|
|
|
|||
148
lib/tls_mosq.c
148
lib/tls_mosq.c
|
|
@ -41,104 +41,30 @@ Contributors:
|
|||
#include "logging_mosq.h"
|
||||
#include "tls_mosq.h"
|
||||
|
||||
extern int tls_ex_index_mosq;
|
||||
|
||||
int mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx)
|
||||
{
|
||||
/* Preverify should have already checked expiry, revocation.
|
||||
* We need to verify the hostname. */
|
||||
struct mosquitto *mosq;
|
||||
SSL *ssl;
|
||||
X509 *cert;
|
||||
UNUSED(ctx);
|
||||
|
||||
/* Always reject if preverify_ok has failed. */
|
||||
if(!preverify_ok) return 0;
|
||||
|
||||
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
mosq = SSL_get_ex_data(ssl, tls_ex_index_mosq);
|
||||
if(!mosq) return 0;
|
||||
|
||||
if(mosq->tls_insecure == false
|
||||
#ifndef WITH_BROKER
|
||||
&& mosq->port != 0 /* no hostname checking for unix sockets */
|
||||
#endif
|
||||
){
|
||||
if(X509_STORE_CTX_get_error_depth(ctx) == 0){
|
||||
/* FIXME - use X509_check_host() etc. for sufficiently new openssl (>=1.1.x) */
|
||||
cert = X509_STORE_CTX_get_current_cert(ctx);
|
||||
/* This is the peer certificate, all others are upwards in the chain. */
|
||||
#if defined(WITH_BROKER)
|
||||
preverify_ok = mosquitto__verify_certificate_hostname(cert, mosq->bridge->addresses[mosq->bridge->cur_address].address);
|
||||
#else
|
||||
preverify_ok = mosquitto__verify_certificate_hostname(cert, mosq->host);
|
||||
#endif
|
||||
if (preverify_ok != 1) {
|
||||
log__printf(mosq, MOSQ_LOG_ERR, "Error: host name verification failed.");
|
||||
}
|
||||
return preverify_ok;
|
||||
}else{
|
||||
return preverify_ok;
|
||||
}
|
||||
}else{
|
||||
return preverify_ok;
|
||||
}
|
||||
return preverify_ok;
|
||||
}
|
||||
|
||||
static int mosquitto__cmp_hostname_wildcard(char *certname, const char *hostname)
|
||||
int tls__set_verify_hostname(struct mosquitto *mosq, const char *hostname)
|
||||
{
|
||||
size_t i;
|
||||
size_t len;
|
||||
|
||||
if(!certname || !hostname){
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(certname[0] == '*'){
|
||||
if(certname[1] != '.'){
|
||||
return 1;
|
||||
}
|
||||
certname += 2;
|
||||
len = strlen(hostname);
|
||||
for(i=0; i<len-1; i++){
|
||||
if(hostname[i] == '.'){
|
||||
hostname += i+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
len = strlen(hostname);
|
||||
int dotcount = 0;
|
||||
for(i=0; i<len-1; i++){
|
||||
if(hostname[i] == '.'){
|
||||
dotcount++;
|
||||
}
|
||||
}
|
||||
if(dotcount < 1){
|
||||
/* Exclude e.g. *.com, allow e.g. *.example.com */
|
||||
return 1;
|
||||
}
|
||||
return strcasecmp(certname, hostname);
|
||||
}else{
|
||||
return strcasecmp(certname, hostname);
|
||||
}
|
||||
}
|
||||
|
||||
/* This code is based heavily on the example provided in "Secure Programming
|
||||
* Cookbook for C and C++".
|
||||
*/
|
||||
int mosquitto__verify_certificate_hostname(X509 *cert, const char *hostname)
|
||||
{
|
||||
int i;
|
||||
char name[256];
|
||||
X509_NAME *subj;
|
||||
bool have_san_dns = false;
|
||||
STACK_OF(GENERAL_NAME) *san;
|
||||
const GENERAL_NAME *nval;
|
||||
const unsigned char *data;
|
||||
unsigned char ipv6_addr[16];
|
||||
unsigned char ipv4_addr[4];
|
||||
int ipv6_ok;
|
||||
int ipv4_ok;
|
||||
int rc;
|
||||
|
||||
if(mosq->tls_cafile == NULL && mosq->tls_capath == NULL){
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
#ifndef WITH_BROKER
|
||||
if(mosq->port == 0){
|
||||
/* No hostname verification for unix sockets */
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
ipv6_ok = InetPton(AF_INET6, hostname, &ipv6_addr);
|
||||
ipv4_ok = InetPton(AF_INET, hostname, &ipv4_addr);
|
||||
|
|
@ -147,46 +73,16 @@ int mosquitto__verify_certificate_hostname(X509 *cert, const char *hostname)
|
|||
ipv4_ok = inet_pton(AF_INET, hostname, &ipv4_addr);
|
||||
#endif
|
||||
|
||||
san = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
|
||||
if(san){
|
||||
for(i=0; i<sk_GENERAL_NAME_num(san); i++){
|
||||
nval = sk_GENERAL_NAME_value(san, i);
|
||||
if(nval->type == GEN_DNS){
|
||||
data = ASN1_STRING_get0_data(nval->d.dNSName);
|
||||
if(data && !mosquitto__cmp_hostname_wildcard((char *)data, hostname)){
|
||||
sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free);
|
||||
return 1;
|
||||
}
|
||||
have_san_dns = true;
|
||||
}else if(nval->type == GEN_IPADD){
|
||||
data = ASN1_STRING_get0_data(nval->d.iPAddress);
|
||||
if(nval->d.iPAddress->length == 4 && ipv4_ok){
|
||||
if(!memcmp(ipv4_addr, data, 4)){
|
||||
sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free);
|
||||
return 1;
|
||||
}
|
||||
}else if(nval->d.iPAddress->length == 16 && ipv6_ok){
|
||||
if(!memcmp(ipv6_addr, data, 16)){
|
||||
sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free);
|
||||
if(have_san_dns){
|
||||
/* Only check CN if subjectAltName DNS entry does not exist. */
|
||||
return 0;
|
||||
}
|
||||
X509_VERIFY_PARAM *param = SSL_get0_param(mosq->ssl);
|
||||
if(ipv4_ok || ipv6_ok){
|
||||
rc = X509_VERIFY_PARAM_set1_ip_asc(param, hostname);
|
||||
}else{
|
||||
rc = X509_VERIFY_PARAM_set1_host(param, hostname, 0);
|
||||
}
|
||||
|
||||
subj = X509_get_subject_name(cert);
|
||||
if(X509_NAME_get_text_by_NID(subj, NID_commonName, name, sizeof(name)) > 0){
|
||||
name[sizeof(name) - 1] = '\0';
|
||||
if (!mosquitto__cmp_hostname_wildcard(name, hostname)) return 1;
|
||||
if(rc == 1){
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}else{
|
||||
return MOSQ_ERR_TLS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,10 @@ Contributors:
|
|||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/engine.h>
|
||||
#include "mosquitto.h"
|
||||
|
||||
int mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx);
|
||||
int mosquitto__verify_certificate_hostname(X509 *cert, const char *hostname);
|
||||
int tls__set_verify_hostname(struct mosquitto *mosq, const char *hostname);
|
||||
|
||||
#endif /* WITH_TLS */
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ publish_packet = mosq_test.gen_publish("bridge/ssl/test", qos=0, payload="messag
|
|||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=f"{ssl_dir}/all-ca.crt")
|
||||
context.load_cert_chain(certfile=f"{ssl_dir}/server.crt", keyfile=f"{ssl_dir}/server.key")
|
||||
context.load_cert_chain(certfile=f"{ssl_dir}/server-san.crt", keyfile=f"{ssl_dir}/server-san.key")
|
||||
ssock = context.wrap_socket(sock, server_side=True)
|
||||
ssock.settimeout(20)
|
||||
ssock.bind(('', port1))
|
||||
|
|
|
|||
Loading…
Reference in a new issue