check_nrpe.c 11 KB

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