Bladeren bron

Second big update for SSL/TLS. Mostly for check_nrpe, but
includes some changes to nrpe and the changes for the
nrpe.cfg file.

John C. Frickson 10 jaren geleden
bovenliggende
commit
69d18b692d
3 gewijzigde bestanden met toevoegingen van 430 en 153 verwijderingen
  1. 70 0
      sample-config/nrpe.cfg.in
  2. 225 108
      src/check_nrpe.c
  3. 135 45
      src/nrpe.c

+ 70 - 0
sample-config/nrpe.cfg.in

@@ -186,6 +186,76 @@ connection_timeout=300
 
 
 
+# SSL/TLS OPTIONS
+# These directives allow you to specify how to use SSL/TLS.
+
+# SSL VERSION
+# This can be any of: SSLv2 (only use SSLv2), SSLv2+ (use any version),
+#        SSLv3 (only use SSLv3), SSLv3+ (use SSLv3 or above), TLSv1 (only use
+#        TLSv1), TLSv1+ (use TLSv1 or above), TLSv1.1 (only use TLSv1.1),
+#        TLSv1.1+ (use TLSv1.1 or above), TLSv1.2 (only use TLSv1.2),
+#        TLSv1.2+ (use TLSv1.2 or above)
+# If an "or above" version is used, the best will be negotiated. So if both
+# ends are able to do TLSv1.2 and use specify SSLv2, you will get TLSv1.2.
+
+#ssl_version=TLSv1+
+
+# SSL USE ADH
+# This is for backward compatibility and is DEPRECATED. Set to 1 to enable
+# ADH or 2 to require ADH. 1 is currently the default but will be changed
+# in a later version.
+
+#ssl_use_adh=1
+
+# SSL CIPHER LIST
+# This lists which ciphers can be used. For backward compatibility, this
+# defaults to 'ssl_cipher_list=ALL:!MD5:@STRENGTH' in this version but
+# will be changed to something something else in a later version of NRPE.
+
+#ssl_cipher_list=ALL:!MD5:@STRENGTH
+
+# SSL Certificate and Private Key Files
+
+#ssl_cacert_file=@sharedstatedir@/ssl/ca-cert.pem
+#ssl_cert_file=@sharedstatedir@/ssl/nagios-cert.pem
+#ssl_privatekey_file=@sharedstatedir@/ssl/nagios-key.pem
+
+
+# SSL CLIENT CERTS
+# This options determines client certificate usage.
+# Values: 0 = Don't ask for or require client certificates
+#         1 = Ask for client certificates
+#         2 = Require client certificates
+
+#ssl_client_certs=0
+
+# SSL LOGGING
+# This option determines which SSL messages are send to syslog. OR values
+# together to specify multiple options.
+
+# Values: 0  = No additional logging (default)
+#         1  = Log startup SSL/TLS parameters
+#         2  = Log SSL/TLS version of connections
+#         4  = Log which cipher is being used for the connection
+#         8  = Log if client has a certificate
+#         16 = Log details of client's certificate if it has one
+#         -1 = All of the above
+
+#ssl_logging=0
+
+# SSL ADH KEY
+# Set this option to use a different Diffie-Hellman key. The argument is either
+# a path to a file containing the key, or a base-64 encoded key. The key from the
+# file or the base-64 decoded key must be a 512, 1024, 2048 or 4096-bit binary
+# key. Recent versions of openSSL require 2048 or 4096 bit keys or it will refuse
+# to use DH. This directive is DEPRECATED and will be removed in a new version.
+# The sample in the second example is a 512-bit key. DO NOT USE THE SAMPLE KEY!
+
+#ssl_adh_key=@sharedstatedir@/ssl/nrpe_adh.key
+#ssl_adh_key=B64:MEYCQQDGl8SHFLW//3CY9H6R3ZnbB68UAHv59aGwatTT0KHrfono/StMIcPtqSsjjioWWugxt91tECG+GTMgBADLiXZTAgEC  DON'T USE THIS ONE!
+
+
+
 # INCLUDE CONFIG FILE
 # This directive allows you to include definitions from an external config file.
 

+ 225 - 108
src/check_nrpe.c

@@ -53,6 +53,25 @@ int use_ssl=TRUE;
 int use_ssl=FALSE;
 #endif
 
