Package: nagios-nrpe-server
Version: <= 2.12
Severity: important
Tags: patch

The SSL option of the NRPE plugin and server does not perform any kind of 
authentication. It has no certificates, only a DH key, which is generated at 
compile time.

This means that the nrpe key is identical to all debian systems, but subject to 
change every time the package maintainer uses the  ./configure script.

My patch adds full SSL certificate verification to nrpe. Note that this breaks 
backwards commandline compatibility, because the previous mode was insecure. 
This means that the check_nrpe rules must be edited in the nagios configuration 
as well.




-- System Information:
Debian Release: 5.0.3
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 2.6.30-1-amd64 (SMP w/2 CPU cores)
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash
diff -urN nagios-nrpe-2.12/src/check_nrpe.c nagios-nrpe-2.12.fullssl/src/check_nrpe.c
--- nagios-nrpe-2.12/src/check_nrpe.c	2008-03-10 22:04:43.000000000 +0100
+++ nagios-nrpe-2.12.fullssl/src/check_nrpe.c	2009-09-17 02:41:42.000000000 +0200
@@ -38,10 +38,12 @@
 int show_version=FALSE;
 
 #ifdef HAVE_SSL
-SSL_METHOD *meth;
 SSL_CTX *ctx;
 SSL *ssl;
 int use_ssl=TRUE;
+char *cert_file=NULL;
+char *cacert_file=NULL;
+char *privatekey_file=NULL;
 #else
 int use_ssl=FALSE;
 #endif
@@ -77,25 +79,28 @@
 		printf("Last Modified: %s\n",MODIFICATION_DATE);
 		printf("License: GPL v2 with exemptions (-l for more info)\n");
 #ifdef HAVE_SSL
