Imported Upstream version 2.15

This commit is contained in:
Mario Fetka
2016-06-24 12:21:25 +02:00
commit 1efb03f433
44 changed files with 20140 additions and 0 deletions

64
src/Makefile.in Normal file
View File

@@ -0,0 +1,64 @@
###############################
# Makefile for NRPE
#
# Last Modified: 08-13-2007
###############################
srcdir=@srcdir@
# Source code directories
SRC_INCLUDE=@srcdir@/../include
CFG_INCLUDE=../include
CC=@CC@
CFLAGS=@CFLAGS@ @DEFS@ -I $(CFG_INCLUDE) -I $(SRC_INCLUDE)
LDFLAGS=@LDFLAGS@ @LIBS@
SOCKETLIBS=@SOCKETLIBS@
LIBWRAPLIBS=@LIBWRAPLIBS@
OTHERLIBS=@OTHERLIBS@
CP=@CP@
prefix=@prefix@
exec_prefix=@exec_prefix@
CFGDIR=@sysconfdir@
BINDIR=@bindir@
SBINDIR=@sbindir@
LIBEXECDIR=@libexecdir@
INSTALL=@INSTALL@
NAGIOS_INSTALL_OPTS=@NAGIOS_INSTALL_OPTS@
NRPE_INSTALL_OPTS=@NRPE_INSTALL_OPTS@
# Generated automatically from configure script
SNPRINTF_O=@SNPRINTF_O@
all: nrpe check_nrpe
nrpe: $(srcdir)/nrpe.c $(srcdir)/utils.c $(srcdir)/acl.c $(SRC_INCLUDE)/nrpe.h $(SRC_INCLUDE)/utils.h $(SRC_INCLUDE)/common.h $(CFG_INCLUDE)/config.h $(SRC_INCLUDE)/acl.h $(SNPRINTF_O)
$(CC) $(CFLAGS) -o $@ $(srcdir)/nrpe.c $(srcdir)/utils.c $(srcdir)/acl.c $(LDFLAGS) $(SOCKETLIBS) $(LIBWRAPLIBS) $(SNPRINTF_O) $(OTHERLIBS)
check_nrpe: $(srcdir)/check_nrpe.c $(srcdir)/utils.c $(SRC_INCLUDE)/utils.h $(SRC_INCLUDE)/common.h $(CFG_INCLUDE)/config.h
$(CC) $(CFLAGS) -o $@ $(srcdir)/check_nrpe.c $(srcdir)/utils.c $(LDFLAGS) $(SOCKETLIBS) $(OTHERLIBS)
install:
$(MAKE) install-plugin
$(MAKE) install-daemon
install-plugin:
$(INSTALL) -m 775 $(NAGIOS_INSTALL_OPTS) -d $(DESTDIR)$(LIBEXECDIR)
$(INSTALL) -m 775 $(NAGIOS_INSTALL_OPTS) check_nrpe $(DESTDIR)$(LIBEXECDIR)
install-daemon:
$(INSTALL) -m 775 $(NAGIOS_INSTALL_OPTS) -d $(DESTDIR)$(BINDIR)
$(INSTALL) -m 775 $(NRPE_INSTALL_OPTS) nrpe $(DESTDIR)$(BINDIR)
clean:
rm -f core nrpe check_nrpe $(SNPRINTF_O)
rm -f *~ */*~
rm -rf nrpe.dSYM check_nrpe.dSYM
distclean: clean
rm -f Makefile
devclean: distclean

613
src/acl.c Normal file
View File

@@ -0,0 +1,613 @@
/*-
* acl.c - a small library for nrpe.c. It adds IPv4 subnets support to ACL in nrpe.
* Copyright (c) 2011 Kaspersky Lab ZAO
* Last Modified: 08-10-2011 by Konstantin Malov with Oleg Koreshkov's help
*
* Description:
* acl.c creates two linked lists. One is for IPv4 hosts and networks, another is for domain names.
* All connecting hosts (if allowed_hosts is defined) are checked in these two lists.
*
* Some notes:
* 1) IPv6 isn't supported in ACL.
* 2) Only ANCII names are supported in ACL.
*
* License: GPL
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <netdb.h>
#include <syslog.h>
#include <stdarg.h>
#include "../include/acl.h"
/* This function checks if a char argumnet from valid char range.
* Valid range is: ASCII only, a number or a letter, a space, a dot, a slash, a dash, a comma.
*
* Returns:
* 0 - char isn't from valid group
* 1 - char is a number
* 2 - char is a letter
* 3 - char is a space(' ')
* 4 - char is a dot('.')
* 5 - char is a slash('/')
* 6 - char is a dash('-')
* 7 - char is a comma(',')
*/
int isvalidchar(int c) {
if (!isascii(c))
return 0;
if (isdigit(c))
return 1;
if (isalpha(c))
return 2;
if (isspace(c))
return 3;
switch (c) {
case '.':
return 4;
break;
case '/':
return 5;
break;
case '-':
return 6;
break;
case ',':
return 7;
break;
default:
return 0;
}
}
/*
* Get substring from allowed_hosts from s position to e position.
*/
char * acl_substring(char *string, int s, int e) {
char *substring;
int len = e - s;
if (len < 0)
return NULL;
if ( (substring = malloc(len + 1)) == NULL)
return NULL;
memmove(substring, string + s, len + 1);
return substring;
}
/*
* Add IPv4 host or network to IP ACL. IPv4 format is X.X.X.X[/X].
* Host will be added to ACL only if it has passed IPv4 format check.
*
* Returns:
* 1 - on success
* 0 - on failure
*
* States for IPv4 format check:
* 0 - numbers(-> 1), dot(-> -1), slash(-> -1), other(-> -1)
* 1 - numbers(-> 1), dot(-> 2), slash(-> -1), other(-> -1)
* 2 - numbers(-> 3), dot(-> -1), slash(-> -1), other(-> -1)
* 3 - numbers(-> 3), dot(-> 4), slash(-> -1), other(-> -1)
* 4 - numbers(-> 5), dot(-> -1), slash(-> -1), other(-> -1)
* 5 - numbers(-> 5), dot(-> 6), slash(-> -1), other(-> -1)
* 6 - numbers(-> 7), dot(-> -1), slash(-> -1), other(-> -1)
* 7 - numbers(-> 7), dor(-> -1), slash(-> 8), other(-> -1)
* 8 - numbers(-> 9), dor(-> -1), slash(-> -1), other(-> -1)
* 9 - numbers(-> 9), dot(-> -1), slash(-> -1), other(-> -1)
*
* Good states are 7(IPv4 host) and 9(IPv4 network)
*/
int add_ipv4_to_acl(char *ipv4) {
int state = 0;
int octet = 0;
int index = 0; /* position in data array */
int data[5]; /* array to store ip octets and mask */
int len = strlen(ipv4);
int i, c;
unsigned long ip, mask;
struct ip_acl *ip_acl_curr;
/* Check for min and max IPv4 valid length */
if (len < 7 || len > 18)
return 0;
/* default mask for ipv4 */
data[4] = 32;
/* Basic IPv4 format check */
for (i = 0; i < len; i++) {
/* Return 0 on error state */
if (state == -1)
return 0;
c = ipv4[i];
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
octet = octet * 10 + CHAR_TO_NUMBER(c);
switch (state) {
case 0: case 2: case 4: case 6: case 8:
state++;
break;
}
break;
case '.':
switch (state) {
case 1: case 3: case 5:
data[index++] = octet;
octet = 0;
state++;
break;
default:
state = -1;
}
break;
case '/':
switch (state) {
case 7:
data[index++] = octet;
octet = 0;
state++;
break;
default:
state = -1;
}
break;
default:
state = -1;
}
}
/* Exit state handling */
switch (state) {
case 7: case 9:
data[index] = octet;
break;
default:
/* Bad states */
return 0;
}
/*
* Final IPv4 format check.
*/
for (i=0; i < 4; i++) {
if (data[i] < 0 || data[i] > 255) {
syslog(LOG_ERR,"Invalid IPv4 address/network format(%s) in allowed_hosts option\n",ipv4);
return 0;
}
}
if (data[4] < 0 || data[4] > 32) {
syslog(LOG_ERR,"Invalid IPv4 network mask format(%s) in allowed_hosts option\n",ipv4);
return 0;
}
/* Conver ip and mask to unsigned long */
ip = htonl((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]);
mask = htonl(-1 << (32 - data[4]));
/* Wrong network address */
if ( (ip & mask) != ip) {
syslog(LOG_ERR,"IP address and mask do not match in %s\n",ipv4);
return 0;
}
/* Add addr to ip_acl list */
if ( (ip_acl_curr = malloc(sizeof(*ip_acl_curr))) == NULL) {
syslog(LOG_ERR,"Can't allocate memory for ACL, malloc error\n");
return 0;
}
/* Save result in ACL ip list */
ip_acl_curr->family = AF_INET;
ip_acl_curr->addr.s_addr = ip;
ip_acl_curr->mask.s_addr = mask;
ip_acl_curr->next = NULL;
if (ip_acl_head == NULL) {
ip_acl_head = ip_acl_curr;
} else {
ip_acl_prev->next = ip_acl_curr;
}
ip_acl_prev = ip_acl_curr;
return 1;
}
/*
* Add IPv6 host or network to IP ACL. Host will be added to ACL only if
* it has passed IPv6 format check.
*
*/
int add_ipv6_to_acl(char *ipv6) {
char *ipv6tmp;
char *addrtok;
char *addrsave;
struct in6_addr addr;
struct in6_addr mask;
int maskval;
int byte, bit;
int nbytes = sizeof(mask.s6_addr) / sizeof(mask.s6_addr[0]);
int x;
struct ip_acl *ip_acl_curr;
/* Save temporary copy of ipv6 so we can use the original in error
messages if needed */
ipv6tmp = strdup(ipv6);
if(NULL == ipv6tmp) {
syslog(LOG_ERR, "Memory allocation failed for copy of address: %s\n",
ipv6);
return 0;
}
/* Parse the address itself */
addrtok = strtok_r(ipv6tmp, "/", &addrsave);
if(inet_pton(AF_INET6, addrtok, &addr) <= 0) {
syslog(LOG_ERR, "Invalid IPv6 address in ACL: %s\n", ipv6);
free(ipv6tmp);
return 0;
}
/* Check whether there is a netmask */
addrtok = strtok_r(NULL, "/", &addrsave);
if(NULL != addrtok) {
/* If so, build a netmask */
/* Get the number of bits in the mask */
maskval = atoi(addrtok);
if(maskval < 0 || maskval > 128) {
syslog(LOG_ERR, "Invalid IPv6 netmask in ACL: %s\n", ipv6);
free(ipv6tmp);
return 0;
}
/* Initialize to zero */
for(x = 0; x < nbytes; x++) {
mask.s6_addr[x] = 0;
}
/* Set mask based on mask bits */
byte = 0;
bit = 7;
while(maskval > 0) {
mask.s6_addr[byte] |= 1 << bit;
bit -= 1;
if(bit < 0) {
bit = 7;
byte++;
}
maskval--;
}
}
else {
/* Otherwise, this is a single address */
for(x = 0; x < nbytes; x++) {
mask.s6_addr[x] = 0xFF;
}
}
/* Add address to ip_acl list */
ip_acl_curr = malloc(sizeof(*ip_acl_curr));
if(NULL == ip_acl_curr) {
syslog(LOG_ERR, "Memory allocation failed for ACL: %s\n", ipv6);
return 0;
}
/* Save result in ACL ip list */
ip_acl_curr->family = AF_INET6;
for(x = 0; x < nbytes; x++) {
ip_acl_curr->addr6.s6_addr[x] =
addr.s6_addr[x] & mask.s6_addr[x];
ip_acl_curr->mask6.s6_addr[x] = mask.s6_addr[x];
}
ip_acl_curr->next = NULL;
if(NULL == ip_acl_head) {
ip_acl_head = ip_acl_curr;
}
else {
ip_acl_prev->next = ip_acl_curr;
}
ip_acl_prev = ip_acl_curr;
free(ipv6tmp);
return 1;
}
/*
* Add domain to DNS ACL list
* Domain will be added only if it has passed domain name check.
*
* In this case domain valid format is:
* 1) Domain names must use only alphanumeric characters and dashes (-).
* 2) Domain names mustn't begin or end with dashes (-).
* 3) Domain names mustn't have more than 63 characters.
*
* Return:
* 1 - for success
* 0 - for failure
*
* 0 - alpha(-> 1), number(-> 1), dot(-> -1), dash(-> -1), all other(-> -1)
* 1 - alpha(-> 1), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
* 2 - alpha(-> 3), number(-> 1), dot(-> -1), dash(-> -1), all other(-> -1)
* 3 - alpha(-> 4), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
* 4 - alpha(-> 5), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
* 5 - alpha(-> 1), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
* 6 - alpha(-> 1), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
* For real FQDN only 4 and 5 states are good for exit.
* I don't check if top domain exists (com, ru and etc.)
* But in real life NRPE could work in LAN,
* with local domain zones like .local or with names like 'mars' added to /etc/hosts.
* So 1 is good state too. And maybe this check is not necessary at all...
*/
int add_domain_to_acl(char *domain) {
int state = 0;
int len = strlen(domain);
int i, c;
struct dns_acl *dns_acl_curr;
if (len > 63)
return 0;
for (i = 0; i < len; i++) {
c = domain[i];
switch (isvalidchar(c)) {
case 1:
state = 1;
break;
case 2:
switch (state) {
case 0: case 1: case 5: case 6:
state = 1;
break;
case 2: case 3: case 4:
state++;
break;
}
break;
case 4:
switch (state) {
case 0: case 2:
state = -1;
break;
default:
state = 2;
}
break;
case 6:
switch (state) {
case 0: case 2:
state = -1;
break;
default:
state = 6;
}
break;
default:
/* Not valid chars */
return 0;
}
}
/* Check exit code */
switch (state) {
case 1: case 4: case 5:
/* Add name to domain ACL list */
if ( (dns_acl_curr = malloc(sizeof(*dns_acl_curr))) == NULL) {
syslog(LOG_ERR,"Can't allocate memory for ACL, malloc error\n");
return 0;
}
strcpy(dns_acl_curr->domain, domain);
dns_acl_curr->next = NULL;
if (dns_acl_head == NULL)
dns_acl_head = dns_acl_curr;
else
dns_acl_prev->next = dns_acl_curr;
dns_acl_prev = dns_acl_curr;
return 1;
default:
return 0;
}
}
/* Checks connectiong host in ACL
*
* Returns:
* 1 - on success
* 0 - on failure
*/
int is_an_allowed_host(int family, void *host) {
struct ip_acl *ip_acl_curr = ip_acl_head;
int nbytes;
int x;
struct dns_acl *dns_acl_curr = dns_acl_head;
struct in_addr addr;
struct in6_addr addr6;
struct hostent *he;
while (ip_acl_curr != NULL) {
if(ip_acl_curr->family == family) {
switch(ip_acl_curr->family) {
case AF_INET:
if((((struct in_addr *)host)->s_addr &
ip_acl_curr->mask.s_addr) ==
ip_acl_curr->addr.s_addr) {
return 1;
}
break;
case AF_INET6:
nbytes = sizeof(ip_acl_curr->mask6.s6_addr) /
sizeof(ip_acl_curr->mask6.s6_addr[0]);
for(x = 0; x < nbytes; x++) {
if((((struct in6_addr *)host)->s6_addr[x] &
ip_acl_curr->mask6.s6_addr[x]) !=
ip_acl_curr->addr6.s6_addr[x]) {
break;
}
}
if(x == nbytes) {
/* All bytes in host's address pass the netmask mask */
return 1;
}
break;
}
}
ip_acl_curr = ip_acl_curr->next;
}
while(dns_acl_curr != NULL) {
he = gethostbyname(dns_acl_curr->domain);
if (he == NULL) return 0;
while (*he->h_addr_list) {
switch(he->h_addrtype) {
case AF_INET:
memmove((char *)&addr,*he->h_addr_list++, sizeof(addr));
if (addr.s_addr == ((struct in_addr *)host)->s_addr) return 1;
break;
case AF_INET6:
memcpy((char *)&addr6, *he->h_addr_list++, sizeof(addr6));
for(x = 0; x < nbytes; x++) {
if(addr6.s6_addr[x] !=
((struct in6_addr *)host)->s6_addr[x]) {
break;
}
}
if(x == nbytes) {
/* All bytes in host's address match the ACL */
return 1;
}
break;
}
}
dns_acl_curr = dns_acl_curr->next;
}
return 0;
}
/* The trim() function takes a source string and copies it to the destination string,
* stripped of leading and training whitespace. The destination string must be
* allocated at least as large as the source string.
*/
void trim( char *src, char *dest) {
char *sptr, *dptr;
for( sptr = src; isblank( *sptr) && *sptr; sptr++); /* Jump past leading spaces */
for( dptr = dest; !isblank( *sptr) && *sptr; ) {
*dptr = *sptr;
sptr++;
dptr++;
}
*dptr = '\0';
return;
}
/* This function splits allowed_hosts to substrings with comma(,) as a delimeter.
* It doesn't check validness of ACL record (add_ipv4_to_acl() and add_domain_to_acl() do),
* just trims spaces from ACL records.
* After this it sends ACL records to add_ipv4_to_acl() or add_domain_to_acl().
*/
void parse_allowed_hosts(char *allowed_hosts) {
char *hosts = strdup( allowed_hosts); /* Copy since strtok* modifes original */
char *saveptr;
char *tok;
const char *delim = ",";
char *trimmed_tok;
tok = strtok_r( hosts, delim, &saveptr);
while( tok) {
trimmed_tok = malloc( sizeof( char) * ( strlen( tok) + 1));
trim( tok, trimmed_tok);
if( strlen( trimmed_tok) > 0) {
if (!add_ipv4_to_acl(trimmed_tok) && !add_ipv6_to_acl(trimmed_tok)
&& !add_domain_to_acl(trimmed_tok)) {
syslog(LOG_ERR,"Can't add to ACL this record (%s). Check allowed_hosts option!\n",trimmed_tok);
}
}
free( trimmed_tok);
tok = strtok_r(( char *)0, delim, &saveptr);
}
free( hosts);
}
/*
* Converts mask in unsigned long format to two digit prefix
*/
unsigned int prefix_from_mask(struct in_addr mask) {
int prefix = 0;
unsigned long bit = 1;
int i;
for (i = 0; i < 32; i++) {
if (mask.s_addr & bit)
prefix++;
bit = bit << 1;
}
return (prefix);
}
/*
* It shows all hosts in ACL lists
*/
void show_acl_lists(void) {
struct ip_acl *ip_acl_curr = ip_acl_head;
struct dns_acl *dns_acl_curr = dns_acl_head;
while (ip_acl_curr != NULL) {
printf(" IP ACL: %s/%u %u\n", inet_ntoa(ip_acl_curr->addr), prefix_from_mask(ip_acl_curr->mask), ip_acl_curr->addr.s_addr);
ip_acl_curr = ip_acl_curr->next;
}
while (dns_acl_curr != NULL) {
printf("DNS ACL: %s\n", dns_acl_curr->domain);
dns_acl_curr = dns_acl_curr->next;
}
}

