File openssl-CVE-2016-0798-101.patch of Package openssl

commit 59a908f1e8380412a81392c468b83bf6071beb2a
Author: Emilia Kasper <emilia@openssl.org>
Date:   Wed Feb 24 12:59:59 2016 +0100

    CVE-2016-0798: avoid memory leak in SRP
    
    The SRP user database lookup method SRP_VBASE_get_by_user had confusing
    memory management semantics; the returned pointer was sometimes newly
    allocated, and sometimes owned by the callee. The calling code has no
    way of distinguishing these two cases.
    
    Specifically, SRP servers that configure a secret seed to hide valid
    login information are vulnerable to a memory leak: an attacker
    connecting with an invalid username can cause a memory leak of around
    300 bytes per connection.
    
    Servers that do not configure SRP, or configure SRP but do not configure
    a seed are not vulnerable.
    
    In Apache, the seed directive is known as SSLSRPUnknownUserSeed.
    
    To mitigate the memory leak, the seed handling in SRP_VBASE_get_by_user
    is now disabled even if the user has configured a seed.
    
    Applications are advised to migrate to SRP_VBASE_get1_by_user. However,
    note that OpenSSL makes no strong guarantees about the
    indistinguishability of valid and invalid logins. In particular,
    computations are currently not carried out in constant time.
    
    Reviewed-by: Rich Salz <rsalz@openssl.org>

