Procházet zdrojové kódy

Initial commit of SSL changes.

Trying to keep it backward-compatible but provide for more secure
encryption, including pkc.
John C. Frickson před 10 roky
rodič
revize
47a0bbd880
3 změnil soubory, kde provedl 267 přidání a 22 odebrání
  1. 2 5
      include/utils.h
  2. 219 17
      src/nrpe.c
  3. 46 0
      src/utils.c

+ 2 - 5
include/utils.h

@@ -53,11 +53,8 @@ int recvall(int,char *,int *,int);
 
 char *my_strsep(char **,const char *);
 
+int b64_decode(unsigned char *encoded);
+
 void display_license(void);
 
 #endif
-
-
-
-
-

+ 219 - 17
src/nrpe.c

@@ -31,6 +31,7 @@
 #include "acl.h"
 
 #ifdef HAVE_SSL
+#include <ssl.h>
 #include "../include/dh.h"
 #endif
 
@@ -77,6 +78,25 @@ int     connection_timeout=DEFAULT_CONNECTION_TIMEOUT;
 int     ssl_shutdown_timeout=DEFAULT_SSL_SHUTDOWN_TIMEOUT;
 char    *command_prefix=NULL;
 
+/* 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, 1, 0 };
+
 command *command_list=NULL;
 
 char    *nrpe_user=NULL;
@@ -109,7 +129,7 @@ void complete_SSL_shutdown( SSL *);
 
 int main(int argc, char **argv){
 	int result=OK;
-	int x;
+	int x, ssl_opts = SSL_OP_ALL, vrfy;
 	char buffer[MAX_INPUT_BUFFER];
 	char *env_string=NULL;
 #ifdef HAVE_SSL
@@ -238,10 +258,10 @@ int main(int argc, char **argv){
 #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();
+
+		meth = SSLv23_server_method();
 
 		/* use week random seed if necessary */
 		if(allow_weak_random_seed && (RAND_status()==0)){
@@ -262,22 +282,96 @@ int main(int argc, char **argv){
 				}
 			}
 
-		if((ctx=SSL_CTX_new(meth))==NULL){
+#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();
+
+		ctx = SSL_CTX_new(meth);
+		if (ctx == NULL) {
 			syslog(LOG_ERR,"Error: could not create SSL context.\n");
+			SSL_CTX_free(ctx);
 			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);
 
-		/* ADDED 01/19/2004 */
-		/* use only TLSv1 protocol */
-		SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+		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);
+				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);
+			}
+		}
 
-		/* 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 (sslprm.client_certs != 0) {
+			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);
+			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);
+			}
+		}
+
+		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");
+			exit(STATE_CRITICAL);
+		}
+
+		if (sslprm.allowDH) {
+			/* use anonymous DH ciphers */
+			if (sslprm.allowDH == 2)
+				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");
+					exit(STATE_CRITICAL);
+				}
+				dh->p = BN_bin2bn(sslprm.adh_key, sslprm.adhk_len, NULL);
+				dh->g = BN_bin2bn("\2", 2, NULL);
+				if ((dh->p == NULL) || (dh->g == NULL)) {
+					DH_free(dh);
+					SSL_CTX_free(ctx);
+					syslog(LOG_ERR, "Error: could not create DH object\n");
+					exit(STATE_CRITICAL);
+				}
+				SSL_CTX_set_tmp_dh(ctx, dh);
+				DH_free(dh);
+			} else {
+				dh = get_dh512();
+				SSL_CTX_set_tmp_dh(ctx, dh);
+				DH_free(dh);
+			}
+
+		}
+		
 		if(debug==TRUE)
 			syslog(LOG_INFO,"INFO: SSL/TLS initialized. All network traffic will be encrypted.");
