1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "sysinfo.h"
22 #include "log.h"
23 
24 #include "zbxmedia.h"
25 
26 #ifdef HAVE_JABBER
27 
28 #include <iksemel.h>
29 
zbx_io_close(void * socket)30 static void	zbx_io_close(void *socket)
31 {
32 	int	*sock = (int *)socket;
33 
34 	if (NULL == sock)
35 		return;
36 
37 	close(*sock);
38 }
39 
40 static int		zbx_j_sock = -1;
41 static const char	*__module_name = "JABBER";
42 
zbx_io_connect(iksparser * prs,void ** socketptr,const char * server,int port)43 static int	zbx_io_connect(iksparser *prs, void **socketptr, const char *server, int port)
44 {
45 	int		tmp;
46 #ifdef HAVE_GETADDRINFO
47 	struct addrinfo	hints, *addr_res, *addr_ptr;
48 	char		port_str[6];
49 
50 	*socketptr = NULL;
51 
52 	hints.ai_flags = AI_CANONNAME;
53 	hints.ai_family = PF_UNSPEC;
54 	hints.ai_socktype = SOCK_STREAM;
55 	hints.ai_protocol = 0;
56 	hints.ai_addrlen = 0;
57 	hints.ai_canonname = NULL;
58 	hints.ai_addr = NULL;
59 	hints.ai_next = NULL;
60 
61 	zbx_snprintf(port_str, sizeof(port_str), "%d", port);
62 
63 	if (0 != getaddrinfo(server, port_str, &hints, &addr_res))
64 		return IKS_NET_NODNS;
65 
66 	addr_ptr = addr_res;
67 
68 	while (NULL != addr_ptr)
69 	{
70 		if (-1 != (zbx_j_sock = socket(addr_ptr->ai_family, addr_ptr->ai_socktype, addr_ptr->ai_protocol)))
71 			break;
72 
73 		addr_ptr = addr_ptr->ai_next;
74 	}
75 
76 	if (-1 == zbx_j_sock)
77 	{
78 		freeaddrinfo(addr_res);
79 		return IKS_NET_NOSOCK;
80 	}
81 
82 	tmp = connect(zbx_j_sock, addr_ptr->ai_addr, addr_ptr->ai_addrlen);
83 
84 	freeaddrinfo(addr_res);
85 #else
86 	struct hostent		*host;
87 	struct sockaddr_in	sin;
88 
89 	if (NULL == (host = gethostbyname(server)))
90 		return IKS_NET_NODNS;
91 
92 	memcpy(&sin.sin_addr, host->h_addr, host->h_length);
93 	sin.sin_family = host->h_addrtype;
94 	sin.sin_port = htons(port);
95 
96 	if (-1 == (zbx_j_sock = socket(host->h_addrtype, SOCK_STREAM, 0)))
97 		return IKS_NET_NOSOCK;
98 
99 	tmp = connect(zbx_j_sock, (struct sockaddr *)&sin, sizeof(sin));
100 #endif
101 	if (0 != tmp)
102 	{
103 		zbx_io_close((void *)&zbx_j_sock);
104 		return IKS_NET_NOCONN;
105 	}
106 
107 	*socketptr = (void *)&zbx_j_sock;
108 
109 	return IKS_OK;
110 }
111 
zbx_io_send(void * socket,const char * data,size_t len)112 static int	zbx_io_send(void *socket, const char *data, size_t len)
113 {
114 	int	*sock = (int *)socket;
115 
116 	if (NULL == sock)
117 		return IKS_NET_RWERR;
118 
119 	if (write(*sock, data, len) < (ssize_t)len)
120 		return IKS_NET_RWERR;
121 
122 	return IKS_OK;
123 }
124 
zbx_io_recv(void * socket,char * buffer,size_t buf_len,int timeout)125 static int	zbx_io_recv(void *socket, char *buffer, size_t buf_len, int timeout)
126 {
127 	int		*sock = (int *)socket, len;
128 	struct timeval	tv;
129 	fd_set		fds;
130 
131 	if (NULL == sock)
132 		return -1;
133 
134 	tv.tv_sec = timeout;
135 	tv.tv_usec = 0;
136 
137 	FD_ZERO(&fds);
138 	FD_SET(*sock, &fds);
139 
140 	if (0 < select(*sock + 1, &fds, NULL, NULL, -1 != timeout ? &tv : NULL))
141 	{
142 		len = recv(*sock, buffer, buf_len, 0);
143 
144 		if (0 < len)
145 			return len;
146 		else if (0 >= len)
147 			return -1;
148 	}
149 
150 	return 0;
151 }
152 
153 static ikstransport	zbx_iks_transport =
154 {
155 	IKS_TRANSPORT_V1,
156 	zbx_io_connect,
157 	zbx_io_send,
158 	zbx_io_recv,
159 	zbx_io_close,
160 	NULL
161 };
162 
163 #define JABBER_DISCONNECTED	0
164 #define JABBER_ERROR		1
165 
166 #define JABBER_CONNECTING	2
167 #define JABBER_CONNECTED	3
168 #define JABBER_AUTHORIZED	4
169 #define JABBER_WORKING		5
170 #define JABBER_READY		10
171 
172 typedef struct
173 {
174 	iksparser	*prs;
175 	iksid		*acc;
176 	char		*pass;
177 	int		features;
178 	iksfilter	*my_filter;
179 	int		opt_use_tls;
180 	int		opt_use_sasl;
181 	int		status;
182 }
183 jabber_session_t, *jabber_session_p;
184 
185 static jabber_session_p jsess = NULL;
186 static char		*jabber_error = NULL;
187 static int		jabber_error_len = 0;
188 
on_result(jabber_session_p sess,ikspak * pak)189 static int	on_result(jabber_session_p sess, ikspak *pak)
190 {
191 	const char	*__function_name = "on_result";
192 
193 	zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s()", __module_name, __function_name);
194 
195 	sess->status = JABBER_READY;
196 
197 	zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s()", __module_name, __function_name);
198 
199 	return IKS_FILTER_EAT;
200 }
201 
202 /******************************************************************************
203  *                                                                            *
204  * Function: lookup_jabber                                                    *
205  *                                                                            *
206  * Purpose: lookup Jabber SRV record                                          *
207  *                                                                            *
208  * Author: Aleksandrs Saveljevs, based on code by Edward Rudd                 *
209  *                                                                            *
210  ******************************************************************************/
lookup_jabber(const char * server,int port,char * real_server,size_t real_server_len,int * real_port)211 static void	lookup_jabber(const char *server, int port, char *real_server, size_t real_server_len, int *real_port)
212 {
213 	const char	*__function_name = "lookup_jabber";
214 	char		buffer[MAX_STRING_LEN], command[MAX_STRING_LEN];
215 	AGENT_RESULT	result;
216 	int		ret = SYSINFO_RET_FAIL;
217 
218 	zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s() server:'%s' port:%d", __module_name, __function_name, server, port);
219 
220 	init_result(&result);
221 
222 	zbx_snprintf(command, sizeof(command), "net.dns.record[,_xmpp-client._tcp.%s,SRV]", server);
223 
224 	if (SUCCEED == process(command, 0, &result))
225 	{
226 		int		max_priority = 65536, max_weight = -1;
227 		int		cur_priority, cur_weight, cur_port;
228 		const char	*p = result.text;
229 
230 		zabbix_log(LOG_LEVEL_DEBUG, "response to DNS query: [%s]", result.text);
231 
232 		/* let us now choose the server with the highest priority and maximum weight */
233 
234 		zbx_snprintf(command, sizeof(command), "_xmpp-client._tcp.%s SRV %%d %%d %%d %%" ZBX_FS_SIZE_T "s",
235 				server, (zbx_fs_size_t)sizeof(buffer));
236 
237 		while (NULL != p)
238 		{
239 			if (4 == sscanf(p, command, &cur_priority, &cur_weight, &cur_port, buffer))
240 			{
241 				if (cur_priority < max_priority || (cur_priority == max_priority && cur_weight > max_weight))
242 				{
243 					ret = SYSINFO_RET_OK;
244 
245 					max_priority = cur_priority;
246 					max_weight = cur_weight;
247 
248 					zbx_strlcpy(real_server, buffer, real_server_len);
249 					*real_port = cur_port;
250 				}
251 			}
252 
253 			if (NULL != (p = strchr(p, '\n')))
254 				p++;
255 		}
256 	}
257 
258 	free_result(&result);
259 
260 	if (SYSINFO_RET_OK != ret)
261 	{
262 		zbx_strlcpy(real_server, server, real_server_len);
263 		*real_port = port;
264 	}
265 
266 	zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s() real_server:'%s' real_port:%d",
267 			__module_name, __function_name, real_server, *real_port);
268 }
269 
270 /******************************************************************************
271  *                                                                            *
272  * Function: disconnect_jabber                                                *
273  *                                                                            *
274  * Purpose: disconnect from Jabber server                                     *
275  *                                                                            *
276  * Return value: always return SUCCEED                                        *
277  *                                                                            *
278  * Author: Eugene Grigorjev                                                   *
279  *                                                                            *
280  ******************************************************************************/
disconnect_jabber()281 static int	disconnect_jabber()
282 {
283 	const char	*__function_name = "disconnect_jabber";
284 
285 	zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s()", __module_name, __function_name);
286 
287 	if (JABBER_DISCONNECTED != jsess->status)
288 		iks_disconnect(jsess->prs);
289 
290 	if (NULL != jsess->my_filter)
291 	{
292 		iks_filter_delete(jsess->my_filter);
293 		jsess->my_filter = NULL;
294 	}
295 
296 	if (NULL != jsess->prs)
297 	{
298 		iks_parser_delete(jsess->prs);
299 		jsess->prs = NULL;
300 	}
301 
302 	zbx_free(jsess->pass);
303 
304 	jsess->acc = NULL;
305 
306 	jsess->status = JABBER_DISCONNECTED;
307 
308 	zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s()", __module_name, __function_name);
309 
310 	return SUCCEED;
311 }
312 
on_stream(jabber_session_p sess,int type,iks * node)313 static int	on_stream(jabber_session_p sess, int type, iks *node)
314 {
315 	const char	*__function_name = "on_stream";
316 	iks		*x = NULL;
317 	ikspak		*pak = NULL;
318 	int		ret = IKS_OK;
319 
320 	zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s()", __module_name, __function_name);
321 
322 	switch (type)
323 	{
324 		case IKS_NODE_START:
325 			break;
326 		case IKS_NODE_NORMAL:
327 			if (0 == strcmp("stream:features", iks_name(node)))
328 			{
329 				sess->features = iks_stream_features(node);
330 
331 				if (IKS_STREAM_STARTTLS == (sess->features & IKS_STREAM_STARTTLS))
332 				{
333 					iks_start_tls(sess->prs);
334 				}
335 				else
336 				{
337 					if (JABBER_AUTHORIZED == sess->status)
338 					{
339 						if (IKS_STREAM_BIND == (sess->features & IKS_STREAM_BIND))
340 						{
341 							x = iks_make_resource_bind(sess->acc);
342 							iks_send(sess->prs, x);
343 							iks_delete(x);
344 						}
345 						if (IKS_STREAM_SESSION == (sess->features & IKS_STREAM_SESSION))
346 						{
347 							x = iks_make_session();
348 							iks_insert_attrib(x, "id", "auth");
349 							iks_send(sess->prs, x);
350 							iks_delete(x);
351 						}
352 					}
353 					else
354 					{
355 						if (IKS_STREAM_SASL_MD5 == (sess->features & IKS_STREAM_SASL_MD5))
356 							iks_start_sasl(sess->prs, IKS_SASL_DIGEST_MD5, sess->acc->user, sess->pass);
357 						else if (IKS_STREAM_SASL_PLAIN == (sess->features & IKS_STREAM_SASL_PLAIN))
358 							iks_start_sasl(sess->prs, IKS_SASL_PLAIN, sess->acc->user, sess->pass);
359 					}
360 				}
361 			}
362 			else if (0 == strcmp("failure", iks_name(node)))
363 			{
364 				zbx_snprintf(jabber_error, jabber_error_len, "sasl authentication failed");
365 				jsess->status = JABBER_ERROR;
366 				ret = IKS_HOOK;
367 			}
368 			else if (0 == strcmp("success", iks_name(node)))
369 			{
370 				zabbix_log(LOG_LEVEL_DEBUG, "%s: authorized", __module_name);
371 				sess->status = JABBER_AUTHORIZED;
372 				iks_send_header(sess->prs, sess->acc->server);
373 			}
374 			else
375 			{
376 				pak = iks_packet(node);
377 				iks_filter_packet(sess->my_filter, pak);
378 				if (JABBER_READY == jsess->status)
379 					ret = IKS_HOOK;
380 			}
381 			break;
382 		case IKS_NODE_STOP:
383 			zbx_snprintf(jabber_error, jabber_error_len, "server disconnected");
384 			jsess->status = JABBER_ERROR;
385 			ret = IKS_HOOK;
386 			break;
387 		case IKS_NODE_ERROR:
388 			zbx_snprintf(jabber_error, jabber_error_len, "stream error");
389 			jsess->status = JABBER_ERROR;
390 			ret = IKS_HOOK;
391 	}
392 
393 	if (NULL != node)
394 		iks_delete(node);
395 
396 	zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s()", __module_name, __function_name);
397 
398 	return ret;
399 }
400 
on_error(void * user_data,ikspak * pak)401 static int	on_error(void *user_data, ikspak *pak)
402 {
403 	zbx_snprintf(jabber_error, jabber_error_len, "authorization failed");
404 
405 	jsess->status = JABBER_ERROR;
406 
407 	return IKS_FILTER_EAT;
408 }
409 
410 #ifdef DEBUG
on_log(jabber_session_p sess,const char * data,size_t size,int is_incoming)411 static void	on_log(jabber_session_p sess, const char *data, size_t size, int is_incoming)
412 {
413 	zabbix_log(LOG_LEVEL_DEBUG, "%s: %s%s: %s",
414 			__module_name, iks_is_secure(sess->prs) ? "Sec" : "", is_incoming ? "RECV" : "SEND", data);
415 }
416 #endif
417 
418 /******************************************************************************
419  *                                                                            *
420  * Function: connect_jabber                                                   *
421  *                                                                            *
422  * Purpose: connect to Jabber server                                          *
423  *                                                                            *
424  * Return value: SUCCEED on successful connection                             *
425  *               FAIL - otherwise                                             *
426  *                                                                            *
427  * Author: Eugene Grigorjev                                                   *
428  *                                                                            *
429  ******************************************************************************/
connect_jabber(const char * jabber_id,const char * password,int use_sasl,int port)430 static int	connect_jabber(const char *jabber_id, const char *password, int use_sasl, int port)
431 {
432 	const char	*__function_name = "connect_jabber";
433 	char		*buf = NULL;
434 	char		real_server[MAX_STRING_LEN];
435 	int		real_port = 0, iks_error, timeout, ret = FAIL;
436 
437 	zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s() jabber_id:'%s'", __module_name, __function_name, jabber_id);
438 
439 	if (NULL == jsess)
440 	{
441 		jsess = zbx_malloc(jsess, sizeof(jabber_session_t));
442 		memset(jsess, 0, sizeof(jabber_session_t));
443 	}
444 	else if (JABBER_DISCONNECTED != jsess->status)
445 	{
446 		disconnect_jabber();
447 	}
448 
449 	if (NULL == (jsess->prs = iks_stream_new(IKS_NS_CLIENT, jsess, (iksStreamHook *)on_stream)))
450 	{
451 		zbx_snprintf(jabber_error, jabber_error_len, "cannot create iksemel parser: %s", zbx_strerror(errno));
452 		goto lbl_fail;
453 	}
454 
455 #ifdef DEBUG
456 	iks_set_log_hook(jsess->prs, (iksLogHook *)on_log);
457 #endif
458 
459 	jsess->acc = iks_id_new(iks_parser_stack(jsess->prs), jabber_id);
460 
461 	if (NULL == jsess->acc->resource)
462 	{
463 		/* user gave no resource name, use the default */
464 		buf = zbx_dsprintf(buf, "%s@%s/%s", jsess->acc->user, jsess->acc->server, "ZABBIX");
465 		jsess->acc = iks_id_new(iks_parser_stack(jsess->prs), buf);
466 		zbx_free(buf);
467 	}
468 
469 	jsess->pass = zbx_strdup(jsess->pass, password);
470 	jsess->opt_use_sasl = use_sasl;
471 
472 	if (NULL == (jsess->my_filter = iks_filter_new()))
473 	{
474 		zbx_snprintf(jabber_error, jabber_error_len, "cannot create filter: %s", zbx_strerror(errno));
475 		goto lbl_fail;
476 	}
477 
478 	iks_filter_add_rule(jsess->my_filter, (iksFilterHook *)on_result, jsess,
479 		IKS_RULE_TYPE, IKS_PAK_IQ,
480 		IKS_RULE_SUBTYPE, IKS_TYPE_RESULT,
481 		IKS_RULE_ID, "auth",
482 		IKS_RULE_DONE);
483 
484 	iks_filter_add_rule(jsess->my_filter, on_error, jsess,
485 		IKS_RULE_TYPE, IKS_PAK_IQ,
486 		IKS_RULE_SUBTYPE, IKS_TYPE_ERROR,
487 		IKS_RULE_ID, "auth",
488 		IKS_RULE_DONE);
489 
490 	lookup_jabber(jsess->acc->server, port, real_server, sizeof(real_server), &real_port);
491 
492 	switch (iks_connect_with(jsess->prs, real_server, real_port, jsess->acc->server, &zbx_iks_transport))
493 	{
494 		case IKS_OK:
495 			break;
496 		case IKS_NET_NODNS:
497 			zbx_snprintf(jabber_error, jabber_error_len, "hostname lookup failed");
498 			goto lbl_fail;
499 		case IKS_NET_NOCONN:
500 			zbx_snprintf(jabber_error, jabber_error_len, "connection failed: %s",
501 					strerror_from_system(errno));
502 			goto lbl_fail;
503 		default:
504 			zbx_snprintf(jabber_error, jabber_error_len, "connection error: %s",
505 					strerror_from_system(errno));
506 			goto lbl_fail;
507 	}
508 
509 	timeout = 30;
510 
511 	while (JABBER_READY != jsess->status && JABBER_ERROR != jsess->status)
512 	{
513 		iks_error = iks_recv(jsess->prs, 1);
514 
515 		if (IKS_HOOK == iks_error)
516 			break;
517 
518 		if (IKS_NET_TLSFAIL == iks_error)
519 		{
520 			zbx_snprintf(jabber_error, jabber_error_len, "tls handshake failed");
521 			break;
522 		}
523 
524 		if (IKS_OK != iks_error)
525 		{
526 			zbx_snprintf(jabber_error, jabber_error_len, "received error [%d]: %s",
527 					iks_error, zbx_strerror(errno));
528 			break;
529 		}
530 
531 		if (0 == --timeout)
532 			break;
533 	}
534 
535 	if (JABBER_READY == jsess->status)
536 		ret = SUCCEED;
537 lbl_fail:
538 	zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s():%s", __module_name, __function_name, zbx_result_string(ret));
539 
540 	return ret;
541 }
542 
543 /******************************************************************************
544  *                                                                            *
545  * Function: send_jabber                                                      *
546  *                                                                            *
547  * Purpose: send Jabber message                                               *
548  *                                                                            *
549  * Return value: SUCCEED if message sent                                      *
550  *               FAIL - otherwise                                             *
551  *                                                                            *
552  * Author: Eugene Grigorjev                                                   *
553  *                                                                            *
554  ******************************************************************************/
send_jabber(const char * username,const char * password,const char * sendto,const char * subject,const char * message,char * error,int max_error_len)555 int	send_jabber(const char *username, const char *password, const char *sendto,
556 		const char *subject, const char *message, char *error, int max_error_len)
557 {
558 	const char	*__function_name = "send_jabber";
559 	iks		*x;
560 	int		ret = FAIL, iks_error = IKS_OK;
561 
562 	assert(error);
563 
564 	zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s()", __module_name, __function_name);
565 
566 	*error = '\0';
567 
568 	jabber_error = error;
569 	jabber_error_len = max_error_len;
570 
571 	if (SUCCEED != connect_jabber(username, password, 1, IKS_JABBER_PORT))
572 		goto lbl_fail;
573 
574 	zabbix_log(LOG_LEVEL_DEBUG, "%s: sending", __module_name);
575 
576 	if (NULL != (x = iks_make_msg(IKS_TYPE_NONE, sendto, message)))
577 	{
578 		iks_insert_cdata(iks_insert(x, "subject"), subject, 0);
579 		iks_insert_attrib(x, "from", username);
580 
581 		if (IKS_OK == (iks_error = iks_send(jsess->prs, x)))
582 		{
583 			zabbix_log(LOG_LEVEL_DEBUG, "%s: message sent", __module_name);
584 			ret = SUCCEED;
585 		}
586 		else
587 		{
588 			zbx_snprintf(error, max_error_len, "cannot send message: %s", strerror_from_system(errno));
589 			jsess->status = JABBER_ERROR;
590 		}
591 
592 		iks_delete(x);
593 	}
594 	else
595 		zbx_snprintf(error, max_error_len, "cannot create message");
596 lbl_fail:
597 	if (NULL != jsess && JABBER_DISCONNECTED != jsess->status)
598 		disconnect_jabber();
599 
600 	jabber_error = NULL;
601 	jabber_error_len = 0;
602 
603 	if ('\0' != *error)
604 		zabbix_log(LOG_LEVEL_WARNING, "%s: [%s] %s", __module_name, username, error);
605 
606 	zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s():%s", __module_name, __function_name, zbx_result_string(ret));
607 
608 	return ret;
609 }
610 
611 #endif	/* HAVE_JABBER */
612