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