diff --git a/Makefile.in b/Makefile.in index 60b7d13..bc2d68f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -74,10 +74,10 @@ all: echo "" nrpe: - cd $(SRC_BASE); $(MAKE) + cd $(SRC_BASE); $(MAKE) $@ check_nrpe: - cd $(SRC_BASE); $(MAKE) + cd $(SRC_BASE); $(MAKE) $@ install-plugin: cd $(SRC_BASE); $(MAKE) $@ diff --git a/include/acl.h b/include/acl.h index 9a661f6..7d2174d 100644 --- a/include/acl.h +++ b/include/acl.h @@ -56,10 +56,6 @@ struct dns_acl { struct dns_acl *next; }; -/* Pointers to head ACL structs */ -static struct ip_acl *ip_acl_head, *ip_acl_prev; -static struct dns_acl *dns_acl_head, *dns_acl_prev; - /* Functions */ void parse_allowed_hosts(char *allowed_hosts); int add_ipv4_to_acl(char *ipv4); @@ -67,7 +63,7 @@ int add_ipv6_to_acl(char *ipv6); int add_domain_to_acl(char *domain); //int is_an_allowed_host(struct in_addr); int is_an_allowed_host(int, void *); -unsigned int prefix_from_mask(struct in_addr mask); +unsigned int prefix_from_mask(int family, const void* mask); void show_acl_lists(void); #endif /* ACL_H_INCLUDED */ diff --git a/include/common.h.in b/include/common.h.in index fe6740c..6797261 100644 --- a/include/common.h.in +++ b/include/common.h.in @@ -33,7 +33,9 @@ # ifdef SSL_TYPE_openssl # include <@SSL_INC_PREFIX@err.h> # include <@SSL_INC_PREFIX@rand.h> +#if OPENSSL_VERSION_NUMBER < 0x30000000 # include <@SSL_INC_PREFIX@engine.h> +#endif # include <@SSL_INC_PREFIX@evp.h> # endif #endif diff --git a/include/nrpe-ssl.h b/include/nrpe-ssl.h index ddfa8eb..0c57b18 100644 --- a/include/nrpe-ssl.h +++ b/include/nrpe-ssl.h @@ -34,7 +34,6 @@ extern const SSL_METHOD *meth; # endif extern SSL_CTX *ctx; extern SslParms sslprm; -#endif extern int use_ssl; @@ -45,3 +44,4 @@ void ssl_log_startup(int server); int ssl_load_certificates(void); int ssl_set_ciphers(void); int ssl_verify_callback_common(int preverify_ok, X509_STORE_CTX * ctx, int is_invalid); +#endif diff --git a/include/utils.h b/include/utils.h index e81f8d1..350dff0 100644 --- a/include/utils.h +++ b/include/utils.h @@ -45,9 +45,9 @@ char* strip(char*); int sendall(int, char*, int*); int recvall(int, char*, int*, int); char *my_strsep(char**, const char*); -void open_log_file(); +void open_log_file(void); void logit(int priority, const char *format, ...); -void close_log_file(); +void close_log_file(void); void display_license(void); extern int disable_syslog; diff --git a/src/acl.c b/src/acl.c index 3c8b3f4..248b056 100644 --- a/src/acl.c +++ b/src/acl.c @@ -53,6 +53,10 @@ #include +/* Pointers to head ACL structs */ +static struct ip_acl *ip_acl_head, *ip_acl_prev; +static struct dns_acl *dns_acl_head, *dns_acl_prev; + extern int debug; /* This function checks if a char argument from valid char range. @@ -237,7 +241,7 @@ int add_ipv4_to_acl(char *ipv4) { /* Convert ip and mask to unsigned long */ ip = htonl((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]); - mask = htonl(-1 << (32 - data[4])); + mask = htonl(~0u << (32 - data[4])); /* Wrong network address */ if ( (ip & mask) != ip) { @@ -496,7 +500,7 @@ int add_domain_to_acl(char *domain) { int is_an_allowed_host(int family, void *host) { - struct ip_acl *ip_acl_curr = ip_acl_head; + struct ip_acl *ip_acl_curr; int nbytes; int x; struct dns_acl *dns_acl_curr = dns_acl_head; @@ -505,43 +509,44 @@ int is_an_allowed_host(int family, void *host) struct addrinfo *res, *ai; struct in_addr tmp; - while (ip_acl_curr != NULL) { - if(ip_acl_curr->family == family) { - switch(ip_acl_curr->family) { + for (ip_acl_curr = ip_acl_head; ip_acl_curr != NULL; ip_acl_curr = ip_acl_curr->next) { + if (ip_acl_curr->family != family) + continue; + + switch (ip_acl_curr->family) { case AF_INET: if (debug == TRUE) { - tmp.s_addr = ((struct in_addr*)host)->s_addr; - logit(LOG_INFO, "is_an_allowed_host (AF_INET): is host >%s< " - "an allowed host >%s<\n", - inet_ntoa(tmp), inet_ntoa(ip_acl_curr->addr)); + char host_addr[INET_ADDRSTRLEN]; + char acl_addr[INET_ADDRSTRLEN]; + logit(LOG_INFO, "is_an_allowed_host (AF_INET): is host >%s< an allowed host >%s<\n", + inet_ntop(AF_INET, host, host_addr, INET_ADDRSTRLEN), + inet_ntop(AF_INET, &ip_acl_curr->addr, acl_addr, INET_ADDRSTRLEN)); } - if((((struct in_addr *)host)->s_addr & + if ((((struct in_addr *)host)->s_addr & ip_acl_curr->mask.s_addr) == ip_acl_curr->addr.s_addr) { if (debug == TRUE) logit(LOG_INFO, "is_an_allowed_host (AF_INET): host is in allowed host list!"); return 1; - } + } break; case AF_INET6: nbytes = sizeof(ip_acl_curr->mask6.s6_addr) / sizeof(ip_acl_curr->mask6.s6_addr[0]); - for(x = 0; x < nbytes; x++) { - if((((struct in6_addr *)host)->s6_addr[x] & + for (x = 0; x < nbytes; x++) { + if ((((struct in6_addr *)host)->s6_addr[x] & ip_acl_curr->mask6.s6_addr[x]) != ip_acl_curr->addr6.s6_addr[x]) { break; - } } - if(x == nbytes) { + } + if (x == nbytes) { /* All bytes in host's address pass the netmask mask */ return 1; - } - break; } - } - ip_acl_curr = ip_acl_curr->next; - } + break; + } + } while(dns_acl_curr != NULL) { if (!getaddrinfo(dns_acl_curr->domain, NULL, NULL, &res)) { @@ -576,7 +581,6 @@ int is_an_allowed_host(int family, void *host) "for allowed host >%s<\n", formattedStr, dns_acl_curr->domain); } - struct in6_addr *resolved = &(((struct sockaddr_in6 *) (ai->ai_addr))->sin6_addr); memcpy((char *) &addr6, ai->ai_addr, sizeof(addr6)); if (!memcmp(&addr6.sin6_addr, host, sizeof(addr6.sin6_addr))) { if (debug == TRUE) @@ -613,6 +617,38 @@ void trim( char *src, char *dest) { return; } +/* + * Free all existing ACLs + */ + +static void clear_allowed_hosts(void) { + int count; + + count = 0; + while (ip_acl_head) { + struct ip_acl *next = ip_acl_head->next; + free(ip_acl_head); + ip_acl_head = next; + count++; + } + ip_acl_prev = NULL; + + if (debug == TRUE) + logit(LOG_INFO, "clear_allowed_hosts: Cleared %i IP ACLs\n", count); + + count = 0; + while (dns_acl_head) { + struct dns_acl *next = dns_acl_head->next; + free(dns_acl_head); + dns_acl_head = next; + count++; + } + dns_acl_prev = NULL; + + if (debug == TRUE) + logit(LOG_INFO, "clear_allowed_hosts: Cleared %i DNS ACLs\n", count); +} + /* This function splits allowed_hosts to substrings with comma(,) as a delimiter. * It doesn't check validness of ACL record (add_ipv4_to_acl() and add_domain_to_acl() do), * just trims spaces from ACL records. @@ -627,6 +663,8 @@ void parse_allowed_hosts(char *allowed_hosts) { char *trimmed_tok; int add_to_acl = 0; + clear_allowed_hosts(); + if (debug == TRUE) logit(LOG_INFO, "parse_allowed_hosts: parsing the allowed host string >%s< to add to ACL list\n", @@ -684,18 +722,26 @@ void parse_allowed_hosts(char *allowed_hosts) { * Converts mask in unsigned long format to two digit prefix */ -unsigned int prefix_from_mask(struct in_addr mask) { - int prefix = 0; - unsigned long bit = 1; - int i; +unsigned int prefix_from_mask(int family, const void* mask) { + int prefix = 0; + int bytes = 4; + int i; + const unsigned char *ptr = mask; - for (i = 0; i < 32; i++) { - if (mask.s_addr & bit) - prefix++; + if (family == AF_INET6) + bytes = 16; - bit = bit << 1; - } - return (prefix); + for (i = 0; i < bytes; i++) { + int j; + + for (j = 0; j < 8; j++) { + unsigned char bit = 1 << j; + + if (ptr[i] & bit) + prefix++; + } + } + return (prefix); } /* @@ -710,8 +756,15 @@ void show_acl_lists(void) logit(LOG_INFO, "Showing ACL lists for both IP and DOMAIN acl's:\n" ); while (ip_acl_curr != NULL) { - logit(LOG_INFO, " IP ACL: %s/%u %u\n", inet_ntoa(ip_acl_curr->addr), - prefix_from_mask(ip_acl_curr->mask), ip_acl_curr->addr.s_addr); + if (ip_acl_curr->family == AF_INET) { + logit(LOG_INFO, " IP ACL: %s/%u %u\n", inet_ntoa(ip_acl_curr->addr), + prefix_from_mask(AF_INET, &ip_acl_curr->mask), ip_acl_curr->addr.s_addr); + } else if (ip_acl_curr->family == AF_INET6) { + char formattedStr[INET6_ADDRSTRLEN]; + logit(LOG_INFO, " IP ACL: %s/%u\n", + inet_ntop(AF_INET6, &ip_acl_curr->addr6, formattedStr, INET6_ADDRSTRLEN), + prefix_from_mask(AF_INET6, &ip_acl_curr->mask6)); + } ip_acl_curr = ip_acl_curr->next; } diff --git a/src/check_nrpe.c b/src/check_nrpe.c index 8b53f60..6a88298 100644 --- a/src/check_nrpe.c +++ b/src/check_nrpe.c @@ -91,11 +91,11 @@ int translate_state (char *state_text); void set_timeout_state (char *state); int parse_timeout_string (char *timeout_str); void usage(int result); -void setup_ssl(); -void set_sig_handlers(); -int connect_to_remote(); -int send_request(); -int read_response(); +void setup_ssl(void); +void set_sig_handlers(void); +int connect_to_remote(void); +int send_request(void); +int read_response(void); int read_packet(int sock, void *ssl_ptr, v2_packet ** v2_pkt, v3_packet ** v3_pkt); #ifdef HAVE_SSL static int verify_callback(int ok, X509_STORE_CTX * ctx); @@ -331,7 +331,9 @@ int process_arguments(int argc, char **argv, int from_config_file) break; case 'n': +#ifdef HAVE_SSL use_ssl = FALSE; +#endif break; case 'u': @@ -554,7 +556,7 @@ int read_config_file(char *fname) logit(LOG_ERR, "Error: read_config_file fail to allocate memory"); return ERROR; } - if ((sz = fread(buf, 1, st.st_size, f)) != st.st_size) { + if ((sz = fread(buf, 1, st.st_size, f)) != (size_t)st.st_size) { fclose(f); free(buf); logit(LOG_ERR, "Error: Failed to completely read config file %s", fname); @@ -768,7 +770,7 @@ void usage(int result) exit(STATE_UNKNOWN); } -void setup_ssl() +void setup_ssl(void) { #ifdef HAVE_SSL int vrfy; @@ -836,7 +838,7 @@ void setup_ssl() #endif } -void set_sig_handlers() +void set_sig_handlers(void) { #ifdef HAVE_SIGACTION struct sigaction sig_action; @@ -856,7 +858,7 @@ void set_sig_handlers() alarm(socket_timeout); } -int connect_to_remote() +int connect_to_remote(void) { #ifdef HAVE_SSL int rc, ssl_err, ern, x, nerrs = 0; @@ -958,7 +960,7 @@ int connect_to_remote() } if ((sslprm.log_opts & SSL_LogIfClientCert) || (sslprm.log_opts & SSL_LogCertDetails)) { - char peer_cn[256], buffer[2048]; + char buffer[2048]; X509 *peer = SSL_get_peer_certificate(ssl); if (peer) { @@ -988,12 +990,13 @@ int connect_to_remote() return result; } -int send_request() +int send_request(void) { v2_packet *v2_send_packet = NULL; v3_packet *v3_send_packet = NULL; u_int32_t calculated_crc32; - int rc, bytes_to_send, pkt_size; + int rc, bytes_to_send; + size_t pkt_size; char *send_pkt; if (packet_ver == NRPE_PACKET_VERSION_2) { @@ -1080,7 +1083,7 @@ int send_request() return STATE_OK; } -int read_response() +int read_response(void) { v2_packet *v2_receive_packet = NULL; /* Note: v4 packets will use the v3_packet structure */ @@ -1229,10 +1232,13 @@ int read_packet(int sock, void *ssl_ptr, v2_packet ** v2_pkt, v3_packet ** v3_pk int rc; char *buff_ptr; + (void)ssl_ptr; /* Read only the part that's common between versions 2 & 3 */ common_size = tot_bytes = bytes_to_recv = (char *)packet.buffer - (char *)&packet; +#ifdef HAVE_SSL if (use_ssl == FALSE) { +#endif rc = recvall(sock, (char *)&packet, &tot_bytes, socket_timeout); if (rc <= 0 || rc != bytes_to_recv) { @@ -1318,8 +1324,8 @@ int read_packet(int sock, void *ssl_ptr, v2_packet ** v2_pkt, v3_packet ** v3_pk return -1; } else tot_bytes += rc; - } #ifdef HAVE_SSL + } else { SSL *ssl = (SSL *) ssl_ptr; @@ -1450,6 +1456,8 @@ void alarm_handler(int sig) const char *text = state_text(timeout_return_code); size_t lth1 = 0, lth2 = 0; + (void)sig; + for (lth1 = 0; lth1 < 10; ++lth1) if (text[lth1] == 0) break; diff --git a/src/nrpe-ssl.c b/src/nrpe-ssl.c index 21f1180..4080b30 100644 --- a/src/nrpe-ssl.c +++ b/src/nrpe-ssl.c @@ -34,7 +34,7 @@ void ssl_initialize(void) void ssl_set_protocol_version(SslVer ssl_proto_ver, unsigned long *ssl_opts) { #if OPENSSL_VERSION_NUMBER >= 0x10100000 - + (void)ssl_opts; SSL_CTX_set_max_proto_version(ctx, 0); switch(ssl_proto_ver) { @@ -223,7 +223,7 @@ int ssl_load_certificates(void) int ssl_set_ciphers(void) { - int x; + size_t x; int changed = FALSE; char errstr[256] = { "" }; diff --git a/src/nrpe.c b/src/nrpe.c index aa2aa6b..568accb 100644 --- a/src/nrpe.c +++ b/src/nrpe.c @@ -71,7 +71,6 @@ int rfc931_timeout=15; #define how_many(x,y) (((x)+((y)-1))/(y)) -extern int errno; struct addrinfo *listen_addrs = NULL; int listen_socks[MAX_LISTEN_SOCKS]; char remote_host[MAX_HOST_ADDRESS_LENGTH]; @@ -135,7 +134,6 @@ int main(int argc, char **argv) { int result = OK; int x; - uint32_t y; char buffer[MAX_INPUT_BUFFER]; init(); @@ -211,10 +209,10 @@ int init(void) int result = OK; /* set some environment variables */ - asprintf(&env_string, "NRPE_MULTILINESUPPORT=1"); - putenv(env_string); - asprintf(&env_string, "NRPE_PROGRAMVERSION=%s", PROGRAM_VERSION); - putenv(env_string); + if (asprintf(&env_string, "NRPE_MULTILINESUPPORT=1") > 0) + putenv(env_string); + if (asprintf(&env_string, "NRPE_PROGRAMVERSION=%s", PROGRAM_VERSION) > 0) + putenv(env_string); /* open a connection to the syslog facility */ /* facility name may be overridden later */ @@ -232,8 +230,8 @@ void init_ssl(void) #ifdef HAVE_SSL char seedfile[FILENAME_MAX]; char errstr[256] = { "" }; - int i, c, x, vrfy; - unsigned long ssl_opts = SSL_OP_ALL | SSL_OP_SINGLE_DH_USE; + int i, x, vrfy; + unsigned long c, ssl_opts = SSL_OP_ALL | SSL_OP_SINGLE_DH_USE; if (use_ssl == FALSE) { if (debug == TRUE) @@ -574,7 +572,7 @@ char* process_metachars(const char* input) char* copy = strdup(input); int i,j; int length = strlen(input); - for (i = 0, j = 0; i < length, j < length; i++, j++) { + for (i = 0, j = 0; j < length; i++, j++) { if (copy[j] != '\\') { copy[i] = copy[j]; continue; @@ -625,7 +623,6 @@ char* process_metachars(const char* input) /* read in the configuration file */ int read_config_file(char *filename) { - struct stat st; FILE *fp; char config_file[MAX_FILENAME_LENGTH]; char input_buffer[MAX_INPUT_BUFFER]; @@ -719,14 +716,16 @@ int read_config_file(char *filename) return ERROR; } - } else if (!strcmp(varname, "command_prefix")) + } else if (!strcmp(varname, "command_prefix")) { + free(command_prefix); command_prefix = strdup(varvalue); - else if (!strcmp(varname, "server_address")) { + } else if (!strcmp(varname, "server_address")) { strncpy(server_address, varvalue, sizeof(server_address) - 1); server_address[sizeof(server_address) - 1] = '\0'; } else if (!strcmp(varname, "allowed_hosts")) { + free(allowed_hosts); allowed_hosts = strdup(varvalue); parse_allowed_hosts(allowed_hosts); if (debug == TRUE) @@ -749,13 +748,15 @@ int read_config_file(char *filename) else debug = FALSE; - } else if (!strcmp(varname, "nrpe_user")) + } else if (!strcmp(varname, "nrpe_user")) { + free(nrpe_user); nrpe_user = strdup(varvalue); - else if (!strcmp(varname, "nrpe_group")) + } else if (!strcmp(varname, "nrpe_group")) { + free(nrpe_group); nrpe_group = strdup(varvalue); - else if (!strcmp(varname, "dont_blame_nrpe")) + } else if (!strcmp(varname, "dont_blame_nrpe")) allow_arguments = (atoi(varvalue) == 1) ? TRUE : FALSE; else if (!strcmp(varname, "disable_syslog")) @@ -793,10 +794,11 @@ int read_config_file(char *filename) } else if (!strcmp(varname, "allow_weak_random_seed")) allow_weak_random_seed = (atoi(varvalue) == 1) ? TRUE : FALSE; - else if (!strcmp(varname, "pid_file")) + else if (!strcmp(varname, "pid_file")) { + free(pid_file); pid_file = strdup(varvalue); - else if (!strcmp(varname, "listen_queue_size")) { + } else if (!strcmp(varname, "listen_queue_size")) { listen_queue_size = atoi(varvalue); if (listen_queue_size == 0) { logit(LOG_ERR, @@ -854,16 +856,19 @@ int read_config_file(char *filename) strncpy(sslprm.cipher_list, varvalue, sizeof(sslprm.cipher_list) - 1); sslprm.cipher_list[sizeof(sslprm.cipher_list) - 1] = '\0'; - } else if (!strcmp(varname, "ssl_cert_file")) + } else if (!strcmp(varname, "ssl_cert_file")) { + free(sslprm.cert_file); sslprm.cert_file = strdup(varvalue); - else if (!strcmp(varname, "ssl_cacert_file")) + } else if (!strcmp(varname, "ssl_cacert_file")) { + free(sslprm.cacert_file); sslprm.cacert_file = strdup(varvalue); - else if (!strcmp(varname, "ssl_privatekey_file")) + } else if (!strcmp(varname, "ssl_privatekey_file")) { + free(sslprm.privatekey_file); sslprm.privatekey_file = strdup(varvalue); - else if (!strcmp(varname, "ssl_client_certs")) { + } else if (!strcmp(varname, "ssl_client_certs")) { sslprm.client_certs = atoi(varvalue); if ((int)sslprm.client_certs < 0 || sslprm.client_certs > Require_Cert) { logit(LOG_ERR, @@ -885,13 +890,15 @@ int read_config_file(char *filename) "Invalid log_facility specified in config file '%s' - Line %d\n", filename, line); - } else if (!strcmp(varname, "keep_env_vars")) + } else if (!strcmp(varname, "keep_env_vars")) { + free(keep_env_vars); keep_env_vars = strdup(varvalue); - else if (!strcmp(varname, "nasty_metachars")) + } else if (!strcmp(varname, "nasty_metachars")) nasty_metachars = process_metachars(varvalue); else if (!strcmp(varname, "log_file")) { + free(log_file); log_file = strdup(varvalue); open_log_file(); @@ -920,6 +927,7 @@ int read_config_dir(char *dirname) struct stat buf; char config_file[MAX_FILENAME_LENGTH]; int result = OK; + int rc; #ifdef HAVE_SCANDIR /* read and sort the directory contents */ @@ -945,7 +953,11 @@ int read_config_dir(char *dirname) /* process all files in the directory... */ /* create the full path to the config file or subdirectory */ - snprintf(config_file, sizeof(config_file) - 1, "%s/%s", dirname, dirfile->d_name); + rc = snprintf(config_file, sizeof(config_file) - 1, "%s/%s", dirname, dirfile->d_name); + if (rc >= (long)sizeof(config_file) - 1) { + logit(LOG_ERR, "Config file path too long '%s/%s'.\n", dirname, dirfile->d_name); + return ERROR; + } config_file[sizeof(config_file) - 1] = '\x0'; stat(config_file, &buf); @@ -1170,11 +1182,13 @@ void wait_for_connections(void) socklen_t fromlen; fd_set *fdset = NULL; int maxfd = 0, new_sd = 0, i, rc, retval; - + int count_fdset = 0; setup_wait_conn(); /* listen for connection requests - fork() if we get one */ while (1) { + int need_fdset; + /* bail out if necessary */ if (sigrestart == TRUE || sigshutdown == TRUE) break; @@ -1184,9 +1198,14 @@ void wait_for_connections(void) maxfd = listen_socks[i]; } - if (fdset != NULL) + need_fdset = how_many(maxfd + 1, NFDBITS); + if (need_fdset > count_fdset) { free(fdset); - fdset = (fd_set *) calloc(how_many(maxfd + 1, NFDBITS), sizeof(fd_mask)); + fdset = (fd_set *) calloc(need_fdset, sizeof(fd_mask)); + count_fdset = need_fdset; + } else { + memset(fdset, 0, count_fdset * sizeof(fd_mask)); + } for (i = 0; i < num_listen_socks; i++) FD_SET(listen_socks[i], fdset); @@ -1253,6 +1272,7 @@ void wait_for_connections(void) close_listen_socks(); freeaddrinfo(listen_addrs); listen_addrs = NULL; + free(fdset); return; } @@ -1268,10 +1288,15 @@ void setup_wait_conn(void) for (ai = listen_addrs; ai; ai = ai->ai_next) { if (debug == TRUE) { + char *fam = ""; inet_ntop (ai->ai_family, ai->ai_addr->sa_data, addrstr, 100); ptr = &((struct sockaddr_in *) ai->ai_addr)->sin_addr; inet_ntop (ai->ai_family, ptr, addrstr, 100); - logit(LOG_INFO, "SETUP_WAIT_CONN FOR: IPv4 address: %s (%s)\n", addrstr, ai->ai_canonname); + if (ai->ai_family == AF_INET) + fam = "AF_INET"; + else if (ai->ai_family == AF_INET6) + fam = "AF_INET6"; + logit(LOG_INFO, "SETUP_WAIT_CONN FOR: %s address: %s (%s)\n", fam, addrstr, ai->ai_canonname); } create_listener(ai); } @@ -1662,12 +1687,17 @@ void handle_connection(int sock) /* see if the command timed out */ if (early_timeout == TRUE) { free(send_buff); - asprintf(&send_buff, "NRPE: Command timed out after %d seconds\n", - command_timeout); + if (asprintf(&send_buff, "NRPE: Command timed out after %d seconds\n", command_timeout) == -1) { + logit(LOG_ERR, "Unable to build response, possible memory issue, bailing out..."); + return; + } result = STATE_UNKNOWN; } else if (!strcmp(send_buff, "")) { free(send_buff); - asprintf(&send_buff, "NRPE: Unable to read output\n"); + if (asprintf(&send_buff, "NRPE: Unable to read output\n") == -1) { + logit(LOG_ERR, "Unable to build response, possible memory issue, bailing out..."); + return; + } result = STATE_UNKNOWN; } @@ -1739,12 +1769,12 @@ void handle_connection(int sock) /* send the response back to the client */ bytes_to_send = pkt_size; - if (use_ssl == FALSE) - sendall(sock, send_pkt, &bytes_to_send); #ifdef HAVE_SSL - else + if (use_ssl) SSL_write(ssl, send_pkt, bytes_to_send); + else #endif + sendall(sock, send_pkt, &bytes_to_send); #ifdef HAVE_SSL if (ssl) { @@ -1788,9 +1818,9 @@ void init_handle_conn(void) alarm(connection_timeout); } +#ifdef HAVE_SSL int handle_conn_ssl(int sock, void *ssl_ptr) { -#ifdef HAVE_SSL # if (defined(__sun) && defined(SOLARIS_10)) || defined(_AIX) || defined(__hpux) SSL_CIPHER *c; #else @@ -1893,10 +1923,10 @@ int handle_conn_ssl(int sock, void *ssl_ptr) logit(LOG_NOTICE, "SSL Client %s did not present a certificate", remote_host); } -#endif return OK; } +#endif int read_packet(int sock, void *ssl_ptr, v2_packet * v2_pkt, v3_packet ** v3_pkt) { @@ -1904,10 +1934,13 @@ int read_packet(int sock, void *ssl_ptr, v2_packet * v2_pkt, v3_packet ** v3_pkt int rc; char *buff_ptr; + (void)ssl_ptr; /* Read only the part that's common between versions 2 & 3 */ common_size = tot_bytes = bytes_to_recv = (char *)&v2_pkt->buffer - (char *)v2_pkt; +#ifdef HAVE_SSL if (use_ssl == FALSE) { +#endif rc = recvall(sock, (char *)v2_pkt, &tot_bytes, socket_timeout); if (rc <= 0 || rc != bytes_to_recv) @@ -1967,8 +2000,8 @@ int read_packet(int sock, void *ssl_ptr, v2_packet * v2_pkt, v3_packet ** v3_pkt return -1; } else tot_bytes += rc; - } #ifdef HAVE_SSL + } else { SSL *ssl = (SSL *) ssl_ptr; int sockfd, retval; @@ -2092,21 +2125,16 @@ void free_memory(void) return; } +static int my_system_parent(pid_t pid, int fd, int timeout, time_t start_time, int *early_timeout, char **output); +static int my_system_child(const char *command, int timeout, int fd); + /* executes a system command via popen(), but protects against timeouts */ int my_system(char *command, int timeout, int *early_timeout, char **output) { - FILE *fp; pid_t pid; - time_t start_time, end_time; - int status; + time_t start_time; int result; - char buffer[MAX_INPUT_BUFFER]; int fd[2]; - int bytes_read = 0, tot_bytes = 0; - int output_size; -#ifdef HAVE_SIGACTION - struct sigaction sig_action; -#endif *early_timeout = FALSE; /* initialize return variables */ @@ -2137,16 +2165,8 @@ int my_system(char *command, int timeout, int *early_timeout, char **output) /* return an error if we couldn't fork */ if (pid == -1) { - snprintf(buffer, sizeof(buffer) - 1, "NRPE: Call to fork() failed\n"); - buffer[sizeof(buffer) - 1] = '\x0'; - - if (packet_ver == NRPE_PACKET_VERSION_2) { - int output_size = sizeof(v2_packet); - *output = calloc(1, output_size); - strncpy(*output, buffer, output_size - 1); - *output[output_size - 1] = '\0'; - } else - *output = strdup(buffer); + if (asprintf(output, "NRPE: Call to fork() failed (errno=%i)\n", errno) == -1) + logit(LOG_ERR, "Unable to build output, possible memory issue, bailing out..."); /* close both ends of the pipe */ close(fd[0]); @@ -2166,134 +2186,244 @@ int my_system(char *command, int timeout, int *early_timeout, char **output) close(fd[0]); /* close pipe for reading */ setpgid(0, 0); /* become process group leader */ - /* trap commands that timeout */ -#ifdef HAVE_SIGACTION - sig_action.sa_sigaction = NULL; - sig_action.sa_handler = my_system_sighandler; - sigfillset(&sig_action.sa_mask); - sig_action.sa_flags = SA_NODEFER | SA_RESTART; - sigaction(SIGALRM, &sig_action, NULL); -#else - signal(SIGALRM, my_system_sighandler); -#endif /* HAVE_SIGACTION */ - alarm(timeout); + result = my_system_child(command, timeout, fd[1]); + exit(result); /* return plugin exit code to parent process */ + } else { + /* parent waits for child to finish executing command */ - fp = popen(command, "r"); /* run the command */ + close(fd[1]); /* close pipe for writing */ - /* report an error if we couldn't run the command */ - if (fp == NULL) { - strncpy(buffer, "NRPE: Call to popen() failed\n", sizeof(buffer) - 1); - buffer[sizeof(buffer) - 1] = '\x0'; + result = my_system_parent(pid, fd[0], timeout, start_time, early_timeout, output); + } - /* write the error back to the parent process */ - if (write(fd[1], buffer, strlen(buffer) + 1) == -1) - logit(LOG_ERR, "ERROR: my_system() write(fd, buffer)-1 failed..."); +#ifdef DEBUG + printf("my_system() end\n"); +#endif - result = STATE_CRITICAL; + return result; +} - } else { +int my_system_parent(pid_t pid, int fd, int timeout, time_t start_time, int *early_timeout, char **output) +{ + time_t end_time; + int status; + int result; + int output_size = 1024 * 64; /* Maximum buffer is 64K */ + int bytes_read = 0; + int do_wait = 1; - /* read all lines of output - supports Nagios 3.x multiline output */ - while ((bytes_read = fread(buffer, 1, sizeof(buffer) - 1, fp)) > 0) { - /* write the output back to the parent process */ - if (write(fd[1], buffer, bytes_read) == -1) - logit(LOG_ERR, "ERROR: my_system() write(fd, buffer)-2 failed..."); + commands_running++; + + if (packet_ver == NRPE_PACKET_VERSION_2) { + output_size = MAX_PACKETBUFFER_LENGTH; + } + *output = calloc(1, output_size); + + while (1) { + int rc; + fd_set rfds; + struct timeval tv; + + if (do_wait) { + /* Check for child exit */ + rc = waitpid(pid, &status, WNOHANG); + if (rc == pid || rc == -1) { + time(&end_time); /* get the end time for running the command */ + do_wait = 0; } + } - if (write(fd[1], "\0", 1) == -1) - logit(LOG_ERR, "ERROR: my_system() write(fd, NULL) failed..."); + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + tv.tv_sec = 1; + tv.tv_usec = 0; - status = pclose(fp); /* close the command and get termination status */ + rc = select(fd + 1, &rfds, 0, 0, &tv); + if (rc == -1) + break; - /* report an error if we couldn't close the command */ - if (status == -1) - result = STATE_CRITICAL; - else if (!WIFEXITED(status)) - /* report an error if child died due to signal (Klas Lindfors) */ - result = STATE_CRITICAL; - else - result = WEXITSTATUS(status); + if (rc == 0) { + /* if child process has already exited and there is nothing to read, don't wait for grandkids */ + if (!do_wait) + break; + continue; } - close(fd[1]); /* close pipe for writing */ - alarm(0); /* reset the alarm */ - exit(result); /* return plugin exit code to parent process */ + if (FD_ISSET(fd, &rfds)) { + /* try and read the results from the command output (retry if we encountered a signal) */ + rc = read(fd, *output + bytes_read, output_size - bytes_read); + if (rc == -1) { + if (errno == EINTR) + continue; + break; + } else if (rc == 0) { + break; + } - } else { - /* parent waits for child to finish executing command */ + bytes_read += rc; + if (bytes_read == output_size) + break; + } + } - commands_running++; + /* Ensure output buffer termination */ + (*output)[output_size - 1] = '\0'; + /* close the pipe for reading */ + close(fd); - close(fd[1]); /* close pipe for writing */ - waitpid(pid, &status, 0); /* wait for child to exit */ - time(&end_time); /* get the end time for running the command */ - result = WEXITSTATUS(status); /* get the exit code returned from the program */ + if (do_wait) { + /* Child hasn't exited yet*/ + waitpid(pid, &status, 0); + time(&end_time); /* get the end time for running the command */ + } + result = WEXITSTATUS(status); /* get the exit code returned from the program */ - /* because of my idiotic idea of having UNKNOWN states be equivalent to -1, I must hack things a bit... */ - if (result == 255) - result = STATE_UNKNOWN; + /* check bounds on the return value */ + if (result < 0 || result > 3) + result = STATE_UNKNOWN; - /* check bounds on the return value */ - if (result < 0 || result > 3) - result = STATE_UNKNOWN; + /* if there was a critical return code and no output AND the + * command time exceeded the timeout thresholds, assume a timeout */ + if (result == STATE_CRITICAL && bytes_read == 0 && (end_time - start_time) >= timeout) { + *early_timeout = TRUE; - if (packet_ver == NRPE_PACKET_VERSION_2) { - output_size = sizeof(v2_packet); - *output = calloc(1, output_size); - } else { - output_size = 1024 * 64; /* Maximum buffer is 64K */ - *output = calloc(1, output_size); - } + /* send termination signal to child process group */ + kill((pid_t) (-pid), SIGTERM); + kill((pid_t) (-pid), SIGKILL); + } - /* try and read the results from the command output (retry if we encountered a signal) */ - for (;;) { - bytes_read = read(fd[0], buffer, sizeof(buffer) - 1); - if (bytes_read == 0) + commands_running--; + return result; +} + +int my_system_child(const char *command, int timeout, int fd) +{ + FILE *fp; + int status; + int result; + char buffer[MAX_INPUT_BUFFER]; +#ifdef HAVE_SIGACTION + struct sigaction sig_action; +#endif + + /* trap commands that timeout */ +#ifdef HAVE_SIGACTION + sig_action.sa_sigaction = NULL; + sig_action.sa_handler = my_system_sighandler; + sigfillset(&sig_action.sa_mask); + sig_action.sa_flags = SA_NODEFER | SA_RESTART; + sigaction(SIGALRM, &sig_action, NULL); +#else + signal(SIGALRM, my_system_sighandler); +#endif /* HAVE_SIGACTION */ + alarm(timeout); + + fp = popen(command, "r"); /* run the command */ + + /* report an error if we couldn't run the command */ + if (fp == NULL) { + strncpy(buffer, "NRPE: Call to popen() failed\n", sizeof(buffer) - 1); + buffer[sizeof(buffer) - 1] = '\x0'; + + /* write the error back to the parent process */ + if (write(fd, buffer, strlen(buffer) + 1) == -1) + logit(LOG_ERR, "ERROR: my_system() write(fd, buffer)-1 failed..."); + + result = STATE_CRITICAL; + + } else { + int do_read = 1; + int bytes_read = 0; + + /* read all lines of output - supports Nagios 3.x multiline output */ + while (do_read || bytes_read) { + int rc; + int max_fd = 0; + fd_set rfds; + fd_set wfds; + struct timeval tv; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + if (do_read && bytes_read < (long)sizeof(buffer)) { + FD_SET(fileno(fp), &rfds); + max_fd = fileno(fp); + } + if (bytes_read) { + FD_SET(fd, &wfds); + max_fd = fd > max_fd ? fd : max_fd; + } + tv.tv_sec = 5; + tv.tv_usec = 0; + + rc = select(max_fd + 1, &rfds, &wfds, 0, &tv); + if (rc == -1) { + logit(LOG_ERR, "ERROR: my_system_child() select failed (errno=%i)", errno); break; - if (bytes_read == -1) { - if (errno == EINTR) - continue; - else - break; } - if (tot_bytes < output_size) /* If buffer is full, discard the rest */ - strncat(*output, buffer, output_size - tot_bytes - 1); - tot_bytes += bytes_read; - } - (*output)[output_size - 1] = '\0'; + if (rc == 0) + continue; + + if (FD_ISSET(fileno(fp), &rfds)) { + rc = fread(buffer + bytes_read, 1, sizeof(buffer) - bytes_read, fp); + if (rc <= 0) { + /* error or eof reached */ + do_read = 0; - /* if there was a critical return code and no output AND the - * command time exceeded the timeout thresholds, assume a timeout */ - if (result == STATE_CRITICAL && bytes_read == -1 && (end_time - start_time) >= timeout) { - *early_timeout = TRUE; + /* Add terminating NUL to send */ + buffer[bytes_read] = '\0'; + bytes_read++; + } else { + bytes_read += rc; + } + } - /* send termination signal to child process group */ - kill((pid_t) (-pid), SIGTERM); - kill((pid_t) (-pid), SIGKILL); + if (bytes_read) { + /* We always try to write if we have anything... we'll just get EAGAIN if still full */ + rc = write(fd, buffer, bytes_read); + if (rc == -1) { + if (errno != EAGAIN) { + logit(LOG_ERR, "ERROR: my_system_child() write(fd, buffer) failed (errno=%i)", errno); + break; + } + } else if (rc > 0) { + memmove(buffer, buffer + rc, bytes_read - rc); + bytes_read -= rc; + } + } } - close(fd[0]); /* close the pipe for reading */ + status = pclose(fp); /* close the command and get termination status */ - commands_running--; + /* report an error if we couldn't close the command */ + if (status == -1) + result = STATE_CRITICAL; + else if (!WIFEXITED(status)) + /* report an error if child died due to signal (Klas Lindfors) */ + result = STATE_CRITICAL; + else + result = WEXITSTATUS(status); } -#ifdef DEBUG - printf("my_system() end\n"); -#endif - + close(fd); /* close pipe for writing */ + alarm(0); /* reset the alarm */ return result; } /* handle timeouts when executing commands via my_system() */ void my_system_sighandler(int sig) { + (void)sig; + /* try to kill any child processes in our group */ + kill(0, SIGTERM); exit(STATE_CRITICAL); /* force the child process to exit... */ } /* handle errors where connection takes too long */ void my_connection_sighandler(int sig) { + (void)sig; logit(LOG_ERR, "Connection has taken too long to establish. Exiting..."); exit(STATE_CRITICAL); } @@ -2449,6 +2579,7 @@ int remove_pid_file(void) void my_disconnect_sighandler(int sig) { + (void)sig; logit(LOG_ERR, "SSL_shutdown() has taken too long to complete. Exiting now.."); exit(STATE_CRITICAL); } @@ -2534,6 +2665,7 @@ void sighandler(int sig) /* handle signals (child processes) */ void child_sighandler(int sig) { + (void)sig; exit(0); /* terminate */ } @@ -2672,7 +2804,7 @@ int contains_nasty_metachars(char *str) return FALSE; result = strcspn(str, nasty_metachars); - if (result != strlen(str)) + if (result != (long)strlen(str)) return TRUE; return FALSE; @@ -2696,7 +2828,7 @@ int process_macros(char *input_buffer, char *output_buffer, int buffer_length) selected_macro = NULL; if (in_macro == FALSE) { - if (strlen(output_buffer) + strlen(temp_buffer) < buffer_length - 1) { + if (strlen(output_buffer) + strlen(temp_buffer) < (size_t)buffer_length - 1) { strncat(output_buffer, temp_buffer, buffer_length - strlen(output_buffer) - 1); output_buffer[buffer_length - 1] = '\x0'; } @@ -2704,7 +2836,7 @@ int process_macros(char *input_buffer, char *output_buffer, int buffer_length) } else { - if (strlen(output_buffer) + strlen(temp_buffer) < buffer_length - 1) { + if (strlen(output_buffer) + strlen(temp_buffer) < (size_t)buffer_length - 1) { /* argument macro */ if (strstr(temp_buffer, "ARG") == temp_buffer) { @@ -2823,7 +2955,9 @@ int process_arguments(int argc, char **argv) break; case 'n': +#ifdef HAVE_SSL use_ssl = FALSE; +#endif break; case 's': /* Argument s to indicate SRC option */ diff --git a/src/utils.c b/src/utils.c index 2fe7271..9f2bc6f 100644 --- a/src/utils.c +++ b/src/utils.c @@ -28,8 +28,12 @@ * ****************************************************************************/ -#include "../include/common.h" -#include "../include/utils.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "common.h" +#include "utils.h" #include #ifdef HAVE_PATHS_H #include @@ -264,16 +268,16 @@ int clean_environ(const char *keep_env_vars, const char *nrpe_user) #else static char *path = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"; #endif - struct passwd *pw; + struct passwd *pw = NULL; size_t len, var_sz = 0; - char **kept = NULL, *value, *var, *keep = NULL; + char **kept = NULL, *value, *var, *keep = NULL, *tmp; int i, j, keepcnt = 0; if (keep_env_vars && *keep_env_vars) - asprintf(&keep, "%s,NRPE_MULTILINESUPPORT,NRPE_PROGRAMVERSION", keep_env_vars); + i = asprintf(&keep, "%s,NRPE_MULTILINESUPPORT,NRPE_PROGRAMVERSION", keep_env_vars); else - asprintf(&keep, "NRPE_MULTILINESUPPORT,NRPE_PROGRAMVERSION"); - if (keep == NULL) { + i = asprintf(&keep, "NRPE_MULTILINESUPPORT,NRPE_PROGRAMVERSION"); + if (i == -1 || keep == NULL) { logit(LOG_ERR, "Could not sanitize the environment. Aborting!"); return ERROR; } @@ -289,7 +293,8 @@ int clean_environ(const char *keep_env_vars, const char *nrpe_user) logit(LOG_ERR, "Could not sanitize the environment. Aborting!"); return ERROR; } - for (i = 0, var = my_strsep(&keep, ","); var != NULL; var = my_strsep(&keep, ",")) + tmp = keep; /* use temp variable as strsep will update it */ + for (i = 0, var = my_strsep(&tmp, ","); var != NULL; var = my_strsep(&tmp, ",")) kept[i++] = strip(var); var = NULL; @@ -485,7 +490,7 @@ char *my_strsep(char **stringp, const char *delim) return begin; } -void open_log_file() +void open_log_file(void) { int fh; int flags = O_RDWR|O_APPEND|O_CREAT; @@ -557,7 +562,7 @@ void logit(int priority, const char *format, ...) va_end(ap); } -void close_log_file() +void close_log_file(void) { if(!log_fp) return;