+/* 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
+				} SslVer;
+typedef enum _CLNT_CERTS {
+					Ask_For_Cert = 1, Require_Cert = 2, Log_Certs = 4
+				} ClntCerts;
+struct _SSL_PARMS {
+	char	*cert_file;
+	char	*cacert_file;
+	char	*privatekey_file;
+	char    cipher_list[MAX_FILENAME_LENGTH];
+	unsigned char	*adh_key;
+	int		adhk_len;
+	SslVer	ssl_min_ver;
+	int		allowDH;
+	int		client_certs;
+} sslprm = { NULL, NULL, NULL, "ALL:!MD5:@STRENGTH", NULL, 0, TLSv1_plus, TRUE, 0 };
+
 
 int process_arguments(int,char **);
 void alarm_handler(int);
@@ -65,7 +84,7 @@ int main(int argc, char **argv){
         u_int32_t packet_crc32;
         u_int32_t calculated_crc32;
 	int16_t result;
-	int rc;
+	int rc, ssl_opts = SSL_OP_ALL, vrfy;
 	packet send_packet;
 	packet receive_packet;
 	int bytes_to_send;
@@ -87,28 +106,45 @@ int main(int argc, char **argv){
 		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> [ -b <bindaddr> ] [-4] [-6] [-n] [-u] [-p <port>] [-t <timeout>] [-c <command>] [-a <arglist...>]\n");
+		printf("Usage: check_nrpe -H <host> [-4] [-6] [-n] [-u] [-V] [-l] [-d]\n"
+			"       [-D <adh-key>] [-S <ssl version>  [-L <cipherlist>] [-C <clientcert>]\n"
+			"       [-K <key>] [-A <ca-certificate>] [-b <bindaddr>] [-p <port>]\n"
+			"       [-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(" <bindaddr> = bind to local address\n");
-		printf(" -4         = bind to ipv4 only\n");
-		printf(" -6         = bind to ipv6 only\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(" <host>       = The address of the host running the NRPE daemon\n");
+		printf(" -4           = bind to ipv4 only\n");
+		printf(" -6           = bind to ipv6 only\n");
+		printf(" -n           = Do no use SSL\n");
+		printf(" -u           = Make socket timeouts return UNKNOWN state instead of CRITICAL\n");
+		printf(" -V           = Show version\n");
+		printf(" -l           = Show license\n");
+		printf(" -d           = Don't use Anonymous Diffie Hellman\n");
+		printf("                (This will be the default in a future release.)\n");
+		printf(" <adh-key>    = Key to use for Anonymous Diffie Hellman\n");
+		printf(" <bindaddr>   = bind to local address\n");
+		printf(" <ssl ver>    = The SSL/TLS version to use. Can be any one of: SSLv2 (only),\n");
+		printf("                SSLv2+ (or above), SSLv3 (only), SSLv3+ (or above),\n");
+		printf("                TLSv1 (only), TLSv1+ (or above DEFAULT), TLSv1.1 (only),\n");
+		printf("                TLSv1.1+ (or above), TLSv1.2 (only), TLSv1.2+ (or above)\n");
+		printf(" <cipherlist> = The list of SSL ciphers to use (currently defaults\n");
+		printf("                to \"ALL:!MD5:@STRENGTH\". WILL change in a future release.)\n");
+		printf(" <clientcert> = The client certificate to use for PKI\n");
+		printf(" <key>        = The private key to use with the client certificate\n");
+		printf(" <ca-cert>    = The CA certificate to use for PKI\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,\n");
+		printf("                separated by a space.  If provided, this must be the last\n");
+		printf("                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");
@@ -133,20 +169,72 @@ int main(int argc, char **argv){
 
 #ifdef HAVE_SSL
 	/* initialize SSL */
-	if(use_ssl==TRUE){
-		SSL_library_init();
-		SSLeay_add_ssl_algorithms();
-		meth=SSLv23_client_method();
+	if(use_ssl==TRUE) {
 		SSL_load_error_strings();
-		if((ctx=SSL_CTX_new(meth))==NULL){
+		SSL_library_init();
+		meth = SSLv23_client_method();
+
+#ifndef OPENSSL_NO_SSL2
+		if (sslprm.ssl_min_ver == SSLv2)
+			meth = SSLv2_server_method();
+#endif
+#ifndef OPENSSL_NO_SSL3
+		if (sslprm.ssl_min_ver == SSLv3)
+			meth = SSLv3_server_method();
+#endif
+		if (sslprm.ssl_min_ver == TLSv1)
+			meth = TLSv1_server_method();
+		if (sslprm.ssl_min_ver == TLSv1_1)
+			meth = TLSv1_1_server_method();
+		if (sslprm.ssl_min_ver == TLSv1_2)
+			meth = TLSv1_2_server_method();
+
+		if ((ctx = SSL_CTX_new(meth)) == NULL) {
 			printf("CHECK_NRPE: Error - could not create SSL context.\n");
 			exit(STATE_CRITICAL);
-		        }
+        }
+
+		if (sslprm.ssl_min_ver >= SSLv3) {
+			ssl_opts |= SSL_OP_NO_SSLv2;
+			if (sslprm.ssl_min_ver >= TLSv1)
+				ssl_opts |= SSL_OP_NO_SSLv3;
+		}
+		SSL_CTX_set_options(ctx, ssl_opts);
+
+		if (sslprm.cert_file != NULL && sslprm.privatekey_file != NULL) {
+			if (!SSL_CTX_use_certificate_file(ctx, sslprm.cert_file, SSL_FILETYPE_PEM)) {
+				SSL_CTX_free(ctx);
+				syslog(LOG_ERR, "Error: could not use certificate file '%s'.\n", sslprm.cert_file);
+				exit(STATE_CRITICAL);
+			}
+			if (!SSL_CTX_use_PrivateKey_file(ctx, sslprm.privatekey_file, SSL_FILETYPE_PEM)) {
+				SSL_CTX_free(ctx);
+				syslog(LOG_ERR, "Error: could not use private key file '%s'.\n", sslprm.privatekey_file);
+				exit(STATE_CRITICAL);
+			}
+		}
+
+		if (sslprm.cacert_file != NULL) {
+			if (!SSL_CTX_load_verify_locations(ctx, sslprm.cacert_file, NULL)) {
+				SSL_CTX_free(ctx);
+				syslog(LOG_ERR, "Error: could not use CA certificate '%s'.\n", sslprm.cacert_file);
+				exit(STATE_CRITICAL);
+			}
+			vrfy = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+			SSL_CTX_set_verify(ctx, vrfy, NULL);
+		}
+
+		if (!sslprm.allowDH) {
+			if (strlen(sslprm.cipher_list) < sizeof(sslprm.cipher_list) - 6)
+				strcat(sslprm.cipher_list, ":!ADH");
+		}
 
-		/* ADDED 01/19/2004 */
-		/* use only TLSv1 protocol */
-		SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-                }
+		if (SSL_CTX_set_cipher_list(ctx, sslprm.cipher_list) == 0) {
+			SSL_CTX_free(ctx);
+			syslog(LOG_ERR, "Error: Could not set SSL/TLS cipher list: %s", sslprm.cipher_list);
+			exit(STATE_CRITICAL);
+		}
+	}
 #endif
 
 	/* initialize alarm signal handling */
