Esempio n. 1
0
def pool_maintenance():
    """cleansen the pool:
    - remove addresses scheduled to be removed (in the in-use address)
    - regenerate the cache"""

    pool = EphemeralPool()

    with pool.TBremovedAddrLock:
        for (address, ttl) in pool.TBremovedAddr.items():
            if ttl <= 0:
                # suppress the address from the system
                warn("scheduled removal for address %s is now removed\n" % address)
                address.remove()
                pool.nc.del_address(str(address))

                del pool.TBremovedAddr[address]
            else:
                pool.TBremovedAddr[address] = ttl -1

    # regenerate the pool of addresses
    remaining_addresses = 0
    with pool.freePoolLock:
        remaining_addresses = len(pool.freePool)
    if remaining_addresses <= EphemeralAddress.POOL_SIZE/2 :
        pool.regenerate(EphemeralAddress.POOL_SIZE - remaining_addresses)
def pool_maintenance():
    """cleansen the pool:
    - remove addresses scheduled to be removed (in the in-use address)
    - regenerate the cache"""

    pool = EphemeralPool()

    with pool.TBremovedAddrLock:
        for (address, ttl) in pool.TBremovedAddr.items():
            if ttl <= 0:
                # suppress the address from the system
                warn("scheduled removal for address %s is now removed\n" %
                     address)
                address.remove()
                pool.nc.del_address(str(address))

                del pool.TBremovedAddr[address]
            else:
                pool.TBremovedAddr[address] = ttl - 1

    # regenerate the pool of addresses
    remaining_addresses = 0
    with pool.freePoolLock:
        remaining_addresses = len(pool.freePool)
    if remaining_addresses <= EphemeralAddress.POOL_SIZE / 2:
        pool.regenerate(EphemeralAddress.POOL_SIZE - remaining_addresses)
Esempio n. 3
0
def run_queues():
    """run the different queues and dispatch
    packets for the different callback functions"""

    in_q = in_queue()
    out_q = out_queue()
    cpscpa_q = cpscpa_queue()

    plugins = get_plugins_by_capability("NFQueue")

    plugin_queues = [ plugin.listening_queue() \
            for plugin in plugins ]

    # associate a file descritor to its corresponding queue
    queues = {}
    for q in [in_q, out_q, cpscpa_q] + plugin_queues:
        q.set_mode(nfqueue.NFQNL_COPY_PACKET)
        queues[q.get_fd()] = q

    warn("running In/Out/CPSCPA NFQueues\n")
    # TC 04/15/10: took me a while to figure out I could use nfqueues this way
    try:
        while True:
            # warn("waiting for new (intercepted) messages\n")
            (r, w, o) = select(queues.keys(), [], [])

            for filedesc in r:
                queues[filedesc].process_pending(1)

    except KeyboardInterrupt, e:
        print "stopping all the queues"
    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
Esempio n. 5
0
    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
Esempio n. 6
0
def run_queues():
    """run the different queues and dispatch
    packets for the different callback functions"""

    in_q = in_queue() 
    out_q = out_queue() 
    cpscpa_q = cpscpa_queue()

    plugins = get_plugins_by_capability("NFQueue")

    plugin_queues = [ plugin.listening_queue() \
            for plugin in plugins ]

    # associate a file descritor to its corresponding queue
    queues = {}
    for q in [in_q, out_q, cpscpa_q] + plugin_queues:
        q.set_mode(nfqueue.NFQNL_COPY_PACKET)
        queues[q.get_fd()] = q

    warn("running In/Out/CPSCPA NFQueues\n")
    # TC 04/15/10: took me a while to figure out I could use nfqueues this way
    try:
        while True:
            # warn("waiting for new (intercepted) messages\n")
            (r,w,o) = select(queues.keys(),[],[])

            for filedesc in r:
                queues[filedesc].process_pending(1)

    except KeyboardInterrupt, e:
        print "stopping all the queues"
