zeek/src/analyzer/protocol/dns/DNS.h
Michael R. Torres fe8390c646 Parse DNSSEC AD and CD bits
Parse authentic data (AD) and checking disabled (CD) bits according to
RFC 2535. Leaves the Z field as-is, in case users are already handling
this elsewhere and depend on the value being the integer for all 3 bits.

https://www.rfc-editor.org/rfc/rfc2535#section-6.1

Fixes #2672
2023-03-13 14:35:06 -07:00

490 lines
16 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include "zeek/analyzer/protocol/tcp/TCP.h"
#include "zeek/binpac_zeek.h"
namespace zeek::analyzer::dns
{
namespace detail
{
enum DNS_Opcode
{
DNS_OP_QUERY = 0, ///< standard query
DNS_OP_IQUERY = 1, ///< reverse query
// ### Is server status 2 or 3? RFC 1035 says it's 2
// DNS_OP_SERVER_STATUS = 3, ///< server status request
DNS_OP_SERVER_STATUS = 2, ///< server status request
// Netbios operations (query = 0).
NETBIOS_REGISTRATION = 5,
NETBIOS_RELEASE = 6,
NETBIOS_WACK = 7, // wait for ACK
NETBIOS_REFRESH = 8,
};
enum DNS_Code
{
DNS_CODE_OK = 0, ///< no error
DNS_CODE_FORMAT_ERR = 1, ///< format error
DNS_CODE_SERVER_FAIL = 2, ///< server failure
DNS_CODE_NAME_ERR = 3, ///< no such domain
DNS_CODE_NOT_IMPL = 4, ///< not implemented
DNS_CODE_REFUSED = 5, ///< refused
};
enum RR_Type
{
TYPE_A = 1, ///< host address
TYPE_NS = 2, ///< authoritative name server
TYPE_CNAME = 5, ///< canonical name
TYPE_SOA = 6, ///< start of authority
TYPE_WKS = 11, ///< well known service
TYPE_PTR = 12, ///< domain name pointer
TYPE_HINFO = 13, ///< host information
TYPE_MX = 15, ///< mail routing information
TYPE_TXT = 16, ///< text strings
TYPE_SIG = 24, ///< digital signature (RFC 2535)
TYPE_KEY = 25, ///< public key (RFC 2535)
TYPE_PX = 26, ///< pointer to X.400/RFC822 mapping info (RFC 1664)
TYPE_AAAA = 28, ///< IPv6 address (RFC 1886)
TYPE_LOC = 29, ///< Location information about hosts (RFC 1876)
TYPE_NBS = 32, ///< Netbios name (RFC 1002)
TYPE_SRV = 33, ///< service location (RFC 2052)
TYPE_NAPTR = 35, ///< naming authority pointer (RFC 2168)
TYPE_KX = 36, ///< Key Exchange (RFC 2230)
TYPE_CERT = 37, ///< Certificate (RFC 2538)
TYPE_A6 = 38, ///< IPv6 address with indirection (RFC 2874)
TYPE_DNAME = 39, ///< Non-terminal DNS name redirection (RFC 2672)
TYPE_EDNS = 41, ///< OPT pseudo-RR (RFC 2671)
TYPE_SSHFP = 44, ///< SSH Public Key Fingerprint (RFC 4255)
TYPE_TKEY = 249, ///< Transaction Key (RFC 2930)
TYPE_TSIG = 250, ///< Transaction Signature (RFC 2845)
TYPE_CAA = 257, ///< Certification Authority Authorization (RFC 6844)
// DNSSEC RR's
TYPE_RRSIG = 46, ///< RR Signature record type (RFC4043)
TYPE_NSEC = 47, ///< Next Secure record (RFC4043)
TYPE_DNSKEY = 48, ///< DNS Key record (RFC 4034)
TYPE_DS = 43, ///< Delegation signer (RFC 4034)
TYPE_NSEC3 = 50,
TYPE_NSEC3PARAM = 51, ///< Contains the NSEC3 parameters (RFC 5155)
TYPE_SVCB =
64, ///< SerViCe Binding (RFC draft:
///< https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-07#section-1.1)
TYPE_HTTPS = 65, ///< HTTPS record (HTTPS specific SVCB resource record)
// Obsoleted
TYPE_SPF = 99, ///< Alternative: storing SPF data in TXT records, using the same format (RFC
///< 4408). Support for it was discontinued in RFC 7208
// The following are only valid in queries.
TYPE_AXFR = 252,
TYPE_ALL = 255,
TYPE_WINS = 65281, ///< Microsoft's WINS RR
TYPE_WINSR = 65282, ///< Microsoft's WINS-R RR
// Private use RR TYPE range: 65280 - 65534
TYPE_BINDS = 65534, ///< Bind9's Private Type Rec for signaling state of signing process
};
#define DNS_CLASS_IN 1
#define DNS_CLASS_ANY 255
enum DNS_AnswerType
{
DNS_QUESTION,
DNS_ANSWER,
DNS_AUTHORITY,
DNS_ADDITIONAL,
};
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
// DNS EDNS0 Option Codes (OPT)
enum EDNS_OPT_Type
{
TYPE_LLQ = 1, ///< https://www.iana.org/go/draft-sekar-dns-llq-06
TYPE_UL = 2, ///< http://files.dns-sd.org/draft-sekar-dns-ul.txt
TYPE_NSID = 3, ///< RFC5001
TYPE_DAU = 5, ///< RFC6975
TYPE_DHU = 6, ///< RFC6975
TYPE_N3U = 7, ///< RFC6975
TYPE_ECS = 8, ///< RFC7871
TYPE_EXPIRE = 9, ///< RFC7314
TYPE_COOKIE = 10, ///< RFC7873
TYPE_TCP_KA = 11, ///< RFC7828
TYPE_PAD = 12, ///< RFC7830
TYPE_CHAIN = 13, ///< RFC7901
TYPE_KEY_TAG = 14, ///< RFC8145
TYPE_ERROR = 15, ///< https://www.iana.org/go/draft-ietf-dnsop-extended-error-16
TYPE_CLIENT_TAG = 16, ///< https://www.iana.org/go/draft-bellis-dnsop-edns-tags
TYPE_SERVER_TAG = 17, ///< https://www.iana.org/go/draft-bellis-dnsop-edns-tags
TYPE_DEVICE_ID =
26946 ///< https://docs.umbrella.com/developer/networkdevices-api/identifying-dns-traffic2
};
enum DNSSEC_Algo
{
reserved0 = 0,
RSA_MD5 = 1, ///< [RFC2537] NOT RECOMMENDED
Diffie_Hellman = 2, ///< [RFC2539]
DSA_SHA1 = 3, ///< [RFC2536] OPTIONAL
Elliptic_Curve = 4,
RSA_SHA1 = 5, ///< [RFC3110] MANDATORY
DSA_NSEC3_SHA1 = 6,
RSA_SHA1_NSEC3_SHA1 = 7,
RSA_SHA256 = 8,
RSA_SHA512 = 10,
GOST_R_34_10_2001 = 12,
ECDSA_curveP256withSHA256 = 13,
ECDSA_curveP384withSHA384 = 14,
Indirect = 252, ///<
PrivateDNS = 253, ///< OPTIONAL
PrivateOID = 254, ///< OPTIONAL
reserved255 = 255,
};
enum DNSSEC_Digest
{
reserved = 0,
SHA1 = 1, ///< [RFC3110] MANDATORY
SHA256 = 2,
GOST_R_34_11_94 = 3,
SHA384 = 4,
};
///< all keys are defined in RFC draft
///< https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-07#section-14.3.2
enum SVCPARAM_Key
{
mandatory = 0,
alpn = 1,
no_default_alpn = 2,
port = 3,
ipv4hint = 4,
ech = 5,
ipv6hint = 6,
};
struct DNS_RawMsgHdr
{
unsigned short id;
unsigned short flags;
unsigned short qdcount;
unsigned short ancount;
unsigned short nscount;
unsigned short arcount;
};
struct EDNS_ADDITIONAL
{ // size
unsigned short name; // -
unsigned short type; // 16 : ExtractShort(data, len)
unsigned short payload_size; // 16
unsigned short extended_rcode; // 8
unsigned short version; // 8
unsigned short z; // 16
unsigned short rdata_len; // 16
};
struct EDNS_ECS
{
StringValPtr ecs_family; ///< EDNS client subnet address family
uint16_t ecs_src_pfx_len; ///< EDNS client subnet source prefix length
uint16_t ecs_scp_pfx_len; ///< EDNS client subnet scope prefix length
IntrusivePtr<AddrVal> ecs_addr; ///< EDNS client subnet address
};
struct EDNS_TCP_KEEPALIVE
{
bool keepalive_timeout_omitted; ///< whether the keepalive timeout is omitted
uint16_t keepalive_timeout; ///< the timeout value (in 100ms) sent by the client/server
};
struct EDNS_COOKIE
{
zeek::String* client_cookie; ///< cookie value sent by the client (8 bytes)
zeek::String* server_cookie; ///< cookie value sent by the server (0 or 8-32 bytes)
};
struct TSIG_DATA
{
String* alg_name;
unsigned long time_s;
unsigned short time_ms;
String* sig;
unsigned short fudge;
unsigned short orig_id;
unsigned short rr_error;
};
struct RRSIG_DATA
{
unsigned short type_covered; // 16 : ExtractShort(data, len)
unsigned short algorithm; // 8
unsigned short labels; // 8
uint32_t orig_ttl; // 32
unsigned long sig_exp; // 32
unsigned long sig_incep; // 32
unsigned short key_tag; // 16
String* signer_name;
String* signature;
};
struct DNSKEY_DATA
{
unsigned short dflags; // 16 : ExtractShort(data, len)
unsigned short dalgorithm; // 8
unsigned short dprotocol; // 8
String* public_key; // Variable length Public Key
};
struct NSEC3_DATA
{
unsigned short nsec_flags;
unsigned short nsec_hash_algo;
unsigned short nsec_iter;
unsigned short nsec_salt_len;
String* nsec_salt;
unsigned short nsec_hlen;
String* nsec_hash;
VectorValPtr bitmaps;
};
struct NSEC3PARAM_DATA
{
unsigned short nsec_flags; // 8
unsigned short nsec_hash_algo; // 8
unsigned short nsec_iter; // 16 : ExtractShort(data, len)
unsigned short nsec_salt_len; // 8
String* nsec_salt; // Variable length salt
};
struct DS_DATA
{
unsigned short key_tag; // 16 : ExtractShort(data, len)
unsigned short algorithm; // 8
unsigned short digest_type; // 8
String* digest_val; // Variable length Digest of DNSKEY RR
};
struct BINDS_DATA
{
unsigned short algorithm; // 8
unsigned short key_id; // 16 : ExtractShort(data, len)
unsigned short removal_flag; // 8
String* complete_flag; // 8
};
struct LOC_DATA
{
unsigned short version; // 8
unsigned short size; // 8
unsigned short horiz_pre; // 8
unsigned short vert_pre; // 8
unsigned long latitude; // 32
unsigned long longitude; // 32
unsigned long altitude; // 32
};
struct SVCB_DATA
{
uint16_t svc_priority; // 2
StringValPtr target_name;
};
class DNS_MsgInfo
{
public:
DNS_MsgInfo(DNS_RawMsgHdr* hdr, int is_query);
RecordValPtr BuildHdrVal();
RecordValPtr BuildAnswerVal();
RecordValPtr BuildEDNS_Val();
RecordValPtr BuildEDNS_ECS_Val(struct EDNS_ECS*);
RecordValPtr BuildEDNS_TCP_KA_Val(struct EDNS_TCP_KEEPALIVE*);
RecordValPtr BuildEDNS_COOKIE_Val(struct EDNS_COOKIE*);
RecordValPtr BuildTSIG_Val(struct TSIG_DATA*);
RecordValPtr BuildRRSIG_Val(struct RRSIG_DATA*);
RecordValPtr BuildDNSKEY_Val(struct DNSKEY_DATA*);
RecordValPtr BuildNSEC3_Val(struct NSEC3_DATA*);
RecordValPtr BuildNSEC3PARAM_Val(struct NSEC3PARAM_DATA*);
RecordValPtr BuildDS_Val(struct DS_DATA*);
RecordValPtr BuildBINDS_Val(struct BINDS_DATA*);
RecordValPtr BuildLOC_Val(struct LOC_DATA*);
RecordValPtr BuildSVCB_Val(const struct SVCB_DATA&);
int id;
int opcode; ///< query type, see DNS_Opcode
int rcode; ///< return code, see DNS_Code
int QR; ///< query record flag
int AA; ///< authoritative answer flag
int TC; ///< truncated - size > 512 bytes for udp
int RD; ///< recursion desired
int RA; ///< recursion available
int Z; ///< 3 bit field (includes AD and CD)
int AD; ///< authentic data
int CD; ///< checking disabled
int qdcount; ///< number of questions
int ancount; ///< number of answers
int nscount; ///< number of authority RRs
int arcount; ///< number of additional RRs
int is_query; ///< whether it came from the session initiator
StringValPtr query_name;
RR_Type atype;
int aclass; ///< normally = 1, inet
uint32_t ttl;
DNS_AnswerType answer_type;
int skip_event; ///< if true, don't generate corresponding events
// int answer_count; ///< count of responders. if >1 and not
///< identical answer, there may be problems
// uint32* addr; ///< cache value to pass back results
///< for forward lookups
};
class DNS_Interpreter
{
public:
explicit DNS_Interpreter(analyzer::Analyzer* analyzer);
void ParseMessage(const u_char* data, int len, int is_query);
void Timeout() { }
protected:
void EndMessage(detail::DNS_MsgInfo* msg);
bool ParseQuestions(detail::DNS_MsgInfo* msg, const u_char*& data, int& len,
const u_char* start);
bool ParseAnswers(detail::DNS_MsgInfo* msg, int n, detail::DNS_AnswerType answer_type,
const u_char*& data, int& len, const u_char* start);
bool ParseQuestion(detail::DNS_MsgInfo* msg, const u_char*& data, int& len,
const u_char* start);
bool ParseAnswer(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, const u_char* start);
u_char* ExtractName(const u_char*& data, int& len, u_char* label, int label_len,
const u_char* msg_start, bool downcase = true);
bool ExtractLabel(const u_char*& data, int& len, u_char*& label, int& label_len,
const u_char* msg_start);
uint16_t ExtractShort(const u_char*& data, int& len);
uint32_t ExtractLong(const u_char*& data, int& len);
void ExtractOctets(const u_char*& data, int& len, String** p);
String* ExtractStream(const u_char*& data, int& len, int sig_len);
bool ParseRR_Name(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_SOA(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_MX(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_NBS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_SRV(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_EDNS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_EDNS_ECS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_A(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength);
bool ParseRR_AAAA(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength);
bool ParseRR_WKS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength);
bool ParseRR_HINFO(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength);
bool ParseRR_TXT(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_SPF(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_CAA(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_TSIG(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_RRSIG(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_DNSKEY(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_NSEC(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_NSEC3(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_NSEC3PARAM(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_DS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_BINDS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_SSHFP(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_LOC(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
bool ParseRR_SVCB(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start, const RR_Type& svcb_type);
void SendReplyOrRejectEvent(detail::DNS_MsgInfo* msg, EventHandlerPtr event,
const u_char*& data, int& len, String* question_name,
String* original_name);
analyzer::Analyzer* analyzer;
bool first_message;
};
enum TCP_DNS_state
{
DNS_LEN_HI, ///< looking for the high-order byte of the length
DNS_LEN_LO, ///< looking for the low-order byte of the length
DNS_MESSAGE_BUFFER, ///< building up the message in the buffer
};
} // namespace detail
// Support analyzer which chunks the TCP stream into "packets".
// ### This should be merged with TCP_Contents_RPC.
class Contents_DNS final : public analyzer::tcp::TCP_SupportAnalyzer
{
public:
Contents_DNS(Connection* c, bool orig, detail::DNS_Interpreter* interp);
~Contents_DNS() override;
void Flush(); ///< process any partially-received data
detail::TCP_DNS_state State() const { return state; }
protected:
void DeliverStream(int len, const u_char* data, bool orig) override;
void ProcessChunk(int& len, const u_char*& data, bool orig);
detail::DNS_Interpreter* interp;
u_char* msg_buf;
int buf_n; ///< number of bytes in msg_buf
int buf_len; ///< size of msg_buf
int msg_size; ///< expected size of message
detail::TCP_DNS_state state;
};
// Works for both TCP and UDP.
class DNS_Analyzer final : public analyzer::tcp::TCP_ApplicationAnalyzer
{
public:
explicit DNS_Analyzer(Connection* conn);
~DNS_Analyzer() override;
void DeliverPacket(int len, const u_char* data, bool orig, uint64_t seq, const IP_Hdr* ip,
int caplen) override;
void Init() override;
void Done() override;
void ConnectionClosed(analyzer::tcp::TCP_Endpoint* endpoint, analyzer::tcp::TCP_Endpoint* peer,
bool gen_event) override;
void ExpireTimer(double t);
static analyzer::Analyzer* Instantiate(Connection* conn) { return new DNS_Analyzer(conn); }
protected:
detail::DNS_Interpreter* interp;
Contents_DNS* contents_dns_orig;
Contents_DNS* contents_dns_resp;
};
} // namespace zeek::analyzer::dns