Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add public key pinning #1475

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/MQTTAsync.c
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options)
}
if (options->struct_version != 0 && options->ssl) /* check validity of SSL options structure */
{
if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version < 0 || options->ssl->struct_version > 5)
if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version < 0 || options->ssl->struct_version > 6)
{
rc = MQTTASYNC_BAD_STRUCTURE;
goto exit;
Expand Down Expand Up @@ -758,6 +758,11 @@ int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options)
if (m->c->sslopts->CApath)
free((void*)m->c->sslopts->CApath);
}
if(m->c->sslopts->struct_version >= 6)
{
if (m->c->sslopts->publicKey)
free((void*)m->c->sslopts->publicKey);
}
free((void*)m->c->sslopts);
m->c->sslopts = NULL;
}
Expand Down
11 changes: 9 additions & 2 deletions src/MQTTAsync.h
Original file line number Diff line number Diff line change
Expand Up @@ -1070,12 +1070,13 @@ typedef struct
/** The eyecatcher for this structure. Must be MQTS */
char struct_id[4];

/** The version number of this structure. Must be 0, 1, 2, 3, 4 or 5.
/** The version number of this structure. Must be 0, 1, 2, 3, 4, 5 or 6.
* 0 means no sslVersion
* 1 means no verify, CApath
* 2 means no ssl_error_context, ssl_error_cb
* 3 means no ssl_psk_cb, ssl_psk_context, disableDefaultTrustStore
* 4 means no protos, protos_len
* 6 means support public key pinning
*/
int struct_version;

Expand All @@ -1095,6 +1096,12 @@ typedef struct
/** The password to load the client's privateKey if encrypted. */
const char* privateKeyPassword;

/**
* This setting points to the file in PEM format containing the server's public key, can be used public key pinning
* Exists only if struct_version >= 6
* */
const char* publicKey;

/**
* The list of cipher suites that the client will present to the server during the SSL handshake. For a
* full explanation of the cipher list format, please see the OpenSSL on-line documentation:
Expand Down Expand Up @@ -1176,7 +1183,7 @@ typedef struct
unsigned int protos_len;
} MQTTAsync_SSLOptions;

#define MQTTAsync_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 }
#define MQTTAsync_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 6, NULL, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 }

/** Utility structure where name/value pairs are needed */
typedef struct
Expand Down
8 changes: 7 additions & 1 deletion src/MQTTClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,12 @@ typedef struct
/** The password to load the client's privateKey if encrypted. */
const char* privateKeyPassword;

/**
* This setting points to the file in PEM format containing the server's public key, can be used public key pinning
* Exists only if struct_version >= 6
* */
const char* publicKey;

/**
* The list of cipher suites that the client will present to the server during the SSL handshake. For a
* full explanation of the cipher list format, please see the OpenSSL on-line documentation:
Expand Down Expand Up @@ -779,7 +785,7 @@ typedef struct
unsigned int protos_len;
} MQTTClient_SSLOptions;

#define MQTTClient_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 }
#define MQTTClient_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 }

/**
* MQTTClient_libraryInfo is used to store details relating to the currently used
Expand Down
94 changes: 93 additions & 1 deletion src/SSLSocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
extern Sockets mod_s;

static int SSLSocket_error(char* aString, SSL* ssl, SOCKET sock, int rc, int (*cb)(const char *str, size_t len, void *u), void* u);
static int SSLSocket_certificate_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
char* SSL_get_verify_result_string(int rc);
void SSL_CTX_info_callback(const SSL* ssl, int where, int ret);
char* SSLSocket_get_version_string(int version);
Expand Down Expand Up @@ -78,6 +79,8 @@ static ssl_mutex_type sslCoreMutex;

/* Used to store MQTTClient_SSLOptions for TLS-PSK callback */
static int tls_ex_index_ssl_opts;
/* Used to store MQTTClient_SSLOptions for TLS Certificate verify callback */
static int tls_ex_index_ssl_opts_for_verify_cb;

