check_nrpe.c 12 KB

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