1 /*
2  * Copyright (c) 2021 One Identity
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 as published
6  * by the Free Software Foundation, or (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16  *
17  * As an additional exemption you are allowed to compile & link against the
18  * OpenSSL libraries as published by the OpenSSL project. See the file
19  * COPYING for details.
20  *
21  */
22 
23 #include "mqtt-options.h"
24 #include "syslog-ng.h"
25 
26 #include <MQTTClient.h>
27 #include <string.h>
28 #include <stddef.h>
29 
30 
31 #define DEFAULT_ADDRESS "tcp://localhost:1883"
32 #define DEFAULT_KEEPALIVE 60
33 #define DEFAULT_QOS 0
34 
35 
36 
37 static gboolean
_validate_protocol(const gchar * address)38 _validate_protocol(const gchar *address)
39 {
40   const gchar *valid_type[] = {"tcp", "ssl", "ws", "wss"};
41   gint i;
42 
43   for (i = 0; i < G_N_ELEMENTS(valid_type); ++i)
44     if (strncmp(valid_type[i], address, strlen(valid_type[i])) == 0)
45       return TRUE;
46 
47   return FALSE;
48 }
49 
50 static gboolean
_validate_address(const gchar * address)51 _validate_address(const gchar *address)
52 {
53   if (strstr(address, "://") == NULL)
54     return FALSE;
55 
56   if (!_validate_protocol(address))
57     return FALSE;
58 
59   return TRUE;
60 }
61 
62 void
mqtt_client_options_defaults(MQTTClientOptions * self)63 mqtt_client_options_defaults(MQTTClientOptions *self)
64 {
65   self->address = g_strdup(DEFAULT_ADDRESS);
66   self->keepalive = DEFAULT_KEEPALIVE;
67   self->qos = DEFAULT_QOS;
68 
69   self->ssl_version = MQTT_SSL_VERSION_DEFAULT;
70   self->peer_verify = TRUE;
71   self->use_system_cert_store = FALSE;
72 }
73 
74 void
mqtt_client_options_destroy(MQTTClientOptions * self)75 mqtt_client_options_destroy(MQTTClientOptions *self)
76 {
77   g_free(self->address);
78   g_free(self->client_id);
79 
80   g_free(self->username);
81   g_free(self->password);
82   g_free(self->http_proxy);
83 
84   g_free(self->ca_dir);
85   g_free(self->ca_file);
86   g_free(self->cert_file);
87   g_free(self->key_file);
88   g_free(self->ciphers);
89 }
90 
91 void
mqtt_client_options_set_keepalive(MQTTClientOptions * self,const gint keepalive)92 mqtt_client_options_set_keepalive(MQTTClientOptions *self, const gint keepalive)
93 {
94   self->keepalive = keepalive;
95 }
96 
97 gboolean
mqtt_client_options_set_address(MQTTClientOptions * self,const gchar * address)98 mqtt_client_options_set_address(MQTTClientOptions *self, const gchar *address)
99 {
100   if (!_validate_address(address))
101     return FALSE;
102 
103   g_free(self->address);
104   self->address = g_strdup(address);
105   return TRUE;
106 }
107 
108 void
mqtt_client_options_set_qos(MQTTClientOptions * self,const gint qos)109 mqtt_client_options_set_qos (MQTTClientOptions *self, const gint qos)
110 {
111   self->qos = qos;
112 }
113 
114 gboolean
mqtt_client_options_set_client_id(MQTTClientOptions * self,const gchar * client_id)115 mqtt_client_options_set_client_id(MQTTClientOptions *self, const gchar *client_id)
116 {
117   if(strcmp("", client_id) == 0)
118     return FALSE;
119 
120   g_free(self->client_id);
121   self->client_id = g_strdup(client_id);
122 
123   return TRUE;
124 }
125 
126 void
mqtt_client_options_set_cleansession(MQTTClientOptions * self,gboolean cleansession)127 mqtt_client_options_set_cleansession(MQTTClientOptions *self, gboolean cleansession)
128 {
129   self->cleansession = cleansession;
130 }
131 
132 void
mqtt_client_options_set_username(MQTTClientOptions * self,const gchar * username)133 mqtt_client_options_set_username(MQTTClientOptions *self, const gchar *username)
134 {
135   g_free(self->username);
136   self->username = g_strdup(username);
137 }
138 
139 void
mqtt_client_options_set_password(MQTTClientOptions * self,const gchar * password)140 mqtt_client_options_set_password(MQTTClientOptions *self, const gchar *password)
141 {
142   g_free(self->password);
143   self->password = g_strdup(password);
144 }
145 
146 void
mqtt_client_options_set_http_proxy(MQTTClientOptions * self,const gchar * http_proxy)147 mqtt_client_options_set_http_proxy(MQTTClientOptions *self, const gchar *http_proxy)
148 {
149   g_free(self->http_proxy);
150   self->http_proxy = g_strdup(http_proxy);
151 }
152 
153 
154 void
mqtt_client_options_set_ca_dir(MQTTClientOptions * self,const gchar * ca_dir)155 mqtt_client_options_set_ca_dir(MQTTClientOptions *self, const gchar *ca_dir)
156 {
157   g_free(self->ca_dir);
158   self->ca_dir = g_strdup(ca_dir);
159 }
160 
161 void
mqtt_client_options_set_ca_file(MQTTClientOptions * self,const gchar * ca_file)162 mqtt_client_options_set_ca_file(MQTTClientOptions *self, const gchar *ca_file)
163 {
164   g_free(self->ca_file);
165   self->ca_file = g_strdup(ca_file);
166 }
167 
168 void
mqtt_client_options_set_cert_file(MQTTClientOptions * self,const gchar * cert_file)169 mqtt_client_options_set_cert_file(MQTTClientOptions *self, const gchar *cert_file)
170 {
171   g_free(self->cert_file);
172   self->cert_file = g_strdup(cert_file);
173 }
174 
175 void
mqtt_client_options_set_key_file(MQTTClientOptions * self,const gchar * key_file)176 mqtt_client_options_set_key_file(MQTTClientOptions *self, const gchar *key_file)
177 {
178   g_free(self->key_file);
179   self->key_file = g_strdup(key_file);
180 }
181 
182 void
mqtt_client_options_set_cipher_suite(MQTTClientOptions * self,const gchar * ciphers)183 mqtt_client_options_set_cipher_suite(MQTTClientOptions *self, const gchar *ciphers)
184 {
185   g_free(self->ciphers);
186   self->ciphers = g_strdup(ciphers);
187 }
188 
189 gboolean
mqtt_client_options_set_ssl_version(MQTTClientOptions * self,const gchar * value)190 mqtt_client_options_set_ssl_version(MQTTClientOptions *self, const gchar *value)
191 {
192   if (strcasecmp(value, "default") == 0)
193     self->ssl_version = MQTT_SSL_VERSION_DEFAULT;
194   else if (strcasecmp(value, "tlsv1_0") == 0)
195     self->ssl_version = MQTT_SSL_VERSION_TLS_1_0;
196   else if (strcasecmp(value, "tlsv1_1") == 0)
197     self->ssl_version = MQTT_SSL_VERSION_TLS_1_1;
198   else if (strcasecmp(value, "tlsv1_2") == 0)
199     self->ssl_version = MQTT_SSL_VERSION_TLS_1_2;
200   else
201     return FALSE;
202 
203   return TRUE;
204 }
205 
206 void
mqtt_client_options_set_peer_verify(MQTTClientOptions * self,gboolean verify)207 mqtt_client_options_set_peer_verify(MQTTClientOptions *self, gboolean verify)
208 {
209   self->peer_verify = verify;
210 }
211 
212 void
mqtt_client_options_use_system_cert_store(MQTTClientOptions * self,gboolean use_system_cert_store)213 mqtt_client_options_use_system_cert_store(MQTTClientOptions *self, gboolean use_system_cert_store)
214 {
215   /* TODO: auto_detect_ca_file() from the HTTP module */
216   self->use_system_cert_store = use_system_cert_store;
217 }
218 
219 void
mqtt_client_options_set_log_ssl_error_fn(MQTTClientOptions * self,gpointer context,gint (* log_error)(const gchar * str,gsize len,gpointer u))220 mqtt_client_options_set_log_ssl_error_fn(MQTTClientOptions *self, gpointer context, gint(*log_error)(const gchar *str,
221                                          gsize len, gpointer u))
222 {
223   self->context = context;
224   self->log_error = log_error;
225 }
226 
227 gchar *
mqtt_client_options_get_address(MQTTClientOptions * self)228 mqtt_client_options_get_address(MQTTClientOptions *self)
229 {
230   return self->address;
231 }
232 
233 gint
mqtt_client_options_get_qos(MQTTClientOptions * self)234 mqtt_client_options_get_qos(MQTTClientOptions *self)
235 {
236   return self->qos;
237 }
238 
239 gchar *
mqtt_client_options_get_client_id(MQTTClientOptions * self)240 mqtt_client_options_get_client_id(MQTTClientOptions *self)
241 {
242   return self->client_id;
243 }
244 
245 static void
_set_ssl_options(MQTTClientOptions * self,MQTTClient_SSLOptions * ssl_opts)246 _set_ssl_options(MQTTClientOptions *self, MQTTClient_SSLOptions *ssl_opts)
247 {
248   *ssl_opts = (MQTTClient_SSLOptions) MQTTClient_SSLOptions_initializer;
249   ssl_opts->trustStore = self->ca_file;
250   ssl_opts->CApath = self->ca_dir;
251   ssl_opts->keyStore = self->cert_file;
252   ssl_opts->privateKey = self->key_file;
253   ssl_opts->enabledCipherSuites = self->ciphers;
254   ssl_opts->sslVersion = self->ssl_version;
255   ssl_opts->enableServerCertAuth = self->peer_verify;
256   ssl_opts->verify = self->peer_verify;
257   ssl_opts->disableDefaultTrustStore = !self->use_system_cert_store;
258   ssl_opts->ssl_error_cb = self->log_error;
259   ssl_opts->ssl_error_context = self->context;
260 }
261 
262 void
mqtt_client_options_to_mqtt_client_connection_option(MQTTClientOptions * self,MQTTClient_connectOptions * conn_opts,MQTTClient_SSLOptions * ssl_opts)263 mqtt_client_options_to_mqtt_client_connection_option(MQTTClientOptions *self, MQTTClient_connectOptions *conn_opts,
264                                                      MQTTClient_SSLOptions *ssl_opts)
265 {
266   *conn_opts = (MQTTClient_connectOptions) MQTTClient_connectOptions_initializer;
267   conn_opts->keepAliveInterval = self->keepalive;
268   conn_opts->cleansession = self->cleansession;
269   conn_opts->username = self->username;
270   conn_opts->password = self->password;
271 
272 #if SYSLOG_NG_HAVE_PAHO_HTTP_PROXY
273   if (self->http_proxy)
274     {
275       conn_opts->httpProxy = self->http_proxy;
276       conn_opts->httpsProxy = self->http_proxy;
277     }
278 #endif
279 
280   _set_ssl_options(self, ssl_opts);
281   conn_opts->ssl = ssl_opts;
282 }
283 
284 static gboolean
_key_or_cert_file_is_not_specified(MQTTClientOptions * self)285 _key_or_cert_file_is_not_specified(MQTTClientOptions *self)
286 {
287   return (!self->key_file || !self->cert_file);
288 }
289 
290 static gboolean
_is_using_tls(MQTTClientOptions * self)291 _is_using_tls(MQTTClientOptions *self)
292 {
293   return (strncmp("ssl", self->address, 3) == 0) ||
294          (strncmp("wss", self->address, 3) == 0);
295 }
296 
297 gboolean
mqtt_client_options_checker(MQTTClientOptions * self)298 mqtt_client_options_checker(MQTTClientOptions *self)
299 {
300 
301 #if !SYSLOG_NG_HAVE_PAHO_HTTP_PROXY
302   if (self->http_proxy)
303     {
304       msg_warning_once("WARNING: the http-proxy() option of the mqtt() destination "
305                        "is not supported on the current libpaho-mqtt version. "
306                        "If you would like to use this feature, update to at least libpaho-mqtt 1.3.7");
307       g_free(self->http_proxy);
308       self->http_proxy = NULL;
309     }
310 #endif
311 
312   if (_is_using_tls(self) && _key_or_cert_file_is_not_specified(self))
313     {
314       msg_warning("MQTT: You have a TLS enabled without a X.509 keypair."
315                   " Make sure you have tls(key-file() and cert-file()) options, "
316                   "TLS handshake to this source will fail");
317     }
318 
319   return TRUE;
320 }
321