#if defined(_WIN32) || defined(_WIN64)
#define iov_len len
Expand Down Expand Up @@ -122,6 +125,92 @@ static int SSLSocket_error(char* aString, SSL* ssl, SOCKET sock, int rc, int (*c
return error;
}

static int SSLSocket_certificate_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
int error = X509_STORE_CTX_get_error(x509_ctx);
FUNC_ENTRY;

int iVerifyOK = 0;

/* depth==0 server certificate */
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
Log(TRACE_MIN, -1, "preverify_ok=%d depth=%d", preverify_ok, depth);
if (depth == 0)
{
/* 1. Extracting the public key from a certificate. */
X509* cert = X509_STORE_CTX_get_current_cert(x509_ctx);
if (cert == NULL)
goto exit;

EVP_PKEY* pubkey_from_cert = X509_get_pubkey(cert);
if (pubkey_from_cert == NULL)
{
Log(TRACE_MIN, -1, "Error extracting public key from certificate");
goto exit;
}

/* 2. The public key from the configuration. */
SSL* ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
if (ssl == NULL)
{
Log(TRACE_MIN, -1, "Error SSL get_ex_data");
goto exit;
}
SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl);
if (ssl_ctx == NULL)
{
Log(TRACE_MIN, -1, "Error SSL_CTX get_ex_data");
goto exit;
}

MQTTClient_SSLOptions* opts = SSL_CTX_get_ex_data(ssl_ctx, tls_ex_index_ssl_opts_for_verify_cb);
if (opts == NULL)
{
Log(TRACE_MIN, -1, "Error opts get_ex_data");
goto exit;
}

if (opts->publicKey == NULL || strlen(opts->publicKey) <= 0 )
{
Log(TRACE_MIN, -1, "Error opts pubKey invalid");
goto exit;
}

FILE* pubkey_file = fopen(opts->publicKey, "r");
if (pubkey_file == NULL)
{
Log(TRACE_MIN, -1,"Error opening public key file");
goto exit;
}

EVP_PKEY* pubkey_from_file = PEM_read_PUBKEY(pubkey_file, NULL, NULL, NULL);
fclose(pubkey_file);
if (pubkey_from_file == NULL)
{
Log(TRACE_MIN, -1, "Error reading public key file");
goto exit;
}

// 3. compare
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) /* 3.0.0 and later */
if (EVP_PKEY_eq(pubkey_from_file, pubkey_from_cert) == 1)
#else
if (EVP_PKEY_cmp(pubkey_from_file, pubkey_from_cert) == 1)
#endif
iVerifyOK = 1;

// 4. cleanup
EVP_PKEY_free(pubkey_from_cert);
EVP_PKEY_free(pubkey_from_file);
}
else
iVerifyOK = preverify_ok;

exit:
FUNC_EXIT_RC(error);
return iVerifyOK;
}

static struct
{
int code;
Expand Down Expand Up @@ -490,6 +579,7 @@ int SSLSocket_initialize(void)
SSL_create_mutex(&sslCoreMutex);

tls_ex_index_ssl_opts = SSL_get_ex_new_index(0, "paho ssl options", NULL, NULL, NULL);
tls_ex_index_ssl_opts_for_verify_cb = SSL_get_ex_new_index(0, "paho ssl options", NULL, NULL, NULL);

exit:
FUNC_EXIT_RC(rc);
Expand Down Expand Up @@ -675,6 +765,8 @@ int SSLSocket_createContext(networkHandles* net, MQTTClient_SSLOptions* opts)
SSL_CTX_set_psk_client_callback(net->ctx, call_ssl_psk_cb);
}
#endif
if (opts->publicKey !=NULL )
SSL_CTX_set_ex_data(net->ctx, tls_ex_index_ssl_opts_for_verify_cb, opts);

#if (OPENSSL_VERSION_NUMBER >= 0x010002000) /* 1.0.2 and later */
if (opts->protos != NULL && opts->protos_len > 0)
Expand Down Expand Up @@ -722,7 +814,7 @@ int SSLSocket_setSocketForSSL(networkHandles* net, MQTTClient_SSLOptions* opts,
SSL_CTX_set_info_callback(net->ctx, SSL_CTX_info_callback);
SSL_CTX_set_msg_callback(net->ctx, SSL_CTX_msg_callback);
if (opts->enableServerCertAuth)
SSL_CTX_set_verify(net->ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify(net->ctx, SSL_VERIFY_PEER, opts->publicKey != NULL ? SSLSocket_certificate_verify_cb : NULL);

net->ssl = SSL_new(net->ctx);

Expand Down