490
src/check_nrpe.c Normal file
View File

@@ -0,0 +1,490 @@
/********************************************************************************************
*
* CHECK_NRPE.C - NRPE Plugin For Nagios
* Copyright (c) 1999-2008 Ethan Galstad (nagios@nagios.org)
* License: GPL
*
* Last Modified: 09-06-2013
*
* Command line: CHECK_NRPE -H <host_address> [-p port] [-c command] [-to to_sec]
*
* Description:
*
* This plugin will attempt to connect to the NRPE daemon on the specified server and port.
* The daemon will attempt to run the command defined as [command]. Program output and
* return code are sent back from the daemon and displayed as this plugin's own output and
* return code.
*
********************************************************************************************/
#include "config.h"
#include "common.h"
#include "utils.h"
#define DEFAULT_NRPE_COMMAND "_NRPE_CHECK" /* check version of NRPE daemon */
u_short server_port=DEFAULT_SERVER_PORT;
char *server_name=NULL;
char *bind_address=NULL;
struct sockaddr_storage hostaddr;
int address_family=AF_UNSPEC;
char *command_name=NULL;
int socket_timeout=DEFAULT_SOCKET_TIMEOUT;
int timeout_return_code=STATE_CRITICAL;
int sd;
char query[MAX_INPUT_BUFFER]="";
int show_help=FALSE;
int show_license=FALSE;
int show_version=FALSE;
#ifdef HAVE_SSL
#ifdef __sun
SSL_METHOD *meth;
#else
const SSL_METHOD *meth;
#endif
SSL_CTX *ctx;
SSL *ssl;
int use_ssl=TRUE;
#else
int use_ssl=FALSE;
#endif
int process_arguments(int,char **);
void alarm_handler(int);
int graceful_close(int,int);
int main(int argc, char **argv){
u_int32_t packet_crc32;
u_int32_t calculated_crc32;
int16_t result;
int rc;
packet send_packet;
packet receive_packet;
int bytes_to_send;
int bytes_to_recv;
result=process_arguments(argc,argv);
if(result!=OK || show_help==TRUE || show_license==TRUE || show_version==TRUE){
if(result!=OK)
printf("Incorrect command line arguments supplied\n");
printf("\n");
printf("NRPE Plugin for Nagios\n");
printf("Copyright (c) 1999-2008 Ethan Galstad (nagios@nagios.org)\n");
printf("Version: %s\n",PROGRAM_VERSION);
printf("Last Modified: %s\n",MODIFICATION_DATE);
printf("License: GPL v2 with exemptions (-l for more info)\n");
#ifdef HAVE_SSL
printf("SSL/TLS Available: Anonymous DH Mode, OpenSSL 0.9.6 or higher required\n");
#endif
printf("\n");
}
if(result!=OK || show_help==TRUE){
printf("Usage: check_nrpe -H <host> [ -b <bindaddr> ] [-4] [-6] [-n] [-u] [-p <port>] [-t <timeout>] [-c <command>] [-a <arglist...>]\n");
printf("\n");
printf("Options:\n");
printf(" -n = Do no use SSL\n");
printf(" -u = Make socket timeouts return an UNKNOWN state instead of CRITICAL\n");
printf(" <host> = The address of the host running the NRPE daemon\n");
printf(" <bindaddr> = bind to local address\n");
printf(" -4 = user ipv4 only\n");
printf(" -6 = user ipv6 only\n");
printf(" [port] = The port on which the daemon is running (default=%d)\n",DEFAULT_SERVER_PORT);
printf(" [timeout] = Number of seconds before connection times out (default=%d)\n",DEFAULT_SOCKET_TIMEOUT);
printf(" [command] = The name of the command that the remote daemon should run\n");
printf(" [arglist] = Optional arguments that should be passed to the command. Multiple\n");
printf(" arguments should be separated by a space. If provided, this must be\n");
printf(" the last option supplied on the command line.\n");
printf("\n");
printf("Note:\n");
printf("This plugin requires that you have the NRPE daemon running on the remote host.\n");
printf("You must also have configured the daemon to associate a specific plugin command\n");
printf("with the [command] option you are specifying here. Upon receipt of the\n");
printf("[command] argument, the NRPE daemon will run the appropriate plugin command and\n");
printf("send the plugin output and return code back to *this* plugin. This allows you\n");
printf("to execute plugins on remote hosts and 'fake' the results to make Nagios think\n");
printf("the plugin is being run locally.\n");
printf("\n");
}
if(show_license==TRUE)
display_license();
if(result!=OK || show_help==TRUE || show_license==TRUE || show_version==TRUE)
exit(STATE_UNKNOWN);
/* generate the CRC 32 table */
generate_crc32_table();
#ifdef HAVE_SSL
/* initialize SSL */
if(use_ssl==TRUE){
SSL_library_init();
SSLeay_add_ssl_algorithms();
meth=SSLv23_client_method();
SSL_load_error_strings();
if((ctx=SSL_CTX_new(meth))==NULL){
printf("CHECK_NRPE: Error - could not create SSL context.\n");
exit(STATE_CRITICAL);
}
/* ADDED 01/19/2004 */
/* use only TLSv1 protocol */
SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
}
#endif
/* initialize alarm signal handling */
signal(SIGALRM,alarm_handler);
/* set socket timeout */
alarm(socket_timeout);
/* try to connect to the host at the given port number */
if((sd=my_connect(server_name, &hostaddr, server_port, address_family,
bind_address)) < 0 ) {
exit (255);
}
else {
result=STATE_OK;
}
#ifdef HAVE_SSL
/* do SSL handshake */
if(result==STATE_OK && use_ssl==TRUE){
if((ssl=SSL_new(ctx))!=NULL){
SSL_CTX_set_cipher_list(ctx,"ADH");
SSL_set_fd(ssl,sd);
if((rc=SSL_connect(ssl))!=1){
printf("CHECK_NRPE: Error - Could not complete SSL handshake.\n");
#ifdef DEBUG
printf("SSL_connect=%d\n",rc);
/*
rc=SSL_get_error(ssl,rc);
printf("SSL_get_error=%d\n",rc);
printf("ERR_get_error=%lu\n",ERR_get_error());
printf("%s\n",ERR_error_string(rc,NULL));
*/
ERR_print_errors_fp(stdout);
#endif
result=STATE_CRITICAL;
}
}
else{
printf("CHECK_NRPE: Error - Could not create SSL connection structure.\n");
result=STATE_CRITICAL;
}
/* bail if we had errors */
if(result!=STATE_OK){
SSL_CTX_free(ctx);
close(sd);
exit(result);
}
}
#endif
/* we're connected and ready to go */
if(result==STATE_OK){
/* clear the packet buffer */
bzero(&send_packet,sizeof(send_packet));
/* fill the packet with semi-random data */
randomize_buffer((char *)&send_packet,sizeof(send_packet));
/* initialize packet data */
send_packet.packet_version=(int16_t)htons(NRPE_PACKET_VERSION_2);
send_packet.packet_type=(int16_t)htons(QUERY_PACKET);
strncpy(&send_packet.buffer[0],query,MAX_PACKETBUFFER_LENGTH);
send_packet.buffer[MAX_PACKETBUFFER_LENGTH-1]='\x0';
/* calculate the crc 32 value of the packet */
send_packet.crc32_value=(u_int32_t)0L;
calculated_crc32=calculate_crc32((char *)&send_packet,sizeof(send_packet));
send_packet.crc32_value=(u_int32_t)htonl(calculated_crc32);
/***** ENCRYPT REQUEST *****/
/* send the packet */
bytes_to_send=sizeof(send_packet);
if(use_ssl==FALSE)
rc=sendall(sd,(char *)&send_packet,&bytes_to_send);
#ifdef HAVE_SSL
else{
rc=SSL_write(ssl,&send_packet,bytes_to_send);
if(rc<0)
rc=-1;
}
#endif
if(rc==-1){
printf("CHECK_NRPE: Error sending query to host.\n");
close(sd);
return STATE_UNKNOWN;
}
/* wait for the response packet */
bytes_to_recv=sizeof(receive_packet);
if(use_ssl==FALSE)
rc=recvall(sd,(char *)&receive_packet,&bytes_to_recv,socket_timeout);
#ifdef HAVE_SSL
else
rc=SSL_read(ssl,&receive_packet,bytes_to_recv);
#endif
/* reset timeout */
alarm(0);
/* close the connection */
#ifdef HAVE_SSL
if(use_ssl==TRUE){
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
}
#endif
graceful_close(sd,1000);
/* recv() error */
if(rc<0){
printf("CHECK_NRPE: Error receiving data from daemon.\n");
return STATE_UNKNOWN;
}
/* server disconnected */
else if(rc==0){
printf("CHECK_NRPE: Received 0 bytes from daemon. Check the remote server logs for error messages.\n");
return STATE_UNKNOWN;
}
/* receive underflow */
else if(bytes_to_recv<sizeof(receive_packet)){
printf("CHECK_NRPE: Receive underflow - only %d bytes received (%d expected).\n",bytes_to_recv,sizeof(receive_packet));
return STATE_UNKNOWN;
}
/***** DECRYPT RESPONSE *****/
/* check the crc 32 value */
packet_crc32=ntohl(receive_packet.crc32_value);
receive_packet.crc32_value=0L;
calculated_crc32=calculate_crc32((char *)&receive_packet,sizeof(receive_packet));
if(packet_crc32!=calculated_crc32){
printf("CHECK_NRPE: Response packet had invalid CRC32.\n");
close(sd);
return STATE_UNKNOWN;
}
/* check packet version */
if(ntohs(receive_packet.packet_version)!=NRPE_PACKET_VERSION_2){
printf("CHECK_NRPE: Invalid packet version received from server.\n");
close(sd);
return STATE_UNKNOWN;
}
/* check packet type */
if(ntohs(receive_packet.packet_type)!=RESPONSE_PACKET){
printf("CHECK_NRPE: Invalid packet type received from server.\n");
close(sd);
return STATE_UNKNOWN;
}
/* get the return code from the remote plugin */
result=(int16_t)ntohs(receive_packet.result_code);
/* print the output returned by the daemon */
receive_packet.buffer[MAX_PACKETBUFFER_LENGTH-1]='\x0';
if(!strcmp(receive_packet.buffer,""))
printf("CHECK_NRPE: No output returned from daemon.\n");
else
printf("%s\n",receive_packet.buffer);
}
/* reset the alarm */
else
alarm(0);
return result;
}
/* process command line arguments */
int process_arguments(int argc, char **argv){
char optchars[MAX_INPUT_BUFFER];
int argindex=0;
int c=1;
int i=1;
#ifdef HAVE_GETOPT_LONG
int option_index=0;
static struct option long_options[]={
{"host", required_argument, 0, 'H'},
{"bind", required_argument, 0, 'b'},
{"command", required_argument, 0, 'c'},
{"args", required_argument, 0, 'a'},
{"no-ssl", no_argument, 0, 'n'},
{"unknown-timeout", no_argument, 0, 'u'},
{"ipv4", no_argument, 0, '4'},
{"ipv6", no_argument, 0, '6'},
{"timeout", required_argument, 0, 't'},
{"port", required_argument, 0, 'p'},
{"help", no_argument, 0, 'h'},
{"license", no_argument, 0, 'l'},
{0, 0, 0, 0}
};
#endif
/* no options were supplied */
if(argc<2)
return ERROR;
snprintf(optchars,MAX_INPUT_BUFFER,"H:b:c:a:t:p:nu46hl");
while(1){
#ifdef HAVE_GETOPT_LONG
c=getopt_long(argc,argv,optchars,long_options,&option_index);
#else
c=getopt(argc,argv,optchars);
#endif
if(c==-1 || c==EOF)
break;
/* process all arguments */
switch(c){
case '?':
case 'h':
show_help=TRUE;
break;
case 'b':
bind_address=strdup(optarg);
break;
case 'V':
show_version=TRUE;
break;
case 'l':
show_license=TRUE;
break;
case 't':
socket_timeout=atoi(optarg);
if(socket_timeout<=0)
return ERROR;
break;
case 'p':
server_port=atoi(optarg);
if(server_port<=0)
return ERROR;
break;
case 'H':
server_name=strdup(optarg);
break;
case 'c':
command_name=strdup(optarg);
break;
case 'a':
argindex=optind;
break;
case 'n':
use_ssl=FALSE;
break;
case 'u':
timeout_return_code=STATE_UNKNOWN;
break;
case '4':
address_family=AF_INET;
break;
case '6':
address_family=AF_INET6;
break;
default:
return ERROR;
break;
}
}
/* determine (base) command query */
snprintf(query,sizeof(query),"%s",(command_name==NULL)?DEFAULT_NRPE_COMMAND:command_name);
query[sizeof(query)-1]='\x0';
/* get the command args */
if(argindex>0){
for(c=argindex-1;c<argc;c++){
i=sizeof(query)-strlen(query)-2;
if(i<=0)
break;
strcat(query,"!");
strncat(query,argv[c],i);
query[sizeof(query)-1]='\x0';
}
}
/* make sure required args were supplied */
if(server_name==NULL && show_help==FALSE && show_version==FALSE && show_license==FALSE)
return ERROR;
return OK;
}
void alarm_handler(int sig){
printf("CHECK_NRPE: Socket timeout after %d seconds.\n",socket_timeout);
exit(timeout_return_code);
}
/* submitted by Mark Plaksin 08/31/2006 */
int graceful_close(int sd, int timeout){
fd_set in;
struct timeval tv;
char buf[1000];
/* send FIN packet */
shutdown(sd,SHUT_WR);
for(;;){
FD_ZERO(&in);
FD_SET(sd,&in);
tv.tv_sec=timeout/1000;
tv.tv_usec=(timeout % 1000)*1000;
/* timeout or error */
if(1!=select(sd+1,&in,NULL,NULL,&tv))
break;
/* no more data (FIN or RST) */
if(0>=recv(sd,buf,sizeof(buf),0))
break;
}
#ifdef HAVE_CLOSESOCKET
closesocket(sd);
#else
close(sd);
#endif
return OK;
}

