diff --git a/README.md b/README.md index babce45..8621dad 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,56 @@ 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 the password as the fourth parameter, or +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 Set the client "last will and testament", which will be sent on an unclean diff --git a/mosquitto.c b/mosquitto.c index 0f9a8bf..f170210 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" @@ -17,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 */ @@ -112,6 +114,131 @@ 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, 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!", + &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; + } + + 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); + + php_stat(ca_path, ca_path_len, FS_IS_DIR, stat TSRMLS_CC); + 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, pw_callback); + } else { + retval = mosquitto_tls_set(object->client, ca_path, NULL, cert_path, key_path, pw_callback); + } + + 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, "bs!s!", + &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, "s!s!|s!", + &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) { @@ -951,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) @@ -961,6 +1096,10 @@ const zend_function_entry mosquitto_client_methods[] = { PHP_ME(Mosquitto_Client, onUnsubscribe, Mosquitto_Client_callback_args, ZEND_ACC_PUBLIC) PHP_ME(Mosquitto_Client, onMessage, Mosquitto_Client_callback_args, 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, Mosquitto_Client_setCredentials_args, ZEND_ACC_PUBLIC) PHP_ME(Mosquitto_Client, setWill, Mosquitto_Client_setWill_args, ZEND_ACC_PUBLIC) PHP_ME(Mosquitto_Client, setReconnectDelay, Mosquitto_Client_setReconnectDelay_args, ZEND_ACC_PUBLIC) @@ -1024,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(); 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)