Esempio n. 7
0
    def storecert(self,id,cert):
        """temporarly store certs, they are sorted by their ID"""
        warn("storing on cert for Certificate Path #%d\n" % id)

        with self.TBApprLock:
            certpath = []
            ttl = TIMEOUT
            try:
                (certpath, oldttl) = self.TBApprovedCert[id]

            except KeyError:
                pass
            certpath.append(Cert(cert))
            self.TBApprovedCert[id] = (certpath, ttl)
Esempio n. 8
0
def readconfig(config_file):
    """read the config file, check the different option, and return them to the main program"""

    NDprotector.ECCsupport = False
    try:
        from scapy6send.ecc import ECCkey, NID_secp256k1,\
                NID_secp384r1, NID_secp521r1
    except ImportError:
        warn("unable to import ECC library\n")
        warn("ECC support is disabled\n")
    else:
        warn("ECC support is available\n")
        NDprotector.ECCsupport = True

    # load the config file
    execfile(config_file)

    # TODO
    # - sanity checks

    # load the various plugins
    init_plugin_path(NDprotector.pluginpath)

    load_plugins(NDprotector.plugins)

    warn("available plugins: " + repr(find_plugins()) + "\n")
Esempio n. 9
0
def readconfig(config_file):
    """read the config file, check the different option, and return them to the main program"""

    NDprotector.ECCsupport = False
    try:
        from scapy6send.ecc import ECCkey, NID_secp256k1,\
                NID_secp384r1, NID_secp521r1
    except ImportError:
        warn("unable to import ECC library\n")
        warn("ECC support is disabled\n")
    else:
        warn("ECC support is available\n")
        NDprotector.ECCsupport = True


    # load the config file
    execfile(config_file)

    # TODO
    # - sanity checks

    # load the various plugins
    init_plugin_path(NDprotector.pluginpath)

    load_plugins(NDprotector.plugins)

    warn("available plugins: " + repr(find_plugins()) + "\n")
Esempio n. 10
0
    def storecert(self, id, cert):
        """temporarly store certs, they are sorted by their ID"""
        warn("storing on cert for Certificate Path #%d\n" % id)

        with self.TBApprLock:
            certpath = []
            ttl = TIMEOUT
            try:
                (certpath, oldttl) = self.TBApprovedCert[id]

            except KeyError:
                pass
            certpath.append(Cert(cert))
            self.TBApprovedCert[id] = (certpath, ttl)
Esempio n. 11
0
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)
Esempio n. 12
0
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)
Esempio n. 13
0
    def trustable_hash(self, hash_cert, prefixes, sigtypeID):
        """check if a hash contained in a RSA signature option corresponds to a trustable certificate
        Also check if the certificate's IP Addr extension matches to the advertised prefixes"""

        hashfunc = getattr(hashlib, SigTypeHashfunc[sigtypeID])
        try:
            with self.ApprLock:
                # case #1: we have accepted a certificate for these prefixes
                for cert in self.ApprovedCert:
                    if NDprotector.x509_ipextension:
                        (addr, preflen) = cert.IPAddrExt
                        addr = addr_to_int(addr)

                    if hash_cert == hashfunc(cert.key.derkey).digest()[:16]:
                        if NDprotector.x509_ipextension:
                            for prefix in\
                                    (((addr_to_int(p) & prefix_mask(preflen)) for p in prefixes)):
                                if prefix != (addr & prefix_mask(preflen)):
                                    return False
                            else:
                                return True
                        elif not NDprotector.x509_ipextension:
                            return True

            # case #2: the certificate linked to the messages
            # is directly a trust anchor
            for certfile in NDprotector.trustanchors:
                cert = Cert(certfile)
                if NDprotector.x509_ipextension:
                    (addr, preflen) = cert.IPAddrExt
                    addr = addr_to_int(addr)
                if hash_cert == sha.sha(cert.key.derkey).digest()[:16]:
                    if NDprotector.x509_ipextension:
                        for prefix in ((addr_to_int(p) & prefix_mask(preflen)
                                        for p in prefixes)):
                            if prefix != (addr & prefix_mask(preflen)):
                                return False
                            else:
                                return True
                    elif not NDprotector.x509_ipextension:
                        return True
        # likely due to a missing IP Addr extension
        except TypeError:
            warn("The verified certificate most likely "
                 "does not have any IP addresses extension field\n")
        return False
