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 
22 #include "threads.h"
23 #include "comms.h"
24 #include "cfg.h"
25 #include "log.h"
26 #include "zbxgetopt.h"
27 #include "zbxcrypto.h"
28 
29 #ifndef _WINDOWS
30 #	include "zbxnix.h"
31 #endif
32 
33 const char	*progname = NULL;
34 const char	title_message[] = "zabbix_get";
35 const char	syslog_app_name[] = "zabbix_get";
36 const char	*usage_message[] = {
37 	"-s host-name-or-IP", "[-p port-number]", "[-I IP-address]", "-k item-key", NULL,
38 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
39 	"-s host-name-or-IP", "[-p port-number]", "[-I IP-address]", "--tls-connect cert", "--tls-ca-file CA-file",
40 	"[--tls-crl-file CRL-file]", "[--tls-agent-cert-issuer cert-issuer]", "[--tls-agent-cert-subject cert-subject]",
41 	"--tls-cert-file cert-file", "--tls-key-file key-file",
42 #if defined(HAVE_OPENSSL)
43 	"[--tls-cipher13 cipher-string]",
44 #endif
45 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
46 	"[--tls-cipher cipher-string]",
47 #endif
48 	"-k item-key", NULL,
49 	"-s host-name-or-IP", "[-p port-number]", "[-I IP-address]", "--tls-connect psk",
50 	"--tls-psk-identity PSK-identity", "--tls-psk-file PSK-file",
51 #if defined(HAVE_OPENSSL)
52 	"[--tls-cipher13 cipher-string]",
53 #endif
54 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
55 	"[--tls-cipher cipher-string]",
56 #endif
57 	"-k item-key", NULL,
58 #endif
59 	"-h", NULL,
60 	"-V", NULL,
61 	NULL	/* end of text */
62 };
63 
64 unsigned char	program_type	= ZBX_PROGRAM_TYPE_GET;
65 
66 const char	*help_message[] = {
67 	"Get data from Zabbix agent.",
68 	"",
69 	"General options:",
70 	"  -s --host host-name-or-IP  Specify host name or IP address of a host",
71 	"  -p --port port-number      Specify port number of agent running on the host",
72 	"                             (default: " ZBX_DEFAULT_AGENT_PORT_STR ")",
73 	"  -I --source-address IP-address   Specify source IP address",
74 	"",
75 	"  -k --key item-key          Specify key of the item to retrieve value for",
76 	"",
77 	"  -h --help                  Display this help message",
78 	"  -V --version               Display version number",
79 	"",
80 	"TLS connection options:",
81 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
82 	"  --tls-connect value        How to connect to agent. Values:",
83 	"                               unencrypted - connect without encryption",
84 	"                                             (default)",
85 	"                               psk         - connect using TLS and a pre-shared",
86 	"                                             key",
87 	"                               cert        - connect using TLS and a",
88 	"                                             certificate",
89 	"",
90 	"  --tls-ca-file CA-file      Full pathname of a file containing the top-level",
91 	"                             CA(s) certificates for peer certificate",
92 	"                             verification",
93 	"",
94 	"  --tls-crl-file CRL-file    Full pathname of a file containing revoked",
95 	"                             certificates",
96 	"",
97 	"  --tls-agent-cert-issuer cert-issuer   Allowed agent certificate issuer",
98 	"",
99 	"  --tls-agent-cert-subject cert-subject   Allowed agent certificate subject",
100 	"",
101 	"  --tls-cert-file cert-file  Full pathname of a file containing the certificate",
102 	"                             or certificate chain",
103 	"",
104 	"  --tls-key-file key-file    Full pathname of a file containing the private key",
105 	"",
106 	"  --tls-psk-identity PSK-identity   Unique, case sensitive string used to",
107 	"                             identify the pre-shared key",
108 	"",
109 	"  --tls-psk-file PSK-file    Full pathname of a file containing the pre-shared",
110 	"                             key",
111 #if defined(HAVE_OPENSSL)
112 	"",
113 	"  --tls-cipher13             Cipher string for OpenSSL 1.1.1 or newer for",
114 	"                             TLS 1.3. Override the default ciphersuite",
115 	"                             selection criteria. This option is not available",
116 	"                             if OpenSSL version is less than 1.1.1",
117 #endif
118 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
119 	"",
120 	"  --tls-cipher               GnuTLS priority string (for TLS 1.2 and up) or",
121 	"                             OpenSSL cipher string (only for TLS 1.2).",
122 	"                             Override the default ciphersuite selection",
123 	"                             criteria",
124 #endif
125 #else
126 	"  Not available. This 'zabbix_get' was compiled without TLS support",
127 #endif
128 	"",
129 	"Example(s):",
130 	"  zabbix_get -s 127.0.0.1 -p " ZBX_DEFAULT_AGENT_PORT_STR " -k \"system.cpu.load[all,avg1]\"",
131 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
132 	"",
133 	"  zabbix_get -s 127.0.0.1 -p " ZBX_DEFAULT_AGENT_PORT_STR " -k \"system.cpu.load[all,avg1]\" \\",
134 	"    --tls-connect cert --tls-ca-file /home/zabbix/zabbix_ca_file \\",
135 	"    --tls-agent-cert-issuer \\",
136 	"    \"CN=Signing CA,OU=IT operations,O=Example Corp,DC=example,DC=com\" \\",
137 	"    --tls-agent-cert-subject \\",
138 	"    \"CN=server1,OU=IT operations,O=Example Corp,DC=example,DC=com\" \\",
139 	"    --tls-cert-file /home/zabbix/zabbix_get.crt \\",
140 	"    --tls-key-file /home/zabbix/zabbix_get.key",
141 	"",
142 	"  zabbix_get -s 127.0.0.1 -p " ZBX_DEFAULT_AGENT_PORT_STR " -k \"system.cpu.load[all,avg1]\" \\",
143 	"    --tls-connect psk --tls-psk-identity \"PSK ID Zabbix agentd\" \\",
144 	"    --tls-psk-file /home/zabbix/zabbix_agentd.psk",
145 #endif
146 	NULL	/* end of text */
147 };
148 
149 /* TLS parameters */
150 unsigned int	configured_tls_connect_mode = ZBX_TCP_SEC_UNENCRYPTED;
151 unsigned int	configured_tls_accept_modes = ZBX_TCP_SEC_UNENCRYPTED;	/* not used in zabbix_get, just for linking */
152 									/* with tls.c */
153 char	*CONFIG_TLS_CONNECT		= NULL;
154 char	*CONFIG_TLS_ACCEPT		= NULL;	/* not used in zabbix_get, just for linking with tls.c */
155 char	*CONFIG_TLS_CA_FILE		= NULL;
156 char	*CONFIG_TLS_CRL_FILE		= NULL;
157 char	*CONFIG_TLS_SERVER_CERT_ISSUER	= NULL;
158 char	*CONFIG_TLS_SERVER_CERT_SUBJECT	= NULL;
159 char	*CONFIG_TLS_CERT_FILE		= NULL;
160 char	*CONFIG_TLS_KEY_FILE		= NULL;
161 char	*CONFIG_TLS_PSK_IDENTITY	= NULL;
162 char	*CONFIG_TLS_PSK_FILE		= NULL;
163 
164 char	*CONFIG_TLS_CIPHER_CERT13	= NULL;	/* not used in zabbix_get, just for linking with tls.c */
165 char	*CONFIG_TLS_CIPHER_CERT		= NULL;	/* not used in zabbix_get, just for linking with tls.c */
166 char	*CONFIG_TLS_CIPHER_PSK13	= NULL;	/* not used in zabbix_get, just for linking with tls.c */
167 char	*CONFIG_TLS_CIPHER_PSK		= NULL;	/* not used in zabbix_get, just for linking with tls.c */
168 char	*CONFIG_TLS_CIPHER_ALL13	= NULL;	/* not used in zabbix_get, just for linking with tls.c */
169 char	*CONFIG_TLS_CIPHER_ALL		= NULL;	/* not used in zabbix_get, just for linking with tls.c */
170 char	*CONFIG_TLS_CIPHER_CMD13	= NULL;	/* parameter '--tls-cipher13' from zabbix_get command line */
171 char	*CONFIG_TLS_CIPHER_CMD		= NULL;	/* parameter '--tls-cipher' from zabbix_get command line */
172 
173 int	CONFIG_PASSIVE_FORKS		= 0;	/* not used in zabbix_get, just for linking with tls.c */
174 int	CONFIG_ACTIVE_FORKS		= 0;	/* not used in zabbix_get, just for linking with tls.c */
175 
176 int	CONFIG_TCP_MAX_BACKLOG_SIZE	= SOMAXCONN;
177 
178 /* COMMAND LINE OPTIONS */
179 
180 /* long options */
181 struct zbx_option	longopts[] =
182 {
183 	{"host",			1,	NULL,	's'},
184 	{"port",			1,	NULL,	'p'},
185 	{"key",				1,	NULL,	'k'},
186 	{"source-address",		1,	NULL,	'I'},
187 	{"help",			0,	NULL,	'h'},
188 	{"version",			0,	NULL,	'V'},
189 	{"tls-connect",			1,	NULL,	'1'},
190 	{"tls-ca-file",			1,	NULL,	'2'},
191 	{"tls-crl-file",		1,	NULL,	'3'},
192 	{"tls-agent-cert-issuer",	1,	NULL,	'4'},
193 	{"tls-agent-cert-subject",	1,	NULL,	'5'},
194 	{"tls-cert-file",		1,	NULL,	'6'},
195 	{"tls-key-file",		1,	NULL,	'7'},
196 	{"tls-psk-identity",		1,	NULL,	'8'},
197 	{"tls-psk-file",		1,	NULL,	'9'},
198 	{"tls-cipher13",		1,	NULL,	'A'},
199 	{"tls-cipher",			1,	NULL,	'B'},
200 	{NULL}
201 };
202 
203 /* short options */
204 static char	shortopts[] = "s:p:k:I:hV";
205 
206 /* end of COMMAND LINE OPTIONS */
207 
208 #if !defined(_WINDOWS)
209 
210 /******************************************************************************
211  *                                                                            *
212  * Function: get_signal_handler                                               *
213  *                                                                            *
214  * Purpose: process signals                                                   *
215  *                                                                            *
216  * Parameters: sig - signal ID                                                *
217  *                                                                            *
218  * Return value:                                                              *
219  *                                                                            *
220  * Comments:                                                                  *
221  *                                                                            *
222  ******************************************************************************/
get_signal_handler(int sig)223 static void	get_signal_handler(int sig)
224 {
225 	if (SIGPIPE == sig)	/* this signal is raised when peer closes connection because of access restrictions */
226 		return;
227 
228 	if (SIGALRM == sig)
229 		zbx_error("Timeout while executing operation");
230 
231 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
232 	if (ZBX_TCP_SEC_UNENCRYPTED != configured_tls_connect_mode)
233 		zbx_tls_free_on_signal();
234 #endif
235 	exit(EXIT_FAILURE);
236 }
237 
238 #endif /* not WINDOWS */
239 
240 /******************************************************************************
241  *                                                                            *
242  * Function: get_value                                                        *
243  *                                                                            *
244  * Purpose: connect to Zabbix agent, receive and print value                  *
245  *                                                                            *
246  * Parameters: host - server name or IP address                               *
247  *             port - port number                                             *
248  *             key  - item's key                                              *
249  *                                                                            *
250  ******************************************************************************/
get_value(const char * source_ip,const char * host,unsigned short port,const char * key)251 static int	get_value(const char *source_ip, const char *host, unsigned short port, const char *key)
252 {
253 	zbx_socket_t	s;
254 	int		ret;
255 	ssize_t		bytes_received = -1;
256 	char		*tls_arg1, *tls_arg2;
257 
258 	switch (configured_tls_connect_mode)
259 	{
260 		case ZBX_TCP_SEC_UNENCRYPTED:
261 			tls_arg1 = NULL;
262 			tls_arg2 = NULL;
263 			break;
264 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
265 		case ZBX_TCP_SEC_TLS_CERT:
266 			tls_arg1 = CONFIG_TLS_SERVER_CERT_ISSUER;
267 			tls_arg2 = CONFIG_TLS_SERVER_CERT_SUBJECT;
268 			break;
269 		case ZBX_TCP_SEC_TLS_PSK:
270 			tls_arg1 = CONFIG_TLS_PSK_IDENTITY;
271 			tls_arg2 = NULL;	/* zbx_tls_connect() will find PSK */
272 			break;
273 #endif
274 		default:
275 			THIS_SHOULD_NEVER_HAPPEN;
276 			return FAIL;
277 	}
278 
279 	if (SUCCEED == (ret = zbx_tcp_connect(&s, source_ip, host, port, GET_SENDER_TIMEOUT,
280 			configured_tls_connect_mode, tls_arg1, tls_arg2)))
281 	{
282 		if (SUCCEED == (ret = zbx_tcp_send(&s, key)))
283 		{
284 			if (0 < (bytes_received = zbx_tcp_recv_ext(&s, 0, 0)))
285 			{
286 				if (0 == strcmp(s.buffer, ZBX_NOTSUPPORTED) && sizeof(ZBX_NOTSUPPORTED) < s.read_bytes)
287 				{
288 					zbx_rtrim(s.buffer + sizeof(ZBX_NOTSUPPORTED), "\r\n");
289 					printf("%s: %s\n", s.buffer, s.buffer + sizeof(ZBX_NOTSUPPORTED));
290 				}
291 				else
292 				{
293 					zbx_rtrim(s.buffer, "\r\n");
294 					printf("%s\n", s.buffer);
295 				}
296 			}
297 			else
298 			{
299 				if (0 == bytes_received)
300 					zbx_error("Check access restrictions in Zabbix agent configuration");
301 				ret = FAIL;
302 			}
303 		}
304 
305 		zbx_tcp_close(&s);
306 
307 		if (SUCCEED != ret && 0 != bytes_received)
308 		{
309 			zbx_error("Get value error: %s", zbx_socket_strerror());
310 			zbx_error("Check access restrictions in Zabbix agent configuration");
311 		}
312 	}
313 	else
314 		zbx_error("Get value error: %s", zbx_socket_strerror());
315 
316 	return ret;
317 }
318 
319 /******************************************************************************
320  *                                                                            *
321  * Function: main                                                             *
322  *                                                                            *
323  * Purpose: main function                                                     *
324  *                                                                            *
325  * Parameters:                                                                *
326  *                                                                            *
327  * Return value:                                                              *
328  *                                                                            *
329  * Comments:                                                                  *
330  *                                                                            *
331  ******************************************************************************/
main(int argc,char ** argv)332 int	main(int argc, char **argv)
333 {
334 	int		i, ret = SUCCEED;
335 	char		*host = NULL, *key = NULL, *source_ip = NULL, ch;
336 	unsigned short	opt_count[256] = {0}, port = ZBX_DEFAULT_AGENT_PORT;
337 #if defined(_WINDOWS)
338 	char		*error = NULL;
339 #endif
340 
341 #if !defined(_WINDOWS) && (defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL))
342 	if (SUCCEED != zbx_coredump_disable())
343 	{
344 		zbx_error("cannot disable core dump, exiting...");
345 		exit(EXIT_FAILURE);
346 	}
347 #endif
348 	progname = get_program_name(argv[0]);
349 
350 	/* parse the command-line */
351 	while ((char)EOF != (ch = (char)zbx_getopt_long(argc, argv, shortopts, longopts, NULL)))
352 	{
353 		opt_count[(unsigned char)ch]++;
354 
355 		switch (ch)
356 		{
357 			case 'k':
358 				if (NULL == key)
359 					key = zbx_strdup(NULL, zbx_optarg);
360 				break;
361 			case 'p':
362 				port = (unsigned short)atoi(zbx_optarg);
363 				break;
364 			case 's':
365 				if (NULL == host)
366 					host = zbx_strdup(NULL, zbx_optarg);
367 				break;
368 			case 'I':
369 				if (NULL == source_ip)
370 					source_ip = zbx_strdup(NULL, zbx_optarg);
371 				break;
372 			case 'h':
373 				help();
374 				exit(EXIT_SUCCESS);
375 				break;
376 			case 'V':
377 				version();
378 				exit(EXIT_SUCCESS);
379 				break;
380 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
381 			case '1':
382 				CONFIG_TLS_CONNECT = zbx_strdup(CONFIG_TLS_CONNECT, zbx_optarg);
383 				break;
384 			case '2':
385 				CONFIG_TLS_CA_FILE = zbx_strdup(CONFIG_TLS_CA_FILE, zbx_optarg);
386 				break;
387 			case '3':
388 				CONFIG_TLS_CRL_FILE = zbx_strdup(CONFIG_TLS_CRL_FILE, zbx_optarg);
389 				break;
390 			case '4':
391 				CONFIG_TLS_SERVER_CERT_ISSUER = zbx_strdup(CONFIG_TLS_SERVER_CERT_ISSUER, zbx_optarg);
392 				break;
393 			case '5':
394 				CONFIG_TLS_SERVER_CERT_SUBJECT = zbx_strdup(CONFIG_TLS_SERVER_CERT_SUBJECT, zbx_optarg);
395 				break;
396 			case '6':
397 				CONFIG_TLS_CERT_FILE = zbx_strdup(CONFIG_TLS_CERT_FILE, zbx_optarg);
398 				break;
399 			case '7':
400 				CONFIG_TLS_KEY_FILE = zbx_strdup(CONFIG_TLS_KEY_FILE, zbx_optarg);
401 				break;
402 			case '8':
403 				CONFIG_TLS_PSK_IDENTITY = zbx_strdup(CONFIG_TLS_PSK_IDENTITY, zbx_optarg);
404 				break;
405 			case '9':
406 				CONFIG_TLS_PSK_FILE = zbx_strdup(CONFIG_TLS_PSK_FILE, zbx_optarg);
407 				break;
408 			case 'A':
409 #if defined(HAVE_OPENSSL)
410 				CONFIG_TLS_CIPHER_CMD13 = zbx_strdup(CONFIG_TLS_CIPHER_CMD13, zbx_optarg);
411 #elif defined(HAVE_GNUTLS)
412 				zbx_error("parameter \"--tls-cipher13\" can be used with OpenSSL 1.1.1 or newer."
413 						" zabbix_get was compiled with GnuTLS");
414 				exit(EXIT_FAILURE);
415 #endif
416 				break;
417 			case 'B':
418 				CONFIG_TLS_CIPHER_CMD = zbx_strdup(CONFIG_TLS_CIPHER_CMD, zbx_optarg);
419 				break;
420 #else
421 			case '1':
422 			case '2':
423 			case '3':
424 			case '4':
425 			case '5':
426 			case '6':
427 			case '7':
428 			case '8':
429 			case '9':
430 			case 'A':
431 			case 'B':
432 				zbx_error("TLS parameters cannot be used: 'zabbix_get' was compiled without TLS"
433 						" support");
434 				exit(EXIT_FAILURE);
435 				break;
436 #endif
437 			default:
438 				usage();
439 				exit(EXIT_FAILURE);
440 				break;
441 		}
442 	}
443 
444 #if defined(_WINDOWS)
445 	if (SUCCEED != zbx_socket_start(&error))
446 	{
447 		zbx_error(error);
448 		zbx_free(error);
449 		exit(EXIT_FAILURE);
450 	}
451 #endif
452 
453 	if (NULL == host || NULL == key)
454 	{
455 		usage();
456 		ret = FAIL;
457 	}
458 
459 	/* every option may be specified only once */
460 
461 	for (i = 0; NULL != longopts[i].name; i++)
462 	{
463 		ch = longopts[i].val;
464 
465 		if (1 < opt_count[(unsigned char)ch])
466 		{
467 			if (NULL == strchr(shortopts, ch))
468 				zbx_error("option \"--%s\" specified multiple times", longopts[i].name);
469 			else
470 				zbx_error("option \"-%c\" or \"--%s\" specified multiple times", ch, longopts[i].name);
471 
472 			ret = FAIL;
473 		}
474 	}
475 
476 	if (FAIL == ret)
477 		goto out;
478 
479 	/* Parameters which are not option values are invalid. The check relies on zbx_getopt_internal() which */
480 	/* always permutes command line arguments regardless of POSIXLY_CORRECT environment variable. */
481 	if (argc > zbx_optind)
482 	{
483 		for (i = zbx_optind; i < argc; i++)
484 			zbx_error("invalid parameter \"%s\"", argv[i]);
485 
486 		ret = FAIL;
487 	}
488 
489 	if (FAIL == ret)
490 	{
491 		printf("Try '%s --help' for more information.\n", progname);
492 		goto out;
493 	}
494 
495 	if (NULL != CONFIG_TLS_CONNECT || NULL != CONFIG_TLS_CA_FILE || NULL != CONFIG_TLS_CRL_FILE ||
496 			NULL != CONFIG_TLS_SERVER_CERT_ISSUER || NULL != CONFIG_TLS_SERVER_CERT_SUBJECT ||
497 			NULL != CONFIG_TLS_CERT_FILE || NULL != CONFIG_TLS_KEY_FILE ||
498 			NULL != CONFIG_TLS_PSK_IDENTITY || NULL != CONFIG_TLS_PSK_FILE ||
499 			NULL != CONFIG_TLS_CIPHER_CMD13 || NULL != CONFIG_TLS_CIPHER_CMD)
500 	{
501 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
502 		zbx_tls_validate_config();
503 
504 		if (ZBX_TCP_SEC_UNENCRYPTED != configured_tls_connect_mode)
505 		{
506 #if defined(_WINDOWS)
507 			zbx_tls_init_parent();
508 #endif
509 			zbx_tls_init_child();
510 		}
511 #endif
512 	}
513 #if !defined(_WINDOWS)
514 	signal(SIGINT, get_signal_handler);
515 	signal(SIGQUIT, get_signal_handler);
516 	signal(SIGTERM, get_signal_handler);
517 	signal(SIGHUP, get_signal_handler);
518 	signal(SIGALRM, get_signal_handler);
519 	signal(SIGPIPE, get_signal_handler);
520 #endif
521 	ret = get_value(source_ip, host, port, key);
522 out:
523 	zbx_free(host);
524 	zbx_free(key);
525 	zbx_free(source_ip);
526 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
527 	if (ZBX_TCP_SEC_UNENCRYPTED != configured_tls_connect_mode)
528 	{
529 		zbx_tls_free();
530 #if defined(_WINDOWS)
531 		zbx_tls_library_deinit();
532 #endif
533 	}
534 #endif
535 #if defined(_WINDOWS)
536 	while (0 == WSACleanup())
537 		;
538 #endif
539 
540 	return SUCCEED == ret ? EXIT_SUCCESS : EXIT_FAILURE;
541 }
542