Back to index

nagios-nrpe  2.13
nrpe.c
Go to the documentation of this file.
00001 /*******************************************************************************
00002  *
00003  * NRPE.C - Nagios Remote Plugin Executor
00004  * Copyright (c) 1999-2008 Ethan Galstad (nagios@nagios.org)
00005  * License: GPL
00006  *
00007  * Last Modified: 11-11-2011
00008  *
00009  * Command line: nrpe -c <config_file> [--inetd | --daemon]
00010  *
00011  * Description:
00012  *
00013  * This program is designed to run as a background process and
00014  * handle incoming requests (from the host running Nagios) for
00015  * plugin execution.  It is useful for running "local" plugins
00016  * such as check_users, check_load, check_disk, etc. without
00017  * having to use rsh or ssh.
00018  * 
00019  ******************************************************************************/
00020 
00021 /*
00022  * 08-10-2011 IPv4 subnetworks support added.
00023  * Main change in nrpe.c is that is_an_allowed_host() moved to acl.c.
00024  * now allowed_hosts is parsed by parse_allowed_hosts() from acl.c.
00025  */ 
00026 
00027 #include "../include/common.h"
00028 #include "../include/config.h"
00029 #include "../include/nrpe.h"
00030 #include "../include/utils.h"
00031 #include "../include/acl.h"
00032 
00033 #ifdef HAVE_SSL
00034 #include "../include/dh.h"
00035 #endif
00036 
00037 #ifdef HAVE_LIBWRAP
00038 int allow_severity=LOG_INFO;
00039 int deny_severity=LOG_WARNING;
00040 #endif
00041 
00042 #ifdef HAVE_SSL
00043 SSL_METHOD *meth;
00044 SSL_CTX *ctx;
00045 int use_ssl=TRUE;
00046 #else
00047 int use_ssl=FALSE;
00048 #endif
00049 
00050 #define DEFAULT_COMMAND_TIMEOUT    60                   /* default timeout for execution of plugins */
00051 #define MAXFD                   64
00052 #define NASTY_METACHARS         "|`&><'\"\\[]{};"
00053 
00054 char    *command_name=NULL;
00055 char    *macro_argv[MAX_COMMAND_ARGUMENTS];
00056 
00057 char    config_file[MAX_INPUT_BUFFER]="nrpe.cfg";
00058 int     log_facility=LOG_DAEMON;
00059 int     server_port=DEFAULT_SERVER_PORT;
00060 char    server_address[16]="0.0.0.0";
00061 int     socket_timeout=DEFAULT_SOCKET_TIMEOUT;
00062 int     command_timeout=DEFAULT_COMMAND_TIMEOUT;
00063 int     connection_timeout=DEFAULT_CONNECTION_TIMEOUT;
00064 char    *command_prefix=NULL;
00065 
00066 command *command_list=NULL;
00067 
00068 char    *nrpe_user=NULL;
00069 char    *nrpe_group=NULL;
00070 
00071 char    *allowed_hosts=NULL;
00072 
00073 char    *pid_file=NULL;
00074 int     wrote_pid_file=FALSE;
00075 
00076 int     allow_arguments=FALSE;
00077 
00078 int     allow_weak_random_seed=FALSE;
00079 
00080 int     sigrestart=FALSE;
00081 int     sigshutdown=FALSE;
00082 
00083 int     show_help=FALSE;
00084 int     show_license=FALSE;
00085 int     show_version=FALSE;
00086 int     use_inetd=TRUE;
00087 int     debug=FALSE;
00088 
00089 
00090 
00091 
00092 int main(int argc, char **argv){
00093        int result=OK;
00094        int x;
00095        char buffer[MAX_INPUT_BUFFER];
00096        char *env_string=NULL;
00097 #ifdef HAVE_SSL
00098        DH *dh;
00099        char seedfile[FILENAME_MAX];
00100        int i,c;
00101 #endif
00102 
00103        /* set some environment variables */
00104        asprintf(&env_string,"NRPE_MULTILINESUPPORT=1");
00105        putenv(env_string);
00106        asprintf(&env_string,"NRPE_PROGRAMVERSION=%s",PROGRAM_VERSION);
00107        putenv(env_string);
00108 
00109        /* process command-line args */
00110        result=process_arguments(argc,argv);
00111 
00112         if(result!=OK || show_help==TRUE || show_license==TRUE || show_version==TRUE){
00113 
00114               printf("\n");
00115               printf("NRPE - Nagios Remote Plugin Executor\n");
00116               printf("Copyright (c) 1999-2008 Ethan Galstad (nagios@nagios.org)\n");
00117               printf("Version: %s\n",PROGRAM_VERSION);
00118               printf("Last Modified: %s\n",MODIFICATION_DATE);
00119               printf("License: GPL v2 with exemptions (-l for more info)\n");
00120 #ifdef HAVE_SSL
00121               printf("SSL/TLS Available: Anonymous DH Mode, OpenSSL 0.9.6 or higher required\n");
00122 #endif
00123 #ifdef HAVE_LIBWRAP
00124               printf("TCP Wrappers Available\n");
00125 #endif
00126               printf("\n");
00127 #ifdef ENABLE_COMMAND_ARGUMENTS
00128               printf("***************************************************************\n");
00129               printf("** POSSIBLE SECURITY RISK - COMMAND ARGUMENTS ARE SUPPORTED! **\n");
00130               printf("**      Read the NRPE SECURITY file for more information     **\n");
00131               printf("***************************************************************\n");
00132               printf("\n");
00133 #endif
00134 #ifndef HAVE_LIBWRAP
00135               printf("***************************************************************\n");
00136               printf("** POSSIBLE SECURITY RISK - TCP WRAPPERS ARE NOT AVAILABLE!  **\n");
00137               printf("**      Read the NRPE SECURITY file for more information     **\n");
00138               printf("***************************************************************\n");
00139               printf("\n");
00140 #endif
00141                }
00142 
00143        if(show_license==TRUE)
00144               display_license();
00145 
00146        else if(result!=OK || show_help==TRUE){
00147 
00148               printf("Usage: nrpe [-n] -c <config_file> <mode>\n");
00149               printf("\n");
00150               printf("Options:\n");
00151               printf(" -n            = Do not use SSL\n");
00152               printf(" <config_file> = Name of config file to use\n");
00153               printf(" <mode>        = One of the following two operating modes:\n");  
00154               printf("   -i          =    Run as a service under inetd or xinetd\n");
00155               printf("   -d          =    Run as a standalone daemon\n");
00156               printf("\n");
00157               printf("Notes:\n");
00158               printf("This program is designed to process requests from the check_nrpe\n");
00159               printf("plugin on the host(s) running Nagios.  It can run as a service\n");
00160               printf("under inetd or xinetd (read the docs for info on this), or as a\n");
00161               printf("standalone daemon. Once a request is received from an authorized\n");
00162               printf("host, NRPE will execute the command/plugin (as defined in the\n");
00163               printf("config file) and return the plugin output and return code to the\n");
00164               printf("check_nrpe plugin.\n");
00165               printf("\n");
00166               }
00167 
00168         if(result!=OK || show_help==TRUE || show_license==TRUE || show_version==TRUE)
00169               exit(STATE_UNKNOWN);
00170 
00171 
00172        /* open a connection to the syslog facility */
00173        /* facility name may be overridden later */
00174        get_log_facility(NRPE_LOG_FACILITY);
00175         openlog("nrpe",LOG_PID,log_facility); 
00176 
00177        /* make sure the config file uses an absolute path */
00178        if(config_file[0]!='/'){
00179 
00180               /* save the name of the config file */
00181               strncpy(buffer,config_file,sizeof(buffer));
00182               buffer[sizeof(buffer)-1]='\x0';
00183 
00184               /* get absolute path of current working directory */
00185               strcpy(config_file,"");
00186               getcwd(config_file,sizeof(config_file));
00187 
00188               /* append a forward slash */
00189               strncat(config_file,"/",sizeof(config_file)-2);
00190               config_file[sizeof(config_file)-1]='\x0';
00191 
00192               /* append the config file to the path */
00193               strncat(config_file,buffer,sizeof(config_file)-strlen(config_file)-1);
00194               config_file[sizeof(config_file)-1]='\x0';
00195                }
00196 
00197        /* read the config file */
00198        result=read_config_file(config_file);     
00199 
00200        /* exit if there are errors... */
00201        if(result==ERROR){
00202               syslog(LOG_ERR,"Config file '%s' contained errors, aborting...",config_file);
00203               return STATE_CRITICAL;
00204               }
00205 
00206         /* generate the CRC 32 table */
00207         generate_crc32_table();
00208 
00209        /* initialize macros */
00210        for(x=0;x<MAX_COMMAND_ARGUMENTS;x++)
00211               macro_argv[x]=NULL;
00212 
00213 #ifdef HAVE_SSL
00214        /* initialize SSL */
00215        if(use_ssl==TRUE){
00216               SSL_library_init();
00217               SSLeay_add_ssl_algorithms();
00218               meth=SSLv23_server_method();
00219               SSL_load_error_strings();
00220 
00221               /* use week random seed if necessary */
00222               if(allow_weak_random_seed && (RAND_status()==0)){
00223 
00224                      if(RAND_file_name(seedfile,sizeof(seedfile)-1))
00225                             if(RAND_load_file(seedfile,-1))
00226                                    RAND_write_file(seedfile);
00227 
00228                      if(RAND_status()==0){
00229                             syslog(LOG_ERR,"Warning: SSL/TLS uses a weak random seed which is highly discouraged");
00230                             srand(time(NULL));
00231                             for(i=0;i<500 && RAND_status()==0;i++){
00232                                    for(c=0;c<sizeof(seedfile);c+=sizeof(int)){
00233                                           *((int *)(seedfile+c))=rand();
00234                                            }
00235                                    RAND_seed(seedfile,sizeof(seedfile));
00236                                    }
00237                             }
00238                      }
00239 
00240               if((ctx=SSL_CTX_new(meth))==NULL){
00241                      syslog(LOG_ERR,"Error: could not create SSL context.\n");
00242                      exit(STATE_CRITICAL);
00243                       }
00244 
00245               /* ADDED 01/19/2004 */
00246               /* use only TLSv1 protocol */
00247               SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
00248 
00249               /* use anonymous DH ciphers */
00250               SSL_CTX_set_cipher_list(ctx,"ADH");
00251               dh=get_dh512();
00252               SSL_CTX_set_tmp_dh(ctx,dh);
00253               DH_free(dh);
00254               if(debug==TRUE)
00255                      syslog(LOG_INFO,"INFO: SSL/TLS initialized. All network traffic will be encrypted.");
00256                }
00257        else{
00258               if(debug==TRUE)
00259                      syslog(LOG_INFO,"INFO: SSL/TLS NOT initialized. Network encryption DISABLED.");
00260                }
00261 #endif
00262 
00263        /* if we're running under inetd... */
00264        if(use_inetd==TRUE){
00265 
00266               /* make sure we're not root */
00267               check_privileges();
00268 
00269               /* redirect STDERR to /dev/null */
00270               close(2);
00271               open("/dev/null",O_WRONLY);
00272 
00273               /* handle the connection */
00274               handle_connection(0);
00275                }
00276 
00277        /* else daemonize and start listening for requests... */
00278        else if(fork()==0){
00279               
00280               /* we're a daemon - set up a new process group */
00281               setsid();
00282 
00283               /* close standard file descriptors */
00284               close(0);
00285               close(1);
00286               close(2);
00287 
00288               /* redirect standard descriptors to /dev/null */
00289               open("/dev/null",O_RDONLY);
00290               open("/dev/null",O_WRONLY);
00291               open("/dev/null",O_WRONLY);
00292 
00293               chdir("/");
00294               /*umask(0);*/
00295 
00296               /* handle signals */
00297               signal(SIGQUIT,sighandler);
00298               signal(SIGTERM,sighandler);
00299               signal(SIGHUP,sighandler);
00300 
00301               /* log info to syslog facility */
00302               syslog(LOG_NOTICE,"Starting up daemon");
00303 
00304               /* write pid file */
00305               if(write_pid_file()==ERROR)
00306                      return STATE_CRITICAL;
00307               
00308               /* drop privileges */
00309               drop_privileges(nrpe_user,nrpe_group);
00310 
00311               /* make sure we're not root */
00312               check_privileges();
00313 
00314               do{
00315 
00316                      /* reset flags */
00317                      sigrestart=FALSE;
00318                      sigshutdown=FALSE;
00319 
00320                      /* wait for connections */
00321                      wait_for_connections();
00322 
00323                      /* free all memory we allocated */
00324                      free_memory();
00325 
00326                      if(sigrestart==TRUE){
00327 
00328                             /* read the config file */
00329                             result=read_config_file(config_file);     
00330 
00331                             /* exit if there are errors... */
00332                             if(result==ERROR){
00333                                    syslog(LOG_ERR,"Config file '%s' contained errors, bailing out...",config_file);
00334                                    return STATE_CRITICAL;
00335                                     }
00336                              }
00337        
00338                       }while(sigrestart==TRUE && sigshutdown==FALSE);
00339 
00340               /* remove pid file */
00341               remove_pid_file();
00342 
00343               syslog(LOG_NOTICE,"Daemon shutdown\n");
00344                }
00345 
00346 #ifdef HAVE_SSL
00347        if(use_ssl==TRUE)
00348               SSL_CTX_free(ctx);
00349 #endif
00350 
00351        /* We are now running in daemon mode, or the connection handed over by inetd has
00352           been completed, so the parent process exits */
00353         return STATE_OK;
00354        }
00355 
00356 
00357 
00358 
00359 /* read in the configuration file */
00360 int read_config_file(char *filename){
00361        FILE *fp;
00362        char config_file[MAX_FILENAME_LENGTH];
00363        char input_buffer[MAX_INPUT_BUFFER];
00364        char *input_line;
00365        char *temp_buffer;
00366        char *varname;
00367        char *varvalue;
00368        int line=0;
00369        int len=0;
00370        int x=0;
00371 
00372 
00373        /* open the config file for reading */
00374        fp=fopen(filename,"r");
00375 
00376        /* exit if we couldn't open the config file */
00377        if(fp==NULL){
00378               syslog(LOG_ERR,"Unable to open config file '%s' for reading\n",filename);
00379               return ERROR;
00380                }
00381 
00382        while(fgets(input_buffer,MAX_INPUT_BUFFER-1,fp)){
00383 
00384               line++;
00385               input_line=input_buffer;
00386 
00387               /* skip leading whitespace */
00388               while(isspace(*input_line))
00389                      ++input_line;
00390 
00391               /* trim trailing whitespace */
00392               len=strlen(input_line);
00393               for(x=len-1;x>=0;x--){
00394                      if(isspace(input_line[x]))
00395                             input_line[x]='\x0';
00396                      else
00397                             break;
00398                       }
00399 
00400               /* skip comments and blank lines */
00401               if(input_line[0]=='#')
00402                      continue;
00403               if(input_line[0]=='\x0')
00404                      continue;
00405               if(input_line[0]=='\n')
00406                      continue;
00407 
00408               /* get the variable name */
00409               varname=strtok(input_line,"=");
00410               if(varname==NULL){
00411                      syslog(LOG_ERR,"No variable name specified in config file '%s' - Line %d\n",filename,line);
00412                      return ERROR;
00413                       }
00414 
00415               /* get the variable value */
00416               varvalue=strtok(NULL,"\n");
00417               if(varvalue==NULL){
00418                      syslog(LOG_ERR,"No variable value specified in config file '%s' - Line %d\n",filename,line);
00419                      return ERROR;
00420                       }
00421 
00422               /* allow users to specify directories to recurse into for config files */
00423               else if(!strcmp(varname,"include_dir")){
00424 
00425                      strncpy(config_file,varvalue,sizeof(config_file)-1);
00426                      config_file[sizeof(config_file)-1]='\x0';
00427 
00428                      /* strip trailing / if necessary */
00429                      if(config_file[strlen(config_file)-1]=='/')
00430                             config_file[strlen(config_file)-1]='\x0';
00431 
00432                      /* process the config directory... */
00433                      if(read_config_dir(config_file)==ERROR)
00434                             syslog(LOG_ERR,"Continuing with errors...");
00435                      }
00436 
00437               /* allow users to specify individual config files to include */
00438               else if(!strcmp(varname,"include") || !strcmp(varname,"include_file")){
00439 
00440                      /* process the config file... */
00441                      if(read_config_file(varvalue)==ERROR)
00442                             syslog(LOG_ERR,"Continuing with errors...");
00443                       }
00444 
00445               else if(!strcmp(varname,"server_port")){
00446                      server_port=atoi(varvalue);
00447                      if(server_port<1024){
00448                             syslog(LOG_ERR,"Invalid port number specified in config file '%s' - Line %d\n",filename,line);
00449                             return ERROR;
00450                              }
00451                       }
00452               else if(!strcmp(varname,"command_prefix"))
00453                      command_prefix=strdup(varvalue);
00454 
00455               else if(!strcmp(varname,"server_address")){
00456                         strncpy(server_address,varvalue,sizeof(server_address) - 1);
00457                         server_address[sizeof(server_address)-1]='\0';
00458                         }
00459               else if(!strcmp(varname,"allowed_hosts")) {
00460                             allowed_hosts=strdup(varvalue);
00461                             parse_allowed_hosts(allowed_hosts);
00462               }
00463               else if(strstr(input_line,"command[")){
00464                      temp_buffer=strtok(varname,"[");
00465                      temp_buffer=strtok(NULL,"]");
00466                      if(temp_buffer==NULL){
00467                             syslog(LOG_ERR,"Invalid command specified in config file '%s' - Line %d\n",filename,line);
00468                             return ERROR;
00469                              }
00470                      add_command(temp_buffer,varvalue);
00471                       }
00472 
00473               else if(strstr(input_buffer,"debug")){
00474                      debug=atoi(varvalue);
00475                      if(debug>0)
00476                             debug=TRUE;
00477                      else 
00478                             debug=FALSE;
00479                       }
00480 
00481                 else if(!strcmp(varname,"nrpe_user"))
00482                      nrpe_user=strdup(varvalue);
00483 
00484                 else if(!strcmp(varname,"nrpe_group"))
00485                      nrpe_group=strdup(varvalue);
00486               
00487               else if(!strcmp(varname,"dont_blame_nrpe"))
00488                      allow_arguments=(atoi(varvalue)==1)?TRUE:FALSE;
00489 
00490               else if(!strcmp(varname,"command_timeout")){
00491                      command_timeout=atoi(varvalue);
00492                      if(command_timeout<1){
00493                             syslog(LOG_ERR,"Invalid command_timeout specified in config file '%s' - Line %d\n",filename,line);
00494                             return ERROR;
00495                              }
00496                       }
00497 
00498               else if(!strcmp(varname,"connection_timeout")){
00499                      connection_timeout=atoi(varvalue);
00500                      if(connection_timeout<1){
00501                             syslog(LOG_ERR,"Invalid connection_timeout specified in config file '%s' - Line %d\n",filename,line);
00502                             return ERROR;
00503                              }
00504                       }
00505 
00506               else if(!strcmp(varname,"allow_weak_random_seed"))
00507                      allow_weak_random_seed=(atoi(varvalue)==1)?TRUE:FALSE;
00508 
00509               else if(!strcmp(varname,"pid_file"))
00510                      pid_file=strdup(varvalue);
00511 
00512               else if(!strcmp(varname,"log_facility")){
00513                      if((get_log_facility(varvalue))==OK){
00514                             /* re-open log using new facility */
00515                             closelog();
00516                             openlog("nrpe",LOG_PID,log_facility); 
00517                             }
00518                      else
00519                             syslog(LOG_WARNING,"Invalid log_facility specified in config file '%s' - Line %d\n",filename,line);
00520                      }
00521 
00522               else{
00523                      syslog(LOG_WARNING,"Unknown option specified in config file '%s' - Line %d\n",filename,line);
00524                      continue;
00525                       }
00526 
00527                }
00528 
00529 
00530        /* close the config file */
00531        fclose(fp);
00532 
00533        return OK;
00534        }
00535 
00536 
00537 /* process all config files in a specific config directory (with directory recursion) */
00538 int read_config_dir(char *dirname){
00539        char config_file[MAX_FILENAME_LENGTH];
00540        DIR *dirp;
00541        struct dirent *dirfile;
00542        struct stat buf;
00543        int result=OK;
00544        int x;
00545 
00546        /* open the directory for reading */
00547        dirp=opendir(dirname);
00548         if(dirp==NULL){
00549               syslog(LOG_ERR,"Could not open config directory '%s' for reading.\n",dirname);
00550               return ERROR;
00551                }
00552 
00553        /* process all files in the directory... */
00554        while((dirfile=readdir(dirp))!=NULL){
00555 
00556               /* create the full path to the config file or subdirectory */
00557               snprintf(config_file,sizeof(config_file)-1,"%s/%s",dirname,dirfile->d_name);
00558               config_file[sizeof(config_file)-1]='\x0';
00559               stat(config_file, &buf);
00560 
00561               /* process this if it's a config file... */
00562               x=strlen(dirfile->d_name);
00563               if(x>4 && !strcmp(dirfile->d_name+(x-4),".cfg")){
00564 
00565                      /* only process normal files */
00566                      if(!S_ISREG(buf.st_mode))
00567                             continue;
00568 
00569                      /* process the config file */
00570                      result=read_config_file(config_file);
00571 
00572                      /* break out if we encountered an error */
00573                      if(result==ERROR)
00574                             break;
00575                       }
00576 
00577               /* recurse into subdirectories... */
00578               if(S_ISDIR(buf.st_mode)){
00579 
00580                      /* ignore current, parent and hidden directory entries */
00581                      if(dirfile->d_name[0]=='.')
00582                             continue;
00583 
00584                      /* process the config directory */
00585                      result=read_config_dir(config_file);
00586 
00587                      /* break out if we encountered an error */
00588                      if(result==ERROR)
00589                             break;
00590                       }
00591               }
00592 
00593        closedir(dirp);
00594 
00595        return result;
00596         }
00597 
00598 
00599 
00600 /* determines facility to use with syslog */
00601 int get_log_facility(char *varvalue){
00602 
00603        if(!strcmp(varvalue,"kern"))
00604               log_facility=LOG_KERN;
00605        else if(!strcmp(varvalue,"user"))
00606               log_facility=LOG_USER;
00607        else if(!strcmp(varvalue,"mail"))
00608               log_facility=LOG_MAIL;
00609        else if(!strcmp(varvalue,"daemon"))
00610               log_facility=LOG_DAEMON;
00611        else if(!strcmp(varvalue,"auth"))
00612               log_facility=LOG_AUTH;
00613        else if(!strcmp(varvalue,"syslog"))
00614               log_facility=LOG_SYSLOG;
00615        else if(!strcmp(varvalue,"lrp"))
00616               log_facility=LOG_LPR;
00617        else if(!strcmp(varvalue,"news"))
00618               log_facility=LOG_NEWS;
00619        else if(!strcmp(varvalue,"uucp"))
00620               log_facility=LOG_UUCP;
00621        else if(!strcmp(varvalue,"cron"))
00622               log_facility=LOG_CRON;
00623        else if(!strcmp(varvalue,"authpriv"))
00624               log_facility=LOG_AUTHPRIV;
00625        else if(!strcmp(varvalue,"ftp"))
00626               log_facility=LOG_FTP;
00627        else if(!strcmp(varvalue,"local0"))
00628               log_facility=LOG_LOCAL0;
00629        else if(!strcmp(varvalue,"local1"))
00630               log_facility=LOG_LOCAL1;
00631        else if(!strcmp(varvalue,"local2"))
00632               log_facility=LOG_LOCAL2;
00633        else if(!strcmp(varvalue,"local3"))
00634               log_facility=LOG_LOCAL3;
00635        else if(!strcmp(varvalue,"local4"))
00636               log_facility=LOG_LOCAL4;
00637        else if(!strcmp(varvalue,"local5"))
00638               log_facility=LOG_LOCAL5;
00639        else if(!strcmp(varvalue,"local6"))
00640               log_facility=LOG_LOCAL6;
00641        else if(!strcmp(varvalue,"local7"))
00642               log_facility=LOG_LOCAL7;
00643        else{
00644               log_facility=LOG_DAEMON;
00645               return ERROR;
00646               }
00647 
00648        return OK;
00649        }
00650 
00651 
00652 
00653 /* adds a new command definition from the config file to the list in memory */
00654 int add_command(char *command_name, char *command_line){
00655        command *new_command;
00656 
00657        if(command_name==NULL || command_line==NULL)
00658               return ERROR;
00659 
00660        /* allocate memory for the new command */
00661        new_command=(command *)malloc(sizeof(command));
00662        if(new_command==NULL)
00663               return ERROR;
00664 
00665        new_command->command_name=strdup(command_name);
00666        if(new_command->command_name==NULL){
00667               free(new_command);
00668               return ERROR;
00669                }
00670        new_command->command_line=strdup(command_line);
00671        if(new_command->command_line==NULL){
00672               free(new_command->command_name);
00673               free(new_command);
00674               return ERROR;
00675                }
00676 
00677        /* add new command to head of list in memory */
00678        new_command->next=command_list;
00679        command_list=new_command;
00680 
00681        if(debug==TRUE)
00682               syslog(LOG_DEBUG,"Added command[%s]=%s\n",command_name,command_line);
00683 
00684        return OK;
00685         }
00686 
00687 
00688 
00689 /* given a command name, find the structure in memory */
00690 command *find_command(char *command_name){
00691        command *temp_command;
00692 
00693        for(temp_command=command_list;temp_command!=NULL;temp_command=temp_command->next)
00694               if(!strcmp(command_name,temp_command->command_name))
00695                      return temp_command;
00696 
00697        return NULL;
00698         }
00699 
00700 
00701 
00702 /* wait for incoming connection requests */
00703 void wait_for_connections(void){
00704        struct sockaddr_in myname;
00705        struct sockaddr_in *nptr;
00706        struct sockaddr addr;
00707        int rc;
00708        int sock, new_sd;
00709        socklen_t addrlen;
00710        pid_t pid;
00711        int flag=1;
00712        fd_set fdread;
00713        struct timeval timeout;
00714        int retval;
00715 #ifdef HAVE_LIBWRAP
00716        struct request_info req;
00717 #endif
00718 
00719        /* create a socket for listening */
00720        sock=socket(AF_INET,SOCK_STREAM,0);
00721 
00722        /* exit if we couldn't create the socket */
00723        if(sock<0){
00724                syslog(LOG_ERR,"Network server socket failure (%d: %s)",errno,strerror(errno));
00725                exit(STATE_CRITICAL);
00726               }
00727 
00728        /* socket should be non-blocking */
00729        fcntl(sock,F_SETFL,O_NONBLOCK);
00730 
00731         /* set the reuse address flag so we don't get errors when restarting */
00732         flag=1;
00733         if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag))<0){
00734               syslog(LOG_ERR,"Could not set reuse address option on socket!\n");
00735               exit(STATE_UNKNOWN);
00736                }
00737 
00738        myname.sin_family=AF_INET;
00739        myname.sin_port=htons(server_port);
00740        bzero(&myname.sin_zero,8);
00741 
00742        /* what address should we bind to? */
00743         if(!strlen(server_address))
00744               myname.sin_addr.s_addr=INADDR_ANY;
00745 
00746        else if(!my_inet_aton(server_address,&myname.sin_addr)){
00747               syslog(LOG_ERR,"Server address is not a valid IP address\n");
00748               exit(STATE_CRITICAL);
00749                 }
00750 
00751        /* bind the address to the Internet socket */
00752        if(bind(sock,(struct sockaddr *)&myname,sizeof(myname))<0){
00753               syslog(LOG_ERR,"Network server bind failure (%d: %s)\n",errno,strerror(errno));
00754                exit(STATE_CRITICAL);
00755                }
00756 
00757        /* open the socket for listening */
00758        if(listen(sock,5)<0){
00759               syslog(LOG_ERR,"Network server listen failure (%d: %s)\n",errno,strerror(errno));
00760                exit(STATE_CRITICAL);
00761               }
00762 
00763        /* log warning about command arguments */
00764 #ifdef ENABLE_COMMAND_ARGUMENTS
00765        if(allow_arguments==TRUE)
00766               syslog(LOG_NOTICE,"Warning: Daemon is configured to accept command arguments from clients!");
00767 #endif
00768 
00769        syslog(LOG_INFO,"Listening for connections on port %d\n",htons(myname.sin_port));
00770 
00771        if(allowed_hosts)
00772               syslog(LOG_INFO,"Allowing connections from: %s\n",allowed_hosts);
00773 
00774        /* listen for connection requests - fork() if we get one */
00775        while(1){
00776 
00777               /* wait for a connection request */
00778                while(1){
00779 
00780                      /* wait until there's something to do */
00781                      FD_ZERO(&fdread);
00782                      FD_SET(sock,&fdread);
00783                      timeout.tv_sec=0;
00784                      timeout.tv_usec=500000;
00785                      retval=select(sock+1,&fdread,NULL,&fdread,&timeout);
00786 
00787                      /* bail out if necessary */
00788                      if(sigrestart==TRUE || sigshutdown==TRUE)
00789                             break;
00790 
00791                      /* error */
00792                      if(retval<0)
00793                             continue;
00794 
00795                      /* accept a new connection request */
00796                      new_sd=accept(sock,0,0);
00797 
00798                      /* some kind of error occurred... */
00799                      if(new_sd<0){
00800 
00801                             /* bail out if necessary */
00802                             if(sigrestart==TRUE || sigshutdown==TRUE)
00803                                    break;
00804 
00805                             /* retry */
00806                             if(errno==EWOULDBLOCK || errno==EINTR)
00807                                    continue;
00808 
00809                             /* socket is nonblocking and we don't have a connection yet */
00810                             if(errno==EAGAIN)
00811                                    continue;
00812 
00813                             /* fix for HP-UX 11.0 - just retry */
00814                             if(errno==ENOBUFS)
00815                                    continue;
00816 
00817                             /* else handle the error later */
00818                             break;
00819                              }
00820 
00821                      /* connection was good */
00822                      break;
00823                       }
00824 
00825               /* bail out if necessary */
00826               if(sigrestart==TRUE || sigshutdown==TRUE)
00827                      break;
00828 
00829               /* child process should handle the connection */
00830               pid=fork();
00831               if(pid==0){
00832 
00833                      /* fork again so we don't create zombies */
00834                      pid=fork();
00835                      if(pid==0){
00836 
00837                             /* hey, there was an error... */
00838                             if(new_sd<0){
00839 
00840                                    /* log error to syslog facility */
00841                                    syslog(LOG_ERR,"Network server accept failure (%d: %s)",errno,strerror(errno));
00842 
00843                                    /* close socket prioer to exiting */
00844                                    close(sock);
00845                      
00846                                    return;
00847                                     }
00848 
00849                             /* handle signals */
00850                             signal(SIGQUIT,child_sighandler);
00851                             signal(SIGTERM,child_sighandler);
00852                             signal(SIGHUP,child_sighandler);
00853 
00854                             /* grandchild does not need to listen for connections, so close the socket */
00855                             close(sock);  
00856 
00857                             /* find out who just connected... */
00858                             addrlen=sizeof(addr);
00859                             rc=getpeername(new_sd,&addr,&addrlen);
00860 
00861                             if(rc<0){
00862 
00863                                     /* log error to syslog facility */
00864                                    syslog(LOG_ERR,"Error: Network server getpeername() failure (%d: %s)",errno,strerror(errno));
00865 
00866                                     /* close socket prior to exiting */
00867                                    close(new_sd);
00868 
00869                                    return;
00870                                       }
00871 
00872                             nptr=(struct sockaddr_in *)&addr;
00873 
00874                             /* log info to syslog facility */
00875                             if(debug==TRUE)
00876                                    syslog(LOG_DEBUG,"Connection from %s port %d",inet_ntoa(nptr->sin_addr),nptr->sin_port);
00877 
00878                             /* is this is a blessed machine? */
00879                             if(allowed_hosts){
00880                      switch(nptr->sin_family) {
00881                      case AF_INET:
00882                             if(!is_an_allowed_host(nptr->sin_addr)) {
00883                                    /* log error to syslog facility */
00884                                    syslog(LOG_ERR,"Host %s is not allowed to talk to us!",inet_ntoa(nptr->sin_addr));
00885 
00886                                                         /* log info to syslog facility */
00887                                                         if ( debug==TRUE )
00888                                    syslog(LOG_DEBUG,"Connection from %s closed.",inet_ntoa(nptr->sin_addr));
00889 
00890                                                         /* close socket prior to exiting */
00891                                                         close(new_sd);
00892                                                         exit(STATE_OK);
00893                                                         } else {
00894                                                                /* log info to syslog facility */
00895                                                                if(debug==TRUE)
00896                                                                       syslog(LOG_DEBUG,"Host address is in allowed_hosts");
00897 
00898                                                         }
00899                                                  break;
00900                            
00901                                           case AF_INET6:
00902                                                  syslog(LOG_DEBUG,"Connection from %s closed. We don't support AF_INET6 addreess family in ACL\n", inet_ntoa(nptr->sin_addr));
00903                                                  break;
00904                                    }
00905                             }
00906 
00907 #ifdef HAVE_LIBWRAP
00908 
00909                             /* Check whether or not connections are allowed from this host */
00910                             request_init(&req,RQ_DAEMON,"nrpe",RQ_FILE,new_sd,0);
00911                             fromhost(&req);
00912 
00913                             if(!hosts_access(&req)){
00914 
00915                                    syslog(LOG_DEBUG,"Connection refused by TCP wrapper");
00916 
00917                                    /* refuse the connection */
00918                                    refuse(&req);
00919                                    close(new_sd);
00920 
00921                                    /* should not be reached */
00922                                    syslog(LOG_ERR,"libwrap refuse() returns!");
00923                                    exit(STATE_CRITICAL);
00924                                    }
00925 #endif
00926 
00927                             /* handle the client connection */
00928                             handle_connection(new_sd);
00929 
00930                             /* log info to syslog facility */
00931                             if(debug==TRUE)
00932                                    syslog(LOG_DEBUG,"Connection from %s closed.",inet_ntoa(nptr->sin_addr));
00933 
00934                             /* close socket prior to exiting */
00935                             close(new_sd);
00936 
00937                             exit(STATE_OK);
00938                              }
00939 
00940                      /* first child returns immediately, grandchild is inherited by INIT process -> no zombies... */
00941                      else
00942                             exit(STATE_OK);
00943                       }
00944               
00945               /* parent ... */
00946               else{
00947                      /* parent doesn't need the new connection */
00948                      close(new_sd);
00949 
00950                      /* parent waits for first child to exit */
00951                      waitpid(pid,NULL,0);
00952                       }
00953               }
00954 
00955        /* close the socket we're listening on */
00956        close(sock);
00957 
00958        return;
00959        }
00960 
00961 
00962 /* handles a client connection */
00963 void handle_connection(int sock){
00964         u_int32_t calculated_crc32;
00965        command *temp_command;
00966        packet receive_packet;
00967        packet send_packet;
00968        int bytes_to_send;
00969        int bytes_to_recv;
00970        char buffer[MAX_INPUT_BUFFER];
00971        char raw_command[MAX_INPUT_BUFFER];
00972        char processed_command[MAX_INPUT_BUFFER];
00973        int result=STATE_OK;
00974        int early_timeout=FALSE;
00975        int rc;
00976        int x;
00977 #ifdef DEBUG
00978        FILE *errfp;
00979 #endif
00980 #ifdef HAVE_SSL
00981        SSL *ssl=NULL;
00982 #endif
00983 
00984 
00985        /* log info to syslog facility */
00986        if(debug==TRUE)
00987               syslog(LOG_DEBUG,"Handling the connection...");
00988 
00989 #ifdef OLDSTUFF
00990        /* socket should be non-blocking */
00991        fcntl(sock,F_SETFL,O_NONBLOCK);
00992 #endif
00993 
00994        /* set connection handler */
00995        signal(SIGALRM,my_connection_sighandler);
00996        alarm(connection_timeout);
00997 
00998 #ifdef HAVE_SSL
00999        /* do SSL handshake */
01000        if(result==STATE_OK && use_ssl==TRUE){
01001               if((ssl=SSL_new(ctx))!=NULL){
01002                      SSL_set_fd(ssl,sock);
01003 
01004                      /* keep attempting the request if needed */
01005                         while(((rc=SSL_accept(ssl))!=1) && (SSL_get_error(ssl,rc)==SSL_ERROR_WANT_READ));
01006 
01007                      if(rc!=1){
01008                             syslog(LOG_ERR,"Error: Could not complete SSL handshake. %d\n",SSL_get_error(ssl,rc));
01009 #ifdef DEBUG
01010                             errfp=fopen("/tmp/err.log","w");
01011                             ERR_print_errors_fp(errfp);
01012                             fclose(errfp);
01013 #endif
01014                             return;
01015                              }
01016                       }
01017               else{
01018                      syslog(LOG_ERR,"Error: Could not create SSL connection structure.\n");
01019 #ifdef DEBUG
01020                      errfp=fopen("/tmp/err.log","w");
01021                      ERR_print_errors_fp(errfp);
01022                      fclose(errfp);
01023 #endif
01024                      return;
01025                       }
01026                }
01027 #endif
01028 
01029        bytes_to_recv=sizeof(receive_packet);
01030        if(use_ssl==FALSE)
01031               rc=recvall(sock,(char *)&receive_packet,&bytes_to_recv,socket_timeout);
01032 #ifdef HAVE_SSL
01033        else{
01034                 while(((rc=SSL_read(ssl,&receive_packet,bytes_to_recv))<=0) && (SSL_get_error(ssl,rc)==SSL_ERROR_WANT_READ));
01035               }
01036 #endif
01037 
01038        /* recv() error or client disconnect */
01039        if(rc<=0){
01040 
01041               /* log error to syslog facility */
01042               syslog(LOG_ERR,"Could not read request from client, bailing out...");
01043 
01044 #ifdef HAVE_SSL
01045               if(ssl){
01046                      SSL_shutdown(ssl);
01047                      SSL_free(ssl);
01048                      syslog(LOG_INFO,"INFO: SSL Socket Shutdown.\n");
01049                      }
01050 #endif
01051 
01052               return;
01053                 }
01054 
01055        /* we couldn't read the correct amount of data, so bail out */
01056        else if(bytes_to_recv!=sizeof(receive_packet)){
01057 
01058               /* log error to syslog facility */
01059               syslog(LOG_ERR,"Data packet from client was too short, bailing out...");
01060 
01061 #ifdef HAVE_SSL
01062               if(ssl){
01063                      SSL_shutdown(ssl);
01064                      SSL_free(ssl);
01065                      }
01066 #endif
01067 
01068               return;
01069                }
01070 
01071 #ifdef DEBUG
01072        fp=fopen("/tmp/packet","w");
01073        if(fp){
01074               fwrite(&receive_packet,1,sizeof(receive_packet),fp);
01075               fclose(fp);
01076                }
01077 #endif
01078 
01079        /* make sure the request is valid */
01080        if(validate_request(&receive_packet)==ERROR){
01081 
01082               /* log an error */
01083               syslog(LOG_ERR,"Client request was invalid, bailing out...");
01084 
01085               /* free memory */
01086               free(command_name);
01087               command_name=NULL;
01088               for(x=0;x<MAX_COMMAND_ARGUMENTS;x++){
01089                      free(macro_argv[x]);
01090                      macro_argv[x]=NULL;
01091                        }
01092 
01093 #ifdef HAVE_SSL
01094               if(ssl){
01095                      SSL_shutdown(ssl);
01096                      SSL_free(ssl);
01097                      }
01098 #endif
01099 
01100               return;
01101                }
01102 
01103        /* log info to syslog facility */
01104        if(debug==TRUE)
01105               syslog(LOG_DEBUG,"Host is asking for command '%s' to be run...",receive_packet.buffer);
01106 
01107        /* disable connection alarm - a new alarm will be setup during my_system */
01108        alarm(0);
01109 
01110        /* if this is the version check command, just spew it out */
01111        if(!strcmp(command_name,NRPE_HELLO_COMMAND)){
01112 
01113               snprintf(buffer,sizeof(buffer),"NRPE v%s",PROGRAM_VERSION);
01114               buffer[sizeof(buffer)-1]='\x0';
01115 
01116               /* log info to syslog facility */
01117               if(debug==TRUE)
01118                      syslog(LOG_DEBUG,"Response: %s",buffer);
01119 
01120               result=STATE_OK;
01121                }
01122 
01123        /* find the command we're supposed to run */
01124        else{
01125               temp_command=find_command(command_name);
01126               if(temp_command==NULL){
01127 
01128                      snprintf(buffer,sizeof(buffer),"NRPE: Command '%s' not defined",command_name);
01129                      buffer[sizeof(buffer)-1]='\x0';
01130 
01131                      /* log error to syslog facility */
01132                      if(debug==TRUE)
01133                             syslog(LOG_DEBUG,"%s",buffer);
01134 
01135                      result=STATE_CRITICAL;
01136                        }
01137 
01138               else{
01139 
01140                      /* process command line */
01141                      if(command_prefix==NULL)
01142                             strncpy(raw_command,temp_command->command_line,sizeof(raw_command)-1);
01143                      else
01144                             snprintf(raw_command,sizeof(raw_command)-1,"%s %s",command_prefix,temp_command->command_line);
01145                      raw_command[sizeof(raw_command)-1]='\x0';
01146                      process_macros(raw_command,processed_command,sizeof(processed_command));
01147 
01148                      /* log info to syslog facility */
01149                      if(debug==TRUE)
01150                             syslog(LOG_DEBUG,"Running command: %s",processed_command);
01151 
01152                      /* run the command */
01153                      strcpy(buffer,"");
01154                      result=my_system(processed_command,command_timeout,&early_timeout,buffer,sizeof(buffer));
01155 
01156                      /* log debug info */
01157                      if(debug==TRUE)
01158                             syslog(LOG_DEBUG,"Command completed with return code %d and output: %s",result,buffer);
01159 
01160                      /* see if the command timed out */
01161                      if(early_timeout==TRUE)
01162                             snprintf(buffer,sizeof(buffer)-1,"NRPE: Command timed out after %d seconds\n",command_timeout);
01163                      else if(!strcmp(buffer,""))
01164                             snprintf(buffer,sizeof(buffer)-1,"NRPE: Unable to read output\n");
01165 
01166                      buffer[sizeof(buffer)-1]='\x0';
01167 
01168                      /* check return code bounds */
01169                      if((result<0) || (result>3)){
01170 
01171                             /* log error to syslog facility */
01172                             syslog(LOG_ERR,"Bad return code for [%s]: %d", buffer,result);
01173 
01174                             result=STATE_UNKNOWN;
01175                              }
01176                       }
01177                }
01178 
01179        /* free memory */
01180        free(command_name);
01181        command_name=NULL;
01182        for(x=0;x<MAX_COMMAND_ARGUMENTS;x++){
01183               free(macro_argv[x]);
01184               macro_argv[x]=NULL;
01185                }
01186 
01187        /* strip newline character from end of output buffer */
01188        if(buffer[strlen(buffer)-1]=='\n')
01189               buffer[strlen(buffer)-1]='\x0';
01190 
01191        /* clear the response packet buffer */
01192        bzero(&send_packet,sizeof(send_packet));
01193 
01194        /* fill the packet with semi-random data */
01195        randomize_buffer((char *)&send_packet,sizeof(send_packet));
01196 
01197        /* initialize response packet data */
01198        send_packet.packet_version=(int16_t)htons(NRPE_PACKET_VERSION_2);
01199        send_packet.packet_type=(int16_t)htons(RESPONSE_PACKET);
01200        send_packet.result_code=(int16_t)htons(result);
01201        strncpy(&send_packet.buffer[0],buffer,MAX_PACKETBUFFER_LENGTH);
01202        send_packet.buffer[MAX_PACKETBUFFER_LENGTH-1]='\x0';
01203        
01204        /* calculate the crc 32 value of the packet */
01205        send_packet.crc32_value=(u_int32_t)0L;
01206        calculated_crc32=calculate_crc32((char *)&send_packet,sizeof(send_packet));
01207        send_packet.crc32_value=(u_int32_t)htonl(calculated_crc32);
01208 
01209 
01210        /***** ENCRYPT RESPONSE *****/
01211 
01212 
01213        /* send the response back to the client */
01214        bytes_to_send=sizeof(send_packet);
01215        if(use_ssl==FALSE)
01216               sendall(sock,(char *)&send_packet,&bytes_to_send);
01217 #ifdef HAVE_SSL
01218        else
01219               SSL_write(ssl,&send_packet,bytes_to_send);
01220 #endif
01221 
01222 #ifdef HAVE_SSL
01223        if(ssl){
01224               SSL_shutdown(ssl);
01225               SSL_free(ssl);
01226               }
01227 #endif
01228 
01229        /* log info to syslog facility */
01230        if(debug==TRUE)
01231               syslog(LOG_DEBUG,"Return Code: %d, Output: %s",result,buffer);
01232 
01233        return;
01234         }
01235 
01236 
01237 
01238 /* free all allocated memory */
01239 void free_memory(void){
01240        command *this_command;
01241        command *next_command;
01242        
01243        /* free memory for the command list */
01244        this_command=command_list;
01245        while(this_command!=NULL){
01246               next_command=this_command->next;
01247               if(this_command->command_name)
01248                      free(this_command->command_name);
01249               if(this_command->command_line)
01250                      free(this_command->command_line);
01251               free(this_command);
01252               this_command=next_command;
01253                }
01254 
01255        command_list=NULL;
01256 
01257        return;
01258         }
01259 
01260 
01261 
01262 /* executes a system command via popen(), but protects against timeouts */
01263 int my_system(char *command,int timeout,int *early_timeout,char *output,int output_length){
01264         pid_t pid;
01265        int status;
01266        int result;
01267        extern int errno;
01268        char buffer[MAX_INPUT_BUFFER];
01269        int fd[2];
01270        FILE *fp;
01271        int bytes_read=0;
01272        time_t start_time,end_time;
01273 
01274        /* initialize return variables */
01275        if(output!=NULL)
01276               strcpy(output,"");
01277        *early_timeout=FALSE;
01278 
01279        /* if no command was passed, return with no error */
01280        if(command==NULL)
01281                return STATE_OK;
01282 
01283        /* create a pipe */
01284        pipe(fd);
01285 
01286        /* make the pipe non-blocking */
01287        fcntl(fd[0],F_SETFL,O_NONBLOCK);
01288        fcntl(fd[1],F_SETFL,O_NONBLOCK);
01289 
01290        /* get the command start time */
01291        time(&start_time);
01292 
01293        /* fork */
01294        pid=fork();
01295 
01296        /* return an error if we couldn't fork */
01297        if(pid==-1){
01298 
01299               snprintf(buffer,sizeof(buffer)-1,"NRPE: Call to fork() failed\n");
01300               buffer[sizeof(buffer)-1]='\x0';
01301 
01302               if(output!=NULL){
01303                      strncpy(output,buffer,output_length-1);
01304                      output[output_length-1]='\x0';
01305                       }
01306 
01307               /* close both ends of the pipe */
01308               close(fd[0]);
01309               close(fd[1]);
01310               
01311                return STATE_UNKNOWN;  
01312                }
01313 
01314        /* execute the command in the child process */
01315         if(pid==0){
01316 
01317               /* close pipe for reading */
01318               close(fd[0]);
01319 
01320               /* become process group leader */
01321               setpgid(0,0);
01322 
01323               /* trap commands that timeout */
01324               signal(SIGALRM,my_system_sighandler);
01325               alarm(timeout);
01326 
01327               /* run the command */
01328               fp=popen(command,"r");
01329               
01330               /* report an error if we couldn't run the command */
01331               if(fp==NULL){
01332 
01333                      strncpy(buffer,"NRPE: Call to popen() failed\n",sizeof(buffer)-1);
01334                      buffer[sizeof(buffer)-1]='\x0';
01335 
01336                      /* write the error back to the parent process */
01337                      write(fd[1],buffer,strlen(buffer)+1);
01338 
01339                      result=STATE_CRITICAL;
01340                       }
01341               else{
01342 
01343                      /* read all lines of output - supports Nagios 3.x multiline output */
01344                      while((bytes_read=fread(buffer,1,sizeof(buffer)-1,fp))>0){
01345 
01346                             /* write the output back to the parent process */
01347                             write(fd[1],buffer,bytes_read);
01348                             }
01349 
01350                      /* close the command and get termination status */
01351                      status=pclose(fp);
01352 
01353                      /* report an error if we couldn't close the command */
01354                      if(status==-1)
01355                             result=STATE_CRITICAL;
01356                      /* report an error if child died due to signal (Klas Lindfors) */
01357                      else if(!WIFEXITED(status))
01358                             result=STATE_CRITICAL;
01359                      else
01360                             result=WEXITSTATUS(status);
01361                       }
01362 
01363               /* close pipe for writing */
01364               close(fd[1]);
01365 
01366               /* reset the alarm */
01367               alarm(0);
01368 
01369               /* return plugin exit code to parent process */
01370               exit(result);
01371                }
01372 
01373        /* parent waits for child to finish executing command */
01374        else{
01375               
01376               /* close pipe for writing */
01377               close(fd[1]);
01378 
01379               /* wait for child to exit */
01380               waitpid(pid,&status,0);
01381 
01382               /* get the end time for running the command */
01383               time(&end_time);
01384 
01385               /* get the exit code returned from the program */
01386               result=WEXITSTATUS(status);
01387 
01388               /* because of my idiotic idea of having UNKNOWN states be equivalent to -1, I must hack things a bit... */
01389               if(result==255)
01390                      result=STATE_UNKNOWN;
01391 
01392               /* check bounds on the return value */
01393               if(result<0 || result>3)
01394                      result=STATE_UNKNOWN;
01395 
01396               /* try and read the results from the command output (retry if we encountered a signal) */
01397               if(output!=NULL){
01398                      do{
01399                             bytes_read=read(fd[0], output, output_length-1);
01400                      }while (bytes_read==-1 && errno==EINTR);
01401 
01402                      if(bytes_read==-1)
01403                             *output='\0';
01404                      else
01405                             output[bytes_read]='\0';
01406                      }
01407 
01408               /* if there was a critical return code and no output AND the command time exceeded the timeout thresholds, assume a timeout */
01409               if(result==STATE_CRITICAL && bytes_read==-1 && (end_time-start_time)>=timeout){
01410                      *early_timeout=TRUE;
01411 
01412                      /* send termination signal to child process group */
01413                      kill((pid_t)(-pid),SIGTERM);
01414                      kill((pid_t)(-pid),SIGKILL);
01415                       }
01416 
01417               /* close the pipe for reading */
01418               close(fd[0]);
01419                }
01420 
01421 #ifdef DEBUG
01422        printf("my_system() end\n");
01423 #endif
01424 
01425        return result;
01426         }
01427 
01428 
01429 
01430 /* handle timeouts when executing commands via my_system() */
01431 void my_system_sighandler(int sig){
01432 
01433        /* force the child process to exit... */
01434        exit(STATE_CRITICAL);
01435         }
01436 
01437 
01438 /* handle errors where connection takes too long */
01439 void my_connection_sighandler(int sig) {
01440 
01441        syslog(LOG_ERR,"Connection has taken too long to establish. Exiting...");
01442 
01443        exit(STATE_CRITICAL);
01444        }
01445 
01446 
01447 /* drops privileges */
01448 int drop_privileges(char *user, char *group){
01449        uid_t uid=-1;
01450        gid_t gid=-1;
01451        struct group *grp;
01452        struct passwd *pw;
01453 
01454        /* set effective group ID */
01455        if(group!=NULL){
01456               
01457               /* see if this is a group name */
01458               if(strspn(group,"0123456789")<strlen(group)){
01459                      grp=(struct group *)getgrnam(group);
01460                      if(grp!=NULL)
01461                             gid=(gid_t)(grp->gr_gid);
01462                      else
01463                             syslog(LOG_ERR,"Warning: Could not get group entry for '%s'",group);
01464                      endgrent();
01465                       }
01466 
01467               /* else we were passed the GID */
01468               else
01469                      gid=(gid_t)atoi(group);
01470 
01471               /* set effective group ID if other than current EGID */
01472               if(gid!=getegid()){
01473 
01474                      if(setgid(gid)==-1)
01475                             syslog(LOG_ERR,"Warning: Could not set effective GID=%d",(int)gid);
01476                       }
01477                }
01478 
01479 
01480        /* set effective user ID */
01481        if(user!=NULL){
01482               
01483               /* see if this is a user name */
01484               if(strspn(user,"0123456789")<strlen(user)){
01485                      pw=(struct passwd *)getpwnam(user);
01486                      if(pw!=NULL)
01487                             uid=(uid_t)(pw->pw_uid);
01488                      else
01489                             syslog(LOG_ERR,"Warning: Could not get passwd entry for '%s'",user);
01490                      endpwent();
01491                       }
01492 
01493               /* else we were passed the UID */
01494               else
01495                      uid=(uid_t)atoi(user);
01496                      
01497               /* set effective user ID if other than current EUID */
01498               if(uid!=geteuid()){
01499 
01500 #ifdef HAVE_INITGROUPS
01501                      /* initialize supplementary groups */
01502                      if(initgroups(user,gid)==-1){
01503                             if(errno==EPERM)
01504                                    syslog(LOG_ERR,"Warning: Unable to change supplementary groups using initgroups()");
01505                             else{
01506                                    syslog(LOG_ERR,"Warning: Possibly root user failed dropping privileges with initgroups()");
01507                                    return ERROR;
01508                                      }
01509                                }
01510 #endif
01511 
01512                      if(setuid(uid)==-1)
01513                             syslog(LOG_ERR,"Warning: Could not set effective UID=%d",(int)uid);
01514                       }
01515                }
01516 
01517        return OK;
01518         }
01519 
01520 
01521 /* write an optional pid file */
01522 int write_pid_file(void){
01523        int fd;
01524        int result=0;
01525        pid_t pid=0;
01526        char pbuf[16];
01527 
01528        /* no pid file was specified */
01529        if(pid_file==NULL)
01530               return OK;
01531 
01532        /* read existing pid file */
01533        if((fd=open(pid_file,O_RDONLY))>=0){
01534 
01535               result=read(fd,pbuf,(sizeof pbuf)-1);
01536 
01537               close(fd);
01538 
01539               if(result>0){
01540 
01541                      pbuf[result]='\x0';
01542                      pid=(pid_t)atoi(pbuf);
01543 
01544                      /* if previous process is no longer running running, remove the old pid file */
01545                      if(pid && (pid==getpid() || kill(pid,0)<0))
01546                             unlink(pid_file);
01547 
01548                      /* previous process is still running */
01549                      else{
01550                             syslog(LOG_ERR,"There's already an NRPE server running (PID %lu).  Bailing out...",(unsigned long)pid);
01551                             return ERROR;
01552                              }
01553                       }
01554                } 
01555 
01556        /* write new pid file */
01557        if((fd=open(pid_file,O_WRONLY | O_CREAT,0644))>=0){
01558               sprintf(pbuf,"%d\n",(int)getpid());
01559               write(fd,pbuf,strlen(pbuf));
01560               close(fd);
01561               wrote_pid_file=TRUE;
01562                }
01563        else{
01564               syslog(LOG_ERR,"Cannot write to pidfile '%s' - check your privileges.",pid_file);
01565                }
01566 
01567        return OK;
01568         }
01569 
01570 
01571 
01572 /* remove pid file */
01573 int remove_pid_file(void){
01574 
01575        /* no pid file was specified */
01576        if(pid_file==NULL)
01577               return OK;
01578 
01579        /* pid file was not written */
01580        if(wrote_pid_file==FALSE)
01581               return OK;
01582 
01583        /* remove existing pid file */
01584        if(unlink(pid_file)==-1){
01585               syslog(LOG_ERR,"Cannot remove pidfile '%s' - check your privileges.",pid_file);
01586               return ERROR;
01587                }
01588 
01589        return OK;
01590         }
01591 
01592 
01593 
01594 /* bail if daemon is running as root */
01595 int check_privileges(void){
01596        uid_t uid=-1;
01597        gid_t gid=-1;
01598 
01599        uid=geteuid();
01600        gid=getegid();
01601 
01602        if(uid==0 || gid==0){
01603               syslog(LOG_ERR,"Error: NRPE daemon cannot be run as user/group root!");
01604               exit(STATE_CRITICAL);
01605                }
01606 
01607        return OK;
01608         }
01609 
01610 
01611 
01612 /* handle signals (parent process) */
01613 void sighandler(int sig){
01614        static char *sigs[]={"EXIT","HUP","INT","QUIT","ILL","TRAP","ABRT","BUS","FPE","KILL","USR1","SEGV","USR2","PIPE","ALRM","TERM","STKFLT","CHLD","CONT","STOP","TSTP","TTIN","TTOU","URG","XCPU","XFSZ","VTALRM","PROF","WINCH","IO","PWR","UNUSED","ZERR","DEBUG",(char *)NULL};
01615        int i;
01616 
01617        if(sig<0)
01618               sig=-sig;
01619 
01620        for(i=0;sigs[i]!=(char *)NULL;i++);
01621 
01622        sig%=i;
01623 
01624        /* we received a SIGHUP, so restart... */
01625        if(sig==SIGHUP){
01626 
01627               sigrestart=TRUE;
01628 
01629               syslog(LOG_NOTICE,"Caught SIGHUP - restarting...\n");
01630                }
01631 
01632        /* else begin shutting down... */
01633        if(sig==SIGTERM){
01634 
01635               /* if shutdown is already true, we're in a signal trap loop! */
01636               if(sigshutdown==TRUE)
01637                      exit(STATE_CRITICAL);
01638 
01639               sigshutdown=TRUE;
01640 
01641               syslog(LOG_NOTICE,"Caught SIG%s - shutting down...\n",sigs[sig]);
01642                }
01643 
01644        return;
01645         }
01646 
01647 
01648 
01649 /* handle signals (child processes) */
01650 void child_sighandler(int sig){
01651 
01652        /* free all memory we allocated */
01653        free_memory();
01654 
01655        /* terminate */
01656        exit(0);
01657        
01658        /* so the compiler doesn't complain... */
01659        return;
01660         }
01661 
01662 
01663 
01664 /* tests whether or not a client request is valid */
01665 int validate_request(packet *pkt){
01666         u_int32_t packet_crc32;
01667         u_int32_t calculated_crc32;
01668        char *ptr;
01669 #ifdef ENABLE_COMMAND_ARGUMENTS
01670        int x;
01671 #endif
01672 
01673 
01674        /***** DECRYPT REQUEST ******/
01675 
01676 
01677         /* check the crc 32 value */
01678         packet_crc32=ntohl(pkt->crc32_value);
01679         pkt->crc32_value=0L;
01680         calculated_crc32=calculate_crc32((char *)pkt,sizeof(packet));
01681         if(packet_crc32!=calculated_crc32){
01682                 syslog(LOG_ERR,"Error: Request packet had invalid CRC32.");
01683                 return ERROR;
01684                 }
01685 
01686        /* make sure this is the right type of packet */
01687        if(ntohs(pkt->packet_type)!=QUERY_PACKET || ntohs(pkt->packet_version)!=NRPE_PACKET_VERSION_2){
01688               syslog(LOG_ERR,"Error: Request packet type/version was invalid!");
01689               return ERROR;
01690                }
01691 
01692        /* make sure buffer is terminated */
01693        pkt->buffer[MAX_PACKETBUFFER_LENGTH-1]='\x0';
01694 
01695        /* client must send some kind of request */
01696        if(!strcmp(pkt->buffer,"")){
01697               syslog(LOG_ERR,"Error: Request contained no query!");
01698               return ERROR;
01699                }
01700 
01701        /* make sure request doesn't contain nasties */
01702        if(contains_nasty_metachars(pkt->buffer)==TRUE){
01703               syslog(LOG_ERR,"Error: Request contained illegal metachars!");
01704               return ERROR;
01705                }
01706 
01707        /* make sure the request doesn't contain arguments */
01708        if(strchr(pkt->buffer,'!')){
01709 #ifdef ENABLE_COMMAND_ARGUMENTS
01710               if(allow_arguments==FALSE){
01711                      syslog(LOG_ERR,"Error: Request contained command arguments, but argument option is not enabled!");
01712                      return ERROR;
01713                        }
01714 #else
01715               syslog(LOG_ERR,"Error: Request contained command arguments!");
01716               return ERROR;
01717 #endif
01718                }
01719 
01720        /* get command name */
01721 #ifdef ENABLE_COMMAND_ARGUMENTS
01722        ptr=strtok(pkt->buffer,"!");
01723 #else
01724        ptr=pkt->buffer;
01725 #endif 
01726        command_name=strdup(ptr);
01727        if(command_name==NULL){
01728               syslog(LOG_ERR,"Error: Memory allocation failed");
01729               return ERROR;
01730                }
01731 
01732 #ifdef ENABLE_COMMAND_ARGUMENTS
01733        /* get command arguments */
01734        if(allow_arguments==TRUE){
01735 
01736               for(x=0;x<MAX_COMMAND_ARGUMENTS;x++){
01737                      ptr=strtok(NULL,"!");
01738                      if(ptr==NULL)
01739                             break;
01740                      macro_argv[x]=strdup(ptr);
01741                      if(macro_argv[x]==NULL){
01742                             syslog(LOG_ERR,"Error: Memory allocation failed");
01743                             return ERROR;
01744                              }
01745                      if(!strcmp(macro_argv[x],"")){
01746                             syslog(LOG_ERR,"Error: Request contained an empty command argument");
01747                             return ERROR;
01748                               }
01749                       }
01750                }
01751 #endif
01752 
01753        return OK;
01754         }
01755 
01756 
01757 
01758 /* tests whether a buffer contains illegal metachars */
01759 int contains_nasty_metachars(char *str){
01760        int result;
01761 
01762        if(str==NULL)
01763               return FALSE;
01764        
01765        result=strcspn(str,NASTY_METACHARS);
01766        if(result!=strlen(str))
01767               return TRUE;
01768 
01769        return FALSE;
01770         }
01771 
01772 
01773 
01774 /* replace macros in buffer */
01775 int process_macros(char *input_buffer,char *output_buffer,int buffer_length){
01776        char *temp_buffer;
01777        int in_macro;
01778        int arg_index=0;
01779        char *selected_macro=NULL;
01780 
01781        strcpy(output_buffer,"");
01782 
01783        in_macro=FALSE;
01784 
01785        for(temp_buffer=my_strsep(&input_buffer,"$");temp_buffer!=NULL;temp_buffer=my_strsep(&input_buffer,"$")){
01786 
01787               selected_macro=NULL;
01788 
01789               if(in_macro==FALSE){
01790                      if(strlen(output_buffer)+strlen(temp_buffer)<buffer_length-1){
01791                             strncat(output_buffer,temp_buffer,buffer_length-strlen(output_buffer)-1);
01792                             output_buffer[buffer_length-1]='\x0';
01793                              }
01794                      in_macro=TRUE;
01795                      }
01796               else{
01797 
01798                      if(strlen(output_buffer)+strlen(temp_buffer)<buffer_length-1){
01799 
01800                             /* argument macro */
01801                             if(strstr(temp_buffer,"ARG")==temp_buffer){
01802                                    arg_index=atoi(temp_buffer+3);
01803                                    if(arg_index>=1 && arg_index<=MAX_COMMAND_ARGUMENTS)
01804                                           selected_macro=macro_argv[arg_index-1];
01805                                     }
01806 
01807                             /* an escaped $ is done by specifying two $$ next to each other */
01808                             else if(!strcmp(temp_buffer,"")){
01809                                    strncat(output_buffer,"$",buffer_length-strlen(output_buffer)-1);
01810                                     }
01811                             
01812                             /* a non-macro, just some user-defined string between two $s */
01813                             else{
01814                                    strncat(output_buffer,"$",buffer_length-strlen(output_buffer)-1);
01815                                    output_buffer[buffer_length-1]='\x0';
01816                                    strncat(output_buffer,temp_buffer,buffer_length-strlen(output_buffer)-1);
01817                                    output_buffer[buffer_length-1]='\x0';
01818                                    strncat(output_buffer,"$",buffer_length-strlen(output_buffer)-1);
01819                                     }
01820 
01821                             
01822                             /* insert macro */
01823                             if(selected_macro!=NULL)
01824                                    strncat(output_buffer,(selected_macro==NULL)?"":selected_macro,buffer_length-strlen(output_buffer)-1);
01825 
01826                             output_buffer[buffer_length-1]='\x0';
01827                             }
01828 
01829                      in_macro=FALSE;
01830                      }
01831               }
01832 
01833        return OK;
01834        }
01835 
01836 
01837 
01838 /* process command line arguments */
01839 int process_arguments(int argc, char **argv){
01840        char optchars[MAX_INPUT_BUFFER];
01841        int c=1;
01842        int have_mode=FALSE;
01843 
01844 #ifdef HAVE_GETOPT_LONG
01845        int option_index=0;
01846        static struct option long_options[]={
01847               {"config", required_argument, 0, 'c'},
01848               {"inetd", no_argument, 0, 'i'},
01849               {"daemon", no_argument, 0, 'd'},
01850               {"no-ssl", no_argument, 0, 'n'},
01851               {"help", no_argument, 0, 'h'},
01852               {"license", no_argument, 0, 'l'},
01853               {0, 0, 0, 0}
01854                 };
01855 #endif
01856 
01857        /* no options were supplied */
01858        if(argc<2)
01859               return ERROR;
01860 
01861        snprintf(optchars,MAX_INPUT_BUFFER,"c:hVldin");
01862 
01863        while(1){
01864 #ifdef HAVE_GETOPT_LONG
01865               c=getopt_long(argc,argv,optchars,long_options,&option_index);
01866 #else
01867               c=getopt(argc,argv,optchars);
01868 #endif
01869               if(c==-1 || c==EOF)
01870                      break;
01871 
01872               /* process all arguments */
01873               switch(c){
01874 
01875               case '?':
01876               case 'h':
01877                      show_help=TRUE;
01878                      break;
01879               case 'V':
01880                      show_version=TRUE;
01881                      break;
01882               case 'l':
01883                      show_license=TRUE;
01884                      break;
01885               case 'c':
01886                      strncpy(config_file,optarg,sizeof(config_file));
01887                      config_file[sizeof(config_file)-1]='\x0';
01888                      break;
01889               case 'd':
01890                      use_inetd=FALSE;
01891                      have_mode=TRUE;
01892                      break;
01893               case 'i':
01894                      use_inetd=TRUE;
01895                      have_mode=TRUE;
01896                      break;
01897               case 'n':
01898                      use_ssl=FALSE;
01899                      break;
01900               default:
01901                      return ERROR;
01902                      break;
01903                       }
01904                }
01905 
01906        /* bail if we didn't get required args */
01907        if(have_mode==FALSE)
01908               return ERROR;
01909 
01910        return OK;
01911         }
01912