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 
check_ssh(const char * host,unsigned short port,int timeout,int * value_int)110 static int	check_ssh(const char *host, unsigned short port, int timeout, int *value_int)
111 {
112 	int		ret, major, minor;
113 	zbx_socket_t	s;
114 	char		send_buf[MAX_STRING_LEN];
115 	const char	*buf;
116 
117 	*value_int = 0;
118 
119 	if (SUCCEED == (ret = zbx_tcp_connect(&s, CONFIG_SOURCE_IP, host, port, timeout, ZBX_TCP_SEC_UNENCRYPTED, NULL,
120 			NULL)))
121 	{
122 		while (NULL != (buf = zbx_tcp_recv_line(&s)))
123 		{
124 			/* parse buf for SSH identification string as per RFC 4253, section 4.2 */
125 			if (2 == sscanf(buf, "SSH-%d.%d-%*s", &major, &minor))
126 			{
127 				zbx_snprintf(send_buf, sizeof(send_buf), "SSH-%d.%d-zabbix_agent\r\n", major, minor);
128 				*value_int = 1;
129 				break;
130 			}
131 		}
132 
133 		if (0 == *value_int)
134 			strscpy(send_buf, "0\n");
135 
136 		ret = zbx_tcp_send_raw(&s, send_buf);
137 		zbx_tcp_close(&s);
138 	}
139 
140 	if (FAIL == ret)
141 		zabbix_log(LOG_LEVEL_DEBUG, "SSH check error: %s", zbx_socket_strerror());
142 
143 	return SYSINFO_RET_OK;
144 }
145 
146 #ifdef HAVE_LIBCURL
check_https(const char * host,unsigned short port,int timeout,int * value_int)147 static int	check_https(const char *host, unsigned short port, int timeout, int *value_int)
148 {
149 	CURL		*easyhandle;
150 	CURLoption	opt;
151 	CURLcode	err;
152 	char		https_host[MAX_STRING_LEN];
153 
154 	*value_int = 0;
155 
156 	if (NULL == (easyhandle = curl_easy_init()))
157 	{
158 		zabbix_log(LOG_LEVEL_DEBUG, "%s: could not init cURL library", __func__);
159 		goto clean;
160 	}
161 
162 	if (SUCCEED == is_ip6(host))
163 		zbx_snprintf(https_host, sizeof(https_host), "%s[%s]", (0 == strncmp(host, "https://", 8) ? "" : "https://"), host);
164 	else
165 		zbx_snprintf(https_host, sizeof(https_host), "%s%s", (0 == strncmp(host, "https://", 8) ? "" : "https://"), host);
166 
167 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_USERAGENT, "Zabbix " ZABBIX_VERSION)) ||
168 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_URL, https_host)) ||
169 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_PORT, (long)port)) ||
170 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_NOBODY, 1L)) ||
171 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_SSL_VERIFYPEER, 0L)) ||
172 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_SSL_VERIFYHOST, 0L)) ||
173 		CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_TIMEOUT, (long)timeout)))
174 	{
175 		zabbix_log(LOG_LEVEL_DEBUG, "%s: could not set cURL option [%d]: %s",
176 				__func__, (int)opt, curl_easy_strerror(err));
177 		goto clean;
178 	}
179 
180 	if (NULL != CONFIG_SOURCE_IP)
181 	{
182 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_INTERFACE, CONFIG_SOURCE_IP)))
183 		{
184 			zabbix_log(LOG_LEVEL_DEBUG, "%s: could not set source interface option [%d]: %s",
185 					__func__, (int)opt, curl_easy_strerror(err));
186 			goto clean;
187 		}
188 	}
189 
190 	if (CURLE_OK == (err = curl_easy_perform(easyhandle)))
191 		*value_int = 1;
192 	else
193 		zabbix_log(LOG_LEVEL_DEBUG, "%s: curl_easy_perform failed for [%s:%hu]: %s",
194 				__func__, host, port, curl_easy_strerror(err));
195 clean:
196 	curl_easy_cleanup(easyhandle);
197 
198 	return SYSINFO_RET_OK;
199 }
200 #endif	/* HAVE_LIBCURL */
201 
check_telnet(const char * host,unsigned short port,int timeout,int * value_int)202 static int	check_telnet(const char *host, unsigned short port, int timeout, int *value_int)
203 {
204 	zbx_socket_t	s;
205 #ifdef _WINDOWS
206 	u_long		argp = 1;
207 #else
208 	int		flags;
209 #endif
210 	*value_int = 0;
211 
212 	if (SUCCEED == zbx_tcp_connect(&s, CONFIG_SOURCE_IP, host, port, timeout, ZBX_TCP_SEC_UNENCRYPTED, NULL, NULL))
213 	{
214 #ifdef _WINDOWS
215 		ioctlsocket(s.socket, FIONBIO, &argp);	/* non-zero value sets the socket to non-blocking */
216 #else
217 		flags = fcntl(s.socket, F_GETFL);
218 		if (-1 == flags)
219 			zabbix_log(LOG_LEVEL_DEBUG, " error in getting the status flag: %s", zbx_strerror(errno));
220 
221 		if (0 == (flags & O_NONBLOCK) && (-1 == fcntl(s.socket, F_SETFL, flags | O_NONBLOCK)))
222 		{
223 			zabbix_log(LOG_LEVEL_DEBUG, " error in setting the status flag: %s",
224 				zbx_strerror(errno));
225 		}
226 #endif
227 		if (SUCCEED == telnet_test_login(s.socket))
228 			*value_int = 1;
229 		else
230 			zabbix_log(LOG_LEVEL_DEBUG, "Telnet check error: no login prompt");
231 
232 		zbx_tcp_close(&s);
233 	}
234 	else
235 	{
236 		zabbix_log(LOG_LEVEL_DEBUG, "%s error: %s", __func__, zbx_socket_strerror());
237 	}
238 
239 	return SYSINFO_RET_OK;
240 }
241 
242 /* validation functions for service checks */
validate_smtp(const char * line)243 static int	validate_smtp(const char *line)
244 {
245 	if (0 == strncmp(line, "220", 3))
246 	{
247 		if ('-' == line[3])
248 			return ZBX_TCP_EXPECT_IGNORE;
249 
250 		if ('\0' == line[3] || ' ' == line[3])
251 			return ZBX_TCP_EXPECT_OK;
252 	}
253 
254 	return ZBX_TCP_EXPECT_FAIL;
255 }
256 
validate_ftp(const char * line)257 static int	validate_ftp(const char *line)
258 {
259 	if (0 == strncmp(line, "220 ", 4))
260 		return ZBX_TCP_EXPECT_OK;
261 
262 	return ZBX_TCP_EXPECT_IGNORE;
263 }
264 
validate_pop(const char * line)265 static int	validate_pop(const char *line)
266 {
267 	return 0 == strncmp(line, "+OK", 3) ? ZBX_TCP_EXPECT_OK : ZBX_TCP_EXPECT_FAIL;
268 }
269 
validate_nntp(const char * line)270 static int	validate_nntp(const char *line)
271 {
272 	if (0 == strncmp(line, "200", 3) || 0 == strncmp(line, "201", 3))
273 		return ZBX_TCP_EXPECT_OK;
274 
275 	return ZBX_TCP_EXPECT_FAIL;
276 }
277 
validate_imap(const char * line)278 static int	validate_imap(const char *line)
279 {
280 	return 0 == strncmp(line, "* OK", 4) ? ZBX_TCP_EXPECT_OK : ZBX_TCP_EXPECT_FAIL;
281 }
282 
check_service(AGENT_REQUEST * request,const char * default_addr,AGENT_RESULT * result,int perf)283 int	check_service(AGENT_REQUEST *request, const char *default_addr, AGENT_RESULT *result, int perf)
284 {
285 	unsigned short	port = 0;
286 	char		*service, *ip_str, ip[MAX_ZBX_DNSNAME_LEN + 1], *port_str;
287 	int		value_int, ret = SYSINFO_RET_FAIL;
288 	double		check_time;
289 
290 	check_time = zbx_time();
291 
292 	if (3 < request->nparam)
293 	{
294 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
295 		return SYSINFO_RET_FAIL;
296 	}
297 
298 	service = get_rparam(request, 0);
299 	ip_str = get_rparam(request, 1);
300 	port_str = get_rparam(request, 2);
301 
302 	if (NULL == service || '\0' == *service)
303 	{
304 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
305 		return SYSINFO_RET_FAIL;
306 	}
307 
308 	if (NULL == ip_str || '\0' == *ip_str)
309 		strscpy(ip, default_addr);
310 	else
311 		strscpy(ip, ip_str);
312 
313 	if (NULL != port_str && '\0' != *port_str && SUCCEED != is_ushort(port_str, &port))
314 	{
315 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
316 		return SYSINFO_RET_FAIL;
317 	}
318 
319 	if (0 == strncmp("net.tcp.service", get_rkey(request), 15))
320 	{
321 		if (0 == strcmp(service, "ssh"))
322 		{
323 			if (NULL == port_str || '\0' == *port_str)
324 				port = ZBX_DEFAULT_SSH_PORT;
325 			ret = check_ssh(ip, port, CONFIG_TIMEOUT, &value_int);
326 		}
327 		else if (0 == strcmp(service, "ldap"))
328 		{
329 #ifdef HAVE_LDAP
330 			if (NULL == port_str || '\0' == *port_str)
331 				port = ZBX_DEFAULT_LDAP_PORT;
332 			ret = check_ldap(ip, port, CONFIG_TIMEOUT, &value_int);
333 #else
334 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Support for LDAP check was not compiled in."));
335 #endif
336 		}
337 		else if (0 == strcmp(service, "smtp"))
338 		{
339 			if (NULL == port_str || '\0' == *port_str)
340 				port = ZBX_DEFAULT_SMTP_PORT;
341 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, validate_smtp, "QUIT\r\n", &value_int);
342 		}
343 		else if (0 == strcmp(service, "ftp"))
344 		{
345 			if (NULL == port_str || '\0' == *port_str)
346 				port = ZBX_DEFAULT_FTP_PORT;
347 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, validate_ftp, "QUIT\r\n", &value_int);
348 		}
349 		else if (0 == strcmp(service, "http"))
350 		{
351 			if (NULL == port_str || '\0' == *port_str)
352 				port = ZBX_DEFAULT_HTTP_PORT;
353 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, NULL, NULL, &value_int);
354 		}
355 		else if (0 == strcmp(service, "pop"))
356 		{
357 			if (NULL == port_str || '\0' == *port_str)
358 				port = ZBX_DEFAULT_POP_PORT;
359 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, validate_pop, "QUIT\r\n", &value_int);
360 		}
361 		else if (0 == strcmp(service, "nntp"))
362 		{
363 			if (NULL == port_str || '\0' == *port_str)
364 				port = ZBX_DEFAULT_NNTP_PORT;
365 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, validate_nntp, "QUIT\r\n", &value_int);
366 		}
367 		else if (0 == strcmp(service, "imap"))
368 		{
369 			if (NULL == port_str || '\0' == *port_str)
370 				port = ZBX_DEFAULT_IMAP_PORT;
371 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, validate_imap, "a1 LOGOUT\r\n", &value_int);
372 		}
373 		else if (0 == strcmp(service, "tcp"))
374 		{
375 			if (NULL == port_str || '\0' == *port_str)
376 			{
377 				SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
378 				return SYSINFO_RET_FAIL;
379 			}
380 			ret = tcp_expect(ip, port, CONFIG_TIMEOUT, NULL, NULL, NULL, &value_int);
381 		}
382 		else if (0 == strcmp(service, "https"))
383 		{
384 #ifdef HAVE_LIBCURL
385 			if (NULL == port_str || '\0' == *port_str)
386 				port = ZBX_DEFAULT_HTTPS_PORT;
387 			ret = check_https(ip, port, CONFIG_TIMEOUT, &value_int);
388 #else
389 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Support for HTTPS check was not compiled in."));
390 #endif
391 		}
392 		else if (0 == strcmp(service, "telnet"))
393 		{
394 			if (NULL == port_str || '\0' == *port_str)
395 				port = ZBX_DEFAULT_TELNET_PORT;
396 			ret = check_telnet(ip, port, CONFIG_TIMEOUT, &value_int);
397 		}
398 		else
399 		{
400 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
401 			return ret;
402 		}
403 	}
404 	else	/* net.udp.service */
405 	{
406 		if (0 == strcmp(service, "ntp"))
407 		{
408 			if (NULL == port_str || '\0' == *port_str)
409 				port = ZBX_DEFAULT_NTP_PORT;
410 			ret = check_ntp(ip, port, CONFIG_TIMEOUT, &value_int);
411 		}
412 		else
413 		{
414 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
415 			return ret;
416 		}
417 	}
418 
419 	if (SYSINFO_RET_OK == ret)
420 	{
421 		if (0 != perf)
422 		{
423 			if (0 != value_int)
424 			{
425 				check_time = zbx_time() - check_time;
426 
427 				if (ZBX_FLOAT_PRECISION > check_time)
428 					check_time = ZBX_FLOAT_PRECISION;
429 
430 				SET_DBL_RESULT(result, check_time);
431 			}
432 			else
433 				SET_DBL_RESULT(result, 0.0);
434 		}
435 		else
436 			SET_UI64_RESULT(result, value_int);
437 	}
438 
439 	return ret;
440 }
441 
442 /* Examples:
443  *
444  *   net.tcp.service[ssh]
445  *   net.tcp.service[smtp,127.0.0.1]
446  *   net.tcp.service[ssh,127.0.0.1,22]
447  *
448  *   net.udp.service[ntp]
449  *   net.udp.service[ntp,127.0.0.1]
450  *   net.udp.service[ntp,127.0.0.1,123]
451  *
452  *   net.tcp.service.perf[ssh]
453  *   net.tcp.service.perf[smtp,127.0.0.1]
454  *   net.tcp.service.perf[ssh,127.0.0.1,22]
455  *
456  *   net.udp.service.perf[ntp]
457  *   net.udp.service.perf[ntp,127.0.0.1]
458  *   net.udp.service.perf[ntp,127.0.0.1,123]
459  *
460  * The old name for these checks is check_service[*].
461  */
462 
CHECK_SERVICE(AGENT_REQUEST * request,AGENT_RESULT * result)463 int	CHECK_SERVICE(AGENT_REQUEST *request, AGENT_RESULT *result)
464 {
465 	return check_service(request, "127.0.0.1", result, 0);
466 }
467 
CHECK_SERVICE_PERF(AGENT_REQUEST * request,AGENT_RESULT * result)468 int	CHECK_SERVICE_PERF(AGENT_REQUEST *request, AGENT_RESULT *result)
469 {
470 	return check_service(request, "127.0.0.1", result, 1);
471 }
472