Merge pull request #2589 from keszybz/resolve-tool-2

Better support of OPENPGPKEY, CAA, TLSA packets and tests
This commit is contained in:
Lennart Poettering 2016-02-13 11:15:41 +01:00
commit 91ba5ac7d0
38 changed files with 692 additions and 400 deletions

1
.gitignore vendored
View File

@ -184,6 +184,7 @@
/test-dhcp-server
/test-dhcp6-client
/test-dns-domain
/test-dns-packet
/test-dnssec
/test-efi-disk.img
/test-ellipsize

View File

@ -1499,6 +1499,7 @@ tests += \
test-af-list \
test-arphrd-list \
test-dns-domain \
test-dns-packet \
test-resolve-tables \
test-install-root \
test-rlimit-util \
@ -1664,16 +1665,6 @@ test_dns_domain_LDADD = \
libsystemd-network.la \
libshared.la
test_resolve_tables_SOURCES = \
src/resolve/test-resolve-tables.c \
src/shared/test-tables.h \
src/resolve/dns-type.c \
src/resolve/dns-type.h \
src/resolve/dns_type-from-name.h \
src/resolve/dns_type-to-name.h
test_resolve_tables_LDADD = \
libshared.la
if ENABLE_EFI
manual_tests += \
@ -4279,7 +4270,9 @@ libsystemd_journal_internal_la_SOURCES += \
src/journal/journal-authenticate.c \
src/journal/journal-authenticate.h \
src/journal/fsprg.c \
src/journal/fsprg.h
src/journal/fsprg.h \
src/shared/gcrypt-util.c \
src/shared/gcrypt-util.h
libsystemd_journal_internal_la_LIBADD += \
$(GCRYPT_LIBS)
@ -5178,6 +5171,20 @@ EXTRA_DIST += \
# ------------------------------------------------------------------------------
if ENABLE_RESOLVED
basic_dns_sources = \
src/resolve/resolved-dns-dnssec.c \
src/resolve/resolved-dns-dnssec.h \
src/resolve/resolved-dns-packet.c \
src/resolve/resolved-dns-packet.h \
src/resolve/resolved-dns-rr.c \
src/resolve/resolved-dns-rr.h \
src/resolve/resolved-dns-answer.c \
src/resolve/resolved-dns-answer.h \
src/resolve/resolved-dns-question.c \
src/resolve/resolved-dns-question.h \
src/resolve/dns-type.c \
src/resolve/dns-type.h
systemd_resolved_SOURCES = \
src/resolve/resolved.c \
src/resolve/resolved-manager.c \
@ -5197,14 +5204,7 @@ systemd_resolved_SOURCES = \
src/resolve/resolved-mdns.h \
src/resolve/resolved-mdns.c \
src/resolve/resolved-def.h \
src/resolve/resolved-dns-rr.h \
src/resolve/resolved-dns-rr.c \
src/resolve/resolved-dns-question.h \
src/resolve/resolved-dns-question.c \
src/resolve/resolved-dns-answer.h \
src/resolve/resolved-dns-answer.c \
src/resolve/resolved-dns-packet.h \
src/resolve/resolved-dns-packet.c \
$(basic_dns_sources) \
src/resolve/resolved-dns-query.h \
src/resolve/resolved-dns-query.c \
src/resolve/resolved-dns-synthesize.h \
@ -5223,14 +5223,12 @@ systemd_resolved_SOURCES = \
src/resolve/resolved-dns-zone.c \
src/resolve/resolved-dns-stream.h \
src/resolve/resolved-dns-stream.c \
src/resolve/resolved-dns-dnssec.h \
src/resolve/resolved-dns-dnssec.c \
src/resolve/resolved-dns-trust-anchor.h \
src/resolve/resolved-dns-trust-anchor.c \
src/resolve/resolved-etc-hosts.h \
src/resolve/resolved-etc-hosts.c \
src/resolve/dns-type.c \
src/resolve/dns-type.h
src/shared/gcrypt-util.c \
src/shared/gcrypt-util.h
nodist_systemd_resolved_SOURCES = \
src/resolve/dns_type-from-name.h \
@ -5290,18 +5288,9 @@ lib_LTLIBRARIES += \
systemd_resolve_SOURCES = \
src/resolve/resolve-tool.c \
src/resolve/resolved-dns-dnssec.c \
src/resolve/resolved-dns-dnssec.h \
src/resolve/resolved-dns-packet.c \
src/resolve/resolved-dns-packet.h \
src/resolve/resolved-dns-rr.c \
src/resolve/resolved-dns-rr.h \
src/resolve/resolved-dns-answer.c \
src/resolve/resolved-dns-answer.h \
src/resolve/resolved-dns-question.c \
src/resolve/resolved-dns-question.h \
src/resolve/dns-type.c \
src/resolve/dns-type.h
$(basic_dns_sources) \
src/shared/gcrypt-util.c \
src/shared/gcrypt-util.h
nodist_systemd_resolve_SOURCES = \
src/resolve/dns_type-from-name.h \
@ -5320,20 +5309,43 @@ tests += \
manual_tests += \
test-dnssec-complex
test_resolve_tables_SOURCES = \
src/resolve/test-resolve-tables.c \
src/resolve/dns_type-from-name.h \
src/resolve/dns_type-to-name.h \
$(basic_dns_sources) \
src/shared/test-tables.h
test_resolve_tables_LDADD = \
libshared.la
test_dns_packet_SOURCES = \
src/resolve/test-dns-packet.c \
$(basic_dns_sources)
test_dns_packet_CPPFLAGS = \
$(AM_CPPFLAGS) \
-DRESOLVE_TEST_DIR=\"$(abs_top_srcdir)/src/resolve/test-data\"
test_dns_packet_LDADD = \
libshared.la
EXTRA_DIST += \
src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts \
src/resolve/test-data/fedoraproject.org.pkts \
src/resolve/test-data/gandi.net.pkts \
src/resolve/test-data/google.com.pkts \
src/resolve/test-data/root.pkts \
src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts \
src/resolve/test-data/teamits.com.pkts \
src/resolve/test-data/zbyszek@fedoraproject.org.pkts \
src/resolve/test-data/_443._tcp.fedoraproject.org.pkts \
src/resolve/test-data/kyhwana.org.pkts \
src/resolve/test-data/fake-caa.pkts
test_dnssec_SOURCES = \
src/resolve/test-dnssec.c \
src/resolve/resolved-dns-packet.c \
src/resolve/resolved-dns-packet.h \
src/resolve/resolved-dns-rr.c \
src/resolve/resolved-dns-rr.h \
src/resolve/resolved-dns-answer.c \
src/resolve/resolved-dns-answer.h \
src/resolve/resolved-dns-question.c \
src/resolve/resolved-dns-question.h \
src/resolve/resolved-dns-dnssec.c \
src/resolve/resolved-dns-dnssec.h \
src/resolve/dns-type.c \
src/resolve/dns-type.h
$(basic_dns_sources)
test_dnssec_LDADD = \
libshared.la

