#include #include #include #include #include #include #include "rr.h" #include "utils.h" #include "constants.h" #include "packet.h" /* ----- Creadores ----- */ DNSRR *dns2_rr_create_a (const char *name, int klass, int ttl, struct in_addr address) { DNSRR *rr; rr = (DNSRR *) malloc (sizeof (DNSRR)); if (rr == NULL) { return NULL; } strncpy (rr->name, name, sizeof (rr->name)); dns2_trim_name (rr->name); if (rr->name[0] == 0) { free (rr); return NULL; } rr->type = RR_TYPE_A; rr->klass = klass; rr->ttl = ttl; /* Copiar la IP */ memcpy (&rr->a.address, &address, sizeof (rr->a.address)); return rr; } DNSRR *dns2_rr_create_tsig (const char *name, const char *signature, const char *algorithm) { DNSRR *rr; if (strcmp (algorithm, RR_TSIG_HMAC_MD5) != 0 && strcmp (algorithm, RR_TSIG_HMAC_SHA1) != 0 && strcmp (algorithm, RR_TSIG_HMAC_SHA224) != 0 && strcmp (algorithm, RR_TSIG_HMAC_SHA256) != 0 && strcmp (algorithm, RR_TSIG_HMAC_SHA384) != 0 && strcmp (algorithm, RR_TSIG_HMAC_SHA512) != 0) { /* Algoritmo no soportado */ return NULL; } rr = (DNSRR *) malloc (sizeof (DNSRR)); if (rr == NULL) { return NULL; } strncpy (rr->name, name, sizeof (rr->name)); dns2_trim_name (rr->name); if (rr->name[0] == 0) { free (rr); return NULL; } rr->type = RR_TYPE_TSIG; rr->klass = RR_CLASS_ANY; rr->ttl = 0; strncpy (rr->tsig.algorithm, algorithm, sizeof (rr->tsig.algorithm)); rr->tsig.key = strdup (signature); if (rr->tsig.key == NULL) { free (rr->name); free (rr); return NULL; } // El tiempo tiene que ser UTC // El tiempo son 6 bytes. rr->tsig.time_signed = time (NULL); //exit (1); //rr->tsig.time_signed = 1602910230u; //printf ("Time: %i\n", ((unsigned int) rr->tsig.time_signed)); rr->tsig.fudge = 300; rr->tsig.mac_size = 0; rr->tsig.original_id = 0; rr->tsig.error = 0; rr->tsig.other_length = 0; return rr; } DNSRR *dns2_rr_create_aaaa (const char *name, int klass, int ttl, struct in6_addr address) { DNSRR *rr; rr = (DNSRR *) malloc (sizeof (DNSRR)); if (rr == NULL) { return NULL; } strncpy (rr->name, name, sizeof (rr->name)); dns2_trim_name (rr->name); if (rr->name[0] == 0) { free (rr); return NULL; } rr->type = RR_TYPE_AAAA; rr->klass = klass; rr->ttl = ttl; /* Copiar la IP */ memcpy (&rr->aaaa.address, &address, sizeof (rr->aaaa.address)); return rr; } DNSRR *dns2_rr_create_ns (const char *name, int klass, int ttl, const char *nsname) { DNSRR *rr; rr = (DNSRR *) malloc (sizeof (DNSRR)); if (rr == NULL) { return NULL; } strncpy (rr->name, name, sizeof (rr->name)); dns2_trim_name (rr->name); if (rr->name[0] == 0) { free (rr); return NULL; } rr->type = RR_TYPE_NS; rr->klass = klass; rr->ttl = ttl; /* Copiar el nombre */ memcpy (&rr->ns.name, &nsname, strlen (nsname) + 1); dns2_trim_name (rr->ns.name); return rr; } /* ----- Empaquetadores ----- */ void dns_rr_pack_a_data (DNSRR *rr, DNSPacket *packet) { char *buf; uint16_t t16; buf = &packet->rdata[packet->rdlength]; t16 = htons (4); memcpy (buf, &t16, 2); memcpy (&buf[2], &rr->a.address, 4); packet->rdlength += 4 + 2; } void dns_rr_pack_aaaa_data (DNSRR *rr, DNSPacket *packet) { char *buf; uint16_t t16; buf = &packet->rdata[packet->rdlength]; t16 = htons (16); memcpy (buf, &t16, 2); memcpy (&buf[2], &rr->aaaa.address, 16); packet->rdlength += 16 + 2; } int dns_rr_sign_hmac (void *data, int data_len, const char *key, const char *algorithm, void *mac) { HMAC_CTX *ctx; const EVP_MD *md_algo; char *decoded_key; size_t key_length; int total_len; int g; printf ("About to sign, data is:\n"); for (g = 0; g < data_len; g++) { printf ("%02x", (unsigned int) (((unsigned char *) data)[g])); } printf ("\nEnd data sig\n"); if (strcmp (algorithm, RR_TSIG_HMAC_MD5) == 0) { md_algo = EVP_md5 (); } else if (strcmp (algorithm, RR_TSIG_HMAC_SHA1) == 0) { md_algo = EVP_sha1 (); } else if (strcmp (algorithm, RR_TSIG_HMAC_SHA224) == 0) { md_algo = EVP_sha224 (); } else if (strcmp (algorithm, RR_TSIG_HMAC_SHA256) == 0) { md_algo = EVP_sha256 (); } else if (strcmp (algorithm, RR_TSIG_HMAC_SHA384) == 0) { md_algo = EVP_sha384 (); } else if (strcmp (algorithm, RR_TSIG_HMAC_SHA512) == 0) { md_algo = EVP_sha512 (); } else { return 0; } ctx = HMAC_CTX_new (); if (ctx == NULL) { return 0; } if (base64_decode (key, (unsigned char **)&decoded_key, &key_length) != 0) { HMAC_CTX_free (ctx); return 0; } HMAC_Init_ex (ctx, decoded_key, key_length, md_algo, NULL); /* Enviar el mensaje a firmar */ HMAC_Update (ctx, data, data_len); HMAC_Final (ctx, mac, &total_len); HMAC_CTX_free (ctx); free (decoded_key); return total_len; } void dns_rr_pack_tsig_data (DNSRR *rr, DNSPacket *packet) { uint16_t offset_rdlength, t16; uint32_t t32; /* Tomar la posición actual para el rdlength */ offset_rdlength = packet->rdlength; packet->rdlength += 2; /* Agregar el algoritmo sin compresión */ dns_packet_pack_name (packet, rr->tsig.algorithm); /* Empaquetar el tiempo, fudge y mac_size */ t16 = 0; memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; t32 = htonl ((intmax_t) rr->tsig.time_signed); memcpy (&packet->rdata[packet->rdlength], &t32, 4); packet->rdlength += 4; /* El fudge */ t16 = htons (rr->tsig.fudge); memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; t16 = htons (rr->tsig.mac_size); memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; /* Agregar el MAC */ memcpy (&packet->rdata[packet->rdlength], rr->tsig.mac, rr->tsig.mac_size); packet->rdlength += rr->tsig.mac_size; /* Copiar el packet id */ t16 = htons (packet->header.id); memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; /* Error y other length */ t16 = htons (rr->tsig.error); memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; t16 = htons (rr->tsig.other_length); memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; /* Agregar el other_data si other_length > 0 */ if (rr->tsig.other_length > 0) { memcpy (&packet->rdata[packet->rdlength], rr->tsig.other_data, rr->tsig.other_length); packet->rdlength += rr->tsig.other_length; } /* Regresar a la posición del rdlength y escribir el valor correcto */ t16 = htons (packet->rdlength - offset_rdlength - 2); memcpy (&packet->rdata[offset_rdlength], &t16, 2); } void dns_rr_pack_generate_signature (DNSRR *signature, DNSPacket *packet) { void *data; int data_count; uint16_t t16; uint32_t t32; /* Reempaquetar todo el paquete para generar los bytes de la firma */ dns_packet_reset (packet); dns_packet_pack_data (packet); /* Agregar el nombre del tsig sin compresión */ dns_packet_pack_name (packet, signature->name); t16 = htons (signature->klass); memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; t32 = htonl (signature->ttl); memcpy (&packet->rdata[packet->rdlength], &t32, 4); packet->rdlength += 4; /* Agregar el algoritmo sin compresión */ dns_packet_pack_name (packet, signature->tsig.algorithm); /* Agregar la hora */ t16 = 0; memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; t32 = htonl ((intmax_t) signature->tsig.time_signed); memcpy (&packet->rdata[packet->rdlength], &t32, 4); packet->rdlength += 4; /* El fudge */ t16 = htons (signature->tsig.fudge); memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; /* Error y other length */ t16 = htons (signature->tsig.error); memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; t16 = htons (signature->tsig.other_length); memcpy (&packet->rdata[packet->rdlength], &t16, 2); packet->rdlength += 2; /* Agregar el other_data si other_length > 0 */ if (signature->tsig.other_length > 0) { memcpy (&packet->rdata[packet->rdlength], signature->tsig.other_data, signature->tsig.other_length); packet->rdlength += signature->tsig.other_length; } /* Recuperar la data */ data_count = dns_packet_get_data (packet, (unsigned char **)&data); /* Realmente firmar la data */ signature->tsig.mac_size = dns_rr_sign_hmac (data, data_count, signature->tsig.key, signature->tsig.algorithm, signature->tsig.mac); /* Pedir reempaquetar la data, otra vez */ dns_packet_reset (packet); } void dns_rr_pack_data (DNSRR *rr, DNSPacket *packet) { uint16_t t16; uint32_t t32; char *buf; dns_packet_compress_name (packet, rr->name); buf = &packet->rdata[packet->rdlength]; /*if (rr->type == OPT) { } else { */ t16 = htons (rr->type); memcpy (&buf[0], &t16, 2); t16 = htons (rr->klass); memcpy (&buf[2], &t16, 2); t32 = htonl (rr->ttl); memcpy (&buf[4], &t32, 4); /* } */ packet->rdlength += 8; /* Data por tipo */ switch (rr->type) { case RR_TYPE_A: dns_rr_pack_a_data (rr, packet); break; case RR_TYPE_AAAA: dns_rr_pack_aaaa_data (rr, packet); break; case RR_TYPE_TSIG: dns_rr_pack_tsig_data (rr, packet); break; } } /* ----- Desempaquetadores ----- */ int dns_rr_unpack_a_data (DNSRR *rr, unsigned char *buf, int rd_length) { if (rd_length < 4) { return -1; } memcpy (&rr->a.address, buf, 4); return 0; } int dns_rr_unpack_aaaa_data (DNSRR *rr, unsigned char *buf, int rd_length) { if (rd_length < 16) { return -1; } memcpy (&rr->aaaa.address, buf, 16); return 0; } int dns_rr_unpack_ns_data (DNSRR *rr, unsigned char *buf, int rd_length, DNSPacket *packet) { /* No hacemos validaciones sobre la longitud porque los nombres son variables */ int save; save = buf - packet->rdata; if (dns_packet_expand_name (rr->ns.name, packet, &save) < 0) { return -1; } return 0; } int dns_rr_unpack_cname_data (DNSRR *rr, unsigned char *buf, int rd_length, DNSPacket *packet) { /* No hacemos validaciones sobre la longitud porque los nombres son variables */ int save; save = buf - packet->rdata; if (dns_packet_expand_name (rr->cname.name, packet, &save) < 0) { return -1; } return 0; } int dns_rr_unpack_soa_data (DNSRR *rr, unsigned char *buf, int rd_length, DNSPacket *packet) { int save, start; int tot; uint32_t u32; if (rd_length < 20) { /* Al menos 5 enteros de 32 bits cada uno */ return -1; } start = save = buf - packet->rdata; if (dns_packet_expand_name (rr->soa.mname, packet, &save) < 0) { return -1; } if (dns_packet_expand_name (rr->soa.rname, packet, &save) < 0) { return -1; } tot = save - start; if (rd_length - tot < 20) { return -1; } /* Leer el serial */ memcpy (&u32, &buf[tot], 4); rr->soa.serial = ntohl (u32); /* Leer el refresh */ memcpy (&u32, &buf[tot + 4], 4); rr->soa.refresh = ntohl (u32); /* Leer el retry */ memcpy (&u32, &buf[tot + 8], 4); rr->soa.retry = ntohl (u32); /* El expire */ memcpy (&u32, &buf[tot + 12], 4); rr->soa.expire = ntohl (u32); /* El minimum */ memcpy (&u32, &buf[tot + 16], 4); rr->soa.minimum = ntohl (u32); return 0; } int dns_rr_unpack_ptr_data (DNSRR *rr, unsigned char *buf, int rd_length, DNSPacket *packet) { int save; save = buf - packet->rdata; if (dns_packet_expand_name (rr->ptr.name, packet, &save) < 0) { return -1; } return 0; } int dns_rr_unpack_mx_data (DNSRR *rr, unsigned char *buf, int rd_length, DNSPacket *packet) { int save; uint16_t u16; if (rd_length < 2) { return -1; } memcpy (&u16, buf, 2); rr->mx.preference = ntohs (u16); save = &buf[2] - packet->rdata; if (dns_packet_expand_name (rr->mx.name, packet, &save) < 0) { return -1; } return 0; } int dns_rr_unpack_txt_data (DNSRR *rr, unsigned char *buf, int rd_length) { /* En la teoría, un TXT puede contener muchas cadenas, * En la práctica, no creo que ocurra. * Tomar solo la primer cadena */ uint8_t txt_len; txt_len = buf[0]; if (txt_len > rd_length - 1) { return -1; } memcpy (rr->txt.txt, &buf[1], txt_len); rr->txt.txt[txt_len] = 0; return 0; } int dns_rr_unpack_tsig_data (DNSRR *rr, unsigned char *buf, int rd_length) { uint32_t t32; uint16_t t16; uint8_t t8; int offset; offset = 0; t8 = dns_packet_expand_name_simple (rr->tsig.algorithm, buf, rd_length); if (t8 < 0) { return -1; } offset += t8; /* Revisar el time, fudge y mac_size */ if (offset + 10 > rd_length) { return -1; } /* ¿Ignorar los primeros dos bytes del tiempo? */ memcpy (&t16, &buf[offset], 2); memcpy (&t32, &buf[offset + 2], 4); rr->tsig.time_signed = ntohl (t32); memcpy (&t16, &buf[offset + 6], 2); rr->tsig.fudge = ntohs (t16); memcpy (&t16, &buf[offset + 8], 2); rr->tsig.mac_size = ntohs (t16); offset += 10; /* Copiar el MAC */ if (offset + rr->tsig.mac_size > rd_length) { return -1; } memcpy (&rr->tsig.mac, &buf[offset], rr->tsig.mac_size); offset += rr->tsig.mac_size; /* Revisar el packet id, error y other_len */ if (offset + 6 > rd_length) { return -1; } /* Copiar el packet id original */ memcpy (&t16, &buf[offset], 2); rr->tsig.original_id = ntohs (t16); memcpy (&t16, &buf[offset + 2], 2); rr->tsig.error = ntohs (t16); memcpy (&t16, &buf[offset + 4], 2); rr->tsig.other_length = ntohs (t16); offset += 6; if (rr->tsig.other_length > 0) { if (offset + rr->tsig.other_length < rd_length) { return -1; } memcpy (&rr->tsig.other_data, &buf[offset], rr->tsig.other_length); } return 0; } DNSRR * dns_rr_unpack_data (DNSPacket *packet) { DNSRR *rr; uint16_t t16; uint32_t t32; unsigned char *buf; int rd_length; int res; rr = (DNSRR *) malloc (sizeof (DNSRR)); if (rr == NULL) { return NULL; } memset (rr, 0, sizeof (DNSRR)); if (dns_packet_expand_name (rr->name, packet, &packet->rdread) < 0) { dns2_rr_free (rr); return NULL; } /* Asegurar al menos 10 bytes */ if (packet->rdlength < packet->rdread + 10) { dns2_rr_free (rr); return NULL; } buf = &packet->rdata[packet->rdread]; packet->rdread += 10; memcpy (&t16, &buf[0], sizeof (t16)); rr->type = ntohs (t16); memcpy (&t16, &buf[2], sizeof (t16)); rr->klass = ntohs (t16); memcpy (&t32, &buf[4], sizeof (t32)); rr->ttl = ntohl (t32); memcpy (&t16, &buf[8], sizeof (t16)); rd_length = ntohs (t16); if (packet->rdlength < packet->rdread + rd_length) { dns2_rr_free (rr); return NULL; } buf = &packet->rdata[packet->rdread]; packet->rdread += rd_length; res = 0; switch (rr->type) { case RR_TYPE_A: res = dns_rr_unpack_a_data (rr, buf, rd_length); break; case RR_TYPE_AAAA: res = dns_rr_unpack_aaaa_data (rr, buf, rd_length); break; case RR_TYPE_NS: res = dns_rr_unpack_ns_data (rr, buf, rd_length, packet); break; case RR_TYPE_CNAME: res = dns_rr_unpack_cname_data (rr, buf, rd_length, packet); break; case RR_TYPE_SOA: res = dns_rr_unpack_soa_data (rr, buf, rd_length, packet); break; case RR_TYPE_PTR: res = dns_rr_unpack_ptr_data (rr, buf, rd_length, packet); break; case RR_TYPE_MX: res = dns_rr_unpack_mx_data (rr, buf, rd_length, packet); break; case RR_TYPE_TXT: res = dns_rr_unpack_txt_data (rr, buf, rd_length); break; case RR_TYPE_TSIG: res = dns_rr_unpack_tsig_data (rr, buf, rd_length); break; } if (res < 0) { dns2_rr_free (rr); return NULL; } return rr; } DNSRR *dns2_rr_dup (const DNSRR *rr) { DNSRR *new; new = (DNSRR *) malloc (sizeof (DNSRR)); if (new == NULL) { return NULL; } memcpy (new, rr, sizeof (DNSRR)); if (rr->type == RR_TYPE_TSIG) { new->tsig.key = strdup (rr->tsig.key); } return new; } void dns2_rr_free (DNSRR *rr) { if (rr->type == RR_TYPE_TSIG) { /* La llave se duplicó, liberarla */ free (rr->tsig.key); } free (rr); }