Esempio n. 14
0
def NeighborCacheStart():
    # start the cleaning Thread for the NC
    NDprotector.Cleanup.cleanup_thread_subscribe(cleaning_nc)


    # register the currently assigned addresses
    nc = NeighCache()
    for addr in NDprotector.configured_addresses:
        nc.store_address(addr)

    #flushing all the old data from the kernel NC
    warn("flushing kernel neighbor cache\n")
    p = Popen( [ '/sbin/ip', '-6', 'neigh', 'flush', 'all' ], \
            stdout=PIPE, stderr=PIPE)
    if p.stdout.read() + p.stderr.read() != "":
        # raise an error
        raise NeighCacheException("unable to flush the kernel neighbor cache")
Esempio n. 15
0
    def trustable_hash(self,hash_cert, prefixes, sigtypeID):
        """check if a hash contained in a RSA signature option corresponds to a trustable certificate
        Also check if the certificate's IP Addr extension matches to the advertised prefixes"""

        hashfunc = getattr(hashlib, SigTypeHashfunc[sigtypeID])
        try:
            with self.ApprLock:
                # case #1: we have accepted a certificate for these prefixes
                for cert in self.ApprovedCert:
                    if NDprotector.x509_ipextension:
                        (addr,preflen) = cert.IPAddrExt
                        addr = addr_to_int(addr)

                    if hash_cert == hashfunc(cert.key.derkey).digest()[:16]:
                        if NDprotector.x509_ipextension: 
                            for prefix in\
                                    (((addr_to_int(p) & prefix_mask(preflen)) for p in prefixes)):
                                        if prefix != (addr & prefix_mask(preflen)):
                                            return False
                            else:
                                return True
                        elif not NDprotector.x509_ipextension:
                            return True

            # case #2: the certificate linked to the messages
            # is directly a trust anchor
            for certfile in NDprotector.trustanchors:
                cert = Cert(certfile)
                if NDprotector.x509_ipextension:
                    (addr,preflen) = cert.IPAddrExt
                    addr = addr_to_int(addr)
                if hash_cert == sha.sha(cert.key.derkey).digest()[:16]:
                    if NDprotector.x509_ipextension:
                        for prefix in ((addr_to_int(p) & prefix_mask(preflen) for p in prefixes)):
                            if prefix != (addr & prefix_mask(preflen)):
                                return False
                            else:
                                return True
                    elif not NDprotector.x509_ipextension:
                        return True
        # likely due to a missing IP Addr extension
        except TypeError:
            warn("The verified certificate most likely "
                 "does not have any IP addresses extension field\n")
        return False
    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)
Esempio n. 17
0
    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)