@@ -174,44 +262,47 @@ int main(int argc, char **argv){
 
 #ifdef HAVE_SSL
 	/* do SSL handshake */
-	if(result==STATE_OK && use_ssl==TRUE){
-		if((ssl=SSL_new(ctx))!=NULL){
-			SSL_CTX_set_cipher_list(ctx,"ADH");
-			SSL_set_fd(ssl,sd);
-			if((rc=SSL_connect(ssl))!=1){
+	if (result == STATE_OK && use_ssl==TRUE) {
+		if ((ssl = SSL_new(ctx)) != NULL) {
+			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");
 #ifdef DEBUG
-				printf("SSL_connect=%d\n",rc);
+				printf("SSL_connect=%d\n", rc);
 				/*
-				rc=SSL_get_error(ssl,rc);
-				printf("SSL_get_error=%d\n",rc);
-				printf("ERR_get_error=%lu\n",ERR_get_error());
-				printf("%s\n",ERR_error_string(rc,NULL));
+				rc = SSL_get_error(ssl, rc);
+				printf("SSL_get_error=%d\n", rc);
+				printf("ERR_get_error=%lu\n", ERR_get_error());
+				printf("%s\n",ERR_error_string(rc, NULL));
 				*/
 				ERR_print_errors_fp(stdout);
 #endif
 				result=STATE_CRITICAL;
-			        }
-		        }
-		else{
+			}
+
+		} else {
+
 			printf("CHECK_NRPE: Error - Could not create SSL connection structure.\n");
 			result=STATE_CRITICAL;
-		        }
+		}
 
 		/* bail if we had errors */
-		if(result!=STATE_OK){
+		if (result != STATE_OK) {
 			SSL_CTX_free(ctx);
 			close(sd);
 			exit(result);
-		        }
-	        }
+        }
+	}
 #endif
 
 	/* we're connected and ready to go */
 	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));
