Ethereal-dev: [Ethereal-dev] ssl decryption patch II
Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.
From: Paolo Abeni <00918190@xxxxxxxxx>
Date: Fri, 15 Apr 2005 15:28:12 +0200
hi, I have modified the ssl decryption patch to make use of gnutls/gcrypt instead of openssl. The autoconf/automake scripts are modified to detect the gnutls libraries and headers. If the gnutls library is not found, openssl is used for the required cryptographic functions. I have solved a few portability issues but I have tested the code only on a GNU/Linux Mandrake 11 system. Any feedback about using the patch will be appreciated. Cheers, Paolo Abeni p.s. the bulk of decryption code is still derived from Eric Rescorla code. I'm waiting for a response from Eric about relicensing of modified code under GPL. Gruppo Telecom Italia - Direzione e coordinamento di Telecom Italia S.p.A. ==================================================================== CONFIDENTIALITY NOTICE This message and its attachments are addressed solely to the persons above and may contain confidential information. If you have received the message in error, be informed that any use of the content hereof is prohibited. Please return it immediately to the sender and delete the message. Should you have any questions, please send an e_mail to MailAdmin@xxxxxxxxx. Thank you ====================================================================
/* This code is derived from ssldump. See: http://www.rtfm.com/ssldump/ Copyright (C) 1999-2001 RTFM, Inc. All Rights Reserved This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla <ekr@xxxxxxxx> and licensed by RTFM, Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by Eric Rescorla for RTFM, Inc. 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE ERIC RESCORLA AND RTFM ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <glib.h> #define SSL_DECRYPT_DEBUG #include <epan/packet.h> #include <epan/dissectors/packet-ssl-utils.h> #if USE_OPENSSL #define SSL_HMAC HMAC_CTX #define SSL_HMAC_INIT(md,key,len,algo) HMAC_Init(md, key,len,algo) #define SSL_HMAC_UPDATE(md, data,len) HMAC_Update(md,data,len) #define SSL_HMAC_FINAL(md, data, len) HMAC_Final(md,data,len) #define SSL_HMAC_CLEANUP(md) HMAC_cleanup(md) #define SSL_GET_DIGEST_BY_NAME(name) EVP_get_digestbyname(name) #define SSL_GET_CIPHER_BY_NAME(name) EVP_get_cipherbyname(name) #define SSL_MD EVP_MD_CTX #define SSL_MD_INIT(md,algo) EVP_DigestInit(md, algo) #define SSL_MD_UPDATE(md, data,len) EVP_DigestUpdate(md,data,len) #define SSL_MD_FINAL(md, data, len) EVP_DigestFinal(md,data,len) #define SSL_MD_CLEANUP(md) EVP_MD_CTX_cleanup(md) #define SSL_SHA_CTX SHA_CTX #define SSL_SHA_INIT(sh) SHA1_Init(sh) #define SSL_SHA_UPDATE(sh, data, len) SHA1_Update(sh, data,len) #define SSL_SHA_FINAL(buf, sh) SHA1_Final(buf, sh) #define SSL_MD5_CTX MD5_CTX #define SSL_MD5_INIT(sh) MD5_Init(sh) #define SSL_MD5_UPDATE(sh, data, len) MD5_Update(sh, data,len) #define SSL_MD5_FINAL(buf, sh) MD5_Final(buf, sh) #define SSL_CIPHER_INIT(chiper, ciph_id,sk,iv,enc) EVP_CIPHER_CTX_init(chiper); \ EVP_CipherInit(chiper,ciph_id,sk,iv,enc); #define SSL_CIPHER_DECRYPT(evp, out, in, inl) EVP_Cipher(evp,out,in,inl); #define SSL_PRIVATE_DECYPT(len, encr_data, decryped_data, pk) \ RSA_private_decrypt(len, encr_data,*(decryped_data), pk->pkey.rsa,RSA_PKCS1_PADDING); #else #define SSL_HMAC gcry_md_hd_t #define SSL_HMAC_INIT(md,key,len,algo) if (*(md)) gcry_md_close(*(md)); \ gcry_md_open(md,algo, GCRY_MD_FLAG_HMAC); \ gcry_md_setkey (*(md), key, len) #define SSL_HMAC_UPDATE(md, data, len) gcry_md_write(*(md), data, len) #define SSL_HMAC_FINAL(md, data, len) ({ int __algo = gcry_md_get_algo (*(md)); \ int __len = gcry_md_get_algo_dlen (__algo);\ memcpy(data, gcry_md_read(*(md), __algo), __len); \ *len =__len;}) #define SSL_HMAC_CLEANUP(md) gcry_md_close(*(md)) #define SSL_MD gcry_md_hd_t #define SSL_MD_INIT(md,algo) if (*(md)) gcry_md_close(*(md)); \ gcry_md_open(md,algo, 0); #define SSL_MD_UPDATE(md, data, len) gcry_md_write(*(md), data, len) #define SSL_MD_FINAL(md, data, len) ({ int __algo = gcry_md_get_algo (*(md)); \ int __len = gcry_md_get_algo_dlen (__algo);\ memcpy(data, gcry_md_read(*(md), __algo), __len); \ *len =__len;}) #define SSL_MD_CLEANUP(md) gcry_md_close(*(md)) #define SSL_SHA_CTX gcry_md_hd_t #define SSL_SHA_INIT(md) if (*(md)) gcry_md_close(*(md)); \ gcry_md_open(md,GCRY_MD_SHA1, 0); #define SSL_SHA_UPDATE(md, data, len) gcry_md_write(*(md), data, len) #define SSL_SHA_FINAL(buf, md) memcpy(buf, gcry_md_read(*(md), GCRY_MD_SHA1), \ gcry_md_get_algo_dlen(GCRY_MD_SHA1)) #define SSL_MD5_CTX gcry_md_hd_t #define SSL_MD5_INIT(md) if (*(md)) gcry_md_close(*(md)); \ gcry_md_open(md,GCRY_MD_MD5, 0); #define SSL_MD5_UPDATE(md, data, len) gcry_md_write(*(md), data, len) #define SSL_MD5_FINAL(buf, md) memcpy(buf, gcry_md_read(*(md), GCRY_MD_MD5), \ gcry_md_get_algo_dlen(GCRY_MD_MD5)) #define SSL_CIPHER_INIT(chiper, algo,sk,iv,enc) gcry_cipher_open(chiper, algo, \ GCRY_CIPHER_MODE_STREAM, 0); \ gcry_cipher_setkey(*(chiper), sk, gcry_cipher_get_algo_keylen (algo)); \ gcry_cipher_setiv(*(chiper), iv, gcry_cipher_get_algo_blklen (algo)) #define SSL_CIPHER_DECRYPT(chiper, out, in, inl) \ gcry_cipher_decrypt ( *(chiper), out, inl, in, inl); #define SSL_GET_DIGEST_BY_NAME(name) gcry_md_map_name(name) #define SSL_GET_CIPHER_BY_NAME(name) gcry_cipher_map_name(name) #define SSL_GET_KEY_LEN(pk) gcry_pk_get_nbits (pk) #define SSL_PRIVATE_DECYPT(len, encr_data, decryped_data, pk) \ pcry_private_decrypt(len, encr_data, decryped_data, pk) int pcry_private_decrypt(int len, char* encr_data, char** decrypted_data, SSL_PRIVATE_KEY* pk) { int rc, i; int decr_len = 0; gcry_sexp_t s_data, s_plain; gcry_mpi_t encr_mpi; int encr_len = len; char* decr_data_ptr; rc = gcry_mpi_scan(&encr_mpi, GCRYMPI_FMT_STD,encr_data, encr_len, &encr_len); if (rc != 0 ) { ssl_debug_printf("pcry_private_decrypt: can't convert encr_data to mpi (size %d):%s\n", len, gcry_strerror(rc)); return 0; } /* put the data into a simple list */ rc = gcry_sexp_build(&s_data, NULL, "(enc-val(rsa(a%m)))", encr_mpi); if (rc != 0) { ssl_debug_printf("pcry_private_decrypt: can't build encr_sexp:%s \n", gcry_strerror(rc)); return 0; } /* pass it to libgcrypt */ rc = gcry_pk_decrypt(&s_plain, s_data, pk); gcry_sexp_release(s_data); if (rc != 0) { ssl_debug_printf("pcry_private_decrypt: can't decrypt key:%s\n", len, gcry_strerror(rc)); goto out; } gcry_mpi_t text = gcry_sexp_nth_mpi(s_plain, 0, 0); decr_len = len; if (gcry_mpi_print(GCRYMPI_FMT_USG, NULL, decr_len, &decr_len, text) != 0) { ssl_debug_printf("pcry_private_decrypt: can't compute decr size:%s\n", gcry_strerror(rc)); decr_len = 0; goto out; } *decrypted_data = decr_data_ptr = g_malloc(decr_len); if (gcry_mpi_print( GCRYMPI_FMT_USG, decr_data_ptr, decr_len, &decr_len, text) != 0) { ssl_debug_printf("pcry_private_decrypt: can't print decr data to mpi (size %d):%s\n", decr_len, gcry_strerror(rc)); decr_len = 0; g_free(*decrypted_data); *decrypted_data = NULL; goto out; } /* strip the padding*/ rc = 0; for (i = 1; i < decr_len; i++) { if (decr_data_ptr[i] == 0) { rc = decr_len; break; } } i++; if (rc == 0) { ssl_debug_printf("pcry_private_decrypt: can't strip padding len %d \n", decr_len); decr_len = 0; goto out; } ssl_debug_printf("pcry_private_decrypt: stripping %d bytes, decr_len\n", i, decr_len); ssl_print_data("decypted_unstrip_pre_master", decr_data_ptr, decr_len); memcpy(decr_data_ptr, &decr_data_ptr[i], decr_len - i); decr_len -= i; out: gcry_sexp_release(s_plain); return decr_len; } #endif #define PRF(ssl,secret,usage,rnd1,rnd2,out) ((ssl->version_netorder==SSLV3_VERSION)? \ ssl3_prf(secret,usage,rnd1,rnd2,out): \ tls_prf(secret,usage,rnd1,rnd2,out)) static char *digests[]={ "MD5", "SHA1" }; static char *ciphers[]={ "DES", "DES3", #if USE_OPENSSL "RC4", #else "ARCFOUR", /* gnutls does not support rc4, but this should be 100% compatible*/ #endif "RC2", "IDEA" }; static SslCipherSuite cipher_suites[]={ {1,KEX_RSA,SIG_RSA,ENC_NULL,0,0,0,DIG_MD5,16,0}, {2,KEX_RSA,SIG_RSA,ENC_NULL,0,0,0,DIG_SHA,20,0}, {3,KEX_RSA,SIG_RSA,ENC_RC4,1,128,40,DIG_MD5,16,1}, {4,KEX_RSA,SIG_RSA,ENC_RC4,1,128,128,DIG_MD5,16,0}, {5,KEX_RSA,SIG_RSA,ENC_RC4,1,128,128,DIG_SHA,20,0}, {6,KEX_RSA,SIG_RSA,ENC_RC2,8,128,40,DIG_SHA,20,1}, {7,KEX_RSA,SIG_RSA,ENC_IDEA,8,128,128,DIG_SHA,20,0}, {8,KEX_RSA,SIG_RSA,ENC_DES,8,64,40,DIG_SHA,20,1}, {9,KEX_RSA,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,0}, {10,KEX_RSA,SIG_RSA,ENC_3DES,8,192,192,DIG_SHA,20,0}, {11,KEX_DH,SIG_DSS,ENC_DES,8,64,40,DIG_SHA,20,1}, {12,KEX_DH,SIG_DSS,ENC_DES,8,64,64,DIG_SHA,20,0}, {13,KEX_DH,SIG_DSS,ENC_3DES,8,192,192,DIG_SHA,20,0}, {14,KEX_DH,SIG_RSA,ENC_DES,8,64,40,DIG_SHA,20,1}, {15,KEX_DH,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,0}, {16,KEX_DH,SIG_RSA,ENC_3DES,8,192,192,DIG_SHA,20,0}, {17,KEX_DH,SIG_DSS,ENC_DES,8,64,40,DIG_SHA,20,1}, {18,KEX_DH,SIG_DSS,ENC_DES,8,64,64,DIG_SHA,20,0}, {19,KEX_DH,SIG_DSS,ENC_3DES,8,192,192,DIG_SHA,20,0}, {20,KEX_DH,SIG_RSA,ENC_DES,8,64,40,DIG_SHA,20,1}, {21,KEX_DH,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,0}, {22,KEX_DH,SIG_RSA,ENC_3DES,8,192,192,DIG_SHA,20,0}, {23,KEX_DH,SIG_NONE,ENC_RC4,1,128,40,DIG_MD5,16,1}, {24,KEX_DH,SIG_NONE,ENC_RC4,1,128,128,DIG_MD5,16,0}, {25,KEX_DH,SIG_NONE,ENC_DES,8,64,40,DIG_MD5,16,1}, {26,KEX_DH,SIG_NONE,ENC_DES,8,64,64,DIG_MD5,16,0}, {27,KEX_DH,SIG_NONE,ENC_3DES,8,192,192,DIG_MD5,16,0}, {96,KEX_RSA,SIG_RSA,ENC_RC4,1,128,56,DIG_MD5,16,1}, {97,KEX_RSA,SIG_RSA,ENC_RC2,1,128,56,DIG_MD5,16,1}, {98,KEX_RSA,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,1}, {99,KEX_DH,SIG_DSS,ENC_DES,8,64,64,DIG_SHA,16,1}, {100,KEX_RSA,SIG_RSA,ENC_RC4,1,128,56,DIG_SHA,20,1}, {101,KEX_DH,SIG_DSS,ENC_RC4,1,128,56,DIG_SHA,20,1}, {102,KEX_DH,SIG_DSS,ENC_RC4,1,128,128,DIG_SHA,20,0}, {-1, 0,0,0,0,0,0,0,20,0} }; typedef struct { int ssl_port; int decrypted_port; } SslPortPair; static SslPortPair ssl_ports [] = { {443, 80}, /* https */ {636, 389}, /* ldap */ {993, 143}, /* imap */ {995, 110}, /* pop3 */ {0,0}}; int ssl_packet_from_server(int port) { SslPortPair* current; for (current = ssl_ports; current->ssl_port != 0; current++) { if (current->ssl_port == port) return 1; } return 0; } int ssl_data_init(StringInfo* str, char* src, unsigned int len) { if (str->data) free(str->data); str->data = malloc(len); if (!str->data) return -1; if (src) memcpy(str->data, src,len); str->data_len = len; return 0; } int ssl_data_alloc(StringInfo* str, unsigned int len) { str->data = malloc(len); if (!str->data) return -1; str->data_len = len; return 0; } int ssl_data_set(StringInfo* str, char* data, unsigned int len) { memcpy(str->data, data, len); str->data_len = len; return 0; } void ssl_debug_printf(char* fmt,...) { #ifdef SSL_DECRYPT_DEBUG va_list ap; int ret=0; va_start(ap, fmt); ret += vfprintf(stderr, fmt, ap); va_end(ap); #endif } void ssl_print_text_data(const char* name, const char* data, int len) { #ifdef SSL_DECRYPT_DEBUG int i; fprintf(stderr,"%s: ",name); for (i=0; i< len; i++) { fprintf(stderr,"%c",data[i]); } fprintf(stderr,"\n"); #endif } void ssl_print_data(const char* name, const char* data, int len) { #ifdef SSL_DECRYPT_DEBUG int i; fprintf(stderr,"%s[%d]:\n",name, len); for (i=0; i< len; i++) { if ((i>0) && (i%16 == 0)) fprintf(stderr,"\n"); fprintf(stderr,"%.2x ",data[i]&255); } fprintf(stderr,"\n"); #endif } void ssl_print_string(const char* name, const StringInfo* data) { #ifdef SSL_DECRYPT_DEBUG ssl_print_data(name, data->data, data->data_len); #endif } int ssl_find_cipher(int num,SslCipherSuite* cs) { SslCipherSuite *c; for(c=cipher_suites;c->number!=-1;c++){ if(c->number==num){ *cs=*c; return 0; } } return -1; } /* get ssl data for this session. if no ssl data is found allocate a new one*/ SslDecryptSession* ssl_alloc_session(void) { SslDecryptSession*ssl_session = g_malloc0(sizeof(SslDecryptSession)); if (!ssl_session) return NULL; ssl_debug_printf("ssl_alloc_session ptr %p size %d\n", ssl_session, sizeof(SslDecryptSession)); ssl_session->master_secret.data = ssl_session->_master_secret; ssl_session->session_id.data = ssl_session->_session_id; ssl_session->client_random.data = ssl_session->_client_random; ssl_session->server_random.data = ssl_session->_server_random; ssl_session->master_secret.data_len = 48; return ssl_session; } #ifdef USE_OPENSSL static int tls_hash(StringInfo* secret, StringInfo* seed, const EVP_MD *md, StringInfo* out) #else static int tls_hash(StringInfo* secret, StringInfo* seed, int md, StringInfo* out) #endif { u_int8_t *ptr=out->data; unsigned int left=out->data_len; int tocpy; u_int8_t *A; u_int8_t _A[20],tmp[20]; unsigned int A_l,tmp_l; SSL_HMAC hm; memset(&hm, 0, sizeof(hm)); ssl_print_string("hash secret", secret); ssl_print_string("hash seed", seed); ssl_debug_printf("tls_hash: hmac %p\n",md); A=seed->data; A_l=seed->data_len; while(left){ SSL_HMAC_INIT(&hm,secret->data,secret->data_len,md); SSL_HMAC_UPDATE(&hm,A,A_l); SSL_HMAC_FINAL(&hm,_A,&A_l); ssl_debug_printf("tls_hash: hmac 1 len %d\n",A_l); A=_A; SSL_HMAC_INIT(&hm,secret->data,secret->data_len,md); SSL_HMAC_UPDATE(&hm,A,A_l); SSL_HMAC_UPDATE(&hm,seed->data,seed->data_len); SSL_HMAC_FINAL(&hm,tmp,&tmp_l); ssl_debug_printf("tls_hash: hmac 2 len %d\n",tmp_l); tocpy=MIN(left,tmp_l); memcpy(ptr,tmp,tocpy); ptr+=tocpy; left-=tocpy; } SSL_HMAC_CLEANUP(&hm); ssl_print_string("hash out", out); return (0); } static int tls_prf(StringInfo* secret,char *usage, StringInfo* rnd1, StringInfo* rnd2, StringInfo* out) { StringInfo seed, sha_out, md5_out; u_int8_t *ptr; StringInfo s1, s2; unsigned int i,s_l, r=-1; int usage_len = strlen(usage); /* initalize buffer for sha, md5 random seed*/ if (ssl_data_alloc(&sha_out, MAX(out->data_len,20)) < 0) return -1; if (ssl_data_alloc(&md5_out, MAX(out->data_len,16)) < 0) goto free_sha; if (ssl_data_alloc(&seed, usage_len+rnd1->data_len+rnd2->data_len) < 0) goto free_md5; ptr=seed.data; memcpy(ptr,usage,usage_len); ptr+=usage_len; memcpy(ptr,rnd1->data,rnd1->data_len); ptr+=rnd1->data_len; memcpy(ptr,rnd2->data,rnd2->data_len); ptr+=rnd2->data_len; /* initalize buffer for client/server seeds*/ s_l=secret->data_len/2 + secret->data_len%2; if (ssl_data_alloc(&s1, s_l) < 0) goto free_seed; if (ssl_data_alloc(&s2, s_l) < 0) goto free_s1; memcpy(s1.data,secret->data,s_l); memcpy(s2.data,secret->data + (secret->data_len - s_l),s_l); ssl_debug_printf("tls_prf: tls_hash(md5 secret_len %d seed_len %d )\n", s1.data_len, seed.data_len); if(tls_hash(&s1,&seed,SSL_GET_DIGEST_BY_NAME("MD5"),&md5_out) != 0) goto free_all; ssl_debug_printf("tls_prf: tls_hash(sha)\n"); if(tls_hash(&s2,&seed,SSL_GET_DIGEST_BY_NAME("SHA1"),&sha_out) != 0) goto free_all; for(i=0;i<out->data_len;i++) out->data[i]=md5_out.data[i] ^ sha_out.data[i]; r =0; ssl_print_string("PRF out",out); free_all: free(s2.data); free_s1: free(s1.data); free_seed: free(seed.data); free_md5: free(md5_out.data); free_sha: free(sha_out.data); return r; } static int ssl3_generate_export_iv(StringInfo* r1, StringInfo* r2, StringInfo* out) { SSL_MD5_CTX md5; u_int8_t tmp[16]; SSL_MD5_INIT(&md5); SSL_MD5_UPDATE(&md5,r1->data,r1->data_len); SSL_MD5_UPDATE(&md5,r2->data,r2->data_len); SSL_MD5_FINAL(tmp,&md5); memcpy(out->data,tmp,out->data_len); ssl_print_string("export iv", out); return(0); } static int ssl3_prf(StringInfo* secret, char* usage, StringInfo* r1, StringInfo* r2,StringInfo* out) { SSL_MD5_CTX md5; SSL_SHA_CTX sha; StringInfo *rnd1,*rnd2; unsigned int off; int i=0,j; u_int8_t buf[20]; rnd1=r1; rnd2=r2; SSL_MD5_INIT(&md5); memset(&sha,0,sizeof(sha)); SSL_SHA_INIT(&sha); for(off=0;off<out->data_len;off+=16){ char outbuf[16]; int tocpy; i++; ssl_debug_printf("ssl3_prf: sha1_update(%d)\n",i); /* A, BB, CCC, ... */ for(j=0;j<i;j++){ buf[j]=64+i; } SSL_SHA_UPDATE(&sha,buf,i); if (secret) SSL_SHA_UPDATE(&sha,secret->data,secret->data_len); if(!strcmp(usage,"client write key") || !strcmp(usage,"server write key")){ SSL_SHA_UPDATE(&sha,rnd2->data,rnd2->data_len); SSL_SHA_UPDATE(&sha,rnd1->data,rnd1->data_len); } else{ SSL_SHA_UPDATE(&sha,rnd1->data,rnd1->data_len); SSL_SHA_UPDATE(&sha,rnd2->data,rnd2->data_len); } SSL_SHA_FINAL(buf,&sha); SSL_SHA_INIT(&sha); ssl_debug_printf("ssl3_prf: md5_update(%d)\n",i); SSL_MD5_UPDATE(&md5,secret->data,secret->data_len); SSL_MD5_UPDATE(&md5,buf,20); SSL_MD5_FINAL(outbuf,&md5); tocpy=MIN(out->data_len-off,16); memcpy(out->data+off,outbuf,tocpy); SSL_MD5_INIT(&md5); } return(0); } int ssl_create_decoder(SslDecoder *dec, SslCipherSuite *cipher_suite, u_int8_t *mk, u_int8_t *sk, u_int8_t *iv) { #if USE_OPENSSL const EVP_CIPHER *ciph=0; #else int ciph=0; #endif /* Find the SSLeay cipher */ if(cipher_suite->enc!=ENC_NULL) { ssl_debug_printf("ssl_create_decoder CIPHER: %s\n", ciphers[cipher_suite->enc-0x30]); ciph=SSL_GET_CIPHER_BY_NAME(ciphers[cipher_suite->enc-0x30]); } if (ciph == 0) { ssl_debug_printf("ssl_create_decoder can't find cipher %s\n", ciphers[cipher_suite->enc-0x30]); return -1; } dec->cipher_suite=cipher_suite; dec->mac_key.data = dec->_mac_key; ssl_data_set(&dec->mac_key, mk, cipher_suite->dig_len); SSL_CIPHER_INIT(&dec->evp,ciph,sk,iv,0); ssl_debug_printf("decoder initialized (digest len %d)\n", cipher_suite->dig_len); return 0; } int ssl_generate_keyring_material(SslDecryptSession*ssl_session) { StringInfo key_block; u_int8_t _iv_c[8],_iv_s[8]; u_int8_t _key_c[16],_key_s[16]; int needed; u_int8_t *ptr,*c_wk,*s_wk,*c_mk,*s_mk,*c_iv,*s_iv; /* if master_key is not yet generate, create it now*/ if (!(ssl_session->state & SSL_MASTER_SECRET)) { ssl_debug_printf("ssl_generate_keyring_material:PRF(pre_master_secret)\n"); if (PRF(ssl_session,&ssl_session->pre_master_secret,"master secret", &ssl_session->client_random, &ssl_session->server_random, &ssl_session->master_secret)) { ssl_debug_printf("ssl_generate_keyring_material can't generate master_secret\n"); return -1; } ssl_print_string("master secret",&ssl_session->master_secret); } /* Compute the key block. First figure out how much data we need*/ needed=ssl_session->cipher_suite.dig_len*2; needed+=ssl_session->cipher_suite.bits / 4; if(ssl_session->cipher_suite.block>1) needed+=ssl_session->cipher_suite.block*2; key_block.data_len = needed; key_block.data = malloc(needed); if (!key_block.data) { ssl_debug_printf("ssl_generate_keyring_material can't allacate key_block\n"); return -1; } ssl_debug_printf("ssl_generate_keyring_material sess key generation\n"); if (PRF(ssl_session,&ssl_session->master_secret,"key expansion", &ssl_session->server_random,&ssl_session->client_random, &key_block)) { ssl_debug_printf("ssl_generate_keyring_material can't generate key_block\n"); goto fail; } ssl_print_string("key expansion", &key_block); ptr=key_block.data; c_mk=ptr; ptr+=ssl_session->cipher_suite.dig_len; s_mk=ptr; ptr+=ssl_session->cipher_suite.dig_len; c_wk=ptr; ptr+=ssl_session->cipher_suite.eff_bits/8; s_wk=ptr; ptr+=ssl_session->cipher_suite.eff_bits/8; if(ssl_session->cipher_suite.block>1){ c_iv=ptr; ptr+=ssl_session->cipher_suite.block; s_iv=ptr; ptr+=ssl_session->cipher_suite.block; } if(ssl_session->cipher_suite.export){ StringInfo iv_c,iv_s; StringInfo key_c,key_s; StringInfo k; if(ssl_session->cipher_suite.block>1){ iv_c.data = _iv_c; iv_c.data_len = sizeof(_iv_c); iv_s.data = _iv_s; iv_s.data_len = sizeof(_iv_s); if(ssl_session->version_netorder==SSLV3_VERSION){ ssl_debug_printf("ssl_generate_keyring_material ssl3_generate_export_iv\n"); if (ssl3_generate_export_iv(&ssl_session->client_random, &ssl_session->server_random,&iv_c)) { ssl_debug_printf("ssl_generate_keyring_material can't generate sslv3 client iv\n"); goto fail; } ssl_debug_printf("ssl_generate_keyring_material ssl3_generate_export_iv(2)\n"); if (ssl3_generate_export_iv(&ssl_session->server_random, &ssl_session->client_random,&iv_s)) { ssl_debug_printf("ssl_generate_keyring_material can't generate sslv3 server iv\n"); goto fail; } } else{ u_int8_t _iv_block[16]; StringInfo iv_block; StringInfo key_null; u_int8_t _key_null; key_null.data = &_key_null; key_null.data_len = 0; /* We only have room for 8 bit IVs, but that's all we should need. This is a sanity check */ if(ssl_session->cipher_suite.block>8) { ssl_debug_printf("ssl_generate_keyring_material cipher suite block must be at most 7 nut is %d\n", ssl_session->cipher_suite.block); goto fail; } iv_block.data = _iv_block; iv_block.data_len = sizeof(_iv_block); ssl_debug_printf("ssl_generate_keyring_material prf(iv_block)\n"); if(PRF(ssl_session,&key_null, "IV block", &ssl_session->client_random, &ssl_session->server_random,&iv_block)) { ssl_debug_printf("ssl_generate_keyring_material can't generate tls31 iv block\n"); goto fail; } memcpy(_iv_c,iv_block.data,8); memcpy(_iv_s,iv_block.data+8,8); } c_iv=_iv_c; s_iv=_iv_s; } if (ssl_session->version_netorder==SSLV3_VERSION){ SSL_MD5_CTX md5; ssl_debug_printf("ssl_generate_keyring_material MD5(client_random)\n"); SSL_MD5_INIT(&md5); SSL_MD5_UPDATE(&md5,c_wk,ssl_session->cipher_suite.eff_bits/8); SSL_MD5_UPDATE(&md5,ssl_session->client_random.data, ssl_session->client_random.data_len); SSL_MD5_UPDATE(&md5,ssl_session->server_random.data, ssl_session->server_random.data_len); SSL_MD5_FINAL(_key_c,&md5); c_wk=_key_c; SSL_MD5_INIT(&md5); ssl_debug_printf("ssl_generate_keyring_material MD5(server_random)\n"); SSL_MD5_UPDATE(&md5,s_wk,ssl_session->cipher_suite.eff_bits/8); SSL_MD5_UPDATE(&md5,ssl_session->server_random.data, ssl_session->server_random.data_len); SSL_MD5_UPDATE(&md5,ssl_session->client_random.data, ssl_session->client_random.data_len); SSL_MD5_FINAL(_key_s,&md5); s_wk=_key_s; } else{ key_c.data = _key_c; key_c.data_len = sizeof(_key_c); key_s.data = _key_s; key_s.data_len = sizeof(_key_s); k.data = c_wk; k.data_len = ssl_session->cipher_suite.eff_bits/8; ssl_debug_printf("ssl_generate_keyring_material PRF(key_c)\n"); if (PRF(ssl_session,&k,"client write key", &ssl_session->client_random, &ssl_session->server_random, &key_c)) { ssl_debug_printf("ssl_generate_keyring_material can't generate tll31 server key \n"); goto fail; } c_wk=_key_c; k.data = s_wk; k.data_len = ssl_session->cipher_suite.eff_bits/8; ssl_debug_printf("ssl_generate_keyring_material PRF(key_s)\n"); if(PRF(ssl_session,&k,"server write key", &ssl_session->client_random, &ssl_session->server_random, &key_s)) { ssl_debug_printf("ssl_generate_keyring_material can't generate tll31 client key \n"); goto fail; } s_wk=_key_s; } } ssl_print_data("Client MAC key",c_mk,ssl_session->cipher_suite.dig_len); ssl_print_data("Server MAC key",s_mk,ssl_session->cipher_suite.dig_len); ssl_print_data("Client Write key",c_wk,ssl_session->cipher_suite.bits/8); ssl_print_data("Server Write key",s_wk,ssl_session->cipher_suite.bits/8); if(ssl_session->cipher_suite.block>1){ ssl_print_data("Client Write IV",c_iv,ssl_session->cipher_suite.block); ssl_print_data("Server Write IV",s_iv,ssl_session->cipher_suite.block); } ssl_debug_printf("ssl_generate_keyring_material ssl_create_decoder(client)\n"); if(ssl_create_decoder(&ssl_session->client, &ssl_session->cipher_suite,c_mk,c_wk,c_iv)) { ssl_debug_printf("ssl_generate_keyring_material can't init client decoder\n"); goto fail; } ssl_debug_printf("ssl_generate_keyring_material ssl_create_decoder(server)\n"); if(ssl_create_decoder(&ssl_session->server, &ssl_session->cipher_suite,s_mk,s_wk,s_iv)) { ssl_debug_printf("ssl_generate_keyring_material can't init client decoder\n"); goto fail; } free(key_block.data); return 0; fail: free(key_block.data); return -1; } int ssl_decrypt_pre_master_secret(SslDecryptSession*ssl_session, StringInfo* entrypted_pre_master, SSL_PRIVATE_KEY *pk) { int i; if(ssl_session->cipher_suite.kex!=KEX_RSA) { ssl_debug_printf("ssl_decrypt_pre_master_secret key %d diferent from KEX_RSA(%d)\n", ssl_session->cipher_suite.kex, KEX_RSA); return(-1); } #if 0 /* can'f find any place where ephemeral_rsa is set ...*/ if(d->ephemeral_rsa) { ssl_debug_printf("ssl_decrypt_pre_master_secret ephimeral RSA\n"); return(-1); } #endif #if USE_OPENSSL if(pk->type!= EVP_PKEY_RSA) { ssl_debug_printf("ssl_decrypt_pre_master_secret type %d different from %d \n", pk->type, EVP_PKEY_RSA); return(-1); } if(ssl_data_init(&ssl_session->pre_master_secret,NULL, BN_num_bytes(pk->pkey.rsa->n)) < 0) { ssl_debug_printf("ssl_decrypt_pre_master_secret can't init pre_master_secret buffer, len %d\n", BN_num_bytes(pk->pkey.rsa->n)); return -1; } /* with tls key loading will fail if not rsa type, so no need to check*/ #endif ssl_print_string("pre master encrypted",entrypted_pre_master); ssl_debug_printf("ssl_decrypt_pre_master_secret:RSA_private_decrypt\n"); i=SSL_PRIVATE_DECYPT(entrypted_pre_master->data_len, entrypted_pre_master->data,&ssl_session->pre_master_secret.data, pk); ssl_debug_printf("ssl_decrypt_pre_master_secret private decripyt(%d) %d\n", entrypted_pre_master->data_len,i); if(i!=48) { return -1; } ssl_session->pre_master_secret.data_len=48; ssl_print_string("pre master secret",&ssl_session->pre_master_secret); /* Remove the master secret if it was there to force keying material regeneration in case we're renegotiating */ ssl_session->state &= ~SSL_MASTER_SECRET; return 0; } #define MSB(a) ((a>>8)&0xff) #define LSB(a) (a&0xff) /* This should go to 2^128, but we're never really going to see more than 2^64, so we cheat*/ static int fmt_seq(u_int32_t num, u_int8_t* buf) { u_int32_t netnum; memset(buf,0,8); netnum=htonl(num); memcpy(buf+4,&netnum,4); return(0); } static int tls_check_mac(SslDecoder*decoder, int ct,int ver, u_int8_t* data, u_int32_t datalen, u_int8_t* mac) { SSL_HMAC hm; #ifdef USE_OPENSSL const EVP_MD *md; #else int md; #endif u_int32_t l; u_int8_t buf[20]; memset(&hm, 0, sizeof(hm)); md=SSL_GET_DIGEST_BY_NAME(digests[decoder->cipher_suite->dig-0x40]); SSL_HMAC_INIT(&hm,decoder->mac_key.data,decoder->mac_key.data_len,md); fmt_seq(decoder->seq,buf); decoder->seq++; SSL_HMAC_UPDATE(&hm,buf,8); buf[0]=ct; SSL_HMAC_UPDATE(&hm,buf,1); buf[0]=MSB(ver); buf[1]=LSB(ver); SSL_HMAC_UPDATE(&hm,buf,2); buf[0]=MSB(datalen); buf[1]=LSB(datalen); SSL_HMAC_UPDATE(&hm,buf,2); SSL_HMAC_UPDATE(&hm,data,datalen); SSL_HMAC_FINAL(&hm,buf,&l); if(memcmp(mac,buf,l)) return -1; SSL_HMAC_CLEANUP(&hm); return(0); } int ssl3_check_mac(SslDecoder*decoder,int ct,u_int8_t* data, u_int32_t datalen, u_int8_t* mac) { SSL_MD mc; #ifdef USE_OPENSSL const EVP_MD *md; #else int md; #endif u_int32_t l; u_int8_t buf[64],dgst[20]; int pad_ct; pad_ct=(decoder->cipher_suite->dig==DIG_SHA)?40:48; md=SSL_GET_DIGEST_BY_NAME(digests[decoder->cipher_suite->dig-0x40]); memset(&mc, 0, sizeof(mc)); SSL_MD_INIT(&mc,md); SSL_MD_UPDATE(&mc,decoder->mac_key.data,decoder->mac_key.data_len); memset(buf,0x36,pad_ct); SSL_MD_UPDATE(&mc,buf,pad_ct); fmt_seq(decoder->seq,buf); decoder->seq++; SSL_MD_UPDATE(&mc,buf,8); buf[0]=ct; SSL_MD_UPDATE(&mc,buf,1); buf[0]=MSB(datalen); buf[1]=LSB(datalen); SSL_MD_UPDATE(&mc,buf,2); SSL_MD_UPDATE(&mc,data,datalen); SSL_MD_FINAL(&mc,dgst,&l); SSL_MD_INIT(&mc,md); SSL_MD_UPDATE(&mc,decoder->mac_key.data,decoder->mac_key.data_len); memset(buf,0x5c,pad_ct); SSL_MD_UPDATE(&mc,buf,pad_ct); SSL_MD_UPDATE(&mc,dgst,l); SSL_MD_FINAL(&mc,dgst,&l); if(memcmp(mac,dgst,l)) return -1; return(0); } int ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, int ct, const char* in, int inl,char*out,int* outl) { int pad; u_int8_t *mac; ssl_debug_printf("ssl_decrypt_record ciphertext len %d\n", inl); /*ssl_print_data("Ciphertext",in, inl);*/ /* First decrypt*/ SSL_CIPHER_DECRYPT(&decoder->evp,out,in,inl); /*ssl_print_data("Plaintext",out,inl);*/ {static int __idx = 0; if (__idx++ > 0)ssl_print_text_data("\n\n",out, inl - decoder->cipher_suite->dig_len);} *outl=inl; /* Now strip off the padding*/ if(decoder->cipher_suite->block!=1){ pad=out[inl-1]; *outl-=(pad+1); } /* And the MAC */ *outl-=decoder->cipher_suite->dig_len; mac=out+(*outl); /*ssl_print_data("Record data",out,*outl);*/ /* Now check the MAC */ ssl_debug_printf("checking mac (len %d, version %X, ct %d)", *outl,ssl->version_netorder, ct); if(ssl->version_netorder==0x300){ if(ssl3_check_mac(decoder,ct,out,*outl,mac) < 0) { ssl_debug_printf("mac falied\n"); return -1; } } else{ if(tls_check_mac(decoder,ct,ssl->version_netorder,out,*outl,mac)< 0) { ssl_debug_printf("mac falied\n"); return -1; } } ssl_debug_printf("mac ok\n"); return(0); } SSL_PRIVATE_KEY* ssl_load_key(FILE* fp) { #if USE_OPENSSL return PEM_read_PrivateKey(fp, NULL, NULL,"XXX"); #else /* gnutls make our work much harded, since we have to work internally with * s-exp formatted data, but PEM loader export only in "gnutls_datum" * format, and a datum -> s-exp convertion function does not exist. */ struct gnutls_x509_privkey_int* priv_key; gnutls_datum key; gnutls_datum m, e, d, p,q, u; int size; unsigned int bytes; int tmp_size; gcry_mpi_t rsa_params[6]; gcry_sexp_t rsa_priv_key; /* init private key data*/ gnutls_x509_privkey_init(&priv_key); /* compute file size and load all file contents into a datum buffer*/ if (fseek(fp, 0, SEEK_END) < 0) { ssl_debug_printf("ssl_load_key: can't fseek file\n"); return NULL; } if ((size = ftell(fp)) < 0) { ssl_debug_printf("ssl_load_key: can't ftell file\n"); return NULL; } if (fseek(fp, 0, SEEK_SET) < 0) { ssl_debug_printf("ssl_load_key: can't refseek file\n"); return NULL; } key.data = g_malloc(size); key.size = size; bytes = fread(key.data, 1, key.size, fp); if (bytes < key.size) { ssl_debug_printf("ssl_load_key: can't read from file %d bytes, got %d\n", key.size, bytes); return NULL; } /* import PEM data*/ if (gnutls_x509_privkey_import(priv_key, &key, GNUTLS_X509_FMT_PEM)!=0) { ssl_debug_printf("ssl_load_key: can't import pem data\n"); return NULL; } free(key.data); /* RSA get parameter */ if (gnutls_x509_privkey_export_rsa_raw(priv_key, &m, &e, &d, &p, &q, &u) != 0) { ssl_debug_printf("ssl_load_key: can't export rsa param (is a rsa private key file ?!?)\n"); return NULL; } /* convert each rsa parameter to mpi format*/ if (gcry_mpi_scan( &rsa_params[0], GCRYMPI_FMT_USG, m.data, m.size, &tmp_size) !=0) { ssl_debug_printf("ssl_load_key: can't convert m rsa param to int (size %d)\n", m.size); return NULL; } ssl_debug_printf("ssl_load_key: got rsa param m size %d\n", tmp_size); if (gcry_mpi_scan( &rsa_params[1], GCRYMPI_FMT_USG, e.data, e.size, &tmp_size) != 0) { ssl_debug_printf("ssl_load_key: can't convert e rsa param to int (size %d)\n", e.size); return NULL; } ssl_debug_printf("ssl_load_key: got rsa param e size %d\n", tmp_size); if (gcry_mpi_scan( &rsa_params[2], GCRYMPI_FMT_USG, d.data, d.size, &tmp_size) !=0) { ssl_debug_printf("ssl_load_key: can't convert d rsa param to int (size %d)\n", m.size); return NULL; } ssl_debug_printf("ssl_load_key: got rsa param d size %d\n", tmp_size); if (gcry_mpi_scan( &rsa_params[3], GCRYMPI_FMT_USG, p.data, p.size, &tmp_size) !=0) { ssl_debug_printf("ssl_load_key: can't convert m rsa param to int (size %d)\n", p.size); return NULL; } ssl_debug_printf("ssl_load_key: got rsa param p size %d\n", tmp_size); if (gcry_mpi_scan( &rsa_params[4], GCRYMPI_FMT_USG, q.data, q.size, &tmp_size) !=0) { ssl_debug_printf("ssl_load_key: can't convert q rsa param to int (size %d)\n", m.size); return NULL; } ssl_debug_printf("ssl_load_key: got rsa param q size %d\n", tmp_size); if (gcry_mpi_scan( &rsa_params[5], GCRYMPI_FMT_USG, u.data, u.size, &tmp_size) !=0) { ssl_debug_printf("ssl_load_key: can't convert u rsa param to int (size %d)\n", m.size); return NULL; } ssl_debug_printf("ssl_load_key: got rsa param u size %d\n", tmp_size); if (gcry_sexp_build( &rsa_priv_key, NULL, "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", rsa_params[0], rsa_params[1], rsa_params[2], rsa_params[3], rsa_params[4], rsa_params[5]) != 0) { ssl_debug_printf("ssl_load_key: can't built rsa private key s-exp\n"); return NULL; } return rsa_priv_key; #endif }
/* This code is based on ssldump. See: http://www.rtfm.com/ssldump/ Copyright (C) 1999-2001 RTFM, Inc. All Rights Reserved This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla <ekr@xxxxxxxx> and licensed by RTFM, Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by Eric Rescorla for RTFM, Inc. 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE ERIC RESCORLA AND RTFM ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __SSL_UTILS_H_ #define __SSL_UTILS_H_ //#define USE_OPENSSL 1 #if USE_OPENSSL #include <openssl/pem.h> #include <openssl/ssl.h> #include <openssl/hmac.h> #include <openssl/evp.h> #include <openssl/x509v3.h> #define SSL_CIPHER_CTX EVP_CIPHER_CTX #define SSL_PRIVATE_KEY EVP_PKEY #define SSL_LIB_INIT OpenSSL_add_all_algorithms #else #include <gcrypt.h> #include <gnutls/x509.h> #include <gnutls/openssl.h> #define SSL_CIPHER_CTX gcry_cipher_hd_t #define SSL_PRIVATE_KEY struct gcry_sexp #define SSL_LIB_INIT gnutls_global_init #endif #include <epan/address.h> #include <netinet/in.h> typedef struct _StringInfo { char* data; unsigned int data_len; } StringInfo; void ssl_debug_printf(char* fmt,...); void ssl_print_data(const char* name, const char* data, int len); void ssl_print_string(const char* name, const StringInfo* data); void ssl_print_text_data(const char* name, const char* data, int len); SSL_PRIVATE_KEY* ssl_load_key(FILE* fp); #define SSL_WRITE_KEY 1 #define SSLV3_VERSION 0x300 #define TLSV1_VERSION 0x301 #define SSL_CLIENT_RANDOM 1 #define SSL_SERVER_RANDOM 2 #define SSL_CIPHER 4 #define SSL_HAVE_SESSION_KEY 8 #define SSL_VERSION 0x10 #define SSL_MASTER_SECRET 0x20 typedef struct _SslCipherSuite { int number; int kex; int sig; int enc; int block; int bits; int eff_bits; int dig; int dig_len; int export; } SslCipherSuite; #define SSL_DECRYPTED_LEN 512 typedef struct _SslDecoder { SslCipherSuite* cipher_suite; char _mac_key[20]; char decrypted_data[SSL_DECRYPTED_LEN]; StringInfo mac_key; SSL_CIPHER_CTX evp; u_int32_t seq; } SslDecoder; #define KEX_RSA 0x10 #define KEX_DH 0x11 #define SIG_RSA 0x20 #define SIG_DSS 0x21 #define SIG_NONE 0x22 #define ENC_DES 0x30 #define ENC_3DES 0x31 #define ENC_RC4 0x32 #define ENC_RC2 0x33 #define ENC_IDEA 0x34 #define ENC_NULL 0x35 #define DIG_MD5 0x40 #define DIG_SHA 0x41 typedef struct _SslService { address addr; guint port; } SslService; typedef struct _SslDecryptSession { char _master_secret[48]; char _session_id[256]; char _client_random[32]; char _server_random[32]; StringInfo session_id; StringInfo server_random; StringInfo client_random; StringInfo master_secret; StringInfo pre_master_secret; int cipher; int state; SslCipherSuite cipher_suite; SslDecoder server; SslDecoder client; SSL_PRIVATE_KEY* private_key; guint version; guint16 version_netorder; } SslDecryptSession; int ssl_data_set(StringInfo* data, char* src, unsigned int len); int ssl_data_init(StringInfo* data, char* src, unsigned int len); SslDecryptSession* ssl_alloc_session(void); int ssl_find_cipher(int num,SslCipherSuite* cs); int ssl_packet_from_server(int port); int ssl_generate_keyring_material(SslDecryptSession*ssl_session); int ssl_decrypt_pre_master_secret(SslDecryptSession*ssl_session, StringInfo* entrypted_pre_master, SSL_PRIVATE_KEY *pk); int ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, int ct, const char* in, int inl,char*out,int* outl); #endif
--- Makefile.am-0.10.10 2005-04-15 14:53:47.319803976 +0200 +++ Makefile.am 2005-04-15 12:47:55.049923512 +0200 @@ -241,7 +241,8 @@ @SNMP_LIBS@ @SSL_LIBS@ \ $(plugin_ldadd) \ @PCRE_LIBS@ \ - @PCAP_LIBS@ @GTK_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ @FRAMEWORKS@ + @PCAP_LIBS@ @GTK_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ @FRAMEWORKS@ \ + @LIBGNUTLS_LIBS@ # Additional libs that I know how to build. These will be # linked into the tethereal executable. @@ -263,7 +264,8 @@ $(plugin_ldadd) \ @PCRE_LIBS@ \ @GLIB_LIBS@ -lm \ - @PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ + @PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ \ + @LIBGNUTLS_LIBS@ if ENABLE_STATIC tethereal_LDFLAGS = -Wl,-static -all-static @@ -379,7 +381,8 @@ $(plugin_ldadd) \ @PCRE_LIBS@ \ @GLIB_LIBS@ -lm \ - @PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ + @PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ \ + @LIBGNUTLS_LIBS@ dftest_LDFLAGS = -export-dynamic --- epan/dissectors/Makefile.common-0.10.10 2005-04-15 14:54:06.253925552 +0200 +++ epan/dissectors/Makefile.common 2005-04-13 11:31:18.000000000 +0200 @@ -501,6 +501,7 @@ packet-skinny.c \ packet-slimp3.c \ packet-sll.c \ + packet-ssl-utils.c \ packet-slowprotocols.c \ packet-slsk.c \ packet-smb-browse.c \ --- configure.in-0.10.10 2005-04-15 14:53:35.294632080 +0200 +++ configure.in 2005-04-15 12:54:43.590815856 +0200 @@ -55,6 +55,13 @@ AC_CHECK_PROG(HAVE_DOXYGEN, doxygen, "yes", "no") AM_CONDITIONAL(HAVE_DOXYGEN, test x$HAVE_DOXYGEN = xyes) +# gnu tls +AM_PATH_LIBGNUTLS(1.0.0, , [ + AC_DEFINE(USE_OPENSSL, 1, [use openssl for ssl session decryption instead of gnutls]) + AC_SUBST(USE_OPENSSL) +]) + + # Check for xsltproc AC_PATH_PROG(XSLTPROC, xsltproc) AC_CHECK_PROG(HAVE_XSLTPROC, xsltproc, "yes", "no") --- epan/dissectors/packet-ssl.c-0.10.10 2005-04-15 14:54:36.252365096 +0200 +++ epan/dissectors/packet-ssl.c 2005-04-15 13:39:40.321850424 +0200 @@ -56,14 +56,7 @@ * * Notes: * - * - Uses conversations in a no-malloc fashion. Since we just want to - * remember the version of the conversation, we store the version - * integer directly in the void *data member of the conversation - * structure. This means that we don't have to manage any memory, - * but will cause problems if anyone assumes that all data pointers - * are actually pointers to memory allocated by g_mem_chunk_alloc. - * - * - Does not support decryption of encrypted frames, nor dissection + * - Does not support dissection * of frames that would require state maintained between frames * (e.g., single ssl records spread across multiple tcp frames) * @@ -82,6 +75,18 @@ * - Request Certificate * - Client Certificate * + * - Decryption is supported only for session that use RSA key exchange, + * if the host private key is provided via preference. + * + * - Decryption need to be performed 'sequentially', so it's done + * at packet reception time. This may cause a significative packet capture + * slow down. This also cause do dissect some ssl info that in previous + * dissector version were dissected only when a proto_tree context was + * available + * + * XXXX I assume packet reception time if prote_tree is not null, because + * I can't access easily capture_file state. + * */ #ifdef HAVE_CONFIG_H @@ -97,6 +102,8 @@ #include <epan/conversation.h> #include <epan/prefs.h> #include <epan/dissectors/packet-x509af.h> +#include <epan/dissectors/packet-ssl-utils.h> + static gboolean ssl_desegment = TRUE; @@ -114,6 +121,7 @@ static int hf_ssl_record_version = -1; static int hf_ssl_record_length = -1; static int hf_ssl_record_appdata = -1; +static int hf_ssl_record_appdata_decrypted = -1; static int hf_ssl2_record = -1; static int hf_ssl2_record_is_escape = -1; static int hf_ssl2_record_padding_length = -1; @@ -199,6 +207,70 @@ static gint ett_pct_cert_suites = -1; static gint ett_pct_exch_suites = -1; +typedef struct { + unsigned int ssl_port; + dissector_handle_t handle; +} SslDissector; + +static char* ssl_keys_list = NULL; +static SslDissector ssl_dissectors[] = { + {443, 0}, /* https */ + {636, 0}, /* ldap */ + {993, 0}, /* imap */ + {995, 0}, /* pop3 */ + {0,0}}; + + +/* Hash Functions */ +static gint ssl_equal (gconstpointer v, gconstpointer v2) +{ + const StringInfo *val1 = (const StringInfo *)v; + const StringInfo *val2 = (const StringInfo *)v2; + + if (val1->data_len == val2->data_len && + !memcmp(val1->data, val2->data, val2->data_len)) { + return 1; + } + return 0; +} + +static guint ssl_hash (gconstpointer v) +{ + guint l,hash = 0; + StringInfo* id = (StringInfo*) v; + guint* cur = (guint*) id->data; + for (l=4; (l<id->data_len); l+=4, cur++) + hash = hash ^ (*cur); + + return hash; +} +static gint ssl_private_key_equal (gconstpointer v, gconstpointer v2) +{ + const SslService *val1 = (const SslService *)v; + const SslService *val2 = (const SslService *)v2; + + if ((val1->port == val2->port) && + ! CMP_ADDRESS(&val1->addr, &val2->addr)) { + return 1; + } + return 0; +} + +static guint ssl_private_key_hash (gconstpointer v) +{ + const SslService *key = (const SslService *)v; + guint l,hash = key->port, len = key->addr.len; + + guint* cur = (guint*) key->addr.data; + for (l=4; (l<len); l+=4, cur++) + hash = hash ^ (*cur); + + return hash; +} + +static GHashTable *ssl_session_hash = NULL; +static GHashTable *ssl_key_hash = NULL; + /* The TCP port to associate with by default */ #define TCP_PORT_SSL 443 #define TCP_PORT_SSL_LDAP 636 @@ -704,7 +776,8 @@ static int dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint *conv_version, - gboolean *need_desegmentation); + gboolean *need_desegmentation, + SslDecryptSession *conv_data); /* change cipher spec dissector */ static void dissect_ssl3_change_cipher_spec(tvbuff_t *tvb, @@ -715,22 +788,26 @@ /* alert message dissector */ static void dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, - guint *conv_version); + guint *conv_version, + SslDecryptSession *conv_data); /* handshake protocol dissector */ static void dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint32 record_length, - guint *conv_version, guint8 content_type); + guint *conv_version, + SslDecryptSession *conv_data, guint8 content_type); static void dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb, proto_tree *tree, - guint32 offset, guint32 length); + guint32 offset, guint32 length, + SslDecryptSession* ssl); static void dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb, proto_tree *tree, - guint32 offset, guint32 length); + guint32 offset, guint32 length, + SslDecryptSession* ssl); static void dissect_ssl3_hnd_cert(tvbuff_t *tvb, proto_tree *tree, guint32 offset, packet_info *pinfo); @@ -742,7 +819,7 @@ static void dissect_ssl3_hnd_finished(tvbuff_t *tvb, proto_tree *tree, guint32 offset, - guint *conv_version); + guint* conv_version); /* @@ -794,7 +871,7 @@ * Support Functions * */ -static void ssl_set_conv_version(packet_info *pinfo, guint version); +/*static void ssl_set_conv_version(packet_info *pinfo, guint version);*/ static int ssl_is_valid_handshake_type(guint8 type); static int ssl_is_valid_content_type(guint8 type); static int ssl_is_valid_ssl_version(guint16 version); @@ -809,7 +886,9 @@ static int ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb, guint32 offset, guint32 record_length); - +static dissector_handle_t ssl_find_dissector(packet_info* pinfo); +static void ssl_save_session(SslDecryptSession* ssl); +static void ssl_restore_session(SslDecryptSession* ssl); /********************************************************************* * * Main dissector @@ -824,12 +903,15 @@ conversation_t *conversation; void *conv_data; - guint conv_version = SSL_VER_UNKNOWN; proto_item *ti = NULL; proto_tree *ssl_tree = NULL; guint32 offset = 0; gboolean first_record_in_frame = TRUE; gboolean need_desegmentation; + int from_server; + SslDecryptSession* ssl_session; + SslService dummy; + guint* conv_version; /* Track the version using conversations to reduce the * chance that a packet that simply *looks* like a v2 or @@ -852,11 +934,46 @@ pinfo->srcport, pinfo->destport, 0); } conv_data = conversation_get_proto_data(conversation, proto_ssl); - if (conv_data != NULL) - { - conv_version = GPOINTER_TO_UINT(conv_data); + + /* PAOLO: manage ssl decryption data */ + + /* we need to know witch side of conversation is speaking*/ + if (ssl_packet_from_server(pinfo->srcport)) { + from_server = 1; + dummy.addr = pinfo->net_src; + dummy.port = pinfo->srcport; + } + else { + from_server = 0; + dummy.addr = pinfo->net_dst; + dummy.port = pinfo->destport; } + /*get a valid ssl session pointer*/ + if (conv_data != NULL) + ssl_session = conv_data; + else { + ssl_session= ssl_alloc_session(); + ssl_session->version = SSL_VER_UNKNOWN; + conversation_add_proto_data(conversation, proto_ssl, ssl_session); + + /* try to retrive private key for this service. Do it now 'cause pinfo + * is not always available*/ + ssl_session->private_key = g_hash_table_lookup(ssl_key_hash, &dummy); + if (!ssl_session->private_key) { + ssl_debug_printf("dissect_ssl can't find private key for " + "%hhd.%hhd.%hhd.%hhd:%d\n", dummy.addr.data[0], + dummy.addr.data[1],dummy.addr.data[2], + dummy.addr.data[3],dummy.port); + } + } + conv_version= & ssl_session->version; + + /* try do decryption only the first time we see this packet and if we have + * the server private key*/ + if (!ssl_session->private_key || pinfo->fd->flags.visited) + ssl_session = NULL; + /* Initialize the protocol column; we'll set it later when we * figure out what flavor of SSL it is (assuming we don't * throw an exception before we get the chance to do so). */ @@ -910,11 +1027,11 @@ /* first try to dispatch off the cached version * known to be associated with the conversation */ - switch(conv_version) { + switch(*conv_version) { case SSL_VER_SSLv2: case SSL_VER_PCT: offset = dissect_ssl2_record(tvb, pinfo, ssl_tree, - offset, &conv_version, + offset, conv_version, &need_desegmentation); break; @@ -929,14 +1046,15 @@ if (ssl_is_v2_client_hello(tvb, offset)) { offset = dissect_ssl2_record(tvb, pinfo, ssl_tree, - offset, &conv_version, + offset, conv_version, &need_desegmentation); } else { offset = dissect_ssl3_record(tvb, pinfo, ssl_tree, - offset, &conv_version, - &need_desegmentation); + offset, conv_version, + &need_desegmentation, + ssl_session); } break; @@ -948,15 +1066,16 @@ { /* looks like sslv2 or pct client hello */ offset = dissect_ssl2_record(tvb, pinfo, ssl_tree, - offset, &conv_version, + offset, conv_version, &need_desegmentation); } else if (ssl_looks_like_sslv3(tvb, offset)) { /* looks like sslv3 or tls */ offset = dissect_ssl3_record(tvb, pinfo, ssl_tree, - offset, &conv_version, - &need_desegmentation); + offset, conv_version, + &need_desegmentation, + ssl_session); } else { @@ -972,7 +1091,7 @@ if (check_col(pinfo->cinfo, COL_PROTOCOL)) { col_set_str(pinfo->cinfo, COL_PROTOCOL, - ssl_version_short_names[conv_version]); + ssl_version_short_names[*conv_version]); } } break; @@ -981,15 +1100,6 @@ /* Desegmentation return check */ if (need_desegmentation) return; - - /* If we haven't already set the version information for - * this conversation, do so. */ - if (conv_data == NULL) - { - conv_data = GINT_TO_POINTER(conv_version); - conversation_add_proto_data(conversation, proto_ssl, conv_data); - } - /* set up for next record in frame, if any */ first_record_in_frame = FALSE; } @@ -1005,7 +1115,8 @@ static int dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, - guint *conv_version, gboolean *need_desegmentation) + guint *conv_version, gboolean *need_desegmentation, + SslDecryptSession* ssl) { /* @@ -1033,8 +1144,9 @@ proto_tree *ti = NULL; proto_tree *ssl_record_tree = NULL; guint32 available_bytes = 0; - gchar *proto_name_str = NULL; - + gchar *proto_name_str = NULL; + StringInfo* decrypted; + available_bytes = tvb_length_remaining(tvb, offset); /* @@ -1152,12 +1264,21 @@ if (version == 0x0300) { *conv_version = SSL_VER_SSLv3; - ssl_set_conv_version(pinfo, *conv_version); + if (ssl) { + ssl->version_netorder = version; + ssl->state |= SSL_VERSION; + } + /*ssl_set_conv_version(pinfo, ssl->version);*/ } else if (version == 0x0301) { + *conv_version = SSL_VER_TLS; - ssl_set_conv_version(pinfo, *conv_version); + if (ssl) { + ssl->version_netorder = version; + ssl->state |= SSL_VERSION; + } + /*ssl_set_conv_version(pinfo, ssl->version);*/ } } if (check_col(pinfo->cinfo, COL_PROTOCOL)) @@ -1191,25 +1312,108 @@ break; case SSL_ID_ALERT: dissect_ssl3_alert(tvb, pinfo, ssl_record_tree, offset, - conv_version); + conv_version, ssl); break; case SSL_ID_HANDSHAKE: dissect_ssl3_handshake(tvb, pinfo, ssl_record_tree, offset, - record_length, conv_version, content_type); + record_length, conv_version, ssl, content_type); break; case SSL_ID_APP_DATA: if (check_col(pinfo->cinfo, COL_INFO)) col_append_str(pinfo->cinfo, COL_INFO, "Application Data"); + if (ssl_record_tree) { - proto_name_str = match_strval(content_type, ssl_31_content_type); + proto_name_str = match_strval(content_type, ssl_31_content_type); proto_item_set_text(ssl_record_tree, "%s Record Layer: %s Protocol: Application Data", ssl_version_short_names[*conv_version], (proto_name_str!=NULL) ? proto_name_str : "unknown"); - proto_tree_add_item(ssl_record_tree, hf_ssl_record_appdata, tvb, - offset, record_length, 0); } + + /* PAOLO test if any decrypted data is attached to this packet*/ + if (ssl) + { + /* first time we visit this packet: try to decrypt appdata */ + int len; + SslDecoder* decoder; + StringInfo* data; + + /* retrive decoder for this packet direction*/ + if (ssl_packet_from_server(pinfo->srcport)) + decoder = &ssl->server; + else + decoder = &ssl->client; + ssl_debug_printf("dissect_ssl3_record app_data len %d ssl state %X\n", + record_length, ssl->state); + + /* if we can decrypt and decryption have success + * add decrypted data to this packet info*/ + if (ssl->state & SSL_HAVE_SESSION_KEY) { + if (ssl_decrypt_record(ssl, decoder, + content_type, tvb_get_ptr(tvb, offset, record_length), + record_length, decoder->decrypted_data, &len) == 0) { + data = p_get_proto_data(pinfo->fd, proto_ssl); + + if (!data) + { + ssl_debug_printf("dissect_ssl3_record allocating app_data %d bytes for app data\n", len); + /* first app data record: allocate and put packet data*/ + data = g_malloc0(sizeof(StringInfo)+ len); + data->data = ((char*)data) + sizeof(StringInfo); + ssl_data_set(data, decoder->decrypted_data, len); + } + else { + /* update previus record*/ + ssl_debug_printf("dissect_ssl3_record reallocating app_data " + "%d bytes for app data (total %d appdata bytes)\n", + len, data->data_len + len+sizeof(StringInfo)); + data = g_realloc(data, data->data_len + len+sizeof(StringInfo)); + data->data= ((char*)data) + sizeof(StringInfo); + memcpy(&data->data[data->data_len], decoder->decrypted_data, len); + data->data_len += len; + + /* realloc can change ptr so remove old one and readd the new one*/ + ssl_debug_printf("dissect_ssl3_record removing old app_data ptr\n"); + p_rem_proto_data(pinfo->fd, proto_ssl); + } + + ssl_debug_printf("dissect_ssl3_record setting decrypted app_data ptr %p\n",data); + p_add_proto_data(pinfo->fd, proto_ssl, data); + } + } + } + + decrypted = p_get_proto_data(pinfo->fd, proto_ssl); + if (decrypted && ssl_record_tree) + { + /* try to dissect decrypted data*/ + dissector_handle_t dissector; + ssl_debug_printf("dissect_ssl3_record decrypted len %d\n", decrypted->data_len); + + /* create new tvbuff for the decrypted data */ + tvbuff_t* new_tvb = tvb_new_real_data(decrypted->data, + decrypted->data_len, decrypted->data_len); + + /* XXXX is this correct ?!? */ + tvb_set_free_cb(new_tvb, g_free); + /*tvb_set_child_real_data_tvbuff(tvb, new_tvb);*/ + + /* find out a dissector using server port*/ + dissector = ssl_find_dissector(pinfo); + if (dissector) { + ssl_debug_printf("dissect_ssl3_record found dissector %p\n", dissector); + ssl_print_text_data("decrypted app data",decrypted->data, + decrypted->data_len); + call_dissector(dissector, new_tvb, pinfo, ssl_record_tree); + } + proto_tree_add_string(ssl_record_tree, hf_ssl_record_appdata_decrypted, tvb, + offset, decrypted->data_len, decrypted->data); + } + else + if (ssl_record_tree) + proto_tree_add_item(ssl_record_tree, hf_ssl_record_appdata, tvb, + offset, record_length, 0); break; default: @@ -1227,7 +1431,7 @@ static void dissect_ssl3_change_cipher_spec(tvbuff_t *tvb, proto_tree *tree, guint32 offset, - guint *conv_version, guint8 content_type) + guint* conv_version, guint8 content_type) { /* * struct { @@ -1235,7 +1439,7 @@ * } ChangeCipherSpec; * */ - gchar *proto_name_str = NULL; + gchar *proto_name_str = NULL; if (tree) { @@ -1253,7 +1457,8 @@ static void dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, - guint *conv_version) + guint* conv_version, + SslDecryptSession* ssl) { /* struct { * AlertLevel level; @@ -1296,6 +1501,10 @@ if (check_col(pinfo->cinfo, COL_INFO)) col_append_str(pinfo->cinfo, COL_INFO, "Encrypted Alert"); } + + /*PAOLO XXX must i decrypt encrypted alert ?!? */ + if (ssl) + ssl_debug_printf("XXXX alert decryption unhandled\n"); if (tree) { @@ -1327,7 +1536,8 @@ static void dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, - guint32 record_length, guint *conv_version, guint8 content_type) + guint32 record_length, guint *conv_version, + SslDecryptSession* ssl, guint8 content_type) { /* struct { * HandshakeType msg_type; @@ -1430,16 +1640,44 @@ /* if we don't have a valid handshake type, just quit dissecting */ if (!msg_type_str) { + /*PAOLO: this is (shold be) an encrypted message. we must + * try to decrypt it to keep decoder state 'aligned'*/ + SslDecoder* decoder; + int len = record_length - offset; + + /* can we try do to decryption ?!?*/ + if (!ssl) + return; + + /* retrive the decoder for this direction*/ + if (ssl_packet_from_server(pinfo->srcport)) + decoder = &ssl->server; + else + decoder = &ssl->client; + + /* run the decoder if we have the session key*/ + ssl_debug_printf("dissect_ssl3_handshake encrypted handshake msg len %d ssl state %X\n", + len, ssl->state); + if (ssl->state & SSL_HAVE_SESSION_KEY) + ssl_decrypt_record(ssl, decoder, content_type, tvb_get_ptr(tvb, offset, len), + len, decoder->decrypted_data, &len); return; } - - if (ssl_hand_tree) - { + + /* PAOLO: if we are doing ssl decryption we must dissect some requests type */ + if (ssl_hand_tree || ssl) + { + /* PAOLO: variables for session key creation*/ + StringInfo encrypted_pre_master; + int ret; + /* add nodes for the message type and message length */ - proto_tree_add_item(ssl_hand_tree, hf_ssl_handshake_type, - tvb, offset, 1, msg_type); + if (ssl_hand_tree) + proto_tree_add_item(ssl_hand_tree, hf_ssl_handshake_type, + tvb, offset, 1, msg_type); offset++; - proto_tree_add_uint(ssl_hand_tree, hf_ssl_handshake_length, + if (ssl_hand_tree) + proto_tree_add_uint(ssl_hand_tree, hf_ssl_handshake_length, tvb, offset, 3, length); offset += 3; @@ -1450,11 +1688,11 @@ break; case SSL_HND_CLIENT_HELLO: - dissect_ssl3_hnd_cli_hello(tvb, ssl_hand_tree, offset, length); + dissect_ssl3_hnd_cli_hello(tvb, ssl_hand_tree, offset, length, ssl); break; case SSL_HND_SERVER_HELLO: - dissect_ssl3_hnd_srv_hello(tvb, ssl_hand_tree, offset, length); + dissect_ssl3_hnd_srv_hello(tvb, ssl_hand_tree, offset, length, ssl); break; case SSL_HND_CERTIFICATE: @@ -1477,8 +1715,46 @@ /* unimplemented */ break; - case SSL_HND_CLIENT_KEY_EXCHG: - /* unimplemented */ + case SSL_HND_CLIENT_KEY_EXCHG: + /* PAOLO: here we can have all the data to build session key*/ + if (!ssl) + break; + + /* check for required session data */ + ssl_debug_printf("dissect_ssl3_handshake found SSL_HND_CLIENT_KEY_EXCHG state %X\n", + ssl->state); + if ((ssl->state & (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION)) != + (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION)) { + ssl_debug_printf("dissect_ssl3_handshake not enough data to generate key (required %X)\n", + (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION)); + break; + } + + /* get encrypted data, we must skip tls record len && version and + * 2 bytes of record data */ + encrypted_pre_master.data = g_malloc(length - 2); + encrypted_pre_master.data_len = length-2; + tvb_memcpy(tvb, encrypted_pre_master.data, offset+2, length-2); + + if (!ssl->private_key) { + ssl_debug_printf("dissect_ssl3_handshake can't find private key\n"); + break; + } + + /* go with ssl key processessing*/ + ret = ssl_decrypt_pre_master_secret(ssl, &encrypted_pre_master, ssl->private_key); + g_free(encrypted_pre_master.data); + if (ret < 0) { + ssl_debug_printf("dissect_ssl3_handshake can't decrypt pre master secret\n"); + break; + } + if (ssl_generate_keyring_material(ssl)<0) { + ssl_debug_printf("dissect_ssl3_handshake can't generate keyring material\n"); + break; + } + ssl->state |= SSL_HAVE_SESSION_KEY; + ssl_save_session(ssl); + ssl_debug_printf("dissect_ssl3_handshake session keys succesfully generated\n"); break; case SSL_HND_FINISHED: @@ -1488,10 +1764,12 @@ } } +#if 0 else { offset += 4; /* skip the handshake header */ } +#endif offset += length; first_iteration = FALSE; /* set up for next pass, if any */ } @@ -1499,44 +1777,85 @@ static int dissect_ssl3_hnd_hello_common(tvbuff_t *tvb, proto_tree *tree, - guint32 offset) + guint32 offset, SslDecryptSession* ssl, gint from_server) { /* show the client's random challenge */ - guint32 initial_offset = offset; + /* guint32 initial_offset = offset; */ nstime_t gmt_unix_time; guint8 session_id_length = 0; - + + if (ssl) + { + /* PAOLO: get proper peer information*/ + StringInfo* rnd; + if (from_server) + rnd = &ssl->server_random; + else + rnd = &ssl->client_random; + + /* get provided random for keyring generation*/ + tvb_memcpy(tvb, rnd->data, offset, 32); + rnd->data_len = 32; + if (from_server) + ssl->state |= SSL_SERVER_RANDOM; + else + ssl->state |= SSL_CLIENT_RANDOM; + ssl_debug_printf("dissect_ssl3_hnd_hello_common found random state %X\n", + ssl->state); + + session_id_length = tvb_get_guint8(tvb, offset + 32); + /* check stored session id info */ + if (from_server && (session_id_length == ssl->session_id.data_len) && + (tvb_memeql(tvb, offset+33, ssl->session_id.data, session_id_length) == 0)) + { + /* clinet/server id match: try to restore a previous cached session*/ + ssl_restore_session(ssl); + } + else { + /* reset state on renegotiation*/ + if (!from_server) + ssl->state &= ~(SSL_HAVE_SESSION_KEY|SSL_MASTER_SECRET| + SSL_CIPHER|SSL_SERVER_RANDOM); + + tvb_memcpy(tvb,ssl->session_id.data, offset+33, session_id_length); + ssl->session_id.data_len = session_id_length; + } + } + if (tree) { + /* show the time */ gmt_unix_time.secs = tvb_get_ntohl(tvb, offset); gmt_unix_time.nsecs = 0; proto_tree_add_time(tree, hf_ssl_handshake_random_time, - tvb, offset, 4, &gmt_unix_time); + tvb, offset, 4, &gmt_unix_time); offset += 4; /* show the random bytes */ proto_tree_add_item(tree, hf_ssl_handshake_random_bytes, tvb, offset, 28, 0); offset += 28; - + /* show the session id */ session_id_length = tvb_get_guint8(tvb, offset); proto_tree_add_item(tree, hf_ssl_handshake_session_id_len, - tvb, offset++, 1, 0); + tvb, offset++, 1, 0); + if (session_id_length > 0) - { + { proto_tree_add_bytes_format(tree, hf_ssl_handshake_session_id, - tvb, offset, session_id_length, - tvb_get_ptr(tvb, offset, session_id_length), - "Session ID (%u byte%s)", - session_id_length, - plurality(session_id_length, "", "s")); + tvb, offset, session_id_length, + tvb_get_ptr(tvb, offset, session_id_length), + "Session ID (%u byte%s)", + session_id_length, + plurality(session_id_length, "", "s")); offset += session_id_length; } - } - return offset - initial_offset; + + + return session_id_length+33; } static int @@ -1594,7 +1913,8 @@ static void dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb, - proto_tree *tree, guint32 offset, guint32 length) + proto_tree *tree, guint32 offset, guint32 length, + SslDecryptSession*ssl) { /* struct { * ProtocolVersion client_version; @@ -1613,20 +1933,23 @@ guint8 compression_method; guint16 start_offset = offset; - if (tree) + if (tree || ssl) { /* show the client version */ - proto_tree_add_item(tree, hf_ssl_handshake_client_version, tvb, + if (tree) + proto_tree_add_item(tree, hf_ssl_handshake_client_version, tvb, offset, 2, FALSE); offset += 2; /* show the fields in common with server hello */ - offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset); + offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset, ssl, 0); /* tell the user how many cipher suites there are */ cipher_suite_length = tvb_get_ntohs(tvb, offset); + if (!tree) + return; proto_tree_add_uint(tree, hf_ssl_handshake_cipher_suites_len, - tvb, offset, 2, cipher_suite_length); + tvb, offset, 2, cipher_suite_length); offset += 2; /* skip opaque length */ if (cipher_suite_length > 0) @@ -1707,7 +2030,7 @@ static void dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb, - proto_tree *tree, guint32 offset, guint32 length) + proto_tree *tree, guint32 offset, guint32 length, SslDecryptSession* ssl) { /* struct { * ProtocolVersion server_version; @@ -1720,21 +2043,56 @@ */ guint16 start_offset = offset; - if (tree) + if (tree || ssl) { /* show the server version */ - proto_tree_add_item(tree, hf_ssl_handshake_server_version, tvb, + if (tree) + proto_tree_add_item(tree, hf_ssl_handshake_server_version, tvb, offset, 2, FALSE); offset += 2; /* first display the elements conveniently in * common with client hello */ - offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset); + offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset, ssl, 1); + + /* PAOLO: handle session cipher suite */ + if (ssl) { + /* store selected cipher suite for decryption */ + ssl->cipher = tvb_get_ntohs(tvb, offset); + if (ssl_find_cipher(ssl->cipher,&ssl->cipher_suite) < 0) { + ssl_debug_printf("dissect_ssl3_hnd_srv_hello can't find cipher suite %X\n", ssl->cipher); + goto no_cipher; + } + + ssl->state |= SSL_CIPHER; + ssl_debug_printf("dissect_ssl3_hnd_srv_hello found cipher %X, state %X\n", + ssl->cipher, ssl->state); + + /* if we have restored a session now we can have enought material + * to build session key, check it out*/ + if ((ssl->state & + (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)) != + (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)) { + ssl_debug_printf("dissect_ssl3_hnd_srv_hello not enough data to generate key (required %X)\n", + (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)); + goto no_cipher; + } + + ssl_debug_printf("dissect_ssl3_hnd_srv_hello trying to generate keys\n"); + if (ssl_generate_keyring_material(ssl)<0) { + ssl_debug_printf("dissect_ssl3_hnd_srv_hello can't generate keyring material\n"); + goto no_cipher; + } + ssl->state |= SSL_HAVE_SESSION_KEY; + } + no_cipher: + if (!tree) + return; /* now the server-selected cipher suite */ proto_tree_add_item(tree, hf_ssl_handshake_cipher_suite, - tvb, offset, 2, FALSE); + tvb, offset, 2, FALSE); offset += 2; /* and the server-selected compression method */ @@ -1908,7 +2266,7 @@ static void dissect_ssl3_hnd_finished(tvbuff_t *tvb, proto_tree *tree, guint32 offset, - guint *conv_version) + guint* conv_version) { /* For TLS: * struct { @@ -1955,7 +2313,7 @@ /* record layer dissector */ static int dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, - guint32 offset, guint *conv_version, + guint32 offset, guint* conv_version, gboolean *need_desegmentation) { guint32 initial_offset = offset; @@ -2055,13 +2413,13 @@ (initial_offset + record_length_length), record_length)) { - *conv_version = SSL_VER_PCT; - ssl_set_conv_version(pinfo, *conv_version); + *conv_version = SSL_VER_PCT; + /*ssl_set_conv_version(pinfo, ssl->version);*/ } else if (msg_type >= 2 && msg_type <= 8) { *conv_version = SSL_VER_SSLv2; - ssl_set_conv_version(pinfo, *conv_version); + /*ssl_set_conv_version(pinfo, ssl->version);*/ } } @@ -2838,7 +3196,7 @@ * Support Functions * *********************************************************************/ - +#if 0 static void ssl_set_conv_version(packet_info *pinfo, guint version) { @@ -2869,6 +3227,7 @@ } conversation_add_proto_data(conversation, proto_ssl, GINT_TO_POINTER(version)); } +#endif static int ssl_is_valid_handshake_type(guint8 type) @@ -3149,6 +3508,108 @@ return 0; } +static void ssl_init(void) +{ + if (ssl_session_hash) + g_hash_table_destroy(ssl_session_hash); + if (ssl_key_hash) + g_hash_table_destroy(ssl_key_hash); + + ssl_key_hash = g_hash_table_new(ssl_private_key_hash,ssl_private_key_equal); + ssl_session_hash = g_hash_table_new(ssl_hash, ssl_equal); + if (ssl_keys_list) + { + char* end; + char* start = strdup(ssl_keys_list); + char* tmp = start; + do { + char* addr, *port, *filename, *ip; + SslService* service; + SSL_PRIVATE_KEY * private_key; + FILE* fp; + + addr = start; + /* split ip/file couple with ',' separator*/ + end = strchr(start, ','); + if (end) { + *end = 0; + start = end+1; + } + + /* for each entry split ip , port and filename with ':' separator */ + ssl_debug_printf("ssl_init found host entry %s\n", addr); + port = strchr(addr, ':'); + if (!port) + return; + *port = 0; + port++; + + filename = strchr(port,':'); + if (!filename) + return; + *filename=0; + filename++; + + /* convert ip and port string to network rappresentation*/ + service = g_malloc(sizeof(SslService) + 4); + service->addr.type = AT_IPv4; + service->addr.len = 4; + service->addr.data = ip = ((char*)service) + sizeof(SslService); + sscanf(addr, "%hhu.%hhu.%hhu.%hhu", &ip[0], + &ip[1], &ip[2], &ip[3]); + service->port = atoi(port); + ssl_debug_printf("ssl_init addr %hhu.%hhu.%hhu.%hhu port %d filename %s\n", + ip[0], ip[1], ip[2], ip[3], service->port, filename); + + /* try to load pen file*/ + fp = fopen(filename, "rb"); + if (!fp) { + fprintf(stderr, "can't open file %s \n",filename); + return; + } + + private_key = ssl_load_key(fp); + if (!private_key) { + fprintf(stderr,"can't load private key from %s\n", + filename); + return; + } + fclose(fp); + + ssl_debug_printf("ssl_init private key file %s successfully loaded\n", + filename); + g_hash_table_insert(ssl_key_hash, service, private_key); + + } while (end != NULL); + free(tmp); + } +} + +static void ssl_save_session(SslDecryptSession* ssl) +{ + StringInfo* session_id = g_malloc0(sizeof(StringInfo) + ssl->session_id.data_len); + StringInfo* master_secret = g_malloc0(48 + sizeof(StringInfo)); + + master_secret->data = ((char*)master_secret+sizeof(StringInfo)); + session_id->data = ((char*)session_id+sizeof(StringInfo)); + ssl_data_set(session_id, ssl->session_id.data, ssl->session_id.data_len); + ssl_data_set(master_secret, ssl->master_secret.data, ssl->master_secret.data_len); + g_hash_table_insert(ssl_session_hash, session_id, master_secret); + ssl_print_string("ssl_save_session stored session id", session_id); + ssl_print_string("ssl_save_session stored master secret", master_secret); +} + +static void ssl_restore_session(SslDecryptSession* ssl) +{ + StringInfo* ms = g_hash_table_lookup(ssl_session_hash, &ssl->session_id); + if (!ms) { + ssl_debug_printf("ssl_restore_session can't find stored session\n"); + return; + } + ssl_data_set(&ssl->master_secret, ms->data, ms->data_len); + ssl->state |= SSL_MASTER_SECRET; + ssl_debug_printf("ssl_restore_session master key retrived\n"); +} /********************************************************************* * @@ -3196,6 +3657,12 @@ FT_NONE, BASE_NONE, NULL, 0x0, "Payload is application data", HFILL } }, + { &hf_ssl_record_appdata_decrypted, + { "Application Data decrypted", "ssl.app_data_decrypted", + FT_STRING, BASE_NONE, NULL, 0x0, + "Payload is decrypted application data", HFILL } + }, + { & hf_ssl2_record, { "SSLv2/PCT Record Header", "ssl.record", FT_NONE, BASE_DEC, NULL, 0x0, @@ -3563,10 +4030,31 @@ "Whether the SSL dissector should reassemble SSL records spanning multiple TCP segments. " "To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", &ssl_desegment); + prefs_register_string_preference(ssl_module, "rsa_private_key", "RSA private keys list", + "comma separated list of RSA private key to be used for decryption; " + "each list entry must be in the form of <ip>:<port>:<key_file_name>", + &ssl_keys_list); + + } register_dissector("ssl", dissect_ssl, proto_ssl); + register_init_routine(ssl_init); + SSL_LIB_INIT(); +} +dissector_handle_t ssl_find_dissector(packet_info* pinfo) +{ + SslDissector* cur; + for (cur = ssl_dissectors; cur->ssl_port != 0; cur++) { + ssl_debug_printf("ssl_find_dissector cur port %d src port %d dest port %d\n", + cur->ssl_port, pinfo->srcport, pinfo->destport); + if ((cur->ssl_port == pinfo->srcport) || (cur->ssl_port == pinfo->destport)) { + ssl_debug_printf("ssl_find_dissector found %p\n", cur->handle); + return cur->handle; + } + } + return 0; } /* If this dissector uses sub-dissector registration add a registration @@ -3579,6 +4067,17 @@ dissector_handle_t ssl_handle; ssl_handle = find_dissector("ssl"); + + ssl_dissectors[0].handle = find_dissector("http"); + ssl_dissectors[1].handle = find_dissector("ldap"); + ssl_dissectors[2].handle = find_dissector("imap"); + ssl_dissectors[3].handle = find_dissector("pop"); + ssl_debug_printf("http: %p ldap %p imap %p pop %p\n", ssl_dissectors[0].handle, + ssl_dissectors[1].handle, ssl_dissectors[2].handle, + ssl_dissectors[3].handle); + ssl_debug_printf("http: %p ldap %p imap %p pop %p\n", find_dissector("http"), + find_dissector("ldap"), find_dissector("imap"), + find_dissector("pop")); dissector_add("tcp.port", TCP_PORT_SSL, ssl_handle); dissector_add("tcp.port", TCP_PORT_SSL_LDAP, ssl_handle); dissector_add("tcp.port", TCP_PORT_SSL_IMAP, ssl_handle); --- acinclude.m4-0.10.10 2005-04-15 14:54:21.583595088 +0200 +++ acinclude.m4 2005-04-15 12:41:01.124849688 +0200 @@ -1180,3 +1180,161 @@ AC_SUBST(KRB5_LIBS) ]) +dnl Autoconf macros for libgnutls +dnl $id$ + +# Modified for LIBGNUTLS -- nmav +# Configure paths for LIBGCRYPT +# Shamelessly stolen from the one of XDELTA by Owen Taylor +# Werner Koch 99-12-09 + +dnl AM_PATH_LIBGNUTLS([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) +dnl Test for libgnutls, and define LIBGNUTLS_CFLAGS and LIBGNUTLS_LIBS +dnl +AC_DEFUN([AM_PATH_LIBGNUTLS], +[dnl +dnl Get the cflags and libraries from the libgnutls-config script +dnl +AC_ARG_WITH(libgnutls-prefix, + [ --with-libgnutls-prefix=PFX Prefix where libgnutls is installed (optional)], + libgnutls_config_prefix="$withval", libgnutls_config_prefix="") + + if test x$libgnutls_config_prefix != x ; then + if test x${LIBGNUTLS_CONFIG+set} != xset ; then + LIBGNUTLS_CONFIG=$libgnutls_config_prefix/bin/libgnutls-config + fi + fi + + AC_PATH_PROG(LIBGNUTLS_CONFIG, libgnutls-config, no) + min_libgnutls_version=ifelse([$1], ,0.1.0,$1) + AC_MSG_CHECKING(for libgnutls - version >= $min_libgnutls_version) + no_libgnutls="" + if test "$LIBGNUTLS_CONFIG" = "no" ; then + no_libgnutls=yes + else + LIBGNUTLS_CFLAGS=`$LIBGNUTLS_CONFIG $libgnutls_config_args --cflags` + LIBGNUTLS_LIBS=`$LIBGNUTLS_CONFIG $libgnutls_config_args --libs` + libgnutls_config_version=`$LIBGNUTLS_CONFIG $libgnutls_config_args --version` + + + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS" + LIBS="$LIBS $LIBGNUTLS_LIBS" +dnl +dnl Now check if the installed libgnutls is sufficiently new. Also sanity +dnl checks the results of libgnutls-config to some extent +dnl + rm -f conf.libgnutlstest + AC_TRY_RUN([ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gnutls/gnutls.h> + +int +main () +{ + system ("touch conf.libgnutlstest"); + + if( strcmp( gnutls_check_version(NULL), "$libgnutls_config_version" ) ) + { + printf("\n*** 'libgnutls-config --version' returned %s, but LIBGNUTLS (%s)\n", + "$libgnutls_config_version", gnutls_check_version(NULL) ); + printf("*** was found! If libgnutls-config was correct, then it is best\n"); + printf("*** to remove the old version of LIBGNUTLS. You may also be able to fix the error\n"); + printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); + printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); + printf("*** required on your system.\n"); + printf("*** If libgnutls-config was wrong, set the environment variable LIBGNUTLS_CONFIG\n"); + printf("*** to point to the correct copy of libgnutls-config, and remove the file config.cache\n"); + printf("*** before re-running configure\n"); + } + else if ( strcmp(gnutls_check_version(NULL), LIBGNUTLS_VERSION ) ) + { + printf("\n*** LIBGNUTLS header file (version %s) does not match\n", LIBGNUTLS_VERSION); + printf("*** library (version %s)\n", gnutls_check_version(NULL) ); + } + else + { + if ( gnutls_check_version( "$min_libgnutls_version" ) ) + { + return 0; + } + else + { + printf("no\n*** An old version of LIBGNUTLS (%s) was found.\n", + gnutls_check_version(NULL) ); + printf("*** You need a version of LIBGNUTLS newer than %s. The latest version of\n", + "$min_libgnutls_version" ); + printf("*** LIBGNUTLS is always available from ftp://gnutls.hellug.gr/pub/gnutls.\n"); + printf("*** \n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the libgnutls-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of LIBGNUTLS, but you can also set the LIBGNUTLS_CONFIG environment to point to the\n"); + printf("*** correct copy of libgnutls-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + } + return 1; +} +],, no_libgnutls=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + + if test "x$no_libgnutls" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + if test -f conf.libgnutlstest ; then + : + else + AC_MSG_RESULT(no) + fi + if test "$LIBGNUTLS_CONFIG" = "no" ; then + echo "*** The libgnutls-config script installed by LIBGNUTLS could not be found" + echo "*** If LIBGNUTLS was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the LIBGNUTLS_CONFIG environment variable to the" + echo "*** full path to libgnutls-config." + else + if test -f conf.libgnutlstest ; then + : + else + echo "*** Could not run libgnutls test program, checking why..." + CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS" + LIBS="$LIBS $LIBGNUTLS_LIBS" + AC_TRY_LINK([ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gnutls/gnutls.h> +], [ return !!gnutls_check_version(NULL); ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding LIBGNUTLS or finding the wrong" + echo "*** version of LIBGNUTLS. If it is not finding LIBGNUTLS, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" + echo "***" ], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means LIBGNUTLS was incorrectly installed" + echo "*** or that you have moved LIBGNUTLS since it was installed. In the latter case, you" + echo "*** may want to edit the libgnutls-config script: $LIBGNUTLS_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + LIBGNUTLS_CFLAGS="" + LIBGNUTLS_LIBS="" + ifelse([$3], , :, [$3]) + fi + rm -f conf.libgnutlstest + AC_SUBST(LIBGNUTLS_CFLAGS) + AC_SUBST(LIBGNUTLS_LIBS) +])
- Prev by Date: Re: [Ethereal-dev] Re: Regarding Memory allocation
- Next by Date: [Ethereal-dev] Online RTP Audio saving
- Previous by thread: Re: [Ethereal-dev] Regarding Memory allocation
- Next by thread: [Ethereal-dev] Online RTP Audio saving
- Index(es):