/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- 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/>.
-***/
#include <errno.h>
#include <netdb.h>
#include <nss.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "sd-bus.h"
#include "bus-common-errors.h"
+#include "errno-util.h"
#include "in-addr-util.h"
#include "macro.h"
#include "nss-util.h"
-#include "string-util.h"
-#include "util.h"
+#include "resolved-def.h"
#include "signal-util.h"
+#include "string-util.h"
NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
-#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
-
static bool bus_error_shall_fallback(sd_bus_error *e) {
return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) ||
return IN6_IS_ADDR_LINKLOCAL(&in6) ? ifindex : 0;
}
+static bool avoid_deadlock(void) {
+
+ /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
+ * code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to
+ * systemd-resolved if we are required to finish before it can be started. This of course won't detect all
+ * possible dead locks of this kind, but it should work for the most obvious cases. */
+
+ if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
+ return false;
+
+ return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-resolved.service") &&
+ streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
+}
+
enum nss_status _nss_resolve_gethostbyname4_r(
const char *name,
struct gaih_addrtuple **pat,
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- enum nss_status ret = NSS_STATUS_UNAVAIL;
const char *canonical = NULL;
size_t l, ms, idx;
char *r_name;
int c, r, i = 0;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
assert(errnop);
assert(h_errnop);
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
if (r < 0)
goto fail;
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
- if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
+ if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
+ !bus_error_shall_fallback(&error))
+ goto not_found;
/* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
allowing falling back to other nss modules. Treat all other error conditions as
NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
case so that the nsswitch.conf configuration can distuingish such executed but
negative replies from complete failure to talk to resolved). */
- if (!bus_error_shall_fallback(&error))
- ret = NSS_STATUS_NOTFOUND;
-
goto fail;
}
r = c;
goto fail;
}
- if (c == 0) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
+ if (c == 0)
+ goto not_found;
if (isempty(canonical))
canonical = name;
l = strlen(canonical);
ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
if (buflen < ms) {
+ UNPROTECT_ERRNO;
*errnop = ERANGE;
*h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
if (ttlp)
*ttlp = 0;
- /* Explicitly reset all error variables */
- *errnop = 0;
+ /* Explicitly reset both *h_errnop and h_errno to work around
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
*h_errnop = NETDB_SUCCESS;
h_errno = 0;
return NSS_STATUS_SUCCESS;
fail:
+ UNPROTECT_ERRNO;
*errnop = -r;
*h_errnop = NO_RECOVERY;
- return ret;
+ return NSS_STATUS_UNAVAIL;
+
+not_found:
+ *h_errnop = HOST_NOT_FOUND;
+ return NSS_STATUS_NOTFOUND;
}
enum nss_status _nss_resolve_gethostbyname3_r(
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *r_name, *r_aliases, *r_addr, *r_addr_list;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- enum nss_status ret = NSS_STATUS_UNAVAIL;
size_t l, idx, ms, alen;
const char *canonical;
int c, r, i = 0;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
goto fail;
}
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
if (r < 0)
goto fail;
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
- if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
-
- if (!bus_error_shall_fallback(&error))
- ret = NSS_STATUS_NOTFOUND;
+ if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
+ !bus_error_shall_fallback(&error))
+ goto not_found;
goto fail;
}
r = c;
goto fail;
}
- if (c == 0) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
+ if (c == 0)
+ goto not_found;
if (isempty(canonical))
canonical = name;
ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
if (buflen < ms) {
+ UNPROTECT_ERRNO;
*errnop = ERANGE;
*h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
result->h_length = alen;
result->h_addr_list = (char**) r_addr_list;
- /* Explicitly reset all error variables */
- *errnop = 0;
- *h_errnop = NETDB_SUCCESS;
- h_errno = 0;
-
if (ttlp)
*ttlp = 0;
if (canonp)
*canonp = r_name;
+ /* Explicitly reset both *h_errnop and h_errno to work around
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
+ *h_errnop = NETDB_SUCCESS;
+ h_errno = 0;
+
return NSS_STATUS_SUCCESS;
fail:
+ UNPROTECT_ERRNO;
*errnop = -r;
*h_errnop = NO_RECOVERY;
- return ret;
+ return NSS_STATUS_UNAVAIL;
+
+not_found:
+ *h_errnop = HOST_NOT_FOUND;
+ return NSS_STATUS_NOTFOUND;
}
enum nss_status _nss_resolve_gethostbyaddr2_r(
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *r_name, *r_aliases, *r_addr, *r_addr_list;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- enum nss_status ret = NSS_STATUS_UNAVAIL;
unsigned c = 0, i = 0;
size_t ms = 0, idx;
const char *n;
int r, ifindex;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(addr);
assert(h_errnop);
if (!IN_SET(af, AF_INET, AF_INET6)) {
+ UNPROTECT_ERRNO;
*errnop = EAFNOSUPPORT;
*h_errnop = NO_DATA;
return NSS_STATUS_UNAVAIL;
}
if (len != FAMILY_ADDRESS_SIZE(af)) {
+ UNPROTECT_ERRNO;
*errnop = EINVAL;
*h_errnop = NO_RECOVERY;
return NSS_STATUS_UNAVAIL;
}
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
if (r < 0)
goto fail;
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
- if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
-
- if (!bus_error_shall_fallback(&error))
- ret = NSS_STATUS_NOTFOUND;
+ if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
+ !bus_error_shall_fallback(&error))
+ goto not_found;
goto fail;
}
r = sd_bus_message_rewind(reply, false);
if (r < 0)
- return r;
+ goto fail;
- if (c <= 0) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
+ if (c <= 0)
+ goto not_found;
ms += ALIGN(len) + /* the address */
2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
if (buflen < ms) {
+ UNPROTECT_ERRNO;
*errnop = ERANGE;
*h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
if (ttlp)
*ttlp = 0;
- /* Explicitly reset all error variables */
- *errnop = 0;
+ /* Explicitly reset both *h_errnop and h_errno to work around
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
*h_errnop = NETDB_SUCCESS;
h_errno = 0;
return NSS_STATUS_SUCCESS;
fail:
+ UNPROTECT_ERRNO;
*errnop = -r;
*h_errnop = NO_RECOVERY;
- return ret;
+ return NSS_STATUS_UNAVAIL;
+
+not_found:
+ *h_errnop = HOST_NOT_FOUND;
+ return NSS_STATUS_NOTFOUND;
}
NSS_GETHOSTBYNAME_FALLBACKS(resolve);