示例#1
0
文件: utils.py 项目: metebalci/digsec
def decode_labels(p, offset):
    i = offset
    qnameparts = []
    while True:
        qnamepartlen = p[i]
        i = i + 1
        if qnamepartlen == 0:
            # termination
            break
        if qnamepartlen & 0b11000000 == 0:
            # no message compression
            # this is a normal qname part
            qnamepart = p[i:i + qnamepartlen]
            i = i + qnamepartlen
            label = qnamepart.decode('ascii')
            qnameparts.append(label)
        elif qnamepartlen & 0b11000000 == 0b11000000:
            # message compression
            # this is a pointer to another label
            # make an unsigned 14-bit number
            pointer = ((qnamepartlen & 0b00111111) << 8) | p[i]
            i = i + 1
            (pointedlabels, pointedoffset) = decode_labels(p, pointer)
            dprint('decoded labels: %s' % pointedlabels)
            # pointed part can be full or partial
            qnameparts.extend(pointedlabels)
            # pointed part can be only the last, so
            # we return from here
            return (qnameparts, i)
        else:
            assert False, "invalid qnamepartlen: 0x%x" % qnamepartlen
    return (qnameparts, i)
示例#2
0
def main():
    print('digsec v%s' % DIGSEC_VERSION)
    if has_flag('+debug'):
        enable_debug()
        dprint('Debug mode enabled.')
    if len(sys.argv) < 2:
        display_help()
    else:
        cmd = sys.argv[1]
        show_help = has_flag('+help')
        dprint('cmd: %s' % cmd)
        if cmd == 'query':
            if show_help:
                display_help_query()
            else:
                do_query(sys.argv[2:])
        elif cmd == 'download':
            if show_help:
                display_help_download()
            else:
                do_download(sys.argv[2:])
        elif cmd == 'validate':
            if show_help:
                display_help_validate()
            else:
                do_validate(sys.argv[2:])
        else:
            display_help()
示例#3
0
文件: utils.py 项目: metebalci/digsec
def parse_flags(argv, default_flags):
    flags = default_flags
    # adding debug and help here to not raise error later
    if 'debug' not in flags:
        flags['debug'] = False
    if 'help' not in flags:
        flags['help'] = False
    for flag in argv:
        dprint('parsing flag: %s' % flag)
        if flag[0] == '+':
            flag = flag[1:]
            # these are flags with =, so with a value
            if set_eq_flag(flags, flag, 'udp_payload_size', None, int):
                continue
            elif set_eq_flag(flags, flag, 'timeout', None, float):
                continue
            elif set_eq_flag(flags, flag, 'save-root-anchors',
                             'root-anchors.xml', str):
                continue
            elif set_eq_flag(flags, flag, 'save-ds-anchors', '_root.IN', str):
                continue
            elif set_eq_flag(flags, flag, 'save-answer-prefix', None, str):
                continue
            elif set_eq_flag(flags, flag, 'save-packets', None, str):
                continue
            elif set_eq_flag(flags, flag, 'save-answer-dir', None, str):
                continue
            else:
                val = True
                if flag[0:2] == 'no':
                    val = False
                    flag = flag[3:]
                found = False
                for f in [
                        'rd', 'cd', 'do', 'debug', 'help', 'save-answer',
                        'show-file-contents', 'show-protocol', 'show-friendly'
                ]:
                    if flag == f:
                        if f in flags:
                            flags[f] = val
                            found = True
                            break
                        else:
                            error('Flag %s not expected here.' % flag)
                if not found:
                    error('Flag %s unknown.' % flag)
    return flags
示例#4
0
def validate_dnskey(dnskey_filename, dnskey, ds):
    if not dnskey.zone_key:
        print_answer_file(dnskey_filename)
        error(('DNSKEY keytag %d, algorithm %s is ' +
               'not marked as Zone Key (bit 7)') %
              (dnskey.keytag, dnskey.algorithm))

    verification_algorithm = dnssec_digests[ds.digest_type]
    if verification_algorithm is None:
        error('digsec does not support digest: %s yet' % ds.digest_type)

    dprint('Hashed Data (DNSKEY NAME + DNSKEY RDATA): 0x%s' %
           binascii.hexlify(dnskey.digest_data).decode('ascii'))

    verified = verification_algorithm(dnskey.digest_data, ds.digest)

    return verified
