systemd-resolve: easy querying of TLSA records
$ systemd-resolve --tlsa fedoraproject.org _443._tcp.fedoraproject.org IN TLSA 0 0 1 GUAL5bejH7czkXcAeJ0vCiRxwMnVBsDlBMBsFtfLF8A= -- Cert. usage: CA constraint -- Selector: Full Certificate -- Matching type: SHA-256 $ systemd-resolve --tlsa=tcp fedoraproject.org:443 _443._tcp.fedoraproject.org IN TLSA 0 0 1 GUAL5bejH7czkXcAeJ0vCiRxwMnVBsDlBMBsFtfLF8A= ... $ systemd-resolve --tlsa=udp fedoraproject.org _443._udp.fedoraproject.org: resolve call failed: '_443._udp.fedoraproject.org' not found v2: - use uint16_t - refuse port 0
This commit is contained in:
parent
5259c0559c
commit
82d1d24093
|
@ -83,6 +83,13 @@
|
||||||
<arg choice="plain"><replaceable>USER@DOMAIN</replaceable></arg>
|
<arg choice="plain"><replaceable>USER@DOMAIN</replaceable></arg>
|
||||||
</cmdsynopsis>
|
</cmdsynopsis>
|
||||||
|
|
||||||
|
<cmdsynopsis>
|
||||||
|
<command>systemd-resolve</command>
|
||||||
|
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||||
|
<command> --tlsa</command>
|
||||||
|
<arg choice="plain"><replaceable>DOMAIN<optional>:PORT</optional></replaceable></arg>
|
||||||
|
</cmdsynopsis>
|
||||||
|
|
||||||
<cmdsynopsis>
|
<cmdsynopsis>
|
||||||
<command>systemd-resolve</command>
|
<command>systemd-resolve</command>
|
||||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||||
|
@ -121,10 +128,15 @@
|
||||||
is assumed to be a domain name, that is already prefixed with an SRV type, and an SRV lookup is done (no
|
is assumed to be a domain name, that is already prefixed with an SRV type, and an SRV lookup is done (no
|
||||||
TXT).</para>
|
TXT).</para>
|
||||||
|
|
||||||
<para>The <option>--openpgp</option> switch may be use to query PGP keys stored as the
|
<para>The <option>--openpgp</option> switch may be used to query PGP keys stored as
|
||||||
<ulink url="https://tools.ietf.org/html/draft-wouters-dane-openpgp-02">OPENPGPKEY</ulink> resource records.
|
<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>
|
When this option is specified one or more e-mail address must be specified.</para>
|
||||||
|
|
||||||
|
<para>The <option>--tlsa</option> switch maybe be used to query TLS public
|
||||||
|
keys stored as
|
||||||
|
<ulink url="https://tools.ietf.org/html/rfc6698">TLSA</ulink> resource records.
|
||||||
|
When this option is specified one or more domain names must be specified.</para>
|
||||||
|
|
||||||
<para>The <option>--statistics</option> switch may be used to show resolver statistics, including information about
|
<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>
|
the number of successful and failed DNSSEC validations.</para>
|
||||||
|
|
||||||
|
@ -216,6 +228,20 @@
|
||||||
printed.</para></listitem>
|
printed.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--tlsa</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Enables TLSA resource record resolution (see above).
|
||||||
|
A query will be performed for each of the specified names prefixed with
|
||||||
|
the port and family
|
||||||
|
(<literal>_<replaceable>port</replaceable>._<replaceable>family</replaceable>.<replaceable>domain</replaceable></literal>).
|
||||||
|
The port number may be specified after a colon
|
||||||
|
(<literal>:</literal>), otherwise <constant>443</constant> will be used
|
||||||
|
by default. The family may be specified as an argument after
|
||||||
|
<option>--tlsa</option>, otherwise <constant>tcp</constant> will be
|
||||||
|
used.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--cname=</option><replaceable>BOOL</replaceable></term>
|
<term><option>--cname=</option><replaceable>BOOL</replaceable></term>
|
||||||
|
|
||||||
|
@ -323,6 +349,18 @@ d08ee310438ca124a6149ea5cc21b6313b390dce485576eff96f8722._openpgpkey.fedoraproje
|
||||||
mQINBFBHPMsBEACeInGYJCb+7TurKfb6wGyTottCDtiSJB310i37/6ZYoeIay/5soJjlMyf
|
mQINBFBHPMsBEACeInGYJCb+7TurKfb6wGyTottCDtiSJB310i37/6ZYoeIay/5soJjlMyf
|
||||||
MFQ9T2XNT/0LM6gTa0MpC1st9LnzYTMsT6tzRly1D1UbVI6xw0g0vE5y2Cjk3xUwAynCsSs
|
MFQ9T2XNT/0LM6gTa0MpC1st9LnzYTMsT6tzRly1D1UbVI6xw0g0vE5y2Cjk3xUwAynCsSs
|
||||||
...
|
...
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<title>Retrieve a TLS key (<literal>=tcp</literal> and
|
||||||
|
<literal>:443</literal> could be skipped)</title>
|
||||||
|
|
||||||
|
<programlisting>$ systemd-resolve --tlsa=tcp fedoraproject.org:443
|
||||||
|
_443._tcp.fedoraproject.org IN TLSA 0 0 1 GUAL5bejH7czkXcAeJ0vCiRxwMnVBsDlBMBsFtfLF8A=
|
||||||
|
-- Cert. usage: CA constraint
|
||||||
|
-- Selector: Full Certificate
|
||||||
|
-- Matching type: SHA-256
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</example>
|
</example>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
|
@ -44,12 +44,19 @@ static uint16_t arg_class = 0;
|
||||||
static bool arg_legend = true;
|
static bool arg_legend = true;
|
||||||
static uint64_t arg_flags = 0;
|
static uint64_t arg_flags = 0;
|
||||||
|
|
||||||
|
typedef enum ServiceFamily {
|
||||||
|
SERVICE_FAMILY_TCP,
|
||||||
|
SERVICE_FAMILY_UDP,
|
||||||
|
SERVICE_FAMILY_SCTP,
|
||||||
|
_SERVICE_FAMILY_INVALID = -1,
|
||||||
|
} ServiceFamily;
|
||||||
|
static ServiceFamily arg_service_family = SERVICE_FAMILY_TCP;
|
||||||
|
|
||||||
typedef enum RawType {
|
typedef enum RawType {
|
||||||
RAW_NONE,
|
RAW_NONE,
|
||||||
RAW_PAYLOAD,
|
RAW_PAYLOAD,
|
||||||
RAW_PACKET,
|
RAW_PACKET,
|
||||||
} RawType;
|
} RawType;
|
||||||
|
|
||||||
static RawType arg_raw = RAW_NONE;
|
static RawType arg_raw = RAW_NONE;
|
||||||
|
|
||||||
static enum {
|
static enum {
|
||||||
|
@ -57,10 +64,34 @@ static enum {
|
||||||
MODE_RESOLVE_RECORD,
|
MODE_RESOLVE_RECORD,
|
||||||
MODE_RESOLVE_SERVICE,
|
MODE_RESOLVE_SERVICE,
|
||||||
MODE_RESOLVE_OPENPGP,
|
MODE_RESOLVE_OPENPGP,
|
||||||
|
MODE_RESOLVE_TLSA,
|
||||||
MODE_STATISTICS,
|
MODE_STATISTICS,
|
||||||
MODE_RESET_STATISTICS,
|
MODE_RESET_STATISTICS,
|
||||||
} arg_mode = MODE_RESOLVE_HOST;
|
} arg_mode = MODE_RESOLVE_HOST;
|
||||||
|
|
||||||
|
static ServiceFamily service_family_from_string(const char *s) {
|
||||||
|
if (s == NULL || streq(s, "tcp"))
|
||||||
|
return SERVICE_FAMILY_TCP;
|
||||||
|
if (streq(s, "udp"))
|
||||||
|
return SERVICE_FAMILY_UDP;
|
||||||
|
if (streq(s, "sctp"))
|
||||||
|
return SERVICE_FAMILY_SCTP;
|
||||||
|
return _SERVICE_FAMILY_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* service_family_to_string(ServiceFamily service) {
|
||||||
|
switch(service) {
|
||||||
|
case SERVICE_FAMILY_TCP:
|
||||||
|
return "_tcp";
|
||||||
|
case SERVICE_FAMILY_UDP:
|
||||||
|
return "_udp";
|
||||||
|
case SERVICE_FAMILY_SCTP:
|
||||||
|
return "_sctp";
|
||||||
|
default:
|
||||||
|
assert_not_reached("invalid service");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void print_source(uint64_t flags, usec_t rtt) {
|
static void print_source(uint64_t flags, usec_t rtt) {
|
||||||
char rtt_str[FORMAT_TIMESTAMP_MAX];
|
char rtt_str[FORMAT_TIMESTAMP_MAX];
|
||||||
|
|
||||||
|
@ -844,6 +875,38 @@ static int resolve_openpgp(sd_bus *bus, const char *address) {
|
||||||
arg_type ?: DNS_TYPE_OPENPGPKEY);
|
arg_type ?: DNS_TYPE_OPENPGPKEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int resolve_tlsa(sd_bus *bus, const char *address) {
|
||||||
|
const char *port;
|
||||||
|
uint16_t port_num = 443;
|
||||||
|
_cleanup_free_ char *full = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
assert(address);
|
||||||
|
|
||||||
|
port = strrchr(address, ':');
|
||||||
|
if (port) {
|
||||||
|
r = safe_atou16(port + 1, &port_num);
|
||||||
|
if (r < 0 || port_num == 0)
|
||||||
|
return log_error_errno(r, "Invalid port \"%s\".", port + 1);
|
||||||
|
|
||||||
|
address = strndupa(address, port - address);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = asprintf(&full, "_%u.%s.%s",
|
||||||
|
port_num,
|
||||||
|
service_family_to_string(arg_service_family),
|
||||||
|
address);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
log_debug("Looking up \"%s\".", full);
|
||||||
|
|
||||||
|
return resolve_record(bus, full,
|
||||||
|
arg_class ?: DNS_CLASS_IN,
|
||||||
|
arg_type ?: DNS_TYPE_TLSA);
|
||||||
|
}
|
||||||
|
|
||||||
static int show_statistics(sd_bus *bus) {
|
static int show_statistics(sd_bus *bus) {
|
||||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
|
@ -1031,6 +1094,7 @@ static void help(void) {
|
||||||
" --service-address=BOOL Resolve address for services (default: yes)\n"
|
" --service-address=BOOL Resolve address for services (default: yes)\n"
|
||||||
" --service-txt=BOOL Resolve TXT records for services (default: yes)\n"
|
" --service-txt=BOOL Resolve TXT records for services (default: yes)\n"
|
||||||
" --openpgp Query OpenPGP public key\n"
|
" --openpgp Query OpenPGP public key\n"
|
||||||
|
" --tlsa Query TLS public key\n"
|
||||||
" --cname=BOOL Follow CNAME redirects (default: yes)\n"
|
" --cname=BOOL Follow CNAME redirects (default: yes)\n"
|
||||||
" --search=BOOL Use search domains for single-label names\n"
|
" --search=BOOL Use search domains for single-label names\n"
|
||||||
" (default: yes)\n"
|
" (default: yes)\n"
|
||||||
|
@ -1050,6 +1114,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
ARG_SERVICE_ADDRESS,
|
ARG_SERVICE_ADDRESS,
|
||||||
ARG_SERVICE_TXT,
|
ARG_SERVICE_TXT,
|
||||||
ARG_OPENPGP,
|
ARG_OPENPGP,
|
||||||
|
ARG_TLSA,
|
||||||
ARG_RAW,
|
ARG_RAW,
|
||||||
ARG_SEARCH,
|
ARG_SEARCH,
|
||||||
ARG_STATISTICS,
|
ARG_STATISTICS,
|
||||||
|
@ -1069,6 +1134,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
{ "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
|
{ "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
|
||||||
{ "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
|
{ "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
|
||||||
{ "openpgp", no_argument, NULL, ARG_OPENPGP },
|
{ "openpgp", no_argument, NULL, ARG_OPENPGP },
|
||||||
|
{ "tlsa", optional_argument, NULL, ARG_TLSA },
|
||||||
{ "raw", optional_argument, NULL, ARG_RAW },
|
{ "raw", optional_argument, NULL, ARG_RAW },
|
||||||
{ "search", required_argument, NULL, ARG_SEARCH },
|
{ "search", required_argument, NULL, ARG_SEARCH },
|
||||||
{ "statistics", no_argument, NULL, ARG_STATISTICS, },
|
{ "statistics", no_argument, NULL, ARG_STATISTICS, },
|
||||||
|
@ -1183,6 +1249,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
arg_mode = MODE_RESOLVE_OPENPGP;
|
arg_mode = MODE_RESOLVE_OPENPGP;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_TLSA:
|
||||||
|
arg_mode = MODE_RESOLVE_TLSA;
|
||||||
|
arg_service_family = service_family_from_string(optarg);
|
||||||
|
if (arg_service_family < 0) {
|
||||||
|
log_error("Unknown service family \"%s\".", optarg);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ARG_RAW:
|
case ARG_RAW:
|
||||||
if (on_tty()) {
|
if (on_tty()) {
|
||||||
log_error("Refusing to write binary data to tty.");
|
log_error("Refusing to write binary data to tty.");
|
||||||
|
@ -1261,7 +1336,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg_type != 0 && arg_mode != MODE_RESOLVE_RECORD) {
|
if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE) {
|
||||||
log_error("--service and --type= may not be combined.");
|
log_error("--service and --type= may not be combined.");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1378,6 +1453,24 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MODE_RESOLVE_TLSA:
|
||||||
|
if (argc < optind + 1) {
|
||||||
|
log_error("Domain name required.");
|
||||||
|
r = -EINVAL;
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
r = 0;
|
||||||
|
while (optind < argc) {
|
||||||
|
int k;
|
||||||
|
|
||||||
|
k = resolve_tlsa(bus, argv[optind++]);
|
||||||
|
if (k < 0)
|
||||||
|
r = k;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case MODE_STATISTICS:
|
case MODE_STATISTICS:
|
||||||
if (argc > optind) {
|
if (argc > optind) {
|
||||||
log_error("Too many arguments.");
|
log_error("Too many arguments.");
|
||||||
|
|
Loading…
Reference in a new issue