nagios-nrpe/src/check_nrpe.c

491 lines
12 KiB
C

/********************************************************************************************
*
* 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;
}