isakmp.py revision 22a55b62eb35e8611fe03b99e4ff4f257a97b5d1
1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <phil@secdev.org>
4## This program is published under a GPLv2 license
5
6"""
7ISAKMP (Internet Security Association and Key Management Protocol).
8"""
9
10from __future__ import absolute_import
11import struct
12from scapy.config import conf
13from scapy.packet import *
14from scapy.fields import *
15from scapy.ansmachine import *
16from scapy.layers.inet import IP,UDP
17from scapy.sendrecv import sr
18from scapy.error import warning
19from functools import reduce
20
21
22# see http://www.iana.org/assignments/ipsec-registry for details
23ISAKMPAttributeTypes= { "Encryption":    (1, { "DES-CBC"  : 1,
24                                                "IDEA-CBC" : 2,
25                                                "Blowfish-CBC" : 3,
26                                                "RC5-R16-B64-CBC" : 4,
27                                                "3DES-CBC" : 5,
28                                                "CAST-CBC" : 6,
29                                                "AES-CBC" : 7,
30                                                "CAMELLIA-CBC" : 8, }, 0),
31                         "Hash":          (2, { "MD5": 1,
32                                                "SHA": 2,
33                                                "Tiger": 3,
34                                                "SHA2-256": 4,
35                                                "SHA2-384": 5,
36                                                "SHA2-512": 6,}, 0),
37                         "Authentication":(3, { "PSK": 1,
38                                                "DSS": 2,
39                                                "RSA Sig": 3,
40                                                "RSA Encryption": 4,
41                                                "RSA Encryption Revised": 5,
42                                                "ElGamal Encryption": 6,
43                                                "ElGamal Encryption Revised": 7,
44                                                "ECDSA Sig": 8,
45                                                "HybridInitRSA": 64221,
46                                                "HybridRespRSA": 64222,
47                                                "HybridInitDSS": 64223,
48                                                "HybridRespDSS": 64224,
49                                                "XAUTHInitPreShared": 65001,
50                                                "XAUTHRespPreShared": 65002,
51                                                "XAUTHInitDSS": 65003,
52                                                "XAUTHRespDSS": 65004,
53                                                "XAUTHInitRSA": 65005,
54                                                "XAUTHRespRSA": 65006,
55                                                "XAUTHInitRSAEncryption": 65007,
56                                                "XAUTHRespRSAEncryption": 65008,
57                                                "XAUTHInitRSARevisedEncryption": 65009,
58                                                "XAUTHRespRSARevisedEncryptio": 65010, }, 0),
59                         "GroupDesc":     (4, { "768MODPgr"  : 1,
60                                                "1024MODPgr" : 2,
61                                                "EC2Ngr155"  : 3,
62                                                "EC2Ngr185"  : 4,
63                                                "1536MODPgr" : 5,
64                                                "2048MODPgr" : 14,
65                                                "3072MODPgr" : 15,
66                                                "4096MODPgr" : 16,
67                                                "6144MODPgr" : 17,
68                                                "8192MODPgr" : 18, }, 0),
69                         "GroupType":      (5,  {"MODP":       1,
70                                                 "ECP":        2,
71                                                 "EC2N":       3}, 0),
72                         "GroupPrime":     (6,  {}, 1),
73                         "GroupGenerator1":(7,  {}, 1),
74                         "GroupGenerator2":(8,  {}, 1),
75                         "GroupCurveA":    (9,  {}, 1),
76                         "GroupCurveB":    (10, {}, 1),
77                         "LifeType":       (11, {"Seconds":     1,
78                                                 "Kilobytes":   2,  }, 0),
79                         "LifeDuration":   (12, {}, 1),
80                         "PRF":            (13, {}, 0),
81                         "KeyLength":      (14, {}, 0),
82                         "FieldSize":      (15, {}, 0),
83                         "GroupOrder":     (16, {}, 1),
84                         }
85
86# the name 'ISAKMPTransformTypes' is actually a misnomer (since the table
87# holds info for all ISAKMP Attribute types, not just transforms, but we'll
88# keep it for backwards compatibility... for now at least
89ISAKMPTransformTypes = ISAKMPAttributeTypes
90
91ISAKMPTransformNum = {}
92for n in ISAKMPTransformTypes:
93    val = ISAKMPTransformTypes[n]
94    tmp = {}
95    for e in val[1]:
96        tmp[val[1][e]] = e
97    ISAKMPTransformNum[val[0]] = (n,tmp, val[2])
98del(n)
99del(e)
100del(tmp)
101del(val)
102
103
104class ISAKMPTransformSetField(StrLenField):
105    islist=1
106    def type2num(self, type_val_tuple):
107        typ, val = type_val_tuple
108        type_val,enc_dict,tlv = ISAKMPTransformTypes.get(typ, (typ,{},0))
109        val = enc_dict.get(val, val)
110        s = ""
111        if (val & ~0xffff):
112            if not tlv:
113                warning("%r should not be TLV but is too big => using TLV encoding" % typ)
114            n = 0
115            while val:
116                s = chr(val&0xff)+s
117                val >>= 8
118                n += 1
119            val = n
120        else:
121            type_val |= 0x8000
122        return struct.pack("!HH",type_val, val)+s
123    def num2type(self, typ, enc):
124        val = ISAKMPTransformNum.get(typ,(typ,{}))
125        enc = val[1].get(enc,enc)
126        return (val[0],enc)
127    def i2m(self, pkt, i):
128        if i is None:
129            return ""
130        i = [self.type2num(e) for e in i]
131        return "".join(i)
132    def m2i(self, pkt, m):
133        # I try to ensure that we don't read off the end of our packet based
134        # on bad length fields we're provided in the packet. There are still
135        # conditions where struct.unpack() may not get enough packet data, but
136        # worst case that should result in broken attributes (which would
137        # be expected). (wam)
138        lst = []
139        while len(m) >= 4:
140            trans_type, = struct.unpack("!H", m[:2])
141            is_tlv = not (trans_type & 0x8000)
142            if is_tlv:
143                # We should probably check to make sure the attribute type we
144                # are looking at is allowed to have a TLV format and issue a
145                # warning if we're given an TLV on a basic attribute.
146                value_len, = struct.unpack("!H", m[2:4])
147                if value_len+4 > len(m):
148                    warning("Bad length for ISAKMP tranform type=%#6x" % trans_type)
149                value = m[4:4+value_len]
150                value = reduce(lambda x,y: (x<<8)|y, struct.unpack("!%s" % ("B"*len(value),), value),0)
151            else:
152                trans_type &= 0x7fff
153                value_len=0
154                value, = struct.unpack("!H", m[2:4])
155            m=m[4+value_len:]
156            lst.append(self.num2type(trans_type, value))
157        if len(m) > 0:
158            warning("Extra bytes after ISAKMP transform dissection [%r]" % m)
159        return lst
160
161
162ISAKMP_payload_type = ["None","SA","Proposal","Transform","KE","ID","CERT","CR","Hash",
163                       "SIG","Nonce","Notification","Delete","VendorID"]
164
165ISAKMP_exchange_type = ["None","base","identity prot.",
166                        "auth only", "aggressive", "info"]
167
168
169class ISAKMP_class(Packet):
170    def guess_payload_class(self, payload):
171        np = self.next_payload
172        if np == 0:
173            return conf.raw_layer
174        elif np < len(ISAKMP_payload_type):
175            pt = ISAKMP_payload_type[np]
176            return globals().get("ISAKMP_payload_%s" % pt, ISAKMP_payload)
177        else:
178            return ISAKMP_payload
179
180
181class ISAKMP(ISAKMP_class): # rfc2408
182    name = "ISAKMP"
183    fields_desc = [
184        StrFixedLenField("init_cookie","",8),
185        StrFixedLenField("resp_cookie","",8),
186        ByteEnumField("next_payload",0,ISAKMP_payload_type),
187        XByteField("version",0x10),
188        ByteEnumField("exch_type",0,ISAKMP_exchange_type),
189        FlagsField("flags",0, 8, ["encryption","commit","auth_only","res3","res4","res5","res6","res7"]), # XXX use a Flag field
190        IntField("id",0),
191        IntField("length",None)
192        ]
193
194    def guess_payload_class(self, payload):
195        if self.flags & 1:
196            return conf.raw_layer
197        return ISAKMP_class.guess_payload_class(self, payload)
198
199    def answers(self, other):
200        if isinstance(other, ISAKMP):
201            if other.init_cookie == self.init_cookie:
202                return 1
203        return 0
204    def post_build(self, p, pay):
205        p += pay
206        if self.length is None:
207            p = p[:24]+struct.pack("!I",len(p))+p[28:]
208        return p
209
210
211
212
213class ISAKMP_payload_Transform(ISAKMP_class):
214    name = "IKE Transform"
215    fields_desc = [
216        ByteEnumField("next_payload",None,ISAKMP_payload_type),
217        ByteField("res",0),
218#        ShortField("len",None),
219        ShortField("length",None),
220        ByteField("num",None),
221        ByteEnumField("id",1,{1:"KEY_IKE"}),
222        ShortField("res2",0),
223        ISAKMPTransformSetField("transforms",None,length_from=lambda x:x.length-8)
224#        XIntField("enc",0x80010005L),
225#        XIntField("hash",0x80020002L),
226#        XIntField("auth",0x80030001L),
227#        XIntField("group",0x80040002L),
228#        XIntField("life_type",0x800b0001L),
229#        XIntField("durationh",0x000c0004L),
230#        XIntField("durationl",0x00007080L),
231        ]
232    def post_build(self, p, pay):
233        if self.length is None:
234            l = len(p)
235            p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:]
236        p += pay
237        return p
238
239
240
241
242class ISAKMP_payload_Proposal(ISAKMP_class):
243    name = "IKE proposal"
244#    ISAKMP_payload_type = 0
245    fields_desc = [
246        ByteEnumField("next_payload",None,ISAKMP_payload_type),
247        ByteField("res",0),
248        FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8),
249        ByteField("proposal",1),
250        ByteEnumField("proto",1,{1:"ISAKMP"}),
251        FieldLenField("SPIsize",None,"SPI","B"),
252        ByteField("trans_nb",None),
253        StrLenField("SPI","",length_from=lambda x:x.SPIsize),
254        PacketLenField("trans",conf.raw_layer(),ISAKMP_payload_Transform,length_from=lambda x:x.length-8),
255        ]
256
257
258class ISAKMP_payload(ISAKMP_class):
259    name = "ISAKMP payload"
260    fields_desc = [
261        ByteEnumField("next_payload",None,ISAKMP_payload_type),
262        ByteField("res",0),
263        FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4),
264        StrLenField("load","",length_from=lambda x:x.length-4),
265        ]
266
267
268class ISAKMP_payload_VendorID(ISAKMP_class):
269    name = "ISAKMP Vendor ID"
270    overload_fields = { ISAKMP: { "next_payload":13 }}
271    fields_desc = [
272        ByteEnumField("next_payload",None,ISAKMP_payload_type),
273        ByteField("res",0),
274        FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4),
275        StrLenField("vendorID","",length_from=lambda x:x.length-4),
276        ]
277
278class ISAKMP_payload_SA(ISAKMP_class):
279    name = "ISAKMP SA"
280    overload_fields = { ISAKMP: { "next_payload":1 }}
281    fields_desc = [
282        ByteEnumField("next_payload",None,ISAKMP_payload_type),
283        ByteField("res",0),
284        FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+12),
285        IntEnumField("DOI",1,{1:"IPSEC"}),
286        IntEnumField("situation",1,{1:"identity"}),
287        PacketLenField("prop",conf.raw_layer(),ISAKMP_payload_Proposal,length_from=lambda x:x.length-12),
288        ]
289
290class ISAKMP_payload_Nonce(ISAKMP_class):
291    name = "ISAKMP Nonce"
292    overload_fields = { ISAKMP: { "next_payload":10 }}
293    fields_desc = [
294        ByteEnumField("next_payload",None,ISAKMP_payload_type),
295        ByteField("res",0),
296        FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4),
297        StrLenField("load","",length_from=lambda x:x.length-4),
298        ]
299
300class ISAKMP_payload_KE(ISAKMP_class):
301    name = "ISAKMP Key Exchange"
302    overload_fields = { ISAKMP: { "next_payload":4 }}
303    fields_desc = [
304        ByteEnumField("next_payload",None,ISAKMP_payload_type),
305        ByteField("res",0),
306        FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4),
307        StrLenField("load","",length_from=lambda x:x.length-4),
308        ]
309
310class ISAKMP_payload_ID(ISAKMP_class):
311    name = "ISAKMP Identification"
312    overload_fields = { ISAKMP: { "next_payload":5 }}
313    fields_desc = [
314        ByteEnumField("next_payload",None,ISAKMP_payload_type),
315        ByteField("res",0),
316        FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8),
317        ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}),
318        ByteEnumField("ProtoID",0,{0:"Unused"}),
319        ShortEnumField("Port",0,{0:"Unused"}),
320#        IPField("IdentData","127.0.0.1"),
321        StrLenField("load","",length_from=lambda x:x.length-8),
322        ]
323
324
325
326class ISAKMP_payload_Hash(ISAKMP_class):
327    name = "ISAKMP Hash"
328    overload_fields = { ISAKMP: { "next_payload":8 }}
329    fields_desc = [
330        ByteEnumField("next_payload",None,ISAKMP_payload_type),
331        ByteField("res",0),
332        FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4),
333        StrLenField("load","",length_from=lambda x:x.length-4),
334        ]
335
336
337
338ISAKMP_payload_type_overload = {}
339for i, payloadname in enumerate(ISAKMP_payload_type):
340    name = "ISAKMP_payload_%s" % payloadname
341    if name in globals():
342        ISAKMP_payload_type_overload[globals()[name]] = {"next_payload": i}
343
344del i, payloadname, name
345ISAKMP_class._overload_fields = ISAKMP_payload_type_overload.copy()
346
347
348bind_layers( UDP,           ISAKMP,        dport=500, sport=500)
349def ikescan(ip):
350    return sr(IP(dst=ip)/UDP()/ISAKMP(init_cookie=RandString(8),
351                                      exch_type=2)/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal()))
352
353