View File

@ -76,6 +76,13 @@
<replaceable>TYPE</replaceable></arg> <replaceable>DOMAIN</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-resolve</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<command> --openpgp</command>
<arg choice="plain"><replaceable>USER@DOMAIN</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-resolve</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
@ -114,6 +121,10 @@
is assumed to be a domain name, that is already prefixed with an SRV type, and an SRV lookup is done (no
TXT).</para>
<para>The <option>--openpgp</option> switch may be use to query PGP keys stored as the
<ulink url="https://tools.ietf.org/html/draft-wouters-dane-openpgp-02">OPENPGPKEY</ulink> resource records.
When this option is specified one or more e-mail address must be specified.</para>
<para>The <option>--statistics</option> switch may be used to show resolver statistics, including information about
the number of successful and failed DNSSEC validations.</para>
@ -197,6 +208,14 @@
<option>--service</option> the TXT service metadata record is resolved as well.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--openpgp</option></term>
<listitem><para>Enables OPENPGPKEY resource record resolution (see above). Specified e-mail
addresses are converted to the corresponding DNS domain name, and any OPENPGPKEY keys are
printed.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--cname=</option><replaceable>BOOL</replaceable></term>

View File

@ -413,6 +413,34 @@ char *xescape(const char *s, const char *bad) {
return r;
}
char *octescape(const char *s, size_t len) {
char *r, *t;
const char *f;
/* Escapes all chars in bad, in addition to \ and " chars,
* in \nnn style escaping. */
r = new(char, len * 4 + 1);
if (!r)
return NULL;
for (f = s, t = r; f < s + len; f++) {
if (*f < ' ' || *f >= 127 || *f == '\\' || *f == '"') {
*(t++) = '\\';
*(t++) = '0' + (*f >> 6);
*(t++) = '0' + ((*f >> 3) & 8);
*(t++) = '0' + (*f & 8);
} else
*(t++) = *f;
}
*t = 0;
return r;
}
static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
assert(bad);

View File

@ -48,6 +48,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit);
char *xescape(const char *s, const char *bad);
char *octescape(const char *s, size_t len);
char *shell_escape(const char *s, const char *bad);
char *shell_maybe_quote(const char *s);

View File