示例#5
0
 def from_packet(packet, offset):
     (name, offset) = decode_name(packet, offset)
     dprint('name: "%s", offset: %d' % (name, offset))
     (typ,
      clas,
      ttl,
      rdlength) = unpack('! H H i H',
                         packet[offset:offset + 10])
     dprint('type: %d, clas: %d, ttl: %d' % (typ, clas, ttl))
     rdata = packet[offset + 10:offset + 10 + rdlength]
     return (DNSRR(name,
                   typ,
                   clas,
                   ttl,
                   rdlength,
                   rdata,
                   packet,
                   offset + 10),
             offset + 10 + rdlength)
示例#6
0
def decode_type_bitmaps(rdata, offset):
    rr_types = list()
    while offset < len(rdata):
        (window_block_number,
         bitmap_length) = unpack("! B B",
                                 rdata[offset:offset+2])
        dprint('window block #: %d, bitmap length: %d' %
               (window_block_number, bitmap_length))
        offset = offset + 2
        bitmap = rdata[offset:offset + bitmap_length]
        offset = offset + bitmap_length
        for bitmap_index in range(0, bitmap_length):
            bitmap_byte = bitmap[bitmap_index]
            dprint('bitmap_byte: %s' % format(bitmap_byte, '#010b'))
            for k in range(0, 8):
                # 7-k because it is in network order
                if (bitmap_byte & (1 << (7-k))) != 0:
                    rr_type = window_block_number * 256 + bitmap_index * 8 + k
                    rr_types.append(rr_type)
    return rr_types
示例#7
0
文件: utils.py 项目: metebalci/digsec
def set_eq_flag(flags, s, flag_name, default, conv):
    dprint('set_eq_flag, s: %s, flag_name: %s, default: %s' %
           (s, flag_name, default))
    if s.startswith(flag_name):
        st = s.split('=')
        if len(st) == 1:
            if flag_name in flags:
                if default is not None:
                    flags[flag_name] = default
                else:
                    error(('Flag %s requires an ' + 'explicit value after =') %
                          flag_name)
            else:
                error('Flag %s not expected here.' % flag_name)
        else:
            if flag_name in flags:
                dprint('st: %s' % st[1])
                flags[flag_name] = conv(st[1])
            else:
                error('Flag %s not expected here.' % flag_name)
        return True
    else:
        return False
