Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(RHEL-30645) resolved: never return ::1 when localhost or local hostname is resolved while IPv6 is off in the kernel #438

Open
wants to merge 11 commits into
base: rhel-8.10.0
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions src/basic/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ basic_sources = files('''
strv.h
strxcpyx.c
strxcpyx.h
sysctl-util.c
sysctl-util.h
syslog-util.c
syslog-util.h
terminal-util.c
Expand Down
43 changes: 41 additions & 2 deletions src/basic/socket-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "sysctl-util.h"
#include "user-util.h"
#include "utf8.h"
#include "util.h"
Expand Down Expand Up @@ -487,10 +488,48 @@ const char* socket_address_get_path(const SocketAddress *a) {
}

bool socket_ipv6_is_supported(void) {
if (access("/proc/net/if_inet6", F_OK) != 0)
static int cached = -1;

if (cached < 0) {

if (access("/proc/net/if_inet6", F_OK) < 0) {

if (errno != ENOENT) {
log_debug_errno(errno, "Unexpected error when checking whether /proc/net/if_inet6 exists: %m");
return false;
}

cached = false;
} else
cached = true;
}

return cached;
}

bool socket_ipv6_is_enabled(void) {
_cleanup_free_ char *v = NULL;
int r;

/* Much like socket_ipv6_is_supported(), but also checks that the sysctl that disables IPv6 on all
* interfaces isn't turned on */

if (!socket_ipv6_is_supported())
return false;

return true;
r = sysctl_read_ip_property(AF_INET6, "all", "disable_ipv6", &v);
if (r < 0) {
log_debug_errno(r, "Unexpected error reading 'net.ipv6.conf.all.disable_ipv6' sysctl: %m");
return true;
}

r = parse_boolean(v);
if (r < 0) {
log_debug_errno(r, "Failed to pare 'net.ipv6.conf.all.disable_ipv6' sysctl: %m");
return true;
}

return !r;
}

bool socket_address_matches_fd(const SocketAddress *a, int fd) {
Expand Down
1 change: 1 addition & 0 deletions src/basic/socket-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_
const char* socket_address_get_path(const SocketAddress *a);

bool socket_ipv6_is_supported(void);
bool socket_ipv6_is_enabled(void);

int sockaddr_port(const struct sockaddr *_sa, unsigned *port);

Expand Down
29 changes: 26 additions & 3 deletions src/shared/sysctl-util.c → src/basic/sysctl-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "fileio.h"
#include "log.h"
#include "macro.h"
#include "alloc-util.h"
#include "string-util.h"
#include "sysctl-util.h"

Expand Down Expand Up @@ -60,12 +61,34 @@ int sysctl_write(const char *property, const char *value) {
return 0;
}

int sysctl_read(const char *property, char **content) {
int sysctl_read(const char *property, char **ret) {
char *p;

assert(property);
assert(content);
assert(ret);

p = strjoina("/proc/sys/", property);
return read_full_file(p, content, NULL);
return read_full_virtual_file(p, ret, NULL);
}

int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret) {
_cleanup_free_ char *value = NULL;
const char *p;
int r;

assert(IN_SET(af, AF_INET, AF_INET6));
assert(property);

p = strjoina("/proc/sys/net/ipv", af == AF_INET ? "4" : "6",
ifname ? "/conf/" : "", strempty(ifname),
property[0] == '/' ? "" : "/", property);

r = read_one_line_file(p, &value);
if (r < 0)
return r;

if (ret)
*ret = TAKE_PTR(value);

return r;
}
1 change: 1 addition & 0 deletions src/shared/sysctl-util.h → src/basic/sysctl-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ char *sysctl_normalize(char *s);
int sysctl_read(const char *property, char **value);
int sysctl_write(const char *property, const char *value);

int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret);
98 changes: 52 additions & 46 deletions src/nss-myhostname/nss-myhostname.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "macro.h"
#include "nss-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "string-util.h"
#include "util.h"

Expand Down Expand Up @@ -64,10 +65,8 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
} else if (is_gateway_hostname(name)) {

n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
if (n_addresses <= 0) {
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
if (n_addresses <= 0)
goto not_found;

canonical = "_gateway";

Expand All @@ -81,10 +80,8 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
}

/* We respond to our local host name, our hostname suffixed with a single dot. */
if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
if (!streq(name, hn) && !streq_ptr(startswith(name, hn), "."))
goto not_found;

n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
if (n_addresses < 0)
Expand All @@ -95,7 +92,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
}

l = strlen(canonical);
ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 1 + socket_ipv6_is_enabled());
if (buflen < ms) {
UNPROTECT_ERRNO;
*errnop = ERANGE;
Expand All @@ -111,15 +108,17 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
assert(n_addresses >= 0);
if (n_addresses == 0) {
/* Second, fill in IPv6 tuple */
r_tuple = (struct gaih_addrtuple*) (buffer + idx);
r_tuple->next = r_tuple_prev;
r_tuple->name = r_name;
r_tuple->family = AF_INET6;
memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
r_tuple->scopeid = 0;

idx += ALIGN(sizeof(struct gaih_addrtuple));
r_tuple_prev = r_tuple;
if (socket_ipv6_is_enabled()) {
r_tuple = (struct gaih_addrtuple*) (buffer + idx);
r_tuple->next = r_tuple_prev;
r_tuple->name = r_name;
r_tuple->family = AF_INET6;
memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
r_tuple->scopeid = 0;

idx += ALIGN(sizeof(struct gaih_addrtuple));
r_tuple_prev = r_tuple;
}

/* Third, fill in IPv4 tuple */
r_tuple = (struct gaih_addrtuple*) (buffer + idx);
Expand Down Expand Up @@ -164,6 +163,10 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
h_errno = 0;

return NSS_STATUS_SUCCESS;

not_found:
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}

static enum nss_status fill_in_hostent(
Expand All @@ -183,6 +186,7 @@ static enum nss_status fill_in_hostent(
unsigned n, c;

assert(canonical);
assert(IN_SET(af, AF_INET, AF_INET6));
assert(result);
assert(buffer);
assert(errnop);
Expand All @@ -202,8 +206,8 @@ static enum nss_status fill_in_hostent(
(additional ? ALIGN(l_additional+1) : 0) +
sizeof(char*) +
(additional ? sizeof(char*) : 0) +
(c > 0 ? c : 1) * ALIGN(alen) +
(c > 0 ? c+1 : 2) * sizeof(char*);
(c > 0 ? c : af == AF_INET ? 1 : socket_ipv6_is_enabled()) * ALIGN(alen) +
(c > 0 ? c+1 : af == AF_INET ? 2 : (unsigned) socket_ipv6_is_enabled() + 1) * sizeof(char*);

if (buflen < ms) {
UNPROTECT_ERRNO;
Expand Down Expand Up @@ -249,12 +253,12 @@ static enum nss_status fill_in_hostent(

assert(i == c);
idx += c*ALIGN(alen);
} else {
if (af == AF_INET)
*(uint32_t*) r_addr = local_address_ipv4;
else
memcpy(r_addr, LOCALADDRESS_IPV6, 16);

} else if (af == AF_INET) {
*(uint32_t*) r_addr = local_address_ipv4;
idx += ALIGN(alen);
} else if (socket_ipv6_is_enabled()) {
memcpy(r_addr, LOCALADDRESS_IPV6, 16);
idx += ALIGN(alen);
}

Expand All @@ -269,10 +273,13 @@ static enum nss_status fill_in_hostent(
((char**) r_addr_list)[i] = NULL;
idx += (c+1) * sizeof(char*);

} else {
} else if (af == AF_INET || socket_ipv6_is_enabled()) {
((char**) r_addr_list)[0] = r_addr;
((char**) r_addr_list)[1] = NULL;
idx += 2 * sizeof(char*);
} else {
((char**) r_addr_list)[0] = NULL;
idx += sizeof(char*);
}

/* Verify the size matches */
Expand Down Expand Up @@ -333,16 +340,17 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
}

if (is_localhost(name)) {
if (af == AF_INET6 && !socket_ipv6_is_enabled())
goto not_found;

canonical = "localhost";
local_address_ipv4 = htobe32(INADDR_LOOPBACK);

} else if (is_gateway_hostname(name)) {

n_addresses = local_gateways(NULL, 0, af, &addresses);
if (n_addresses <= 0) {
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
if (n_addresses <= 0)
goto not_found;

canonical = "_gateway";

Expand All @@ -355,10 +363,8 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
return NSS_STATUS_TRYAGAIN;
}

if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
if (!streq(name, hn) && !streq_ptr(startswith(name, hn), "."))
goto not_found;

n_addresses = local_addresses(NULL, 0, af, &addresses);
if (n_addresses < 0)
Expand All @@ -381,6 +387,10 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
errnop, h_errnop,
ttlp,
canonp);

not_found:
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}

enum nss_status _nss_myhostname_gethostbyaddr2_r(
Expand Down Expand Up @@ -436,35 +446,31 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
} else {
assert(af == AF_INET6);

if (socket_ipv6_is_enabled())
goto not_found;

if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
canonical = "localhost";
additional_from_hostname = true;
goto found;
}
}

n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
if (af != a->family)
continue;

n_addresses = local_addresses(NULL, 0, af, &addresses);
for (a = addresses, n = 0; (int) n < n_addresses; n++, a++)
if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
goto found;
}

addresses = mfree(addresses);

n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
if (af != a->family)
continue;

n_addresses = local_gateways(NULL, 0, af, &addresses);
for (a = addresses, n = 0; (int) n < n_addresses; n++, a++)
if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
canonical = "_gateway";
goto found;
}
}

not_found:
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;

Expand Down
6 changes: 6 additions & 0 deletions src/resolve/resolved-dns-question.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "dns-domain.h"
#include "dns-type.h"
#include "resolved-dns-question.h"
#include "socket-util.h"

DnsQuestion *dns_question_new(size_t n) {
DnsQuestion *q;
Expand Down Expand Up @@ -287,6 +288,11 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo
if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
return -EAFNOSUPPORT;

/* If IPv6 is off and the request has an unspecified lookup family, restrict it automatically to
* IPv4. */
if (family == AF_UNSPEC && !socket_ipv6_is_enabled())
family = AF_INET;

if (convert_idna) {
r = dns_name_apply_idna(name, &buf);
if (r < 0)
Expand Down
4 changes: 2 additions & 2 deletions src/resolve/resolved-dns-synthesize.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int if
return r;
}

if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) {
if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY) && socket_ipv6_is_enabled()) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;

rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, dns_resource_key_name(key));
Expand Down Expand Up @@ -233,7 +233,7 @@ static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key,
.address.in.s_addr = htobe32(0x7F000002),
};

if (IN_SET(af, AF_INET6, AF_UNSPEC))
if (IN_SET(af, AF_INET6, AF_UNSPEC) && socket_ipv6_is_enabled())
buffer[n++] = (struct local_address) {
.family = AF_INET6,
.ifindex = dns_synthesize_ifindex(ifindex),
Expand Down
2 changes: 0 additions & 2 deletions src/shared/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,6 @@ shared_sources = files('''
specifier.h
switch-root.c
switch-root.h
sysctl-util.c
sysctl-util.h
tests.c
tests.h
tomoyo-util.c
Expand Down
Loading
Loading