def SendRTSol(): """send a simple Router Solicitation message on all the configured interfaces""" # get all the interfaces on # which we should send a message on nc = NeighCache() configured_addresses = nc.dump_addresses() interfaces = used_interfaces(configured_addresses) for iface in interfaces: p = Ether(src=get_if_hwaddr(iface)) / \ IPv6(src = "::",dst = "ff02::2")/ \ ICMPv6ND_RS() sendp(p,iface=iface,verbose=NDprotector.verbose) warn("Sending an RS on interface %s\n" % iface)
def SendRTSol(): """send a simple Router Solicitation message on all the configured interfaces""" # get all the interfaces on # which we should send a message on nc = NeighCache() configured_addresses = nc.dump_addresses() interfaces = used_interfaces(configured_addresses) for iface in interfaces: p = Ether(src=get_if_hwaddr(iface)) / \ IPv6(src = "::",dst = "ff02::2")/ \ ICMPv6ND_RS() sendp(p, iface=iface, verbose=NDprotector.verbose) warn("Sending an RS on interface %s\n" % iface)
def __init__(self): """initialize the Ephemeral Address Pool""" # DP borg, all instances share the same variables self.__dict__ = self.__shared_object # DP borg if not hasattr(self, "freePool"): self.freePool = [] self.freePoolLock = RLock() if not hasattr(self, "inUsePool"): self.inUsePool = [] self.inUsePoolLock = RLock() if not hasattr(self, "TBremovedAddr"): self.TBremovedAddr = {} self.TBremovedAddrLock = RLock() # some connxion are required to the Neighbor Cache # it seems better to always stay connected if not hasattr(self, "nc"): self.nc = NeighCache() if not hasattr(self, "clean"): self.clean = True
def __init__(self): """initialize the Ephemeral Address Pool""" # DP borg, all instances share the same variables self.__dict__ = self.__shared_object # DP borg if not hasattr(self,"freePool"): self.freePool = [] self.freePoolLock = RLock() if not hasattr(self,"inUsePool"): self.inUsePool = [] self.inUsePoolLock = RLock() if not hasattr(self,"TBremovedAddr"): self.TBremovedAddr = {} self.TBremovedAddrLock = RLock() # some connxion are required to the Neighbor Cache # it seems better to always stay connected if not hasattr(self,"nc"): self.nc = NeighCache() if not hasattr(self,"clean"): self.clean = True
class EphemeralPool(object): """the pool contains two types of Ephemeral CGA: - the ones that are currently in use - the ones that are free to be used""" __shared_object = {} def __init__(self): """initialize the Ephemeral Address Pool""" # DP borg, all instances share the same variables self.__dict__ = self.__shared_object # DP borg if not hasattr(self, "freePool"): self.freePool = [] self.freePoolLock = RLock() if not hasattr(self, "inUsePool"): self.inUsePool = [] self.inUsePoolLock = RLock() if not hasattr(self, "TBremovedAddr"): self.TBremovedAddr = {} self.TBremovedAddrLock = RLock() # some connxion are required to the Neighbor Cache # it seems better to always stay connected if not hasattr(self, "nc"): self.nc = NeighCache() if not hasattr(self, "clean"): self.clean = True def get_address_in_use(self): """returns the addresses that are currently in use""" with self.inUsePoolLock: return self.inUsePool def get_not_yet_used_addresses(self): """returns the list of addresses that are currently free to be used""" with self.freePoolLock: return self.freePool def prepare_next_valid_address(self): """pick on address from the pool and change it valid state""" with nested(self.freePoolLock, self.inUsePoolLock): try: address = self.freePool[0] address.modify( ) # by default, place the address in "valid" state del self.freePool[0] self.inUsePool.append(address) except IndexError: # or will crash when no address is left pass def is_ephemeral_CGA(self, address): """return True if the address is an Ephemeral CGA""" with nested(self.freePoolLock, self.inUsePoolLock): return str(address) in [ str(address) for address in self.freePool + self.inUsePool ] def reportaddress_as_used(self, address): """report the address as an address that is currently in use""" # connect to the NC to obtain the list of currently assigned addresses configured_addresses = self.nc.dump_addresses() # address = None # obtain the address object for address_obj in (a for a in configured_addresses if str(a) == address): address = address_obj break # we can stop here as the address is only recorded one # change the state of the address to deprecated try: address.modify(preferred_lft=0) warn( "Address %s reported to have initialized a connexion is now being deprecated\n" % address) except AttributeError: # the address does not belong to our program pass # if this is an ephemeral CGA from the "free" pool, place it # in the "in use" pool try: with nested(self.freePoolLock, self.inUsePoolLock): index = self.freePool.index(address) del self.freePool[index] self.inUsePool.append(address) except (ValueError, IndexError): pass def regenerate(self, n_address): """create n_address addresses to fill the pool there should be POOL_SIZE addresses in the pool""" warn("Ephemeral CGA pool: regeneration of %i addresses\n" % n_address) for i in range(n_address): a = Address(key=NDprotector.default_publickey, interface=EphemeralAddress.INTERFACE, prefix=EphemeralAddress.PREFIX, ephemeral=True, sec=0, dad=False) # temporary a.modify(preferred_lft=0) with self.freePoolLock: self.freePool.append(a) # connect to the NC to store the new address self.nc.store_address(a) warn("Ephemeral CGA pool: regeneration of %i addresses complete\n" % n_address) def schedule_address_removal(self, address): """schedule an address for removal after TIMEOUT seconds""" EphemeralAddress.TIMEOUT with nested(self.inUsePoolLock, self.TBremovedAddrLock): for address in (a for a in self.inUsePool if str(a) == address): index = self.inUsePool.index(address) del self.inUsePool[index] self.TBremovedAddr[address] = EphemeralAddress.TIMEOUT break def close_cleaning_thread(self): """close the cleaning/maintenance thread""" self.clean = False
def callback(i, payload): """a callback function called on each ingoing packets""" data = payload.get_data() packet = IPv6(data) # we try to extract the SSAO if available try: (signalgs, verifalgs) = SigAlgList_split(packet[ICMPv6NDOptSSA].sigalgs) except AttributeError: signalgs = [] verifalgs = [] # may be buggy due to the order of get_if_list() # (in that case, we could use the if_nametoindex() ) interface = get_if_list()[payload.get_indev() - 1] nc = NeighCache() if packet.haslayer(ICMPv6ND_NS) \ or packet.haslayer(ICMPv6ND_NA): secured = False try: address = packet[IPv6].src # we can receive messages from other node performing a DAD if address == "::": address = packet.tgt cga_prop = CGAverify(address, packet.getlayer(CGAParams)) if not cga_prop: warn("an ingoing packet has failed CGA verification test\n") else: # only perform the RSA signature here, # Timestamp and Nonce verification are performed in the NC # FIXME: this is not "good", we are subject to a DoS attack here list_of_pubkeys = CGAPKExtListtoPubKeyList( packet[CGAParams].ext) list_of_pubkeys.insert(0, packet[CGAParams].pubkey) pubkey = list_of_pubkeys[packet[ICMPv6NDOptUSSig].pos] if isinstance(pubkey, PubKey) and \ not (NDprotector.min_RSA_key_size <= pubkey.modulusLen and pubkey.modulusLen <= NDprotector.max_RSA_key_size): warn("A message we received contains" " a Public Key whose size is not allowed\n") else: # Public Key is of a correct size if packet[ICMPv6NDOptUSSig].keyh !=\ get_public_key_hash(pubkey, packet[ICMPv6NDOptUSSig].sigtypeID) : warn("Public key contained in the CGA PDS does not match " \ "the Public Key hash in the Universal Sig option\n") else: if not packet[ICMPv6NDOptUSSig].verify_sig(pubkey): warn( "an ingoing packet has failed Universal Signature verification\n" ) else: secured = True except AttributeError: warn("An unsecured packet passed\n") # if the status is true, we allow the packet to pass status = False # we can extract the Source Link Layer option from a NS if packet.haslayer(ICMPv6ND_NS): mac_address = None try: mac_address = packet.getlayer(ICMPv6NDOptSrcLLAddr).lladdr except AttributeError: pass # the request does not contain # any information on L2 address if mac_address == None: status = True else: src_address = packet.getlayer(IPv6).src if secured: public_key = str(packet.getlayer(CGAParams).pubkey) timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp warn( "NC updating an entry with an secured NS message from the address: %s\n" % packet.src) status = nc.update(src_address, mac_address, public_key, timestamp, secured, ssao=(signalgs, verifalgs)) if not status: warn( "NS Message from %s rejected (due to a bad timestamp or nonce)\n" % packet.src) # record the nonce if any if packet.haslayer(ICMPv6NDOptNonce): nc.record_nonce_in(packet[IPv6].src, packet[IPv6].dst, packet[ICMPv6NDOptNonce].nonce) elif NDprotector.mixed_mode == True: warn( "NC updating an entry with an unsecured NS message from the address: %s\n" % packet.src) status = nc.update(src_address, mac_address, None, None, secured, ssao=(signalgs, verifalgs)) else: warn( "NC not updating an entry with an unsecured NS message from the address: %s\n" % packet.src) status = False # this is a NA, we extract the Target Link Layer option else: target = packet.tgt mac_address = None try: mac_address = packet.getlayer(ICMPv6NDOptDstLLAddr).lladdr except AttributeError: pass if mac_address == None: status = True else: if secured: public_key = str(packet.getlayer(CGAParams).pubkey) timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp try: nonce = packet.getlayer(ICMPv6NDOptNonce).nonce except AttributeError: nonce = None warn( "NC updating an entry with an secured NA message from the address: %s\n" % packet.src) # multicasted messages may not contain any nonce as they are sent spontaneously if packet[IPv6].dst != "ff02::1": status = nc.check_nonce_in(packet[IPv6].src, packet[IPv6].dst, nonce) # only update when the nonce is correct status = status and nc.update(target, mac_address, public_key,\ timestamp, secured, ssao= (signalgs, verifalgs)) elif NDprotector.mixed_mode == True: warn( "NC updating an entry with an unsecured NA message from the address: %s\n" % packet.src) status = nc.update(target, mac_address, None, None,\ secured, ssao= (signalgs, verifalgs)) else: warn( "NC not updating an entry with an unsecured NA message from the address: %s\n" % packet.src) status = False elif packet.haslayer(ICMPv6ND_RA): if NDprotector.is_router == True: # a router should not receive RA and process them anyway status = False else: status = False # check RSA signature, extract PK certcache = CertCache() # extract the prefix(es) from the RA message prefixes = [] try: next_prefix = packet.getlayer(ICMPv6NDOptPrefixInfo) while next_prefix: if next_prefix.validlifetime != 0 and next_prefix.prefixlen == 64: prefixes.append(next_prefix.prefix) next_prefix = next_prefix.payload.getlayer( ICMPv6NDOptPrefixInfo) except AttributeError: pass warn("RA contains following prefix(es): %s\n" % ` prefixes `) if packet.haslayer(ICMPv6NDOptUSSig): secured = False list_of_pubkeys = CGAPKExtListtoPubKeyList( packet[CGAParams].ext) list_of_pubkeys.insert(0, packet[CGAParams].pubkey) pubkey = list_of_pubkeys[packet[ICMPv6NDOptUSSig].pos] pkhash = get_public_key_hash( pubkey, packet[ICMPv6NDOptUSSig].sigtypeID) if (isinstance(pubkey,PubKey) and \ (NDprotector.min_RSA_key_size <= pubkey.modulusLen and pubkey.modulusLen <= NDprotector.max_RSA_key_size))\ or\ (NDprotector.ECCsupport and isinstance(pubkey, ECCkey)): if CGAverify(packet[IPv6].src,packet.getlayer(CGAParams)) and \ packet[ICMPv6NDOptUSSig].keyh == pkhash and \ packet[ICMPv6NDOptUSSig].verify_sig(pubkey): secured = True else: warn( "an ingoing RA has failed CGA verification or Universal Signature test\n" ) else: warn("A message we received contains" " a Public Key whose size is not allowed\n") # extract the Link-Layer Address from the RA message if any mac_address = None try: mac_address = packet[ICMPv6NDOptSrcLLAddr].lladdr except AttributeError: pass if secured: keyh = packet[ICMPv6NDOptUSSig].keyh if certcache.trustable_hash(keyh, prefixes,\ packet[ICMPv6NDOptUSSig].sigtypeID): warn( "Public Key associated to the signature corresponds to a certificate\n" ) else: # we need to send some CPS in order to recover the certpath secured = False import random warn("Sending a CPS message\n") req_id = random.randrange(1, 0xFFFF) certcache.storeid(req_id) p = Ether(src=get_if_hwaddr(interface),dst=mac_address) / \ IPv6(src="::", dst=packet[IPv6].src)/ \ ICMPv6SEND_CPS(id=req_id,comp=0xffff) sendp(p, iface=interface, verbose=NDprotector.verbose) # we wait for the answers time.sleep(0.4) # we check if the certificate path is now valid secured = certcache.trustable_hash(keyh, prefixes,\ packet[ICMPv6NDOptUSSig].sigtypeID) # check the nonce if the destination address is not ff02::1 if secured and not packet[ IPv6].dst == "ff02::1" and packet.haslayer( ICMPv6NDOptNonce): nonce = packet.getlayer(ICMPv6NDOptNonce).nonce status = nc.check_nonce_in(packet[IPv6].src, packet[IPv6].dst, nonce) elif secured and packet[IPv6].dst == "ff02::1": status = True public_key = str(packet.getlayer(CGAParams).pubkey) timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp if status: warn( "NC updating an entry with an secured RA message from the address: %s\n" % packet.src) status = nc.update(packet[IPv6].src, mac_address, public_key,\ timestamp,secured, ssao= (signalgs, verifalgs)) if status: configured_addresses = nc.dump_addresses() # Address Autoconfiguration part if NDprotector.assign_addresses: # extract the prefix(es) from the RA message that contains the Autonomous flag prefixes_aut = [] try: next_prefix = packet.getlayer( ICMPv6NDOptPrefixInfo) while next_prefix: if next_prefix.prefixlen == 64 \ and next_prefix.A == 1 \ and next_prefix.validlifetime >= next_prefix.preferredlifetime: # we update the prefix cache # (note that the prefix cache will take care # of prefix whose valid lifetime is now 0) nc.update_prefix( next_prefix.prefix, next_prefix.preferredlifetime, next_prefix.validlifetime) # we will do autoconfiguration on these addresses if next_prefix.validlifetime != 0: prefixes_aut.append( (next_prefix.prefix, next_prefix.validlifetime, next_prefix.preferredlifetime)) # set the Autonomous flag to 0 so linux kernel won't # set a new address too next_prefix.A = 0 next_prefix = next_prefix.payload.getlayer( ICMPv6NDOptPrefixInfo) except AttributeError: pass for (prefix, valid, preferred) in prefixes_aut: for address in configured_addresses: # extract the prefix from the address if address.get_prefix() == prefix: # we have a correspondance to this address # refresh the valid and preferred lifetime address.modify(valid, preferred) # let the RA message go break # if no address exists for this prefix else: # create an address for each prefixes new_address = Address( prefix=prefix, key=NDprotector.default_publickey, sec=1, interface=interface, autoconf=True, valid=valid, preferred=preferred) warn( "created a new address (%s) for prefix %s\n" % (str(new_address), prefix)) nc.store_address(new_address) # force checksum calculation del (packet[IPv6].payload.cksum) packet = IPv6(str(packet)) # force checksum calculation # TC 02/19/10: for the record, # that was the previous ugly version # dirty hack to copy the message, except the PIO # let the RA message go , but only after the prefix(es) has been removed # (avoid that kernel autoconfiguration process get involved) # eth = Ether(data) # payld = packet # while payld and not isinstance(payld,NoPayload): # current_layer = payld # try: # payld = payld.payload # except AttributeError: # payld = None # try: # if not isinstance(current_layer,ICMPv6NDOptPrefixInfo): # current_layer.remove_payload() # new_eth /= current_layer # except AttributeError: # payld = None # TC 03/15/10: previous version included a Ethernet header that does not seem appropriate # new_eth = Ether(src=eth.src, dst=eth.dst) / packet # payload.set_verdict_modified(nfqueue.NF_ACCEPT,str(new_eth),len(str(new_eth))) payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(packet), len(str(packet))) return 0 elif NDprotector.mixed_mode: status = True else: status = False elif packet.haslayer(ICMPv6ND_RS): if NDprotector.is_router == True: # if the message does not come from the unspecified address, # perform normal checks if packet[IPv6].src == "::": # nothing to do on a message with the unspecified address as # source address status = True elif packet.haslayer(ICMPv6NDOptUSSig): if not (CGAverify(packet[IPv6].src,packet.getlayer(CGAParams)) and \ packet[ICMPv6NDOptUSSig].keyh == \ get_public_key_hash(packet[CGAParams].pubkey,packet[ICMPv6NDOptUSSig].sigtypeID) and \ packet[ICMPv6NDOptUSSig].verify_sig(packet[CGAParams].pubkey)): warn( "an ingoing packet has failed CGA verification or USO signature test\n" ) status = False else: # record the nonce if any if packet.haslayer(ICMPv6NDOptNonce): nc.record_nonce_in(packet[IPv6].src, packet[IPv6].dst, packet[ICMPv6NDOptNonce].nonce) src_address = packet[IPv6].src mac_address = None try: mac_address = packet.getlayer( ICMPv6NDOptSrcLLAddr).lladdr except AttributeError: pass public_key = str(packet.getlayer(CGAParams).pubkey) timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp warn( "NC updating an entry with an secured RS message from the address: %s\n" % packet.src) status = nc.update(src_address, mac_address, public_key, timestamp, secured=True, ssao=(signalgs, verifalgs)) elif NDprotector.mixed_mode: warn("letting in an unsecured RS message\n") status = True else: warn("dropping an unsecured RS message\n") status = False else: # Host do not process RS messages status = False else: warn("letting in a non NDP message\n") status = True if status: payload.set_verdict(nfqueue.NF_ACCEPT) else: payload.set_verdict(nfqueue.NF_DROP) return 0
def callback(i,payload): """a callback function called on each outgoing packets""" data = payload.get_data() packet = IPv6(data) nc = NeighCache() # fetching the latest configured addresses on the interfaces configured_addresses = nc.dump_addresses() if packet.haslayer(ICMPv6ND_NS) \ or packet.haslayer(ICMPv6ND_NA): for addr in configured_addresses: print "Out.py | addr in config %s" % str(addr) # jochoi: debug print "Out.py | source address %s" % packet[IPv6].src # jochoi: debug if str(addr) == packet[IPv6].src: print "Out.py | addr in config matches packet source address" # jochoi: debug if packet.haslayer(ICMPv6ND_NS): nonce = "".join([ chr(random.randrange(255)) for i in range(6)]) nc.record_nonce_out(packet[IPv6].src,packet[IPv6].dst,nonce) else: nonce = nc.pop_nonce_out(packet[IPv6].src,packet[IPv6].dst) data = addr.sign(data,nonce=nonce) # jochoi: split warning for NS and NA messages # warn("signing a NS or NA message\n") if packet.haslayer(ICMPv6ND_NS): warn("signing a NS message\n") else: warn("signing a NA message\n") payload.set_verdict_modified(nfqueue.NF_ACCEPT,str(data),len(str(data))) return 0 else: if NDprotector.mixed_mode: warn("letting go one outgoing unsecured packet\n") payload.set_verdict(nfqueue.NF_ACCEPT) return 0 # added return 0 else: warn("dropping one unsecured packet\n") payload.set_verdict(nfqueue.NF_DROP) return 0 elif packet.haslayer(ICMPv6ND_RS): if NDprotector.is_router == False: if packet[IPv6].src == "::" : payload.set_verdict(nfqueue.NF_ACCEPT) return 0 else: # we need to sign the message for addr in configured_addresses: if str(addr) == packet[IPv6].src: # we generate a nonce for this request nonce = "".join([ chr(random.randrange(255)) for i in range(6)]) nc.record_nonce_out(packet[IPv6].src,packet[IPv6].dst, nonce) warn("signing an outgoing RS message\n") data = addr.sign(data,nonce=nonce) payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(data), len(str(data))) return 0 else: if NDprotector.mixed_mode: warn("letting go one outgoing unsecure RS packet\n") payload.set_verdict(nfqueue.NF_ACCEPT) return 0 else: warn("dropping one unsecure RS packet\n") payload.set_verdict(nfqueue.NF_DROP) return 0 else: # a router does not send this kind of messages payload.set_verdict(nfqueue.NF_DROP) elif packet.haslayer(ICMPv6ND_RA): if NDprotector.is_router: # we need to sign the message for addr in configured_addresses: if str(addr) == packet[IPv6].src: nonce = nc.pop_nonce_out(packet[IPv6].src,packet[IPv6].dst) if nonce != None: nonce = nonce.data warn("signing an outgoing RA message\n") # note that if nonce is None, no nonce value will join this message data = addr.sign(data,nonce=nonce) payload.set_verdict_modified(nfqueue.NF_ACCEPT,str(data),len(str(data))) return 0 else: if NDprotector.mixed_mode: warn("letting go one outgoing unsecure RA packet\n") payload.set_verdict(nfqueue.NF_ACCEPT) return 0 else: warn("dropping one unsecure RA packet\n") payload.set_verdict(nfqueue.NF_DROP) return 0 else: # a host does not send RA messages payload.set_verdict(nfqueue.NF_DROP) return 0 else: warn("letting a non NDP message go out\n") payload.set_verdict(nfqueue.NF_ACCEPT) return 0
def callback(i, payload): """a callback function called on each ingoing packets""" data = payload.get_data() packet = IPv6(data) # we try to extract the SSAO if available try: (signalgs, verifalgs) = SigAlgList_split(packet[ICMPv6NDOptSSA].sigalgs) except AttributeError: signalgs = [] verifalgs = [] print "In.py | Interface index is : %s" % payload.get_indev() # jochoi: debug print "In.py | Interface name is : %s" % if_indextoname(payload.get_indev()) # jochoi: debug # may be buggy due to the order of get_if_list() # (in that case, we could use the if_nametoindex() ) # interface = get_if_list()[ payload.get_indev() - 1] interface = if_indextoname(payload.get_indev()) # jochoi: edit nc = NeighCache() if packet.haslayer(ICMPv6ND_NS) \ or packet.haslayer(ICMPv6ND_NA): # jochoi: begin debug to match arpsec02 printout if packet.haslayer(ICMPv6ND_NS): print "In.py | Received NS message" else: print "In.py | Received NA message" print "In.py | Source address: %s" % packet[IPv6].src print "In.py | Target address: %s" % packet[IPv6].tgt # jochoi: end debug to match arpsec02 secured = False try: address = packet[IPv6].src # we can receive messages from other node performing a DAD if address == "::": address = packet.tgt print "In.py | About to invoke CGAverify, check 1" # jochoi: debug cga_prop = CGAverify(address,packet.getlayer(CGAParams)) if not cga_prop: warn("an ingoing packet has failed CGA verification test\n") else: # only perform the RSA signature here, # Timestamp and Nonce verification are performed in the NC # FIXME: this is not "good", we are subject to a DoS attack here print "In.py | CGAverify successful" # jochoi: debug list_of_pubkeys = CGAPKExtListtoPubKeyList(packet[CGAParams].ext) list_of_pubkeys.insert(0,packet[CGAParams].pubkey) pubkey = list_of_pubkeys[packet[ICMPv6NDOptUSSig].pos] if isinstance(pubkey, PubKey) and \ not (NDprotector.min_RSA_key_size <= pubkey.modulusLen and pubkey.modulusLen <= NDprotector.max_RSA_key_size): warn("A message we received contains" " a Public Key whose size is not allowed\n") else: # Public Key is of a correct size if packet[ICMPv6NDOptUSSig].keyh !=\ get_public_key_hash(pubkey, packet[ICMPv6NDOptUSSig].sigtypeID) : warn("Public key contained in the CGA PDS does not match " \ "the Public Key hash in the Universal Sig option\n") else: if not packet[ICMPv6NDOptUSSig].verify_sig(pubkey): warn("an ingoing packet has failed Universal Signature verification\n") else: secured = True print "In.py | Set secured to TRUE" # jochoi: debug except AttributeError: warn("An unsecured packet passed\n") # if the status is true, we allow the packet to pass status = False # we can extract the Source Link Layer option from a NS if packet.haslayer(ICMPv6ND_NS): mac_address = None try: mac_address = packet.getlayer(ICMPv6NDOptSrcLLAddr).lladdr except AttributeError: pass # the request does not contain # any information on L2 address if mac_address == None: status = True else: src_address = packet.getlayer(IPv6).src if secured: public_key = str(packet.getlayer(CGAParams).pubkey) timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp warn("NC updating an entry with an secured NS message from the address: %s\n" % packet.src) status = nc.update(src_address, mac_address, public_key, timestamp, secured, ssao= (signalgs, verifalgs)) if not status: warn("NS Message from %s rejected (due to a bad timestamp or nonce)\n" % packet.src) # record the nonce if any if packet.haslayer(ICMPv6NDOptNonce): nc.record_nonce_in(packet[IPv6].src,packet[IPv6].dst, packet[ICMPv6NDOptNonce].nonce) elif NDprotector.mixed_mode == True: warn("NC updating an entry with an unsecured NS message from the address: %s\n" % packet.src) status = nc.update(src_address, mac_address, None, None, secured, ssao= (signalgs, verifalgs)) else: warn("NC not updating an entry with an unsecured NS message from the address: %s\n" % packet.src) status = False # this is a NA, we extract the Target Link Layer option else: target = packet.tgt mac_address = None try: mac_address = packet.getlayer(ICMPv6NDOptDstLLAddr).lladdr except AttributeError: pass if mac_address == None: status = True else: if secured: public_key = str(packet.getlayer(CGAParams).pubkey) timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp try: nonce = packet.getlayer(ICMPv6NDOptNonce).nonce except AttributeError: nonce = None warn("NC updating an entry with an secured NA message from the address: %s\n" % packet.src) # multicasted messages may not contain any nonce as they are sent spontaneously if packet[IPv6].dst != "ff02::1": status = nc.check_nonce_in(packet[IPv6].src,packet[IPv6].dst,nonce) # only update when the nonce is correct status = status and nc.update(target, mac_address, public_key,\ timestamp, secured, ssao= (signalgs, verifalgs)) elif NDprotector.mixed_mode == True: warn("NC updating an entry with an unsecured NA message from the address: %s\n" % packet.src) status = nc.update(target, mac_address, None, None,\ secured, ssao= (signalgs, verifalgs)) else: warn("NC not updating an entry with an unsecured NA message from the address: %s\n" % packet.src) status = False elif packet.haslayer(ICMPv6ND_RA): if NDprotector.is_router == True: # a router should not receive RA and process them anyway status = False else: status = False # check RSA signature, extract PK certcache = CertCache() # extract the prefix(es) from the RA message prefixes = [] try: next_prefix = packet.getlayer(ICMPv6NDOptPrefixInfo) while next_prefix: if next_prefix.validlifetime != 0 and next_prefix.prefixlen == 64: prefixes.append(next_prefix.prefix) next_prefix = next_prefix.payload.getlayer(ICMPv6NDOptPrefixInfo) except AttributeError: pass warn("RA contains following prefix(es): %s\n" % `prefixes`) if packet.haslayer(ICMPv6NDOptUSSig): secured = False list_of_pubkeys = CGAPKExtListtoPubKeyList(packet[CGAParams].ext) list_of_pubkeys.insert(0,packet[CGAParams].pubkey) pubkey = list_of_pubkeys[packet[ICMPv6NDOptUSSig].pos] pkhash = get_public_key_hash(pubkey, packet[ICMPv6NDOptUSSig].sigtypeID) if (isinstance(pubkey,PubKey) and \ (NDprotector.min_RSA_key_size <= pubkey.modulusLen and pubkey.modulusLen <= NDprotector.max_RSA_key_size))\ or\ (NDprotector.ECCsupport and isinstance(pubkey, ECCkey)): print "About to invoke CGAverify in In.py, check 2" # jochoi: debug if CGAverify(packet[IPv6].src,packet.getlayer(CGAParams)) and \ packet[ICMPv6NDOptUSSig].keyh == pkhash and \ packet[ICMPv6NDOptUSSig].verify_sig(pubkey): secured = True else: warn("an ingoing RA has failed CGA verification or Universal Signature test\n") else: warn("A message we received contains" " a Public Key whose size is not allowed\n") # extract the Link-Layer Address from the RA message if any mac_address = None try: mac_address = packet[ICMPv6NDOptSrcLLAddr].lladdr except AttributeError: pass if secured : keyh = packet[ICMPv6NDOptUSSig].keyh if certcache.trustable_hash(keyh, prefixes,\ packet[ICMPv6NDOptUSSig].sigtypeID): warn("Public Key associated to the signature corresponds to a certificate\n") else: # we need to send some CPS in order to recover the certpath secured = False import random warn("Sending a CPS message\n") req_id = random.randrange(1,0xFFFF) certcache.storeid(req_id) p = Ether(src=get_if_hwaddr(interface),dst=mac_address) / \ IPv6(src="::", dst=packet[IPv6].src)/ \ ICMPv6SEND_CPS(id=req_id,comp=0xffff) sendp(p,iface=interface,verbose=NDprotector.verbose) # we wait for the answers time.sleep(0.4) # we check if the certificate path is now valid secured = certcache.trustable_hash(keyh, prefixes,\ packet[ICMPv6NDOptUSSig].sigtypeID) # check the nonce if the destination address is not ff02::1 if secured and not packet[IPv6].dst == "ff02::1" and packet.haslayer(ICMPv6NDOptNonce): nonce = packet.getlayer(ICMPv6NDOptNonce).nonce status = nc.check_nonce_in(packet[IPv6].src,packet[IPv6].dst,nonce) elif secured and packet[IPv6].dst == "ff02::1": status = True public_key = str(packet.getlayer(CGAParams).pubkey) timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp if status: warn("NC updating an entry with an secured RA message from the address: %s\n" % packet.src) status = nc.update(packet[IPv6].src, mac_address, public_key,\ timestamp,secured, ssao= (signalgs, verifalgs)) if status: configured_addresses = nc.dump_addresses() # Address Autoconfiguration part if NDprotector.assign_addresses: # extract the prefix(es) from the RA message that contains the Autonomous flag prefixes_aut = [] try: next_prefix = packet.getlayer(ICMPv6NDOptPrefixInfo) while next_prefix: if next_prefix.prefixlen == 64 \ and next_prefix.A == 1 \ and next_prefix.validlifetime >= next_prefix.preferredlifetime: # we update the prefix cache # (note that the prefix cache will take care # of prefix whose valid lifetime is now 0) nc.update_prefix(next_prefix.prefix, next_prefix.preferredlifetime, next_prefix.validlifetime) # we will do autoconfiguration on these addresses if next_prefix.validlifetime !=0: prefixes_aut.append((next_prefix.prefix, next_prefix.validlifetime, next_prefix.preferredlifetime) ) # set the Autonomous flag to 0 so linux kernel won't # set a new address too next_prefix.A = 0 next_prefix = next_prefix.payload.getlayer(ICMPv6NDOptPrefixInfo) except AttributeError: pass for (prefix, valid, preferred) in prefixes_aut: for address in configured_addresses: # extract the prefix from the address if address.get_prefix() == prefix: # we have a correspondance to this address # refresh the valid and preferred lifetime address.modify(valid,preferred) # let the RA message go break # if no address exists for this prefix else: # create an address for each prefixes new_address = Address(prefix = prefix, key = NDprotector.default_publickey, sec = 1, interface = interface, autoconf = True, valid = valid, preferred = preferred) warn("created a new address (%s) for prefix %s\n" % (str(new_address), prefix)) nc.store_address(new_address) # force checksum calculation del (packet[IPv6].payload.cksum) packet = IPv6(str(packet)) # force checksum calculation # TC 02/19/10: for the record, # that was the previous ugly version # dirty hack to copy the message, except the PIO # let the RA message go , but only after the prefix(es) has been removed # (avoid that kernel autoconfiguration process get involved) # eth = Ether(data) # payld = packet # while payld and not isinstance(payld,NoPayload): # current_layer = payld # try: # payld = payld.payload # except AttributeError: # payld = None # try: # if not isinstance(current_layer,ICMPv6NDOptPrefixInfo): # current_layer.remove_payload() # new_eth /= current_layer # except AttributeError: # payld = None # TC 03/15/10: previous version included a Ethernet header that does not seem appropriate # new_eth = Ether(src=eth.src, dst=eth.dst) / packet # payload.set_verdict_modified(nfqueue.NF_ACCEPT,str(new_eth),len(str(new_eth))) payload.set_verdict_modified(nfqueue.NF_ACCEPT,str(packet),len(str(packet))) return 0 elif NDprotector.mixed_mode: status = True else: status = False elif packet.haslayer(ICMPv6ND_RS): if NDprotector.is_router == True: # if the message does not come from the unspecified address, # perform normal checks if packet[IPv6].src == "::": # nothing to do on a message with the unspecified address as # source address status = True elif packet.haslayer(ICMPv6NDOptUSSig): if not (CGAverify(packet[IPv6].src,packet.getlayer(CGAParams)) and \ packet[ICMPv6NDOptUSSig].keyh == \ get_public_key_hash(packet[CGAParams].pubkey,packet[ICMPv6NDOptUSSig].sigtypeID) and \ packet[ICMPv6NDOptUSSig].verify_sig(packet[CGAParams].pubkey)): warn("an ingoing packet has failed CGA verification or USO signature test\n") status = False else: # record the nonce if any if packet.haslayer(ICMPv6NDOptNonce): nc.record_nonce_in(packet[IPv6].src,packet[IPv6].dst, packet[ICMPv6NDOptNonce].nonce) src_address = packet[IPv6].src mac_address = None try: mac_address = packet.getlayer(ICMPv6NDOptSrcLLAddr).lladdr except AttributeError: pass public_key = str(packet.getlayer(CGAParams).pubkey) timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp warn("NC updating an entry with an secured RS message from the address: %s\n" % packet.src) status = nc.update(src_address, mac_address, public_key, timestamp, secured = True, ssao= (signalgs, verifalgs)) elif NDprotector.mixed_mode: warn("letting in an unsecured RS message\n") status = True else: warn("dropping an unsecured RS message\n") status = False else: # Host do not process RS messages status = False else: warn("letting in a non NDP message\n") status = True if status: payload.set_verdict(nfqueue.NF_ACCEPT) else: payload.set_verdict(nfqueue.NF_DROP) return 0
def sign(self,data, dad=False, nonce=None): """sign the data with RSA signature option, adds timestamp, nonce, etc. Data must be a valid NS, NA, RS, RA or ICMPv6 redirect packet""" ndp_msg = IPv6(data) if dad and ndp_msg[IPv6].src != "::" : raise AddressException("tried to sign a DAD message with the null address") elif not dad and ndp_msg[IPv6].src != self.__address: raise AddressException("tried to sign a message with an incorrect source address") if not ndp_msg.haslayer(ICMPv6ND_RS) \ and not ndp_msg.haslayer(ICMPv6ND_RA) \ and not ndp_msg.haslayer(ICMPv6ND_NS) \ and not ndp_msg.haslayer(ICMPv6ND_NA) \ and not ndp_msg.haslayer(ICMPv6ND_Redirect) : raise AddressException("tried to sign a packet that is not a NDP message") # TODO: # for now, we only take the first algorithm of the list, # latter, we will implement a round-robin mechanism nc = NeighCache() ( sign, verif ) = nc.get_ssao(ndp_msg.dst) neigh_sigalg = (sign + verif) sigtypeID = self.sign_doable(neigh_sigalg) if sigtypeID == None: if NDprotector.ECCsupport and isinstance(self.__key,ECCkey): sigtypeID = self.__key.get_sigtypeID()[0] warn("No matching Signature Algorithm to sign the message, using %s\n" \ % SigTypeID[sigtypeID]) else: warn("No matching Signature Algorithm to sign the message, using RSA/SHA-1\n") sigtypeID = 0 cgapds = CGAParams(modifier = pkcs_i2osp(self.__modifier, 16), \ prefix = self.__prefix, \ ccount = self.__collcount, \ pubkey = self.__pubkey, \ ext = self.__ext) # in some case, we may not really want to add a nonce value if nonce is None: # no nonce value are joined with this message ndp_msg /= ICMPv6NDOptCGA(cgaparams = cgapds) / \ ICMPv6NDOptTimestamp() else: # add the different options to a message ndp_msg /= ICMPv6NDOptCGA(cgaparams = cgapds) / \ ICMPv6NDOptTimestamp() / \ ICMPv6NDOptNonce(nonce=nonce) # compute the available Signature Algorithms sigalgs = SigAlgList_compute(sigtypeID, NDprotector.SignatureAlgorithms) ndp_msg /= ICMPv6NDOptSSA(sigalgs=sigalgs) # dirty hack: force the update of the payload length extra_payload_len = len(str(ndp_msg.getlayer(ICMPv6NDOptCGA))) ndp_msg[IPv6].plen += extra_payload_len # dirty hack: force to recompute the (ICMP) checksum del(ndp_msg[IPv6].payload.cksum) # freezing data inside the new option fields ndp_msg = IPv6(str(ndp_msg)) # need to bind the key hash with address' PubKey pubkey = load_pkey(self.__key, NDprotector.ECCsupport) keyh = get_public_key_hash(pubkey, sigtypeID=sigtypeID) # adding the signature ndp_msg /= ICMPv6NDOptUSSig(key=self.__key, pos = self.__keypos, keyh = keyh, sigtypeID=sigtypeID) # dirty hack: force the update of the payload length extra_payload_len = len(str(ndp_msg.getlayer(ICMPv6NDOptUSSig))) ndp_msg[IPv6].plen += extra_payload_len # dirty hack: force to recompute the (ICMP) checksum (once again) del(ndp_msg[IPv6].payload.cksum) return ndp_msg
def callback(i, payload): """a callback function called on each outgoing packets""" data = payload.get_data() packet = IPv6(data) nc = NeighCache() # fetching the latest configured addresses on the interfaces configured_addresses = nc.dump_addresses() if packet.haslayer(ICMPv6ND_NS) \ or packet.haslayer(ICMPv6ND_NA): for addr in configured_addresses: if str(addr) == packet[IPv6].src: if packet.haslayer(ICMPv6ND_NS): nonce = "".join( [chr(random.randrange(255)) for i in range(6)]) nc.record_nonce_out(packet[IPv6].src, packet[IPv6].dst, nonce) else: nonce = nc.pop_nonce_out(packet[IPv6].src, packet[IPv6].dst) data = addr.sign(data, nonce=nonce) warn("signing a NS or NA message\n") payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(data), len(str(data))) return 0 else: if NDprotector.mixed_mode: warn("letting go one outgoing unsecured packet\n") payload.set_verdict(nfqueue.NF_ACCEPT) return else: warn("dropping one unsecured packet\n") payload.set_verdict(nfqueue.NF_DROP) return 0 elif packet.haslayer(ICMPv6ND_RS): if NDprotector.is_router == False: if packet[IPv6].src == "::": payload.set_verdict(nfqueue.NF_ACCEPT) return 0 else: # we need to sign the message for addr in configured_addresses: if str(addr) == packet[IPv6].src: # we generate a nonce for this request nonce = "".join( [chr(random.randrange(255)) for i in range(6)]) nc.record_nonce_out(packet[IPv6].src, packet[IPv6].dst, nonce) warn("signing an outgoing RS message\n") data = addr.sign(data, nonce=nonce) payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(data), len(str(data))) return 0 else: if NDprotector.mixed_mode: warn("letting go one outgoing unsecure RS packet\n") payload.set_verdict(nfqueue.NF_ACCEPT) return 0 else: warn("dropping one unsecure RS packet\n") payload.set_verdict(nfqueue.NF_DROP) return 0 else: # a router does not send this kind of messages payload.set_verdict(nfqueue.NF_DROP) elif packet.haslayer(ICMPv6ND_RA): if NDprotector.is_router: # we need to sign the message for addr in configured_addresses: if str(addr) == packet[IPv6].src: nonce = nc.pop_nonce_out(packet[IPv6].src, packet[IPv6].dst) if nonce != None: nonce = nonce.data warn("signing an outgoing RA message\n") # note that if nonce is None, no nonce value will join this message data = addr.sign(data, nonce=nonce) payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(data), len(str(data))) return 0 else: if NDprotector.mixed_mode: warn("letting go one outgoing unsecure RA packet\n") payload.set_verdict(nfqueue.NF_ACCEPT) return 0 else: warn("dropping one unsecure RA packet\n") payload.set_verdict(nfqueue.NF_DROP) return 0 else: # a host does not send RA messages payload.set_verdict(nfqueue.NF_DROP) return 0 else: warn("letting a non NDP message go out\n") payload.set_verdict(nfqueue.NF_ACCEPT) return 0
class EphemeralPool(object): """the pool contains two types of Ephemeral CGA: - the ones that are currently in use - the ones that are free to be used""" __shared_object = {} def __init__(self): """initialize the Ephemeral Address Pool""" # DP borg, all instances share the same variables self.__dict__ = self.__shared_object # DP borg if not hasattr(self,"freePool"): self.freePool = [] self.freePoolLock = RLock() if not hasattr(self,"inUsePool"): self.inUsePool = [] self.inUsePoolLock = RLock() if not hasattr(self,"TBremovedAddr"): self.TBremovedAddr = {} self.TBremovedAddrLock = RLock() # some connxion are required to the Neighbor Cache # it seems better to always stay connected if not hasattr(self,"nc"): self.nc = NeighCache() if not hasattr(self,"clean"): self.clean = True def get_address_in_use(self): """returns the addresses that are currently in use""" with self.inUsePoolLock: return self.inUsePool def get_not_yet_used_addresses(self): """returns the list of addresses that are currently free to be used""" with self.freePoolLock: return self.freePool def prepare_next_valid_address(self): """pick on address from the pool and change it valid state""" with nested(self.freePoolLock, self.inUsePoolLock): try: address = self.freePool[0] address.modify() # by default, place the address in "valid" state del self.freePool[0] self.inUsePool.append(address) except IndexError: # or will crash when no address is left pass def is_ephemeral_CGA(self, address): """return True if the address is an Ephemeral CGA""" with nested(self.freePoolLock, self.inUsePoolLock): return str(address) in [ str(address) for address in self.freePool + self.inUsePool ] def reportaddress_as_used(self, address): """report the address as an address that is currently in use""" # connect to the NC to obtain the list of currently assigned addresses configured_addresses = self.nc.dump_addresses() # address = None # obtain the address object for address_obj in ( a for a in configured_addresses if str(a) == address): address = address_obj break # we can stop here as the address is only recorded one # change the state of the address to deprecated try: address.modify(preferred_lft=0) warn("Address %s reported to have initialized a connexion is now being deprecated\n" % address) except AttributeError: # the address does not belong to our program pass # if this is an ephemeral CGA from the "free" pool, place it # in the "in use" pool try: with nested(self.freePoolLock, self.inUsePoolLock): index = self.freePool.index(address) del self.freePool[index] self.inUsePool.append(address) except (ValueError, IndexError): pass def regenerate(self, n_address): """create n_address addresses to fill the pool there should be POOL_SIZE addresses in the pool""" warn("Ephemeral CGA pool: regeneration of %i addresses\n" % n_address) for i in range(n_address): a = Address(key=NDprotector.default_publickey, interface = EphemeralAddress.INTERFACE, prefix = EphemeralAddress.PREFIX, ephemeral = True, sec = 0, dad=False) # temporary a.modify(preferred_lft=0) with self.freePoolLock: self.freePool.append(a) # connect to the NC to store the new address self.nc.store_address(a) warn("Ephemeral CGA pool: regeneration of %i addresses complete\n" % n_address) def schedule_address_removal(self,address): """schedule an address for removal after TIMEOUT seconds""" EphemeralAddress.TIMEOUT with nested(self.inUsePoolLock, self.TBremovedAddrLock): for address in ( a for a in self.inUsePool if str(a) == address ): index = self.inUsePool.index(address) del self.inUsePool[index] self.TBremovedAddr[address] = EphemeralAddress.TIMEOUT break def close_cleaning_thread(self): """close the cleaning/maintenance thread""" self.clean = False
def callback(i, payload): """a callback function called on each ingoing packets""" data = payload.get_data() packet = IPv6(data) # if something goes wrong and makes this callback crash, # the packet is dropped payload.set_verdict(nfqueue.NF_DROP) # receiving interface interface = get_if_list()[payload.get_indev() - 1] # extract all the TA option from the CPS/CPA list_of_node_trustanchor = [] ta = packet[ICMPv6NDOptTrustAnchor] while ta: if ta.nametype == 1: # we get the name field of the TA option list_of_node_trustanchor.append(ta.name_field) ta = ta.payload[ICMPv6NDOptTrustAnchor] if NDprotector.is_router: # we have a CPS # (filtering rules were set accordingly) # CPS's message ID req_id = packet[ICMPv6SEND_CPS].id dest_node = packet[IPv6].src if dest_node == "::": # if the origin is the unspecified address # the answer is on the All-Node multicast address dest_node = "ff02::1" src_addr = "::" if packet[IPv6].dst != "ff02::1": src_addr = packet[IPv6].dst else: # lookup for an adress on this interface: nc = NeighCache() configured_addresses = nc.dump_addresses() for address in configured_addresses: if address.get_interface() == interface: src_addr = str(address) break # send multiple as many Certification Path # as there is Trust Anchor options for path in deepcopy(NDprotector.certification_path): trust_anchor = path[0] skip_ta = True if list_of_node_trustanchor == []: skip_ta = False else: # check if this trust anchor is trusted by the node # if it isn't, we check for the next cert in the path while path and skip_ta: trust_anchor = path[0] for ta in list_of_node_trustanchor: # we found the correct trust anchor if ta in str(Cert(trust_anchor)): skip_ta = False break else: try: del path[0] except IndexError: warn( "CertPath.py - callback - this is likely to be a bug\n" ) if skip_ta: # we do not have a Certification Path # down to this Trust Anchor continue # number of certificates to send # (we does not count the TA as we do not send it num_components = len(path) - 2 # send as many CPA as there is certificates in the Certification Path for cert in path[1:]: c = Cert(cert) warn("sending a CPA message\n") p = Ether(src=get_if_hwaddr(interface)) / \ IPv6(src=src_addr,dst=dest_node)/ \ ICMPv6SEND_CPA(id=req_id,comp=num_components,allcomp=len(path) -1)/ \ ICMPv6NDOptCertificate(cert=str(c)) sendp(p, iface=interface, verbose=NDprotector.verbose) num_components -= 1 else: # we have a CPA # connect to the Certificate Cache for future decisions certcache = CertCache() warn("Receiving a CPA message\n") req_id = packet[ICMPv6SEND_CPA].id # we only accept CPA if they are destined to all the nodes or # if they are destined to our node if (packet[IPv6].dst == "ff02::1" and req_id == 0) or certcache.id_match(req_id): lastCPA = (packet[ICMPv6SEND_CPA].comp == 0) # extract all the certificates and feed them to the cache certopt = packet[ICMPv6NDOptCertificate] while certopt: cert = certopt.cert cert = cert.output("PEM") certcache.storecert(req_id, cert) certopt = certopt.payload[ICMPv6NDOptCertificate] # when this is the last CPA message, we ask for the # certificate path validation process if lastCPA: certcache.checkcertpath(req_id)
def callback(i, payload): """a callback function called on each ingoing packets""" data = payload.get_data() packet = IPv6(data) # if something goes wrong and makes this callback crash, # the packet is dropped payload.set_verdict(nfqueue.NF_DROP) # receiving interface interface = get_if_list()[payload.get_indev() - 1] # extract all the TA option from the CPS/CPA list_of_node_trustanchor = [] ta = packet[ICMPv6NDOptTrustAnchor] while ta: if ta.nametype == 1: # we get the name field of the TA option list_of_node_trustanchor.append(ta.name_field) ta = ta.payload[ICMPv6NDOptTrustAnchor] if NDprotector.is_router: # we have a CPS # (filtering rules were set accordingly) # CPS's message ID req_id = packet[ICMPv6SEND_CPS].id dest_node = packet[IPv6].src if dest_node == "::": # if the origin is the unspecified address # the answer is on the All-Node multicast address dest_node = "ff02::1" src_addr = "::" if packet[IPv6].dst != "ff02::1": src_addr = packet[IPv6].dst else: # lookup for an adress on this interface: nc = NeighCache() configured_addresses = nc.dump_addresses() for address in configured_addresses: if address.get_interface() == interface: src_addr = str(address) break # send multiple as many Certification Path # as there is Trust Anchor options for path in deepcopy(NDprotector.certification_path): trust_anchor = path[0] skip_ta = True if list_of_node_trustanchor == []: skip_ta = False else: # check if this trust anchor is trusted by the node # if it isn't, we check for the next cert in the path while path and skip_ta: trust_anchor = path[0] for ta in list_of_node_trustanchor: # we found the correct trust anchor if ta in str(Cert(trust_anchor)): skip_ta = False break else: try: del path[0] except IndexError: warn("CertPath.py - callback - this is likely to be a bug\n") if skip_ta: # we do not have a Certification Path # down to this Trust Anchor continue # number of certificates to send # (we does not count the TA as we do not send it num_components = len(path) - 2 # send as many CPA as there is certificates in the Certification Path for cert in path[1:]: c = Cert(cert) warn("sending a CPA message\n") p = ( Ether(src=get_if_hwaddr(interface)) / IPv6(src=src_addr, dst=dest_node) / ICMPv6SEND_CPA(id=req_id, comp=num_components, allcomp=len(path) - 1) / ICMPv6NDOptCertificate(cert=str(c)) ) sendp(p, iface=interface, verbose=NDprotector.verbose) num_components -= 1 else: # we have a CPA # connect to the Certificate Cache for future decisions certcache = CertCache() warn("Receiving a CPA message\n") req_id = packet[ICMPv6SEND_CPA].id # we only accept CPA if they are destined to all the nodes or # if they are destined to our node if (packet[IPv6].dst == "ff02::1" and req_id == 0) or certcache.id_match(req_id): lastCPA = packet[ICMPv6SEND_CPA].comp == 0 # extract all the certificates and feed them to the cache certopt = packet[ICMPv6NDOptCertificate] while certopt: cert = certopt.cert cert = cert.output("PEM") certcache.storecert(req_id, cert) certopt = certopt.payload[ICMPv6NDOptCertificate] # when this is the last CPA message, we ask for the # certificate path validation process if lastCPA: certcache.checkcertpath(req_id)