From Naoyoshi Ueda:
authoretxrab <etxrab@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 16 Feb 2012 05:21:09 +0000 (05:21 +0000)
committeretxrab <etxrab@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 16 Feb 2012 05:21:09 +0000 (05:21 +0000)
Patch to fix DTLS decryption.

https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6847

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@41036 f5534014-38df-0310-8fa8-9805f1628bb7

AUTHORS
epan/dissectors/packet-dtls.c
epan/dissectors/packet-ssl-utils.c

diff --git a/AUTHORS b/AUTHORS
index 7a5697a..fad8da2 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -2919,6 +2919,7 @@ Christian Durrer  <christian.durrer [AT] sensemail.ch> {
 Naoyoshi Ueda          <piyomaru3141 [AT] gmail.com> {
        IKEv2 decryption support
        TLS 1.2 decryption support
+       DTLS 1.0 decryption fixes
 }
 
 Javier Cardona         <javier [AT] cozybit.com> {
index ead6325..3ae3fd3 100644 (file)
@@ -371,6 +371,7 @@ dissect_dtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   gboolean           first_record_in_frame;
   SslDecryptSession *ssl_session;
   guint*             conv_version;
+  Ssl_private_key_t *private_key;
 
   ti                    = NULL;
   dtls_tree             = NULL;
@@ -420,9 +421,13 @@ dissect_dtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
      * is not always available
      * Note that with HAVE_LIBGNUTLS undefined private_key is allways 0
      * and thus decryption never engaged*/
-    ssl_session->private_key = g_hash_table_lookup(dtls_key_hash, &dummy);
-    if (!ssl_session->private_key)
+    private_key = g_hash_table_lookup(dtls_key_hash, &dummy);
+    if (!private_key) {
       ssl_debug_printf("dissect_dtls can't find private key for this server!\n");
+    }
+    else {
+      ssl_session->private_key = private_key->sexp_pkey;
+    }
   }
   conv_version= & ssl_session->version;
 
@@ -584,6 +589,11 @@ decrypt_dtls_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
     decoder = ssl->client;
   }
 
+  if (!decoder) {
+    ssl_debug_printf("decrypt_dtls_record: no decoder available\n");
+    return ret;
+  }
+
   /* ensure we have enough storage space for decrypted data */
   if (record_length > dtls_decrypted_data.data_len)
     {
@@ -806,6 +816,7 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
     col_append_str(pinfo->cinfo, COL_INFO, "Change Cipher Spec");
     dissect_dtls_change_cipher_spec(tvb, dtls_record_tree,
                                     offset, conv_version, content_type);
+    if (ssl) ssl_change_cipher(ssl, ssl_packet_from_server(ssl, dtls_associations, pinfo));
     break;
   case SSL_ID_ALERT:
     {
@@ -1315,6 +1326,18 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
                 break;
               }
 
+              /* Skip leading two bytes length field. Older openssl's DTLS implementation seems not to have this field.
+               * See implementation note in RFC 4346 section 7.4.7.1
+               */
+              if (ssl->cipher_suite.kex==KEX_RSA && ssl->version_netorder != DTLSV1DOT0_VERSION_NOT) {
+                encrlen = tvb_get_ntohs(tvb, offset);
+                skip = 2;
+                if (encrlen > length - 2) {
+                  ssl_debug_printf("dissect_dtls_handshake wrong encrypted length (%d max %d)\n", encrlen, length);
+                  break;
+                }
+              }
+
               encrypted_pre_master.data = se_alloc(encrlen);
               encrypted_pre_master.data_len = encrlen;
               tvb_memcpy(tvb, encrypted_pre_master.data, offset+skip, encrlen);
@@ -1362,67 +1385,71 @@ dissect_dtls_hnd_hello_common(tvbuff_t *tvb, proto_tree *tree,
   nstime_t gmt_unix_time;
   guint8   session_id_length;
 
-  if (ssl)
+  if (tree || ssl)
     {
-      /* 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_dtls_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, dtls_session_hash);
+         if (ssl)
+               {
+                 /* 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_dtls_hnd_hello_common found random state %X\n",
+                                                  ssl->state);
         }
-      else {
-        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_dtls_handshake_random_time,
-                          tvb, offset, 4, &gmt_unix_time);
+      if (tree)
+        {
+          gmt_unix_time.secs  = tvb_get_ntohl(tvb, offset);
+          gmt_unix_time.nsecs = 0;
+          proto_tree_add_time(tree, hf_dtls_handshake_random_time,
+                              tvb, offset, 4, &gmt_unix_time);
+        }
       offset += 4;
 
       /* show the random bytes */
-      proto_tree_add_item(tree, hf_dtls_handshake_random_bytes,
-                          tvb, offset, 28, ENC_NA);
+      if (tree)
+          proto_tree_add_item(tree, hf_dtls_handshake_random_bytes,
+                              tvb, offset, 28, ENC_NA);
       offset += 28;
 
       /* show the session id */
       session_id_length = tvb_get_guint8(tvb, offset);
-      proto_tree_add_item(tree, hf_dtls_handshake_session_id_len,
-                          tvb, offset++, 1, ENC_BIG_ENDIAN);
-      if (session_id_length > 0)
+      if (tree)
+          proto_tree_add_item(tree, hf_dtls_handshake_session_id_len,
+                              tvb, offset, 1, ENC_BIG_ENDIAN);
+         offset++;
+      if (ssl)
         {
-          proto_tree_add_bytes_format(tree, hf_dtls_handshake_session_id,
-                                      tvb, offset, session_id_length,
-                                      NULL, "Session ID (%u byte%s)",
-                                      session_id_length,
-                                      plurality(session_id_length, "", "s"));
-          offset += session_id_length;
+          /* check stored session id info */
+          if (from_server && (session_id_length == ssl->session_id.data_len) &&
+              (tvb_memeql(tvb, offset, ssl->session_id.data, session_id_length) == 0))
+            {
+              /* clinet/server id match: try to restore a previous cached session*/
+              ssl_restore_session(ssl, dtls_session_hash);
+            }
+          else {
+            tvb_memcpy(tvb,ssl->session_id.data, offset, session_id_length);
+            ssl->session_id.data_len = session_id_length;
+          }
         }
-
+         if (tree && session_id_length > 0)
+                 proto_tree_add_bytes_format(tree, hf_dtls_handshake_session_id,
+                                                                         tvb, offset, session_id_length,
+                                                                         NULL, "Session ID (%u byte%s)",
+                                                                         session_id_length,
+                                                                         plurality(session_id_length, "", "s"));
+         offset += session_id_length;
     }
 
   /* XXXX */
@@ -1729,6 +1756,10 @@ dissect_dtls_hnd_srv_hello(tvbuff_t *tvb,
         ssl->state |= SSL_HAVE_SESSION_KEY;
       }
     no_cipher:
+      if (ssl) {
+        /* store selected compression method for decompression */
+        ssl->compression = tvb_get_guint8(tvb, offset+2);
+      }
       if (!tree)
         return offset;
 
index 1b5af39..10c6fc1 100644 (file)
@@ -1885,7 +1885,8 @@ static gint prf(SslDecryptSession* ssl,StringInfo* secret,gchar* usage,StringInf
     gint ret;
     if (ssl->version_netorder==SSLV3_VERSION){
         ret = ssl3_prf(secret,usage,rnd1,rnd2,out);
-    }else if (ssl->version_netorder==TLSV1_VERSION || ssl->version_netorder==TLSV1DOT1_VERSION){
+    }else if (ssl->version_netorder==TLSV1_VERSION || ssl->version_netorder==TLSV1DOT1_VERSION || 
+            ssl->version_netorder==DTLSV1DOT0_VERSION || ssl->version_netorder==DTLSV1DOT0_VERSION_NOT){
         ret = tls_prf(secret,usage,rnd1,rnd2,out);
     }else{
         if (ssl->cipher_suite.dig == DIG_SHA384){
@@ -2410,7 +2411,6 @@ ssl3_check_mac(SslDecoder*decoder,int ct,guint8* data,
     return(0);
 }
 
-#if 0
 static gint
 dtls_check_mac(SslDecoder*decoder, gint ct,int ver, guint8* data,
         guint32 datalen, guint8* mac)
@@ -2419,7 +2419,8 @@ dtls_check_mac(SslDecoder*decoder, gint ct,int ver, guint8* data,
     gint md;
     guint32 len;
     guint8 buf[20];
-    guint32 netnum;
+    gint16 temp;
+
     md=ssl_get_digest_by_name(digests[decoder->cipher_suite->dig-0x40]);
     ssl_debug_printf("dtls_check_mac mac type:%s md %d\n",
         digests[decoder->cipher_suite->dig-0x40], md);
@@ -2430,7 +2431,7 @@ dtls_check_mac(SslDecoder*decoder, gint ct,int ver, guint8* data,
     /* hash sequence number */
     fmt_seq(decoder->seq,buf);
     buf[0]=decoder->epoch>>8;
-    buf[1]=decoder->epoch;
+    buf[1]=(guint8)decoder->epoch;
 
     ssl_hmac_update(&hm,buf,8);
 
@@ -2439,10 +2440,12 @@ dtls_check_mac(SslDecoder*decoder, gint ct,int ver, guint8* data,
     ssl_hmac_update(&hm,buf,1);
 
     /* hash version,data length and data */
-    *((gint16*)buf) = g_htons(ver);
+    temp = g_htons(ver);
+    memcpy(buf, &temp, 2);
     ssl_hmac_update(&hm,buf,2);
 
-    *((gint16*)buf) = g_htons(datalen);
+    temp = g_htons(datalen);
+    memcpy(buf, &temp, 2);
     ssl_hmac_update(&hm,buf,2);
     ssl_hmac_update(&hm,data,datalen);
     /* get digest and digest len */
@@ -2454,7 +2457,6 @@ dtls_check_mac(SslDecoder*decoder, gint ct,int ver, guint8* data,
 
     return(0);
 }
-#endif
 
 #ifdef HAVE_LIBZ
 int
@@ -2584,21 +2586,20 @@ ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, gint ct,
     }
     else if(ssl->version_netorder==DTLSV1DOT0_VERSION ||
         ssl->version_netorder==DTLSV1DOT0_VERSION_NOT){
-        /* following the openssl dtls errors the right test is:
-        if(dtls_check_mac(decoder,ct,ssl->version_netorder,out_str->data,worklen,mac)< 0) { */
-        if(tls_check_mac(decoder,ct,TLSV1_VERSION,out_str->data,worklen,mac)< 0) {
-                       if(ssl_ignore_mac_failed) {
-                       ssl_debug_printf("ssl_decrypt_record: mac failed, but ignored for troubleshooting ;-)\n");
-            }
-                       else{
-                               ssl_debug_printf("ssl_decrypt_record: mac failed\n");
-                               return -1;
-                       }
+        /* Try rfc-compliant mac first, and if failed, try old openssl's non-rfc-compliant mac */
+        if(dtls_check_mac(decoder,ct,ssl->version_netorder,out_str->data,worklen,mac)>= 0) {
+            ssl_debug_printf("ssl_decrypt_record: mac ok\n");
+        }
+        else if(tls_check_mac(decoder,ct,TLSV1_VERSION,out_str->data,worklen,mac)>= 0) {
+            ssl_debug_printf("ssl_decrypt_record: dtls rfc-compliant mac failed, but old openssl's non-rfc-compliant mac ok\n");
+        }
+        else if(ssl_ignore_mac_failed) {
+            ssl_debug_printf("ssl_decrypt_record: mac failed, but ignored for troubleshooting ;-)\n");
         }
         else{
-            ssl_debug_printf("ssl_decrypt_record: mac ok\n");
+            ssl_debug_printf("ssl_decrypt_record: mac failed\n");
+            return -1;
         }
-
     }
 
        *outl = worklen;