Esempio n. 18
0
def callback(i, payload):
    """a callback function for the NFQUEUE"""
    
    data = payload.get_data()
    data = IPv6(data)
    
    CGApool = EphemeralPool()


    # we might have intercepted packet for addresses
    # that are NOT Ephemeral CGA


    # if this is a SYN packet:
    # - put back the address in deprecated mode
    # - preparre the next address for the preferred state
    
    # a TCP SYN only
    if data[TCP].flags == 2:
        warn("this is a TCP SYN (address %s goes to deprecated state)\n" % data.src)
        CGApool.reportaddress_as_used(data.src)
        if CGApool.is_ephemeral_CGA(data.src):
            CGApool.prepare_next_valid_address()

    
    

    # first idea: if this is a FIN + ACK (17) packet, remove the address for the interface
    # second idea: if there is one FIN on each side, wait a bit and remove the address 
    # (we implemen the second idea)
    elif data[TCP].flags & 1:

        warn("this is a TCP FIN (addresses %s-%s)\n" % (data.src, data.dst))

        if EphemeralAddress.connexion_closed(data.src, data.dst):
            if CGApool.is_ephemeral_CGA(data.src):
                warn("address %s scheduled to be removed from the interface\n"\
                        % data.src)
                CGApool.schedule_address_removal( data.src )

            elif CGApool.is_ephemeral_CGA(data.dst):
                warn("address %s scheduled to be removed from the interface\n"\
                        % data.src)
                CGApool.schedule_address_removal( data.dst )
    
    payload.set_verdict(nfqueue.NF_ACCEPT)
    
    return 1
def callback(i, payload):
    """a callback function for the NFQUEUE"""

    data = payload.get_data()
    data = IPv6(data)

    CGApool = EphemeralPool()

    # we might have intercepted packet for addresses
    # that are NOT Ephemeral CGA

    # if this is a SYN packet:
    # - put back the address in deprecated mode
    # - preparre the next address for the preferred state

    # a TCP SYN only
    if data[TCP].flags == 2:
        warn("this is a TCP SYN (address %s goes to deprecated state)\n" %
             data.src)
        CGApool.reportaddress_as_used(data.src)
        if CGApool.is_ephemeral_CGA(data.src):
            CGApool.prepare_next_valid_address()

    # first idea: if this is a FIN + ACK (17) packet, remove the address for the interface
    # second idea: if there is one FIN on each side, wait a bit and remove the address
    # (we implemen the second idea)
    elif data[TCP].flags & 1:

        warn("this is a TCP FIN (addresses %s-%s)\n" % (data.src, data.dst))

        if EphemeralAddress.connexion_closed(data.src, data.dst):
            if CGApool.is_ephemeral_CGA(data.src):
                warn("address %s scheduled to be removed from the interface\n"\
                        % data.src)
                CGApool.schedule_address_removal(data.src)

            elif CGApool.is_ephemeral_CGA(data.dst):
                warn("address %s scheduled to be removed from the interface\n"\
                        % data.src)
                CGApool.schedule_address_removal(data.dst)

    payload.set_verdict(nfqueue.NF_ACCEPT)

    return 1
Esempio n. 20
0
    def update(self, address, mac_address, pubkey, timestamp, secured = False, ssao = ([], []) ):
        """update an entry if the new record is more secure or up-to-date than the old one"""

        TSnew = timestamp
        # local reception time of the packet
        RDnew = ICMPv6NDOptTimestamp(str(ICMPv6NDOptTimestamp())).timestamp


        # clocks must be loosely synchronized
        if TSnew and fabs( RDnew - TSnew) >= NDprotector.ts_delta:
            warn("current message's Timestamp is out of sync\n")
            return False


        # an unsecured entry can not be recorded when using strict mode
        if not secured and NDprotector.mixed_mode == False:
            return False

        with self.lock:
            try:
                old_params = self.nd[address] 
                (_, _, TSlast, oldsecured, oldssao, RDlast, _) = old_params

                if oldsecured and not secured:
                    # an unsecured entry will not overwrite a secure entry
                    warn("Trying to overwrite a secure NC entry with an unsecure entry\n")
                    return False


                if TSnew and TSnew + NDprotector.ts_fuzz <= \
                   TSlast + (RDnew - RDlast) * (1 - NDprotector.ts_drift) - NDprotector.ts_fuzz:
                    warn("current message has been replayed\n")
                    return False

            except KeyError:
                pass


            self.nd[address] = tuple([ mac_address, pubkey, TSnew, secured, ssao, RDnew, TIMEOUT] )

        return True
Esempio n. 21
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 = []

    # 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
