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));