@@ -271,22 +362,22 @@ int main(int argc, char **argv){
 		graceful_close(sd,1000);
 
 		/* recv() error */
-		if(rc<0){
+		if (rc < 0) {
 			printf("CHECK_NRPE: Error receiving data from daemon.\n");
 			return STATE_UNKNOWN;
-		        }
+		}
 
 		/* server disconnected */
-		else if(rc==0){
+		else if (rc == 0) {
 			printf("CHECK_NRPE: Received 0 bytes from daemon.  Check the remote server logs for error messages.\n");
 			return STATE_UNKNOWN;
-		        }
+		}
 
 		/* 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));
+		else if(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;
-		        }
+		}
 
 		
 		/***** DECRYPT RESPONSE *****/
@@ -337,125 +428,151 @@ int main(int argc, char **argv){
 
 
 /* process command line arguments */
-int process_arguments(int argc, char **argv){
+int process_arguments(int argc, char **argv)
+{
 	char optchars[MAX_INPUT_BUFFER];
-	int argindex=0;
-	int c=1;
-	int i=1;
+	int argindex = 0;
+	int c = 1;
+	int i = 1;
+	int rc;
 
 #ifdef HAVE_GETOPT_LONG
-	int option_index=0;
-	static struct option long_options[]={
-		{"host", required_argument, 0, 'H'},
-		{"bind", required_argument, 0, 'b'},
-		{"command", required_argument, 0, 'c'},
-		{"args", required_argument, 0, 'a'},
-		{"no-ssl", no_argument, 0, 'n'},
-		{"unknown-timeout", no_argument, 0, 'u'},
-		{"ipv4", no_argument, 0, '4'},
-		{"ipv6", no_argument, 0, '6'},
-		{"timeout", required_argument, 0, 't'},
-		{"port", required_argument, 0, 'p'},
-		{"help", no_argument, 0, 'h'},
-		{"license", no_argument, 0, 'l'},
-		{0, 0, 0, 0}
-                };
+	int option_index = 0;
+	static struct option long_options[] = {
+		{ "host",			required_argument,	0, 'H'},
+		{ "bind",			required_argument,	0, 'b'},
+		{ "command",		required_argument,	0, 'c'},
+		{ "args",			required_argument,	0, 'a'},
+		{ "no-ssl",			no_argument,		0, 'n'},
+		{ "unknown-timeout",no_argument,		0, 'u'},
+		{ "ipv4",			no_argument,		0, '4'},
+		{ "ipv6",			no_argument,		0, '6'},
+		{ "no-adh",			no_argument,		0, 'd'},
+		{ "ssl-version",	required_argument,	0, 'S'},
+		{ "cipher-list",	required_argument,	0, 'L'},
+		{ "client-cert",	required_argument,	0, 'C'},
+		{ "key-file",		required_argument,	0, 'K'},
+		{ "ca-cert-file",	required_argument,	0, 'A'},
+		{ "timeout",		required_argument,	0, 't'},
+		{ "port",			required_argument,	0, 'p'},
+		{ "help",			no_argument,		0, 'h'},
+		{ "license",		no_argument,		0, 'l'},
+		{ 0, 0, 0, 0}
+	};
 #endif
 
 	/* no options were supplied */
-	if(argc<2)
+	if (argc < 2)
 		return ERROR;
 
-	snprintf(optchars,MAX_INPUT_BUFFER,"H:b:c:a:t:p:nu46hl");
+	snprintf(optchars, MAX_INPUT_BUFFER, "H:b:c:a:t:p:S:L:C:K:A:D:46dhlnuV");
 
-	while(1){
+	while(1) {
 #ifdef HAVE_GETOPT_LONG
-		c=getopt_long(argc,argv,optchars,long_options,&option_index);
+		c = getopt_long(argc, argv, optchars, long_options, &option_index);
 #else
-		c=getopt(argc,argv,optchars);
+		c = getopt(argc, argv, optchars);
 #endif
-		if(c==-1 || c==EOF || argindex > 0)
+		if (c == -1 || c == EOF || argindex > 0)
 			break;
 
+		rc = nssl_set_opt(prms, c, optarg);
+		if (rc == TRUE)
+			continue;
+		else if (rc == ERROR)
+			return ERROR;
+
 		/* process all arguments */
-		switch(c){
+		switch(c) {
 
 		case '?':
 		case 'h':
-			show_help=TRUE;
+			show_help = TRUE;
 			break;
+
 		case 'b':
-			bind_address=strdup(optarg);
+			bind_address = strdup(optarg);
 			break;
+
 		case 'V':
-			show_version=TRUE;
+			show_version = TRUE;
 			break;
+
 		case 'l':
-			show_license=TRUE;
+			show_license = TRUE;
 			break;
+
 		case 't':
-			socket_timeout=atoi(optarg);
-			if(socket_timeout<=0)
+			socket_timeout = atoi(optarg);
+			if(socket_timeout <= 0)
 				return ERROR;
 			break;
+
 		case 'p':
-			server_port=atoi(optarg);
-			if(server_port<=0)
+			server_port = atoi(optarg);
+			if(server_port <= 0)
 				return ERROR;
 			break;
+
 		case 'H':
-			server_name=strdup(optarg);
+			server_name = strdup(optarg);
 			break;
+
 		case 'c':
-			command_name=strdup(optarg);
+			command_name = strdup(optarg);
 			break;
+
 		case 'a':
-			argindex=optind;
+			argindex = optind;
 			break;
+
 		case 'n':
-			use_ssl=FALSE;
+			use_ssl = FALSE;
 			break;
+
 		case 'u':
-			timeout_return_code=STATE_UNKNOWN;
+			timeout_return_code = STATE_UNKNOWN;
 			break;
+
 		case '4':
-			address_family=AF_INET;
+			address_family = AF_INET;
 			break;
+
 		case '6':
-			address_family=AF_INET6;
+			address_family = AF_INET6;
 			break;
+
 		default:
 			return ERROR;
 			break;
-		        }
-	        }
+		}
+	}
 
 	/* determine (base) command query */
-	snprintf(query,sizeof(query),"%s",(command_name==NULL)?DEFAULT_NRPE_COMMAND:command_name);
-	query[sizeof(query)-1]='\x0';
+	snprintf(query, sizeof(query), "%s", (command_name == NULL) ? DEFAULT_NRPE_COMMAND : command_name);
+	query[sizeof(query)-1] = '\x0';
 
 	/* get the command args */
-	if(argindex>0){
+	if (argindex > 0) {
 
-		for(c=argindex-1;c<argc;c++){
+		for (c = argindex - 1; c < argc; c++) {
 
-			i=sizeof(query)-strlen(query)-2;
-			if(i<=0)
+			i = sizeof(query) - strlen(query) - 2;
+			if (i <= 0)
 				break;
 
-			strcat(query,"!");
-			strncat(query,argv[c],i);
-			query[sizeof(query)-1]='\x0';
-		        }
-	        }
+			strcat(query, "!");
+			strncat(query, argv[c], i);
+			query[sizeof(query) - 1] = '\x0';
+		}
+	}
 
 	/* make sure required args were supplied */
-	if(server_name==NULL && show_help==FALSE && show_version==FALSE  && show_license==FALSE)
+	if (server_name == NULL && show_help == FALSE && show_version == FALSE  && show_license == FALSE)
 		return ERROR;
 
-
 	return OK;
-        }
+}
 
 
 

+ 135 - 45
src/nrpe.c

@@ -83,19 +83,23 @@ typedef enum _SSL_VER { SSLv2 = 1, SSLv2_plus, SSLv3, SSLv3_plus, TLSv1,
 					TLSv1_plus, TLSv1_1, TLSv1_1_plus, TLSv1_2, TLSv1_2_plus
 				} SslVer;
 typedef enum _CLNT_CERTS {
-					Ask_For_Cert = 1, Require_Cert = 2, Log_Certs = 4
+					Ask_For_Cert = 1, Require_Cert = 2
 				} ClntCerts;
+typedef enum _SSL_LOGGING { SSL_NoLogging, SSL_LogStartup, SSL_LogVersion,
+					SSL_LogCipher, SSL_LogIfClientCert, SSL_LogCertDetails
+				} SslLogging;
 struct _SSL_PARMS {
-	char	*cert_file;
-	char	*cacert_file;
-	char	*privatekey_file;
-	char    cipher_list[MAX_FILENAME_LENGTH];
+	char		*cert_file;
+	char		*cacert_file;
+	char		*privatekey_file;
+	char		cipher_list[MAX_FILENAME_LENGTH];
 	unsigned char	*adh_key;
-	int		adhk_len;
-	SslVer	ssl_min_ver;
-	int		allowDH;
-	int		client_certs;
-} sslprm = { NULL, NULL, NULL, "ALL:!MD5:@STRENGTH", NULL, 0, TLSv1_plus, 1, 0 };
+	int			adhk_len;
+	SslVer		ssl_min_ver;
+	int			allowDH;
+	ClntCerts	client_certs;
+	SslLogging	log_opts;
+} sslprm = { NULL, NULL, NULL, "ALL:!MD5:@STRENGTH", NULL, 0, TLSv1_plus, TRUE, 0, 0 };
 
 command *command_list=NULL;
 
@@ -124,7 +128,8 @@ int     use_src=FALSE; /* Define parameter for SRC option */
 int		listen_queue_size=DEFAULT_LISTEN_QUEUE_SIZE;
 
 #ifdef HAVE_SSL
-void complete_SSL_shutdown( SSL *);
+static void complete_SSL_shutdown( SSL *);
+static int verify_callback(int ok, X509_STORE_CTX *ctx);
 #endif
 
 int main(int argc, char **argv){
@@ -217,7 +222,7 @@ int main(int argc, char **argv){
 	/* open a connection to the syslog facility */
 	/* facility name may be overridden later */
 	get_log_facility(NRPE_LOG_FACILITY);
-        openlog("nrpe",LOG_PID,log_facility); 
+	openlog("nrpe",LOG_PID,log_facility); 
 
 	/* make sure the config file uses an absolute path */
 	if(config_file[0]!='/'){
@@ -256,6 +261,33 @@ int main(int argc, char **argv){
 		macro_argv[x]=NULL;
 
 #ifdef HAVE_SSL
+	if (sslprm.log_opts & SSL_LogStartup) {
+		syslog(LOG_INFO, "SSL Certificate File: %s", sslprm.cert_file);
+		syslog(LOG_INFO, "SSL Private Key File: %s", sslprm.privatekey_file);
+		syslog(LOG_INFO, "SSL CA Certificate File: %s", sslprm.cacert_file);
+		syslog(LOG_INFO, "SSL Cipher List: %s", sslprm.cipher_list);
+		syslog(LOG_INFO, "SSL Allow ADH: %s",
+				sslprm.allowDH == 0 ? "No" : (sslprm.allowDH == 1 ? "Allow" : "Require"));
+		syslog(LOG_INFO, "SSL ADH Key: %s", sslprm.adh_key);
+		syslog(LOG_INFO, "SSL Client Certs: %s",
+				sslprm.client_certs == 0 ? "Don't Ask" : (sslprm.client_certs == 1 ? "Accept" : "Require"));
+		syslog(LOG_INFO, "SSL Log Options: %d", sslprm.log_opts);
+		switch (sslprm.ssl_min_ver) {
+			case SSLv2:			env_string = "SSLv2";					break;
+			case SSLv2_plus:	env_string = "SSLv2 And Above";			break;
+			case SSLv3:			env_string = "SSLv3";					break;
+			case SSLv3_plus:	env_string = "SSLv3_plus And Above";	break;
+			case TLSv1:			env_string = "TLSv1";					break;
+			case TLSv1_plus:	env_string = "TLSv1_plus And Above";	break;
+			case TLSv1_1:		env_string = "TLSv1_1";					break;
+			case TLSv1_1_plus:	env_string = "TLSv1_1_plus And Above";	break;
+			case TLSv1_2:		env_string = "TLSv1_2";					break;
+			case TLSv1_2_plus:	env_string = "TLSv1_2_plus And Above";	break;
+			defualt:			env_string = "INVALID VALUE!";			break;
+		}
+		syslog(LOG_INFO, "SSL Version: %s", env_string);
+	}
+
 	/* initialize SSL */
 	if(use_ssl==TRUE){
 		SSL_load_error_strings();
@@ -299,7 +331,7 @@ int main(int argc, char **argv){
 
 		ctx = SSL_CTX_new(meth);
 		if (ctx == NULL) {
-			syslog(LOG_ERR,"Error: could not create SSL context.\n");
+			syslog(LOG_ERR,"Error: could not create SSL context");
 			SSL_CTX_free(ctx);
 			exit(STATE_CRITICAL);
 		}
@@ -314,12 +346,12 @@ int main(int argc, char **argv){
 		if (sslprm.cert_file != NULL) {
 			if (!SSL_CTX_use_certificate_file(ctx, sslprm.cert_file, SSL_FILETYPE_PEM)) {
 				SSL_CTX_free(ctx);
-				syslog(LOG_ERR, "Error: could not use certificate file '%s'.\n", sslprm.cert_file);
+				syslog(LOG_ERR, "Error: could not use certificate file '%s'", sslprm.cert_file);
 				exit(STATE_CRITICAL);
 			}
 			if (!SSL_CTX_use_PrivateKey_file(ctx, sslprm.privatekey_file, SSL_FILETYPE_PEM)) {
 				SSL_CTX_free(ctx);
-				syslog(LOG_ERR, "Error: could not use private key file '%s'.\n", sslprm.privatekey_file);
+				syslog(LOG_ERR, "Error: could not use private key file '%s'", sslprm.privatekey_file);
 				exit(STATE_CRITICAL);
 			}
 		}
@@ -328,10 +360,10 @@ int main(int argc, char **argv){
 			vrfy = SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE;
 			if ((sslprm.client_certs & Require_Cert) != 0)
 				vrfy |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
-			SSL_CTX_set_verify(ctx, vrfy, NULL);
+			SSL_CTX_set_verify(ctx, vrfy, verify_callback);
 			if (!SSL_CTX_load_verify_locations(ctx, sslprm.cacert_file, NULL)) {
 				SSL_CTX_free(ctx);
-				syslog(LOG_ERR, "Error: could not use CA certificate '%s'.\n", sslprm.cacert_file);
+				syslog(LOG_ERR, "Error: could not use CA certificate '%s'", sslprm.cacert_file);
 				exit(STATE_CRITICAL);
 			}
 		}
@@ -348,7 +380,7 @@ int main(int argc, char **argv){
 				SSL_CTX_set_cipher_list(ctx, "ADH");
 			if (sslprm.adh_key != NULL && sslprm.adhk_len > 0) {
 				if ((dh=DH_new()) == NULL) {
-					syslog(LOG_ERR, "Error: could not create DH object\n");
+					syslog(LOG_ERR, "Error: could not create DH object");
 					exit(STATE_CRITICAL);
 				}
 				dh->p = BN_bin2bn(sslprm.adh_key, sslprm.adhk_len, NULL);
@@ -356,13 +388,13 @@ int main(int argc, char **argv){
 				if ((dh->p == NULL) || (dh->g == NULL)) {
 					DH_free(dh);
 					SSL_CTX_free(ctx);
-					syslog(LOG_ERR, "Error: could not create DH object\n");
+					syslog(LOG_ERR, "Error: could not create DH object");
 					exit(STATE_CRITICAL);
 				}
 				SSL_CTX_set_tmp_dh(ctx, dh);
 				DH_free(dh);
 			} else {
-				dh = get_dh512();
+				dh = get_dh2048();
 				SSL_CTX_set_tmp_dh(ctx, dh);
 				DH_free(dh);
 			}
@@ -561,6 +593,32 @@ int main(int argc, char **argv){
 	}
 
 
+int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
+{
+	char		name[256], issuer[256];
+	X509		*err_cert;
+	int			err;
+	SSL			*ssl;
+
+	if (preverify_ok || (sslprm.log_opts & SSL_LogCertDetails == 0))
+		return preverify_ok;
+
+	err_cert = X509_STORE_CTX_get_current_cert(ctx);
+	err = X509_STORE_CTX_get_error(ctx);
+
+	/* Get the pointer to the SSL of the current connection */
+	ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+
+	X509_NAME_oneline(X509_get_subject_name(err_cert), name, 256);
+	X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer, 256);
+
+	if (!preverify_ok && (sslprm.log_opts & SSL_LogCertDetails)) {
+		syslog(LOG_ERR, "SSL Client has an invalid certificate: %s (issuer=%s) err=%d:%s",
+				name, issuer, err, X509_verify_cert_error_string(err));
+	}
+
+	return preverify_ok;
+}
 
 
 /* read in the configuration file */
@@ -759,7 +817,7 @@ int read_config_file(char *filename){
 			else if (!strcmp(varvalue, "TLSv1.2+"))
 				sslprm.ssl_min_ver = TLSv1_2_plus;
 			else {
-				syslog(LOG_ERR, "Invalid ssl version specified in config file '%s' - Line %d\n", filename, line);
+				syslog(LOG_ERR, "Invalid ssl version specified in config file '%s' - Line %d", filename, line);
 				return ERROR;
 			}
 		}
@@ -767,11 +825,14 @@ int read_config_file(char *filename){
 		else if (!strcmp(varname, "ssl_use_adh")) {
 			sslprm.allowDH = atoi(varvalue);
 			if (sslprm.allowDH < 0 || sslprm.allowDH > 2) {
-				syslog(LOG_ERR, "Invalid use adh value specified in config file '%s' - Line %d\n", filename, line);
+				syslog(LOG_ERR, "Invalid use adh value specified in config file '%s' - Line %d", filename, line);
 				return ERROR;
 			}
         }
 
+		else if (!strcmp(varname, "ssl_logging"))
+			sslprm.log_opts = atoi(varvalue);
+
 		else if (!strcmp(varname, "ssl_cipher_list")) {
 			strncpy(sslprm.cipher_list, varvalue, sizeof(sslprm.cipher_list) - 1);
 			sslprm.cipher_list[sizeof(sslprm.cipher_list)-1]='\0';
@@ -789,11 +850,11 @@ int read_config_file(char *filename){
 		else if (!strcmp(varname, "ssl_client_certs")) {
 			sslprm.client_certs = atoi(varvalue);
 			if (sslprm.client_certs < 0 || sslprm.client_certs > 7) {
-				syslog(LOG_ERR, "Invalid client certs value specified in config file '%s' - Line %d\n", filename, line);
+				syslog(LOG_ERR, "Invalid client certs value specified in config file '%s' - Line %d", filename, line);
 				return ERROR;
 			}
 			/* if requiring or logging client certs, make sure "Ask" is turned on */
-			if ((sslprm.client_certs & Require_Cert) || (sslprm.client_certs & Log_Certs))
+			if (sslprm.client_certs & Require_Cert)
 				sslprm.client_certs |= Ask_For_Cert;
 		}
 
@@ -804,39 +865,39 @@ int read_config_file(char *filename){
 			} else {
 				sslprm.adh_key = strdup(varvalue);
 				if (sslprm.adh_key[0] != '/' && sslprm.adh_key[0] != '\\' && strncmp(&sslprm.adh_key[1], ":\\", 2)) {
-					syslog(LOG_ERR, "Invalid ssl adh key value specified in config file '%s' - Line %d\n", filename, line);
+					syslog(LOG_ERR, "Invalid ssl adh key value specified in config file '%s' - Line %d", filename, line);
 					return ERROR;
 				}
 				if ((pskfd = open(sslprm.adh_key, O_RDONLY)) < 0) {
-					syslog(LOG_ERR, "Unable to open adh key file '%s' for reading\n", pskfd);
+					syslog(LOG_ERR, "Unable to open adh key file '%s' for reading", pskfd);
 					return ERROR;
 				}
 				if (fstat(pskfd, &st) < 0) {
 					close(pskfd);
-					syslog(LOG_ERR, "Unable to stat adh key file '%s'\n", pskfd);
+					syslog(LOG_ERR, "Unable to stat adh key file '%s'", pskfd);
 					return ERROR;
 				}
 				if (st.st_mode != S_IFREG) {
 					close(pskfd);
-					syslog(LOG_ERR, "adh key file '%s' is not a regular file\n", pskfd);
+					syslog(LOG_ERR, "adh key file '%s' is not a regular file", pskfd);
 					return ERROR;
 				}
 				if (st.st_size == 0 || st.st_size > 4096) {
 					close(pskfd);
-					syslog(LOG_ERR, "adh key file '%s' is not a valid file\n", pskfd);
+					syslog(LOG_ERR, "adh key file '%s' is not a valid file", pskfd);
 					return ERROR;
 				}
 				if (st.st_size > strlen(sslprm.adh_key)) {
 					if ((sslprm.adh_key = realloc(sslprm.adh_key, st.st_size))  == NULL) {
 						close(pskfd);
-						syslog(LOG_ERR, "Memory allocation error\n");
+						syslog(LOG_ERR, "Memory allocation error");
 						return ERROR;
 					}
 				}
 				sslprm.adhk_len = st.st_size;
 				if (read(pskfd, sslprm.adh_key, sslprm.adhk_len) != sslprm.adhk_len) {
 					close(pskfd);
-					syslog(LOG_ERR, "Error reading adh key file '%s'\n", pskfd);
+					syslog(LOG_ERR, "Error reading adh key file '%s'", pskfd);
 					return ERROR;
 				}
 				close(pskfd);
@@ -1453,6 +1514,8 @@ void handle_connection(int sock){
 #endif
 #ifdef HAVE_SSL
 	SSL *ssl=NULL;
+	const SSL_CIPHER *c;
+	X509 *peer;
 #endif
 #ifdef HAVE_SIGACTION
 	struct sigaction sig_action;
@@ -1482,33 +1545,60 @@ void handle_connection(int sock){
 
 #ifdef HAVE_SSL
 	/* do SSL handshake */
-	if(result==STATE_OK && use_ssl==TRUE){
-		if((ssl=SSL_new(ctx))!=NULL){
+	if (result == STATE_OK && use_ssl == TRUE) {
+		if ((ssl = SSL_new(ctx)) != NULL) {
 			SSL_set_fd(ssl,sock);
 
 			/* keep attempting the request if needed */
-                        while(((rc=SSL_accept(ssl))!=1) && (SSL_get_error(ssl,rc)==SSL_ERROR_WANT_READ));
-
-			if(rc!=1){
-				syslog(LOG_ERR,"Error: Could not complete SSL handshake. %d\n",SSL_get_error(ssl,rc));
+			while (((rc = SSL_accept(ssl)) != 1) && (SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ));
+
+			if (rc != 1) {
+				if (sslprm.log_opts & (SSL_LogCertDetails|SSL_LogIfClientCert)) {
+					while ((x = ERR_get_error_line_data(NULL, NULL, NULL, NULL)) != 0)
+						syslog(LOG_ERR, "Error: Could not complete SSL handshake: %s", ERR_reason_error_string(x));
+				} else
+					syslog(LOG_ERR, "Error: Could not complete SSL handshake. %d", SSL_get_error(ssl,rc));
 #ifdef DEBUG
-				errfp=fopen("/tmp/err.log","w");
+				errfp = fopen("/tmp/err.log", "a");
 				ERR_print_errors_fp(errfp);
 				fclose(errfp);
 #endif
 				return;
-			        }
-		        }
-		else{
-			syslog(LOG_ERR,"Error: Could not create SSL connection structure.\n");
+			}
+			if (sslprm.log_opts & SSL_LogVersion)
+				syslog(LOG_NOTICE, "SSL Version: %s", SSL_get_version(ssl));
+			if (sslprm.log_opts & SSL_LogCipher) {
+				c = SSL_get_current_cipher(ssl);
+				syslog(LOG_NOTICE, "%s, Cipher is %s",
+					   SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c));
+			}
+			if ((sslprm.log_opts & SSL_LogIfClientCert) || (sslprm.log_opts & SSL_LogCertDetails)) {
+				peer = SSL_get_peer_certificate(ssl);
+				if (peer) {
+					if (sslprm.log_opts & SSL_LogIfClientCert)
+						syslog(LOG_NOTICE, "SSL Client has %s certificate",
+								peer->valid ? "a valid" : "an invalid");
+					if (sslprm.log_opts & SSL_LogCertDetails) {
+						syslog(LOG_NOTICE, "SSL Client Cert Name: %s", peer->name);
+						X509_NAME_oneline(X509_get_issuer_name(peer), buffer, sizeof(buffer));
+						syslog(LOG_NOTICE, "SSL Client Cert Issuer: %s", buffer);
+					}
+				} else if (sslprm.client_certs == 0)
+					syslog(LOG_NOTICE, "SSL Not asking for client certification");
+				else
+					syslog(LOG_NOTICE, "SSL Client did not present a certificate");
+			}
+		}
+		else {
+			syslog(LOG_ERR, "Error: Could not create SSL connection structure.");
 #ifdef DEBUG
-			errfp=fopen("/tmp/err.log","w");
+			errfp = fopen("/tmp/err.log", "a");
 			ERR_print_errors_fp(errfp);
 			fclose(errfp);
 #endif
 			return;
-		        }
-	        }
+		}
+	}
 #endif
 
 	bytes_to_recv=sizeof(receive_packet);