pgcrypto crypt()/md5 and hmac() leak memory when compiled against
OpenSSL as openssl.c digest ->reset will do two DigestInit calls
against a context.  This happened to work with OpenSSL 0.9.6
but not with 0.9.7+.

Reason for the messy code was that I tried to avoid creating
wrapper structure to transport algorithm info and tried to use
OpenSSL context for it.  The fix is to create wrapper structure.

It also uses newer digest API to avoid memory allocations
on reset with newer OpenSSLs.

Attached are one patch for 7.3, 7.4, 8.0 branches and another
for 8.1 and HEAD.

Thanks to Daniel Blaisdell for reporting it.

--
marko
Index: contrib/pgcrypto/openssl.c
===================================================================
RCS file: /opt/cvs/pgsql/contrib/pgcrypto/openssl.c,v
retrieving revision 1.12.4.1
diff -u -c -r1.12.4.1 openssl.c
*** contrib/pgcrypto/openssl.c	12 Mar 2005 06:55:14 -0000	1.12.4.1
--- contrib/pgcrypto/openssl.c	17 Feb 2006 23:31:29 -0000
***************
*** 36,95 ****
  #include <openssl/evp.h>
  
  /*
   * Hashes
   */
  static unsigned
  digest_result_size(PX_MD * h)
  {
! 	return EVP_MD_CTX_size((EVP_MD_CTX *) h->p.ptr);
  }
  
  static unsigned
  digest_block_size(PX_MD * h)
  {
! 	return EVP_MD_CTX_block_size((EVP_MD_CTX *) h->p.ptr);
  }
  
  static void
  digest_reset(PX_MD * h)
  {
! 	EVP_MD_CTX *ctx = (EVP_MD_CTX *) h->p.ptr;
! 	const EVP_MD *md;
  
! 	md = EVP_MD_CTX_md(ctx);
! 
! 	EVP_DigestInit(ctx, md);
  }
  
  static void
  digest_update(PX_MD * h, const uint8 *data, unsigned dlen)
  {
! 	EVP_MD_CTX *ctx = (EVP_MD_CTX *) h->p.ptr;
  
! 	EVP_DigestUpdate(ctx, data, dlen);
  }
  
  static void
  digest_finish(PX_MD * h, uint8 *dst)
  {
! 	EVP_MD_CTX *ctx = (EVP_MD_CTX *) h->p.ptr;
! 	const EVP_MD *md = EVP_MD_CTX_md(ctx);
! 
! 	EVP_DigestFinal(ctx, dst, NULL);
  
! 	/*
! 	 * Some builds of 0.9.7x clear all of ctx in EVP_DigestFinal.
! 	 * Fix it by reinitializing ctx.
! 	 */
! 	EVP_DigestInit(ctx, md);
  }
  
  static void
  digest_free(PX_MD * h)
  {
! 	EVP_MD_CTX *ctx = (EVP_MD_CTX *) h->p.ptr;
  
! 	px_free(ctx);
  	px_free(h);
  }
  
--- 36,124 ----
  #include <openssl/evp.h>
  
  /*
+  * Backwards compatibility code for digest.
+  */
+ #if OPENSSL_VERSION_NUMBER < 0x00907000L
+ 
+ static void EVP_MD_CTX_init(EVP_MD_CTX *ctx)
+ {
+ 	memset(ctx, 0, sizeof(*ctx));
+ }
+ 
+ static int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx)
+ {
+ 	memset(ctx, 0, sizeof(*ctx));
+ 	return 1;
+ }
+ 
+ static int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *md, void *engine)
+ {
+ 	EVP_DigestInit(ctx, md);
+ 	return 1;
+ }
+ 
+ static int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *res, unsigned int *len)
+ {
+ 	EVP_DigestFinal(ctx, res, len);
+ 	return 1;
+ }
+ 
+ #endif
+ 
+ /*
   * Hashes
   */
+ 
+ typedef struct OSSLDigest {
+ 	const EVP_MD *algo;
+ 	EVP_MD_CTX ctx;
+ } OSSLDigest;
+ 
  static unsigned
  digest_result_size(PX_MD * h)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
! 	return EVP_MD_CTX_size(&digest->ctx);
  }
  
  static unsigned
  digest_block_size(PX_MD * h)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
! 	return EVP_MD_CTX_block_size(&digest->ctx);
  }
  
  static void
  digest_reset(PX_MD * h)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
  
! 	EVP_DigestInit_ex(&digest->ctx, digest->algo, NULL);
  }
  
  static void
  digest_update(PX_MD * h, const uint8 *data, unsigned dlen)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
  
! 	EVP_DigestUpdate(&digest->ctx, data, dlen);
  }
  
  static void
  digest_finish(PX_MD * h, uint8 *dst)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
  
! 	EVP_DigestFinal_ex(&digest->ctx, dst, NULL);
  }
  
  static void
  digest_free(PX_MD * h)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
  
! 	EVP_MD_CTX_cleanup(&digest->ctx);
! 	px_free(digest);
  	px_free(h);
  }
  