Esempio n. 22
0
    def checkcertpath(self, id):
        """check if a complete cert path is valid,
        if it is, the last cert is moved to the ApprovedCert list
        if it isn't, it is discarded"""

        warn("Verifying certification path for #%d\n" % id)

        with self.TBApprLock:

            try:

                valid_path = False

                certs, _ = self.TBApprovedCert[id]

                # removes everything if the last certificate in the chain
                # is already trusted
                already_trusted = False

                with self.ApprLock:
                    for accepted_cert in [
                            c.output("DER") for c in self.ApprovedCert
                    ]:
                        if accepted_cert == certs[-1].output("DER"):
                            warn(
                                "The Certificate Path we received is already trusted\n"
                            )
                            already_trusted = True

                if not already_trusted:
                    # we concat all the cert we got in a new file
                    cert_desc, certfilename = tempfile.mkstemp()

                    valid_IPExt = True

                    if NDprotector.x509_ipextension:
                        # we check that each certificate includes the previous one
                        # each certificate are expected to carry an IP extension

                        # address with only 1s
                        (prev_addr, prev_preflen) = certs[0].IPAddrExt
                        prev_addr = addr_to_int(prev_addr)

                        try:
                            for cert in certs:
                                (addr, preflen) = cert.IPAddrExt

                                addr = addr_to_int(addr)

                                if (addr & prefix_mask(prev_preflen)) == \
                                        (prev_addr & prefix_mask(prev_preflen)) and \
                                            prev_preflen <= preflen :
                                    prev_addr = addr
                                    prev_preflen = preflen

                                # this prefix is not contained inside its parent's certificate
                                else:
                                    warn("Certificate's IP extension does not"
                                         " match its parent's Certificate\n")
                                    valid_IPExt = False
                                    break

                        # if we get in there, it probably means that one certificate is lacking
                        # of IP address extension
                        except TypeError:
                            warn(
                                "At least one certificate in the chain seems "
                                "to lack of the X.509 Extensions for IP Address\n"
                            )
                            valid_IPExt = False

                    if valid_IPExt:
                        for cert in certs:
                            os.write(cert_desc, cert.output(fmt="PEM"))
                        os.close(cert_desc)

                        for ta in NDprotector.trustanchors:
                            tacert = Cert(ta)

                            # we copy the TA in a temporary file
                            ca_desc, cafilename = tempfile.mkstemp()
                            os.write(ca_desc, tacert.output(fmt="PEM"))
                            os.close(ca_desc)

                            # XXX double check this command
                            # we ask openssl to check the certificate for us
                            cmd = "openssl verify -CAfile %s %s" % (
                                cafilename, certfilename)
                            res = Popen(cmd, stdout=PIPE, shell=True)

                            output = res.stdout.read()

                            # we clean all the temporary files
                            os.unlink(cafilename)

                            if "OK" in output:
                                valid_path = True
                                break

                        os.unlink(certfilename)

                        if valid_path:
                            warn(
                                "We received a complete and valid Certification Path\n"
                            )
                            with self.ApprLock:

                                # only the last certificate from the chain is valuable
                                self.ApprovedCert.append(certs[-1])

                # either way, we remove the cert path that has been processed
                del self.TBApprovedCert[id]
            except KeyError:
                pass
        with self.IdLock:
            try:
                del self.Id[id]
            except KeyError:
                pass
Esempio n. 23
0
 def storeid(self,id):
     """store the Identifier when sending a CPS"""
     warn("storing ID %d for a new CPS\n" % id)
     with self.IdLock:
         self.Id[id] = TIMEOUT
Esempio n. 24
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
Esempio n. 25
0
 def id_match(self,id):
     """verifies that a Identifier carried in a CPA 
     matches a sent CPS"""
     warn("checking ID %d against a previously sent CPS\n" %  id)
     with self.IdLock:
         return id in self.Id
