objective c - Possible double BIO_free in OpenSSL enc application -


(sorry verbose) experimenting adding openssl support cocoa application written in objective c os x 10.6 (snow leopard). simplify matters, have small wrapper class holds various bio , cipher context structures, aetopensslwrapper. looks following

.h

@interface aetopensslwrapper: public nsobject {    bio *writebio,encbio;    evp_cipher_ctx *ctx;    unsigned char *readwritebuff; }  @property (readwrite,assign) bio *writebio,*encbio; @property (readwrite,assign) evp_cipher_ctx *ctx; @property (readwrite,assign) unsigned char *readwritebuff;  -(id)init; ... -(void)dealloc; @end 

.m

@implementation aetopensslwrapper  @synthesize writebio,encbio,ctx,readwritebuff;  -(id)init {    self=[super init];    if(self)       {       writebio=bio_new(bio_s_file());       encbio=...       ctx=...       buff=...       (error handling omitted)       }     return self; } @end 

then various utility methods chain bios, write output bio, flush etc, in particular, 1 -(void)pushencryptingbio chain enciphering filter bio (which has been initialised key, salt , initial vector)

-(void)pushencryptingbio {    writebio=bio_push(encbio,writebio); } 

finally there dealloc routine. lifted directly enc program supplied openssl-1.0.1c distribution

-(void)dealloc {    if(readwritebuff!=null)       openssl_free(readwritebuff);    if(writebio!=null)       bio_free_all(writebio);    if(encbio!=null) <----------- looks wrong       bio_free(encbio); <---+     [super dealloc]; } 

the equivalent code prior loop encrypts input buffer , @ end of main() routine in apps/enc.c in openssl source tree:

lines 657 - 658

if (benc != null)    wbio=bio_push(benc,wbio); 

and lines 682 - 688

end:    err_print_errors(bio_err);    if (strbuf != null) openssl_free(strbuf);    if (buff != null) openssl_free(buff);    if (in != null) bio_free(in);    if (out != null) bio_free_all(out);    if (benc != null) bio_free(benc); <--- sure this? 

the question (finally): should call bio_free(benc) (or bio_free(encbio) in code) there since bio pushed onto writebio/out chain, freed bio_free_all? looking @ implementation of bio_free_all, runs down bio chain, decrementing ref counts , freeing goes , not null out pointers. looks must bug, reluctant assume ssl maintainers have missed , haven't. occasional crashes (1 in 10) if leave bio_free(encbio) call in , not if don't, don't want leak. in apple event handler, debugging complicated further. suggestions?

i've had opportunity work bios time ago , think correct. openssl man pages:

bio_free_all() frees entire bio chain, not halt if error occurs freeing individual bio in chain.

also, since openssl cleanup functions take pointer structure, can't change value of pointer (i.e require address of pointer so). if openssl set pointer parameter null, copy null, turning out useless behavior. it's set null.

to give concrete example, following c++ code used encrypt plain text using pkcs#5 standard. receives password parameter, used derive aes key. following code not leak (checked valgrind).

static int encryption_failed = 1; static const evp_md *md = evp_sha256(); static const evp_cipher *cipher = evp_aes_256_cbc();  static int base64encrypt(const string& toencrypt, const string& password, string& base64encrypted) {     static const char magic[]="salted__";     int ret = 0;     evp_cipher_ctx *ctx = null;     buf_mem *bptr = null;     unsigned char key[evp_max_key_length], iv[evp_max_iv_length];     unsigned char salt[pkcs5_salt_len];     char *encrypted = null;     /* allow enough space in output buffer additional block */     bio *bmem = null;     bio *b64 = null;     bio *benc = null;      // setting bio context     if ( (bmem = bio_new(bio_s_mem())) == null ){         ret = encryption_failed; goto err0;     }     if ( (b64 = bio_new(bio_f_base64())) == null ){         ret = encryption_failed; goto err0;     }     bio_push(b64,bmem);      // generating salt     if (rand_pseudo_bytes(salt, sizeof(salt) ) < 0){         ret = encryption_failed; goto err0;     }      if ((password.size() == 0) && evp_cipher_iv_length(cipher) != 0) {         ret = encryption_failed; goto err0;      }      // writing salt bio, base 64 encoded     if (bio_write(b64, magic, sizeof magic-1) != sizeof magic-1 || bio_write(b64, (char *)salt, sizeof salt) != sizeof salt) {         ret = encryption_failed; goto err0;     }         // deriving key     if (!evp_bytestokey(cipher, md, salt, (unsigned char *)password.c_str(), password.size(), pkcs5_default_iter, key, iv)){         ret = encryption_failed; goto err0;     }      if ( (benc=bio_new(bio_f_cipher())) == null ){         ret = encryption_failed; goto err0;     }     bio_get_cipher_ctx(benc, &ctx);      if (!evp_cipherinit_ex(ctx, cipher, null, null, null, 1)){         ret = encryption_failed; goto err0;     }      if (!evp_cipherinit_ex(ctx, null, null, key, iv, 1)){         ret = encryption_failed; goto err0;     }     bio_push(benc, b64);          // writing mem bio     if (bio_write(benc, (char *)toencrypt.c_str(), toencrypt.size()) != (int)toencrypt.size()){         ret = encryption_failed; goto err0;     }         if (!bio_flush(benc)){          ret = encryption_failed; goto err0;     }      bio_get_mem_ptr(benc, &bptr);     if (bptr->length <= 0){         ret = encryption_failed; goto err0;     }     encrypted = new char[bptr->length + 1];     memcpy(encrypted, bptr->data, bptr->length);     encrypted[bptr->length] = '\0';     base64encrypted = encrypted;     delete[] encrypted;      if (benc != null) bio_free_all(benc);     return 0;      err0:     if (benc != null) bio_free_all(benc);     return ret; } 

as can see, i've chained 3 bios, , single call bio_free_all(benc) cleans bios.

regards.


Comments

Popular posts from this blog

java - Play! framework 2.0: How to display multiple image? -

gmail - Is there any documentation for read-only access to the Google Contacts API? -

php - Controller/JToolBar not working in Joomla 2.5 -