-		printf("SSL/TLS Available: Anonymous DH Mode, OpenSSL 0.9.6 or higher required\n");
+		printf("SSL/TLS Available, OpenSSL 0.9.6 or higher required\n");
 #endif
 		printf("\n");
 	        }
 
 	if(result!=OK || show_help==TRUE){
 
-		printf("Usage: check_nrpe -H <host> [-n] [-u] [-p <port>] [-t <timeout>] [-c <command>] [-a <arglist...>]\n");
+		printf("Usage: check_nrpe -H <host> -C <certificate> -k <key> -r <ca-certificate> [-n] [-u] [-p <port>] [-t <timeout>] [-c <command>] [-a <arglist...>]\n");
 		printf("\n");
 		printf("Options:\n");
-		printf(" -n         = Do no use SSL\n");
-		printf(" -u         = Make socket timeouts return an UNKNOWN state instead of CRITICAL\n");
-		printf(" <host>     = The address of the host running the NRPE daemon\n");
-		printf(" [port]     = The port on which the daemon is running (default=%d)\n",DEFAULT_SERVER_PORT);
-		printf(" [timeout]  = Number of seconds before connection times out (default=%d)\n",DEFAULT_SOCKET_TIMEOUT);
-		printf(" [command]  = The name of the command that the remote daemon should run\n");
-		printf(" [arglist]  = Optional arguments that should be passed to the command.  Multiple\n");
-		printf("              arguments should be separated by a space.  If provided, this must be\n");
-		printf("              the last option supplied on the command line.\n");
+		printf(" -n               = Do no use SSL\n");
+		printf(" -u               = Make socket timeouts return an UNKNOWN state instead of CRITICAL\n");
+		printf(" <host>           = The address of the host running the NRPE daemon\n");
+		printf(" <certificate>    = The client certificate to use\n");
+		printf(" <ca-certificate> = The client certificate to use\n");
+		printf(" <key>            = The private key to be used with the certificate\n");
+		printf(" [port]           = The port on which the daemon is running (default=%d)\n",DEFAULT_SERVER_PORT);
+		printf(" [timeout]        = Number of seconds before connection times out (default=%d)\n",DEFAULT_SOCKET_TIMEOUT);
+		printf(" [command]        = The name of the command that the remote daemon should run\n");
+		printf(" [arglist]        = Optional arguments that should be passed to the command.  Multiple\n");
+		printf("                    arguments should be separated by a space. If provided, this must be\n");
+		printf("                    the last option supplied on the command line.\n");
 		printf("\n");
 		printf("Note:\n");
 		printf("This plugin requires that you have the NRPE daemon running on the remote host.\n");
@@ -121,11 +126,10 @@
 #ifdef HAVE_SSL
 	/* initialize SSL */
 	if(use_ssl==TRUE){
-		SSL_library_init();
-		SSLeay_add_ssl_algorithms();
-		meth=SSLv23_client_method();
 		SSL_load_error_strings();
-		if((ctx=SSL_CTX_new(meth))==NULL){
+		SSL_library_init();
+		ctx = SSL_CTX_new(SSLv23_client_method());
+		if(ctx == NULL){
 			printf("CHECK_NRPE: Error - could not create SSL context.\n");
 			exit(STATE_CRITICAL);
 		        }
@@ -133,6 +137,19 @@
 		/* ADDED 01/19/2004 */
 		/* use only TLSv1 protocol */
 		SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+		if (SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM) <= 0) {
+			ERR_print_errors_fp(stderr);
+			exit(1);
+			}
+		if (SSL_CTX_use_PrivateKey_file(ctx, privatekey_file, SSL_FILETYPE_PEM) <= 0) {
+			ERR_print_errors_fp(stderr);
+			exit(1);
+			}
+		if (SSL_CTX_load_verify_locations(ctx, cacert_file, NULL) <= 0) {
+			ERR_print_errors_fp(stderr);
+			exit(1);
+			}
                 }
 #endif
 
@@ -149,7 +166,9 @@
 	/* do SSL handshake */
 	if(result==STATE_OK && use_ssl==TRUE){
 		if((ssl=SSL_new(ctx))!=NULL){
-			SSL_CTX_set_cipher_list(ctx,"ADH");
+			X509 *peer;
+			char peer_CN[256];
+
 			SSL_set_fd(ssl,sd);
 			if((rc=SSL_connect(ssl))!=1){
 				printf("CHECK_NRPE: Error - Could not complete SSL handshake.\n");
@@ -165,7 +184,24 @@
 #endif
 				result=STATE_CRITICAL;
 			        }
-		        }
+			if ((peer = SSL_get_peer_certificate(ssl))) {
+				if (SSL_get_verify_result(ssl) != X509_V_OK) {
+					printf("CHECK_NRPE: Error - Failed to verify server certificate.\n");
+					result = STATE_CRITICAL;
+					}
+				X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
+						NID_commonName, peer_CN, sizeof(peer_CN));
+				if (strcmp(peer_CN, server_name) != 0) {
+					printf("CHECK_NRPE: Error - Common Name in server certificate does not match host name.\n");
+					result = STATE_CRITICAL;
+					}
+				free(peer);
+				}
+			else {
+				printf("CHECK_NRPE: Error - Failed to get peer certificate.\n");
+				result = STATE_CRITICAL;
+				}
+			}
 		else{
 			printf("CHECK_NRPE: Error - Could not create SSL connection structure.\n");
 			result=STATE_CRITICAL;
@@ -184,7 +220,7 @@
 	if(result==STATE_OK){
 
 		/* clear the packet buffer */
-		bzero(&send_packet,sizeof(send_packet));
+		memset(&send_packet, 0, sizeof(send_packet));
 
 		/* fill the packet with semi-random data */
 		randomize_buffer((char *)&send_packet,sizeof(send_packet));
@@ -257,7 +293,7 @@
 
 		/* receive underflow */
 		else if(bytes_to_recv<sizeof(receive_packet)){
-			printf("CHECK_NRPE: Receive underflow - only %d bytes received (%d expected).\n",bytes_to_recv,sizeof(receive_packet));
+			printf("CHECK_NRPE: Receive underflow - only %d bytes received (%ld expected).\n",bytes_to_recv,sizeof(receive_packet));
 			return STATE_UNKNOWN;
 		        }
 
@@ -321,6 +357,9 @@
 	static struct option long_options[]={
 		{"host", required_argument, 0, 'H'},
 		{"command", required_argument, 0, 'c'},
+		{"ca-certificate", required_argument, 0, 'r'},
+		{"certificate", required_argument, 0, 'C'},
+		{"privatekey", required_argument, 0, 'k'},
 		{"args", required_argument, 0, 'a'},
 		{"no-ssl", no_argument, 0, 'n'},
 		{"unknown-timeout", no_argument, 0, 'u'},
@@ -336,7 +375,7 @@
 	if(argc<2)
 		return ERROR;
 
-	snprintf(optchars,MAX_INPUT_BUFFER,"H:c:a:t:p:nuhl");
+	snprintf(optchars,MAX_INPUT_BUFFER,"H:C:k:r:c:a:t:p:nuhl");
 
 	while(1){
 #ifdef HAVE_GETOPT_LONG
@@ -373,6 +412,15 @@
 		case 'H':
 			server_name=strdup(optarg);
 			break;
+		case 'C':
+			cert_file=strdup(optarg);
+			break;
+		case 'r':
+			cacert_file=strdup(optarg);
+			break;
+		case 'k':
+			privatekey_file=strdup(optarg);
+			break;
 		case 'c':
 			command_name=strdup(optarg);
 			break;
@@ -414,6 +462,8 @@
 	if(server_name==NULL && show_help==FALSE && show_version==FALSE  && show_license==FALSE)
 		return ERROR;
 
+	if ((cert_file==NULL || cacert_file==NULL || privatekey_file==NULL) && use_ssl && show_help==FALSE && show_version==FALSE && show_license==FALSE)
+		return ERROR;
 
 	return OK;
         }
diff -urN nagios-nrpe-2.12/src/nrpe.c nagios-nrpe-2.12.fullssl/src/nrpe.c
--- nagios-nrpe-2.12/src/nrpe.c	2008-03-10 22:04:43.000000000 +0100
+++ nagios-nrpe-2.12.fullssl/src/nrpe.c	2009-09-17 02:08:11.000000000 +0200
@@ -24,7 +24,7 @@
 #include "../include/utils.h"
 
 #ifdef HAVE_SSL
-#include "../include/dh.h"
+#include <ssl.h>
 #endif
 
 #ifdef HAVE_LIBWRAP
@@ -33,7 +33,6 @@
 #endif
 
 #ifdef HAVE_SSL
-SSL_METHOD *meth;
 SSL_CTX *ctx;
 int use_ssl=TRUE;
 #else
@@ -56,6 +55,11 @@
 int     connection_timeout=DEFAULT_CONNECTION_TIMEOUT;
 char    *command_prefix=NULL;
 
+/* SSL/TLS additions */
+char	*cert_file=NULL;
+char	*cacert_file=NULL;
+char	*privatekey_file=NULL;
+
 command *command_list=NULL;
 
 char    *nrpe_user=NULL;
@@ -87,11 +91,6 @@
 	int x;
 	char buffer[MAX_INPUT_BUFFER];
 	char *env_string=NULL;
-#ifdef HAVE_SSL
-	DH *dh;
-	char seedfile[FILENAME_MAX];
-	int i,c;
-#endif
 
 	/* set some environment variables */
 	asprintf(&env_string,"NRPE_MULTILINESUPPORT=1");
@@ -206,46 +205,40 @@
 #ifdef HAVE_SSL
 	/* initialize SSL */
 	if(use_ssl==TRUE){
-		SSL_library_init();
-		SSLeay_add_ssl_algorithms();
-		meth=SSLv23_server_method();
 		SSL_load_error_strings();
+		SSL_library_init();
 
-		/* use week random seed if necessary */
-		if(allow_weak_random_seed && (RAND_status()==0)){
-
-			if(RAND_file_name(seedfile,sizeof(seedfile)-1))
-				if(RAND_load_file(seedfile,-1))
-					RAND_write_file(seedfile);
-
-			if(RAND_status()==0){
-				syslog(LOG_ERR,"Warning: SSL/TLS uses a weak random seed which is highly discouraged");
-				srand(time(NULL));
-				for(i=0;i<500 && RAND_status()==0;i++){
-					for(c=0;c<sizeof(seedfile);c+=sizeof(int)){
-						*((int *)(seedfile+c))=rand();
-					        }
-					RAND_seed(seedfile,sizeof(seedfile));
-					}
-				}
-			}
-
-		if((ctx=SSL_CTX_new(meth))==NULL){
+		ctx = SSL_CTX_new(SSLv23_server_method());
+		if (ctx == NULL) {
 			syslog(LOG_ERR,"Error: could not create SSL context.\n");
+			SSL_CTX_free(ctx);
 			exit(STATE_CRITICAL);
-		        }
+			}
 
 		/* ADDED 01/19/2004 */
 		/* use only TLSv1 protocol */
 		SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 
-		/* use anonymous DH ciphers */
-		SSL_CTX_set_cipher_list(ctx,"ADH");
-		dh=get_dh512();
-		SSL_CTX_set_tmp_dh(ctx,dh);
-		DH_free(dh);
+		if(!SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM)) {
+			SSL_CTX_free(ctx);
+			syslog(LOG_ERR, "Error: could not use certificate file '%s'.\n", cert_file);
+			exit(STATE_CRITICAL);
+			}
+		if (!SSL_CTX_use_PrivateKey_file(ctx, privatekey_file, SSL_FILETYPE_PEM)) {
+			SSL_CTX_free(ctx);
+			syslog(LOG_ERR, "Error: could not use private key file '%s'.\n", privatekey_file);
+			exit(STATE_CRITICAL);
+			}
+		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
+		if (!SSL_CTX_load_verify_locations(ctx, cacert_file, NULL)) {
+			SSL_CTX_free(ctx);
+			syslog(LOG_ERR, "Error: could not use CA certificate '%s'.\n", cacert_file);
+			exit(STATE_CRITICAL);
+			}
+		
 		if(debug==TRUE)
 			syslog(LOG_INFO,"INFO: SSL/TLS initialized. All network traffic will be encrypted.");
+
 	        }
 	else{
 		if(debug==TRUE)
@@ -502,6 +495,15 @@
 		else if(!strcmp(varname,"pid_file"))
 			pid_file=strdup(varvalue);
 
+		else if(!strcmp(varname,"cert_file"))
+			cert_file=strdup(varvalue);
+		
+		else if(!strcmp(varname,"cacert_file"))
+			cacert_file=strdup(varvalue);
+
+		else if(!strcmp(varname,"privatekey_file"))
+			privatekey_file=strdup(varvalue);
+
 		else if(!strcmp(varname,"log_facility")){
 			if((get_log_facility(varvalue))==OK){
 				/* re-open log using new facility */
@@ -698,7 +700,7 @@
 	struct sockaddr_in *nptr;
 	struct sockaddr addr;
 	int rc;
-	int sock, new_sd;
+	int sock, new_sd=0;
 	socklen_t addrlen;
 	pid_t pid;
 	int flag=1;
@@ -730,7 +732,7 @@
 
 	myname.sin_family=AF_INET;
 	myname.sin_port=htons(server_port);
- 	bzero(&myname.sin_zero,8);
+ 	memset(&myname.sin_zero, 0, 8);
 
 	/* what address should we bind to? */
         if(!strlen(server_address))
@@ -1087,7 +1089,7 @@
 		rc=recvall(sock,(char *)&receive_packet,&bytes_to_recv,socket_timeout);
 #ifdef HAVE_SSL
 	else{
-                while(((rc=SSL_read(ssl,&receive_packet,bytes_to_recv))<=0) && (SSL_get_error(ssl,rc)==SSL_ERROR_WANT_READ));
+	        while(((rc=SSL_read(ssl,&receive_packet,bytes_to_recv))<=0) && (SSL_get_error(ssl,rc)==SSL_ERROR_WANT_READ));
 		}
 #endif
 
@@ -1245,7 +1247,7 @@
 		buffer[strlen(buffer)-1]='\x0';
 
 	/* clear the response packet buffer */
-	bzero(&send_packet,sizeof(send_packet));
+	memset(&send_packet, 0, sizeof(send_packet));
 
 	/* fill the packet with semi-random data */
 	randomize_buffer((char *)&send_packet,sizeof(send_packet));

Reply via email to