Esempio n. 26
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
Esempio n. 27
0
    def checkcertpath(self,id):
        """check if a complete cert path is valid,
        if it is, the last cert is moved to the ApprovedCert list
        if it isn't, it is discarded"""
        
        warn("Verifying certification path for #%d\n" % id)

        with self.TBApprLock:

            try:


                valid_path = False

                certs, _ = self.TBApprovedCert[id]

                # removes everything if the last certificate in the chain
                # is already trusted
                already_trusted = False

                with self.ApprLock:
                    for accepted_cert in [ c.output("DER") for c in self.ApprovedCert ] :
                        if accepted_cert == certs[-1].output("DER"):
                            warn("The Certificate Path we received is already trusted\n")
                            already_trusted = True

                if not already_trusted:
                    # we concat all the cert we got in a new file
                    cert_desc, certfilename = tempfile.mkstemp()

                    valid_IPExt = True

                    if NDprotector.x509_ipextension:
                        # we check that each certificate includes the previous one
                        # each certificate are expected to carry an IP extension

                        # address with only 1s
                        (prev_addr,prev_preflen) = certs[0].IPAddrExt
                        prev_addr = addr_to_int(prev_addr)

                        try:
                            for cert in certs:
                                (addr,preflen) = cert.IPAddrExt

                                addr = addr_to_int(addr)

                                if (addr & prefix_mask(prev_preflen)) == \
                                        (prev_addr & prefix_mask(prev_preflen)) and \
                                            prev_preflen <= preflen :
                                    prev_addr = addr
                                    prev_preflen = preflen

                                # this prefix is not contained inside its parent's certificate
                                else:
                                    warn("Certificate's IP extension does not"
                                         " match its parent's Certificate\n")
                                    valid_IPExt = False
                                    break



                        # if we get in there, it probably means that one certificate is lacking
                        # of IP address extension
                        except TypeError:
                            warn("At least one certificate in the chain seems "
                                 "to lack of the X.509 Extensions for IP Address\n")
                            valid_IPExt = False


                    if valid_IPExt:
                        for cert in certs:
                            os.write(cert_desc,cert.output(fmt="PEM"))
                        os.close(cert_desc)

                        for ta in NDprotector.trustanchors:
                            tacert = Cert(ta)

                            # we copy the TA in a temporary file
                            ca_desc, cafilename = tempfile.mkstemp()
                            os.write(ca_desc, tacert.output(fmt="PEM"))
                            os.close(ca_desc)



                            # XXX double check this command
                            # we ask openssl to check the certificate for us
                            cmd = "openssl verify -CAfile %s %s" % (cafilename,certfilename)
                            res = Popen(cmd, stdout=PIPE, shell=True)

                            output = res.stdout.read()

                            # we clean all the temporary files
                            os.unlink(cafilename)

                            if "OK" in output:
                                valid_path = True
                                break

                        os.unlink(certfilename)


                        if valid_path:
                            warn("We received a complete and valid Certification Path\n")
                            with self.ApprLock:


                                # only the last certificate from the chain is valuable
                                self.ApprovedCert.append(certs[-1])

                # either way, we remove the cert path that has been processed
                del self.TBApprovedCert[id]
            except KeyError:
                pass
        with self.IdLock:
            try:
                del self.Id[id]
            except KeyError:
                pass
Esempio n. 28
0
 def id_match(self, id):
     """verifies that a Identifier carried in a CPA 
     matches a sent CPS"""
     warn("checking ID %d against a previously sent CPS\n" % id)
     with self.IdLock:
         return id in self.Id
Esempio n. 29
0
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)
Esempio n. 30
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:
            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
Esempio n. 31
0
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)
Esempio n. 32
0
 def storeid(self, id):
     """store the Identifier when sending a CPS"""
     warn("storing ID %d for a new CPS\n" % id)
     with self.IdLock:
         self.Id[id] = TIMEOUT