2162
src/nrpe.c Normal file

File diff suppressed because it is too large Load Diff

1452
src/snprintf.c Normal file

File diff suppressed because it is too large Load Diff

390
src/utils.c Normal file
View File

@@ -0,0 +1,390 @@
/****************************************************************************
*
* UTILS.C - NRPE Utility Functions
*
* License: GPL
* Copyright (c) 1999-2006 Ethan Galstad (nagios@nagios.org)
*
* Last Modified: 12-11-2006
*
* Description:
*
* This file contains common network functions used in nrpe and check_nrpe.
*
* License Information:
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
****************************************************************************/
#include "../include/common.h"
#include "../include/utils.h"
#ifndef NI_MAXSERV
#define NI_MAXSERV 32
#endif
#ifndef NI_MAXHOST
#define NI_MAXHOST 1025
#endif
static unsigned long crc32_table[256];
/* build the crc table - must be called before calculating the crc value */
void generate_crc32_table(void){
unsigned long crc, poly;
int i, j;
poly=0xEDB88320L;
for(i=0;i<256;i++){
crc=i;
for(j=8;j>0;j--){
if(crc & 1)
crc=(crc>>1)^poly;
else
crc>>=1;
}
crc32_table[i]=crc;
}
return;
}
/* calculates the CRC 32 value for a buffer */
unsigned long calculate_crc32(char *buffer, int buffer_size){
register unsigned long crc;
int this_char;
int current_index;
crc=0xFFFFFFFF;
for(current_index=0;current_index<buffer_size;current_index++){
this_char=(int)buffer[current_index];
crc=((crc>>8) & 0x00FFFFFF) ^ crc32_table[(crc ^ this_char) & 0xFF];
}
return (crc ^ 0xFFFFFFFF);
}
/* fill a buffer with semi-random data */
void randomize_buffer(char *buffer,int buffer_size){
FILE *fp;
int x;
int seed;
/**** FILL BUFFER WITH RANDOM ALPHA-NUMERIC CHARACTERS ****/
/***************************************************************
Only use alpha-numeric characters becase plugins usually
only generate numbers and letters in their output. We
want the buffer to contain the same set of characters as
plugins, so its harder to distinguish where the real output
ends and the rest of the buffer (padded randomly) starts.
***************************************************************/
/* try to get seed value from /dev/urandom, as its a better source of entropy */
fp=fopen("/dev/urandom","r");
if(fp!=NULL){
seed=fgetc(fp);
fclose(fp);
}
/* else fallback to using the current time as the seed */
else
seed=(int)time(NULL);
srand(seed);
for(x=0;x<buffer_size;x++)
buffer[x]=(int)'0'+(int)(72.0*rand()/(RAND_MAX+1.0));
return;
}
/* opens a connection to a remote host */
int my_connect(const char *host, struct sockaddr_storage * hostaddr, u_short port,
int address_family, const char *bind_address){
int gaierr;
int sock = -1;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
struct addrinfo hints, *ai, *aitop;
memset(&hints, 0, sizeof(hints));
hints.ai_family = address_family;
hints.ai_socktype = SOCK_STREAM;
snprintf(strport, sizeof strport, "%u", port);
if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
fprintf(stderr,"Could not resolve hostname %.100s: %s\n", host,
gai_strerror(gaierr));
exit(1);
}
/*
* Loop through addresses for this host, and try each one in
* sequence until the connection succeeds.
*/
for (ai = aitop; ai; ai = ai->ai_next) {
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue;
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
fprintf(stderr, "my_connect: getnameinfo failed\n");
continue;
}
/* Create a socket for connecting. */
sock = my_create_socket(ai, bind_address);
if (sock < 0) {
/* Any error is already output */
continue;
}
if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
/* Successful connection. */
memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
break;
}
else {
fprintf(stderr,"connect to address %s port %s: %s\n", ntop, strport,
strerror(errno));
close(sock);
sock = -1;
}
}
freeaddrinfo(aitop);
/* Return failure if we didn't get a successful connection. */
if (sock == -1) {
fprintf(stderr, "connect to host %s port %s: %s", host, strport,
strerror(errno));
return -1;
}
return sock;
}
/* Creates a socket for the connection. */
int my_create_socket(struct addrinfo *ai, const char *bind_address) {
int sock, gaierr;
struct addrinfo hints, *res;
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) fprintf(stderr,"socket: %.100s\n", strerror(errno));
/* Bind the socket to an alternative local IP address */
if (bind_address == NULL) return sock;
memset(&hints, 0, sizeof(hints));
hints.ai_family = ai->ai_family;
hints.ai_socktype = ai->ai_socktype;
hints.ai_protocol = ai->ai_protocol;
hints.ai_flags = AI_PASSIVE;
gaierr = getaddrinfo(bind_address, NULL, &hints, &res);
if(gaierr) {
fprintf(stderr, "getaddrinfo: %s: %s\n", bind_address,
gai_strerror(gaierr));
close(sock);
return -1;
}
if(bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
fprintf(stderr, "bind: %s: %s\n", bind_address, strerror(errno));
close(sock);
freeaddrinfo(res);
return -1;
}
freeaddrinfo(res);
return sock;
}
void add_listen_addr(struct addrinfo **listen_addrs, int address_family,
char *addr, int port) {
struct addrinfo hints, *ai, *aitop;
char strport[NI_MAXSERV];
int gaierr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = address_family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0;
snprintf(strport, sizeof strport, "%d", port);
if((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
syslog(LOG_ERR,"bad addr or host: %s (%s)\n", addr ? addr : "<NULL>",
gai_strerror(gaierr));
exit(1);
}
for(ai = aitop; ai->ai_next; ai = ai->ai_next);
ai->ai_next = *listen_addrs;
*listen_addrs = aitop;
}
void strip(char *buffer){
int x;
int index;
for(x=strlen(buffer);x>=1;x--){
index=x-1;
if(buffer[index]==' ' || buffer[index]=='\r' || buffer[index]=='\n' || buffer[index]=='\t')
buffer[index]='\x0';
else
break;
}
return;
}
/* sends all data - thanks to Beej's Guide to Network Programming */
int sendall(int s, char *buf, int *len){
int total=0;
int bytesleft=*len;
int n=0;
/* send all the data */
while(total<*len){
/* send some data */
n=send(s,buf+total,bytesleft,0);
/* break on error */
if(n==-1)
break;
/* apply bytes we sent */
total+=n;
bytesleft-=n;
}
/* return number of bytes actually send here */
*len=total;
/* return -1 on failure, 0 on success */
return n==-1?-1:0;
}
/* receives all data - modelled after sendall() */
int recvall(int s, char *buf, int *len, int timeout){
int total=0;
int bytesleft=*len;
int n=0;
time_t start_time;
time_t current_time;
/* clear the receive buffer */
bzero(buf,*len);
time(&start_time);
/* receive all data */
while(total<*len){
/* receive some data */
n=recv(s,buf+total,bytesleft,0);
/* no data has arrived yet (non-blocking socket) */
if(n==-1 && errno==EAGAIN){
time(&current_time);
if(current_time-start_time>timeout)
break;
sleep(1);
continue;
}
/* receive error or client disconnect */
else if(n<=0)
break;
/* apply bytes we received */
total+=n;
bytesleft-=n;
}
/* return number of bytes actually received here */
*len=total;
/* return <=0 on failure, bytes received on success */
return (n<=0)?n:total;
}
/* fixes compiler problems under Solaris, since strsep() isn't included */
/* this code is taken from the glibc source */
char *my_strsep (char **stringp, const char *delim){
char *begin, *end;
begin = *stringp;
if (begin == NULL)
return NULL;
/* A frequent case is when the delimiter string contains only one
character. Here we don't need to call the expensive `strpbrk'
function and instead work using `strchr'. */
if(delim[0]=='\0' || delim[1]=='\0'){
char ch = delim[0];
if(ch=='\0')
end=NULL;
else{
if(*begin==ch)
end=begin;
else
end=strchr(begin+1,ch);
}
}
else
/* Find the end of the token. */
end = strpbrk (begin, delim);
if(end){
/* Terminate the token and set *STRINGP past NUL character. */
*end++='\0';
*stringp=end;
}
else
/* No more delimiters; this is the last token. */
*stringp=NULL;
return begin;
}
/* show license */
void display_license(void){
printf("This program is released under the GPL (see below) with the additional\n");
printf("exemption that compiling, linking, and/or using OpenSSL is allowed.\n\n");
printf("This program is free software; you can redistribute it and/or modify\n");
printf("it under the terms of the GNU General Public License as published by\n");
printf("the Free Software Foundation; either version 2 of the License, or\n");
printf("(at your option) any later version.\n\n");
printf("This program is distributed in the hope that it will be useful,\n");
printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
printf("GNU General Public License for more details.\n\n");
printf("You should have received a copy of the GNU General Public License\n");
printf("along with this program; if not, write to the Free Software\n");
printf("Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
return;
}