示例#8
0
def do_validate(argv):
    if len(argv) < 3:
        display_help_validate()
    non_plus = list(filter(lambda x: x[0] != '+', argv))
    dprint(non_plus)
    if len(non_plus) != 3:
        error('Missing arguments, see usage')
    else:
        an_rrset_filename = non_plus[0]
        ensure_file_exists(an_rrset_filename)
        corresponding_rrsig_filename = non_plus[1]
        ensure_file_exists(corresponding_rrsig_filename)
        dnskey_or_ds_rrset_filename = non_plus[2]
        ensure_file_exists(dnskey_or_ds_rrset_filename)
    default_flags = {'show-file-contents': False}
    flags = parse_flags(argv[3:], default_flags)
    dprint(flags)
    an_rrset = read_answer_file(an_rrset_filename)
    corresponding_rrsig = read_answer_file(corresponding_rrsig_filename)
    dnskey_or_ds_rrset = read_answer_file(dnskey_or_ds_rrset_filename)
    if flags['show-file-contents']:
        print_answer_file(an_rrset_filename)
        print_answer_file(corresponding_rrsig_filename)
        print_answer_file(dnskey_or_ds_rrset_filename)
    if len(an_rrset) == 0:
        print_answer_file(an_rrset_filename)
        error('no RR in the %s file' % an_rrset_filename)
    if len(corresponding_rrsig) == 0:
        print_answer_file(corresponding_rrsig_filename)
        error('no RRSIG in the %s file' % corresponding_rrsig_filename)
    if len(dnskey_or_ds_rrset) == 0:
        print_answer_file(dnskey_or_ds_rrset_filename)
        error('no DNSKEY or DS in the %s file' % dnskey_or_ds_rrset_filename)
    rrset = list(map(lambda x: x.l2(), an_rrset))
    an_rr = rrset[0]
    now = datetime.now()
    any_of_dnskey_is_valid = False
    for one_corresponding_rrsig in corresponding_rrsig:
        rrsig = one_corresponding_rrsig.l2()
        for rr in rrset:
            if rr.name != an_rr.name:
                print_answer_file(an_rrset_filename)
                error('multiple names exist in %s' % an_rrset_filename)
            if rr.typ != an_rr.typ:
                print_answer_file(an_rrset_filename)
                error('multiple types exists in %s' % an_rrset_filename)
            if rr.clas != an_rr.clas:
                print_answer_file(an_rrset_filename)
                error('multiple classes exist in %s' % an_rrset_filename)
            if rr.name != rrsig.name:
                print_answer_file(an_rrset_filename)
                print_answer_file(corresponding_rrsig_filename)
                error('RR.name: %s is different than RRSIG.name: %s' %
                      (rr.name, rrsig.name))
            if rr.typ != rrsig.type_covered:
                print_answer_file(an_rrset_filename)
                print_answer_file(corresponding_rrsig_filename)
                error('RRSIG does not cover RR type %s but %s' %
                      (rr.typ, rrsig.typ))
            if rr.clas != rrsig.clas:
                print_answer_file(an_rrset_filename)
                print_answer_file(corresponding_rrsig_filename)
                error('RR.class: %s is different than RRSIG.class: %s' %
                      (rr.clas, rrsig.clas))
            # len control is for root
            if len(rr.name) > 0:
                if len(rr.name.split('.')) != rrsig.labels:
                    print_answer_file(an_rrset_filename)
                    print_answer_file(corresponding_rrsig_filename)
                    error('RR.name has different number of labels than ' +
                          'RRSIG.labels')

        if now < rrsig.signature_inception:
            error('RRSIG is not valid yet')

        if now > rrsig.signature_expiration:
            error('RRSIG is not valid anymore')

        use_ds = False
        if rrsig.type_covered == 'DNSKEY':
            ds_rrset = []
            for ds_rr in dnskey_or_ds_rrset:
                ds_rr = ds_rr.l2()
                if ds_rr.typ != 'DS':
                    error(('RRSIG covers DNSKEY but DS is not provided, ' +
                           'but %s' % ds_rr.typ))
                ds_rrset.append(ds_rr)
            use_ds = True
        else:
            dnskey_rrset = []
            for dnskey_rr in dnskey_or_ds_rrset:
                dnskey_rr = dnskey_rr.l2()
                if dnskey_rr.typ != 'DNSKEY':
                    error(('RRSIG covers %s but DNSKEY is not provided, ' +
                           'but %s') % (rrsig.type_covered, dnskey_rr.typ))
                dnskey_rrset.append(dnskey_rr)

        if use_ds:
            dnskey_rrset = rrset

            valid, dnskey_signed_rrsig = validate_rrsig(
                corresponding_rrsig_filename, an_rrset_filename, dnskey_rrset,
                rrsig, dnskey_rrset)

            if valid:
                print('OK RRSIG (%s, %s) with DNSKEY (%d, %s)' %
                      (rrsig.type_covered, rrsig.algorithm,
                       dnskey_signed_rrsig.keytag,
                       dnskey_signed_rrsig.algorithm))

                # checking the dnskey signed RRSIG is same as the one
                #  having the digest in DS
                dprint(ds_rrset)
                selected_ds_list = list(
                    filter(lambda x: x.keytag == dnskey_signed_rrsig.keytag,
                           ds_rrset))
                if len(selected_ds_list) == 0:
                    print('WARNING: no DS for DNSKEY with keytag: %d' %
                          dnskey_signed_rrsig.keytag)
                    continue
                if len(selected_ds_list) > 1:
                    error('multiple DS with keytag: %d' %
                          dnskey_signed_rrsig.keytag)

                ds = selected_ds_list[0]

                valid = validate_dnskey(dnskey_or_ds_rrset_filename,
                                        dnskey_signed_rrsig, ds)
                if valid:
                    any_of_dnskey_is_valid = True
                    print('OK DNSKEY (%d, %s) with DS (%s)' %
                          (ds.keytag, ds.algorithm, ds.digest_type))
                else:
                    print(('ERROR in DS (%s) DNSKEY (%d, %s) Validation ! ' +
                           'Digest Mismatch') %
                          (ds.digest_type, ds.keytag, ds.algorithm))
                    sys.exit(1)
            else:
                print('ERROR in RRSIG (%s, %s) Validation !' %
                      (rrsig.type_covered, rrsig.algorithm))
                sys.exit(1)

        else:
            valid, dnskey_signed_rrsig = validate_rrsig(
                corresponding_rrsig_filename, dnskey_or_ds_rrset_filename,
                rrset, rrsig, dnskey_rrset)

            if valid:
                print('OK RRSIG (%s, %s) with DNSKEY (%d, %s)' %
                      (rrsig.type_covered, rrsig.algorithm,
                       dnskey_signed_rrsig.keytag,
                       dnskey_signed_rrsig.algorithm))
            else:
                print(('ERROR in RRSIG (%s, %s) Validation ! ' +
                       'Signature Mismatch') %
                      (rrsig.type_covered, rrsig.algorithm))
                sys.exit(1)
    if use_ds and not any_of_dnskey_is_valid:
        print('ERROR in RRSIG (%s, %s) Validation !' %
              (rrsig.type_covered, rrsig.algorithm))
        print('None of DNSKEYs could be validated with DS !')
        sys.exit(1)