***************
*** 101,108 ****
  px_find_digest(const char *name, PX_MD ** res)
  {
  	const EVP_MD *md;
- 	EVP_MD_CTX *ctx;
  	PX_MD	   *h;
  
  	if (!px_openssl_initialized)
  	{
--- 130,137 ----
  px_find_digest(const char *name, PX_MD ** res)
  {
  	const EVP_MD *md;
  	PX_MD	   *h;
+ 	OSSLDigest *digest;
  
  	if (!px_openssl_initialized)
  	{
***************
*** 114,121 ****
  	if (md == NULL)
  		return -1;
  
! 	ctx = px_alloc(sizeof(*ctx));
! 	EVP_DigestInit(ctx, md);
  
  	h = px_alloc(sizeof(*h));
  	h->result_size = digest_result_size;
--- 143,154 ----
  	if (md == NULL)
  		return -1;
  
! 	digest = px_alloc(sizeof(*digest));
! 	digest->algo = md;
! 	
! 	EVP_MD_CTX_init(&digest->ctx);
! 	if (EVP_DigestInit_ex(&digest->ctx, digest->algo, NULL) == 0)
! 		return -1;
  
  	h = px_alloc(sizeof(*h));
  	h->result_size = digest_result_size;
***************
*** 124,130 ****
  	h->update = digest_update;
  	h->finish = digest_finish;
  	h->free = digest_free;
! 	h->p.ptr = (void *) ctx;
  
  	*res = h;
  	return 0;
--- 157,163 ----
  	h->update = digest_update;
  	h->finish = digest_finish;
  	h->free = digest_free;
! 	h->p.ptr = (void *) digest;
  
  	*res = h;
  	return 0;









Index: contrib/pgcrypto/openssl.c
===================================================================
RCS file: /opt/cvs/pgsql/contrib/pgcrypto/openssl.c,v
retrieving revision 1.26
diff -u -c -r1.26 openssl.c
*** contrib/pgcrypto/openssl.c	15 Oct 2005 02:49:06 -0000	1.26
--- contrib/pgcrypto/openssl.c	17 Feb 2006 23:54:52 -0000
***************
*** 47,63 ****
  #define MAX_IV		(128/8)
  
  /*
!  * Does OpenSSL support AES?
   */
  #if OPENSSL_VERSION_NUMBER >= 0x00907000L
  
! /* Yes, it does. */
  #include <openssl/aes.h>
  #else							/* old OPENSSL */
  
  /*
!  * No, it does not.  So use included rijndael code to emulate it.
   */
  #include "rijndael.c"
  
  #define AES_ENCRYPT 1
--- 47,70 ----
  #define MAX_IV		(128/8)
  
  /*
!  * Compatibility with OpenSSL 0.9.6
!  *
!  * It needs AES and newer DES and digest API.
   */
  #if OPENSSL_VERSION_NUMBER >= 0x00907000L
  
! /*
!  * Nothing needed for OpenSSL 0.9.7+
!  */
! 
  #include <openssl/aes.h>
+ 
  #else							/* old OPENSSL */
  
  /*
!  * Emulate OpenSSL AES.
   */
+ 
  #include "rijndael.c"
  
  #define AES_ENCRYPT 1
***************
*** 90,101 ****
  			memcpy(iv, (src) + (len) - 16, 16); \
  		} \
  	} while (0)
- #endif   /* old OPENSSL */
  
  /*
!  * Compatibility with older OpenSSL API for DES.
   */
! #if OPENSSL_VERSION_NUMBER < 0x00907000L
  #define DES_key_schedule des_key_schedule
  #define DES_cblock des_cblock
  #define DES_set_key(k, ks) \
--- 97,107 ----
  			memcpy(iv, (src) + (len) - 16, 16); \
  		} \
  	} while (0)
  
  /*
!  * Emulate DES_* API
   */
! 
  #define DES_key_schedule des_key_schedule
  #define DES_cblock des_cblock
  #define DES_set_key(k, ks) \
***************
*** 110,172 ****
  #define DES_ede3_cbc_encrypt(i, o, l, k1, k2, k3, iv, e) \
  		des_ede3_cbc_encrypt((i), (o), \
  				(l), *(k1), *(k2), *(k3), (iv), (e))
