/* ** Zabbix ** Copyright (C) 2001-2023 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ #include "zbxstr.h" #include "zbxnum.h" #include "zbxcomms.h" #include "zbxgetopt.h" #include "zbxcrypto.h" #ifndef _WINDOWS # include "zbxnix.h" #endif const char *progname = NULL; const char title_message[] = "zabbix_get"; const char syslog_app_name[] = "zabbix_get"; const char *usage_message[] = { "-s host-name-or-IP", "[-p port-number]", "[-I IP-address]", "[-t timeout]", "-k item-key", NULL, #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) "-s host-name-or-IP", "[-p port-number]", "[-I IP-address]", "[-t timeout]", "--tls-connect cert", "--tls-ca-file CA-file", "[--tls-crl-file CRL-file]", "[--tls-agent-cert-issuer cert-issuer]", "[--tls-agent-cert-subject cert-subject]", "--tls-cert-file cert-file", "--tls-key-file key-file", #if defined(HAVE_OPENSSL) "[--tls-cipher13 cipher-string]", #endif #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) "[--tls-cipher cipher-string]", #endif "-k item-key", NULL, "-s host-name-or-IP", "[-p port-number]", "[-I IP-address]", "[-t timeout]", "--tls-connect psk", "--tls-psk-identity PSK-identity", "--tls-psk-file PSK-file", #if defined(HAVE_OPENSSL) "[--tls-cipher13 cipher-string]", #endif #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) "[--tls-cipher cipher-string]", #endif "-k item-key", NULL, #endif "-h", NULL, "-V", NULL, NULL /* end of text */ }; unsigned char program_type = ZBX_PROGRAM_TYPE_GET; static unsigned char get_program_type(void) { return program_type; } #define CONFIG_GET_TIMEOUT_MIN 1 #define CONFIG_GET_TIMEOUT_MAX 30 #define CONFIG_GET_TIMEOUT_MIN_STR ZBX_STR(CONFIG_GET_TIMEOUT_MIN) #define CONFIG_GET_TIMEOUT_MAX_STR ZBX_STR(CONFIG_GET_TIMEOUT_MAX) static int CONFIG_GET_TIMEOUT = CONFIG_GET_TIMEOUT_MAX; const char *help_message[] = { "Get data from Zabbix agent.", "", "General options:", " -s --host host-name-or-IP Specify host name or IP address of a host", " -p --port port-number Specify port number of agent running on the host", " (default: " ZBX_DEFAULT_AGENT_PORT_STR ")", " -I --source-address IP-address Specify source IP address", "", " -t --timeout seconds Specify timeout. Valid range: " CONFIG_GET_TIMEOUT_MIN_STR "-" CONFIG_GET_TIMEOUT_MAX_STR " seconds", " (default: " CONFIG_GET_TIMEOUT_MAX_STR " seconds)", "", " -k --key item-key Specify key of the item to retrieve value for", "", " -h --help Display this help message", " -V --version Display version number", "", "TLS connection options:", #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) " --tls-connect value How to connect to agent. Values:", " unencrypted - connect without encryption", " (default)", " psk - connect using TLS and a pre-shared", " key", " cert - connect using TLS and a", " certificate", "", " --tls-ca-file CA-file Full pathname of a file containing the top-level", " CA(s) certificates for peer certificate", " verification", "", " --tls-crl-file CRL-file Full pathname of a file containing revoked", " certificates", "", " --tls-agent-cert-issuer cert-issuer Allowed agent certificate issuer", "", " --tls-agent-cert-subject cert-subject Allowed agent certificate subject", "", " --tls-cert-file cert-file Full pathname of a file containing the certificate", " or certificate chain", "", " --tls-key-file key-file Full pathname of a file containing the private key", "", " --tls-psk-identity PSK-identity Unique, case sensitive string used to", " identify the pre-shared key", "", " --tls-psk-file PSK-file Full pathname of a file containing the pre-shared", " key", #if defined(HAVE_OPENSSL) "", " --tls-cipher13 Cipher string for OpenSSL 1.1.1 or newer for", " TLS 1.3. Override the default ciphersuite", " selection criteria. This option is not available", " if OpenSSL version is less than 1.1.1", #endif #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) "", " --tls-cipher GnuTLS priority string (for TLS 1.2 and up) or", " OpenSSL cipher string (only for TLS 1.2).", " Override the default ciphersuite selection", " criteria", #endif #else " Not available. This 'zabbix_get' was compiled without TLS support", #endif "", "Example(s):", " zabbix_get -s 127.0.0.1 -p " ZBX_DEFAULT_AGENT_PORT_STR " -k \"system.cpu.load[all,avg1]\"", #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) "", " zabbix_get -s 127.0.0.1 -p " ZBX_DEFAULT_AGENT_PORT_STR " -k \"system.cpu.load[all,avg1]\" \\", " --tls-connect cert --tls-ca-file /home/zabbix/zabbix_ca_file \\", " --tls-agent-cert-issuer \\", " \"CN=Signing CA,OU=IT operations,O=Example Corp,DC=example,DC=com\" \\", " --tls-agent-cert-subject \\", " \"CN=server1,OU=IT operations,O=Example Corp,DC=example,DC=com\" \\", " --tls-cert-file /home/zabbix/zabbix_get.crt \\", " --tls-key-file /home/zabbix/zabbix_get.key", "", " zabbix_get -s 127.0.0.1 -p " ZBX_DEFAULT_AGENT_PORT_STR " -k \"system.cpu.load[all,avg1]\" \\", " --tls-connect psk --tls-psk-identity \"PSK ID Zabbix agentd\" \\", " --tls-psk-file /home/zabbix/zabbix_agentd.psk", #endif NULL /* end of text */ }; static zbx_config_tls_t *zbx_config_tls = NULL; int CONFIG_TCP_MAX_BACKLOG_SIZE = SOMAXCONN; /* COMMAND LINE OPTIONS */ /* long options */ struct zbx_option longopts[] = { {"host", 1, NULL, 's'}, {"port", 1, NULL, 'p'}, {"key", 1, NULL, 'k'}, {"source-address", 1, NULL, 'I'}, {"timeout", 1, NULL, 't'}, {"help", 0, NULL, 'h'}, {"version", 0, NULL, 'V'}, {"tls-connect", 1, NULL, '1'}, {"tls-ca-file", 1, NULL, '2'}, {"tls-crl-file", 1, NULL, '3'}, {"tls-agent-cert-issuer", 1, NULL, '4'}, {"tls-agent-cert-subject", 1, NULL, '5'}, {"tls-cert-file", 1, NULL, '6'}, {"tls-key-file", 1, NULL, '7'}, {"tls-psk-identity", 1, NULL, '8'}, {"tls-psk-file", 1, NULL, '9'}, {"tls-cipher13", 1, NULL, 'A'}, {"tls-cipher", 1, NULL, 'B'}, {NULL} }; /* short options */ static char shortopts[] = "s:p:k:I:t:hV"; /* end of COMMAND LINE OPTIONS */ #if !defined(_WINDOWS) /****************************************************************************** * * * Purpose: process signals * * * * Parameters: sig - signal ID * * * ******************************************************************************/ static void get_signal_handler(int sig) { if (SIGPIPE == sig) /* this signal is raised when peer closes connection because of access restrictions */ return; if (SIGALRM == sig) zbx_error("Timeout while executing operation"); #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) if (ZBX_TCP_SEC_UNENCRYPTED != zbx_config_tls->connect_mode) zbx_tls_free_on_signal(); #endif exit(EXIT_FAILURE); } #endif /* not WINDOWS */ /****************************************************************************** * * * Purpose: connect to Zabbix agent, receive and print value * * * * Parameters: host - server name or IP address * * port - port number * * key - item's key * * * ******************************************************************************/ static int get_value(const char *source_ip, const char *host, unsigned short port, const char *key) { zbx_socket_t s; int ret; ssize_t bytes_received = -1; char *tls_arg1, *tls_arg2; switch (zbx_config_tls->connect_mode) { case ZBX_TCP_SEC_UNENCRYPTED: tls_arg1 = NULL; tls_arg2 = NULL; break; #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) case ZBX_TCP_SEC_TLS_CERT: tls_arg1 = zbx_config_tls->server_cert_issuer; tls_arg2 = zbx_config_tls->server_cert_subject; break; case ZBX_TCP_SEC_TLS_PSK: tls_arg1 = zbx_config_tls->psk_identity; tls_arg2 = NULL; /* zbx_tls_connect() will find PSK */ break; #endif default: THIS_SHOULD_NEVER_HAPPEN; return FAIL; } if (SUCCEED == (ret = zbx_tcp_connect(&s, source_ip, host, port, CONFIG_GET_TIMEOUT, zbx_config_tls->connect_mode, tls_arg1, tls_arg2))) { if (SUCCEED == (ret = zbx_tcp_send(&s, key))) { if (0 < (bytes_received = zbx_tcp_recv_ext(&s, 0, 0))) { if (0 == strcmp(s.buffer, ZBX_NOTSUPPORTED) && sizeof(ZBX_NOTSUPPORTED) < s.read_bytes) { zbx_rtrim(s.buffer + sizeof(ZBX_NOTSUPPORTED), "\r\n"); printf("%s: %s\n", s.buffer, s.buffer + sizeof(ZBX_NOTSUPPORTED)); } else { zbx_rtrim(s.buffer, "\r\n"); printf("%s\n", s.buffer); } } else { if (0 == bytes_received) zbx_error("Check access restrictions in Zabbix agent configuration"); ret = FAIL; } } zbx_tcp_close(&s); if (SUCCEED != ret && 0 != bytes_received) { zbx_error("Get value error: %s", zbx_socket_strerror()); zbx_error("Check access restrictions in Zabbix agent configuration"); } } else zbx_error("Get value error: %s", zbx_socket_strerror()); return ret; } int main(int argc, char **argv) { int i, ret = SUCCEED; char *host = NULL, *key = NULL, *source_ip = NULL, ch; unsigned short opt_count[256] = {0}, port = ZBX_DEFAULT_AGENT_PORT; #if defined(_WINDOWS) char *error = NULL; #endif /* see description of 'optarg' in 'man 3 getopt' */ char *zbx_optarg = NULL; /* see description of 'optind' in 'man 3 getopt' */ int zbx_optind = 0; #if !defined(_WINDOWS) && (defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)) if (SUCCEED != zbx_coredump_disable()) { zbx_error("cannot disable core dump, exiting..."); exit(EXIT_FAILURE); } #endif zbx_config_tls = zbx_config_tls_new(); progname = get_program_name(argv[0]); /* parse the command-line */ while ((char)EOF != (ch = (char)zbx_getopt_long(argc, argv, shortopts, longopts, NULL, &zbx_optarg, &zbx_optind))) { opt_count[(unsigned char)ch]++; switch (ch) { case 'k': if (NULL == key) key = zbx_strdup(NULL, zbx_optarg); break; case 'p': port = (unsigned short)atoi(zbx_optarg); break; case 's': if (NULL == host) host = zbx_strdup(NULL, zbx_optarg); break; case 'I': if (NULL == source_ip) source_ip = zbx_strdup(NULL, zbx_optarg); break; case 't': if (FAIL == zbx_is_uint_n_range(zbx_optarg, ZBX_MAX_UINT64_LEN, &CONFIG_GET_TIMEOUT, sizeof(CONFIG_GET_TIMEOUT), CONFIG_GET_TIMEOUT_MIN, CONFIG_GET_TIMEOUT_MAX)) { zbx_error("Invalid timeout, valid range %d:%d seconds", CONFIG_GET_TIMEOUT_MIN, CONFIG_GET_TIMEOUT_MAX); exit(EXIT_FAILURE); } break; case 'h': zbx_help(); exit(EXIT_SUCCESS); break; case 'V': zbx_version(); #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) printf("\n"); zbx_tls_version(); #endif exit(EXIT_SUCCESS); break; #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) case '1': zbx_config_tls->connect = zbx_strdup(zbx_config_tls->connect, zbx_optarg); break; case '2': zbx_config_tls->ca_file = zbx_strdup(zbx_config_tls->ca_file, zbx_optarg); break; case '3': zbx_config_tls->crl_file = zbx_strdup(zbx_config_tls->crl_file, zbx_optarg); break; case '4': zbx_config_tls->server_cert_issuer = zbx_strdup(zbx_config_tls->server_cert_issuer, zbx_optarg); break; case '5': zbx_config_tls->server_cert_subject = zbx_strdup(zbx_config_tls->server_cert_subject, zbx_optarg); break; case '6': zbx_config_tls->cert_file = zbx_strdup(zbx_config_tls->cert_file, zbx_optarg); break; case '7': zbx_config_tls->key_file = zbx_strdup(zbx_config_tls->key_file, zbx_optarg); break; case '8': zbx_config_tls->psk_identity = zbx_strdup(zbx_config_tls->psk_identity, zbx_optarg); break; case '9': zbx_config_tls->psk_file = zbx_strdup(zbx_config_tls->psk_file, zbx_optarg); break; case 'A': #if defined(HAVE_OPENSSL) zbx_config_tls->cipher_cmd13 = zbx_strdup(zbx_config_tls->cipher_cmd13, zbx_optarg); #elif defined(HAVE_GNUTLS) zbx_error("parameter \"--tls-cipher13\" can be used with OpenSSL 1.1.1 or newer." " zabbix_get was compiled with GnuTLS"); exit(EXIT_FAILURE); #endif break; case 'B': zbx_config_tls->cipher_cmd = zbx_strdup(zbx_config_tls->cipher_cmd, zbx_optarg); break; #else case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': zbx_error("TLS parameters cannot be used: 'zabbix_get' was compiled without TLS" " support"); exit(EXIT_FAILURE); break; #endif default: zbx_usage(); exit(EXIT_FAILURE); break; } } #if defined(_WINDOWS) if (SUCCEED != zbx_socket_start(&error)) { zbx_error(error); zbx_free(error); exit(EXIT_FAILURE); } #endif if (NULL == host || NULL == key) { zbx_usage(); ret = FAIL; } /* every option may be specified only once */ for (i = 0; NULL != longopts[i].name; i++) { ch = longopts[i].val; if (1 < opt_count[(unsigned char)ch]) { if (NULL == strchr(shortopts, ch)) zbx_error("option \"--%s\" specified multiple times", longopts[i].name); else zbx_error("option \"-%c\" or \"--%s\" specified multiple times", ch, longopts[i].name); ret = FAIL; } } if (FAIL == ret) goto out; /* Parameters which are not option values are invalid. The check relies on zbx_getopt_internal() which */ /* always permutes command line arguments regardless of POSIXLY_CORRECT environment variable. */ if (argc > zbx_optind) { for (i = zbx_optind; i < argc; i++) zbx_error("invalid parameter \"%s\"", argv[i]); ret = FAIL; } if (FAIL == ret) { printf("Try '%s --help' for more information.\n", progname); goto out; } if (NULL != zbx_config_tls->connect || NULL != zbx_config_tls->ca_file || NULL != zbx_config_tls->crl_file || NULL != zbx_config_tls->server_cert_issuer || NULL != zbx_config_tls->server_cert_subject || NULL != zbx_config_tls->cert_file || NULL != zbx_config_tls->key_file || NULL != zbx_config_tls->psk_identity || NULL != zbx_config_tls->psk_file || NULL != zbx_config_tls->cipher_cmd13 || NULL != zbx_config_tls->cipher_cmd) { #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) zbx_tls_validate_config(zbx_config_tls, 0, 0, get_program_type); if (ZBX_TCP_SEC_UNENCRYPTED != zbx_config_tls->connect_mode) { #if defined(_WINDOWS) zbx_tls_init_parent(get_program_type); #endif zbx_tls_init_child(zbx_config_tls, get_program_type); } #else ZBX_UNUSED(get_program_type); #endif } #if !defined(_WINDOWS) signal(SIGINT, get_signal_handler); signal(SIGQUIT, get_signal_handler); signal(SIGTERM, get_signal_handler); signal(SIGHUP, get_signal_handler); signal(SIGALRM, get_signal_handler); signal(SIGPIPE, get_signal_handler); #endif ret = get_value(source_ip, host, port, key); out: zbx_free(host); zbx_free(key); zbx_free(source_ip); #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) if (ZBX_TCP_SEC_UNENCRYPTED != zbx_config_tls->connect_mode) { zbx_tls_free(); #if defined(_WINDOWS) zbx_tls_library_deinit(); #endif } #endif zbx_config_tls_free(zbx_config_tls); #if defined(_WINDOWS) while (0 == WSACleanup()) ; #endif return SUCCEED == ret ? EXIT_SUCCESS : EXIT_FAILURE; }