示例#9
0
def validate_rrsig(corresponding_rrsig_filename, dnskey_or_ds_rrset_filename,
                   rrset, rrsig, dnskey_rrset):

    verification_algorithm = dnssec_algorithms.get(rrsig.algorithm)

    if verification_algorithm is None:
        error('digsec does not support algorithm: %s yet' % rrsig.algorithm)

    dnskeys = list(filter(lambda x: x.keytag == rrsig.keytag, dnskey_rrset))
    if len(dnskeys) == 0:
        print_answer_file(corresponding_rrsig_filename)
        print_answer_file(dnskey_or_ds_rrset_filename)
        error('No DNSKEY with keytag %d in %s' %
              (rrsig.keytag, dnskey_or_ds_rrset_filename))

    dnskeys = list(filter(lambda x: x.algorithm == rrsig.algorithm, dnskeys))

    if len(dnskeys) == 0:
        print_answer_file(corresponding_rrsig_filename)
        print_answer_file(dnskey_or_ds_rrset_filename)
        error('No DNSKEY with keytag %d and algorithm %s' %
              (rrsig.keytag, rrsig.algorithm))

    dnskeys = list(filter(lambda x: x.name == rrsig.signers_name, dnskeys))

    if len(dnskeys) == 0:
        print_answer_file(corresponding_rrsig_filename)
        print_answer_file(dnskey_or_ds_rrset_filename)
        error('No DNSKEY with keytag %d, algorithm %s and name %s' %
              (rrsig.keytag, rrsig.algorithm, rrsig.signers_name))

    dnskeys = list(filter(lambda x: x.zone_key, dnskeys))

    if len(dnskeys) == 0:
        print_answer_file(corresponding_rrsig_filename)
        print_answer_file(dnskey_or_ds_rrset_filename)
        error('No ZSK DNSKEY with keytag %d, algorithm %s and name %s' %
              (rrsig.keytag, rrsig.algorithm, rrsig.signers_name))

    canonical_rrset = map(lambda rr: rr.canonical_l1(rrsig.original_ttl),
                          rrset)

    canonical_rrset = sorted(canonical_rrset,
                             key=lambda x: binascii.hexlify(x.rdata))

    signed_data = bytearray()
    signed_data.extend(rrsig.rrsig_rdata)

    for canonical_rr in canonical_rrset:
        signed_data.extend(canonical_rr.to_packet())

    dprint('Signed Data (RRSIG + canonical RRSET): 0x%s' %
           binascii.hexlify(signed_data).decode('ascii'))

    # if multiple dnskeys match the requirements, each should be tried
    for dnskey in dnskeys:
        dprint('Using DNSKEY name: %s, keytag: %d, algorithm: %s' %
               (dnskey.name, dnskey.keytag, dnskey.algorithm))

        verified = verification_algorithm(bytes(signed_data),
                                          bytes(rrsig.signature), dnskey)
        if verified:
            return True, dnskey

    return False, None
示例#10
0
def send_recv(req, addr, port, timeout):
    dprint("----- START NETWORK COMMUNICATION -----")
    sendaddress = (addr, port)
    dprint('Sending %d bytes to %s' % (len(req), sendaddress))
    dprint('0x%s' % binascii.hexlify(req).decode('ascii'))
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(('', 4242))  # 4242 just a random port number
    sock.sendto(req, sendaddress)
    try:
        sock.settimeout(timeout)
        (res, resaddress) = sock.recvfrom(4096)
        dprint('Received %d bytes from %s' % (len(res), resaddress))
        dprint('0x%s' % binascii.hexlify(res).decode('ascii'))
    except socket.timeout:
        print('Error: Timeout when waiting for the response.')
        res = None
    dprint("----- END NETWORK COMMUNICATION -----")
    return res