Просмотр исходного кода

Merge branch 'maint' into master

Sebastian Wolf 6 лет назад
Родитель
Сommit
a2ed36f3e6
9 измененных файлов с 501 добавлено и 97 удалено
  1. 6 0
      CHANGELOG.md
  2. 164 0
      CONTRIBUTING.md
  3. 14 1
      include/common.h.in
  4. 2 0
      include/nrpe.h
  5. 38 24
      src/acl.c
  6. 106 46
      src/check_nrpe.c
  7. 164 23
      src/nrpe.c
  8. 1 1
      startup/default-xinetd.in
  9. 6 2
      startup/openrc-init.in

+ 6 - 0
CHANGELOG.md

@@ -1,6 +1,12 @@
 NRPE Changelog
 ==============
 
+3.3.0 - 2019-??-??
+------------------
+**ENHANCEMENTS**
+* Added ability to use TLSv1.3 and TLSv1.3+ on systems with it available
+* Added IPv6 ip address to list of default allow_from hosts (Troy Lea)
+
 [3.2.1](https://github.com/NagiosEnterprises/nrpe/releases/tag/nrpe-3.2.1) - 2017-08-31
 ---------------------------------------------------------------------------------------
 **FIXES**

+ 164 - 0
CONTRIBUTING.md

@@ -0,0 +1,164 @@
+# Contributing
+
+Thank you for considering contributing your time and effort to this Nagios project.
+This document serves as our guidelines for contribution. Keep in mind that these 
+are simply *guidelines* - nothing here is set in stone.
+
+## Questions
+
+If you have a question, you don't need to file an Issue. You can simply connect
+with the Nagios Support Team via the 
+[Nagios Support Forum](https://support.nagios.com/forum/).
+
+Not to say that you **can't** open an Issue - but you'll likely get a much faster
+response by posting it on the forum.
+
+## Ideas
+
+If you have an idea your best bet is to open an Issue. This gets it on the radar much
+quicker than any other method.
+
+First, let's define what an "Idea" really is. An Idea is simply an 
+[Enhancement](#enhancements) request in its infancy. 
+There's really nothing to it!
+
+Something as simple as "I think that this project should somehow connect with a 
+widget" is a valid Idea.
+
+These are unrefined and raw. That's why you open an issue - so everyone gets a chance
+to chime in and come up with a plan!
+
+## Feedback
+
+Feedback can be given via several methods. The *easiest* method is by opening an Issue.
+You're more than welcome to leave feedback on the 
+[Nagios Support Forum](https://support.nagios.com/forum/) as well.
+
+By opening an Issue, however, you're insuring that the maintainers and reviewers are
+the first ones to see the feedback. In most cases, this is likely ideal.
+
+## Bugs
+
+Here's where it starts to get serious. 
+
+Following the guidelines outlined in this section allows the maintainers, developers, and
+community to understand and reproduce your bug report.
+
+Make sure to search existing open and closed [Issues](https://guides.github.com/features/issues/)
+before opening a bug report. If you find a closed Issue that seems like it's the same 
+thing that you're experiencing, open a new Issue and include a link to the original Issue 
+in the body of the new one.
+
+**If you have a bug, you *NEED* to open an Issue.**
+
+Not only that, but when you open the Issue, this is what we ***absolutely require***:
+
+* Use a clear and concise title for the Issue to identify the problem accurately
+
+* Describe the bug with as much detail as you can
+
+* Include the version of the project containing the bug you're reporting
+
+* Include your operating system information (`uname -a`)
+
+* Include a list of third party modules that are installed and/or loaded
+
+* Explain the behavior you expected to see (and why) vs. what actually happened
+
+Once you've got that covered - there's still more to include if you want to
+make a ***killer*** report:
+
+* Describe the ***exact steps*** that reproduce the problem
+
+* Provide **specific** examples to demonstrate those steps
+ 
+* If your bug is from an older version, make sure test against the latest (and/or the `maint` branch)
+
+* Include any screenshots that can help explain the issue
+
+* Include a file containing `strace` and/or `valgrind` output
+
+* Explain when the problem started happening: was it after an upgrade? or was it always present?
+
+* Define how reliably you can reproduce the bug
+
+* Any other information that you decide is relevant is also welcome
+
+## Enhancements
+
+An enhancement is either a completely new feature or an improvement to existing 
+functionality. We consider it to be a bit different than idea - based solely
+on the fact that it's more detailed than an idea would be.
+
+So you've got an idea for an ehancement? Great!
+
+Following the guidelines outlined in this section allows maintainers, developers, and
+the community to understand your enhancement and determine whether or not it's worth 
+doing and/or what's involved in carrying it out.
+
+Make sure to search open and closed Issues and Pull Requests to determine if
+someone has either submitted the enhancement. If you feel like your enhancement
+is similar to one found, make sure to link the original in your request.
+
+Enhancements are submitted by opening an Issue.
+
+Unlike an [Idea](#idea), when you decide to submit your enhancement and open 
+the Issue, we require at least the following information:
+
+* Use a clear and descriptive title to illustrate the enhancement you're requesting
+
+* Describe the current behavior (if it exists) and what changes you think should be made
+
+* Explain the enhancement in detail - make sure it makes sense and is easily understandable
+
+* Specify why the enhancement would be useful and who it would be useful to
+
+* If there is some other project or program where this enhancement already exists, make sure
+to link to it
+
+Beyond that, there are a few more things you can do to make sure you **really** get your
+point across:
+
+* Create a mockup of the enhancement (if applicable) and attach whatever files you can
+
+* Provide a step-by-step description of the suggested enhancement
+
+* Generate a fully dressed use-case for the enhancement request
+
+* Create a specification for the preferred implementation of the enhancement
+
+* Include a timeline regarding development expectations towards the request
+
+## Submitting Code
+
+Everything else in this document has lead up to this moment - how can ***you*** submit 
+code to the **project**.
+
+We allow code submissions via [Pull Requests](https://help.github.com/articles/about-pull-requests/).
+These let you (and us) discuss and review any changes to code in any repository you've made.
+
+How to create and manage Pull Requests is outside of the scope of this document, but make
+sure to check out GitHub's official documentation ([link here](https://help.github.com/))
+to get a handle on it.
+
+While you're forking the repository to create a patch or an enhancement, create a *new 
+branch* to make the change - it will be easier to submit a pull request using a new
+branch in your forked repository!
+
+When you submit a Pull Request, make sure you follow the guidelines:
+
+* Make sure you're submitting to the proper branch. Branch `maint` is used for the 
+**next** bugfix release. The next enhancement release branch will vary.
+
+* ***NEVER*** submit a Pull Request to `master` branch.
+
+* Keep commit messages as concise as possible.
+* Update the appropriate files in regards to your changes:
+
+  * `CHANGES`
+
+  * `THANKS`
+
+* End all committed files with a newline.
+
+* Test your changes and include the results as a comment.

+ 14 - 1
include/common.h.in

@@ -66,12 +66,23 @@
 
 #define QUERY_PACKET				1		/* id code for a packet containing a query */
 #define	RESPONSE_PACKET				2		/* id code for a packet containing a response */
-#define NRPE_PACKET_VERSION_3		3		/* packet version identifier */
+
+/* v4 takes struct padding into account, so the buffer "takes" 4 bytes
+ * v3 removes the 1 byte that "should" be allocated to buffer.
+ */
+#define NRPE_V4_PACKET_SIZE_OFFSET  4
+#define NRPE_V3_PACKET_SIZE_OFFSET  1
+
+/* packet version identifiers */
+#define NRPE_PACKET_VERSION_4		4       /* Same as version 3, but accounts for struct padding in network code */
+#define NRPE_PACKET_VERSION_3		3		/* Allows for variable-length buffer */
 #define NRPE_PACKET_VERSION_2		2
 #define NRPE_PACKET_VERSION_1		1		/* older packet version identifiers (no longer supported) */
 
 #define MAX_PACKETBUFFER_LENGTH		1024	/* amount of data to send in one query/response vor version 2 */
 
+#define NRPE_DEFAULT_PACKET_VERSION NRPE_PACKET_VERSION_4
+
 typedef struct _v2_packet {
 	int16_t		packet_version;
 	int16_t		packet_type;
@@ -89,6 +100,8 @@ typedef struct _v3_packet {
 	char		buffer[1];
 } v3_packet;
 
+typedef v3_packet v4_packet;
+
 /**************** OPERATING SYSTEM SPECIFIC DEFINITIONS **********/
 #if defined(__sun) || defined(__hpux)
 

+ 2 - 0
include/nrpe.h

@@ -24,6 +24,8 @@
  *
  ****************************************************************************/
 
+#include <limits.h>
+
 typedef struct command_struct {
 	char					*command_name;
 	char					*command_line;

+ 38 - 24
src/acl.c

@@ -544,31 +544,45 @@ int is_an_allowed_host(int family, void *host)
 		if (!getaddrinfo(dns_acl_curr->domain, NULL, NULL, &res)) {
 
 			for (ai = res; ai; ai = ai->ai_next) {
-
-				switch(ai->ai_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), dns_acl_curr->domain);
+				if (ai->ai_family == family) {
+					switch (ai->ai_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): test match host >%s< "
+											  "for allowed host >%s<\n",
+									  inet_ntoa(tmp), dns_acl_curr->domain);
+							}
+
+							addr = (struct sockaddr_in *) (ai->ai_addr);
+							if (addr->sin_addr.s_addr == ((struct in_addr *) host)->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:
+							if (debug == TRUE) {
+								char formattedStr[INET6_ADDRSTRLEN];
+								inet_ntop(ai->ai_family, (void *) &(((struct sockaddr_in6 *) (ai->ai_addr))->sin6_addr),
+										  formattedStr, INET6_ADDRSTRLEN);
+								logit(LOG_INFO, "is_an_allowed_host (AF_INET6): test match host against >%s< "
+											  "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)
+									logit(LOG_INFO, "is_an_allowed_host (AF_INET6): "
+											"host is in allowed host list!");
+								return 1;
+							}
+							break;
 					}
-
-					addr = (struct sockaddr_in*)(ai->ai_addr);
-					if (addr->sin_addr.s_addr == ((struct in_addr*)host)->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:
-					memcpy((char*)&addr6, ai->ai_addr, sizeof(addr6));
-					if (!memcmp(&addr6.sin6_addr, &host, sizeof(addr6.sin6_addr)))
-						return 1;
-					break;
 				}
 			}
 		}

+ 106 - 46
src/check_nrpe.c

@@ -65,8 +65,9 @@ char query[MAX_INPUT_BUFFER] = "";
 int show_help = FALSE;
 int show_license = FALSE;
 int show_version = FALSE;
-int packet_ver = NRPE_PACKET_VERSION_3;
+int packet_ver = NRPE_DEFAULT_PACKET_VERSION;
 int force_v2_packet = 0;
+int force_v3_packet = 0;
 int payload_size = 0;
 extern char *log_file;
 
@@ -87,7 +88,7 @@ int use_ssl = FALSE;
 /* SSL/TLS parameters */
 typedef enum _SSL_VER {
 	SSL_Ver_Invalid = 0, SSLv2 = 1, SSLv2_plus, SSLv3, SSLv3_plus,
-	TLSv1, TLSv1_plus, TLSv1_1, TLSv1_1_plus, TLSv1_2, TLSv1_2_plus
+	TLSv1, TLSv1_plus, TLSv1_1, TLSv1_1_plus, TLSv1_2, TLSv1_2_plus, TLSv1_3, TLSv1_3_plus
 } SslVer;
 
 typedef enum _CLNT_CERTS { Ask_For_Cert = 1, Require_Cert = 2 } ClntCerts;
@@ -177,7 +178,7 @@ int main(int argc, char **argv)
 
 	if (result == -1) {
 		/* Failure reading from remote, so try version 2 packet */
-		logit(LOG_INFO, "Remote %s does not support Version 3 Packets", rem_host);
+		logit(LOG_INFO, "Remote %s does not support version 3/4 packets", rem_host);
 		packet_ver = NRPE_PACKET_VERSION_2;
 
 		/* Rerun the setup */
@@ -200,7 +201,7 @@ int main(int argc, char **argv)
 	}
 
 	if (result != -1 && force_v2_packet == 0 && packet_ver == NRPE_PACKET_VERSION_2)
-		logit(LOG_DEBUG, "Remote %s accepted a Version %d Packet", rem_host, packet_ver);
+		logit(LOG_DEBUG, "Remote %s accepted a version %d packet", rem_host, packet_ver);
 
 	close_log_file();			/* close the log file */
 	return result;
@@ -226,6 +227,7 @@ int process_arguments(int argc, char **argv, int from_config_file)
 		{"no-ssl", no_argument, 0, 'n'},
 		{"unknown-timeout", no_argument, 0, 'u'},
 		{"v2-packets-only", no_argument, 0, '2'},
+		{"v3-packets-only", no_argument, 0, '3'},
 		{"ipv4", no_argument, 0, '4'},
 		{"ipv6", no_argument, 0, '6'},
 		{"use-adh", required_argument, 0, 'd'},
@@ -252,7 +254,7 @@ int process_arguments(int argc, char **argv, int from_config_file)
 		return ERROR;
 
 	optind = 0;
-	snprintf(optchars, MAX_INPUT_BUFFER, "H:f:b:c:a:t:p:S:L:C:K:A:d:s:P:g:246hlnuVEe");
+	snprintf(optchars, MAX_INPUT_BUFFER, "H:f:b:c:a:t:p:S:L:C:K:A:d:s:P:g:2346hlnuVEe");
 
 	while (1) {
 		if (argindex > 0)
@@ -368,14 +370,21 @@ int process_arguments(int argc, char **argv, int from_config_file)
 			break;
 
 		case '2':
-			if (from_config_file && packet_ver != NRPE_PACKET_VERSION_3) {
+			if (from_config_file && packet_ver != NRPE_DEFAULT_PACKET_VERSION) {
 				logit(LOG_WARNING, "WARNING: Command-line v2-packets-only (-2) overrides the config file option.");
 				break;
 			}
 			packet_ver = NRPE_PACKET_VERSION_2;
 			force_v2_packet = 1;
 			break;
-
+		case '3':
+			if (from_config_file && packet_ver != NRPE_DEFAULT_PACKET_VERSION) {
+				logit(LOG_WARNING, "Warning: Command-line v3-packets-only (-3) overrides the config file option.");
+				break;
+			}
+			packet_ver = NRPE_PACKET_VERSION_3;
+			force_v3_packet = 1;
+			break;
 		case '4':
 			if (from_config_file && address_family != AF_UNSPEC) {
 				logit(LOG_WARNING, "WARNING: Command-line ipv4 (-4) or ipv6 (-6) overrides the config file option.");
@@ -434,7 +443,11 @@ int process_arguments(int argc, char **argv, int from_config_file)
 				break;
 			}
 
-			if (!strcmp(optarg, "TLSv1.2"))
+			if (!strcmp(optarg, "TLSv1.3"))
+				sslprm.ssl_proto_ver = TLSv1_3;
+			else if (!strcmp(optarg, "TLSv1.3+"))
+				sslprm.ssl_proto_ver = TLSv1_3_plus;
+			else if (!strcmp(optarg, "TLSv1.2"))
 				sslprm.ssl_proto_ver = TLSv1_2;
 			else if (!strcmp(optarg, "TLSv1.2+"))
 				sslprm.ssl_proto_ver = TLSv1_2_plus;
@@ -533,6 +546,11 @@ int process_arguments(int argc, char **argv, int from_config_file)
 		return ERROR;
 	}
 
+	if (force_v2_packet && force_v3_packet) {
+		printf("Error: Only one of force_v2_packet (-2) and force_v3_packet (-3) can be specified.\n");
+		return ERROR;
+	}
+
 	/* make sure required args were supplied */
 	if (server_name == NULL && show_help == FALSE && show_version == FALSE
 		&& show_license == FALSE)
@@ -694,7 +712,7 @@ void usage(int result)
 		printf("SSL/TLS Available: OpenSSL 0.9.6 or higher required\n");
 		printf("\n");
 #endif
-		printf("Usage: check_nrpe -H <host> [-2] [-4] [-6] [-n] [-u] [-V] [-l] [-d <dhopt>]\n");
+		printf("Usage: check_nrpe -H <host> [-2] [-3] [-4] [-6] [-n] [-u] [-V] [-l] [-d <dhopt>]\n");
 		printf("       [-P <size>] [-S <ssl version>]  [-L <cipherlist>] [-C <clientcert>]\n");
 		printf("       [-K <key>] [-A <ca-certificate>] [-s <logopts>] [-b <bindaddr>]\n");
 		printf("       [-f <cfg-file>] [-p <port>] [-t <interval>:<state>] [-g <log-file>]\n");
@@ -702,7 +720,8 @@ void usage(int result)
 		printf("\n");
 		printf("Options:\n");
 		printf(" -H, --host=HOST              The address of the host running the NRPE daemon\n");
-		printf(" -2, --v2-packets-only        Only use version 2 packets, not version 3\n");
+		printf(" -2, --v2-packets-only        Only use version 2 packets, not version 3/4\n");
+		printf(" -3, --v3-packets-only        Only use version 3 packets, not version 4\n");
 		printf(" -4, --ipv4                   Bind to ipv4 only\n");
 		printf(" -6, --ipv6                   Bind to ipv6 only\n");
 		printf(" -n, --no-ssl                 Do no use SSL\n");
@@ -819,6 +838,12 @@ void setup_ssl()
 		case TLSv1_2_plus:
 			val = "TLSv1_2_plus And Above";
 			break;
+		case TLSv1_3:
+			val = "TLSv1_3";
+			break;
+		case TLSv1_3_plus:
+			val = "TLSv1_3_plus And Above";
+			break;
 		default:
 			val = "INVALID VALUE!";
 			break;
@@ -858,6 +883,10 @@ void setup_ssl()
 #  ifdef SSL_TXT_TLSV1_2
 		if (sslprm.ssl_proto_ver == TLSv1_2)
 			meth = TLSv1_2_client_method();
+#  ifdef SSL_TXT_TLSV1_3
+		if (sslprm.ssl_proto_ver == TLSv1_3)
+			meth = TLSv1_3_client_method();
+#  endif	/* ifdef SSL_TXT_TLSV1_3 */
 #  endif	/* ifdef SSL_TXT_TLSV1_2 */
 # endif	/* ifdef SSL_TXT_TLSV1_1 */
 
@@ -873,6 +902,11 @@ void setup_ssl()
 	SSL_CTX_set_max_proto_version(ctx, 0);
 
 	switch(sslprm.ssl_proto_ver) {
+		case TLSv1_3:
+			SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
+		case TLSv1_3_plus:
+			SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
+			break;
 
 		case TLSv1_2:
 			SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
@@ -905,11 +939,14 @@ void setup_ssl()
 			case SSLv2:
 			case SSLv2_plus:
 				break;
+			case TLSv1_3:
+			case TLSv1_3_plus:
+#ifdef SSL_OP_NO_TLSv1_2
+				ssl_opts |= SSL_OP_NO_TLSv1_2;
+#endif
 			case TLSv1_2:
 			case TLSv1_2_plus:
-#ifdef SSL_OP_NO_TLSv1_1
 				ssl_opts |= SSL_OP_NO_TLSv1_1;
-#endif
 			case TLSv1_1:
 			case TLSv1_1_plus:
 				ssl_opts |= SSL_OP_NO_TLSv1;
@@ -1173,9 +1210,13 @@ int send_request()
 
 	} else {
 
-		pkt_size = (sizeof(v3_packet) - 1) + strlen(query) + 1;
-		if (pkt_size < sizeof(v2_packet))
+		pkt_size = (sizeof(v3_packet) - NRPE_V4_PACKET_SIZE_OFFSET) + strlen(query) + 1;
+		if (packet_ver == NRPE_PACKET_VERSION_3) {
+			pkt_size = (sizeof(v3_packet) - NRPE_V3_PACKET_SIZE_OFFSET) + strlen(query) + 1;
+		}
+		if (pkt_size < sizeof(v2_packet)) {
 			pkt_size = sizeof(v2_packet);
+		}
 
 		v3_send_packet = calloc(1, pkt_size);
 		send_pkt = (char *)v3_send_packet;
@@ -1205,10 +1246,12 @@ int send_request()
 	}
 #endif
 
-	if (v3_send_packet)
+	if (v3_send_packet) {
 		free(v3_send_packet);
-	if (v2_send_packet)
+	}
+	if (v2_send_packet) {
 		free(v2_send_packet);
+	}
 
 	if (rc == -1) {
 		printf("CHECK_NRPE: Error sending query to host.\n");
@@ -1222,10 +1265,11 @@ int send_request()
 int read_response()
 {
 	v2_packet *v2_receive_packet = NULL;
+	/* Note: v4 packets will use the v3_packet structure */
 	v3_packet *v3_receive_packet = NULL;
 	u_int32_t packet_crc32;
 	u_int32_t calculated_crc32;
-	int32_t pkt_size;
+	int32_t pkt_size, buffer_size;
 	int rc, result;
 
 	alarm(0);
@@ -1251,32 +1295,50 @@ int read_response()
 
 	/* recv() error */
 	if (rc < 0) {
-		if (packet_ver == NRPE_PACKET_VERSION_3) {
-			if (v3_receive_packet)
-				free(v3_receive_packet);
+		if (v2_receive_packet) {
+			free(v2_receive_packet);
+		}
+		if (v3_receive_packet) {
+			free(v3_receive_packet);
+		}
+		if (packet_ver >= NRPE_PACKET_VERSION_3) {
 			return -1;
 		}
-		if (v2_receive_packet)
-			free(v2_receive_packet);
 		return STATE_UNKNOWN;
 
 	} else if (rc == 0) {
 
 		/* server disconnected */
 		printf("CHECK_NRPE: Received 0 bytes from daemon.  Check the remote server logs for error messages.\n");
-		if (packet_ver == NRPE_PACKET_VERSION_3) {
-			if (v3_receive_packet) {
-				free(v3_receive_packet);
-			}
-		} else if (v2_receive_packet) {
+		if (v3_receive_packet) {
+			free(v3_receive_packet);
+		}
+		if (v2_receive_packet) {
 			free(v2_receive_packet);
 		}
 		return STATE_UNKNOWN;
 	}
 
 	/* check the crc 32 value */
-	if (packet_ver == NRPE_PACKET_VERSION_3) {
-		pkt_size = (sizeof(v3_packet) - 1) + ntohl(v3_receive_packet->buffer_length);
+	if (packet_ver >= NRPE_PACKET_VERSION_3) {
+
+		buffer_size = ntohl(v3_receive_packet->buffer_length);
+		if (buffer_size < 0 || buffer_size > INT_MAX - pkt_size) {
+			printf("CHECK_NRPE: Response packet had invalid buffer size.\n");
+			close(sd);
+			if (v3_receive_packet) {
+				free(v3_receive_packet);
+			}
+			if (v2_receive_packet) {
+				free(v2_receive_packet);
+			}
+			return STATE_UNKNOWN;
+		}
+
+		pkt_size = sizeof(v3_packet);
+		pkt_size -= (packet_ver == NRPE_PACKET_VERSION_3 ? NRPE_V3_PACKET_SIZE_OFFSET : NRPE_V4_PACKET_SIZE_OFFSET);
+		pkt_size += buffer_size;
+
 		packet_crc32 = ntohl(v3_receive_packet->crc32_value);
 		v3_receive_packet->crc32_value = 0L;
 		v3_receive_packet->alignment = 0;
@@ -1294,11 +1356,10 @@ int read_response()
 	if (packet_crc32 != calculated_crc32) {
 		printf("CHECK_NRPE: Response packet had invalid CRC32.\n");
 		close(sd);
-		if (packet_ver == NRPE_PACKET_VERSION_3) {
-			if (v3_receive_packet) {
-				free(v3_receive_packet);
-			}
-		} else if (v2_receive_packet) {
+		if (v3_receive_packet) {
+			free(v3_receive_packet);
+		}
+		if (v2_receive_packet) {
 			free(v2_receive_packet);
 		}
 		return STATE_UNKNOWN;
@@ -1330,11 +1391,10 @@ int read_response()
 		}
 	}
 
-	if (packet_ver == NRPE_PACKET_VERSION_3) {
-		if (v3_receive_packet) {
-			free(v3_receive_packet);
-		}
-	} else if (v2_receive_packet) {
+	if (v3_receive_packet) {
+		free(v3_receive_packet);
+	}
+	if (v2_receive_packet) {
 		free(v2_receive_packet);
 	}
 
@@ -1356,14 +1416,14 @@ int read_packet(int sock, void *ssl_ptr, v2_packet ** v2_pkt, v3_packet ** v3_pk
 
 		if (rc <= 0 || rc != bytes_to_recv) {
 			if (rc < bytes_to_recv) {
-				if (packet_ver != NRPE_PACKET_VERSION_3)
+				if (packet_ver <= NRPE_PACKET_VERSION_3)
 					printf("CHECK_NRPE: Receive header underflow - only %d bytes received (%ld expected).\n", rc, sizeof(bytes_to_recv));
 			}
 			return -1;
 		}
 
 		packet_ver = ntohs(packet.packet_version);
-		if (packet_ver != NRPE_PACKET_VERSION_2 && packet_ver != NRPE_PACKET_VERSION_3) {
+		if (packet_ver != NRPE_PACKET_VERSION_2 && packet_ver != NRPE_PACKET_VERSION_3 && packet_ver != NRPE_PACKET_VERSION_4) {
 			printf("CHECK_NRPE: Invalid packet version received from server.\n");
 			return -1;
 		}
@@ -1421,7 +1481,7 @@ int read_packet(int sock, void *ssl_ptr, v2_packet ** v2_pkt, v3_packet ** v3_pk
 		rc = recvall(sock, buff_ptr, &bytes_to_recv, socket_timeout);
 
 		if (rc <= 0 || rc != buffer_size) {
-			if (packet_ver == NRPE_PACKET_VERSION_3) {
+			if (packet_ver >= NRPE_PACKET_VERSION_3) {
 				free(*v3_pkt);
 				*v3_pkt = NULL;
 			} else {
@@ -1444,14 +1504,14 @@ int read_packet(int sock, void *ssl_ptr, v2_packet ** v2_pkt, v3_packet ** v3_pk
 
 		if (rc <= 0 || rc != bytes_to_recv) {
 			if (rc < bytes_to_recv) {
-				if (packet_ver != NRPE_PACKET_VERSION_3)
+				if (packet_ver < NRPE_PACKET_VERSION_3 || packet_ver > NRPE_PACKET_VERSION_4)
 					printf("CHECK_NRPE: Receive header underflow - only %d bytes received (%ld expected).\n", rc, sizeof(bytes_to_recv));
 			}
 			return -1;
 		}
 
 		packet_ver = ntohs(packet.packet_version);
-		if (packet_ver != NRPE_PACKET_VERSION_2 && packet_ver != NRPE_PACKET_VERSION_3) {
+		if (packet_ver != NRPE_PACKET_VERSION_2 && packet_ver != NRPE_PACKET_VERSION_3 && packet_ver != NRPE_PACKET_VERSION_4) {
 			printf("CHECK_NRPE: Invalid packet version received from server.\n");
 			return -1;
 		}
@@ -1525,7 +1585,7 @@ int read_packet(int sock, void *ssl_ptr, v2_packet ** v2_pkt, v3_packet ** v3_pk
 		buff_ptr[bytes_read] = 0;
 
 		if (rc < 0 || bytes_read != buffer_size) {
-			if (packet_ver == NRPE_PACKET_VERSION_3) {
+			if (packet_ver >= NRPE_PACKET_VERSION_3) {
 				free(*v3_pkt);
 				*v3_pkt = NULL;
 			} else {
@@ -1533,7 +1593,7 @@ int read_packet(int sock, void *ssl_ptr, v2_packet ** v2_pkt, v3_packet ** v3_pk
 				*v2_pkt = NULL;
 			}
 			if (bytes_read != buffer_size) {
-				if (packet_ver == NRPE_PACKET_VERSION_3) {
+				if (packet_ver >= NRPE_PACKET_VERSION_3) {
 					printf("CHECK_NRPE: Receive buffer size - %ld bytes received (%ld expected).\n", (long)bytes_read, sizeof(buffer_size));
 				} else {
 					printf("CHECK_NRPE: Receive underflow - only %ld bytes received (%ld expected).\n", (long)bytes_read, sizeof(buffer_size));

+ 164 - 23
src/nrpe.c

@@ -124,7 +124,7 @@ extern char *log_file;
 /* SSL/TLS parameters */
 typedef enum _SSL_VER {
 	SSLv2 = 1, SSLv2_plus, SSLv3, SSLv3_plus, TLSv1,
-	TLSv1_plus, TLSv1_1, TLSv1_1_plus, TLSv1_2, TLSv1_2_plus
+	TLSv1_plus, TLSv1_1, TLSv1_1_plus, TLSv1_2, TLSv1_2_plus, TLSv1_3, TLSv1_3_plus
 } SslVer;
 
 typedef enum _CLNT_CERTS {
@@ -148,11 +148,11 @@ struct _SSL_PARMS {
 	SslLogging log_opts;
 } sslprm = {
 #if OPENSSL_VERSION_NUMBER >= 0x10100000
-NULL, NULL, NULL, "ALL:!MD5:@STRENGTH:@SECLEVEL=0", TLSv1_plus, TRUE, 0, SSL_NoLogging};
+NULL, NULL, NULL, "ALL:!MD5:@STRENGTH:@SECLEVEL=0", TLSv1_plus, TRUE, 0, SSL_NoLogging
 #else
-NULL, NULL, NULL, "ALL:!MD5:@STRENGTH", TLSv1_plus, TRUE, 0, SSL_NoLogging};
+NULL, NULL, NULL, "ALL:!MD5:@STRENGTH", TLSv1_plus, TRUE, 0, SSL_NoLogging
 #endif
-
+};
 
 #ifdef HAVE_SSL
 static int verify_callback(int ok, X509_STORE_CTX * ctx);
@@ -331,6 +331,10 @@ void init_ssl(void)
 #  ifdef SSL_TXT_TLSV1_2
 	if (sslprm.ssl_proto_ver == TLSv1_2)
 		meth = TLSv1_2_server_method();
+#  ifdef SSL_TXT_TLSV1_3
+	if (sslprm.ssl_proto_ver == TLSv1_3)
+		meth = TLSv1_3_server_method();
+#  endif	/* ifdef SSL_TXT_TLSV1_3 */
 #  endif	/* ifdef SSL_TXT_TLSV1_2 */
 # endif		/* SSL_TXT_TLSV1_1 */
 
@@ -351,6 +355,11 @@ void init_ssl(void)
 	SSL_CTX_set_max_proto_version(ctx, 0);
 
 	switch(sslprm.ssl_proto_ver) {
+		case TLSv1_3:
+			SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
+		case TLSv1_3_plus:
+			SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
+			break;
 
 		case TLSv1_2:
 			SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
@@ -383,11 +392,14 @@ void init_ssl(void)
 		case SSLv2:
 		case SSLv2_plus:
 			break;
+		case TLSv1_3:
+		case TLSv1_3_plus:
+#ifdef SSL_OP_NO_TLSv1_2
+			ssl_opts |= SSL_OP_NO_TLSv1_2;
+#endif
 		case TLSv1_2:
 		case TLSv1_2_plus:
-#ifdef SSL_OP_NO_TLSv1_1
 			ssl_opts |= SSL_OP_NO_TLSv1_1;
-#endif
 		case TLSv1_1:
 		case TLSv1_1_plus:
 			ssl_opts |= SSL_OP_NO_TLSv1;
@@ -519,6 +531,12 @@ void log_ssl_startup(void)
 	case TLSv1_2_plus:
 		vers = "TLSv1_2 And Above";
 		break;
+	case TLSv1_3:
+		vers = "TLSv1_3";
+		break;
+	case TLSv1_3_plus:
+		vers = "TLSv1_3 And Above";
+		break;
 	default:
 		vers = "INVALID VALUE!";
 		break;
@@ -747,6 +765,62 @@ int verify_callback(int preverify_ok, X509_STORE_CTX * ctx)
 }
 #endif
 
+/*
+ * Given a string, convert any byte pairs representing an escape sequence (e.g. "\\r" into 
+ * the single-byte metacharacter (e.g. '\r')
+ * Currently, this doesn't support octal/hex numbers or unicode code points (\n, \x, \u, \U)
+ */
+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++) {
+		if (copy[j] != '\\') {
+			copy[i] = copy[j];
+			continue;
+		}
+
+		j += 1;
+		switch (copy[j]) {
+			case 'a':
+				copy[i] = '\a';
+				break;
+			case 'b':
+				copy[i] = '\b';
+				break;
+			case 'f':
+				copy[i] = '\f';
+				break;
+			case 'n':
+				copy[i] = '\n';
+				break;
+			case 'r':
+				copy[i] = '\r';
+				break;
+			case 't':
+				copy[i] = '\t';
+				break;
+			case 'v':
+				copy[i] = '\v';
+				break;
+			case '\\':
+				copy[i] = '\\';
+				break;
+			case '\'':
+				copy[i] = '\'';
+				break;
+			case '"':
+				copy[i] = '\"';
+				break;
+			case '?':
+				copy[i] = '\?';
+				break;
+		}
+	}
+	copy[j] = '\0';
+}
+
 /* read in the configuration file */
 int read_config_file(char *filename)
 {
@@ -931,7 +1005,11 @@ int read_config_file(char *filename)
 			}
 
 		} else if (!strcmp(varname, "ssl_version")) {
-			if (!strcmp(varvalue, "TLSv1.2"))
+			if (!strcmp(varvalue, "TLSv1.3"))
+				sslprm.ssl_proto_ver = TLSv1_3;
+			else if (!strcmp(varvalue, "TLSv1.3+"))
+				sslprm.ssl_proto_ver = TLSv1_3_plus;
+			else if (!strcmp(varvalue, "TLSv1.2"))
 				sslprm.ssl_proto_ver = TLSv1_2;
 			else if (!strcmp(varvalue, "TLSv1.2+"))
 				sslprm.ssl_proto_ver = TLSv1_2_plus;
@@ -1010,7 +1088,7 @@ int read_config_file(char *filename)
 			keep_env_vars = strdup(varvalue);
 
 		else if (!strcmp(varname, "nasty_metachars"))
-			nasty_metachars = strdup(varvalue);
+			nasty_metachars = process_metachars(varvalue);
 
 		else if (!strcmp(varname, "log_file")) {
 			log_file = strdup(varvalue);
@@ -1839,7 +1917,10 @@ void handle_connection(int sock)
 
 	} else {
 
-		pkt_size = (sizeof(v3_packet) - 1) + strlen(send_buff);
+		pkt_size = (sizeof(v3_packet) - NRPE_V4_PACKET_SIZE_OFFSET) + strlen(send_buff);
+		if (packet_ver == NRPE_PACKET_VERSION_3) {
+			pkt_size = (sizeof(v3_packet) - NRPE_V3_PACKET_SIZE_OFFSET) + strlen(send_buff);
+		}
 		v3_send_packet = calloc(1, pkt_size);
 		send_pkt = (char *)v3_send_packet;
 		/* initialize response packet data */
@@ -1919,13 +2000,31 @@ int handle_conn_ssl(int sock, void *ssl_ptr)
 	char      buffer[MAX_INPUT_BUFFER];
 	SSL      *ssl = (SSL*)ssl_ptr;
 	X509     *peer;
-	int       rc, x;
+	int       rc, x, sockfd, retval;
+	fd_set    rfds;
+	struct timeval timeout;
 
 	SSL_set_fd(ssl, sock);
+	sockfd = SSL_get_fd(ssl);
+
+	FD_ZERO(&rfds);
+	FD_SET(sockfd, &rfds);
+
+	timeout.tv_sec = connection_timeout;
+	timeout.tv_usec = 0;
+
 
 	/* keep attempting the request if needed */
-	while (((rc = SSL_accept(ssl)) != 1)
-			&& (SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ));
+	do {
+		retval = select(sockfd + 1, &rfds, NULL, NULL, &timeout);
+
+		if (retval > 0) {
+			rc = SSL_accept(ssl);
+		} else {
+			logit(LOG_ERR, "Error: (!log_opts) Could not complete SSL handshake with %s: timeout %d seconds", remote_host, connection_timeout);
+			return ERROR;
+		}
+	} while (SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ);
 
 	if (rc != 1) {
 		/* oops, got an unrecoverable error -- get out */
@@ -2015,7 +2114,7 @@ int read_packet(int sock, void *ssl_ptr, v2_packet * v2_pkt, v3_packet ** v3_pkt
 			return -1;
 
 		packet_ver = ntohs(v2_pkt->packet_version);
-		if (packet_ver != NRPE_PACKET_VERSION_2 && packet_ver != NRPE_PACKET_VERSION_3) {
+		if (packet_ver != NRPE_PACKET_VERSION_2 && packet_ver != NRPE_PACKET_VERSION_3 && packet_ver != NRPE_PACKET_VERSION_4) {
 			logit(LOG_ERR, "Error: (use_ssl == false): Request packet version was invalid!");
 			return -1;
 		}
@@ -2042,6 +2141,10 @@ int read_packet(int sock, void *ssl_ptr, v2_packet * v2_pkt, v3_packet ** v3_pkt
 			tot_bytes += rc;
 
 			buffer_size = ntohl(buffer_size);
+			if (buffer_size < 0 || buffer_size > INT_MAX - pkt_size) {
+				logit(LOG_ERR, "Error: (use_ssl == false): Received packet with invalid buffer size");
+				return -1;
+			}
 			pkt_size += buffer_size;
 			if ((*v3_pkt = calloc(1, pkt_size)) == NULL) {
 				logit(LOG_ERR, "Error: (use_ssl == false): Could not allocate memory for packet");
@@ -2068,16 +2171,34 @@ int read_packet(int sock, void *ssl_ptr, v2_packet * v2_pkt, v3_packet ** v3_pkt
 #ifdef HAVE_SSL
 	else {
 		SSL      *ssl = (SSL *) ssl_ptr;
+		int       sockfd, retval;
+		fd_set    rfds;
+		struct timeval timeout;
 
-		while (((rc = SSL_read(ssl, v2_pkt, bytes_to_recv)) <= 0)
-			   && (SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ)) {
-		}
+		sockfd = SSL_get_fd(ssl);
+
+		FD_ZERO(&rfds);
+		FD_SET(sockfd, &rfds);
+
+		timeout.tv_sec = connection_timeout;
+		timeout.tv_usec = 0;
+
+		do {
+			retval = select(sockfd + 1, &rfds, NULL, NULL, &timeout);
+
+			if (retval > 0) {
+				rc = SSL_read(ssl, v2_pkt, bytes_to_recv);
+			} else {
+				logit(LOG_ERR, "Error (!log_opts): Could not complete SSL_read with %s: timeout %d seconds", remote_host, connection_timeout);
+				return -1;
+			}
+		} while (SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ);
 
 		if (rc <= 0 || rc != bytes_to_recv)
 			return -1;
 
 		packet_ver = ntohs(v2_pkt->packet_version);
-		if (packet_ver != NRPE_PACKET_VERSION_2 && packet_ver != NRPE_PACKET_VERSION_3) {
+		if (packet_ver != NRPE_PACKET_VERSION_2 && packet_ver != NRPE_PACKET_VERSION_3 && packet_ver != NRPE_PACKET_VERSION_4) {
 			logit(LOG_ERR, "Error: (use_ssl == true): Request packet version was invalid!");
 			return -1;
 		}
@@ -2086,7 +2207,13 @@ int read_packet(int sock, void *ssl_ptr, v2_packet * v2_pkt, v3_packet ** v3_pkt
 			buffer_size = sizeof(v2_packet) - common_size;
 			buff_ptr = (char *)v2_pkt + common_size;
 		} else {
-			int32_t   pkt_size = sizeof(v3_packet) - 1;
+			int32_t   pkt_size = sizeof(v3_packet);
+			if (packet_ver == NRPE_PACKET_VERSION_3) {
+				pkt_size -= NRPE_V3_PACKET_SIZE_OFFSET;
+			}
+			else if (packet_ver == NRPE_PACKET_VERSION_4) {
+				pkt_size -= NRPE_V4_PACKET_SIZE_OFFSET;
+			}
 
 			/* Read the alignment filler */
 			bytes_to_recv = sizeof(int16_t);
@@ -2109,6 +2236,9 @@ int read_packet(int sock, void *ssl_ptr, v2_packet * v2_pkt, v3_packet ** v3_pkt
 			tot_bytes += rc;
 
 			buffer_size = ntohl(buffer_size);
+			if (buffer_size < 0 || buffer_size > INT_MAX - pkt_size) {
+				logit(LOG_ERR, "Error: (use_ssl == true): Received packet with invalid buffer size");
+			}
 			pkt_size += buffer_size;
 			if ((*v3_pkt = calloc(1, pkt_size)) == NULL) {
 				logit(LOG_ERR, "Error: (use_ssl == true): Could not allocate memory for packet");
@@ -2611,6 +2741,7 @@ int validate_request(v2_packet * v2pkt, v3_packet * v3pkt)
 {
 	u_int32_t	packet_crc32;
 	u_int32_t	calculated_crc32;
+	int32_t		pkt_size, buffer_size;
 	char		*buff, *ptr;
 	int			rc;
 #ifdef ENABLE_COMMAND_ARGUMENTS
@@ -2618,8 +2749,18 @@ int validate_request(v2_packet * v2pkt, v3_packet * v3pkt)
 #endif
 
 	/* check the crc 32 value */
-	if (packet_ver == NRPE_PACKET_VERSION_3) {
-		int32_t   pkt_size = (sizeof(v3_packet) - 1) + ntohl(v3pkt->buffer_length);
+	if (packet_ver >= NRPE_PACKET_VERSION_3) {
+
+		buffer_size = ntohl(v3pkt->buffer_length);
+		if (buffer_size < 0 || buffer_size > INT_MAX - pkt_size) {
+			logit(LOG_ERR, "Error: Request packet had invalid buffer size.");
+			return ERROR;
+		}
+
+		pkt_size = sizeof(v3_packet);
+		pkt_size -= (packet_ver == NRPE_PACKET_VERSION_3 ? NRPE_V3_PACKET_SIZE_OFFSET : NRPE_V4_PACKET_SIZE_OFFSET);
+		pkt_size += buffer_size;
+
 		packet_crc32 = ntohl(v3pkt->crc32_value);
 		v3pkt->crc32_value = 0L;
 		v3pkt->alignment = 0;
@@ -2642,7 +2783,7 @@ int validate_request(v2_packet * v2pkt, v3_packet * v3pkt)
 	}
 
 	/* make sure buffer is terminated */
-	if (packet_ver == NRPE_PACKET_VERSION_3) {
+	if (packet_ver >= NRPE_PACKET_VERSION_3) {
 		int32_t   l = ntohs(v3pkt->buffer_length);
 		v3pkt->buffer[l - 1] = '\x0';
 		buff = v3pkt->buffer;
@@ -2658,7 +2799,7 @@ int validate_request(v2_packet * v2pkt, v3_packet * v3pkt)
 	}
 
 	/* make sure request doesn't contain nasties */
-	if (packet_ver == NRPE_PACKET_VERSION_3)
+	if (packet_ver >= NRPE_PACKET_VERSION_3)
 		rc = contains_nasty_metachars(v3pkt->buffer);
 	else
 		rc = contains_nasty_metachars(v2pkt->buffer);
@@ -2668,7 +2809,7 @@ int validate_request(v2_packet * v2pkt, v3_packet * v3pkt)
 	}
 
 	/* make sure the request doesn't contain arguments */
-	if (strchr(v2pkt->buffer, '!')) {
+	if (strchr(buff, '!')) {
 #ifdef ENABLE_COMMAND_ARGUMENTS
 		if (allow_arguments == FALSE) {
 			logit(LOG_ERR, "Error: Request contained command arguments, but argument option is not enabled!");

+ 1 - 1
startup/default-xinetd.in

@@ -10,6 +10,6 @@ service nrpe
     group           = @nrpe_group@
     server          = @sbindir@/nrpe
     server_args     = -c @pkgsysconfdir@/nrpe.cfg --inetd
-    only_from       = 127.0.0.1
+    only_from       = 127.0.0.1 ::1
     log_on_success  = 
 }

+ 6 - 2
startup/openrc-init.in

@@ -3,15 +3,19 @@
 # Copyright (c) 2017 Nagios(R) Core(TM) Development Team
 #
 
+# Supply a default value for NRPE_CFG in case the corresponding
+# conf.d file is not installed.
+: ${NRPE_CFG:="@sysconfdir@/nrpe.cfg"}
+
 command="@sbindir@/nrpe"
 command_args="--config=${NRPE_CFG} ${NRPE_OPTS}"
 command_args_background="--daemon"
 description="Nagios Remote Plugin Executor (NRPE) daemon"
 extra_started_commands="reload"
-pidfile="@piddir@/nrpe.pid"
+pidfile="@piddir@/${RC_SVCNAME}.pid"
 
 reload() {
-    ebegin "Reloading ${SVCNAME}"
+    ebegin "Reloading ${RC_SVCNAME}"
     start-stop-daemon --signal HUP --pidfile "${pidfile}"
     eend $?
 }