@ -25,6 +25,7 @@
#include "alloc-util.h"
#include "hexdecoct.h"
#include "macro.h"
#include "util.h"
char octchar(int x) {
return '0' + (x & 7);
@ -572,7 +573,7 @@ static int base64_append_width(char **prefix, int plen,
if (!t)
return -ENOMEM;
memcpy(t + plen, sep, slen);
memcpy_safe(t + plen, sep, slen);
for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) {
int act = MIN(width, avail);

View File

@ -102,6 +102,16 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_
qsort(base, nmemb, size, compar);
}
/**
* Normal memcpy requires src to be nonnull. We do nothing if n is 0.
*/
static inline void memcpy_safe(void *dst, const void *src, size_t n) {
if (n == 0)
return;
assert(src);
memcpy(dst, src, n);
}
int on_ac_power(void);
#define memzero(x,l) (memset((x), 0, (l)))

View File

@ -30,6 +30,7 @@
#include <string.h>
#include "fsprg.h"
#include "gcrypt-util.h"
#define ISVALID_SECPAR(secpar) (((secpar) % 16 == 0) && ((secpar) >= 16) && ((secpar) <= 16384))
#define VALIDATE_SECPAR(secpar) assert(ISVALID_SECPAR(secpar));
@ -206,20 +207,6 @@ static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq,
gcry_mpi_release(u);
}
static void initialize_libgcrypt(void) {
const char *p;
if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
return;
p = gcry_check_version("1.4.5");
assert(p);
/* Turn off "secmem". Clients which whish to make use of this
* feature should initialize the library manually */
gcry_control(GCRYCTL_DISABLE_SECMEM);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
/******************************************************************************/
size_t FSPRG_mskinbytes(unsigned _secpar) {
@ -259,7 +246,7 @@ void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigne
VALIDATE_SECPAR(_secpar);
secpar = _secpar;
initialize_libgcrypt();
initialize_libgcrypt(false);
if (!seed) {
gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM);
@ -295,7 +282,7 @@ void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seed
gcry_mpi_t n, x;
uint16_t secpar;
initialize_libgcrypt();
initialize_libgcrypt(false);
secpar = read_secpar(mpk + 0);
n = mpi_import(mpk + 2, secpar / 8);
@ -314,7 +301,7 @@ void FSPRG_Evolve(void *state) {
uint16_t secpar;
uint64_t epoch;
initialize_libgcrypt();
initialize_libgcrypt(false);
secpar = read_secpar(state + 0);
n = mpi_import(state + 2 + 0 * secpar / 8, secpar / 8);
@ -341,7 +328,7 @@ void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed,
gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm;
uint16_t secpar;
initialize_libgcrypt();
initialize_libgcrypt(false);
secpar = read_secpar(msk + 0);
p = mpi_import(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8);
@ -380,7 +367,7 @@ void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed,
void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
uint16_t secpar;
initialize_libgcrypt();
initialize_libgcrypt(false);
secpar = read_secpar(state + 0);
det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx);

View File

@ -22,6 +22,7 @@
#include "fd-util.h"
#include "fsprg.h"
#include "gcrypt-util.h"
#include "hexdecoct.h"
#include "journal-authenticate.h"
#include "journal-def.h"
@ -424,25 +425,13 @@ finish:
return r;
}
static void initialize_libgcrypt(void) {
const char *p;
if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
return;
p = gcry_check_version("1.4.5");
assert(p);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
int journal_file_hmac_setup(JournalFile *f) {
gcry_error_t e;
if (!f->seal)
return 0;
initialize_libgcrypt();
initialize_libgcrypt(true);
e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
if (e != 0)

View File

@ -1123,8 +1123,8 @@ static int journal_file_append_data(
}
#endif
if (compression == 0 && size > 0)
memcpy(o->data.payload, data, size);
if (compression == 0)
memcpy_safe(o->data.payload, data, size);
r = journal_file_link_data(f, o, p, hash);
if (r < 0)
@ -1389,7 +1389,7 @@ static int journal_file_append_entry_internal(
return r;
o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum));
memcpy(o->entry.items, items, n_items * sizeof(EntryItem));
memcpy_safe(o->entry.items, items, n_items * sizeof(EntryItem));
o->entry.realtime = htole64(ts->realtime);
o->entry.monotonic = htole64(ts->monotonic);
o->entry.xor_hash = htole64(xor_hash);

View File

@ -54,12 +54,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
options[*offset] = code;
options[*offset + 1] = optlen;
if (optlen) {
assert(optval);
memcpy(&options[*offset + 2], optval, optlen);
}
memcpy_safe(&options[*offset + 2], optval, optlen);
*offset += optlen + 2;
break;

View File

@ -71,8 +71,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
if (r < 0)
return r;
if (optval)
memcpy(*buf, optval, optlen);
memcpy_safe(*buf, optval, optlen);
*buf += optlen;
*buflen -= optlen;

View File

@ -110,14 +110,9 @@ static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
message = malloc0(len);
assert_se(message);
if (options && optlen)
memcpy(&message->options, options, optlen);
if (file && filelen <= 128)
memcpy(&message->file, file, filelen);
if (sname && snamelen <= 64)
memcpy(&message->sname, sname, snamelen);
memcpy_safe(&message->options, options, optlen);
memcpy_safe(&message->file, file, filelen);
memcpy_safe(&message->sname, sname, snamelen);
return message;
}

View File

@ -1131,8 +1131,7 @@ static int add_name_change_match(sd_bus *bus,
item->name_change.old_id.id = old_owner_id;
item->name_change.new_id.id = new_owner_id;
if (name)
memcpy(item->name_change.name, name, l);
memcpy_safe(item->name_change.name, name, l);
/* If the old name is unset or empty, then
* this can match against added names */

View File

@ -2631,8 +2631,7 @@ _public_ int sd_bus_message_append_array(
if (r < 0)
return r;
if (size > 0)
memcpy(p, ptr, size);
memcpy_safe(p, ptr, size);
return 0;
}

View File

@ -350,7 +350,7 @@ static int bus_socket_auth_write(sd_bus *b, const char *t) {
if (!p)
return -ENOMEM;
memcpy(p, b->auth_iovec[0].iov_base, b->auth_iovec[0].iov_len);
memcpy_safe(p, b->auth_iovec[0].iov_base, b->auth_iovec[0].iov_len);
memcpy(p + b->auth_iovec[0].iov_len, t, l);
b->auth_iovec[0].iov_base = p;
@ -787,7 +787,7 @@ int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
n = m->n_iovec * sizeof(struct iovec);
iov = alloca(n);
memcpy(iov, m->iovec, n);
memcpy_safe(iov, m->iovec, n);
j = 0;
iovec_advance(iov, &j, *idx);
@ -998,7 +998,7 @@ int bus_socket_read_message(sd_bus *bus) {
return -ENOMEM;
}
memcpy(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int));
memcpy_safe(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int));
bus->fds = f;
bus->n_fds += n;
} else

View File

