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