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