Sfoglia il codice sorgente

Fixes (#275)

* Fix various warnings when building with -Wall.

* Split my_system() into seperate functions for child & parent.

* Rewrite my_system_*() IO loops to handle large buffers.

In my_system_parent(), don't wait for child to exit before reading buffer.
The child may fill the pipe buffer and start erroring, so do both operations
concurrently.

In my_system_child(), similarly check that data/space is available before
we read/write to handle asymetric data rates.

* Fix debug display of acl IP addresses

inet_ntoa uses a static buffer which causes duplicate IP addresses to be printed.

* Show correct address family when setting up listen addresses.

* Only make requested binary

* Fix a few pedantic warnings, mostly prototype related.

* Fix use of uninitialized variable.

Spotted via valgrind.

* Clear any existing ACLs before parsing new config.

Multiple allowed_hosts config lines or every SIGHUP would cause duplicates.
Free previous allowed_hosts to prevent leaking.
Fix dumping of ACLs to properly display AF_INET6 addresses.

* Fix various leaks detected by valgrind.

Mostly config options that strdup() their value, causing leaks if
there are multiple config lines or after restarting with SIGHUP.

The last was the fd_set for incoming connections wouldn't be freed
on restart. Also optimize the allocation of fdset and re-use if possible.

* Fix minor leak in clean_environ()

my_strpos() modifies the string pointer causing us to free
an incorrect pointer.

* asprintf fixes

* Don't include engine.h if not needed.

OpenSSL engines have been deprecated since version 3.0 and at
least Fedora 41 is no longer including the header by default.

* Misc minor cleanups.

Fix various [-Wunused-parameter], [-Wsign-compare], [-Wshift-negative-value], etc.
warnings that exist with recent compilers.

---------

Co-authored-by: Emmett Kapsner <ekapsner@nagios.com>
Doug Nazar 4 giorni fa
parent
commit
813ca0d2a5
9 ha cambiato i file con 411 aggiunte e 215 eliminazioni
  1. 2 2
      Makefile.in
  2. 1 5
      include/acl.h
  3. 1 1
      include/nrpe-ssl.h
  4. 2 2
      include/utils.h
  5. 86 33
      src/acl.c
  6. 22 14
      src/check_nrpe.c
  7. 2 2
      src/nrpe-ssl.c
  8. 280 146
      src/nrpe.c
  9. 15 10
      src/utils.c

+ 2 - 2
Makefile.in

@@ -74,10 +74,10 @@ all:
 	echo ""
 	echo ""
 
 
 nrpe:
 nrpe:
-	cd $(SRC_BASE); $(MAKE)
+	cd $(SRC_BASE); $(MAKE) $@
 
 
 check_nrpe:
 check_nrpe:
-	cd $(SRC_BASE); $(MAKE)
+	cd $(SRC_BASE); $(MAKE) $@
 
 
 install-plugin:
 install-plugin:
 	cd $(SRC_BASE); $(MAKE) $@
 	cd $(SRC_BASE); $(MAKE) $@

+ 1 - 5
include/acl.h

@@ -56,10 +56,6 @@ struct dns_acl {
         struct dns_acl *next;
         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 */
 /* Functions */
 void parse_allowed_hosts(char *allowed_hosts);
 void parse_allowed_hosts(char *allowed_hosts);
 int add_ipv4_to_acl(char *ipv4);
 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 add_domain_to_acl(char *domain);
 //int is_an_allowed_host(struct in_addr);
 //int is_an_allowed_host(struct in_addr);
 int is_an_allowed_host(int, void *);
 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);
 void show_acl_lists(void);
 
 
 #endif /* ACL_H_INCLUDED */
 #endif /* ACL_H_INCLUDED */

+ 1 - 1
include/nrpe-ssl.h

@@ -34,7 +34,6 @@ extern const SSL_METHOD *meth;
 # endif
 # endif
 extern SSL_CTX  *ctx;
 extern SSL_CTX  *ctx;
 extern SslParms sslprm;
 extern SslParms sslprm;
-#endif
 
 
 extern int       use_ssl;
 extern int       use_ssl;
 
 
@@ -45,3 +44,4 @@ void ssl_log_startup(int server);
 int ssl_load_certificates(void);
 int ssl_load_certificates(void);
 int ssl_set_ciphers(void);
 int ssl_set_ciphers(void);
 int ssl_verify_callback_common(int preverify_ok, X509_STORE_CTX * ctx, int is_invalid);
 int ssl_verify_callback_common(int preverify_ok, X509_STORE_CTX * ctx, int is_invalid);
+#endif

+ 2 - 2
include/utils.h

@@ -45,9 +45,9 @@ char* strip(char*);
 int sendall(int, char*, int*);
 int sendall(int, char*, int*);
 int recvall(int, char*, int*, int);
 int recvall(int, char*, int*, int);
 char *my_strsep(char**, const char*);
 char *my_strsep(char**, const char*);
-void open_log_file();
+void open_log_file(void);
 void logit(int priority, const char *format, ...);
 void logit(int priority, const char *format, ...);
-void close_log_file();
+void close_log_file(void);
 void display_license(void);
 void display_license(void);
 extern int disable_syslog;
 extern int disable_syslog;
 
 

+ 86 - 33
src/acl.c

@@ -53,6 +53,10 @@
 #include <stdarg.h>
 #include <stdarg.h>
 
 
 
 
+/* 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;
 extern int debug;
 
 
 /* This function checks if a char argument from valid char range.
 /* 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 */
         /* Convert ip and mask to unsigned long */
         ip = htonl((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]);
         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 */
         /* Wrong network address */
         if ( (ip & mask) != ip) {
         if ( (ip & mask) != ip) {
@@ -496,7 +500,7 @@ int add_domain_to_acl(char *domain) {
 
 
 int is_an_allowed_host(int family, void *host)
 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					nbytes;
 	int					x;
 	int					x;
 	struct dns_acl		*dns_acl_curr = dns_acl_head;
 	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 addrinfo		*res, *ai;
 	struct in_addr		tmp;
 	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:
 			case AF_INET:
 				if (debug == TRUE) {
 				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->mask.s_addr) == 
 						ip_acl_curr->addr.s_addr) {
 						ip_acl_curr->addr.s_addr) {
 					if (debug == TRUE)
 					if (debug == TRUE)
 						logit(LOG_INFO, "is_an_allowed_host (AF_INET): host is in allowed host list!");
 						logit(LOG_INFO, "is_an_allowed_host (AF_INET): host is in allowed host list!");
 					return 1;
 					return 1;
-					}
+				}
 				break;
 				break;
 			case AF_INET6:
 			case AF_INET6:
 				nbytes = sizeof(ip_acl_curr->mask6.s6_addr) / 
 				nbytes = sizeof(ip_acl_curr->mask6.s6_addr) / 
 						sizeof(ip_acl_curr->mask6.s6_addr[0]);
 						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->mask6.s6_addr[x]) != 
 							ip_acl_curr->addr6.s6_addr[x]) {
 							ip_acl_curr->addr6.s6_addr[x]) {
 						break;
 						break;
-						}
 					}
 					}