diff --git a/apps/s_server.c b/apps/s_server.c
index f47328a..7ddb266 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -395,6 +395,8 @@ typedef struct srpsrvparm_st
 static int MS_CALLBACK ssl_srp_server_param_cb(SSL *s, int *ad, void *arg)
 	{
 	srpsrvparm *p = (srpsrvparm *)arg;
+    int ret = SSL3_AL_FATAL;
+
 	if (p->login == NULL && p->user == NULL )
 		{
 		p->login = SSL_get_srp_username(s);
@@ -405,19 +407,23 @@ static int MS_CALLBACK ssl_srp_server_param_cb(SSL *s, int *ad, void *arg)
 	if (p->user == NULL)
 		{
 		BIO_printf(bio_err, "User %s doesn't exist\n", p->login);
-		return SSL3_AL_FATAL;
+        goto err;
 		}
+
 	if (SSL_set_srp_server_param(s, p->user->N, p->user->g, p->user->s, p->user->v,
 				     p->user->info) < 0)
 		{
 		*ad = SSL_AD_INTERNAL_ERROR;
-		return SSL3_AL_FATAL;
+        goto err;
 		}
 	BIO_printf(bio_err, "SRP parameters set: username = \"%s\" info=\"%s\" \n", p->login,p->user->info);
-	/* need to check whether there are memory leaks */
+    ret = SSL_ERROR_NONE;
+
+err:
+    SRP_user_pwd_free(p->user);
 	p->user = NULL;
 	p->login = NULL;
-	return SSL_ERROR_NONE;
+	return ret;
 	}
 
 #endif
@@ -2284,7 +2290,8 @@ static int sv_body(char *hostname, int s, unsigned char *context)
 				while (SSL_get_error(con,k) == SSL_ERROR_WANT_X509_LOOKUP)
 					{
 					BIO_printf(bio_s_out,"LOOKUP renego during write\n");
-					srp_callback_parm.user = SRP_VBASE_get_by_user(srp_callback_parm.vb, srp_callback_parm.login); 
+                    SRP_user_pwd_free(srp_callback_parm.user);
+					srp_callback_parm.user =                         SRP_VBASE_get1_by_user(srp_callback_parm.vb, srp_callback_parm.login); 
 					if (srp_callback_parm.user) 
 						BIO_printf(bio_s_out,"LOOKUP done %s\n",srp_callback_parm.user->info);
 					else 
@@ -2343,7 +2350,8 @@ again:
 				while (SSL_get_error(con,i) == SSL_ERROR_WANT_X509_LOOKUP)
 					{
 					BIO_printf(bio_s_out,"LOOKUP renego during read\n");
-					srp_callback_parm.user = SRP_VBASE_get_by_user(srp_callback_parm.vb, srp_callback_parm.login); 
+                    SRP_user_pwd_free(srp_callback_parm.user);
+					srp_callback_parm.user =                         SRP_VBASE_get1_by_user(srp_callback_parm.vb, srp_callback_parm.login); 
 					if (srp_callback_parm.user) 
 						BIO_printf(bio_s_out,"LOOKUP done %s\n",srp_callback_parm.user->info);
 					else 
@@ -2432,7 +2440,8 @@ static int init_ssl_connection(SSL *con)
 	while (i <= 0 &&  SSL_get_error(con,i) == SSL_ERROR_WANT_X509_LOOKUP) 
 		{
 			BIO_printf(bio_s_out,"LOOKUP during accept %s\n",srp_callback_parm.login);
-			srp_callback_parm.user = SRP_VBASE_get_by_user(srp_callback_parm.vb, srp_callback_parm.login); 
+        SRP_user_pwd_free(srp_callback_parm.user);
+			srp_callback_parm.user =             SRP_VBASE_get1_by_user(srp_callback_parm.vb, srp_callback_parm.login); 
 			if (srp_callback_parm.user) 
 				BIO_printf(bio_s_out,"LOOKUP done %s\n",srp_callback_parm.user->info);
 			else 
@@ -2674,7 +2683,8 @@ static int www_body(char *hostname, int s, unsigned char *context)
 			while (i <= 0 &&  SSL_get_error(con,i) == SSL_ERROR_WANT_X509_LOOKUP) 
 		{
 			BIO_printf(bio_s_out,"LOOKUP during accept %s\n",srp_callback_parm.login);
-			srp_callback_parm.user = SRP_VBASE_get_by_user(srp_callback_parm.vb, srp_callback_parm.login); 
+                SRP_user_pwd_free(srp_callback_parm.user);
+			srp_callback_parm.user =                     SRP_VBASE_get1_by_user(srp_callback_parm.vb, srp_callback_parm.login); 
 			if (srp_callback_parm.user) 
 				BIO_printf(bio_s_out,"LOOKUP done %s\n",srp_callback_parm.user->info);
 			else 
diff --git a/crypto/srp/srp.h b/crypto/srp/srp.h
index 7ec7825..bfd4388 100644
--- a/crypto/srp/srp.h
+++ b/crypto/srp/srp.h
@@ -83,16 +83,21 @@ DECLARE_STACK_OF(SRP_gN_cache)
 
 typedef struct SRP_user_pwd_st
 	{
+    /* Owned by us. */
 	char *id;
 	BIGNUM *s;
 	BIGNUM *v;
+    /* Not owned by us. */
 	const BIGNUM *g;
 	const BIGNUM *N;
+    /* Owned by us. */
 	char *info;
 	} SRP_user_pwd;
 
 DECLARE_STACK_OF(SRP_user_pwd)
 
+void SRP_user_pwd_free(SRP_user_pwd *user_pwd);
+
 typedef struct SRP_VBASE_st
 	{
 	STACK_OF(SRP_user_pwd) *users_pwd;
@@ -117,7 +122,12 @@ DECLARE_STACK_OF(SRP_gN)
 SRP_VBASE *SRP_VBASE_new(char *seed_key);
 int SRP_VBASE_free(SRP_VBASE *vb);
 int SRP_VBASE_init(SRP_VBASE *vb, char * verifier_file);
+
+/* This method ignores the configured seed and fails for an unknown user. */
 SRP_user_pwd *SRP_VBASE_get_by_user(SRP_VBASE *vb, char *username);
+/* NOTE: unlike in SRP_VBASE_get_by_user, caller owns the returned pointer.*/
+SRP_user_pwd *SRP_VBASE_get1_by_user(SRP_VBASE *vb, char *username);
+
 char *SRP_create_verifier(const char *user, const char *pass, char **salt,
 			  char **verifier, const char *N, const char *g);
 int SRP_create_verifier_BN(const char *user, const char *pass, BIGNUM **salt, BIGNUM **verifier, BIGNUM *N, BIGNUM *g);
diff --git a/crypto/srp/srp_vfy.c b/crypto/srp/srp_vfy.c
index fdca19f..4ba4cad 100644
--- a/crypto/srp/srp_vfy.c
+++ b/crypto/srp/srp_vfy.c
@@ -182,7 +182,7 @@ static char *t_tob64(char *dst, const unsigned char *src, int size)
 	return olddst;
 	}
 
-static void SRP_user_pwd_free(SRP_user_pwd *user_pwd)
+void SRP_user_pwd_free(SRP_user_pwd *user_pwd)
 	{
 	if (user_pwd == NULL) 
 		return;
@@ -244,6 +244,24 @@ static int SRP_user_pwd_set_sv_BN(SRP_user_pwd *vinfo, BIGNUM *s, BIGNUM *v)
 	return (vinfo->s != NULL && vinfo->v != NULL) ;
 	}
 
+static SRP_user_pwd *srp_user_pwd_dup(SRP_user_pwd *src)
+{
+    SRP_user_pwd *ret;
+
+    if (src == NULL)
+        return NULL;
+    if ((ret = SRP_user_pwd_new()) == NULL)
+        return NULL;
+
+    SRP_user_pwd_set_gN(ret, src->g, src->N);
+    if (!SRP_user_pwd_set_ids(ret, src->id, src->info)
+        || !SRP_user_pwd_set_sv_BN(ret, BN_dup(src->s), BN_dup(src->v))) {
+            SRP_user_pwd_free(ret);
+            return NULL;
+    }
+    return ret;
+}
+
 SRP_VBASE *SRP_VBASE_new(char *seed_key)
 	{
 	SRP_VBASE *vb = (SRP_VBASE *) OPENSSL_malloc(sizeof(SRP_VBASE));
@@ -474,23 +492,51 @@ int SRP_VBASE_init(SRP_VBASE *vb, char *verifier_file)
 
 	}
 
-
-SRP_user_pwd *SRP_VBASE_get_by_user(SRP_VBASE *vb, char *username)
+static SRP_user_pwd *find_user(SRP_VBASE *vb, char *username)
 	{
 	int i;
 	SRP_user_pwd *user;
-	unsigned char digv[SHA_DIGEST_LENGTH];
-	unsigned char digs[SHA_DIGEST_LENGTH];
-	EVP_MD_CTX ctxt;
 
 	if (vb == NULL)
 		return NULL;
+
 	for(i = 0; i < sk_SRP_user_pwd_num(vb->users_pwd); i++)
 		{
 		user = sk_SRP_user_pwd_value(vb->users_pwd, i);
 		if (strcmp(user->id,username)==0)
 			return user;
 		}
+
+    return NULL;
+}
+
+/*
+ * This method ignores the configured seed and fails for an unknown user.
+ * Ownership of the returned pointer is not released to the caller.
+ * In other words, caller must not free the result.
+ */
+SRP_user_pwd *SRP_VBASE_get_by_user(SRP_VBASE *vb, char *username)
+{
+    return find_user(vb, username);
+}
+
+/*
+ * Ownership of the returned pointer is released to the caller.
+ * In other words, caller must free the result once done.
+ */
+SRP_user_pwd *SRP_VBASE_get1_by_user(SRP_VBASE *vb, char *username)
+{
+    SRP_user_pwd *user;
+    unsigned char digv[SHA_DIGEST_LENGTH];
+    unsigned char digs[SHA_DIGEST_LENGTH];
+    EVP_MD_CTX ctxt;
+
+    if (vb == NULL)
+        return NULL;
+
+    if ((user = find_user(vb, username)) != NULL)
+        return srp_user_pwd_dup(user);
+
 	if ((vb->seed_key == NULL) ||
 		(vb->default_g == NULL) ||
 		(vb->default_N == NULL))
diff --git a/util/libeay.num b/util/libeay.num
index 3821c9c..d8edb3b 100755
--- a/util/libeay.num
+++ b/util/libeay.num
@@ -1806,6 +1806,8 @@ d2i_ASN1_SET_OF_PKCS12_SAFEBAG          2341	NOEXIST::FUNCTION:
 ASN1_UTCTIME_get                        2350	NOEXIST::FUNCTION:
 X509_REQ_digest                         2362	EXIST::FUNCTION:EVP
 X509_CRL_digest                         2391	EXIST::FUNCTION:EVP
+SRP_VBASE_get1_by_user                  2393	EXIST::FUNCTION:SRP
+SRP_user_pwd_free                       2394	EXIST::FUNCTION:SRP
 d2i_ASN1_SET_OF_PKCS7                   2397	NOEXIST::FUNCTION:
 X509_ALGOR_cmp                          2398	EXIST::FUNCTION:
 EVP_CIPHER_CTX_set_key_length           2399	EXIST::FUNCTION: