1
0
mirror of https://github.com/systemd/systemd synced 2025-12-26 19:04:45 +01:00

Compare commits

..

17 Commits

Author SHA1 Message Date
Lennart Poettering
565147b7bb stub: don't ever respond to datagrams coming in on non-localhost addreses, on the stub 2020-12-03 09:35:40 +09:00
Lennart Poettering
9ca875e80c resolved: beef up logic for suppressing "localhost" entry in /etc/hosts
Either suppress the entry entirely, or not at all. But do not suppress
the "localhost" names we recognize, leaving the ones we do not in place.

On Fedora, where "localhost4.localdomain4" is among those listed in
/etc/hosts for 127.0.0.1 we'd thus otherwise drop the "localhost" but
keep the "localhost4.localdomain4" and then on reverse lookups only
return that, which is highly confusing.
2020-12-03 09:06:27 +09:00
Lennart Poettering
36d892b7e6 resolved: use stat_inode_unmodified() to detect /etc/hosts changes 2020-12-03 08:58:45 +09:00
Lennart Poettering
fbbc72189f resolved: never allow _gateway lookups to go to the network
Make them rather fail than go to the network.

Previously we'd filter them on LLMNR (explicitly) and MDNS (implicitly,
because it doesn't have .local suffix), but not on DNS.

In order to make _gateway truly reliable, let's not allow it to go to
DNS either, and keep it local.

This is particular relevant, as clients can now request lookups without
local RR synthesis, where we'd rather have NXDOMAIN returned for
_gateway than have it hit the network.
2020-12-03 08:56:27 +09:00
Lennart Poettering
19bcef9dc3 resolved: lower SERVFAIL cache timeout from 30s to 10s
Apparently 30s is a bit too long for some cases, see #5552. But not
caching SERVFAIL at all also breaks stuff, see explanation in
201d99584ed7af8078bb243ce2587e5455074713.

Let's try to find some middle ground, by lowering the cache timeout to
10s. This should be ample for the problem
201d99584ed7af8078bb243ce2587e5455074713 attackes, but not as long as
half a miute, as #5552 complains.

Fixes: #5552
2020-12-03 08:52:27 +09:00
Yu Watanabe
34f80876f8
Merge pull request #17807 from poettering/bindtodevice
use SO_BINDTOIFINDEX while connect()
2020-12-03 08:50:43 +09:00
Lennart Poettering
0a489d3f5d resolved: insert large dgram size into EDNS0 only when in LARGE UDP mode
Specifically, in TLS-DO there's no reason to set the exotic dgram size.
2020-12-03 08:49:58 +09:00
Lennart Poettering
d80e72ec60 dns-domain: try IDN2003 rules if IDN2008 doesn't work
This follows more closely what web browsers do, and makes sure emojis in
domains work.