+
 	        }
 	else{
 		if(debug==TRUE)
@@ -481,6 +575,8 @@ int read_config_file(char *filename){
 	int line=0;
 	int len=0;
 	int x=0;
+	int pskfd;
+	struct stat st;
 
 
 	/* open the config file for reading */
@@ -641,6 +737,112 @@ int read_config_file(char *filename){
 				}
 			}
 
+		else if (!strcmp(varname, "ssl_version")) {
+			if (!strcmp(varvalue, "SSLv2"))
+				sslprm.ssl_min_ver = SSLv2;
+			else if (!strcmp(varvalue, "SSLv2+"))
+				sslprm.ssl_min_ver = SSLv2_plus;
+			else if (!strcmp(varvalue, "SSLv3"))
+				sslprm.ssl_min_ver = SSLv3;
+			else if (!strcmp(varvalue, "SSLv3+"))
+				sslprm.ssl_min_ver = SSLv3_plus;
+			else if (!strcmp(varvalue, "TLSv1"))
+				sslprm.ssl_min_ver = TLSv1;
+			else if (!strcmp(varvalue, "TLSv1+"))
+				sslprm.ssl_min_ver = TLSv1_plus;
+			else if (!strcmp(varvalue, "TLSv1.1"))
+				sslprm.ssl_min_ver = TLSv1_1;
+			else if (!strcmp(varvalue, "TLSv1.1+"))
+				sslprm.ssl_min_ver = TLSv1_1_plus;
+			else if (!strcmp(varvalue, "TLSv1.2"))
+				sslprm.ssl_min_ver = TLSv1_2;
+			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);
+				return ERROR;
+			}
+		}
+
+		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);
+				return ERROR;
+			}
+        }
+
+		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';
+		}
+
+		else if(!strcmp(varname, "ssl_cert_file"))
+			sslprm.cert_file = strdup(varvalue);
+		
+		else if(!strcmp(varname, "ssl_cacert_file"))
+			sslprm.cacert_file = strdup(varvalue);
+
+		else if(!strcmp(varname, "ssl_privatekey_file"))
+			sslprm.privatekey_file = strdup(varvalue);
+
+		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);
+				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))
+				sslprm.client_certs |= Ask_For_Cert;
+		}
+
+		else if (!strcmp(varname, "ssl_adh_key")) {
+			if (!strncmp(varvalue, "B64:", 4)) {
+				sslprm.adh_key = strdup(&varvalue[4]);
+				sslprm.adhk_len = b64_decode(sslprm.adh_key);
+			} 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);
+					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);
+					return ERROR;
+				}
+				if (fstat(pskfd, &st) < 0) {
+					close(pskfd);
+					syslog(LOG_ERR, "Unable to stat adh key file '%s'\n", 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);
+					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);
+					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");
+						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);
+					return ERROR;
+				}
+				close(pskfd);
+			}
+		}
+
 		else if(!strcmp(varname,"log_facility")){
 			if((get_log_facility(varvalue))==OK){
 				/* re-open log using new facility */
@@ -927,7 +1129,7 @@ void wait_for_connections(void){
 	struct sockaddr_in6 *nptr6;
 	struct sockaddr_storage addr;
 	int rc;
-	int sock, new_sd;
+	int sock, new_sd=0;
 	socklen_t addrlen;
 	pid_t pid;
 	fd_set fdread;
@@ -1314,7 +1516,7 @@ void handle_connection(int sock){
 		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
 
@@ -1472,7 +1674,7 @@ void handle_connection(int sock){
 		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));

+ 46 - 0
src/utils.c

@@ -364,6 +364,52 @@ char *my_strsep (char **stringp, const char *delim){
 	return begin;
 	}
 
+int b64_decode(unsigned char *encoded)
+{
+	static const char *b64 = { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" };
+	int		i, j, l, padding = 0;
+	unsigned char	c[4], *outp = encoded;
+	union {
+		unsigned c3;
+		struct {
+			unsigned f1:6;
+			unsigned f2:6;
+			unsigned f3:6;
+			unsigned f4:6;
+		} fields;
+	} enc;
+
+	enc.c3 = 0;
+	l = strlen((char*)encoded);
+	for (i = 0; i < l; i += 4) {
+		for (j = 0; j < 4; ++j) {
+			if (encoded[i+j] == '=') {
+				c[j] = 0;
+				++padding;
+			} else if (encoded[i+j] >= 'A' && encoded[i+j] <= 'Z')
+				c[j] = encoded[i+j] - 'A';
+			else if (encoded[i+j] >= 'a' && encoded[i+j] <= 'z')
+				c[j] = encoded[i+j] - 'a' + 26;
+			else if (encoded[i+j] >= '0' && encoded[i+j] <= '9')
+				c[j] = encoded[i+j] - '0' + 52;
+			else if (encoded[i+j] == '+')
+				c[j] = encoded[i+j] - '+' + 62;
+			else
+				c[j] = encoded[i+j] - '/' + 63;
+		}
+		enc.fields.f1 = c[3];
+		enc.fields.f2 = c[2];
+		enc.fields.f3 = c[1];
+		enc.fields.f4 = c[0];
+		*outp++ = (enc.c3 >> 16) & 0xff;
+		*outp++ = (enc.c3 >> 8) & 0xff;
+		*outp++ = (enc.c3) & 0xff;
+	}
+	*outp = '\0';
+
+	return outp - encoded - padding;
+}
+
 
 /* show license */
 void display_license(void){