diff options
-rw-r--r-- | catalog/systemd.hu.catalog | 262 | ||||
-rw-r--r-- | po/hu.po | 139 | ||||
-rw-r--r-- | src/basic/conf-files.c | 22 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/bus-common-errors.c | 4 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/bus-common-errors.h | 1 | ||||
-rw-r--r-- | src/resolve-host/resolve-host.c | 147 | ||||
-rw-r--r-- | src/resolve/RFCs | 59 | ||||
-rw-r--r-- | src/resolve/resolved-bus.c | 18 | ||||
-rw-r--r-- | src/resolve/resolved-dns-answer.c | 24 | ||||
-rw-r--r-- | src/resolve/resolved-dns-cache.c | 3 | ||||
-rw-r--r-- | src/resolve/resolved-dns-dnssec.c | 260 | ||||
-rw-r--r-- | src/resolve/resolved-dns-dnssec.h | 3 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.c | 42 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.h | 13 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.c | 48 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.h | 39 | ||||
-rw-r--r-- | src/resolve/resolved-dns-server.c | 2 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.c | 43 | ||||
-rw-r--r-- | src/resolve/resolved-dns-trust-anchor.c | 324 | ||||
-rw-r--r-- | src/resolve/resolved-dns-trust-anchor.h | 6 |
20 files changed, 1247 insertions, 212 deletions
diff --git a/catalog/systemd.hu.catalog b/catalog/systemd.hu.catalog new file mode 100644 index 0000000000..30d76916cc --- /dev/null +++ b/catalog/systemd.hu.catalog @@ -0,0 +1,262 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2016 Gabor Kelemen +# +# 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/>. + +# Message catalog for systemd's own messages + +# The catalog format is documented on +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: A napló elindult +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A rendszernapló folyamat elindult, megnyitotta írásra a naplófájlokat, +és most készen áll kérések feldolgozására. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: A napló leállt +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A rendszernapló folyamat leállt, és bezárt minden jelenleg aktív naplófájlt. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Egy szolgáltatás üzenetei elnémítva +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: man:journald.conf(5) + +Egy szolgáltatás túl sok üzenetet naplózott adott idő alatt. A +szolgáltatástól származó üzenetek eldobásra kerültek. + +Ne feledje, hogy csak a kérdéses szolgáltatás üzenetei kerültek eldobásra, + más szolgáltatások üzeneteit ez nem befolyásolja. + +Az üzenetek eldobását vezérlő korlátok az /etc/systemd/journald.conf +RateLimitInterval= és RateLimitBurst= beállításaival adhatók meg. +Részletekért lásd a journald.conf(5) man oldalt. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Naplóüzenetek vesztek el +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Kernelüzenetek vesztek el, mert a naplózó rendszer nem tudta elég gyorsan +feldolgozni azokat. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Egy folyamat összeomlott: @COREDUMP_PID@ (@COREDUMP_COMM@) +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: man:core(5) + +Ez a folyamat: @COREDUMP_PID@ (@COREDUMP_COMM@) összeomlott, és core fájlt + írt ki. + +Ez általában programozási hibát jelez az összeomló programban, és +a szállítója felé kell bejelenteni. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Új munkamenet (@SESSION_ID@) létrehozva, felhasználója: @USER_ID@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Létrejött egy új munkamenet @SESSION_ID@ azonosítóval ezen felhasználóhoz: +@USER_ID@. + +A munkamenet vezető folyamata: @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Munkamenet (@SESSION_ID@) befejezve +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +A következő azonosítójú munkamenet befejeződött: @SESSION_ID@. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Elérhető egy új munkaállomás: @SEAT_ID@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Beállításra kerül és használható egy új munkaállomás: @SEAT_ID@. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: A munkaállomás eltávolítva: @SEAT_ID@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +A munkaállomás el lett távolítva, és már nem érhető el: @SEAT_ID@ + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Időmódosítás +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A rendszeróra beállítva @REALTIME@ ezredmásodpercre 1970. január 1. után. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Időzóna-módosítás erre: @TIMEZONE@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A rendszer időzónája módosítva lett erre: @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: A rendszer indítása kész +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A rendszerindításkor szükséges indításhoz sorba állított összes +rendszerszolgáltatás elindult. Ne feledje, hogy ez nem jelenti, hogy a +gép üresjáratban van, mivel egyes szolgáltatások még az indítás +befejezésével lehetnek elfoglalva. + +A kernel indítása @KERNEL_USEC@ ezredmásodpercet igényelt. + +A kiinduló RAM lemez indítása @INITRD_USEC@ ezredmásodpercet igényelt. + +A felhasználói programok indítása @USERSPACE_USEC@ ezredmásodpercet igényelt. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: A rendszer „@SLEEP@” alvási állapotba lépett +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A rendszer belépett ebbe az alvási állapotba: @SLEEP@. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: A rendszer „@SLEEP@” alvási állapotból kilépett +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A rendszer kilépett ebből az alvási állapotból: @SLEEP@. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Rendszer leállítása kezdeményezve +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A systemd leállítása kezdeményezve. A leállítás megkezdődött, minden +rendszerszolgáltatás befejeződik, minden fájlrendszer leválasztásra kerül. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: A(z) @UNIT@ egység indítása megkezdődött +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A(z) @UNIT@ egység megkezdte az indulást. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: A(z) @UNIT@ egység befejezte az indulást +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A(z) @UNIT@ egység befejezte az indulást + +Az indítás eredménye: @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: A(z) @UNIT@ egység megkezdte a leállást +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A(z) @UNIT@ egység megkezdte a leállást. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: A(z) @UNIT@ egység befejezte a leállást +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A(z) @UNIT@ egység befejezte a leállást. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: A(z) @UNIT@ egység hibát jelzett +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A(z) @UNIT@ egység hibát jelzett. + +Az eredmény: @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: A(z) @UNIT@ egység megkezdte a beállításainak újratöltését +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A(z) @UNIT@ egység megkezdte a beállításainak újratöltését. + +-- 7b05ebc668384222baa8881179cfda54 +Subject: A(z) @UNIT@ egység befejezte a beállításainak újratöltését +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A(z) @UNIT@ egység befejezte a beállításainak újratöltését. + +Az eredmény: @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: A folyamat végrehajtása sikertelen: @EXECUTABLE@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A folyamat végrehajtása sikertelen volt, és hibát jelzett: @EXECUTABLE@. + +A folyamat által visszaadott hibaszám: @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Legalább egy üzenet nem továbbítható a rendszernaplónak +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Legalább egy üzenet nem volt továbbítható a journald-vel párhuzamosan futó +syslog szolgáltatásnak. Ez általában azt jelenti, hogy a syslog +megvalósítás nem volt képes lépést tartani a sorba állított +üzenetek sebességével. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: A csatolási pont nem üres +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A csatolási pontként megadott @WHERE@ könyvtár (második mező az /etc/fstab +fájlban, vagy a Where= sor a systemd egységfájlban) nem üres. Ez nem +akadályozza meg a csatolást, de a könyvtárban már meglévő fájlok +elérhetetlenné válnak. A fájlok láthatóvá tételéhez csatolja +az azokat tartalmazó fájlrendszert egy másodlagos helyre. + +-- 24d8d4452573402496068381a6312df2 +Subject: Egy virtuális gép vagy konténer elindult +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A(z) @NAME@ nevű virtuális gép (vezető PID: @LEADER@) elindult, és +használatra kész. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Egy virtuális gép vagy konténer befejeződött +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +A(z) @NAME@ nevű virtuális gép (vezető PID: @LEADER@) leállt. @@ -1,14 +1,14 @@ # Hungarian translation of systemd -# Copyright (C) 2015. Free Software Foundation, Inc. +# Copyright (C) 2015, 2016. Free Software Foundation, Inc. # This file is distributed under the same license as the systemd package. # -# Gabor Kelemen <kelemeng at gnome dot hu>, 2015. +# Gabor Kelemen <kelemeng at gnome dot hu>, 2015, 2016. msgid "" msgstr "" "Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-22 16:37+0100\n" -"PO-Revision-Date: 2015-01-02 22:58+0100\n" +"POT-Creation-Date: 2016-01-02 13:41+0100\n" +"PO-Revision-Date: 2016-01-02 13:45+0100\n" "Last-Translator: Gabor Kelemen <kelemeng at ubuntu dot com>\n" "Language-Team: Hungarian <openscope at googlegroups dot com>\n" "Language: hu\n" @@ -29,15 +29,13 @@ msgstr "" "Hitelesítés szükséges a bevitt jelmondat visszaküldéséhez a rendszernek." #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3 -#, fuzzy msgid "Manage system services or other units" -msgstr "Rendszerszolgáltatások vagy -egységek kezelése" +msgstr "Rendszerszolgáltatások vagy más egységek kezelése" #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:4 -#, fuzzy msgid "Authentication is required to manage system services or other units." msgstr "" -"Hitelesítés szükséges a rendszerszolgáltatások vagy -egységek kezeléséhez." +"Hitelesítés szükséges a rendszerszolgáltatások vagy más egységek kezeléséhez." #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:5 msgid "Manage system service or unit files" @@ -51,14 +49,16 @@ msgstr "" #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7 msgid "Set or unset system and service manager environment variables" msgstr "" +"Rendszer- és szolgáltatáskezelő környezeti változóinak beállítása vagy " +"törlése" #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:8 -#, fuzzy msgid "" "Authentication is required to set or unset system and service manager " "environment variables." msgstr "" -"Hitelesítés szükséges a rendszerszolgáltatás- vagy egységfájlok kezeléséhez." +"Hitelesítés szükséges a rendszer- és szolgáltatáskezelő környezeti " +"változóinak beállításához vagy törléséhez." #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9 msgid "Reload the systemd state" @@ -98,30 +98,27 @@ msgstr "Hitelesítés szükséges a helyi gép információinak beállításáho #: ../src/import/org.freedesktop.import1.policy.in.h:1 msgid "Import a VM or container image" -msgstr "" +msgstr "VM vagy konténer lemezkép importálása" #: ../src/import/org.freedesktop.import1.policy.in.h:2 -#, fuzzy msgid "Authentication is required to import a VM or container image" -msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe." +msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép importálásához." #: ../src/import/org.freedesktop.import1.policy.in.h:3 msgid "Export a VM or container image" -msgstr "" +msgstr "VM vagy konténer lemezkép exportálása" #: ../src/import/org.freedesktop.import1.policy.in.h:4 -#, fuzzy msgid "Authentication is required to export a VM or container image" -msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe." +msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép exportálásához." #: ../src/import/org.freedesktop.import1.policy.in.h:5 msgid "Download a VM or container image" -msgstr "" +msgstr "VM vagy konténer lemezkép letöltése" #: ../src/import/org.freedesktop.import1.policy.in.h:6 -#, fuzzy msgid "Authentication is required to download a VM or container image" -msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe." +msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép letöltéséhez." #: ../src/locale/org.freedesktop.locale1.policy.in.h:1 msgid "Set system locale" @@ -409,123 +406,113 @@ msgstr "" #: ../src/login/org.freedesktop.login1.policy.in.h:49 msgid "Manage active sessions, users and seats" -msgstr "" +msgstr "Aktív munkamenetek, felhasználók és munkaállomások kezelése" #: ../src/login/org.freedesktop.login1.policy.in.h:50 -#, fuzzy msgid "" "Authentication is required for managing active sessions, users and seats." msgstr "" -"Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy " -"munkaállomáshoz" +"Hitelesítés szükséges az aktív munkamenetek, felhasználók és munkaállomások " +"kezeléséhez." #: ../src/login/org.freedesktop.login1.policy.in.h:51 msgid "Lock or unlock active sessions" -msgstr "" +msgstr "Aktív munkamenetek zárolása vagy feloldása" #: ../src/login/org.freedesktop.login1.policy.in.h:52 -#, fuzzy msgid "Authentication is required to lock or unlock active sessions." -msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe." +msgstr "" +"Hitelesítés szükséges az aktív munkamenetek zárolásához vagy feloldásához." #: ../src/login/org.freedesktop.login1.policy.in.h:53 msgid "Allow indication to the firmware to boot to setup interface" -msgstr "" +msgstr "A firmware-nek jelezhető, hogy a beállítófelületet bootolja" #: ../src/login/org.freedesktop.login1.policy.in.h:54 -#, fuzzy msgid "" "Authentication is required to indicate to the firmware to boot to setup " "interface." -msgstr "Hitelesítés szükséges a helyi gépnév beállításához." +msgstr "" +"Hitelesítés szükséges a firmware-nek jelzéshez, hogy a beállítófelületet " +"bootolja" #: ../src/login/org.freedesktop.login1.policy.in.h:55 msgid "Set a wall message" -msgstr "" +msgstr "Falüzenet beállítása" #: ../src/login/org.freedesktop.login1.policy.in.h:56 -#, fuzzy msgid "Authentication is required to set a wall message" -msgstr "Hitelesítés szükséges a helyi gépnév beállításához." +msgstr "Hitelesítés szükséges a falüzenet beállításához" #: ../src/machine/org.freedesktop.machine1.policy.in.h:1 msgid "Log into a local container" msgstr "Bejelentkezés helyi konténerbe" #: ../src/machine/org.freedesktop.machine1.policy.in.h:2 -#, fuzzy msgid "Authentication is required to log into a local container." msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe." #: ../src/machine/org.freedesktop.machine1.policy.in.h:3 -#, fuzzy msgid "Log into the local host" -msgstr "Bejelentkezés helyi konténerbe" +msgstr "Bejelentkezés a helyi gépre" #: ../src/machine/org.freedesktop.machine1.policy.in.h:4 -#, fuzzy msgid "Authentication is required to log into the local host." -msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe." +msgstr "Hitelesítés szükséges a bejelentkezéshez a helyi gépre." #: ../src/machine/org.freedesktop.machine1.policy.in.h:5 -#, fuzzy msgid "Acquire a shell in a local container" -msgstr "Bejelentkezés helyi konténerbe" +msgstr "Parancsértelmező elérése helyi konténerben" #: ../src/machine/org.freedesktop.machine1.policy.in.h:6 -#, fuzzy msgid "Authentication is required to acquire a shell in a local container." -msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe." +msgstr "Hitelesítés szükséges a parancsértelmező eléréséhez helyi konténerben." #: ../src/machine/org.freedesktop.machine1.policy.in.h:7 msgid "Acquire a shell on the local host" -msgstr "" +msgstr "Parancsértelmező elérése a helyi gépen" #: ../src/machine/org.freedesktop.machine1.policy.in.h:8 -#, fuzzy msgid "Authentication is required to acquire a shell on the local host." -msgstr "Hitelesítés szükséges a helyi gépnév beállításához." +msgstr "Hitelesítés szükséges a parancsértelmező eléréséhez a helyi gépen." #: ../src/machine/org.freedesktop.machine1.policy.in.h:9 -#, fuzzy msgid "Acquire a pseudo TTY in a local container" -msgstr "Bejelentkezés helyi konténerbe" +msgstr "Pszeudoterminál elérése helyi konténerben" #: ../src/machine/org.freedesktop.machine1.policy.in.h:10 -#, fuzzy msgid "" "Authentication is required to acquire a pseudo TTY in a local container." -msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe." +msgstr "Hitelesítés szükséges a pszeudoterminál eléréséhez helyi konténerben." #: ../src/machine/org.freedesktop.machine1.policy.in.h:11 msgid "Acquire a pseudo TTY on the local host" -msgstr "" +msgstr "Pszeudoterminál elérése helyi gépen" #: ../src/machine/org.freedesktop.machine1.policy.in.h:12 -#, fuzzy msgid "Authentication is required to acquire a pseudo TTY on the local host." -msgstr "Hitelesítés szükséges a helyi gépnév beállításához." +msgstr "Hitelesítés szükséges a pszeudoterminál eléréséhez a helyi gépen." #: ../src/machine/org.freedesktop.machine1.policy.in.h:13 msgid "Manage local virtual machines and containers" -msgstr "" +msgstr "Virtuális gépek és konténerek kezelése" #: ../src/machine/org.freedesktop.machine1.policy.in.h:14 -#, fuzzy msgid "" "Authentication is required to manage local virtual machines and containers." -msgstr "Hitelesítés szükséges a helyi gép információinak beállításához." +msgstr "Hitelesítés szükséges helyi virtuális gépek és konténerek kezeléséhez." #: ../src/machine/org.freedesktop.machine1.policy.in.h:15 msgid "Manage local virtual machine and container images" -msgstr "" +msgstr "Helyi virtuális gép és konténer lemezképek kezelése" #: ../src/machine/org.freedesktop.machine1.policy.in.h:16 -#, fuzzy msgid "" "Authentication is required to manage local virtual machine and container " "images." -msgstr "Hitelesítés szükséges a helyi gép információinak beállításához." +msgstr "" +"Hitelesítés szükséges a helyi virtuális gép és konténer lemezképek " +"kezeléséhez." #: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1 msgid "Set system time" @@ -565,37 +552,33 @@ msgid "" "shall be enabled." msgstr "Hitelesítés szükséges a hálózati időszinkronizáció engedélyezéséhez." -#: ../src/core/dbus-unit.c:428 -#, fuzzy +#: ../src/core/dbus-unit.c:449 msgid "Authentication is required to start '$(unit)'." -msgstr "Hitelesítés szükséges a rendszeridő beállításához." +msgstr "Hitelesítés szükséges a következő elindításához: „$(unit)”." -#: ../src/core/dbus-unit.c:429 -#, fuzzy +#: ../src/core/dbus-unit.c:450 msgid "Authentication is required to stop '$(unit)'." -msgstr "Hitelesítés szükséges a rendszeridő beállításához." +msgstr "Hitelesítés szükséges a következő leállításához: „$(unit)”." -#: ../src/core/dbus-unit.c:430 -#, fuzzy +#: ../src/core/dbus-unit.c:451 msgid "Authentication is required to reload '$(unit)'." -msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez." +msgstr "Hitelesítés szükséges a következő újratöltéséhez: „$(unit)”." -#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432 -#, fuzzy +#: ../src/core/dbus-unit.c:452 ../src/core/dbus-unit.c:453 msgid "Authentication is required to restart '$(unit)'." -msgstr "Hitelesítés szükséges a rendszeridő beállításához." +msgstr "Hitelesítés szükséges a következő újraindításához: „$(unit)”." -#: ../src/core/dbus-unit.c:535 -#, fuzzy +#: ../src/core/dbus-unit.c:556 msgid "Authentication is required to kill '$(unit)'." -msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe." +msgstr "Hitelesítés szükséges a következő kilövéséhez: „$(unit)”." -#: ../src/core/dbus-unit.c:565 -#, fuzzy +#: ../src/core/dbus-unit.c:586 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." -msgstr "Hitelesítés szükséges a helyi gépnév beállításához." +msgstr "" +"Hitelesítés szükséges a következő „sikertelen” állapotának törléséhez: " +"„$(unit)”." -#: ../src/core/dbus-unit.c:597 -#, fuzzy +#: ../src/core/dbus-unit.c:618 msgid "Authentication is required to set properties on '$(unit)'." -msgstr "Hitelesítés szükséges a rendszeridő beállításához." +msgstr "" +"Hitelesítés szükséges a következő tulajdonságainak beállításához: „$(unit)”." diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index 75dad228e3..5854caeb51 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -41,6 +41,7 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) { _cleanup_closedir_ DIR *dir = NULL; const char *dirpath; + struct dirent *de; int r; assert(path); @@ -55,18 +56,9 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char return -errno; } - for (;;) { - struct dirent *de; + FOREACH_DIRENT(de, dir, return -errno) { char *p; - errno = 0; - de = readdir(dir); - if (!de && errno != 0) - return -errno; - - if (!de) - break; - if (!dirent_is_file_with_suffix(de, suffix)) continue; @@ -116,17 +108,15 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const STRV_FOREACH(p, dirs) { r = files_add(fh, root, *p, suffix); - if (r == -ENOMEM) { + if (r == -ENOMEM) return r; - } else if (r < 0) - log_debug_errno(r, "Failed to search for files in %s: %m", - *p); + if (r < 0) + log_debug_errno(r, "Failed to search for files in %s, ignoring: %m", *p); } files = hashmap_get_strv(fh); - if (files == NULL) { + if (!files) return -ENOMEM; - } qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp); *strv = files; diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 8d486fcbbd..6e2594d001 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -67,12 +67,14 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PROCESS, ESRCH), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_NAME_SERVERS, EIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_NAME_SERVERS, ESRCH), SD_BUS_ERROR_MAP(BUS_ERROR_INVALID_REPLY, EINVAL), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_RR, ENOENT), SD_BUS_ERROR_MAP(BUS_ERROR_NO_RESOURCES, ENOMEM), SD_BUS_ERROR_MAP(BUS_ERROR_CNAME_LOOP, EDEADLK), SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED), + SD_BUS_ERROR_MAP(BUS_ERROR_CONNECTION_FAILURE, ECONNREFUSED), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SERVICE, EUNATCH), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index d7d210b0c4..9076993f01 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -73,6 +73,7 @@ #define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop" #define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted" #define BUS_ERROR_CONNECTION_FAILURE "org.freedesktop.resolve1.ConnectionFailure" +#define BUS_ERROR_NO_SUCH_SERVICE "org.freedesktop.resolve1.NoSuchService" #define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError." #define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer" diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index dc82769ac6..2cabfeaefa 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -328,8 +328,7 @@ static int parse_address(const char *s, int *family, union in_addr_union *addres return 0; } -static int resolve_record(sd_bus *bus, const char *name) { - +static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char ifname[IF_NAMESIZE] = ""; @@ -343,7 +342,7 @@ static int resolve_record(sd_bus *bus, const char *name) { if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname)) return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex); - log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type), isempty(ifname) ? "*" : ifname); + log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(ifname) ? "*" : ifname); r = sd_bus_message_new_method_call( bus, @@ -355,7 +354,7 @@ static int resolve_record(sd_bus *bus, const char *name) { if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags); + r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, class, type, arg_flags); if (r < 0) return bus_log_create_error(r); @@ -442,6 +441,127 @@ static int resolve_record(sd_bus *bus, const char *name) { return 0; } +static int resolve_rfc4501(sd_bus *bus, const char *name) { + uint16_t type = 0, class = 0; + const char *p, *q, *n; + int r; + + assert(bus); + assert(name); + assert(startswith(name, "dns:")); + + /* Parse RFC 4501 dns: URIs */ + + p = name + 4; + + if (p[0] == '/') { + const char *e; + + if (p[1] != '/') + goto invalid; + + e = strchr(p + 2, '/'); + if (!e) + goto invalid; + + if (e != p + 2) + log_warning("DNS authority specification not supported; ignoring specified authority."); + + p = e + 1; + } + + q = strchr(p, '?'); + if (q) { + n = strndupa(p, q - p); + q++; + + for (;;) { + const char *f; + + f = startswith_no_case(q, "class="); + if (f) { + _cleanup_free_ char *t = NULL; + const char *e; + + if (class != 0) { + log_error("DNS class specified twice."); + return -EINVAL; + } + + e = strchrnul(f, ';'); + t = strndup(f, e - f); + if (!t) + return log_oom(); + + r = dns_class_from_string(t); + if (r < 0) { + log_error("Unknown DNS class %s.", t); + return -EINVAL; + } + + class = r; + + if (*e == ';') { + q = e + 1; + continue; + } + + break; + } + + f = startswith_no_case(q, "type="); + if (f) { + _cleanup_free_ char *t = NULL; + const char *e; + + if (type != 0) { + log_error("DNS type specified twice."); + return -EINVAL; + } + + e = strchrnul(f, ';'); + t = strndup(f, e - f); + if (!t) + return log_oom(); + + r = dns_type_from_string(t); + if (r < 0) { + log_error("Unknown DNS type %s.", t); + return -EINVAL; + } + + type = r; + + if (*e == ';') { + q = e + 1; + continue; + } + + break; + } + + goto invalid; + } + } 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; + + return resolve_record(bus, n, class, type); + +invalid: + log_error("Invalid DNS URI: %s", name); + return -EINVAL; +} + static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) { const char *canonical_name, *canonical_type, *canonical_domain; _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; @@ -1009,6 +1129,9 @@ static int parse_argv(int argc, char *argv[]) { if (arg_type != 0 && arg_class == 0) arg_class = DNS_CLASS_IN; + if (arg_class != 0 && arg_type == 0) + arg_type = DNS_TYPE_A; + return 1 /* work to do */; } @@ -1042,11 +1165,15 @@ int main(int argc, char **argv) { int family, ifindex, k; union in_addr_union a; - k = parse_address(argv[optind], &family, &a, &ifindex); - if (k >= 0) - k = resolve_address(bus, family, &a, ifindex); - else - k = resolve_host(bus, argv[optind]); + if (startswith(argv[optind], "dns:")) + k = resolve_rfc4501(bus, argv[optind]); + else { + k = parse_address(argv[optind], &family, &a, &ifindex); + if (k >= 0) + k = resolve_address(bus, family, &a, ifindex); + else + k = resolve_host(bus, argv[optind]); + } if (r == 0) r = k; @@ -1065,7 +1192,7 @@ int main(int argc, char **argv) { while (argv[optind]) { int k; - k = resolve_record(bus, argv[optind]); + k = resolve_record(bus, argv[optind], arg_class, arg_type); if (r == 0) r = k; diff --git a/src/resolve/RFCs b/src/resolve/RFCs new file mode 100644 index 0000000000..ccc7f0d640 --- /dev/null +++ b/src/resolve/RFCs @@ -0,0 +1,59 @@ +Y = Comprehensively Implemented, to the point appropriate for resolved +D = Comprehensively Implemented, by a dependency of resolved +! = Missing and something we might want to implement +~ = Needs no explicit support or doesn't apply +? = Is this relevant today? + = We are working on this + +Y https://tools.ietf.org/html/rfc1034 → DOMAIN NAMES - CONCEPTS AND FACILITIES +Y https://tools.ietf.org/html/rfc1035 → DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION +? https://tools.ietf.org/html/rfc1101 → DNS Encoding of Network Names and Other Types +Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- Application and Support + https://tools.ietf.org/html/rfc1464 → Using the Domain Name System To Store Arbitrary String Attributes +Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes +Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System +Y https://tools.ietf.org/html/rfc2181 → Clarifications to the DNS Specification + https://tools.ietf.org/html/rfc2308 → Negative Caching of DNS Queries (DNS NCACHE) +Y https://tools.ietf.org/html/rfc2782 → A DNS RR for specifying the location of services (DNS SRV) +D https://tools.ietf.org/html/rfc3492 → Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA) +Y https://tools.ietf.org/html/rfc3596 → DNS Extensions to Support IP Version 6 +Y https://tools.ietf.org/html/rfc3597 → Handling of Unknown DNS Resource Record (RR) Types + https://tools.ietf.org/html/rfc4033 → DNS Security Introduction and Requirements + https://tools.ietf.org/html/rfc4034 → Resource Records for the DNS Security Extensions + https://tools.ietf.org/html/rfc4035 → Protocol Modifications for the DNS Security Extensions +! https://tools.ietf.org/html/rfc4183 → A Suggested Scheme for DNS Resolution of Networks and Gateways +Y https://tools.ietf.org/html/rfc4255 → Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints +Y https://tools.ietf.org/html/rfc4343 → Domain Name System (DNS) Case Insensitivity Clarification +~ https://tools.ietf.org/html/rfc4470 → Minimally Covering NSEC Records and DNSSEC On-line Signing +Y https://tools.ietf.org/html/rfc4501 → Domain Name System Uniform Resource Identifiers +Y https://tools.ietf.org/html/rfc4509 → Use of SHA-256 in DNSSEC Delegation Signer (DS) Resource Records (RRs) +~ https://tools.ietf.org/html/rfc4592 → The Role of Wildcards in the Domain Name System +~ https://tools.ietf.org/html/rfc4697 → Observed DNS Resolution Misbehavior +Y https://tools.ietf.org/html/rfc4795 → Link-Local Multicast Name Resolution (LLMNR) +! https://tools.ietf.org/html/rfc5011 → Automated Updates of DNS Security (DNSSEC) Trust Anchors + https://tools.ietf.org/html/rfc5155 → DNS Security (DNSSEC) Hashed Authenticated Denial of Existence + https://tools.ietf.org/html/rfc5452 → Measures for Making DNS More Resilient against Forged Answers +Y https://tools.ietf.org/html/rfc5702 → Use of SHA-2 Algorithms with RSA in DNSKEY and RRSIG Resource Records for DNSSEC +Y https://tools.ietf.org/html/rfc5890 → Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework +Y https://tools.ietf.org/html/rfc5891 → Internationalized Domain Names in Applications (IDNA): Protocol +Y https://tools.ietf.org/html/rfc5966 → DNS Transport over TCP - Implementation Requirements +Y https://tools.ietf.org/html/rfc6303 → Locally Served DNS Zones + https://tools.ietf.org/html/rfc6604 → xNAME RCODE and Status Bits Clarification +Y https://tools.ietf.org/html/rfc6605 → Elliptic Curve Digital Signature Algorithm (DSA) for DNSSEC + https://tools.ietf.org/html/rfc6672 → DNAME Redirection in the DNS + https://tools.ietf.org/html/rfc6731 → Improved Recursive DNS Server Selection for Multi-Interfaced Nodes +Y https://tools.ietf.org/html/rfc6761 → Special-Use Domain Names + https://tools.ietf.org/html/rfc6762 → Multicast DNS + https://tools.ietf.org/html/rfc6763 → DNS-Based Service Discovery + https://tools.ietf.org/html/rfc6781 → DNSSEC Operational Practices, Version 2 + https://tools.ietf.org/html/rfc6840 → Clarifications and Implementation Notes for DNS Security (DNSSEC) +Y https://tools.ietf.org/html/rfc6891 → Extension Mechanisms for DNS (EDNS(0)) +Y https://tools.ietf.org/html/rfc6944 → Applicability Statement: DNS Security (DNSSEC) DNSKEY Algorithm Implementation Status +Y https://tools.ietf.org/html/rfc6975 → Signaling Cryptographic Algorithm Understanding in DNS Security Extensions (DNSSEC) + https://tools.ietf.org/html/rfc7129 → Authenticated Denial of Existence in the DNS +! https://tools.ietf.org/html/rfc7646 → Definition and Use of DNSSEC Negative Trust Anchors +~ https://tools.ietf.org/html/rfc7719 → DNS Terminology + +Also relevant: + + https://www.iab.org/documents/correspondence-reports-documents/2013-2/iab-statement-dotless-domains-considered-harmful/ diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 5c7893d01c..4d4c1ca014 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -976,6 +976,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { return; if (q->answer) { + bool has_root_domain = false; DnsResourceRecord *rr; int ifindex; @@ -989,6 +990,11 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { if (rr->key->type != DNS_TYPE_SRV) continue; + if (dns_name_is_root(rr->srv.name)) { + has_root_domain = true; + continue; + } + if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { q->block_all_complete ++; r = resolve_service_hostname(q, rr, ifindex); @@ -1000,6 +1006,18 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { found++; } + + if (has_root_domain && found == 0) { + /* If there's exactly one SRV RR and it uses + * the root domain as host name, then the + * service is explicitly not offered on the + * domain. Report this as a recognizable + * error. See RFC 2782, Section "Usage + * Rules". */ + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_question_first_name(q->question)); + goto finish; + } + } if (found <= 0) { diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 399b518644..445999f545 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -125,10 +125,13 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFl if (r < 0) return r; if (r > 0) { - /* Entry already exists, keep the entry with - * the higher RR, or the one with TTL 0 */ + /* Don't mix contradicting TTLs (see below) */ + if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0)) + return -EINVAL; - if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) { + /* Entry already exists, keep the entry with + * the higher RR. */ + if (rr->ttl > a->items[i].rr->ttl) { dns_resource_record_ref(rr); dns_resource_record_unref(a->items[i].rr); a->items[i].rr = rr; @@ -137,6 +140,21 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFl a->items[i].flags |= flags; return 0; } + + r = dns_resource_key_equal(a->items[i].rr->key, rr->key); + if (r < 0) + return r; + if (r > 0) { + /* There's already an RR of the same RRset in + * place! Let's see if the TTLs more or less + * match. We don't really care if they match + * precisely, but we do care whether one is 0 + * and the other is not. See RFC 2181, Section + * 5.2.*/ + + if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0)) + return -EINVAL; + } } return dns_answer_add_raw(a, rr, ifindex, flags); diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 3193985542..1c7dd56b3b 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -26,7 +26,8 @@ #include "resolved-dns-packet.h" #include "string-util.h" -/* Never cache more than 4K entries */ +/* Never cache more than 4K entries. RFC 1536, Section 5 suggests to + * leave DNS caches unbounded, but that's crazy. */ #define CACHE_MAX 4096 /* We never keep any item longer than 2h in our cache */ diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index a3aa90e98d..1182201b7d 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -35,14 +35,14 @@ * * TODO: * - * - Make trust anchor store read additional DS+DNSKEY data from disk * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing) * - multi-label zone compatibility * - cname/dname compatibility * - per-interface DNSSEC setting * - nxdomain on qname * - retry on failed validation? - * - DSA support? + * - DNSSEC key revocation support? https://tools.ietf.org/html/rfc5011 + * - when doing negative caching, use NSEC/NSEC3 RR instead of SOA for TTL * * */ @@ -52,6 +52,9 @@ /* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */ #define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE) +/* Maximum number of NSEC3 iterations we'll do. */ +#define NSEC3_ITERATIONS_MAX 2048 + /* * The DNSSEC Chain of trust: * @@ -237,8 +240,8 @@ static int dnssec_rsa_verify( exponent = (uint8_t*) dnskey->dnskey.key + 3; exponent_size = - ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) | - ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]); + ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) | + ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]); if (exponent_size < 256) return -EINVAL; @@ -458,7 +461,15 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) { static int algorithm_to_gcrypt_md(uint8_t algorithm) { - /* Translates a DNSSEC signature algorithm into a gcrypt digest identifier */ + /* Translates a DNSSEC signature algorithm into a gcrypt + * digest identifier. + * + * Note that we implement all algorithms listed as "Must + * implement" and "Recommended to Implement" in RFC6944. We + * don't implement any algorithms that are listed as + * "Optional" or "Must Not Implement". Specifically, we do not + * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and + * GOST-ECC. */ switch (algorithm) { @@ -516,9 +527,6 @@ int dnssec_verify_rrset( if (md_algorithm < 0) return md_algorithm; - if (a->n_rrs > VERIFY_RRS_MAX) - return -E2BIG; - r = dnssec_rrsig_expired(rrsig, realtime); if (r < 0) return r; @@ -543,6 +551,9 @@ int dnssec_verify_rrset( return r; list[n++] = rr; + + if (n > VERIFY_RRS_MAX) + return -E2BIG; } if (n <= 0) @@ -1048,7 +1059,21 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ return 0; } -int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { +static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) { + + /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */ + + switch (algorithm) { + + case NSEC3_ALGORITHM_SHA1: + return GCRY_MD_SHA1; + + default: + return -EOPNOTSUPP; + } +} + +int dnssec_nsec3_hash(const DnsResourceRecord *nsec3, const char *name, void *ret) { uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX]; gcry_md_hd_t md = NULL; size_t hash_size; @@ -1064,7 +1089,10 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { if (nsec3->key->type != DNS_TYPE_NSEC3) return -EINVAL; - algorithm = digest_to_gcrypt_md(nsec3->nsec3.algorithm); + if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) + return -EOPNOTSUPP; + + algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm); if (algorithm < 0) return algorithm; @@ -1129,6 +1157,13 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourc if (!IN_SET(rr->nsec3.flags, 0, 1)) return 0; + /* Ignore NSEC3 RRs whose algorithm we don't know */ + if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0) + return 0; + /* Ignore NSEC3 RRs with an excessive number of required iterations */ + if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX) + return 0; + if (!nsec3) return 1; @@ -1165,21 +1200,62 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourc return dns_name_equal(a, b); } -static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) { - _cleanup_free_ char *next_closer_domain = NULL, *l = NULL; +static int nsec3_hashed_domain(const DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) { + _cleanup_free_ char *l = NULL, *hashed_domain = NULL; uint8_t hashed[DNSSEC_HASH_SIZE_MAX]; - const char *suffix, *p, *pp = NULL; - DnsResourceRecord *rr, *suffix_rr; + int hashed_size; + + assert(nsec3); + assert(domain); + assert(zone); + assert(ret); + + hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed); + if (hashed_size < 0) + return hashed_size; + + l = base32hexmem(hashed, hashed_size, false); + if (!l) + return -ENOMEM; + + hashed_domain = strjoin(l, ".", zone, NULL); + if (!hashed_domain) + return -ENOMEM; + + *ret = hashed_domain; + hashed_domain = NULL; + + return hashed_size; +} + +/* See RFC 5155, Section 8 + * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest + * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there + * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that + * matches the wildcard domain. + * + * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or + * that there is no proof either way. The latter is the case if a the proof of non-existence of a given + * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records + * to conclude anything we indicate this by returning NO_RR. */ +static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) { + _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL; + const char *zone, *p, *pp = NULL; + DnsResourceRecord *rr, *enclosure_rr, *suffix_rr, *wildcard_rr = NULL; DnsAnswerFlags flags; int hashed_size, r; - bool a; + bool a, no_closer = false, no_wildcard = false, optout = false; assert(key); assert(result); assert(authenticated); - /* First step, look for the longest common suffix we find with any NSEC3 RR in the response. */ - suffix = DNS_RESOURCE_KEY_NAME(key); + /* First step, find the zone name and the NSEC3 parameters of the zone. + * it is sufficient to look for the longest common suffix we find with + * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3 + * records from a given zone in a response must use the same + * parameters. */ + zone = DNS_RESOURCE_KEY_NAME(key); for (;;) { DNS_ANSWER_FOREACH_FLAGS(suffix_rr, flags, answer) { r = nsec3_is_good(suffix_rr, flags, NULL); @@ -1188,15 +1264,15 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR if (r == 0) continue; - r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, suffix); + r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, zone); if (r < 0) return r; if (r > 0) - goto found_suffix; + goto found_zone; } /* Strip one label from the front */ - r = dns_name_parent(&suffix); + r = dns_name_parent(&zone); if (r < 0) return r; if (r == 0) @@ -1206,13 +1282,13 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR *result = DNSSEC_NSEC_NO_RR; return 0; -found_suffix: +found_zone: /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */ p = DNS_RESOURCE_KEY_NAME(key); for (;;) { - _cleanup_free_ char *hashed_domain = NULL, *label = NULL; + _cleanup_free_ char *hashed_domain = NULL; - hashed_size = dnssec_nsec3_hash(suffix_rr, p, hashed); + hashed_size = nsec3_hashed_domain(suffix_rr, p, zone, &hashed_domain); if (hashed_size == -EOPNOTSUPP) { *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM; return 0; @@ -1220,26 +1296,18 @@ found_suffix: if (hashed_size < 0) return hashed_size; - label = base32hexmem(hashed, hashed_size, false); - if (!label) - return -ENOMEM; - - hashed_domain = strjoin(label, ".", suffix, NULL); - if (!hashed_domain) - return -ENOMEM; - - DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { + DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) { - r = nsec3_is_good(rr, flags, suffix_rr); + r = nsec3_is_good(enclosure_rr, flags, suffix_rr); if (r < 0) return r; if (r == 0) continue; - if (rr->nsec3.next_hashed_name_size != (size_t) hashed_size) + if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain); + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain); if (r < 0) return r; if (r > 0) { @@ -1269,37 +1337,48 @@ found_closest_encloser: /* We found a closest encloser in 'p'; next closer is 'pp' */ /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */ - if (bitmap_isset(rr->nsec3.types, DNS_TYPE_DNAME)) + if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME)) return -EBADMSG; /* Ensure that this data is from the delegated domain * (i.e. originates from the "lower" DNS server), and isn't * just glue records (i.e. doesn't originate from the "upper" * DNS server). */ - if (bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) && - !bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA)) + if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) && + !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA)) return -EBADMSG; if (!pp) { /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */ - *result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA; + if (bitmap_isset(enclosure_rr->nsec3.types, key->type)) + *result = DNSSEC_NSEC_FOUND; + else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME)) + *result = DNSSEC_NSEC_CNAME; + else + *result = DNSSEC_NSEC_NODATA; + *authenticated = a; + return 0; } - r = dnssec_nsec3_hash(rr, pp, hashed); + /* Prove that there is no next closer and whether or not there is a wildcard domain. */ + + wildcard = strappend("*.", p); + if (!wildcard) + return -ENOMEM; + + r = nsec3_hashed_domain(enclosure_rr, wildcard, zone, &wildcard_domain); if (r < 0) return r; if (r != hashed_size) return -EBADMSG; - l = base32hexmem(hashed, hashed_size, false); - if (!l) - return -ENOMEM; - - next_closer_domain = strjoin(l, ".", p, NULL); - if (!next_closer_domain) - return -ENOMEM; + r = nsec3_hashed_domain(enclosure_rr, pp, zone, &next_closer_domain); + if (r < 0) + return r; + if (r != hashed_size) + return -EBADMSG; DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL; @@ -1314,7 +1393,7 @@ found_closest_encloser: if (!label) return -ENOMEM; - next_hashed_domain = strjoin(label, ".", p, NULL); + next_hashed_domain = strjoin(label, ".", zone, NULL); if (!next_hashed_domain) return -ENOMEM; @@ -1323,16 +1402,82 @@ found_closest_encloser: return r; if (r > 0) { if (rr->nsec3.flags & 1) - *result = DNSSEC_NSEC_OPTOUT; - else - *result = DNSSEC_NSEC_NXDOMAIN; + optout = true; - *authenticated = a && (flags & DNS_ANSWER_AUTHENTICATED); - return 1; + a = a && (flags & DNS_ANSWER_AUTHENTICATED); + + no_closer = true; + } + + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain); + if (r < 0) + return r; + if (r > 0) { + a = a && (flags & DNS_ANSWER_AUTHENTICATED); + + wildcard_rr = rr; + } + + r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain); + if (r < 0) + return r; + if (r > 0) { + if (rr->nsec3.flags & 1) + /* This only makes sense if we have a wildcard delegation, which is + * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on + * this not happening, so hence cannot simply conclude NXDOMAIN as + * we would wish */ + optout = true; + + a = a && (flags & DNS_ANSWER_AUTHENTICATED); + + no_wildcard = true; } } - *result = DNSSEC_NSEC_NO_RR; + if (wildcard_rr && no_wildcard) + return -EBADMSG; + + if (!no_closer) { + *result = DNSSEC_NSEC_NO_RR; + + return 0; + } + + if (wildcard_rr) { + /* A wildcard exists that matches our query. */ + if (optout) + /* This is not specified in any RFC to the best of my knowledge, but + * if the next closer enclosure is covered by an opt-out NSEC3 RR + * it means that we cannot prove that the source of synthesis is + * correct, as there may be a closer match. */ + *result = DNSSEC_NSEC_OPTOUT; + else if (bitmap_isset(wildcard_rr->nsec3.types, key->type)) + *result = DNSSEC_NSEC_FOUND; + else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME)) + *result = DNSSEC_NSEC_CNAME; + else + *result = DNSSEC_NSEC_NODATA; + } else { + if (optout) + /* The RFC only specifies that we have to care for optout for NODATA for + * DS records. However, children of an insecure opt-out delegation should + * also be considered opt-out, rather than verified NXDOMAIN. + * Note that we do not require a proof of wildcard non-existence if the + * next closer domain is covered by an opt-out, as that would not provide + * any additional information. */ + *result = DNSSEC_NSEC_OPTOUT; + else if (no_wildcard) + *result = DNSSEC_NSEC_NXDOMAIN; + else { + *result = DNSSEC_NSEC_NO_RR; + + return 0; + } + } + + *authenticated = a; + return 0; } @@ -1361,7 +1506,12 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r if (r < 0) return r; if (r > 0) { - *result = bitmap_isset(rr->nsec.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA; + if (bitmap_isset(rr->nsec.types, key->type)) + *result = DNSSEC_NSEC_FOUND; + else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME)) + *result = DNSSEC_NSEC_CNAME; + else + *result = DNSSEC_NSEC_NODATA; *authenticated = flags & DNS_ANSWER_AUTHENTICATED; return 0; } diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h index d7aecbce13..f106875027 100644 --- a/src/resolve/resolved-dns-dnssec.h +++ b/src/resolve/resolved-dns-dnssec.h @@ -87,10 +87,11 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey); int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max); -int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret); +int dnssec_nsec3_hash(const DnsResourceRecord *nsec3, const char *name, void *ret); typedef enum DnssecNsecResult { DNSSEC_NSEC_NO_RR, /* No suitable NSEC/NSEC3 RR found */ + DNSSEC_NSEC_CNAME, /* Would be NODATA, but for the existence of a CNAME RR */ DNSSEC_NSEC_UNSUPPORTED_ALGORITHM, DNSSEC_NSEC_NXDOMAIN, DNSSEC_NSEC_NODATA, diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 5f79701296..4750bf1f5d 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -499,7 +499,7 @@ int dns_packet_append_name( saved_size = p->size; - while (*name) { + while (!dns_name_is_root(name)) { const char *z = name; char label[DNS_LABEL_MAX]; size_t n = 0; @@ -723,7 +723,40 @@ int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, si goto fail; /* RDLENGTH */ - r = dns_packet_append_uint16(p, 0, NULL); + + if (edns0_do) { + /* If DO is on, also append RFC6975 Algorithm data */ + + static const uint8_t rfc6975[] = { + + 0, 5, /* OPTION_CODE: DAU */ + 0, 6, /* LIST_LENGTH */ + DNSSEC_ALGORITHM_RSASHA1, + DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1, + DNSSEC_ALGORITHM_RSASHA256, + DNSSEC_ALGORITHM_RSASHA512, + DNSSEC_ALGORITHM_ECDSAP256SHA256, + DNSSEC_ALGORITHM_ECDSAP384SHA384, + + 0, 6, /* OPTION_CODE: DHU */ + 0, 3, /* LIST_LENGTH */ + DNSSEC_DIGEST_SHA1, + DNSSEC_DIGEST_SHA256, + DNSSEC_DIGEST_SHA384, + + 0, 7, /* OPTION_CODE: N3U */ + 0, 1, /* LIST_LENGTH */ + NSEC3_ALGORITHM_SHA1, + }; + + r = dns_packet_append_uint16(p, sizeof(rfc6975), NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rfc6975, sizeof(rfc6975), NULL); + } else + r = dns_packet_append_uint16(p, 0, NULL); + if (r < 0) goto fail; @@ -1580,6 +1613,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl if (r < 0) goto fail; + /* RFC 2181, Section 8, suggests to + * treat a TTL with the MSB set as a zero TTL. */ + if (rr->ttl & UINT32_C(0x80000000)) + rr->ttl = 0; + r = dns_packet_read_uint16(p, &rdlength, NULL); if (r < 0) goto fail; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index a09ace5b75..6821be73e4 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -119,7 +119,17 @@ static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) { #define DNS_PACKET_RA(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 7) & 1) #define DNS_PACKET_AD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 5) & 1) #define DNS_PACKET_CD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 4) & 1) -#define DNS_PACKET_RCODE(p) (be16toh(DNS_PACKET_HEADER(p)->flags) & 15) + +static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) { + uint16_t rcode; + + if (p->opt) + rcode = (uint16_t) (p->opt->ttl >> 24); + else + rcode = 0; + + return rcode | (be16toh(DNS_PACKET_HEADER(p)->flags) & 15); +} /* LLMNR defines some bits differently */ #define DNS_PACKET_LLMNR_C(p) DNS_PACKET_AA(p) @@ -203,6 +213,7 @@ static inline bool DNS_PACKET_SHALL_CACHE(DnsPacket *p) { return in_addr_is_localhost(p->family, &p->sender) == 0; } +/* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 */ enum { DNS_RCODE_SUCCESS = 0, DNS_RCODE_FORMERR = 1, diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index d479de7125..76723ec4d0 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -308,9 +308,12 @@ const struct hash_ops dns_resource_key_hash_ops = { int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) { char cbuf[strlen("CLASS") + DECIMAL_STR_MAX(uint16_t)], tbuf[strlen("TYPE") + DECIMAL_STR_MAX(uint16_t)]; - const char *c, *t; + const char *c, *t, *n; char *s; + /* If we cannot convert the CLASS/TYPE into a known string, + use the format recommended by RFC 3597, Section 5. */ + c = dns_class_to_string(key->class); if (!c) { sprintf(cbuf, "CLASS%u", key->class); @@ -323,7 +326,8 @@ int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) { t = tbuf; } - if (asprintf(&s, "%s. %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0) + n = DNS_RESOURCE_KEY_NAME(key); + if (asprintf(&s, "%s%s %s %-5s", n, endswith(n, ".") ? "" : ".", c, t) < 0) return -ENOMEM; *ret = s; @@ -912,20 +916,21 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { break; case DNS_TYPE_DNSKEY: { - const char *alg; + _cleanup_free_ char *alg = NULL; - alg = dnssec_algorithm_to_string(rr->dnskey.algorithm); + r = dnssec_algorithm_to_string_alloc(rr->dnskey.algorithm, &alg); + if (r < 0) + return NULL; t = base64mem(rr->dnskey.key, rr->dnskey.key_size); if (!t) return NULL; - r = asprintf(&s, "%s %u %u %.*s%.*u %s", + r = asprintf(&s, "%s %u %u %s %s", k, rr->dnskey.flags, rr->dnskey.protocol, - alg ? -1 : 0, alg, - alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm, + alg, t); if (r < 0) return NULL; @@ -933,11 +938,15 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { } case DNS_TYPE_RRSIG: { - const char *type, *alg; + _cleanup_free_ char *alg = NULL; char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1]; + const char *type; type = dns_type_to_string(rr->rrsig.type_covered); - alg = dnssec_algorithm_to_string(rr->rrsig.algorithm); + + r = dnssec_algorithm_to_string_alloc(rr->rrsig.algorithm, &alg); + if (r < 0) + return NULL; t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size); if (!t) @@ -954,12 +963,11 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { /* TYPE?? follows * http://tools.ietf.org/html/rfc3597#section-5 */ - r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %s %s %u %s %s", + r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s %s", k, type ?: "TYPE", type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered, - alg ? -1 : 0, alg, - alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm, + alg, rr->rrsig.labels, rr->rrsig.original_ttl, expiration, @@ -1021,6 +1029,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { if (!t) return NULL; + /* Format as documented in RFC 3597, Section 5 */ r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t); if (r < 0) return NULL; @@ -1109,6 +1118,7 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) { } static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = { + /* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */ [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5", [DNSSEC_ALGORITHM_DH] = "DH", [DNSSEC_ALGORITHM_DSA] = "DSA", @@ -1118,14 +1128,20 @@ static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1", [DNSSEC_ALGORITHM_RSASHA256] = "RSASHA256", [DNSSEC_ALGORITHM_RSASHA512] = "RSASHA512", + [DNSSEC_ALGORITHM_ECC_GOST] = "ECC-GOST", + [DNSSEC_ALGORITHM_ECDSAP256SHA256] = "ECDSAP256SHA256", + [DNSSEC_ALGORITHM_ECDSAP384SHA384] = "ECDSAP384SHA384", [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT", [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS", [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID", }; -DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int); +DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_algorithm, int, 255); static const char* const dnssec_digest_table[_DNSSEC_DIGEST_MAX_DEFINED] = { - [DNSSEC_DIGEST_SHA1] = "SHA1", - [DNSSEC_DIGEST_SHA256] = "SHA256", + /* Names as listed on https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */ + [DNSSEC_DIGEST_SHA1] = "SHA-1", + [DNSSEC_DIGEST_SHA256] = "SHA-256", + [DNSSEC_DIGEST_GOST_R_34_11_94] = "GOST_R_34.11-94", + [DNSSEC_DIGEST_SHA384] = "SHA-384", }; -DEFINE_STRING_TABLE_LOOKUP(dnssec_digest, int); +DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_digest, int, 255); diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index fccc4dba6a..90c3629166 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -51,8 +51,9 @@ enum { DNSSEC_ALGORITHM_RSASHA1, DNSSEC_ALGORITHM_DSA_NSEC3_SHA1, DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1, - DNSSEC_ALGORITHM_RSASHA256 = 8, /* RFC 5702 */ - DNSSEC_ALGORITHM_RSASHA512 = 10, /* RFC 5702 */ + DNSSEC_ALGORITHM_RSASHA256 = 8, /* RFC 5702 */ + DNSSEC_ALGORITHM_RSASHA512 = 10, /* RFC 5702 */ + DNSSEC_ALGORITHM_ECC_GOST = 12, /* RFC 5933 */ DNSSEC_ALGORITHM_ECDSAP256SHA256 = 13, /* RFC 6605 */ DNSSEC_ALGORITHM_ECDSAP384SHA384 = 14, /* RFC 6605 */ DNSSEC_ALGORITHM_INDIRECT = 252, @@ -65,11 +66,19 @@ enum { * https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */ enum { DNSSEC_DIGEST_SHA1 = 1, - DNSSEC_DIGEST_SHA256 = 2, - DNSSEC_DIGEST_SHA384 = 4, + DNSSEC_DIGEST_SHA256 = 2, /* RFC 4509 */ + DNSSEC_DIGEST_GOST_R_34_11_94 = 3, /* RFC 5933 */ + DNSSEC_DIGEST_SHA384 = 4, /* RFC 6605 */ _DNSSEC_DIGEST_MAX_DEFINED }; +/* DNSSEC NSEC3 hash algorithms, see + * https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml */ +enum { + NSEC3_ALGORITHM_SHA1 = 1, + _NSEC3_ALGORITHM_MAX_DEFINED +}; + struct DnsResourceKey { unsigned n_ref; uint16_t class, type; @@ -155,6 +164,7 @@ struct DnsResourceRecord { char *exchange; } mx; + /* https://tools.ietf.org/html/rfc1876 */ struct { uint8_t version; uint8_t size; @@ -165,14 +175,6 @@ struct DnsResourceRecord { uint32_t altitude; } loc; - struct { - uint16_t key_tag; - uint8_t algorithm; - uint8_t digest_type; - void *digest; - size_t digest_size; - } ds; - /* https://tools.ietf.org/html/rfc4255#section-3.1 */ struct { uint8_t algorithm; @@ -210,6 +212,15 @@ struct DnsResourceRecord { Bitmap *types; } nsec; + /* https://tools.ietf.org/html/rfc4034#section-5.1 */ + struct { + uint16_t key_tag; + uint8_t algorithm; + uint8_t digest_type; + void *digest; + size_t digest_size; + } ds; + struct { uint8_t algorithm; uint8_t flags; @@ -268,8 +279,8 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b); extern const struct hash_ops dns_resource_key_hash_ops; -const char* dnssec_algorithm_to_string(int i) _const_; +int dnssec_algorithm_to_string_alloc(int i, char **ret); int dnssec_algorithm_from_string(const char *s) _pure_; -const char *dnssec_digest_to_string(int i) _const_; +int dnssec_digest_to_string_alloc(int i, char **ret); int dnssec_digest_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index a57385d47d..fbd1c27c14 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -253,7 +253,7 @@ void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel level, usec_ if (s->max_rtt < rtt) { s->max_rtt = rtt; - s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC); + s->resend_timeout = CLAMP(s->max_rtt * 2, DNS_TIMEOUT_MIN_USEC, DNS_TIMEOUT_MAX_USEC); } } diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index fb95554db3..f7671e070f 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -588,6 +588,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { break; case DNS_PROTOCOL_DNS: + /* Note that we do not need to verify the + * addresses/port numbers of incoming traffic, as we + * invoked connect() on our UDP socket in which case + * the kernel already does the needed verification for + * us. */ break; default: @@ -887,7 +892,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { /* Check the trust anchor. Do so only on classic DNS, since DNSSEC does not apply otherwise. */ if (t->scope->protocol == DNS_PROTOCOL_DNS) { - r = dns_trust_anchor_lookup(&t->scope->manager->trust_anchor, t->key, &t->answer); + r = dns_trust_anchor_lookup_positive(&t->scope->manager->trust_anchor, t->key, &t->answer); if (r < 0) return r; if (r > 0) { @@ -1265,7 +1270,7 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey * return 0; /* Try to get the data from the trust anchor */ - r = dns_trust_anchor_lookup(&t->scope->manager->trust_anchor, key, &a); + r = dns_trust_anchor_lookup_positive(&t->scope->manager->trust_anchor, key, &a); if (r < 0) return r; if (r > 0) { @@ -1323,6 +1328,14 @@ static int dns_transaction_has_unsigned_negative_answer(DnsTransaction *t) { if (r > 0) return false; + /* Is this key explicitly listed as a negative trust anchor? + * If so, it's nothing we need to care about */ + r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, DNS_RESOURCE_KEY_NAME(t->key)); + if (r < 0) + return r; + if (r > 0) + return false; + /* The answer does not contain any RRs that match to the * question. If so, let's see if there are any NSEC/NSEC3 RRs * included. If not, the answer is unsigned. */ @@ -1407,6 +1420,13 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (dns_type_is_pseudo(rr->key->type)) continue; + /* If this RR is in the negative trust anchor, we don't need to validate it. */ + r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, DNS_RESOURCE_KEY_NAME(rr->key)); + if (r < 0) + return r; + if (r > 0) + continue; + switch (rr->key->type) { case DNS_TYPE_RRSIG: { @@ -1751,6 +1771,12 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * if (dns_type_is_pseudo(rr->key->type)) return -EINVAL; + r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, DNS_RESOURCE_KEY_NAME(rr->key)); + if (r < 0) + return r; + if (r > 0) + return false; + switch (rr->key->type) { case DNS_TYPE_RRSIG: @@ -1888,6 +1914,12 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { if (dns_type_is_pseudo(t->key->type)) return -EINVAL; + r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, DNS_RESOURCE_KEY_NAME(t->key)); + if (r < 0) + return r; + if (r > 0) + return false; + name = DNS_RESOURCE_KEY_NAME(t->key); if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) { @@ -1939,6 +1971,12 @@ static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRe * the specified RRset is authenticated (i.e. has a matching * DS RR). */ + r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, DNS_RESOURCE_KEY_NAME(rr->key)); + if (r < 0) + return r; + if (r > 0) + return false; + DNS_ANSWER_FOREACH(rrsig, t->answer) { DnsTransaction *dt; Iterator i; @@ -2300,6 +2338,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { break; case DNSSEC_NSEC_FOUND: + case DNSSEC_NSEC_CNAME: /* NSEC says it needs to be there, but we couldn't find it? Bummer! */ t->answer_dnssec_result = DNSSEC_NSEC_MISMATCH; break; diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 208b7fefc4..03c5b9406e 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -20,25 +20,37 @@ ***/ #include "alloc-util.h" +#include "conf-files.h" +#include "def.h" +#include "dns-domain.h" +#include "fd-util.h" +#include "fileio.h" +#include "hexdecoct.h" +#include "parse-util.h" #include "resolved-dns-trust-anchor.h" +#include "set.h" +#include "string-util.h" +#include "strv.h" -/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml */ +static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("systemd/dnssec-trust-anchors.d"); + +/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */ static const uint8_t root_digest[] = { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60, 0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 }; -int dns_trust_anchor_load(DnsTrustAnchor *d) { +static int dns_trust_anchor_add_builtin(DnsTrustAnchor *d) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; int r; assert(d); - r = hashmap_ensure_allocated(&d->by_key, &dns_resource_key_hash_ops); + r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops); if (r < 0) return r; - if (hashmap_get(d->by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, "."))) + if (hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, "."))) return 0; /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */ @@ -62,11 +74,297 @@ int dns_trust_anchor_load(DnsTrustAnchor *d) { if (r < 0) return r; - r = hashmap_put(d->by_key, rr->key, answer); + r = hashmap_put(d->positive_by_key, rr->key, answer); + if (r < 0) + return r; + + answer = NULL; + return 0; +} + +static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + _cleanup_free_ char *domain = NULL, *class = NULL, *type = NULL; + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + DnsAnswer *old_answer = NULL; + const char *p = s; + int r; + + assert(d); + assert(line); + + r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES); + if (r < 0) + return log_warning_errno(r, "Unable to parse domain in line %s:%u: %m", path, line); + + if (!dns_name_is_valid(domain)) { + log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line); + return -EINVAL; + } + + r = extract_many_words(&p, NULL, 0, &class, &type, NULL); + if (r < 0) + return log_warning_errno(r, "Unable to parse class and type in line %s:%u: %m", path, line); + if (r != 2) { + log_warning("Missing class or type in line %s:%u", path, line); + return -EINVAL; + } + + if (!strcaseeq(class, "IN")) { + log_warning("RR class %s is not supported, ignoring line %s:%u.", class, path, line); + return -EINVAL; + } + + if (strcaseeq(type, "DS")) { + _cleanup_free_ char *key_tag = NULL, *algorithm = NULL, *digest_type = NULL, *digest = NULL; + _cleanup_free_ void *dd = NULL; + uint16_t kt; + int a, dt; + size_t l; + + r = extract_many_words(&p, NULL, 0, &key_tag, &algorithm, &digest_type, &digest, NULL); + if (r < 0) { + log_warning_errno(r, "Failed to parse DS parameters on line %s:%u: %m", path, line); + return -EINVAL; + } + if (r != 4) { + log_warning("Missing DS parameters on line %s:%u", path, line); + return -EINVAL; + } + + r = safe_atou16(key_tag, &kt); + if (r < 0) + return log_warning_errno(r, "Failed to parse DS key tag %s on line %s:%u: %m", key_tag, path, line); + + a = dnssec_algorithm_from_string(algorithm); + if (a < 0) { + log_warning("Failed to parse DS algorithm %s on line %s:%u", algorithm, path, line); + return -EINVAL; + } + + dt = dnssec_digest_from_string(digest_type); + if (dt < 0) { + log_warning("Failed to parse DS digest type %s on line %s:%u", digest_type, path, line); + return -EINVAL; + } + + r = unhexmem(digest, strlen(digest), &dd, &l); + if (r < 0) { + log_warning("Failed to parse DS digest %s on line %s:%u", digest, path, line); + return -EINVAL; + } + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, domain); + if (!rr) + return log_oom(); + + rr->ds.key_tag = kt; + rr->ds.algorithm = a; + rr->ds.digest_type = dt; + rr->ds.digest_size = l; + rr->ds.digest = dd; + dd = NULL; + + } else if (strcaseeq(type, "DNSKEY")) { + _cleanup_free_ char *flags = NULL, *protocol = NULL, *algorithm = NULL, *key = NULL; + _cleanup_free_ void *k = NULL; + uint16_t f; + size_t l; + int a; + + r = extract_many_words(&p, NULL, 0, &flags, &protocol, &algorithm, &key, NULL); + if (r < 0) + return log_warning_errno(r, "Failed to parse DNSKEY parameters on line %s:%u: %m", path, line); + if (r != 4) { + log_warning("Missing DNSKEY parameters on line %s:%u", path, line); + return -EINVAL; + } + + if (!streq(protocol, "3")) { + log_warning("DNSKEY Protocol is not 3 on line %s:%u", path, line); + return -EINVAL; + } + + r = safe_atou16(flags, &f); + if (r < 0) + return log_warning_errno(r, "Failed to parse DNSKEY flags field %s on line %s:%u", flags, path, line); + + a = dnssec_algorithm_from_string(algorithm); + if (a < 0) { + log_warning("Failed to parse DNSKEY algorithm %s on line %s:%u", algorithm, path, line); + return -EINVAL; + } + + r = unbase64mem(key, strlen(key), &k, &l); + if (r < 0) + return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", key, path, line); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, domain); + if (!rr) + return log_oom(); + + rr->dnskey.flags = f; + rr->dnskey.protocol = 3; + rr->dnskey.algorithm = a; + rr->dnskey.key_size = l; + rr->dnskey.key = k; + k = NULL; + + } else { + log_warning("RR type %s is not supported, ignoring line %s:%u.", type, path, line); + return -EINVAL; + } + + if (!isempty(p)) { + log_warning("Trailing garbage on line %s:%u, ignoring line.", path, line); + return -EINVAL; + } + + r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops); if (r < 0) return r; + old_answer = hashmap_get(d->positive_by_key, rr->key); + answer = dns_answer_ref(old_answer); + + r = dns_answer_add_extend(&answer, rr, 0, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return log_error_errno(r, "Failed to add trust anchor RR: %m"); + + r = hashmap_replace(d->positive_by_key, rr->key, answer); + if (r < 0) + return log_error_errno(r, "Failed to add answer to trust anchor: %m"); + + old_answer = dns_answer_unref(old_answer); answer = NULL; + + return 0; +} + +static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) { + _cleanup_free_ char *domain = NULL; + const char *p = s; + int r; + + assert(d); + assert(line); + + r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES); + if (r < 0) + return log_warning_errno(r, "Unable to parse line %s:%u: %m", path, line); + + if (!dns_name_is_valid(domain)) { + log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line); + return -EINVAL; + } + + if (!isempty(p)) { + log_warning("Trailing garbage at line %s:%u, ignoring line.", path, line); + return -EINVAL; + } + + r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops); + if (r < 0) + return r; + + r = set_put(d->negative_by_name, domain); + if (r < 0) + return log_oom(); + if (r > 0) + domain = NULL; + + return 0; +} + +static int dns_trust_anchor_load_files( + DnsTrustAnchor *d, + const char *suffix, + int (*loader)(DnsTrustAnchor *d, const char *path, unsigned n, const char *line)) { + + _cleanup_strv_free_ char **files = NULL; + char **f; + int r; + + assert(d); + assert(suffix); + assert(loader); + + r = conf_files_list_nulstr(&files, suffix, NULL, trust_anchor_dirs); + if (r < 0) + return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix); + + STRV_FOREACH(f, files) { + _cleanup_fclose_ FILE *g = NULL; + char line[LINE_MAX]; + unsigned n = 0; + + g = fopen(*f, "r"); + if (!g) { + if (errno == ENOENT) + continue; + + log_warning_errno(errno, "Failed to open %s: %m", *f); + continue; + } + + FOREACH_LINE(line, g, log_warning_errno(errno, "Failed to read %s, ignoring: %m", *f)) { + char *l; + + n++; + + l = strstrip(line); + if (isempty(l)) + continue; + + if (*l == ';') + continue; + + (void) loader(d, *f, n, l); + } + } + + return 0; +} + +static void dns_trust_anchor_dump(DnsTrustAnchor *d) { + DnsAnswer *a; + Iterator i; + + assert(d); + + log_info("Positive Trust Anchors:"); + HASHMAP_FOREACH(a, d->positive_by_key, i) { + DnsResourceRecord *rr; + + DNS_ANSWER_FOREACH(rr, a) + log_info("%s", dns_resource_record_to_string(rr)); + } + + if (!set_isempty(d->negative_by_name)) { + char *n; + log_info("Negative trust anchors:"); + + SET_FOREACH(n, d->negative_by_name, i) + log_info("%s%s", n, endswith(n, ".") ? "" : "."); + } +} + +int dns_trust_anchor_load(DnsTrustAnchor *d) { + int r; + + assert(d); + + /* If loading things from disk fails, we don't consider this fatal */ + (void) dns_trust_anchor_load_files(d, ".positive", dns_trust_anchor_load_positive); + (void) dns_trust_anchor_load_files(d, ".negative", dns_trust_anchor_load_negative); + + /* However, if the built-in DS fails, then we have a problem. */ + r = dns_trust_anchor_add_builtin(d); + if (r < 0) + return log_error_errno(r, "Failed to add trust anchor built-in: %m"); + + dns_trust_anchor_dump(d); + return 0; } @@ -75,13 +373,14 @@ void dns_trust_anchor_flush(DnsTrustAnchor *d) { assert(d); - while ((a = hashmap_steal_first(d->by_key))) + while ((a = hashmap_steal_first(d->positive_by_key))) dns_answer_unref(a); - d->by_key = hashmap_free(d->by_key); + d->positive_by_key = hashmap_free(d->positive_by_key); + d->negative_by_name = set_free_free(d->negative_by_name); } -int dns_trust_anchor_lookup(DnsTrustAnchor *d, DnsResourceKey *key, DnsAnswer **ret) { +int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) { DnsAnswer *a; assert(d); @@ -92,10 +391,17 @@ int dns_trust_anchor_lookup(DnsTrustAnchor *d, DnsResourceKey *key, DnsAnswer ** if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY)) return 0; - a = hashmap_get(d->by_key, key); + a = hashmap_get(d->positive_by_key, key); if (!a) return 0; *ret = dns_answer_ref(a); return 1; } + +int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) { + assert(d); + assert(name); + + return set_contains(d->negative_by_name, name); +} diff --git a/src/resolve/resolved-dns-trust-anchor.h b/src/resolve/resolved-dns-trust-anchor.h index 06f3723914..1140cde041 100644 --- a/src/resolve/resolved-dns-trust-anchor.h +++ b/src/resolve/resolved-dns-trust-anchor.h @@ -30,10 +30,12 @@ typedef struct DnsTrustAnchor DnsTrustAnchor; /* This contains a fixed database mapping domain names to DS or DNSKEY records. */ struct DnsTrustAnchor { - Hashmap *by_key; + Hashmap *positive_by_key; + Set *negative_by_name; }; int dns_trust_anchor_load(DnsTrustAnchor *d); void dns_trust_anchor_flush(DnsTrustAnchor *d); -int dns_trust_anchor_lookup(DnsTrustAnchor *d, DnsResourceKey* key, DnsAnswer **answer); +int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey* key, DnsAnswer **answer); +int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name); |