@ -217,9 +217,8 @@ static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *leng
memcpy((uint8_t*) p, &s, sizeof(AddrInfoSerialization));
memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen);
if (ai->ai_canonname)
memcpy((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, ai->ai_canonname, cnl);
memcpy_safe((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen,
ai->ai_canonname, cnl);
*length += l;
return (uint8_t*) p + l;

View File

@ -2623,12 +2623,10 @@ static int inner_child(
/* Automatically search for the init system */
m = 1 + strv_length(arg_parameters);
a = newa(char*, m + 1);
if (strv_isempty(arg_parameters))
a[1] = NULL;
else
memcpy(a + 1, arg_parameters, m * sizeof(char*));
m = strv_length(arg_parameters);
a = newa(char*, m + 2);
memcpy_safe(a + 1, arg_parameters, m * sizeof(char*));
a[1 + m] = NULL;
a[0] = (char*) "/usr/lib/systemd/systemd";
execve(a[0], a, env_use);

View File

@ -152,3 +152,6 @@ const char *tlsa_selector_to_string(uint8_t selector);
/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.4 */
const char *tlsa_matching_type_to_string(uint8_t selector);
/* https://tools.ietf.org/html/rfc6844#section-5.1 */
#define CAA_FLAG_CRITICAL (1u << 7)

View File

@ -17,6 +17,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <gcrypt.h>
#include <getopt.h>
#include <net/if.h>
@ -28,6 +29,7 @@
#include "bus-util.h"
#include "escape.h"
#include "in-addr-util.h"
#include "gcrypt-util.h"
#include "parse-util.h"
#include "resolved-def.h"
#include "resolved-dns-packet.h"
@ -46,6 +48,7 @@ static enum {
MODE_RESOLVE_HOST,
MODE_RESOLVE_RECORD,
MODE_RESOLVE_SERVICE,
MODE_RESOLVE_OPENPGP,
MODE_STATISTICS,
MODE_RESET_STATISTICS,
} arg_mode = MODE_RESOLVE_HOST;
@ -545,15 +548,10 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) {
} else
n = p;
if (type == 0)
type = arg_type;
if (type == 0)
type = DNS_TYPE_A;
if (class == 0)
class = arg_class;
if (class == 0)
class = DNS_CLASS_IN;
class = arg_class ?: DNS_CLASS_IN;
if (type == 0)
type = arg_type ?: DNS_TYPE_A;
return resolve_record(bus, n, class, type);
@ -763,6 +761,36 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
return 0;
}
static int resolve_openpgp(sd_bus *bus, const char *address) {
const char *domain, *full;
int r;
_cleanup_free_ char *hashed = NULL;
assert(bus);
assert(address);
domain = strrchr(address, '@');
if (!domain) {
log_error("Address does not contain '@': \"%s\"", address);
return -EINVAL;
} else if (domain == address || domain[1] == '\0') {
log_error("Address starts or ends with '@': \"%s\"", address);
return -EINVAL;
}
domain++;
r = string_hashsum(address, domain - 1 - address, GCRY_MD_SHA224, &hashed);
if (r < 0)
return log_error_errno(r, "Hashing failed: %m");
full = strjoina(hashed, "._openpgpkey.", domain);
log_debug("Looking up \"%s\".", full);
return resolve_record(bus, full,
arg_class ?: DNS_CLASS_IN,
arg_type ?: DNS_TYPE_OPENPGPKEY);
}
static int show_statistics(sd_bus *bus) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
@ -945,6 +973,7 @@ static void help(void) {
" --service Resolve service (SRV)\n"
" --service-address=BOOL Do [not] resolve address for services\n"
" --service-txt=BOOL Do [not] resolve TXT records for services\n"
" --openpgp Query OpenPGP public key\n"
" --cname=BOOL Do [not] follow CNAME redirects\n"
" --search=BOOL Do [not] use search domains\n"
" --legend=BOOL Do [not] print column headers and meta information\n"
@ -961,6 +990,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_CNAME,
ARG_SERVICE_ADDRESS,
ARG_SERVICE_TXT,
ARG_OPENPGP,
ARG_SEARCH,
ARG_STATISTICS,
ARG_RESET_STATISTICS,
@ -978,6 +1008,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "service", no_argument, NULL, ARG_SERVICE },
{ "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
{ "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
{ "openpgp", no_argument, NULL, ARG_OPENPGP },
{ "search", required_argument, NULL, ARG_SEARCH },
{ "statistics", no_argument, NULL, ARG_STATISTICS, },
{ "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS },
@ -1087,6 +1118,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_mode = MODE_RESOLVE_SERVICE;
break;
case ARG_OPENPGP:
arg_mode = MODE_RESOLVE_OPENPGP;
break;
case ARG_CNAME:
r = parse_boolean(optarg);
if (r < 0)
@ -1246,6 +1281,24 @@ int main(int argc, char **argv) {
break;
case MODE_RESOLVE_OPENPGP:
if (argc < optind + 1) {
log_error("E-mail address required.");
r = -EINVAL;
goto finish;
}
r = 0;
while (optind < argc) {
int k;
k = resolve_openpgp(bus, argv[optind++]);
if (k < 0)
r = k;
}
break;
case MODE_STATISTICS:
if (argc > optind) {
log_error("Too many arguments.");

View File

@ -23,6 +23,7 @@
#include "alloc-util.h"
#include "dns-domain.h"
#include "gcrypt-util.h"
#include "hexdecoct.h"
#include "resolved-dns-dnssec.h"
#include "resolved-dns-packet.h"
@ -126,19 +127,6 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
#ifdef HAVE_GCRYPT
static void initialize_libgcrypt(void) {
const char *p;
if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
return;
p = gcry_check_version("1.4.5");
assert(p);
gcry_control(GCRYCTL_DISABLE_SECMEM);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
static int rr_compare(const void *a, const void *b) {
DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
size_t m;
@ -737,7 +725,7 @@ int dnssec_verify_rrset(
qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
/* OK, the RRs are now in canonical order. Let's calculate the digest */
initialize_libgcrypt();
initialize_libgcrypt(false);
hash_size = gcry_md_get_algo_dlen(md_algorithm);
assert(hash_size > 0);
@ -1070,7 +1058,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds,
if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
return 0;
initialize_libgcrypt();
initialize_libgcrypt(false);
md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
if (md_algorithm < 0)
@ -1189,7 +1177,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
if (algorithm < 0)
return algorithm;
initialize_libgcrypt();
initialize_libgcrypt(false);
hash_size = gcry_md_get_algo_dlen(algorithm);
assert(hash_size > 0);

View File

@ -28,6 +28,19 @@
#define EDNS0_OPT_DO (1<<15)
typedef struct DnsPacketRewinder {
DnsPacket *packet;
size_t saved_rindex;
} DnsPacketRewinder;
static void rewind_dns_packet(DnsPacketRewinder *rewinder) {
if (rewinder->packet)
dns_packet_rewind(rewinder->packet, rewinder->saved_rindex);
}
#define INIT_REWINDER(rewinder, p) do { rewinder.packet = p; rewinder.saved_rindex = p->rindex; } while(0)
#define CANCEL_REWINDER(rewinder) do { rewinder.packet = NULL; } while(0)
int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
DnsPacket *p;
size_t a;
@ -431,8 +444,7 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_
((uint8_t*) d)[0] = (uint8_t) size;
if (size > 0)
memcpy(((uint8_t*) d) + 1, s, size);
memcpy_safe(((uint8_t*) d) + 1, s, size);
return 0;
}
@ -1072,6 +1084,18 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
r = dns_packet_append_blob(p, rr->tlsa.data, rr->tlsa.data_size, NULL);
break;
case DNS_TYPE_CAA:
r = dns_packet_append_uint8(p, rr->caa.flags, NULL);
if (r < 0)
goto fail;
r = dns_packet_append_string(p, rr->caa.tag, NULL);
if (r < 0)
goto fail;
r = dns_packet_append_blob(p, rr->caa.value, rr->caa.value_size, NULL);
break;
case DNS_TYPE_OPT:
case DNS_TYPE_OPENPGPKEY:
case _DNS_TYPE_INVALID: /* unparseable */
@ -1230,80 +1254,67 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
}
int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
size_t saved_rindex;
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
const void *d;
char *t;
uint8_t c;
int r;
assert(p);
saved_rindex = p->rindex;
INIT_REWINDER(rewinder, p);
r = dns_packet_read_uint8(p, &c, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read(p, c, &d, NULL);
if (r < 0)
goto fail;
return r;
if (memchr(d, 0, c)) {
r = -EBADMSG;
goto fail;
}
if (memchr(d, 0, c))
return -EBADMSG;
t = strndup(d, c);
if (!t) {
r = -ENOMEM;
goto fail;
}
if (!t)
return -ENOMEM;
if (!utf8_is_valid(t)) {
free(t);
r = -EBADMSG;
goto fail;
return -EBADMSG;
}
*ret = t;
if (start)
*start = saved_rindex;
*start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
}
int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) {
size_t saved_rindex;
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
uint8_t c;
int r;
assert(p);
saved_rindex = p->rindex;
INIT_REWINDER(rewinder, p);
r = dns_packet_read_uint8(p, &c, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read(p, c, ret, NULL);
if (r < 0)
goto fail;
return r;
if (size)
*size = c;
if (start)
*start = saved_rindex;
*start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
}
int dns_packet_read_name(
@ -1312,7 +1323,8 @@ int dns_packet_read_name(
bool allow_compression,
size_t *start) {
size_t saved_rindex, after_rindex = 0, jump_barrier;
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
size_t after_rindex = 0, jump_barrier;
_cleanup_free_ char *ret = NULL;
size_t n = 0, allocated = 0;
bool first = true;
@ -1320,19 +1332,18 @@ int dns_packet_read_name(
assert(p);
assert(_ret);
INIT_REWINDER(rewinder, p);
jump_barrier = p->rindex;
if (p->refuse_compression)
allow_compression = false;
saved_rindex = p->rindex;
jump_barrier = p->rindex;
for (;;) {
uint8_t c, d;
r = dns_packet_read_uint8(p, &c, NULL);
if (r < 0)
goto fail;
return r;
if (c == 0)
/* End of name */
@ -1343,12 +1354,10 @@ int dns_packet_read_name(
/* Literal label */
r = dns_packet_read(p, c, (const void**) &label, NULL);
if (r < 0)
goto fail;
return r;
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
r = -ENOMEM;
goto fail;
}
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
return -ENOMEM;
if (first)
first = false;
@ -1357,7 +1366,7 @@ int dns_packet_read_name(
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
if (r < 0)
goto fail;
return r;
n += r;
continue;
@ -1367,13 +1376,11 @@ int dns_packet_read_name(
/* Pointer */
r = dns_packet_read_uint8(p, &d, NULL);
if (r < 0)
goto fail;
return r;
ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) {
r = -EBADMSG;
goto fail;
}
if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier)
return -EBADMSG;
if (after_rindex == 0)
after_rindex = p->rindex;
@ -1381,16 +1388,12 @@ int dns_packet_read_name(
/* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */
jump_barrier = ptr;
p->rindex = ptr;
} else {
r = -EBADMSG;
goto fail;
}
} else
return -EBADMSG;
}
if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
r = -ENOMEM;
goto fail;
}
if (!GREEDY_REALLOC(ret, allocated, n + 1))
return -ENOMEM;
ret[n] = 0;
@ -1401,13 +1404,10 @@ int dns_packet_read_name(
ret = NULL;
if (start)
*start = saved_rindex;
*start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
}
static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) {
@ -1417,32 +1417,31 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
uint8_t bit = 0;
unsigned i;
bool found = false;
size_t saved_rindex;
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
int r;
assert(p);
assert(types);
saved_rindex = p->rindex;
INIT_REWINDER(rewinder, p);
r = bitmap_ensure_allocated(types);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &window, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &length, NULL);
if (r < 0)
goto fail;
return r;
if (length == 0 || length > 32)
return -EBADMSG;
r = dns_packet_read(p, length, (const void **)&bitmap, NULL);
if (r < 0)
goto fail;
return r;
for (i = 0; i < length; i++) {
uint8_t bitmask = 1 << 7;
@ -1467,7 +1466,7 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
r = bitmap_set(*types, n);
if (r < 0)
goto fail;
return r;
}
bit ++;
@ -1479,70 +1478,61 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
return -EBADMSG;
if (start)
*start = saved_rindex;
*start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
}
static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) {
size_t saved_rindex;
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
int r;
saved_rindex = p->rindex;
INIT_REWINDER(rewinder, p);
while (p->rindex < saved_rindex + size) {
while (p->rindex < rewinder.saved_rindex + size) {
r = dns_packet_read_type_window(p, types, NULL);
if (r < 0)
goto fail;
return r;
/* don't read past end of current RR */
if (p->rindex > saved_rindex + size) {
r = -EBADMSG;
goto fail;
}
if (p->rindex > rewinder.saved_rindex + size)
return -EBADMSG;
}
if (p->rindex != saved_rindex + size) {
r = -EBADMSG;
goto fail;
}
if (p->rindex != rewinder.saved_rindex + size)
return -EBADMSG;
if (start)
*start = saved_rindex;
*start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
}
int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) {
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
_cleanup_free_ char *name = NULL;
bool cache_flush = false;
uint16_t class, type;
DnsResourceKey *key;
size_t saved_rindex;
int r;
assert(p);
assert(ret);
saved_rindex = p->rindex;
INIT_REWINDER(rewinder, p);
r = dns_packet_read_name(p, &name, true, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint16(p, &type, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint16(p, &class, NULL);
if (r < 0)
goto fail;
return r;
if (p->protocol == DNS_PROTOCOL_MDNS) {
/* See RFC6762, Section 10.2 */
@ -1554,10 +1544,8 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus
}
key = dns_resource_key_new_consume(class, type, name);
if (!key) {
r = -ENOMEM;
goto fail;
}
if (!key)
return -ENOMEM;
name = NULL;
*ret = key;
@ -1565,12 +1553,10 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus
if (ret_cache_flush)
*ret_cache_flush = cache_flush;
if (start)
*start = saved_rindex;
*start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
}
static bool loc_size_ok(uint8_t size) {
@ -1582,7 +1568,8 @@ static bool loc_size_ok(uint8_t size) {
int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
size_t saved_rindex, offset;
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
size_t offset;
uint16_t rdlength;
bool cache_flush;
int r;
@ -1590,27 +1577,22 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
assert(p);
assert(ret);
saved_rindex = p->rindex;
INIT_REWINDER(rewinder, p);
r = dns_packet_read_key(p, &key, &cache_flush, NULL);
if (r < 0)
goto fail;
return r;
if (!dns_class_is_valid_rr(key->class)||
!dns_type_is_valid_rr(key->type)) {
r = -EBADMSG;
goto fail;
}
if (!dns_class_is_valid_rr(key->class) || !dns_type_is_valid_rr(key->type))
return -EBADMSG;
rr = dns_resource_record_new(key);
if (!rr) {
r = -ENOMEM;
goto fail;
}
if (!rr)
return -ENOMEM;
r = dns_packet_read_uint32(p, &rr->ttl, NULL);
if (r < 0)
goto fail;
return r;
/* RFC 2181, Section 8, suggests to
* treat a TTL with the MSB set as a zero TTL. */
@ -1619,12 +1601,10 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_uint16(p, &rdlength, NULL);
if (r < 0)
goto fail;
return r;
if (p->rindex + rdlength > p->size) {
r = -EBADMSG;
goto fail;
}
if (p->rindex + rdlength > p->size)
return -EBADMSG;
offset = p->rindex;
@ -1633,13 +1613,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_SRV:
r = dns_packet_read_uint16(p, &rr->srv.priority, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint16(p, &rr->srv.weight, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint16(p, &rr->srv.port, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_name(p, &rr->srv.name, true, NULL);
break;
@ -1653,7 +1633,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_HINFO:
r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_string(p, &rr->hinfo.os, NULL);
break;
@ -1709,27 +1689,27 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_SOA:
r = dns_packet_read_name(p, &rr->soa.mname, true, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_name(p, &rr->soa.rname, true, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint32(p, &rr->soa.serial, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint32(p, &rr->soa.retry, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint32(p, &rr->soa.expire, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL);
break;
@ -1737,7 +1717,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_MX:
r = dns_packet_read_uint16(p, &rr->mx.priority, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL);
break;
@ -1748,49 +1728,43 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_uint8(p, &t, &pos);
if (r < 0)
goto fail;
return r;
if (t == 0) {
rr->loc.version = t;
r = dns_packet_read_uint8(p, &rr->loc.size, NULL);
if (r < 0)
goto fail;
return r;
if (!loc_size_ok(rr->loc.size)) {
r = -EBADMSG;
goto fail;
}
if (!loc_size_ok(rr->loc.size))
return -EBADMSG;
r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL);
if (r < 0)
goto fail;
return r;
if (!loc_size_ok(rr->loc.horiz_pre)) {
r = -EBADMSG;
goto fail;
}
if (!loc_size_ok(rr->loc.horiz_pre))
return -EBADMSG;
r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL);
if (r < 0)
goto fail;
return r;
if (!loc_size_ok(rr->loc.vert_pre)) {
r = -EBADMSG;
goto fail;
}
if (!loc_size_ok(rr->loc.vert_pre))
return -EBADMSG;
r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL);
if (r < 0)
goto fail;
return r;
break;
} else {
@ -1803,122 +1777,114 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_DS:
r = dns_packet_read_uint16(p, &rr->ds.key_tag, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &rr->ds.algorithm, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &rr->ds.digest_type, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_memdup(p, rdlength - 4,
&rr->ds.digest, &rr->ds.digest_size,
NULL);
if (r < 0)
goto fail;
return r;
if (rr->ds.digest_size <= 0) {
if (rr->ds.digest_size <= 0)
/* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */
r = -EBADMSG;
goto fail;
}
return -EBADMSG;
break;
case DNS_TYPE_SSHFP:
r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_memdup(p, rdlength - 2,
&rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size,
NULL);
if (rr->sshfp.fingerprint_size <= 0) {
if (rr->sshfp.fingerprint_size <= 0)
/* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */
r = -EBADMSG;
goto fail;
}
return -EBADMSG;
break;
case DNS_TYPE_DNSKEY:
r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_memdup(p, rdlength - 4,
&rr->dnskey.key, &rr->dnskey.key_size,
NULL);
if (rr->dnskey.key_size <= 0) {
if (rr->dnskey.key_size <= 0)
/* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */
r = -EBADMSG;
goto fail;
}
return -EBADMSG;
break;
case DNS_TYPE_RRSIG:
r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_memdup(p, offset + rdlength - p->rindex,
&rr->rrsig.signature, &rr->rrsig.signature_size,
NULL);
if (rr->rrsig.signature_size <= 0) {
if (rr->rrsig.signature_size <= 0)
/* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */
r = -EBADMSG;
goto fail;
}
return -EBADMSG;
break;
@ -1933,11 +1899,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL);
if (r < 0)
goto fail;
/* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself
* is redundant and in e.g., RFC4956 this fact is used to define a use for NSEC records
@ -1950,41 +1914,39 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL);
if (r < 0)
goto fail;
return r;
/* this may be zero */
r = dns_packet_read_uint8(p, &size, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &size, NULL);
if (r < 0)
goto fail;
return r;
if (size <= 0) {
r = -EBADMSG;
goto fail;
}
if (size <= 0)
return -EBADMSG;
r = dns_packet_read_memdup(p, size, &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, NULL);
r = dns_packet_read_memdup(p, size,
&rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size,
NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL);
if (r < 0)
goto fail;
/* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */
@ -1994,25 +1956,39 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_TLSA:
r = dns_packet_read_uint8(p, &rr->tlsa.cert_usage, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &rr->tlsa.selector, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_uint8(p, &rr->tlsa.matching_type, NULL);
if (r < 0)
goto fail;
return r;
r = dns_packet_read_memdup(p, rdlength - 3,
&rr->tlsa.data, &rr->tlsa.data_size,
NULL);
if (rr->tlsa.data_size <= 0) {
if (rr->tlsa.data_size <= 0)
/* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */
r = -EBADMSG;
goto fail;
}
return -EBADMSG;
break;
case DNS_TYPE_CAA:
r = dns_packet_read_uint8(p, &rr->caa.flags, NULL);
if (r < 0)
return r;
r = dns_packet_read_string(p, &rr->caa.tag, NULL);
if (r < 0)
return r;
r = dns_packet_read_memdup(p,
rdlength + offset - p->rindex,
&rr->caa.value, &rr->caa.value_size, NULL);
break;
@ -2021,16 +1997,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
default:
unparseable:
r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL);
if (r < 0)
goto fail;
break;
}
if (r < 0)
goto fail;
if (p->rindex != offset + rdlength) {
r = -EBADMSG;
goto fail;
}
return r;
if (p->rindex != offset + rdlength)
return -EBADMSG;
*ret = rr;
rr = NULL;
@ -2038,12 +2011,10 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
if (ret_cache_flush)
*ret_cache_flush = cache_flush;
if (start)
*start = saved_rindex;
*start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
}
static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
@ -2091,23 +2062,21 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
int dns_packet_extract(DnsPacket *p) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
size_t saved_rindex;
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {};
unsigned n, i;
int r;
if (p->extracted)
return 0;
saved_rindex = p->rindex;
INIT_REWINDER(rewinder, p);
dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
n = DNS_PACKET_QDCOUNT(p);
if (n > 0) {
question = dns_question_new(n);
if (!question) {
r = -ENOMEM;
goto finish;
}
if (!question)
return -ENOMEM;
for (i = 0; i < n; i++) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
@ -2115,21 +2084,17 @@ int dns_packet_extract(DnsPacket *p) {
r = dns_packet_read_key(p, &key, &cache_flush, NULL);
if (r < 0)
goto finish;
return r;
if (cache_flush) {
r = -EBADMSG;
goto finish;
}
if (cache_flush)
return -EBADMSG;
if (!dns_type_is_valid_query(key->type)) {
r = -EBADMSG;
goto finish;
}
if (!dns_type_is_valid_query(key->type))
return -EBADMSG;
r = dns_question_add(question, key);
if (r < 0)
goto finish;
return r;
}
}
@ -2139,10 +2104,8 @@ int dns_packet_extract(DnsPacket *p) {
bool bad_opt = false;
answer = dns_answer_new(n);
if (!answer) {
r = -ENOMEM;
goto finish;
}
if (!answer)
return -ENOMEM;
for (i = 0; i < n; i++) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
@ -2150,7 +2113,7 @@ int dns_packet_extract(DnsPacket *p) {
r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
if (r < 0)
goto finish;
return r;
/* Try to reduce memory usage a bit */
if (previous)
@ -2213,7 +2176,7 @@ int dns_packet_extract(DnsPacket *p) {
(i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
(p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0));
if (r < 0)
goto finish;
return r;
}
/* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note
@ -2234,11 +2197,8 @@ int dns_packet_extract(DnsPacket *p) {
p->extracted = true;
r = 0;
finish:
p->rindex = saved_rindex;
return r;
/* no CANCEL, always rewind */
return 0;
}
int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) {

View File

@ -22,6 +22,7 @@
#include "alloc-util.h"
#include "dns-domain.h"
#include "dns-type.h"
#include "escape.h"
#include "hexdecoct.h"
#include "resolved-dns-dnssec.h"
#include "resolved-dns-packet.h"
@ -490,6 +491,11 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
free(rr->tlsa.data);
break;
case DNS_TYPE_CAA:
free(rr->caa.tag);
free(rr->caa.value);
break;
case DNS_TYPE_OPENPGPKEY:
default:
free(rr->generic.data);
@ -697,6 +703,12 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
a->tlsa.matching_type == b->tlsa.matching_type &&
FIELD_EQUAL(a->tlsa, b->tlsa, data);
case DNS_TYPE_CAA:
return a->caa.flags == b->caa.flags &&
streq(a->caa.tag, b->caa.tag) &&
FIELD_EQUAL(a->caa, b->caa, value);
case DNS_TYPE_OPENPGPKEY:
default:
return FIELD_EQUAL(a->generic, b->generic, data);
}
@ -966,7 +978,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
case DNS_TYPE_DNSKEY: {
_cleanup_free_ char *alg = NULL;
char *ss;
int n, n1;
int n;
uint16_t key_tag;
key_tag = dnssec_keytag(rr, true);
@ -975,9 +987,8 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
if (r < 0)
return NULL;
r = asprintf(&s, "%s %n%u %u %s %n",
r = asprintf(&s, "%s %u %u %s %n",
k,
&n1,
rr->dnskey.flags,
rr->dnskey.protocol,
alg,
@ -992,14 +1003,12 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
return NULL;
r = asprintf(&ss, "%s\n"
"%*s-- Flags:%s%s%s\n"
"%*s-- Key tag: %u",
" -- Flags:%s%s%s\n"
" -- Key tag: %u",
s,
n1, "",
rr->dnskey.flags & DNSKEY_FLAG_SEP ? " SEP" : "",
rr->dnskey.flags & DNSKEY_FLAG_REVOKE ? " REVOKE" : "",
rr->dnskey.flags & DNSKEY_FLAG_ZONE_KEY ? " ZONE_KEY" : "",
n1, "",
key_tag);
if (r < 0)
return NULL;
@ -1125,13 +1134,13 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
return NULL;
r = asprintf(&ss, "%s\n"
"%*s-- Cert. usage: %s\n"
"%*s-- Selector: %s\n"
"%*s-- Matching type: %s",
" -- Cert. usage: %s\n"
" -- Selector: %s\n"
" -- Matching type: %s",
s,
n - 6, "", cert_usage,
n - 6, "", selector,
n - 6, "", matching_type);
cert_usage,
selector,
matching_type);
if (r < 0)
return NULL;
free(s);
@ -1140,6 +1149,28 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
break;
}
case DNS_TYPE_CAA: {
_cleanup_free_ char *value;
value = octescape(rr->caa.value, rr->caa.value_size);
if (!value)
return NULL;
r = asprintf(&s, "%s %u %s \"%s\"%s%s%s%.0u",
k,
rr->caa.flags,
rr->caa.tag,
value,
rr->caa.flags ? "\n -- Flags:" : "",
rr->caa.flags & CAA_FLAG_CRITICAL ? " critical" : "",
rr->caa.flags & ~CAA_FLAG_CRITICAL ? " " : "",
rr->caa.flags & ~CAA_FLAG_CRITICAL);
if (r < 0)
return NULL;
break;
}
case DNS_TYPE_OPENPGPKEY: {
int n;
@ -1300,7 +1331,7 @@ int dns_resource_record_is_synthetic(DnsResourceRecord *rr) {
return !r;
}
static void dns_resource_record_hash_func(const void *i, struct siphash *state) {
void dns_resource_record_hash_func(const void *i, struct siphash *state) {
const DnsResourceRecord *rr = i;
assert(rr);
@ -1427,7 +1458,13 @@ static void dns_resource_record_hash_func(const void *i, struct siphash *state)
siphash24_compress(&rr->tlsa.cert_usage, sizeof(rr->tlsa.cert_usage), state);
siphash24_compress(&rr->tlsa.selector, sizeof(rr->tlsa.selector), state);
siphash24_compress(&rr->tlsa.matching_type, sizeof(rr->tlsa.matching_type), state);
siphash24_compress(&rr->tlsa.data, rr->tlsa.data_size, state);
siphash24_compress(rr->tlsa.data, rr->tlsa.data_size, state);
break;
case DNS_TYPE_CAA:
siphash24_compress(&rr->caa.flags, sizeof(rr->caa.flags), state);
string_hash_func(rr->caa.tag, state);
siphash24_compress(rr->caa.value, rr->caa.value_size, state);
break;
case DNS_TYPE_OPENPGPKEY:

View File

@ -249,6 +249,14 @@ struct DnsResourceRecord {
void *data;
size_t data_size;
} tlsa;
/* https://tools.ietf.org/html/rfc6844 */
struct {
uint8_t flags;
char *tag;
void *value;
size_t value_size;
} caa;
};
};
@ -323,6 +331,8 @@ int dns_resource_record_is_synthetic(DnsResourceRecord *rr);
DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
void dns_resource_record_hash_func(const void *i, struct siphash *state);
extern const struct hash_ops dns_resource_key_hash_ops;
extern const struct hash_ops dns_resource_record_hash_ops;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,115 @@
/***
This file is part of systemd
Copyright 2016 Zbigniew Jędrzejewski-Szmek
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <net/if.h>
#include <glob.h>
#include "alloc-util.h"
#include "fileio.h"
#include "glob-util.h"
#include "log.h"
#include "macro.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-rr.h"
#include "string-util.h"
#include "strv.h"
#define HASH_KEY SD_ID128_MAKE(d3,1e,48,90,4b,fa,4c,fe,af,9d,d5,a1,d7,2e,8a,b1)
static uint64_t hash(DnsResourceRecord *rr) {
struct siphash state;
siphash24_init(&state, HASH_KEY.bytes);
dns_resource_record_hash_func(rr, &state);
return siphash24_finalize(&state);
}
static void test_packet_from_file(const char* filename, bool canonical) {
_cleanup_free_ char *data = NULL;
size_t data_size, packet_size, offset;
assert_se(read_full_file(filename, &data, &data_size) >= 0);
assert_se(data);
assert_se(data_size > 8);
log_info("============== %s %s==============", filename, canonical ? "canonical " : "");
for (offset = 0; offset < data_size; offset += 8 + packet_size) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL, *p2 = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *rr2 = NULL;
const char *s, *s2;
uint64_t hash1, hash2;
packet_size = le64toh( *(uint64_t*)(data + offset) );
assert_se(packet_size > 0);
assert_se(offset + 8 + packet_size <= data_size);
assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0) >= 0);
assert_se(dns_packet_append_blob(p, data + offset + 8, packet_size, NULL) >= 0);
assert_se(dns_packet_read_rr(p, &rr, NULL, NULL) >= 0);
s = dns_resource_record_to_string(rr);
assert_se(s);
puts(s);
hash1 = hash(rr);
assert_se(dns_resource_record_to_wire_format(rr, canonical) >= 0);
assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, 0) >= 0);
assert_se(dns_packet_append_blob(p2, rr->wire_format, rr->wire_format_size, NULL) >= 0);
assert_se(dns_packet_read_rr(p2, &rr2, NULL, NULL) >= 0);
s2 = dns_resource_record_to_string(rr);
assert_se(s2);
assert_se(streq(s, s2));
hash2 = hash(rr);
assert_se(hash1 == hash2);
}
}
int main(int argc, char **argv) {
int i, N;
_cleanup_globfree_ glob_t g = {};
_cleanup_strv_free_ char **globs = NULL;
char **fnames;
log_parse_environment();
if (argc >= 2) {
N = argc - 1;
fnames = argv + 1;
} else {
assert_se(glob(RESOLVE_TEST_DIR "/*.pkts", GLOB_NOSORT, NULL, &g) == 0);
N = g.gl_pathc;
fnames = g.gl_pathv;
}
for (i = 0; i < N; i++) {
test_packet_from_file(fnames[i], false);
puts("");
test_packet_from_file(fnames[i], true);
if (i + 1 < N)
puts("");
}
return EXIT_SUCCESS;
}

69
src/shared/gcrypt-util.c Normal file
View File

@ -0,0 +1,69 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <gcrypt.h>
#include "hexdecoct.h"
#include "gcrypt-util.h"
void initialize_libgcrypt(bool secmem) {
const char *p;
if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
return;
p = gcry_check_version("1.4.5");
assert(p);
/* Turn off "secmem". Clients which whish to make use of this
* feature should initialize the library manually */
if (!secmem)
gcry_control(GCRYCTL_DISABLE_SECMEM);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) {
gcry_md_hd_t md = NULL;
size_t hash_size;
void *hash;
char *enc;
initialize_libgcrypt(false);
hash_size = gcry_md_get_algo_dlen(md_algorithm);
assert(hash_size > 0);
gcry_md_open(&md, md_algorithm, 0);
if (!md)
return -EIO;
gcry_md_write(md, s, len);
hash = gcry_md_read(md, 0);
if (!hash)
return -EIO;
enc = hexmem(hash, hash_size);
if (!enc)
return -ENOMEM;
*out = enc;
return 0;
}

25
src/shared/gcrypt-util.h Normal file
View File

@ -0,0 +1,25 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2016 Zbigniew Jędrzejewski-Szmek
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
void initialize_libgcrypt(bool secmem);
int string_hashsum(const char *s, size_t len, int md_algorithm, char **out);