! #endif
  
  /*
   * Hashes
   */
  static unsigned
  digest_result_size(PX_MD * h)
  {
! 	return EVP_MD_CTX_size((EVP_MD_CTX *) h->p.ptr);
  }
  
  static unsigned
  digest_block_size(PX_MD * h)
  {
! 	return EVP_MD_CTX_block_size((EVP_MD_CTX *) h->p.ptr);
  }
  
  static void
  digest_reset(PX_MD * h)
  {
! 	EVP_MD_CTX *ctx = (EVP_MD_CTX *) h->p.ptr;
! 	const EVP_MD *md;
  
! 	md = EVP_MD_CTX_md(ctx);
! 
! 	EVP_DigestInit(ctx, md);
  }
  
  static void
  digest_update(PX_MD * h, const uint8 *data, unsigned dlen)
  {
! 	EVP_MD_CTX *ctx = (EVP_MD_CTX *) h->p.ptr;
  
! 	EVP_DigestUpdate(ctx, data, dlen);
  }
  
  static void
  digest_finish(PX_MD * h, uint8 *dst)
  {
! 	EVP_MD_CTX *ctx = (EVP_MD_CTX *) h->p.ptr;
! 	const EVP_MD *md = EVP_MD_CTX_md(ctx);
! 
! 	EVP_DigestFinal(ctx, dst, NULL);
  
! 	/*
! 	 * Some builds of 0.9.7x clear all of ctx in EVP_DigestFinal. Fix it by
! 	 * reinitializing ctx.
! 	 */
! 	EVP_DigestInit(ctx, md);
  }
  
  static void
  digest_free(PX_MD * h)
  {
! 	EVP_MD_CTX *ctx = (EVP_MD_CTX *) h->p.ptr;
  
! 	px_free(ctx);
  	px_free(h);
  }
  
--- 116,206 ----
  #define DES_ede3_cbc_encrypt(i, o, l, k1, k2, k3, iv, e) \
  		des_ede3_cbc_encrypt((i), (o), \
  				(l), *(k1), *(k2), *(k3), (iv), (e))
! 
! /*
!  * Emulate newer digest API.
!  */
! 
! static void EVP_MD_CTX_init(EVP_MD_CTX *ctx)
! {
! 	memset(ctx, 0, sizeof(*ctx));
! }
! 
! static int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx)
! {
! 	memset(ctx, 0, sizeof(*ctx));
! 	return 1;
! }
! 
! static int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *md, void *engine)
! {
! 	EVP_DigestInit(ctx, md);
! 	return 1;
! }
! 
! static int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *res, unsigned int *len)
! {
! 	EVP_DigestFinal(ctx, res, len);
! 	return 1;
! }
! 
! #endif   /* old OpenSSL */
  
  /*
   * Hashes
   */
+ 
+ typedef struct OSSLDigest {
+ 	const EVP_MD *algo;
+ 	EVP_MD_CTX ctx;
+ } OSSLDigest;
+ 
  static unsigned
  digest_result_size(PX_MD * h)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
! 	return EVP_MD_CTX_size(&digest->ctx);
  }
  
  static unsigned
  digest_block_size(PX_MD * h)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
! 	return EVP_MD_CTX_block_size(&digest->ctx);
  }
  
  static void
  digest_reset(PX_MD * h)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
  
! 	EVP_DigestInit_ex(&digest->ctx, digest->algo, NULL);
  }
  
  static void
  digest_update(PX_MD * h, const uint8 *data, unsigned dlen)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
  
! 	EVP_DigestUpdate(&digest->ctx, data, dlen);
  }
  
  static void
  digest_finish(PX_MD * h, uint8 *dst)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
  
! 	EVP_DigestFinal_ex(&digest->ctx, dst, NULL);
  }
  
  static void
  digest_free(PX_MD * h)
  {
! 	OSSLDigest *digest = (OSSLDigest *)h->p.ptr;
! 
! 	EVP_MD_CTX_cleanup(&digest->ctx);
  
! 	px_free(digest);
  	px_free(h);
  }
  
***************
*** 178,185 ****
  px_find_digest(const char *name, PX_MD ** res)
  {
  	const EVP_MD *md;
- 	EVP_MD_CTX *ctx;
  	PX_MD	   *h;
  
  	if (!px_openssl_initialized)
  	{
--- 212,219 ----
  px_find_digest(const char *name, PX_MD ** res)
  {
  	const EVP_MD *md;
  	PX_MD	   *h;
+ 	OSSLDigest *digest;
  
  	if (!px_openssl_initialized)
  	{
***************
*** 191,198 ****
  	if (md == NULL)
  		return PXE_NO_HASH;
  
! 	ctx = px_alloc(sizeof(*ctx));
! 	EVP_DigestInit(ctx, md);
  
  	h = px_alloc(sizeof(*h));
  	h->result_size = digest_result_size;
--- 225,236 ----
  	if (md == NULL)
  		return PXE_NO_HASH;
  
! 	digest = px_alloc(sizeof(*digest));
! 	digest->algo = md;
! 
! 	EVP_MD_CTX_init(&digest->ctx);
! 	if (EVP_DigestInit_ex(&digest->ctx, digest->algo, NULL) == 0)
! 		return -1;
  
  	h = px_alloc(sizeof(*h));
  	h->result_size = digest_result_size;
***************
*** 201,207 ****
  	h->update = digest_update;
  	h->finish = digest_finish;
  	h->free = digest_free;
! 	h->p.ptr = (void *) ctx;
  
  	*res = h;
  	return 0;
--- 239,245 ----
  	h->update = digest_update;
  	h->finish = digest_finish;
  	h->free = digest_free;
! 	h->p.ptr = (void *) digest;
  
  	*res = h;
  	return 0;









---------------------------(end of broadcast)---------------------------
TIP 2: Don't 'kill -9' the postmaster

Reply via email to