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 "comms.h"
23 #include "log.h"
24 #include "cfg.h"
25 #include "telnet.h"
26 #include "../common/net.h"
27 #include "ntp.h"
28 #include "simple.h"
29 
30 #ifdef HAVE_LDAP
31 #	include <ldap.h>
32 #endif
33 
34 #ifdef HAVE_LBER_H
35 #	include <lber.h>
36 #endif
37 
38 ZBX_METRIC	parameters_simple[] =
39 /*      KEY                     FLAG		FUNCTION        	TEST PARAMETERS */
40 {
41 	{"net.tcp.service",	CF_HAVEPARAMS,	CHECK_SERVICE, 		"ssh,127.0.0.1,22"},
42 	{"net.tcp.service.perf",CF_HAVEPARAMS,	CHECK_SERVICE_PERF, 	"ssh,127.0.0.1,22"},
43 	{"net.udp.service",	CF_HAVEPARAMS,	CHECK_SERVICE, 		"ntp,127.0.0.1,123"},
44 	{"net.udp.service.perf",CF_HAVEPARAMS,	CHECK_SERVICE_PERF, 	"ntp,127.0.0.1,123"},
45 	{NULL}
46 };
47 
48 #ifdef HAVE_LDAP
check_ldap(const char * host,unsigned short port,int timeout,int * value_int)49 static int    check_ldap(const char *host, unsigned short port, int timeout, int *value_int)
50 {
51 	LDAP		*ldap	= NULL;
52 	LDAPMessage	*res	= NULL;
53 	LDAPMessage	*msg	= NULL;
54 	BerElement	*ber	= NULL;
55 
56 	char	*attrs[2] = { "namingContexts", NULL };
57 	char	*attr	 = NULL;
58 	char	**valRes = NULL;
59 	int	ldapErr = 0;
60 
61 	zbx_alarm_on(timeout);
62 
63 	*value_int = 0;
64 
65 	if (NULL == (ldap = ldap_init(host, port)))
66 	{
67 		zabbix_log(LOG_LEVEL_DEBUG, "LDAP - initialization failed [%s:%hu]", host, port);
68 		goto lbl_ret;
69 	}
70 
71 	if (LDAP_SUCCESS != (ldapErr = ldap_search_s(ldap, "", LDAP_SCOPE_BASE, "(objectClass=*)", attrs, 0, &res)))
72 	{
73 		zabbix_log(LOG_LEVEL_DEBUG, "LDAP - searching failed [%s] [%s]", host, ldap_err2string(ldapErr));
74 		goto lbl_ret;
75 	}
76 
77 	if (NULL == (msg = ldap_first_entry(ldap, res)))
78 	{
79 		zabbix_log(LOG_LEVEL_DEBUG, "LDAP - empty sort result. [%s] [%s]", host, ldap_err2string(ldapErr));
80 		goto lbl_ret;
81 	}
82 
83 	if (NULL == (attr = ldap_first_attribute(ldap, msg, &ber)))
84 	{
85 		zabbix_log(LOG_LEVEL_DEBUG, "LDAP - empty first entry result. [%s] [%s]", host, ldap_err2string(ldapErr));
86 		goto lbl_ret;
87 	}
88 
89 	valRes = ldap_get_values(ldap, msg, attr);
90 
91 	*value_int = 1;
92 lbl_ret:
93 	zbx_alarm_off();
94 
95 	if (NULL != valRes)
96 		ldap_value_free(valRes);
97 	if (NULL != attr)
98 		ldap_memfree(attr);
99 	if (NULL != ber)
100 		ber_free(ber, 0);
101 	if (NULL != res)
102 		ldap_msgfree(res);
103 	if (NULL != ldap)
104 		ldap_unbind(ldap);
105 
106 	return SYSINFO_RET_OK;
107 }
108 #endif	/* HAVE_LDAP */
109 
110 /******************************************************************************
111  *                                                                            *
112  * Function: find_ssh_ident_string                                            *
113  *                                                                            *
114  * Purpose: parse recv_buf for ssh identification string as per               *
115  *          RFC 4253, section 4.2                                             *
116  *                                                                            *
117  * Parameters: recv_buf     - [IN] buffer to parse                            *
118  *             remote_major - [OUT] memory pointer where protocol major is    *
119  *                                  to be written to                          *
120  *             remote_minor - [OUT] memory pointer where protocol minor is    *
121  *                                  to be written to                          *
122  *                                                                            *
123  * Returns: SUCCEED - if a string matching the specification is found         *
124  *          FAIL - otherwise                                                  *
125  *                                                                            *
126  ******************************************************************************/
find_ssh_ident_string(const char * recv_buf,int * remote_major,int * remote_minor)127 static int	find_ssh_ident_string(const char *recv_buf, int *remote_major, int *remote_minor)
128 {
129 	const char	*r, *l = recv_buf;
130 
131 	while (NULL != (r = strchr(l, '\n')))
132 	{
133 		if (2 == sscanf(l, "SSH-%d.%d-%*s", remote_major, remote_minor))
134 			return SUCCEED;
135 
136 		l = r + 1;
137 	}
138 
139 	return FAIL;
140 }
141 
check_ssh(const char * host,unsigned short port,int timeout,int * value_int)142 static int	check_ssh(const char *host, unsigned short port, int timeout, int *value_int)
143 {
144 	int		ret;
145 	zbx_socket_t	s;
146 	char		send_buf[MAX_STRING_LEN];
147 	int		remote_major, remote_minor;
148 
149 	*value_int = 0;
150 
151 	if (SUCCEED == (ret = zbx_tcp_connect(&s, CONFIG_SOURCE_IP, host, port, timeout, ZBX_TCP_SEC_UNENCRYPTED, NULL,
152 			NULL)))
153 	{
154 		if (SUCCEED == (ret = zbx_tcp_recv(&s)))
155 		{
156 			if (SUCCEED == find_ssh_ident_string(s.buffer, &remote_major, &remote_minor))
157 			{
158 				zbx_snprintf(send_buf, sizeof(send_buf), "SSH-%d.%d-zabbix_agent\r\n",
159 						remote_major, remote_minor);
160 				*value_int = 1;
161 			}
162 			else
163 				strscpy(send_buf, "0\n");
164 
165 			ret = zbx_tcp_send_raw(&s, send_buf);
166 		}
167 
168 		zbx_tcp_close(&s);
169 	}
170 
171 	if (FAIL == ret)
172 		zabbix_log(LOG_LEVEL_DEBUG, "SSH check error: %s", zbx_socket_strerror());
173 
174 	return SYSINFO_RET_OK;
175 }
176 
177 #ifdef HAVE_LIBCURL
check_https(const char * host,unsigned short port,int timeout,int * value_int)178 static int	check_https(const char *host, unsigned short port, int timeout, int *value_int)
179 {
180 	const char	*__function_name = "check_https";
181 	int		err, opt;
182 	char		https_host[MAX_STRING_LEN];
183 	CURL            *easyhandle;
184 
185 	*value_int = 0;
186 
187 	if (NULL == (easyhandle = curl_easy_init()))
188 	{
189 		zabbix_log(LOG_LEVEL_DEBUG, "%s: could not init cURL library", __function_name);
190 		goto clean;
191 	}
192 
193 	if (SUCCEED == is_ip6(host))
194 		zbx_snprintf(https_host, sizeof(https_host), "%s[%s]", (0 == strncmp(host, "https://", 8) ? "" : "https://"), host);
195 	else
196 		zbx_snprintf(https_host, sizeof(https_host), "%s%s", (0 == strncmp(host, "https://", 8) ? "" : "https://"), host);
197 
198 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_USERAGENT, "Zabbix " ZABBIX_VERSION)) ||
199 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_URL, https_host)) ||
200 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_PORT, (long)port)) ||
201 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_NOBODY, 1L)) ||
202 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_SSL_VERIFYPEER, 0L)) ||
203 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_SSL_VERIFYHOST, 0L)) ||
204 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_TIMEOUT, (long)timeout)))
205 	{
206 		zabbix_log(LOG_LEVEL_DEBUG, "%s: could not set cURL option [%d]: %s",
207 				__function_name, opt, curl_easy_strerror(err));
208 		goto clean;
209 	}
210 
211 	if (NULL != CONFIG_SOURCE_IP)
212 	{
213 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_INTERFACE, CONFIG_SOURCE_IP)))
214 		{
215 			zabbix_log(LOG_LEVEL_DEBUG, "%s: could not set source interface option [%d]: %s",
216 					__function_name, opt, curl_easy_strerror(err));
217 			goto clean;
218 		}
219 	}
220 
221 	if (CURLE_OK == (err = curl_easy_perform(easyhandle)))
222 		*value_int = 1;
223 	else
224 		zabbix_log(LOG_LEVEL_DEBUG, "%s: curl_easy_perform failed for [%s:%hu]: %s",
225 				__function_name, host, port, curl_easy_strerror(err));
226 clean:
227 	curl_easy_cleanup(easyhandle);
228 
229 	return SYSINFO_RET_OK;
230 }
231 #endif	/* HAVE_LIBCURL */
232 
check_telnet(const char * host,unsigned short port,int timeout,int * value_int)233 static int	check_telnet(const char *host, unsigned short port, int timeout, int *value_int)
234 {
235 	const char	*__function_name = "check_telnet";
236 	zbx_socket_t	s;
237 #ifdef _WINDOWS
238 	u_long		argp = 1;
239 #else
240 	int		flags;
241 #endif
242 	*value_int = 0;
243 
244 	if (SUCCEED == zbx_tcp_connect(&s, CONFIG_SOURCE_IP, host, port, timeout, ZBX_TCP_SEC_UNENCRYPTED, NULL, NULL))
245 	{
246 #ifdef _WINDOWS
247 		ioctlsocket(s.socket, FIONBIO, &argp);	/* non-zero value sets the socket to non-blocking */
248 #else
249 		flags = fcntl(s.socket, F_GETFL);
250 		if (0 == (flags & O_NONBLOCK))
251 			fcntl(s.socket, F_SETFL, flags | O_NONBLOCK);
252 #endif
253 
254 		if (SUCCEED == telnet_test_login(s.socket))
255 			*value_int = 1;
256 		else
257 			zabbix_log(LOG_LEVEL_DEBUG, "Telnet check error: no login prompt");
258 
259 		zbx_tcp_close(&s);
260 	}
261 	else
262 		zabbix_log(LOG_LEVEL_DEBUG, "%s error: %s", __function_name, zbx_socket_strerror());
263 
264 	return SYSINFO_RET_OK;
265 }
266 
267 /* validation functions for service checks */
validate_smtp(const char * line)268 static int	validate_smtp(const char *line)
269 {
270 	if (0 == strncmp(line, "220", 3))
271 	{
272 		if ('-' == line[3])
273 			return ZBX_TCP_EXPECT_IGNORE;
274 
275 		if ('\0' == line[3] || ' ' == line[3])
276 			return ZBX_TCP_EXPECT_OK;
277 	}
278 
279 	return ZBX_TCP_EXPECT_FAIL;
280 }
281 
validate_ftp(const char * line)282 static int	validate_ftp(const char *line)
283 {
284 	if (0 == strncmp(line, "220 ", 4))
285 		return ZBX_TCP_EXPECT_OK;
286 
287 	return ZBX_TCP_EXPECT_IGNORE;
288 }
289 
validate_pop(const char * line)290 static int	validate_pop(const char *line)
291 {
292 	return 0 == strncmp(line, "+OK", 3) ? ZBX_TCP_EXPECT_OK : ZBX_TCP_EXPECT_FAIL;
293 }
294 
validate_nntp(const char * line)295 static int	validate_nntp(const char *line)
296 {
297 	if (0 == strncmp(line, "200", 3) || 0 == strncmp(line, "201", 3))
298 		return ZBX_TCP_EXPECT_OK;
299 
300 	return ZBX_TCP_EXPECT_FAIL;
301 }
302 
validate_imap(const char * line)303 static int	validate_imap(const char *line)
304 {
305 	return 0 == strncmp(line, "* OK", 4) ? ZBX_TCP_EXPECT_OK : ZBX_TCP_EXPECT_FAIL;
306 }
307 
check_service(AGENT_REQUEST * request,const char * default_addr,AGENT_RESULT * result,int perf)308 int	check_service(AGENT_REQUEST *request, const char *default_addr, AGENT_RESULT *result, int perf)
309 {
310 	unsigned short	port = 0;
311 	char		*service, *ip_str, ip[MAX_ZBX_DNSNAME_LEN + 1], *port_str;
312 	int		value_int, ret = SYSINFO_RET_FAIL;
313 	double		check_time;
314 
315 	check_time = zbx_time();
316 
317 	if (3 < request->nparam)
318 	{
319 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
320 		return SYSINFO_RET_FAIL;
321 	}
322 
323 	service = get_rparam(request, 0);
324 	ip_str = get_rparam(request, 1);
325 	port_str = get_rparam(request, 2);
326 
327 	if (NULL == service || '\0' == *service)
328 	{
329 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
330 		return SYSINFO_RET_FAIL;
331 	}
332 
333 	if (NULL == ip_str || '\0' == *ip_str)
334 		strscpy(ip, default_addr);
335 	else
336 		strscpy(ip, ip_str);
337 
338 	if (NULL != port_str && '\0' != *port_str && SUCCEED != is_ushort(port_str, &port))
339 	{
340 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
341 		return SYSINFO_RET_FAIL;
342 	}
343 
344 	if (0 == strncmp("net.tcp.service", get_rkey(request), 15))
345 	{
346 		if (0 == strcmp(service, "ssh"))
347 		{
348 			if (NULL == port_str || '\0' == *port_str)
349 				port = ZBX_DEFAULT_SSH_PORT;
350 			ret = check_ssh(ip, port, CONFIG_TIMEOUT, &value_int);
351 		}
352 		else if (0 == strcmp(service, "ldap"))
353 		{
354 #ifdef HAVE_LDAP
355 			if (NULL == port_str || '\0' == *port_str)
356 				port = ZBX_DEFAULT_LDAP_PORT;
357 			ret = check_ldap(ip, port, CONFIG_TIMEOUT, &value_int);
358 #else
359 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Support for LDAP check was not compiled in."));
360 #endif
361 		}
362 		else if (0 == strcmp(service, "smtp"))
363 		{
364 			if (NULL == port_str || '\0' == *port_str)
365 				port = ZBX_DEFAULT_SMTP_PORT;
366 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, validate_smtp, "QUIT\r\n", &value_int);
367 		}
368 		else if (0 == strcmp(service, "ftp"))
369 		{
370 			if (NULL == port_str || '\0' == *port_str)
371 				port = ZBX_DEFAULT_FTP_PORT;
372 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, validate_ftp, "QUIT\r\n", &value_int);
373 		}
374 		else if (0 == strcmp(service, "http"))
375 		{
376 			if (NULL == port_str || '\0' == *port_str)
377 				port = ZBX_DEFAULT_HTTP_PORT;
378 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, NULL, NULL, &value_int);
379 		}
380 		else if (0 == strcmp(service, "pop"))
381 		{
382 			if (NULL == port_str || '\0' == *port_str)
383 				port = ZBX_DEFAULT_POP_PORT;
384 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, validate_pop, "QUIT\r\n", &value_int);
385 		}
386 		else if (0 == strcmp(service, "nntp"))
387 		{
388 			if (NULL == port_str || '\0' == *port_str)
389 				port = ZBX_DEFAULT_NNTP_PORT;
390 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, validate_nntp, "QUIT\r\n", &value_int);
391 		}
392 		else if (0 == strcmp(service, "imap"))
393 		{
394 			if (NULL == port_str || '\0' == *port_str)
395 				port = ZBX_DEFAULT_IMAP_PORT;
396 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, validate_imap, "a1 LOGOUT\r\n", &value_int);
397 		}
398 		else if (0 == strcmp(service, "tcp"))
399 		{
400 			if (NULL == port_str || '\0' == *port_str)
401 			{
402 				SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
403 				return SYSINFO_RET_FAIL;
404 			}
405 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, NULL, NULL, &value_int);
406 		}
407 		else if (0 == strcmp(service, "https"))
408 		{
409 #ifdef HAVE_LIBCURL
410 			if (NULL == port_str || '\0' == *port_str)
411 				port = ZBX_DEFAULT_HTTPS_PORT;
412 			ret = check_https(ip, port, CONFIG_TIMEOUT, &value_int);
413 #else
414 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Support for HTTPS check was not compiled in."));
415 #endif
416 		}
417 		else if (0 == strcmp(service, "telnet"))
418 		{
419 			if (NULL == port_str || '\0' == *port_str)
420 				port = ZBX_DEFAULT_TELNET_PORT;
421 			ret = check_telnet(ip, port, CONFIG_TIMEOUT, &value_int);
422 		}
423 		else
424 		{
425 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
426 			return ret;
427 		}
428 	}
429 	else	/* net.udp.service */
430 	{
431 		if (0 == strcmp(service, "ntp"))
432 		{
433 			if (NULL == port_str || '\0' == *port_str)
434 				port = ZBX_DEFAULT_NTP_PORT;
435 			ret = check_ntp(ip, port, CONFIG_TIMEOUT, &value_int);
436 		}
437 		else
438 		{
439 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
440 			return ret;
441 		}
442 	}
443 
444 	if (SYSINFO_RET_OK == ret)
445 	{
446 		if (0 != perf)
447 		{
448 			if (0 != value_int)
449 			{
450 				check_time = zbx_time() - check_time;
451 
452 				if (ZBX_FLOAT_PRECISION > check_time)
453 					check_time = ZBX_FLOAT_PRECISION;
454 
455 				SET_DBL_RESULT(result, check_time);
456 			}
457 			else
458 				SET_DBL_RESULT(result, 0.0);
459 		}
460 		else
461 			SET_UI64_RESULT(result, value_int);
462 	}
463 
464 	return ret;
465 }
466 
467 /* Examples:
468  *
469  *   net.tcp.service[ssh]
470  *   net.tcp.service[smtp,127.0.0.1]
471  *   net.tcp.service[ssh,127.0.0.1,22]
472  *
473  *   net.udp.service[ntp]
474  *   net.udp.service[ntp,127.0.0.1]
475  *   net.udp.service[ntp,127.0.0.1,123]
476  *
477  *   net.tcp.service.perf[ssh]
478  *   net.tcp.service.perf[smtp,127.0.0.1]
479  *   net.tcp.service.perf[ssh,127.0.0.1,22]
480  *
481  *   net.udp.service.perf[ntp]
482  *   net.udp.service.perf[ntp,127.0.0.1]
483  *   net.udp.service.perf[ntp,127.0.0.1,123]
484  *
485  * The old name for these checks is check_service[*].
486  */
487 
CHECK_SERVICE(AGENT_REQUEST * request,AGENT_RESULT * result)488 int	CHECK_SERVICE(AGENT_REQUEST *request, AGENT_RESULT *result)
489 {
490 	return check_service(request, "127.0.0.1", result, 0);
491 }
492 
CHECK_SERVICE_PERF(AGENT_REQUEST * request,AGENT_RESULT * result)493 int	CHECK_SERVICE_PERF(AGENT_REQUEST *request, AGENT_RESULT *result)
494 {
495 	return check_service(request, "127.0.0.1", result, 1);
496 }
497