Gmane
Favicon
From: William McVey <wam <at> cisco.com>
Subject: More updates to IKE transforms
Newsgroups: gmane.comp.security.scapy.general
Date: 2006-03-08 00:40:35 GMT (3 years, 17 weeks, 1 day, 3 hours and 38 minutes ago)
If you recall, in the postscript of my mesg from about a week ago I
mentioned that the IKE patch I submitted then was only for proper
dissecting of packets with long IKE attribute values(*). Getting proper
packet construction with long attribute handling(*) was more difficult
than I thought it would be, but I finally got it done. I'm attaching a
patch that addresses this issue. There is only one slight incompatible
change (beyond the changes I have already covered in my last IKE update
email). The return value of ISAKMPTransformSetField.type2num() was
changed to be a sequence of integers which later get packed into 2-byte
fields, as many as as are necessary to handle the data that is passed in
to the method. Previously, the result was a single integer which would
get packed into a 4 byte long word. I did *NOT* change the name of this
method, although it should probably get changed to more accurately
reflect the current behavior (type2list_of_long_ints doesn't quite have
the same brevity.)

In addition to the patch, I'm again attaching a testcase suite which
demonstrates the problem and shows that after the patch a proper
timestamp lifetime attribute value can be specified. For the curious,
the testcase driver program takes a -w option, which will write the
generated packet out to a pcap file that allows you to see the packet
generated... useful if you want to compare the before/after behavior of
my change. I'm also attaching the ike-proposals.pcap file which is used
by the the test suite for determining if IKE attribute parsing is
happening properly on dissection).

Finally, there are a couple of requests for enhancement which go beyond
my current understanding of the deep machinery of scapy. It'd be
especially nice if scapy could auto-magically fill in the
ISAKMP_payload_Proposal.trans_nb (number of included transforms)
attribute on the ISAKMP_payload_Proposal as well as auto-populate the
'num' attribute on the ISAKMP_payload_Transform instance itself. I
looked into doing this, but wasn't sure whether it was best to override
'build()', 'do_build()', or 'post_build()'. In general, more docstrings
on the common base class methods would certainly help 3rd party
contributors. In addition to those build* methods, the meaning of i2m,
m2i, i2repr, any2i, i2h and others are fairly nebulous (to me at least).

  -- William

* BTW, the complete specification of long attribute vs basic attribute
handling is defined in RFC 2408, section 3.3.
--- scapy.py-1.0.3.14	2006-02-21 17:04:23.017716605 -0600
+++ scapy.py	2006-03-07 17:14:49.282591957 -0600
@@ -1363,7 +1363,7 @@
 ##################

 import socket, sys, getopt, string, struct, random, os, code
-import cPickle, copy, types, gzip, base64, re
+import cPickle, copy, types, gzip, base64, re, itertools
 from select import select
 from fcntl import ioctl
 import fcntl
@@ -3485,24 +3485,82 @@
 #            x = len(v)+self.shift
 #        return x

-ISAKMPTransformTypes = { "Encryption":    (1, { "DES-CBS"  : 1,
-                                                "3DES-CBC" : 5, }),
+# see http://www.iana.org/assignments/ipsec-registry for details
+ISAKMPAttributeTypes= { "Encryption":    (1, { "DES-CBS"  : 1,
+                                                "IDEA-CBC" : 2,
+                                                "Blowfish-CBC" : 3,
+                                                "RC5-R16-B64-CBC" : 4,
+                                                "3DES-CBC" : 5, 
+                                                "CAST-CBC" : 6, 
+                                                "AES-CBC" : 7, 
+                                                "CAMELLIA-CBC" : 8, }, 0),
                          "Hash":          (2, { "MD5": 1,
-                                                "SHA": 2, }),
-                         "Authentication":(3, { "PSK": 1, }),
+                                                "SHA": 2,
+                                                "Tiger": 3,
+                                                "SHA2-256": 4,
+                                                "SHA2-384": 5,
+                                                "SHA2-512": 6,}, 0),
+                         "Authentication":(3, { "PSK": 1, 
+                                                "DSS": 2,
+                                                "RSA Sig": 3,
+                                                "RSA Encryption": 4,
+                                                "RSA Encryption Revised": 5,
+                                                "ElGamal Encryption": 6,
+                                                "ElGamal Encryption Revised": 7,
+                                                "ECDSA Sig": 8,
+                                                "HybridInitRSA": 64221,
+                                                "HybridRespRSA": 64222,
+                                                "HybridInitDSS": 64223,
+                                                "HybridRespDSS": 64224,
+                                                "XAUTHInitPreShared": 65001,
+                                                "XAUTHRespPreShared": 65002,
+                                                "XAUTHInitDSS": 65003,
+                                                "XAUTHRespDSS": 65004,
+                                                "XAUTHInitRSA": 65005,
+                                                "XAUTHRespRSA": 65006,
+                                                "XAUTHInitRSAEncryption": 65007,
+                                                "XAUTHRespRSAEncryption": 65008,
+                                                "XAUTHInitRSARevisedEncryption": 65009,
+                                                "XAUTHRespRSARevisedEncryptio": 65010, }, 0),
                          "GroupDesc":     (4, { "768MODPgr"  : 1,
-                                                "1024MODPgr" : 2, }),
-                         "LifeType":      (11,{ "Seconds":1, }),
-                         "LifeDuration":  (12,{}),
+                                                "1024MODPgr" : 2, 
+                                                "EC2Ngr155"  : 3,
+                                                "EC2Ngr185"  : 4,
+                                                "1536MODPgr" : 5, 
+                                                "2048MODPgr" : 14, 
+                                                "3072MODPgr" : 15, 
+                                                "4096MODPgr" : 16, 
+                                                "6144MODPgr" : 17, 
+                                                "8192MODPgr" : 18, }, 0),
+                         "GroupType":      (5,  {"MODP":       1,
+                                                 "ECP":        2,
+                                                 "EC2N":       3}, 0),
+                         "GroupPrime":     (6,  {}, 1),
+                         "GroupGenerator1":(7,  {}, 1),
+                         "GroupGenerator2":(8,  {}, 1),
+                         "GroupCurveA":    (9,  {}, 1),
+                         "GroupCurveB":    (10, {}, 1),
+                         "LifeType":       (11, {"Seconds":     1,
+                                                 "Kilobytes":   2,  }, 0),
+                         "LifeDuration":   (12, {}, 1),
+                         "PRF":            (13, {}, 0),
+                         "KeyLength":      (14, {}, 0),
+                         "FieldSize":      (15, {}, 0),
+                         "GroupOrder":     (16, {}, 1),
                          }

+# the name 'ISAKMPTransformTypes' is actually a misnomer (since the table 
+# holds info for all ISAKMP Attribute types, not just transforms) but we'll 
+# keep it for backwards compatibility... for now at least
+ISAKMPTransformTypes = ISAKMPAttributeTypes
+
 ISAKMPTransformNum = {}
 for n in ISAKMPTransformTypes:
     val = ISAKMPTransformTypes[n]
     tmp = {}
     for e in val[1]:
         tmp[val[1][e]] = e
-    ISAKMPTransformNum[val[0]] = (n,tmp)
+    ISAKMPTransformNum[val[0]] = (n,tmp, val[2])
 del(n)
 del(e)
 del(tmp)
@@ -3511,38 +3569,64 @@

 class ISAKMPTransformSetField(StrLenField):
     islist=1
+    # all of this TLV attribute versus "simple attribute" complexity is 
+    # specified in RFC 2408, section 3.3
     def type2num(self, (typ,enc)):
-        if ISAKMPTransformTypes.has_key(typ):
-            val = ISAKMPTransformTypes[typ]
-        else:
-            val = (int(typ),{})
-        if val[1].has_key(enc):
-            enc = val[1][enc]
-        else:
-            enc = int(enc)
-        return ((val[0] | 0x8000L) << 16) | enc
-    def num2type(self, num):
-        typ = (num >> 16) & 0x7fff
-        enc = num & 0xffff
+        attrnum, attrsubs, can_beTLV = ISAKMPAttributeTypes.get(typ, (typ,{},0))
+        enc = attrsubs.get(enc) or int(enc)
+        if not can_beTLV or enc < 65536:
+	    # The attribute is either not allowed to be a TLV, or *is*
+	    # allowed to be a TLV but doesn't need to be.
+	    # Chop off the attr value at 16 bits so we don't overflow into
+	    # the attribute type
+	    return [(attrnum | 0x8000L), (enc & ((1<<16)-1))]
+        long_vals = []
+        while enc:
+            # peel off every word of the value into a list, least 
+            # significant words first, then reverse the list when we're done
+            long_vals.append(enc & (1<<16)-1)
+            enc >>= 16
+        long_vals.append(2*len(long_vals))
+        long_vals.append(attrnum) 
+        long_vals.reverse()
+        return long_vals
+
+    def num2type(self, typ, enc):
         val = ISAKMPTransformNum.get(typ,(typ,{}))
         enc = val[1].get(enc,enc)
         return (val[0],enc)
-        
-        
     def i2m(self, pkt, i):
         if i is None:
             return ""
-        i = map(self.type2num, i)
-        return struct.pack("!"+"I"*len(i),*i)
+        i = [x for x in itertools.chain(*map(self.type2num, i))]
+        return struct.pack("!"+"H"*len(i),*i)
     def m2i(self, pkt, m):
-        lst = struct.unpack("!"+"I"*(len(m)/4),m)
-        lst = map(self.num2type, lst)
+        # I try to ensure that we don't read off the end of our packet based
+        # on bad length fields we're provided in the packet. There are still
+        # conditions where struct.unpack() may not get enough packet data, but
+        # worst case that should result in broken attributes (which would
+        # be expected). (wam)
+        lst = []
+        while len(m)>0:
+            trans_type, = struct.unpack("!H", m[:2])
+            is_tlv = not (trans_type >>15)
+            trans_type &= 0x7fff
+            if is_tlv:
+                # We should probably check to make sure the attribute type we
+                # are looking at is allowed to have a TLV format and issue a 
+                # warning if we're given an TLV on a basic attribute.
+                value_len, = struct.unpack("!H", m[2:4])
+                value = reduce(lambda x,y: (x<<8)|y, struct.unpack("!%s" % ("B"*value_len,), m[4:4+value_len]))
+            else:
+                value_len=0
+                value, = struct.unpack("!H", m[2:4])
+            m=m[4+value_len:]
+            lst.append(self.num2type(trans_type, value))
         return lst
     def getfield(self, pkt, s):
         l = getattr(pkt, self.fld)
         l += pkt.get_field(self.fld).shift
         i = self.m2i(pkt, s[:l])
-      
         return s[l:],i

 class StrNullField(StrField):
@@ -6311,7 +6395,6 @@
         ]

 
-        
 class ISAKMP_payload_Proposal(ISAKMP_class):
     name = "IKE proposal"
 #    ISAKMP_payload_type = 0
Attachment (ike-proposals.pcap): application/octet-stream, 1452 bytes
Attachment (test_ike.py): text/x-python, 4134 bytes
---------------------------------------------------------------------
Desinscription: envoyez un message a: scapy.ml-unsubscribe <at> secdev.org
Pour obtenir de l'aide, ecrivez a: scapy.ml-help <at> secdev.org