check_nrpe.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. /********************************************************************************************
  2. *
  3. * CHECK_NRPE.C - NRPE Plugin For Nagios
  4. * Copyright (c) 1999-2008 Ethan Galstad (nagios@nagios.org)
  5. * License: GPL
  6. *
  7. * Last Modified: 09-06-2013
  8. *
  9. * Command line: CHECK_NRPE -H <host_address> [-p port] [-c command] [-to to_sec]
  10. *
  11. * Description:
  12. *
  13. * This plugin will attempt to connect to the NRPE daemon on the specified server and port.
  14. * The daemon will attempt to run the command defined as [command]. Program output and
  15. * return code are sent back from the daemon and displayed as this plugin's own output and
  16. * return code.
  17. *
  18. ********************************************************************************************/
  19. #include "config.h"
  20. #include "common.h"
  21. #include "utils.h"
  22. #define DEFAULT_NRPE_COMMAND "_NRPE_CHECK" /* check version of NRPE daemon */
  23. u_short server_port=DEFAULT_SERVER_PORT;
  24. char *server_name=NULL;
  25. char *bind_address=NULL;
  26. struct sockaddr_storage hostaddr;
  27. int address_family=AF_UNSPEC;
  28. char *command_name=NULL;
  29. int socket_timeout=DEFAULT_SOCKET_TIMEOUT;
  30. int timeout_return_code=STATE_CRITICAL;
  31. int sd;
  32. char query[MAX_INPUT_BUFFER]="";
  33. int show_help=FALSE;
  34. int show_license=FALSE;
  35. int show_version=FALSE;
  36. #ifdef HAVE_SSL
  37. #ifdef __sun
  38. SSL_METHOD *meth;
  39. #else
  40. const SSL_METHOD *meth;
  41. #endif
  42. SSL_CTX *ctx;
  43. SSL *ssl;
  44. int use_ssl=TRUE;
  45. #else
  46. int use_ssl=FALSE;
  47. #endif
  48. int process_arguments(int,char **);
  49. void alarm_handler(int);
  50. int graceful_close(int,int);
  51. int main(int argc, char **argv){
  52. u_int32_t packet_crc32;
  53. u_int32_t calculated_crc32;
  54. int16_t result;
  55. int rc;
  56. packet send_packet;
  57. packet receive_packet;
  58. int bytes_to_send;
  59. int bytes_to_recv;
  60. result=process_arguments(argc,argv);
  61. if(result!=OK || show_help==TRUE || show_license==TRUE || show_version==TRUE){
  62. if(result!=OK)
  63. printf("Incorrect command line arguments supplied\n");
  64. printf("\n");
  65. printf("NRPE Plugin for Nagios\n");
  66. printf("Copyright (c) 1999-2008 Ethan Galstad (nagios@nagios.org)\n");
  67. printf("Version: %s\n",PROGRAM_VERSION);
  68. printf("Last Modified: %s\n",MODIFICATION_DATE);
  69. printf("License: GPL v2 with exemptions (-l for more info)\n");
  70. #ifdef HAVE_SSL
  71. printf("SSL/TLS Available: Anonymous DH Mode, OpenSSL 0.9.6 or higher required\n");
  72. #endif
  73. printf("\n");
  74. }
  75. if(result!=OK || show_help==TRUE){
  76. printf("Usage: check_nrpe -H <host> [ -b <bindaddr> ] [-4] [-6] [-n] [-u] [-p <port>] [-t <timeout>] [-c <command>] [-a <arglist...>]\n");
  77. printf("\n");
  78. printf("Options:\n");
  79. printf(" -n = Do no use SSL\n");
  80. printf(" -u = Make socket timeouts return an UNKNOWN state instead of CRITICAL\n");
  81. printf(" <host> = The address of the host running the NRPE daemon\n");
  82. printf(" <bindaddr> = bind to local address\n");
  83. printf(" -4 = bind to ipv4 only\n");
  84. printf(" -6 = bind to ipv6 only\n");
  85. printf(" [port] = The port on which the daemon is running (default=%d)\n",DEFAULT_SERVER_PORT);
  86. printf(" [timeout] = Number of seconds before connection times out (default=%d)\n",DEFAULT_SOCKET_TIMEOUT);
  87. printf(" [command] = The name of the command that the remote daemon should run\n");
  88. printf(" [arglist] = Optional arguments that should be passed to the command. Multiple\n");
  89. printf(" arguments should be separated by a space. If provided, this must be\n");
  90. printf(" the last option supplied on the command line.\n");
  91. printf("\n");
  92. printf("Note:\n");
  93. printf("This plugin requires that you have the NRPE daemon running on the remote host.\n");
  94. printf("You must also have configured the daemon to associate a specific plugin command\n");
  95. printf("with the [command] option you are specifying here. Upon receipt of the\n");
  96. printf("[command] argument, the NRPE daemon will run the appropriate plugin command and\n");
  97. printf("send the plugin output and return code back to *this* plugin. This allows you\n");
  98. printf("to execute plugins on remote hosts and 'fake' the results to make Nagios think\n");
  99. printf("the plugin is being run locally.\n");
  100. printf("\n");
  101. }
  102. if(show_license==TRUE)
  103. display_license();
  104. if(result!=OK || show_help==TRUE || show_license==TRUE || show_version==TRUE)
  105. exit(STATE_UNKNOWN);
  106. /* generate the CRC 32 table */
  107. generate_crc32_table();
  108. #ifdef HAVE_SSL
  109. /* initialize SSL */
  110. if(use_ssl==TRUE){
  111. SSL_library_init();
  112. SSLeay_add_ssl_algorithms();
  113. meth=SSLv23_client_method();
  114. SSL_load_error_strings();
  115. if((ctx=SSL_CTX_new(meth))==NULL){
  116. printf("CHECK_NRPE: Error - could not create SSL context.\n");
  117. exit(STATE_CRITICAL);
  118. }
  119. /* ADDED 01/19/2004 */
  120. /* use only TLSv1 protocol */
  121. SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
  122. }
  123. #endif
  124. /* initialize alarm signal handling */
  125. signal(SIGALRM,alarm_handler);
  126. /* set socket timeout */
  127. alarm(socket_timeout);
  128. /* try to connect to the host at the given port number */
  129. if((sd=my_connect(server_name, &hostaddr, server_port, address_family,
  130. bind_address)) < 0 ) {
  131. exit (255);
  132. }
  133. else {
  134. result=STATE_OK;
  135. }
  136. #ifdef HAVE_SSL
  137. /* do SSL handshake */
  138. if(result==STATE_OK && use_ssl==TRUE){
  139. if((ssl=SSL_new(ctx))!=NULL){
  140. SSL_CTX_set_cipher_list(ctx,"ADH");
  141. SSL_set_fd(ssl,sd);
  142. if((rc=SSL_connect(ssl))!=1){
  143. printf("CHECK_NRPE: Error - Could not complete SSL handshake.\n");
  144. #ifdef DEBUG
  145. printf("SSL_connect=%d\n",rc);
  146. /*
  147. rc=SSL_get_error(ssl,rc);
  148. printf("SSL_get_error=%d\n",rc);
  149. printf("ERR_get_error=%lu\n",ERR_get_error());
  150. printf("%s\n",ERR_error_string(rc,NULL));
  151. */
  152. ERR_print_errors_fp(stdout);
  153. #endif
  154. result=STATE_CRITICAL;
  155. }
  156. }
  157. else{
  158. printf("CHECK_NRPE: Error - Could not create SSL connection structure.\n");
  159. result=STATE_CRITICAL;
  160. }
  161. /* bail if we had errors */
  162. if(result!=STATE_OK){
  163. SSL_CTX_free(ctx);
  164. close(sd);
  165. exit(result);
  166. }
  167. }
  168. #endif
  169. /* we're connected and ready to go */
  170. if(result==STATE_OK){
  171. /* clear the packet buffer */
  172. bzero(&send_packet,sizeof(send_packet));
  173. /* fill the packet with semi-random data */
  174. randomize_buffer((char *)&send_packet,sizeof(send_packet));
  175. /* initialize packet data */
  176. send_packet.packet_version=(int16_t)htons(NRPE_PACKET_VERSION_2);
  177. send_packet.packet_type=(int16_t)htons(QUERY_PACKET);
  178. strncpy(&send_packet.buffer[0],query,MAX_PACKETBUFFER_LENGTH);
  179. send_packet.buffer[MAX_PACKETBUFFER_LENGTH-1]='\x0';
  180. /* calculate the crc 32 value of the packet */
  181. send_packet.crc32_value=(u_int32_t)0L;
  182. calculated_crc32=calculate_crc32((char *)&send_packet,sizeof(send_packet));
  183. send_packet.crc32_value=(u_int32_t)htonl(calculated_crc32);
  184. /***** ENCRYPT REQUEST *****/
  185. /* send the packet */
  186. bytes_to_send=sizeof(send_packet);
  187. if(use_ssl==FALSE)
  188. rc=sendall(sd,(char *)&send_packet,&bytes_to_send);
  189. #ifdef HAVE_SSL
  190. else{
  191. rc=SSL_write(ssl,&send_packet,bytes_to_send);
  192. if(rc<0)
  193. rc=-1;
  194. }
  195. #endif
  196. if(rc==-1){
  197. printf("CHECK_NRPE: Error sending query to host.\n");
  198. close(sd);
  199. return STATE_UNKNOWN;
  200. }
  201. /* wait for the response packet */
  202. bytes_to_recv=sizeof(receive_packet);
  203. if(use_ssl==FALSE)
  204. rc=recvall(sd,(char *)&receive_packet,&bytes_to_recv,socket_timeout);
  205. #ifdef HAVE_SSL
  206. else
  207. rc=SSL_read(ssl,&receive_packet,bytes_to_recv);
  208. #endif
  209. /* reset timeout */
  210. alarm(0);
  211. /* close the connection */
  212. #ifdef HAVE_SSL
  213. if(use_ssl==TRUE){
  214. SSL_shutdown(ssl);
  215. SSL_free(ssl);
  216. SSL_CTX_free(ctx);
  217. }
  218. #endif
  219. graceful_close(sd,1000);
  220. /* recv() error */
  221. if(rc<0){
  222. printf("CHECK_NRPE: Error receiving data from daemon.\n");
  223. return STATE_UNKNOWN;
  224. }
  225. /* server disconnected */
  226. else if(rc==0){
  227. printf("CHECK_NRPE: Received 0 bytes from daemon. Check the remote server logs for error messages.\n");
  228. return STATE_UNKNOWN;
  229. }
  230. /* receive underflow */
  231. else if(bytes_to_recv<sizeof(receive_packet)){
  232. printf("CHECK_NRPE: Receive underflow - only %d bytes received (%d expected).\n",bytes_to_recv,sizeof(receive_packet));
  233. return STATE_UNKNOWN;
  234. }
  235. /***** DECRYPT RESPONSE *****/
  236. /* check the crc 32 value */
  237. packet_crc32=ntohl(receive_packet.crc32_value);
  238. receive_packet.crc32_value=0L;
  239. calculated_crc32=calculate_crc32((char *)&receive_packet,sizeof(receive_packet));
  240. if(packet_crc32!=calculated_crc32){
  241. printf("CHECK_NRPE: Response packet had invalid CRC32.\n");
  242. close(sd);
  243. return STATE_UNKNOWN;
  244. }
  245. /* check packet version */
  246. if(ntohs(receive_packet.packet_version)!=NRPE_PACKET_VERSION_2){
  247. printf("CHECK_NRPE: Invalid packet version received from server.\n");
  248. close(sd);
  249. return STATE_UNKNOWN;
  250. }
  251. /* check packet type */
  252. if(ntohs(receive_packet.packet_type)!=RESPONSE_PACKET){
  253. printf("CHECK_NRPE: Invalid packet type received from server.\n");
  254. close(sd);
  255. return STATE_UNKNOWN;
  256. }
  257. /* get the return code from the remote plugin */
  258. result=(int16_t)ntohs(receive_packet.result_code);
  259. /* print the output returned by the daemon */
  260. receive_packet.buffer[MAX_PACKETBUFFER_LENGTH-1]='\x0';
  261. if(!strcmp(receive_packet.buffer,""))
  262. printf("CHECK_NRPE: No output returned from daemon.\n");
  263. else
  264. printf("%s\n",receive_packet.buffer);
  265. }
  266. /* reset the alarm */
  267. else
  268. alarm(0);
  269. return result;
  270. }
  271. /* process command line arguments */
  272. int process_arguments(int argc, char **argv){
  273. char optchars[MAX_INPUT_BUFFER];
  274. int argindex=0;
  275. int c=1;
  276. int i=1;
  277. #ifdef HAVE_GETOPT_LONG
  278. int option_index=0;
  279. static struct option long_options[]={
  280. {"host", required_argument, 0, 'H'},
  281. {"bind", required_argument, 0, 'b'},
  282. {"command", required_argument, 0, 'c'},
  283. {"args", required_argument, 0, 'a'},
  284. {"no-ssl", no_argument, 0, 'n'},
  285. {"unknown-timeout", no_argument, 0, 'u'},
  286. {"ipv4", no_argument, 0, '4'},
  287. {"ipv6", no_argument, 0, '6'},
  288. {"timeout", required_argument, 0, 't'},
  289. {"port", required_argument, 0, 'p'},
  290. {"help", no_argument, 0, 'h'},
  291. {"license", no_argument, 0, 'l'},
  292. {0, 0, 0, 0}
  293. };
  294. #endif
  295. /* no options were supplied */
  296. if(argc<2)
  297. return ERROR;
  298. snprintf(optchars,MAX_INPUT_BUFFER,"H:b:c:a:t:p:nu46hl");
  299. while(1){
  300. #ifdef HAVE_GETOPT_LONG
  301. c=getopt_long(argc,argv,optchars,long_options,&option_index);
  302. #else
  303. c=getopt(argc,argv,optchars);
  304. #endif
  305. if(c==-1 || c==EOF)
  306. break;
  307. /* process all arguments */
  308. switch(c){
  309. case '?':
  310. case 'h':
  311. show_help=TRUE;
  312. break;
  313. case 'b':
  314. bind_address=strdup(optarg);
  315. break;
  316. case 'V':
  317. show_version=TRUE;
  318. break;
  319. case 'l':
  320. show_license=TRUE;
  321. break;
  322. case 't':
  323. socket_timeout=atoi(optarg);
  324. if(socket_timeout<=0)
  325. return ERROR;
  326. break;
  327. case 'p':
  328. server_port=atoi(optarg);
  329. if(server_port<=0)
  330. return ERROR;
  331. break;
  332. case 'H':
  333. server_name=strdup(optarg);
  334. break;
  335. case 'c':
  336. command_name=strdup(optarg);
  337. break;
  338. case 'a':
  339. argindex=optind;
  340. break;
  341. case 'n':
  342. use_ssl=FALSE;
  343. break;
  344. case 'u':
  345. timeout_return_code=STATE_UNKNOWN;
  346. break;
  347. case '4':
  348. address_family=AF_INET;
  349. break;
  350. case '6':
  351. address_family=AF_INET6;
  352. break;
  353. default:
  354. return ERROR;
  355. break;
  356. }
  357. }
  358. /* determine (base) command query */
  359. snprintf(query,sizeof(query),"%s",(command_name==NULL)?DEFAULT_NRPE_COMMAND:command_name);
  360. query[sizeof(query)-1]='\x0';
  361. /* get the command args */
  362. if(argindex>0){
  363. for(c=argindex-1;c<argc;c++){
  364. i=sizeof(query)-strlen(query)-2;
  365. if(i<=0)
  366. break;
  367. strcat(query,"!");
  368. strncat(query,argv[c],i);
  369. query[sizeof(query)-1]='\x0';
  370. }
  371. }
  372. /* make sure required args were supplied */
  373. if(server_name==NULL && show_help==FALSE && show_version==FALSE && show_license==FALSE)
  374. return ERROR;
  375. return OK;
  376. }
  377. void alarm_handler(int sig){
  378. printf("CHECK_NRPE: Socket timeout after %d seconds.\n",socket_timeout);
  379. exit(timeout_return_code);
  380. }
  381. /* submitted by Mark Plaksin 08/31/2006 */
  382. int graceful_close(int sd, int timeout){
  383. fd_set in;
  384. struct timeval tv;
  385. char buf[1000];
  386. /* send FIN packet */
  387. shutdown(sd,SHUT_WR);
  388. for(;;){
  389. FD_ZERO(&in);
  390. FD_SET(sd,&in);
  391. tv.tv_sec=timeout/1000;
  392. tv.tv_usec=(timeout % 1000)*1000;
  393. /* timeout or error */
  394. if(1!=select(sd+1,&in,NULL,NULL,&tv))
  395. break;
  396. /* no more data (FIN or RST) */
  397. if(0>=recv(sd,buf,sizeof(buf),0))
  398. break;
  399. }
  400. #ifdef HAVE_CLOSESOCKET
  401. closesocket(sd);
  402. #else
  403. close(sd);
  404. #endif
  405. return OK;
  406. }