From eb1bec60967de61a2e2c7d8a8d902654a3c159a3 Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Fri, 1 Nov 2013 22:26:52 +0000 Subject: [PATCH 01/10] First attempt at setTlsCertificates --- mosquitto.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/mosquitto.c b/mosquitto.c index b1fb247..c16849d 100644 --- a/mosquitto.c +++ b/mosquitto.c @@ -42,6 +42,43 @@ PHP_METHOD(Mosquitto_Client, __construct) } /* }}} */ +/* {{{ Mosquitto\Client::setTlsCertificates() */ +PHP_METHOD(Mosquitto_Client, setTlsCertificates) +{ + mosquitto_client_object *object; + char *ca_path = NULL, *cert_path = NULL, *key_path = NULL, *key_pw = NULL; + int ca_path_len = 0, cert_path_len = 0, key_path_len = 0, key_pw_len; + zval *stat; + zend_bool is_dir = 0; + + PHP_MOSQUITTO_ERROR_HANDLING(); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss", + &ca_path, &ca_path_len, + &cert_path, &cert_path_len, + &key_path, &key_path_len, + &key_pw, &key_pw_len) == FAILURE) { + PHP_MOSQUITTO_RESTORE_ERRORS(); + return; + } + PHP_MOSQUITTO_RESTORE_ERRORS(); + + object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + + php_stat(ca_path, ca_path_len, FS_IS_DIR, stat); + is_dir = Z_BVAL_P(stat); + zval_dtor(stat); + FREE_ZVAL(stat); + + if (is_dir) { + mosquitto_tls_set(object->client, NULL, ca_path, cert_path, key_path, NULL); + } else { + mosquitto_tls_set(object->client, ca_path, NULL, cert_path, key_path, NULL); + } + + php_mosquitto_handle_errno(retval, errno TSRMLS_CC); +} +/* }}} */ + /* {{{ Mosquitto\Client::setCredentials() */ PHP_METHOD(Mosquitto_Client, setCredentials) { From d1a4dbd50474396e8e8335a6062fba27a9d48ca7 Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Mon, 4 Nov 2013 21:48:17 +0000 Subject: [PATCH 02/10] Initial commit of TLS functions --- mosquitto.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/mosquitto.c b/mosquitto.c index 9d80c64..b43f930 100644 --- a/mosquitto.c +++ b/mosquitto.c @@ -7,6 +7,7 @@ #include "zend_variables.h" #include "zend_exceptions.h" #include "zend_API.h" +#include "ext/standard/php_filestat.h" #include "ext/standard/info.h" #include "php_mosquitto.h" @@ -48,7 +49,7 @@ PHP_METHOD(Mosquitto_Client, setTlsCertificates) { mosquitto_client_object *object; char *ca_path = NULL, *cert_path = NULL, *key_path = NULL, *key_pw = NULL; - int ca_path_len = 0, cert_path_len = 0, key_path_len = 0, key_pw_len; + int ca_path_len = 0, cert_path_len = 0, key_path_len = 0, key_pw_len, retval = 0; zval *stat; zend_bool is_dir = 0; @@ -65,21 +66,94 @@ PHP_METHOD(Mosquitto_Client, setTlsCertificates) object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC); - php_stat(ca_path, ca_path_len, FS_IS_DIR, stat); + php_stat(ca_path, ca_path_len, FS_IS_DIR, stat TSRMLS_CC); is_dir = Z_BVAL_P(stat); zval_dtor(stat); FREE_ZVAL(stat); if (is_dir) { - mosquitto_tls_set(object->client, NULL, ca_path, cert_path, key_path, NULL); + retval = mosquitto_tls_set(object->client, NULL, ca_path, cert_path, key_path, NULL); } else { - mosquitto_tls_set(object->client, ca_path, NULL, cert_path, key_path, NULL); + retval = mosquitto_tls_set(object->client, ca_path, NULL, cert_path, key_path, NULL); } php_mosquitto_handle_errno(retval, errno TSRMLS_CC); } /* }}} */ +/* {{{ Mosquitto\Client::setTlsInsecure() */ +PHP_METHOD(Mosquitto_Client, setTlsInsecure) +{ + mosquitto_client_object *object; + zend_bool value = 0; + int retval = 0; + + PHP_MOSQUITTO_ERROR_HANDLING(); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &value) == FAILURE) { + PHP_MOSQUITTO_RESTORE_ERRORS(); + return; + } + PHP_MOSQUITTO_RESTORE_ERRORS(); + + object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + + retval = mosquitto_tls_insecure_set(object->client, value); + + php_mosquitto_handle_errno(retval, errno TSRMLS_CC); +} +/* }}} */ + +/* {{{ Mosquitto\Client::setTlsOptions() */ +PHP_METHOD(Mosquitto_Client, setTlsOptions) +{ + mosquitto_client_object *object; + char *tls_version = NULL, *ciphers = NULL; + int tls_version_len = 0, ciphers_len = 0, retval = 0; + zend_bool verify_peer = 0; + + PHP_MOSQUITTO_ERROR_HANDLING(); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "bss", + &verify_peer, + &tls_version, &tls_version_len, + &ciphers, &ciphers_len + ) == FAILURE) { + PHP_MOSQUITTO_RESTORE_ERRORS(); + return; + } + PHP_MOSQUITTO_RESTORE_ERRORS(); + + object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + + retval = mosquitto_tls_opts_set(object->client, verify_peer, tls_version, ciphers); + + php_mosquitto_handle_errno(retval, errno TSRMLS_CC); +} +/* }}} */ + +/* {{{ Mosquitto\Client::setTlsPSK() */ +PHP_METHOD(Mosquitto_Client, setTlsPSK) +{ + mosquitto_client_object *object; + char *psk = NULL, *identity = NULL, *ciphers = NULL; + int psk_len = 0, identity_len = 0, ciphers_len = 0, retval = 0; + + PHP_MOSQUITTO_ERROR_HANDLING(); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "bss", + &psk, &psk_len, &identity, &identity_len, &ciphers, &ciphers_len + ) == FAILURE) { + PHP_MOSQUITTO_RESTORE_ERRORS(); + return; + } + PHP_MOSQUITTO_RESTORE_ERRORS(); + + object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + + retval = mosquitto_tls_psk_set(object->client, psk, identity, ciphers); + + php_mosquitto_handle_errno(retval, errno TSRMLS_CC); +} +/* }}} */ + /* {{{ Mosquitto\Client::setCredentials() */ PHP_METHOD(Mosquitto_Client, setCredentials) { @@ -943,6 +1017,10 @@ const zend_function_entry mosquitto_client_methods[] = { PHP_ME(Mosquitto_Client, onUnsubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(Mosquitto_Client, onMessage, NULL, ZEND_ACC_PUBLIC) PHP_ME(Mosquitto_Client, getSocket, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Mosquitto_Client, setTlsCertificates, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Mosquitto_Client, setTlsInsecure, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Mosquitto_Client, setTlsOptions, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Mosquitto_Client, setTlsPSK, NULL, ZEND_ACC_PUBLIC) PHP_ME(Mosquitto_Client, setCredentials, NULL, ZEND_ACC_PUBLIC) PHP_ME(Mosquitto_Client, setWill, NULL, ZEND_ACC_PUBLIC) PHP_ME(Mosquitto_Client, clearWill, NULL, ZEND_ACC_PUBLIC) From 8446d5154af83c8978b9814fe838496aad16778e Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Mon, 4 Nov 2013 21:48:28 +0000 Subject: [PATCH 03/10] Start adding documentation to README --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 9a97d18..75740a2 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,12 @@ This is the actual Mosquitto client. 1. [__construct](#__construct) - create a new client 1. [setCredentials](#setcredentials) - set the credentials to use on connection +1. [setTlsCertificates](#settlscertificates) - set the TLS certificate sources +1. [setTlsInsecure](#settlsinsecure) - Set verification of the server hostname + in TLS certificates +1. [setTlsOptions](#settlsoptions) - Set advanced TLS options +1. [setTlsPSK](#settlspsk) - Configure the client for pre-shared-key based TLS + support. 1. [setWill](#setwill) - set the client will, to be delivered if disconnected uncleanly 1. [clearWill](#clearwill) - clear a previously-set will @@ -83,6 +89,21 @@ called before connect(). | Username | string | Username to supply to the broker | | Password | string | Password to supply to the broker | +#### setTlsCertificates + +Configure the client for certificate based SSL/TLS support. Must be called +before connect(). Cannot be used in conjunction with setTlsPSK(). + +Define the Certificate Authority certificates to be trusted (ie. the server +certificate must be signed with one of these certificates) using cafile. +If the server you are connecting to requires clients to provide a certificate, +define certfile and keyfile with your client certificate and private key. If +your private key is encrypted, provide a password callback function or you will +have to enter the password at the command line. + +| Parameter | Type | Description | +| --- | --- | ---- | + #### setWill Set the client "last will and testament", which will be sent on an unclean From fbabab8c5491917cb753820a5d58cdec5d7dafb7 Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Wed, 4 Dec 2013 20:12:32 +0000 Subject: [PATCH 04/10] Actually assign the parameter to the array --- mosquitto.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mosquitto.c b/mosquitto.c index a104fbe..9b07ee3 100644 --- a/mosquitto.c +++ b/mosquitto.c @@ -886,6 +886,7 @@ PHP_MOSQUITTO_API void php_mosquitto_disconnect_callback(struct mosquitto *mosq, MAKE_STD_ZVAL(rc_zval); ZVAL_LONG(rc_zval, rc); + params[0] = &rc_zval; object->disconnect_callback.params = params; object->disconnect_callback.param_count = 1; From 9a9bdb5a27292d10f0e2b8f66339ad9d15dd6437 Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Wed, 4 Dec 2013 22:43:50 +0000 Subject: [PATCH 05/10] Handle open_basedir checks --- mosquitto.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mosquitto.c b/mosquitto.c index 9b07ee3..88c404e 100644 --- a/mosquitto.c +++ b/mosquitto.c @@ -122,7 +122,7 @@ PHP_METHOD(Mosquitto_Client, setTlsCertificates) zend_bool is_dir = 0; PHP_MOSQUITTO_ERROR_HANDLING(); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss", + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!|s!s!s!", &ca_path, &ca_path_len, &cert_path, &cert_path_len, &key_path, &key_path_len, @@ -130,6 +130,15 @@ PHP_METHOD(Mosquitto_Client, setTlsCertificates) PHP_MOSQUITTO_RESTORE_ERRORS(); return; } + + if ((php_check_open_basedir(ca_path TSRMLS_CC) < 0) || + (php_check_open_basedir(cert_path TSRMLS_CC) < 0) || + (php_check_open_basedir(key_path TSRMLS_CC) < 0)) + { + PHP_MOSQUITTO_RESTORE_ERRORS(); + return; + } + PHP_MOSQUITTO_RESTORE_ERRORS(); object = (mosquitto_client_object *) zend_object_store_get_object(getThis() TSRMLS_CC); @@ -137,7 +146,6 @@ PHP_METHOD(Mosquitto_Client, setTlsCertificates) php_stat(ca_path, ca_path_len, FS_IS_DIR, stat TSRMLS_CC); is_dir = Z_BVAL_P(stat); zval_dtor(stat); - FREE_ZVAL(stat); if (is_dir) { retval = mosquitto_tls_set(object->client, NULL, ca_path, cert_path, key_path, NULL); From 35d477fdbca3bbeb0452c64e7615d9a3cf8d8975 Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Wed, 4 Dec 2013 23:32:13 +0000 Subject: [PATCH 06/10] Add callback to handle encrypted client keys --- mosquitto.c | 20 ++++++++++++++++++-- php_mosquitto.h | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/mosquitto.c b/mosquitto.c index 2df650c..5654715 100644 --- a/mosquitto.c +++ b/mosquitto.c @@ -18,6 +18,7 @@ zend_object_handlers mosquitto_std_object_handlers; ZEND_DECLARE_MODULE_GLOBALS(mosquitto) static inline mosquitto_client_object *mosquitto_client_object_get(zval *zobj TSRMLS_DC); +static int php_mosquitto_pw_callback(char *buf, int size, int rwflag, void *userdata); /* {{{ Arginfo */ @@ -121,6 +122,7 @@ PHP_METHOD(Mosquitto_Client, setTlsCertificates) int ca_path_len = 0, cert_path_len = 0, key_path_len = 0, key_pw_len, retval = 0; zval *stat; zend_bool is_dir = 0; + int (*pw_callback)(char *, int, int, void *) = NULL; PHP_MOSQUITTO_ERROR_HANDLING(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!|s!s!s!", @@ -148,10 +150,16 @@ PHP_METHOD(Mosquitto_Client, setTlsCertificates) is_dir = Z_BVAL_P(stat); zval_dtor(stat); + if (key_pw != NULL) { + pw_callback = php_mosquitto_pw_callback; + MQTTG(client_key) = estrdup(key_pw); + MQTTG(client_key_len) = key_pw_len; + } + if (is_dir) { - retval = mosquitto_tls_set(object->client, NULL, ca_path, cert_path, key_path, NULL); + retval = mosquitto_tls_set(object->client, NULL, ca_path, cert_path, key_path, pw_callback); } else { - retval = mosquitto_tls_set(object->client, ca_path, NULL, cert_path, key_path, NULL); + retval = mosquitto_tls_set(object->client, ca_path, NULL, cert_path, key_path, pw_callback); } php_mosquitto_handle_errno(retval, errno TSRMLS_CC); @@ -1070,6 +1078,14 @@ PHP_MOSQUITTO_API void php_mosquitto_unsubscribe_callback(struct mosquitto *mosq } } +static int php_mosquitto_pw_callback(char *buf, int size, int rwflag, void *userdata) { + TSRMLS_FETCH(); + + strncpy(buf, MQTTG(client_key), size); + efree(MQTTG(client_key)); + return MQTTG(client_key_len); +} + /* {{{ mosquitto_client_methods */ const zend_function_entry mosquitto_client_methods[] = { PHP_ME(Mosquitto_Client, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) diff --git a/php_mosquitto.h b/php_mosquitto.h index 532a06c..ee5634f 100644 --- a/php_mosquitto.h +++ b/php_mosquitto.h @@ -128,6 +128,7 @@ static int php_mosquitto_message_write_##name(mosquitto_message_object *mosquitt ZEND_BEGIN_MODULE_GLOBALS(mosquitto) char *client_key; + int client_key_len; zend_object_handlers mosquitto_std_object_handlers; zend_error_handling mosquitto_original_error_handling; ZEND_END_MODULE_GLOBALS(mosquitto) From fddbc19eb97372589426fe89ddd23816873cced5 Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Wed, 4 Dec 2013 23:42:47 +0000 Subject: [PATCH 07/10] Fix argument types for setTlsPSK --- mosquitto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mosquitto.c b/mosquitto.c index 5654715..99712e2 100644 --- a/mosquitto.c +++ b/mosquitto.c @@ -197,7 +197,7 @@ PHP_METHOD(Mosquitto_Client, setTlsOptions) zend_bool verify_peer = 0; PHP_MOSQUITTO_ERROR_HANDLING(); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "bss", + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "bs!s!", &verify_peer, &tls_version, &tls_version_len, &ciphers, &ciphers_len @@ -223,7 +223,7 @@ PHP_METHOD(Mosquitto_Client, setTlsPSK) int psk_len = 0, identity_len = 0, ciphers_len = 0, retval = 0; PHP_MOSQUITTO_ERROR_HANDLING(); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "bss", + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!s!|s!", &psk, &psk_len, &identity, &identity_len, &ciphers, &ciphers_len ) == FAILURE) { PHP_MOSQUITTO_RESTORE_ERRORS(); From f448675ebd56ea7500f88823721dd29429d17dfe Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Wed, 4 Dec 2013 23:43:28 +0000 Subject: [PATCH 08/10] Describe how to handle encrypted certificates --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b09dbe7..cface34 100644 --- a/README.md +++ b/README.md @@ -98,8 +98,8 @@ Define the Certificate Authority certificates to be trusted (ie. the server certificate must be signed with one of these certificates) using cafile. If the server you are connecting to requires clients to provide a certificate, define certfile and keyfile with your client certificate and private key. If -your private key is encrypted, provide a password callback function or you will -have to enter the password at the command line. +your private key is encrypted, provide the password as the fourth parameter, or +you will have to enter the password at the command line. | Parameter | Type | Description | | --- | --- | ---- | From d3f7a17c49193fac38877670f7abf8ab7857cad0 Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Thu, 5 Dec 2013 21:23:11 +0000 Subject: [PATCH 09/10] Add SSL-related constants --- mosquitto.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/mosquitto.c b/mosquitto.c index 99712e2..f170210 100644 --- a/mosquitto.c +++ b/mosquitto.c @@ -1163,15 +1163,18 @@ PHP_MINIT_FUNCTION(mosquitto) mosquitto_ce_exception = zend_register_internal_class_ex(&exception_ce, zend_exception_get_default(TSRMLS_C), "Exception" TSRMLS_CC); - #define REGISTER_MOSQUITTO_LOG_LONG_CONST(const_name, value) \ + #define REGISTER_MOSQUITTO_LONG_CONST(const_name, value) \ zend_declare_class_constant_long(mosquitto_ce_client, const_name, sizeof(const_name)-1, (long)value TSRMLS_CC); \ REGISTER_LONG_CONSTANT(#value, value, CONST_CS | CONST_PERSISTENT); - REGISTER_MOSQUITTO_LOG_LONG_CONST("LOG_INFO", MOSQ_LOG_INFO); - REGISTER_MOSQUITTO_LOG_LONG_CONST("LOG_NOTICE", MOSQ_LOG_NOTICE); - REGISTER_MOSQUITTO_LOG_LONG_CONST("LOG_WARNING", MOSQ_LOG_WARNING); - REGISTER_MOSQUITTO_LOG_LONG_CONST("LOG_ERR", MOSQ_LOG_ERR); - REGISTER_MOSQUITTO_LOG_LONG_CONST("LOG_DEBUG", MOSQ_LOG_DEBUG); + REGISTER_MOSQUITTO_LONG_CONST("LOG_INFO", MOSQ_LOG_INFO); + REGISTER_MOSQUITTO_LONG_CONST("LOG_NOTICE", MOSQ_LOG_NOTICE); + REGISTER_MOSQUITTO_LONG_CONST("LOG_WARNING", MOSQ_LOG_WARNING); + REGISTER_MOSQUITTO_LONG_CONST("LOG_ERR", MOSQ_LOG_ERR); + REGISTER_MOSQUITTO_LONG_CONST("LOG_DEBUG", MOSQ_LOG_DEBUG); + + REGISTER_MOSQUITTO_LONG_CONST("SSL_VERIFY_NONE", 0); + REGISTER_MOSQUITTO_LONG_CONST("SSL_VERIFY_PEER", 1); mosquitto_lib_init(); From 74647c8ee3f13b94821690e70448f4a17fd11cc9 Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Thu, 5 Dec 2013 21:23:28 +0000 Subject: [PATCH 10/10] Document SSL options --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 585eb6d..8621dad 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,41 @@ you will have to enter the password at the command line. | Parameter | Type | Description | | --- | --- | ---- | +| capath | string | Path to the PEM encoded trusted CA certificate files, or to a directory containing them | +| certfile | string | Path to the PEM encoded certificate file for this client. Optional. | +| keyfile | string | Path to a file containing the PEM encoded private key for this client. Required if certfile is set. | +| password | string | The password for the keyfile, if it is encrypted. If null, the password will be asked for on the command line. | + +#### setTlsInsecure + +Configure verification of the server hostname in the server certificate. If +value is set to true, it is impossible to guarantee that the host you are +connecting to is not impersonating your server. Do not use this function in +a real system. Must be called before connect(). + +| Parameter | Type | Description | +| --- | --- | ---- | +| value | boolean | If set to false, the default, certificate hostname checking is performed. If set to true, no hostname checking is performed and the connection is insecure. | + +#### setTlsOptions + +Set advanced SSL/TLS options. Must be called before connect(). + +| Parameter | Type | Description | +| --- | --- | ---- | +| certReqs | int | Whether or not to verify the server. Can be Mosquitto\Client::SSL_VERIFY_NONE, to disable certificate verification, or Mosquitto\Client::SSL_VERIFY_PEER (the default), to verify the server certificate. | +| tlsVersion | string | The TLS version to use. If NULL, a default is used. The default value depends on the version of OpenSSL the library was compiled against. Available options on OpenSSL >= 1.0.1 are 'tlsv1.2', 'tlsv1.1' and 'tlsv1'. | +| cipers | string | A string describing the ciphers available for use. See the `openssl ciphers` tool for more information. If NULL, the default set will be used. | + +#### setTlsPSK + +Configure the client for pre-shared-key based TLS support. Must be called before connect(). Cannot be used in conjunction with setTlsCertificates. + +| Parameter | Type | Description | +| --- | --- | ---- | +| psk | string | The pre-shared key in hex format with no leading "0x". +| identity | string " The identity of this client. May be used as the username depending on server settings. | +| cipers | string | A string describing the ciphers available for use. See the `openssl ciphers` tool for more information. If NULL, the default set will be used. | #### setWill