]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolve-tool.c
systemd-resolve: allow keys to be dumped in binary form
[thirdparty/systemd.git] / src / resolve / resolve-tool.c
index 9bee9538391c2d900df61cd71f7a396b607334d5..a24bb546d4fef11ae7bf15a0eac45db02a10866a 100644 (file)
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
 /***
   This file is part of systemd.
 
@@ -19,6 +17,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <gcrypt.h>
 #include <getopt.h>
 #include <net/if.h>
 
@@ -30,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"
@@ -43,11 +43,13 @@ static uint16_t arg_type = 0;
 static uint16_t arg_class = 0;
 static bool arg_legend = true;
 static uint64_t arg_flags = 0;
+static bool arg_raw = false;
 
 static enum {
         MODE_RESOLVE_HOST,
         MODE_RESOLVE_RECORD,
         MODE_RESOLVE_SERVICE,
+        MODE_RESOLVE_OPENPGP,
         MODE_STATISTICS,
         MODE_RESET_STATISTICS,
 } arg_mode = MODE_RESOLVE_HOST;
@@ -338,6 +340,7 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
         uint64_t flags;
         int r;
         usec_t ts;
+        bool needs_authentication = false;
 
         assert(name);
 
@@ -377,7 +380,6 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
         while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) {
                 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
                 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
-                const char *s;
                 uint16_t c, t;
                 int ifindex;
                 const void *d;
@@ -411,15 +413,33 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
                 if (r < 0)
                         return log_error_errno(r, "Failed to parse RR: %m");
 
-                s = dns_resource_record_to_string(rr);
-                if (!s)
-                        return log_oom();
+                if (arg_raw) {
+                        void *data;
+                        ssize_t k;
 
-                ifname[0] = 0;
-                if (ifindex > 0 && !if_indextoname(ifindex, ifname))
-                        log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
+                        k = dns_resource_record_payload(rr, &data);
+                        if (k < 0)
+                                return log_error_errno(k, "Cannot dump RR: %m");
+                        fwrite(data, 1, k, stdout);
+                } else {
+                        const char *s;
+
+                        s = dns_resource_record_to_string(rr);
+                        if (!s)
+                                return log_oom();
+
+                        ifname[0] = 0;
+                        if (ifindex > 0 && !if_indextoname(ifindex, ifname))
+                                log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
+
+                        printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname);
+                }
 
                 printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname);
+
+                if (dns_type_needs_authentication(t))
+                        needs_authentication = true;
+
                 n++;
         }
         if (r < 0)
@@ -440,6 +460,18 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
 
         print_source(flags, ts);
 
+        if ((flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) {
+                fflush(stdout);
+
+                fprintf(stderr, "\n%s"
+                       "WARNING: The resources shown contain cryptographic key data which could not be\n"
+                       "         authenticated. It is not suitable to authenticate any communication.\n"
+                       "         This is usually indication that DNSSEC authentication was not enabled\n"
+                       "         or is not available for the selected protocol or DNS servers.%s\n",
+                       ansi_highlight_red(),
+                       ansi_normal());
+        }
+
         return 0;
 }
 
@@ -547,15 +579,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);
 
@@ -765,6 +792,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;
@@ -933,26 +990,32 @@ static void help_dns_classes(void) {
 }
 
 static void help(void) {
-        printf("%s [OPTIONS...] NAME...\n"
-               "%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n"
+        printf("%1$s [OPTIONS...] HOSTNAME|ADDRESS...\n"
+               "%1$s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n"
+               "%1$s [OPTIONS...] --openpgp EMAIL@DOMAIN...\n"
+               "%1$s [OPTIONS...] --statistics\n"
+               "%1$s [OPTIONS...] --reset-statistics\n"
+               "\n"
                "Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n"
-               "  -h --help                   Show this help\n"
-               "     --version                Show package version\n"
-               "  -4                          Resolve IPv4 addresses\n"
-               "  -6                          Resolve IPv6 addresses\n"
-               "  -i --interface=INTERFACE    Look on interface\n"
-               "  -p --protocol=PROTOCOL|help Look via protocol\n"
-               "  -t --type=TYPE|help         Query RR with DNS type\n"
-               "  -c --class=CLASS|help       Query RR with DNS class\n"
-               "     --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"
-               "     --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"
-               "     --statistics             Show resolver statistics\n"
-               "     --reset-statistics       Reset resolver statistics\n"
-               , program_invocation_short_name, program_invocation_short_name);
+               "  -h --help                 Show this help\n"
+               "     --version              Show package version\n"
+               "  -4                        Resolve IPv4 addresses\n"
+               "  -6                        Resolve IPv6 addresses\n"
+               "  -i --interface=INTERFACE  Look on interface\n"
+               "  -p --protocol=PROTO|help  Look via protocol\n"
+               "  -t --type=TYPE|help       Query RR with DNS type\n"
+               "  -c --class=CLASS|help     Query RR with DNS class\n"
+               "     --service              Resolve service (SRV)\n"
+               "     --service-address=BOOL Resolve address for services (default: yes)\n"
+               "     --service-txt=BOOL     Resolve TXT records for services (default: yes)\n"
+               "     --openpgp              Query OpenPGP public key\n"
+               "     --cname=BOOL           Follow CNAME redirects (default: yes)\n"
+               "     --search=BOOL          Use search domains for single-label names\n"
+               "                                                              (default: yes)\n"
+               "     --legend=BOOL          Print headers and additional info (default: yes)\n"
+               "     --statistics           Show resolver statistics\n"
+               "     --reset-statistics     Reset resolver statistics\n"
+               , program_invocation_short_name);
 }
 
 static int parse_argv(int argc, char *argv[]) {
@@ -963,6 +1026,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_CNAME,
                 ARG_SERVICE_ADDRESS,
                 ARG_SERVICE_TXT,
+                ARG_OPENPGP,
+                ARG_RAW,
                 ARG_SEARCH,
                 ARG_STATISTICS,
                 ARG_RESET_STATISTICS,
@@ -980,6 +1045,8 @@ 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          },
+                { "raw",              no_argument,       NULL, ARG_RAW              },
                 { "search",           required_argument, NULL, ARG_SEARCH           },
                 { "statistics",       no_argument,       NULL, ARG_STATISTICS,      },
                 { "reset-statistics", no_argument,       NULL, ARG_RESET_STATISTICS },
@@ -1089,6 +1156,20 @@ 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_RAW:
+                        if (on_tty()) {
+                                log_error("Refusing to write binary data to tty.");
+                                return -ENOTTY;
+                        }
+
+                        arg_raw = true;
+                        arg_legend = false;
+                        break;
+
                 case ARG_CNAME:
                         r = parse_boolean(optarg);
                         if (r < 0)
@@ -1248,6 +1329,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.");