Fixes: #14483
2020-12-03 08:48:11 +09:00
Steve Ramage
7d27d39aa7
Adds missing documentation for Assertions (#17825) 2020-12-03 08:47:24 +09:00
Yu Watanabe
f01a3b79fa
Merge pull request #17810 from systemd/meson-allows-fuzzer-building
meson: always allow fuzzers to be built
2020-12-03 07:36:43 +09:00
Lennart Poettering
90df0fbea8 resolved: automatically flush caches on clock change
DNSSEC validation takes the system clock into account to validate
signatures. This means if we had incorrect time and the time is then
changed to the correct one we should flush out everything and
re-validate taking the new time into account.

(This logic will also trigger after system suspend, which is not bad
either, given that quite possibly we are connected to a different
network, and thus would get different DNS data, without us noticing
otherwise via link beat).
2020-12-03 07:25:17 +09:00
Christopher Obbard
c8037dbf05 virt: Properly detect nested UML inside another hypervisor
UML runs as a user-process so it can quite easily be ran inside of
another hypervisor, for instance inside a KVM instance. UML passes
through the CPUID from the host machine so in this case detect_vm
incorrectly identifies as running under KVM. So check we are running
a UML kernel first, before we check any other hypervisors.

Resolves: #17754

Signed-off-by: Christopher Obbard <chris.obbard@collabora.com>
2020-12-03 07:19:41 +09:00
Lennart Poettering
0b261ac5be resolved: log when a bus client changes per-link DNS info
Fixes: #16298
2020-12-03 07:08:07 +09:00
Lennart Poettering
d301c52383 resolved: bind socket to interface during connect()
Apparently, IF_UNICAST_IF does not influence the routing decisions done
during connect(). But SO_BINDTODEVICE/SO_BINDTOINDEX does, which however
brings a lot of other semantics with it, we are not so interested in
(i.e. it doesn't not allow packets from any other iface to us, even if
routing otherwise allows it).

Hence, let's bind to the ifindex immediately before the connect() and
unbind right after again, so that we get the semantics we want, but not
the ones we don't.

Fixes: #11935
Replaces: #12004
2020-12-02 15:15:02 +01:00
Lennart Poettering
3132597182 socket-util: add sockaddr_in_addr() helper
This extracts the IP address (as union in_addr_union) from a socket
address (i.e. a struct sockaddr).
2020-12-02 15:14:21 +01:00
Zbigniew Jędrzejewski-Szmek
7e299ffe10 meson: allow fuzzers to be built even if fuzz testing is disabled
This makes commands like 'ninja -C build fuzz-journal-remote' or
'ninja -C build fuzzers' work, even if we have -Dfuzz-tests=false.
Two advantages: correctness of the meson declarations is verified even
if fuzzers are not built, and it easier to do a one-off build to check for
regressions or such.

Follow-up for 1763ef1d49cc1263b40f157060a61cdd6e91d3a4.
2020-12-02 13:51:31 +01:00
Zbigniew Jędrzejewski-Szmek
ce9067697b meson: add missing "S" to SYSTEMD_CGROUPS_AGENT_PATH
Other similar variables use the binary name underscorified and upppercased
(with "_BINARY" appended in some cases to avoid ambiguity). Add "S" to follow
the same pattern for systemd-cgroups-agent.

Based on the discussion in #16715.
2020-12-01 09:34:17 +01:00
17 changed files with 335 additions and 75 deletions

View File

@ -1495,6 +1495,7 @@
<term><varname>AssertHost=</varname></term>
<term><varname>AssertKernelCommandLine=</varname></term>
<term><varname>AssertKernelVersion=</varname></term>
<term><varname>AssertEnvironment=</varname></term>
<term><varname>AssertSecurity=</varname></term>
<term><varname>AssertCapability=</varname></term>
<term><varname>AssertACPower=</varname></term>
@ -1506,12 +1507,15 @@
<term><varname>AssertPathIsSymbolicLink=</varname></term>
<term><varname>AssertPathIsMountPoint=</varname></term>
<term><varname>AssertPathIsReadWrite=</varname></term>
<term><varname>AssertPathIsEncrypted=</varname></term>
<term><varname>AssertDirectoryNotEmpty=</varname></term>
<term><varname>AssertFileNotEmpty=</varname></term>
<term><varname>AssertFileIsExecutable=</varname></term>
<term><varname>AssertUser=</varname></term>
<term><varname>AssertGroup=</varname></term>
<term><varname>AssertControlGroupController=</varname></term>
<term><varname>AssertMemory=</varname></term>
<term><varname>AssertCPUs=</varname></term>
<listitem><para>Similar to the <varname>ConditionArchitecture=</varname>,
<varname>ConditionVirtualization=</varname>, …, condition settings described above, these settings

View File

@ -221,11 +221,11 @@ conf.set_quoted('USER_CONFIG_UNIT_DIR', join_paths(pkgsysc
conf.set_quoted('USER_DATA_UNIT_DIR', userunitdir)
conf.set_quoted('CERTIFICATE_ROOT', get_option('certificate-root'))
conf.set_quoted('CATALOG_DATABASE', join_paths(catalogstatedir, 'database'))
conf.set_quoted('SYSTEMD_CGROUP_AGENT_PATH', join_paths(rootlibexecdir, 'systemd-cgroups-agent'))
conf.set_quoted('SYSTEMD_BINARY_PATH', join_paths(rootlibexecdir, 'systemd'))
conf.set_quoted('SYSTEMD_CGROUPS_AGENT_PATH', join_paths(rootlibexecdir, 'systemd-cgroups-agent'))
conf.set_quoted('SYSTEMD_FSCK_PATH', join_paths(rootlibexecdir, 'systemd-fsck'))
conf.set_quoted('SYSTEMD_MAKEFS_PATH', join_paths(rootlibexecdir, 'systemd-makefs'))
conf.set_quoted('SYSTEMD_GROWFS_PATH', join_paths(rootlibexecdir, 'systemd-growfs'))
conf.set_quoted('SYSTEMD_MAKEFS_PATH', join_paths(rootlibexecdir, 'systemd-makefs'))
conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-shutdown'))
conf.set_quoted('SYSTEMCTL_BINARY_PATH', join_paths(rootbindir, 'systemctl'))
conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', join_paths(rootbindir, 'systemd-tty-ask-password-agent'))
@ -3438,40 +3438,39 @@ endif
fuzzer_exes = []
if fuzz_tests or fuzzer_build
foreach tuple : fuzzers
sources = tuple[0]
link_with = tuple[1].length() > 0 ? tuple[1] : [libshared]
dependencies = tuple[2]
defs = tuple.length() >= 4 ? tuple[3] : []
incs = tuple.length() >= 5 ? tuple[4] : includes
link_args = []
foreach tuple : fuzzers
sources = tuple[0]
link_with = tuple[1].length() > 0 ? tuple[1] : [libshared]
dependencies = tuple[2]
defs = tuple.length() >= 4 ? tuple[3] : []
incs = tuple.length() >= 5 ? tuple[4] : includes
link_args = []
if want_ossfuzz
if want_ossfuzz
dependencies += fuzzing_engine
elif want_libfuzzer
if fuzzing_engine.found()
dependencies += fuzzing_engine
elif want_libfuzzer
if fuzzing_engine.found()
dependencies += fuzzing_engine
else
link_args += ['-fsanitize=fuzzer']
endif
else
sources += 'src/fuzz/fuzz-main.c'
link_args += ['-fsanitize=fuzzer']
endif
else
sources += 'src/fuzz/fuzz-main.c'
endif
name = sources[0].split('/')[-1].split('.')[0]
name = sources[0].split('/')[-1].split('.')[0]
fuzzer_exes += executable(
name,
sources,
include_directories : [incs, include_directories('src/fuzz')],
link_with : link_with,
dependencies : dependencies,
c_args : defs,
link_args: link_args,
install : false)
endforeach
endif
fuzzer_exes += executable(
name,
sources,
include_directories : [incs, include_directories('src/fuzz')],
link_with : link_with,
dependencies : dependencies,
c_args : defs,
link_args: link_args,
install : false,
build_by_default : fuzz_tests or fuzzer_build)
endforeach
run_target(
'fuzzers',

View File

@ -320,7 +320,7 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
}
int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
const union sockaddr_union *sa = (const union sockaddr_union*) _sa;
/* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */
@ -345,6 +345,25 @@ int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) {
}
}
const union in_addr_union *sockaddr_in_addr(const struct sockaddr *_sa) {
const union sockaddr_union *sa = (const union sockaddr_union*) _sa;
if (!sa)
return NULL;
switch (sa->sa.sa_family) {
case AF_INET:
return (const union in_addr_union*) &sa->in.sin_addr;
case AF_INET6:
return (const union in_addr_union*) &sa->in6.sin6_addr;
default:
return NULL;
}
}
int sockaddr_pretty(
const struct sockaddr *_sa,
socklen_t salen,

View File

@ -102,6 +102,7 @@ const char* socket_address_get_path(const SocketAddress *a);
bool socket_ipv6_is_supported(void);
int sockaddr_port(const struct sockaddr *_sa, unsigned *port);
const union in_addr_union *sockaddr_in_addr(const struct sockaddr *sa);
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
int getpeername_pretty(int fd, bool include_port, char **ret);

View File

@ -345,7 +345,7 @@ int detect_vm(void) {
/* We have to use the correct order here:
*
* First, try to detect Oracle Virtualbox, even if it uses KVM, as well as Xen even if it cloaks as Microsoft
* Hyper-V.
* Hyper-V. Attempt to detect uml at this stage also since it runs as a user-process nested inside other VMs.
*
* Second, try to detect from CPUID, this will report KVM for whatever software is used even if info in DMI is
* overwritten.
@ -358,6 +358,16 @@ int detect_vm(void) {
goto finish;
}
/* Detect UML */
r = detect_vm_uml();
if (r < 0)
return r;
if (r == VIRTUALIZATION_VM_OTHER)
other = true;
else if (r != VIRTUALIZATION_NONE)
goto finish;
/* Detect from CPUID */
r = detect_vm_cpuid();
if (r < 0)
return r;
@ -406,14 +416,6 @@ int detect_vm(void) {
else if (r != VIRTUALIZATION_NONE)
goto finish;
r = detect_vm_uml();
if (r < 0)
return r;
if (r == VIRTUALIZATION_VM_OTHER)
other = true;
else if (r != VIRTUALIZATION_NONE)
goto finish;
r = detect_vm_zvm();
if (r < 0)
return r;

View File

@ -3052,7 +3052,7 @@ int manager_setup_cgroup(Manager *m) {
/* On the legacy hierarchy we only get notifications via cgroup agents. (Which isn't really reliable,
* since it does not generate events when control groups with children run empty. */
r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUPS_AGENT_PATH);
if (r < 0)
log_warning_errno(r, "Failed to install release agent, ignoring: %m");
else if (r > 0)

View File

@ -1762,7 +1762,7 @@ static int bus_method_flush_caches(sd_bus_message *message, void *userdata, sd_b
assert(message);
assert(m);
manager_flush_caches(m);
manager_flush_caches(m, LOG_INFO);
return sd_bus_reply_method_return(message, NULL);
}

View File

@ -20,7 +20,7 @@
/* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for
* now) */
#define CACHE_TTL_STRANGE_RCODE_USEC (30 * USEC_PER_SEC)
#define CACHE_TTL_STRANGE_RCODE_USEC (10 * USEC_PER_SEC)
typedef enum DnsCacheItemType DnsCacheItemType;
typedef struct DnsCacheItem DnsCacheItem;

View File

@ -412,9 +412,36 @@ static int dns_scope_socket(
if (ret_socket_address)
*ret_socket_address = sa;
else {
bool bound = false;
/* Let's temporarily bind the socket to the specified ifindex. The kernel currently takes
* only the SO_BINDTODEVICE/SO_BINDTOINDEX ifindex into account when making routing decisions
* in connect() and not IP_UNICAST_IF. We don't really want any of the other semantics of
* SO_BINDTODEVICE/SO_BINDTOINDEX, hence we immediately unbind the socket after the fact
* again.
*
* As a special exception we don't do this if we notice that the specified IP address is on
* the local host. SO_BINDTODEVICE in combination with destination addresses on the local
* host result in EHOSTUNREACH, since Linux won't send the packets out of the specified
* interface, but delivers them directly to the local socket. */
if (s->link &&
!manager_find_link_address(s->manager, sa.sa.sa_family, sockaddr_in_addr(&sa.sa))) {
r = socket_bind_to_ifindex(fd, ifindex);
if (r < 0)
return r;
bound = true;
}
r = connect(fd, &sa.sa, salen);
if (r < 0 && errno != EINPROGRESS)
return -errno;
if (bound) {
r = socket_bind_to_ifindex(fd, 0);
if (r < 0)
return r;
}
}
return TAKE_FD(fd);
@ -478,9 +505,8 @@ DnsScopeMatch dns_scope_good_domain(
if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family, 0) & flags) == 0)
return DNS_SCOPE_NO;
/* Never resolve any loopback hostname or IP address via DNS,
* LLMNR or mDNS. Instead, always rely on synthesized RRs for
* these. */
/* Never resolve any loopback hostname or IP address via DNS, LLMNR or mDNS. Instead, always rely on
* synthesized RRs for these. */
if (is_localhost(domain) ||
dns_name_endswith(domain, "127.in-addr.arpa") > 0 ||
dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
@ -496,6 +522,15 @@ DnsScopeMatch dns_scope_good_domain(
if (dns_name_endswith(domain, "invalid") > 0)
return DNS_SCOPE_NO;
/* Never go to network for the _gateway domain, it's something special, synthesized locally. Note
* that we don't use is_gateway_hostname() here, since that has support for the legacy "gateway"
* hostname (without the prefix underscore), which we don't want to filter on all protocols. i.e. we
* don't want to filter "gateway" on classic DNS, since there might very well be such a host inside
* some search domain, and we shouldn't block that. We do filter it in LLMNR however (and on mDNS by
* side-effect, since it's a single-label name which mDNS doesn't accept anyway). */
if (dns_name_equal(domain, "_gateway") > 0)
return DNS_SCOPE_NO;
switch (s->protocol) {
case DNS_PROTOCOL_DNS: {
@ -588,7 +623,7 @@ DnsScopeMatch dns_scope_good_domain(
return DNS_SCOPE_MAYBE;
if ((dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
!is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
!is_gateway_hostname(domain) && /* don't resolve "_gateway" with LLMNR, let local synthesizing logic handle that */
dns_name_equal(domain, "local") == 0 && /* don't resolve "local" with LLMNR, it's the top-level domain of mDNS after all, see above */
manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
return DNS_SCOPE_YES_BASE + 1; /* Return +1, as we consider ourselves authoritative

View File

@ -530,7 +530,7 @@ int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeature
edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO;
if (level >= DNS_SERVER_FEATURE_LEVEL_LARGE)
if (level == DNS_SERVER_FEATURE_LEVEL_LARGE)
packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
else
packet_size = server->received_udp_packet_max;

View File

@ -379,8 +379,7 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
if (!l && /* l == NULL if this is the main stub */
(in_addr_is_localhost(p->family, &p->sender) <= 0 ||
in_addr_is_localhost(p->family, &p->destination) <= 0)) {
log_error("Got packet on unexpected IP range, refusing.");
dns_stub_send_failure(m, l, s, p, DNS_RCODE_SERVFAIL, false);
log_warning("Got packet on unexpected (i.e. non-localhost) IP range, ignoring.");
return;
}

View File

@ -10,6 +10,7 @@
#include "resolved-dns-synthesize.h"
#include "resolved-etc-hosts.h"
#include "socket-netlink.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@ -36,9 +37,7 @@ void etc_hosts_free(EtcHosts *hosts) {
void manager_etc_hosts_flush(Manager *m) {
etc_hosts_free(&m->etc_hosts);
m->etc_hosts_mtime = USEC_INFINITY;
m->etc_hosts_ino = 0;
m->etc_hosts_dev = 0;
m->etc_hosts_stat = (struct stat) {};
}
static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
@ -114,10 +113,6 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
continue;
}
if (is_localhost(name))
/* Suppress the "localhost" line that is often seen */
continue;
if (!item) {
/* Optimize the case where we don't need to store any addresses, by storing
* only the name in a dedicated Set instead of the hashmap */
@ -164,6 +159,95 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
return 0;
}
static void strip_localhost(EtcHosts *hosts) {
static const struct in_addr_data local_in_addrs[] = {
{
.family = AF_INET,
#if __BYTE_ORDER == __LITTLE_ENDIAN
/* We want constant expressions here, that's why we don't use htole32() here */
.address.in.s_addr = UINT32_C(0x0100007F),
#else
.address.in.s_addr = UINT32_C(0x7F000001),
#endif
},
{
.family = AF_INET6,
.address.in6 = IN6ADDR_LOOPBACK_INIT,
},
};
EtcHostsItem *item;
assert(hosts);
/* Removes the 'localhost' entry from what we loaded. But only if the mapping is exclusively between
* 127.0.0.1 and localhost (or aliases to that we recognize). If there's any other name assigned to
* it, we leave the entry in.
*
* This way our regular synthesizing can take over, but only if it would result in the exact same
* mappings. */
for (size_t j = 0; j < ELEMENTSOF(local_in_addrs); j++) {
bool all_localhost, in_order;
char **i;
item = hashmap_get(hosts->by_address, local_in_addrs + j);
if (!item)
continue;
/* Check whether all hostnames the loopback address points to are localhost ones */
all_localhost = true;
STRV_FOREACH(i, item->names)
if (!is_localhost(*i)) {
all_localhost = false;
break;
}
if (!all_localhost) /* Not all names are localhost, hence keep the entries for this address. */
continue;
/* Now check if the names listed for this address actually all point back just to this
* address (or the other loopback address). If not, let's stay away from this too. */
in_order = true;
STRV_FOREACH(i, item->names) {
EtcHostsItemByName *n;
bool all_local_address;
n = hashmap_get(hosts->by_name, *i);
if (!n) /* No reverse entry? Then almost certainly the entry already got deleted from
* the previous iteration of this loop, i.e. via the other protocol */
break;
/* Now check if the addresses of this item are all localhost addresses */
all_local_address = true;
for (size_t m = 0; m < n->n_addresses; m++)
if (!in_addr_is_localhost(n->addresses[m]->family, &n->addresses[m]->address)) {
all_local_address = false;
break;
}
if (!all_local_address) {
in_order = false;
break;
}
}
if (!in_order)
continue;
STRV_FOREACH(i, item->names) {
EtcHostsItemByName *n;
n = hashmap_remove(hosts->by_name, *i);
if (n)
etc_hosts_item_by_name_free(n);
}
assert_se(hashmap_remove(hosts->by_address, local_in_addrs + j) == item);
etc_hosts_item_free(item);
}
}
int etc_hosts_parse(EtcHosts *hosts, FILE *f) {
_cleanup_(etc_hosts_free) EtcHosts t = {};
unsigned nr = 0;
@ -194,6 +278,8 @@ int etc_hosts_parse(EtcHosts *hosts, FILE *f) {
return r;
}
strip_localhost(&t);
etc_hosts_free(hosts);
*hosts = t;
t = (EtcHosts) {}; /* prevent cleanup */
@ -214,7 +300,7 @@ static int manager_etc_hosts_read(Manager *m) {
m->etc_hosts_last = ts;
if (m->etc_hosts_mtime != USEC_INFINITY) {
if (m->etc_hosts_stat.st_mode != 0) {
if (stat("/etc/hosts", &st) < 0) {
if (errno != ENOENT)
return log_error_errno(errno, "Failed to stat /etc/hosts: %m");
@ -224,8 +310,7 @@ static int manager_etc_hosts_read(Manager *m) {
}
/* Did the mtime or ino/dev change? If not, there's no point in re-reading the file. */
if (timespec_load(&st.st_mtim) == m->etc_hosts_mtime &&
st.st_ino == m->etc_hosts_ino && st.st_dev == m->etc_hosts_dev)
if (stat_inode_unmodified(&m->etc_hosts_stat, &st))
return 0;
}
@ -248,9 +333,7 @@ static int manager_etc_hosts_read(Manager *m) {
if (r < 0)
return r;
m->etc_hosts_mtime = timespec_load(&st.st_mtim);
m->etc_hosts_ino = st.st_ino;
m->etc_hosts_dev = st.st_dev;
m->etc_hosts_stat = st;
m->etc_hosts_last = ts;
return 1;

View File

@ -9,6 +9,7 @@
#include "bus-get-properties.h"
#include "bus-message-util.h"
#include "bus-polkit.h"
#include "log-link.h"
#include "parse-util.h"
#include "resolve-util.h"
#include "resolved-bus.h"
@ -252,6 +253,7 @@ static int verify_unmanaged_link(Link *l, sd_bus_error *error) {
}
static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, void *userdata, sd_bus_error *error, bool extended) {
_cleanup_free_ char *j = NULL;
struct in_addr_full **dns;
Link *l = userdata;
size_t n;
@ -279,6 +281,21 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
goto finalize;
}
for (size_t i = 0; i < n; i++) {
const char *s;
s = in_addr_full_to_string(dns[i]);
if (!s) {
r = -ENOMEM;
goto finalize;
}
if (!strextend_with_separator(&j, ", ", s, NULL)) {
r = -ENOMEM;
goto finalize;
}
}
dns_server_mark_all(l->dns_servers);
for (size_t i = 0; i < n; i++) {
@ -304,6 +321,11 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
(void) manager_write_resolv_conf(l->manager);
(void) manager_send_changed(l->manager, "DNS");
if (j)
log_link_info(l, "Bus client set DNS server list to: %s", j);
else
log_link_info(l, "Bus client reset DNS server list.");
r = sd_bus_reply_method_return(message, NULL);
finalize:
@ -323,6 +345,7 @@ int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata,
}
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *j = NULL;
Link *l = userdata;
int r;
@ -338,6 +361,7 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
return r;
for (;;) {
_cleanup_free_ char *prefixed = NULL;
const char *name;
int route_only;
@ -354,6 +378,17 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
if (!route_only && dns_name_is_root(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
if (route_only) {
prefixed = strjoin("~", name);
if (!prefixed)
return -ENOMEM;
name = prefixed;
}
if (!strextend_with_separator(&j, ", ", name, NULL))
return -ENOMEM;
}
r = sd_bus_message_rewind(message, false);
@ -406,6 +441,11 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
(void) link_save_user(l);
(void) manager_write_resolv_conf(l->manager);
if (j)
log_link_info(l, "Bus client set search domain list to: %s", j);
else
log_link_info(l, "Bus client reset search domain list.");
return sd_bus_reply_method_return(message, NULL);
clear:
@ -444,6 +484,8 @@ int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, s
(void) manager_write_resolv_conf(l->manager);
}
log_link_info(l, "Bus client set default route setting: %s", yes_no(b));
return sd_bus_reply_method_return(message, NULL);
}
@ -487,6 +529,8 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er
(void) link_save_user(l);
log_link_info(l, "Bus client set LLMNR setting: %s", resolve_support_to_string(mode));
return sd_bus_reply_method_return(message, NULL);
}
@ -530,6 +574,8 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err
(void) link_save_user(l);
log_link_info(l, "Bus client set MulticastDNS setting: %s", resolve_support_to_string(mode));
return sd_bus_reply_method_return(message, NULL);
}
@ -571,6 +617,9 @@ int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd
(void) link_save_user(l);
log_link_info(l, "Bus client set DNSOverTLS setting: %s",
mode < 0 ? "default" : dns_over_tls_mode_to_string(mode));
return sd_bus_reply_method_return(message, NULL);
}
@ -612,12 +661,16 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e
(void) link_save_user(l);
log_link_info(l, "Bus client set DNSSEC setting: %s",
mode < 0 ? "default" : dnssec_mode_to_string(mode));
return sd_bus_reply_method_return(message, NULL);
}
int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_set_free_free_ Set *ns = NULL;
_cleanup_strv_free_ char **ntas = NULL;
_cleanup_free_ char *j = NULL;
Link *l = userdata;
int r;
char **i;
@ -648,6 +701,9 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
r = set_put_strdup(&ns, *i);
if (r < 0)
return r;
if (!strextend_with_separator(&j, ", ", *i, NULL))
return -ENOMEM;
}
r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
@ -664,6 +720,11 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
(void) link_save_user(l);
if (j)
log_link_info(l, "Bus client set NTA list to: %s", j);
else
log_link_info(l, "Bus client reset NTA list.");
return sd_bus_reply_method_return(message, NULL);
}

View File

@ -313,6 +313,54 @@ static int manager_network_monitor_listen(Manager *m) {
return 0;
}
static int manager_clock_change_listen(Manager *m);
static int on_clock_change(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
assert(m);
/* The clock has changed, let's flush all caches. Why that? That's because DNSSEC validation takes
* the system clock into consideration, and if the clock changes the old validations might have been
* wrong. Let's redo all validation with the new, correct time.
*
* (Also, this is triggered after system suspend, which is also a good reason to drop caches, since
* we might be connected to a different network now without this being visible in a dropped link
* carrier or so.) */
log_info("Clock change detected. Flushing caches.");
manager_flush_caches(m, LOG_DEBUG /* downgrade the functions own log message, since we already logged here at LOG_INFO level */);
/* The clock change timerfd is unusable after it triggered once, create a new one. */
return manager_clock_change_listen(m);
}
static int manager_clock_change_listen(Manager *m) {
_cleanup_close_ int fd = -1;
int r;
assert(m);
m->clock_change_event_source = sd_event_source_unref(m->clock_change_event_source);
fd = time_change_fd();
if (fd < 0)
return log_error_errno(fd, "Failed to allocate clock change timer fd: %m");
r = sd_event_add_io(m->event, &m->clock_change_event_source, fd, EPOLLIN, on_clock_change, m);
if (r < 0)
return log_error_errno(r, "Failed to create clock change event source: %m");
r = sd_event_source_set_io_fd_own(m->clock_change_event_source, true);
if (r < 0)
return log_error_errno(r, "Failed to pass ownership of clock fd to event source: %m");
TAKE_FD(fd);
(void) sd_event_source_set_description(m->clock_change_event_source, "clock-change");
return 0;
}
static int determine_hostname(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) {
_cleanup_free_ char *h = NULL, *n = NULL;
#if HAVE_LIBIDN2
@ -549,7 +597,7 @@ static int manager_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si
assert(si);
assert(m);
manager_flush_caches(m);
manager_flush_caches(m, LOG_INFO);
return 0;
}
@ -593,9 +641,6 @@ int manager_new(Manager **ret) {
.read_resolv_conf = true,
.need_builtin_fallbacks = true,
.etc_hosts_last = USEC_INFINITY,
.etc_hosts_mtime = USEC_INFINITY,
.etc_hosts_ino = 0,
.etc_hosts_dev = 0,
.read_etc_hosts = true,
};
@ -642,6 +687,10 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
r = manager_clock_change_listen(m);
if (r < 0)
return r;
r = manager_connect_bus(m);
if (r < 0)
return r;
@ -709,6 +758,7 @@ Manager *manager_free(Manager *m) {
sd_netlink_unref(m->rtnl);
sd_event_source_unref(m->rtnl_event_source);
sd_event_source_unref(m->clock_change_event_source);
manager_llmnr_stop(m);
manager_mdns_stop(m);
@ -1440,7 +1490,7 @@ bool manager_routable(Manager *m) {
return false;
}
void manager_flush_caches(Manager *m) {
void manager_flush_caches(Manager *m, int log_level) {
DnsScope *scope;
assert(m);
@ -1448,7 +1498,7 @@ void manager_flush_caches(Manager *m) {
LIST_FOREACH(scopes, scope, m->dns_scopes)
dns_cache_flush(&scope->cache);
log_info("Flushed all caches.");
log_full(log_level, "Flushed all caches.");
}
void manager_reset_server_features(Manager *m) {

View File

@ -129,9 +129,8 @@ struct Manager {
/* Data from /etc/hosts */
EtcHosts etc_hosts;
usec_t etc_hosts_last, etc_hosts_mtime;
ino_t etc_hosts_ino;
dev_t etc_hosts_dev;
usec_t etc_hosts_last;
struct stat etc_hosts_stat;
bool read_etc_hosts;
OrderedSet *dns_extra_stub_listeners;
@ -143,6 +142,8 @@ struct Manager {
Hashmap *polkit_registry;
VarlinkServer *varlink_server;
sd_event_source *clock_change_event_source;
};
/* Manager */
@ -188,7 +189,7 @@ void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResource
bool manager_routable(Manager *m);
void manager_flush_caches(Manager *m);
void manager_flush_caches(Manager *m, int log_level);
void manager_reset_server_features(Manager *m);
void manager_cleanup_saved_user(Manager *m);

View File

@ -58,7 +58,7 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Could not create runtime directory: %m");
/* Drop privileges, but keep three caps. Note that we drop those too, later on (see below) */
/* Drop privileges, but keep three caps. Note that we drop two of those too, later on (see below) */
r = drop_privileges(uid, gid,
(UINT64_C(1) << CAP_NET_RAW)| /* needed for SO_BINDTODEVICE */
(UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */
@ -83,7 +83,7 @@ static int run(int argc, char *argv[]) {
(void) manager_check_resolv_conf(m);
/* Let's drop the remaining caps now */
r = capability_bounding_set_drop(0, true);
r = capability_bounding_set_drop((UINT64_C(1) << CAP_NET_RAW), true);
if (r < 0)
return log_error_errno(r, "Failed to drop remaining caps: %m");

View File

@ -1291,8 +1291,14 @@ int dns_name_apply_idna(const char *name, char **ret) {
assert(name);
assert(ret);
/* First, try non-transitional mode (i.e. IDN2008 rules) */
r = sym_idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
if (r == IDN2_DISALLOWED) /* If that failed, because of disallowed characters, try transitional mode.
* (i.e. IDN2003 rules which supports some unicode chars IDN2008 doesn't allow). */
r = sym_idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
IDN2_NFC_INPUT | IDN2_TRANSITIONAL);
log_debug("idn2_lookup_u8: %s → %s", name, t);
if (r == IDN2_OK) {
if (!startswith(name, "xn--")) {