-				if(x == nbytes) { 
+				}
+				if (x == nbytes) {
 					/* All bytes in host's address pass the netmask mask */
 					/* All bytes in host's address pass the netmask mask */
 					return 1;
 					return 1;
-					}
-				break;
 				}
 				}
-			}
-		ip_acl_curr = ip_acl_curr->next;
-        }
+				break;
+		}
+	}
 
 
 	while(dns_acl_curr != NULL) {
 	while(dns_acl_curr != NULL) {
 		if (!getaddrinfo(dns_acl_curr->domain, NULL, NULL, &res)) {
 		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",
 											  "for allowed host >%s<\n",
 									  formattedStr, dns_acl_curr->domain);
 									  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));
 							memcpy((char *) &addr6, ai->ai_addr, sizeof(addr6));
 							if (!memcmp(&addr6.sin6_addr, host, sizeof(addr6.sin6_addr))) {
 							if (!memcmp(&addr6.sin6_addr, host, sizeof(addr6.sin6_addr))) {
 								if (debug == TRUE)
 								if (debug == TRUE)
@@ -613,6 +617,38 @@ void trim( char *src, char *dest) {
 	return;
 	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.
 /* 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),
  * It doesn't check validness of ACL record (add_ipv4_to_acl() and add_domain_to_acl() do),
  * just trims spaces from ACL records.
  * just trims spaces from ACL records.
@@ -627,6 +663,8 @@ void parse_allowed_hosts(char *allowed_hosts) {
 	char *trimmed_tok;
 	char *trimmed_tok;
     int add_to_acl = 0;
     int add_to_acl = 0;
 
 
+	clear_allowed_hosts();
+
 	if (debug == TRUE)
 	if (debug == TRUE)
 		logit(LOG_INFO,
 		logit(LOG_INFO,
 			 "parse_allowed_hosts: parsing the allowed host string >%s< to add to ACL list\n",
 			 "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
  * 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" );
 	logit(LOG_INFO, "Showing ACL lists for both IP and DOMAIN acl's:\n" );
 
 
 	while (ip_acl_curr != NULL) {
 	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;
 		ip_acl_curr = ip_acl_curr->next;
 	}
 	}
 
 

+ 22 - 14
src/check_nrpe.c

@@ -91,11 +91,11 @@ int translate_state (char *state_text);
 void set_timeout_state (char *state);
 void set_timeout_state (char *state);
 int parse_timeout_string (char *timeout_str);
 int parse_timeout_string (char *timeout_str);
 void usage(int result);
 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);
 int read_packet(int sock, void *ssl_ptr, v2_packet ** v2_pkt, v3_packet ** v3_pkt);
 #ifdef HAVE_SSL
 #ifdef HAVE_SSL
 static int verify_callback(int ok, X509_STORE_CTX * ctx);
 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;
 			break;
 
 
 		case 'n':
 		case 'n':
+#ifdef HAVE_SSL
 			use_ssl = FALSE;
 			use_ssl = FALSE;
+#endif
 			break;
 			break;
 
 
 		case 'u':
 		case 'u':
@@ -554,7 +556,7 @@ int read_config_file(char *fname)
 		logit(LOG_ERR, "Error: read_config_file fail to allocate memory");
 		logit(LOG_ERR, "Error: read_config_file fail to allocate memory");
 		return ERROR;
 		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);
 		fclose(f);
 		free(buf);
 		free(buf);
 		logit(LOG_ERR, "Error: Failed to completely read config file %s", fname);
 		logit(LOG_ERR, "Error: Failed to completely read config file %s", fname);
@@ -768,7 +770,7 @@ void usage(int result)
 	exit(STATE_UNKNOWN);
 	exit(STATE_UNKNOWN);
 }
 }
 
 
-void setup_ssl()
+void setup_ssl(void)
 {
 {
 #ifdef HAVE_SSL
 #ifdef HAVE_SSL
 	int vrfy;
 	int vrfy;
@@ -836,7 +838,7 @@ void setup_ssl()
 #endif
 #endif
 }
 }
 
 
-void set_sig_handlers()
+void set_sig_handlers(void)
 {
 {
 #ifdef HAVE_SIGACTION
 #ifdef HAVE_SIGACTION
 	struct sigaction sig_action;
 	struct sigaction sig_action;
@@ -856,7 +858,7 @@ void set_sig_handlers()
 	alarm(socket_timeout);
 	alarm(socket_timeout);
 }
 }
 
 
-int connect_to_remote()
+int connect_to_remote(void)
 {
 {
 #ifdef HAVE_SSL
 #ifdef HAVE_SSL
 	int rc, ssl_err, ern, x, nerrs = 0;
 	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)) {
 		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);
 			X509 *peer = SSL_get_peer_certificate(ssl);
 
 
 			if (peer) {
 			if (peer) {
@@ -988,12 +990,13 @@ int connect_to_remote()
 	return result;
 	return result;
 }
 }
 
 
-int send_request()
+int send_request(void)
 {
 {
 	v2_packet *v2_send_packet = NULL;
 	v2_packet *v2_send_packet = NULL;
 	v3_packet *v3_send_packet = NULL;
 	v3_packet *v3_send_packet = NULL;
 	u_int32_t calculated_crc32;
 	u_int32_t calculated_crc32;
-	int rc, bytes_to_send, pkt_size;
+	int rc, bytes_to_send;
+	size_t pkt_size;
 	char *send_pkt;
 	char *send_pkt;
 
 
 	if (packet_ver == NRPE_PACKET_VERSION_2) {
 	if (packet_ver == NRPE_PACKET_VERSION_2) {
@@ -1080,7 +1083,7 @@ int send_request()
 	return STATE_OK;
 	return STATE_OK;
 }
 }
 
 
-int read_response()
+int read_response(void)
 {
 {
 	v2_packet *v2_receive_packet = NULL;
 	v2_packet *v2_receive_packet = NULL;
 	/* Note: v4 packets will use the v3_packet structure */
 	/* 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;
 	int rc;
 	char *buff_ptr;
 	char *buff_ptr;
 
 
+	(void)ssl_ptr;
 	/* Read only the part that's common between versions 2 & 3 */
 	/* Read only the part that's common between versions 2 & 3 */
 	common_size = tot_bytes = bytes_to_recv = (char *)packet.buffer - (char *)&packet;
 	common_size = tot_bytes = bytes_to_recv = (char *)packet.buffer - (char *)&packet;
 
 
+#ifdef HAVE_SSL
 	if (use_ssl == FALSE) {
 	if (use_ssl == FALSE) {
+#endif
 		rc = recvall(sock, (char *)&packet, &tot_bytes, socket_timeout);
 		rc = recvall(sock, (char *)&packet, &tot_bytes, socket_timeout);
 
 
 		if (rc <= 0 || rc != bytes_to_recv) {
 		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;
 			return -1;
 		} else
 		} else
 			tot_bytes += rc;
 			tot_bytes += rc;
-	}
 #ifdef HAVE_SSL
 #ifdef HAVE_SSL
+	}
 	else {
 	else {
 		SSL *ssl = (SSL *) ssl_ptr;
 		SSL *ssl = (SSL *) ssl_ptr;
 
 
@@ -1450,6 +1456,8 @@ void alarm_handler(int sig)
 	const char	*text = state_text(timeout_return_code);
 	const char	*text = state_text(timeout_return_code);
 	size_t		lth1 = 0, lth2 = 0;
 	size_t		lth1 = 0, lth2 = 0;
 
 
+	(void)sig;
+
 	for (lth1 = 0; lth1 < 10; ++lth1)
 	for (lth1 = 0; lth1 < 10; ++lth1)
 		if (text[lth1] == 0)
 		if (text[lth1] == 0)
 			break;
 			break;

+ 2 - 2
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)
 void ssl_set_protocol_version(SslVer ssl_proto_ver, unsigned long *ssl_opts)
 {
 {
 #if OPENSSL_VERSION_NUMBER >= 0x10100000
 #if OPENSSL_VERSION_NUMBER >= 0x10100000
-
+	(void)ssl_opts;
 	SSL_CTX_set_max_proto_version(ctx, 0);
 	SSL_CTX_set_max_proto_version(ctx, 0);
 
 
 	switch(ssl_proto_ver) {
 	switch(ssl_proto_ver) {
@@ -223,7 +223,7 @@ int ssl_load_certificates(void)
 
 
 int ssl_set_ciphers(void)
 int ssl_set_ciphers(void)
 {
 {
-    int x;
+    size_t x;
     int changed = FALSE;
     int changed = FALSE;
 	char errstr[256] = { "" };
 	char errstr[256] = { "" };
 
 

+ 280 - 146
src/nrpe.c

@@ -71,7 +71,6 @@ int       rfc931_timeout=15;
 
 
 #define how_many(x,y) (((x)+((y)-1))/(y))
 #define how_many(x,y) (((x)+((y)-1))/(y))
 
 
-extern int errno;
 struct addrinfo *listen_addrs = NULL;
 struct addrinfo *listen_addrs = NULL;
 int       listen_socks[MAX_LISTEN_SOCKS];
 int       listen_socks[MAX_LISTEN_SOCKS];
 char      remote_host[MAX_HOST_ADDRESS_LENGTH];
 char      remote_host[MAX_HOST_ADDRESS_LENGTH];
@@ -135,7 +134,6 @@ int main(int argc, char **argv)
 {
 {
 	int       result = OK;
 	int       result = OK;
 	int       x;
 	int       x;
-	uint32_t  y;
 	char      buffer[MAX_INPUT_BUFFER];
 	char      buffer[MAX_INPUT_BUFFER];
 
 
 	init();
 	init();
@@ -211,10 +209,10 @@ int init(void)
 	int       result = OK;
 	int       result = OK;
 
 
 	/* set some environment variables */
 	/* 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 */
 	/* open a connection to the syslog facility */
 	/* facility name may be overridden later */
 	/* facility name may be overridden later */
@@ -232,8 +230,8 @@ void init_ssl(void)
 #ifdef HAVE_SSL
 #ifdef HAVE_SSL
 	char          seedfile[FILENAME_MAX];
 	char          seedfile[FILENAME_MAX];
 	char          errstr[256] = { "" };
 	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 (use_ssl == FALSE) {
 		if (debug == TRUE)
 		if (debug == TRUE)
@@ -574,7 +572,7 @@ char* process_metachars(const char* input)
 	char* copy = strdup(input);
 	char* copy = strdup(input);
 	int i,j;
 	int i,j;
 	int length = strlen(input);
 	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] != '\\') {
 		if (copy[j] != '\\') {
 			copy[i] = copy[j];
 			copy[i] = copy[j];
 			continue;
 			continue;
@@ -625,7 +623,6 @@ char* process_metachars(const char* input)
 /* read in the configuration file */
 /* read in the configuration file */
 int read_config_file(char *filename)
 int read_config_file(char *filename)
 {
 {
-	struct stat st;
 	FILE     *fp;
 	FILE     *fp;
 	char      config_file[MAX_FILENAME_LENGTH];
 	char      config_file[MAX_FILENAME_LENGTH];
 	char      input_buffer[MAX_INPUT_BUFFER];
 	char      input_buffer[MAX_INPUT_BUFFER];
@@ -719,14 +716,16 @@ int read_config_file(char *filename)
 				return ERROR;
 				return ERROR;
 			}
 			}
 
 
-		} else if (!strcmp(varname, "command_prefix"))
+		} else if (!strcmp(varname, "command_prefix")) {
+			free(command_prefix);
 			command_prefix = strdup(varvalue);
 			command_prefix = strdup(varvalue);
 
 
-		else if (!strcmp(varname, "server_address")) {
+		} else if (!strcmp(varname, "server_address")) {
 			strncpy(server_address, varvalue, sizeof(server_address) - 1);
 			strncpy(server_address, varvalue, sizeof(server_address) - 1);
 			server_address[sizeof(server_address) - 1] = '\0';
 			server_address[sizeof(server_address) - 1] = '\0';
 
 
 		} else if (!strcmp(varname, "allowed_hosts")) {
 		} else if (!strcmp(varname, "allowed_hosts")) {
+			free(allowed_hosts);
 			allowed_hosts = strdup(varvalue);
 			allowed_hosts = strdup(varvalue);
 			parse_allowed_hosts(allowed_hosts);
 			parse_allowed_hosts(allowed_hosts);
 			if (debug == TRUE)
 			if (debug == TRUE)
@@ -749,13 +748,15 @@ int read_config_file(char *filename)
 			else
 			else
 				debug = FALSE;
 				debug = FALSE;
 
 
-		} else if (!strcmp(varname, "nrpe_user"))
+		} else if (!strcmp(varname, "nrpe_user")) {
+			free(nrpe_user);
 			nrpe_user = strdup(varvalue);
 			nrpe_user = strdup(varvalue);
 
 
-		else if (!strcmp(varname, "nrpe_group"))
+		} else if (!strcmp(varname, "nrpe_group")) {
+			free(nrpe_group);
 			nrpe_group = strdup(varvalue);
 			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;
 			allow_arguments = (atoi(varvalue) == 1) ? TRUE : FALSE;
 
 
 		else if (!strcmp(varname, "disable_syslog"))
 		else if (!strcmp(varname, "disable_syslog"))
@@ -793,10 +794,11 @@ int read_config_file(char *filename)
 		} else if (!strcmp(varname, "allow_weak_random_seed"))
 		} else if (!strcmp(varname, "allow_weak_random_seed"))
 			allow_weak_random_seed = (atoi(varvalue) == 1) ? TRUE : FALSE;
 			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);
 			pid_file = strdup(varvalue);
 
 
-		else if (!strcmp(varname, "listen_queue_size")) {
+		} else if (!strcmp(varname, "listen_queue_size")) {
 			listen_queue_size = atoi(varvalue);
 			listen_queue_size = atoi(varvalue);
 			if (listen_queue_size == 0) {
 			if (listen_queue_size == 0) {
 				logit(LOG_ERR,
 				logit(LOG_ERR,
@@ -854,16 +856,19 @@ int read_config_file(char *filename)
 			strncpy(sslprm.cipher_list, varvalue, sizeof(sslprm.cipher_list) - 1);
 			strncpy(sslprm.cipher_list, varvalue, sizeof(sslprm.cipher_list) - 1);
 			sslprm.cipher_list[sizeof(sslprm.cipher_list) - 1] = '\0';
 			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);
 			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);
 			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);
 			sslprm.privatekey_file = strdup(varvalue);
 
 
-		else if (!strcmp(varname, "ssl_client_certs")) {
+		} else if (!strcmp(varname, "ssl_client_certs")) {
 			sslprm.client_certs = atoi(varvalue);
 			sslprm.client_certs = atoi(varvalue);
 			if ((int)sslprm.client_certs < 0 || sslprm.client_certs > Require_Cert) {
 			if ((int)sslprm.client_certs < 0 || sslprm.client_certs > Require_Cert) {
 				logit(LOG_ERR,
 				logit(LOG_ERR,
@@ -885,13 +890,15 @@ int read_config_file(char *filename)
 					   "Invalid log_facility specified in config file '%s' - Line %d\n",
 					   "Invalid log_facility specified in config file '%s' - Line %d\n",
 					   filename, line);
 					   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);
 			keep_env_vars = strdup(varvalue);
 
 
-		else if (!strcmp(varname, "nasty_metachars"))
+		} else if (!strcmp(varname, "nasty_metachars"))
 			nasty_metachars = process_metachars(varvalue);
 			nasty_metachars = process_metachars(varvalue);
 
 
 		else if (!strcmp(varname, "log_file")) {
 		else if (!strcmp(varname, "log_file")) {
+			free(log_file);
 			log_file = strdup(varvalue);
 			log_file = strdup(varvalue);
 			open_log_file();
 			open_log_file();
 
 
@@ -920,6 +927,7 @@ int read_config_dir(char *dirname)
 	struct stat buf;
 	struct stat buf;
 	char      config_file[MAX_FILENAME_LENGTH];
 	char      config_file[MAX_FILENAME_LENGTH];
 	int       result = OK;
 	int       result = OK;
+	int rc;
 
 
 #ifdef HAVE_SCANDIR
 #ifdef HAVE_SCANDIR
 	/* read and sort the directory contents */
 	/* read and sort the directory contents */
@@ -945,7 +953,11 @@ int read_config_dir(char *dirname)
 		/* process all files in the directory... */
 		/* process all files in the directory... */
 
 
 		/* create the full path to the config file or subdirectory */
 		/* 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';
 		config_file[sizeof(config_file) - 1] = '\x0';
 		stat(config_file, &buf);
 		stat(config_file, &buf);
 
 
@@ -1170,11 +1182,13 @@ void wait_for_connections(void)
 	socklen_t fromlen;
 	socklen_t fromlen;
 	fd_set   *fdset = NULL;
 	fd_set   *fdset = NULL;
 	int       maxfd = 0, new_sd = 0, i, rc, retval;
 	int       maxfd = 0, new_sd = 0, i, rc, retval;
-
+	int       count_fdset = 0;
 	setup_wait_conn();
 	setup_wait_conn();
 
 
 	/* listen for connection requests - fork() if we get one */
 	/* listen for connection requests - fork() if we get one */
 	while (1) {
 	while (1) {
+		int need_fdset;
+
 		/* bail out if necessary */
 		/* bail out if necessary */
 		if (sigrestart == TRUE || sigshutdown == TRUE)
 		if (sigrestart == TRUE || sigshutdown == TRUE)
 			break;
 			break;
@@ -1184,9 +1198,14 @@ void wait_for_connections(void)
 				maxfd = listen_socks[i];
 				maxfd = listen_socks[i];
 		}
 		}
 
 
-		if (fdset != NULL)
+		need_fdset = how_many(maxfd + 1, NFDBITS);
+		if (need_fdset > count_fdset) {
 			free(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++)
 		for (i = 0; i < num_listen_socks; i++)
 			FD_SET(listen_socks[i], fdset);
 			FD_SET(listen_socks[i], fdset);
@@ -1253,6 +1272,7 @@ void wait_for_connections(void)
 	close_listen_socks();
 	close_listen_socks();
 	freeaddrinfo(listen_addrs);
 	freeaddrinfo(listen_addrs);
 	listen_addrs = NULL;
 	listen_addrs = NULL;
+	free(fdset);
 
 
 	return;
 	return;
 }
 }
@@ -1268,10 +1288,15 @@ void setup_wait_conn(void)
 
 
 	for (ai = listen_addrs; ai; ai = ai->ai_next) {
 	for (ai = listen_addrs; ai; ai = ai->ai_next) {
 		if (debug == TRUE) {
 		if (debug == TRUE) {
+			char *fam = "";
 			inet_ntop (ai->ai_family, ai->ai_addr->sa_data, addrstr, 100);
 			inet_ntop (ai->ai_family, ai->ai_addr->sa_data, addrstr, 100);
 			ptr = &((struct sockaddr_in *) ai->ai_addr)->sin_addr;
 			ptr = &((struct sockaddr_in *) ai->ai_addr)->sin_addr;
 			inet_ntop (ai->ai_family, ptr, addrstr, 100);
 			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);
 		create_listener(ai);
 	}
 	}
@@ -1662,12 +1687,17 @@ void handle_connection(int sock)
 			/* see if the command timed out */
 			/* see if the command timed out */
 			if (early_timeout == TRUE) {
 			if (early_timeout == TRUE) {
 				free(send_buff);
 				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;
 				result = STATE_UNKNOWN;
 			} else if (!strcmp(send_buff, "")) {
 			} else if (!strcmp(send_buff, "")) {
 				free(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;
 				result = STATE_UNKNOWN;
 			}
 			}
 
 
@@ -1739,12 +1769,12 @@ void handle_connection(int sock)
 
 
 	/* send the response back to the client */
 	/* send the response back to the client */
 	bytes_to_send = pkt_size;
 	bytes_to_send = pkt_size;
-	if (use_ssl == FALSE)
-		sendall(sock, send_pkt, &bytes_to_send);
 #ifdef HAVE_SSL
 #ifdef HAVE_SSL
-	else
+	if (use_ssl)
 		SSL_write(ssl, send_pkt, bytes_to_send);
 		SSL_write(ssl, send_pkt, bytes_to_send);
+	else
 #endif
 #endif
+		sendall(sock, send_pkt, &bytes_to_send);
 
 
 #ifdef HAVE_SSL
 #ifdef HAVE_SSL
 	if (ssl) {
 	if (ssl) {
@@ -1788,9 +1818,9 @@ void init_handle_conn(void)
 	alarm(connection_timeout);
 	alarm(connection_timeout);
 }
 }
 
 
+#ifdef HAVE_SSL
 int handle_conn_ssl(int sock, void *ssl_ptr)
 int handle_conn_ssl(int sock, void *ssl_ptr)
 {
 {
-#ifdef HAVE_SSL
 # if (defined(__sun) && defined(SOLARIS_10)) || defined(_AIX) || defined(__hpux)
 # if (defined(__sun) && defined(SOLARIS_10)) || defined(_AIX) || defined(__hpux)
 	SSL_CIPHER *c;
 	SSL_CIPHER *c;
 #else
 #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",
 			logit(LOG_NOTICE, "SSL Client %s did not present a certificate",
 				   remote_host);
 				   remote_host);
 	}
 	}
-#endif
 
 
 	return OK;
 	return OK;
 }
 }
+#endif
 
 
 int read_packet(int sock, void *ssl_ptr, v2_packet * v2_pkt, v3_packet ** v3_pkt)
 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;
 	int       rc;
 	char     *buff_ptr;
 	char     *buff_ptr;
 
 
+	(void)ssl_ptr;
 	/* Read only the part that's common between versions 2 & 3 */
 	/* 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;
 	common_size = tot_bytes = bytes_to_recv = (char *)&v2_pkt->buffer - (char *)v2_pkt;
 
 
+#ifdef HAVE_SSL
 	if (use_ssl == FALSE) {
 	if (use_ssl == FALSE) {
+#endif
 		rc = recvall(sock, (char *)v2_pkt, &tot_bytes, socket_timeout);
 		rc = recvall(sock, (char *)v2_pkt, &tot_bytes, socket_timeout);
 
 
 		if (rc <= 0 || rc != bytes_to_recv)
 		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;
 			return -1;
 		} else
 		} else
 			tot_bytes += rc;
 			tot_bytes += rc;
-	}
 #ifdef HAVE_SSL
 #ifdef HAVE_SSL
+	}
 	else {
 	else {
 		SSL      *ssl = (SSL *) ssl_ptr;
 		SSL      *ssl = (SSL *) ssl_ptr;
 		int       sockfd, retval;
 		int       sockfd, retval;
@@ -2092,21 +2125,16 @@ void free_memory(void)
 	return;
 	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 */
 /* executes a system command via popen(), but protects against timeouts */
 int my_system(char *command, int timeout, int *early_timeout, char **output)
 int my_system(char *command, int timeout, int *early_timeout, char **output)
 {
 {
-	FILE     *fp;
 	pid_t     pid;
 	pid_t     pid;
-	time_t    start_time, end_time;
-	int       status;
+	time_t    start_time;
 	int       result;
 	int       result;
-	char      buffer[MAX_INPUT_BUFFER];
 	int       fd[2];
 	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 */
 	*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 */
 	/* return an error if we couldn't fork */
 	if (pid == -1) {
 	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 both ends of the pipe */
 		close(fd[0]);
 		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 */
 		close(fd[0]);			/* close pipe for reading */
 		setpgid(0, 0);			/* become process group leader */
 		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;
 				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;
 	return result;
 }
 }
 
 
 /* handle timeouts when executing commands via my_system() */
 /* handle timeouts when executing commands via my_system() */
 void my_system_sighandler(int sig)
 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... */
 	exit(STATE_CRITICAL);		/* force the child process to exit... */
 }
 }
 
 
 /* handle errors where connection takes too long */
 /* handle errors where connection takes too long */
 void my_connection_sighandler(int sig)
 void my_connection_sighandler(int sig)
 {
 {
+	(void)sig;
 	logit(LOG_ERR, "Connection has taken too long to establish. Exiting...");
 	logit(LOG_ERR, "Connection has taken too long to establish. Exiting...");
 	exit(STATE_CRITICAL);
 	exit(STATE_CRITICAL);
 }
 }
@@ -2449,6 +2579,7 @@ int remove_pid_file(void)
 
 
 void my_disconnect_sighandler(int sig)
 void my_disconnect_sighandler(int sig)
 {
 {
+	(void)sig;
 	logit(LOG_ERR, "SSL_shutdown() has taken too long to complete. Exiting now..");
 	logit(LOG_ERR, "SSL_shutdown() has taken too long to complete. Exiting now..");
 	exit(STATE_CRITICAL);
 	exit(STATE_CRITICAL);
 }
 }
@@ -2534,6 +2665,7 @@ void sighandler(int sig)
 /* handle signals (child processes) */
 /* handle signals (child processes) */
 void child_sighandler(int sig)
 void child_sighandler(int sig)
 {
 {
+	(void)sig;
 	exit(0);					/* terminate */
 	exit(0);					/* terminate */
 }
 }
 
 
@@ -2672,7 +2804,7 @@ int contains_nasty_metachars(char *str)
 		return FALSE;
 		return FALSE;
 
 
 	result = strcspn(str, nasty_metachars);
 	result = strcspn(str, nasty_metachars);
-	if (result != strlen(str))
+	if (result != (long)strlen(str))
 		return TRUE;
 		return TRUE;
 
 
 	return FALSE;
 	return FALSE;
@@ -2696,7 +2828,7 @@ int process_macros(char *input_buffer, char *output_buffer, int buffer_length)
 		selected_macro = NULL;
 		selected_macro = NULL;
 
 
 		if (in_macro == FALSE) {
 		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);
 				strncat(output_buffer, temp_buffer, buffer_length - strlen(output_buffer) - 1);
 				output_buffer[buffer_length - 1] = '\x0';
 				output_buffer[buffer_length - 1] = '\x0';
 			}
 			}
@@ -2704,7 +2836,7 @@ int process_macros(char *input_buffer, char *output_buffer, int buffer_length)
 
 
 		} else {
 		} 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 */
 				/* argument macro */
 				if (strstr(temp_buffer, "ARG") == temp_buffer) {
 				if (strstr(temp_buffer, "ARG") == temp_buffer) {
@@ -2823,7 +2955,9 @@ int process_arguments(int argc, char **argv)
 			break;
 			break;
 
 
 		case 'n':
 		case 'n':
+#ifdef HAVE_SSL
 			use_ssl = FALSE;
 			use_ssl = FALSE;
+#endif
 			break;
 			break;
 
 
 		case 's':				/* Argument s to indicate SRC option */
 		case 's':				/* Argument s to indicate SRC option */

+ 15 - 10
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 <stdarg.h>
 #include <stdarg.h>
 #ifdef HAVE_PATHS_H
 #ifdef HAVE_PATHS_H
 #include <paths.h>
 #include <paths.h>
@@ -264,16 +268,16 @@ int clean_environ(const char *keep_env_vars, const char *nrpe_user)
 #else
 #else
 	static char	*path = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
 	static char	*path = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
 #endif
 #endif
-	struct passwd *pw;
+	struct passwd *pw = NULL;
 	size_t len, var_sz = 0;
 	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;
 	int i, j, keepcnt = 0;
 
 
 	if (keep_env_vars && *keep_env_vars)
 	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
 	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!");
 		logit(LOG_ERR, "Could not sanitize the environment. Aborting!");
 		return ERROR;
 		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!");
 		logit(LOG_ERR, "Could not sanitize the environment. Aborting!");
 		return ERROR;
 		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);
 		kept[i++] = strip(var);
 
 
 	var = NULL;
 	var = NULL;
@@ -485,7 +490,7 @@ char *my_strsep(char **stringp, const char *delim)
 	return begin;
 	return begin;
 }
 }
 
 
-void open_log_file()
+void open_log_file(void)
 {
 {
 	int fh;
 	int fh;
 	int flags = O_RDWR|O_APPEND|O_CREAT;
 	int flags = O_RDWR|O_APPEND|O_CREAT;
@@ -557,7 +562,7 @@ void logit(int priority, const char *format, ...)
 	va_end(ap);
 	va_end(ap);
 }
 }
 
 
-void close_log_file()
+void close_log_file(void)
 {
 {
 	if(!log_fp)
 	if(!log_fp)
 		return;
 		return;