2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2016-07-14 19:19:49 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2016 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 <nss.h>
|
|
|
|
|
|
|
|
#include "sd-bus.h"
|
|
|
|
|
2016-08-02 12:28:51 +02:00
|
|
|
#include "alloc-util.h"
|
2016-07-14 19:19:49 +02:00
|
|
|
#include "bus-common-errors.h"
|
|
|
|
#include "env-util.h"
|
2016-08-02 12:28:51 +02:00
|
|
|
#include "fs-util.h"
|
2016-07-14 19:19:49 +02:00
|
|
|
#include "macro.h"
|
|
|
|
#include "nss-util.h"
|
|
|
|
#include "signal-util.h"
|
2016-08-02 12:28:51 +02:00
|
|
|
#include "stdio-util.h"
|
2016-07-27 13:14:01 +02:00
|
|
|
#include "string-util.h"
|
2016-07-14 19:19:49 +02:00
|
|
|
#include "user-util.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
2018-03-06 07:39:26 +01:00
|
|
|
#define DYNAMIC_USER_GECOS "Dynamic User"
|
|
|
|
#define DYNAMIC_USER_PASSWD "*" /* locked */
|
|
|
|
#define DYNAMIC_USER_DIR "/"
|
|
|
|
#define DYNAMIC_USER_SHELL "/sbin/nologin"
|
|
|
|
|
2016-07-27 13:14:01 +02:00
|
|
|
static const struct passwd root_passwd = {
|
|
|
|
.pw_name = (char*) "root",
|
|
|
|
.pw_passwd = (char*) "x", /* see shadow file */
|
|
|
|
.pw_uid = 0,
|
|
|
|
.pw_gid = 0,
|
|
|
|
.pw_gecos = (char*) "Super User",
|
|
|
|
.pw_dir = (char*) "/root",
|
|
|
|
.pw_shell = (char*) "/bin/sh",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct passwd nobody_passwd = {
|
|
|
|
.pw_name = (char*) NOBODY_USER_NAME,
|
|
|
|
.pw_passwd = (char*) "*", /* locked */
|
2017-12-04 17:06:56 +01:00
|
|
|
.pw_uid = UID_NOBODY,
|
|
|
|
.pw_gid = GID_NOBODY,
|
2016-07-27 13:14:01 +02:00
|
|
|
.pw_gecos = (char*) "User Nobody",
|
|
|
|
.pw_dir = (char*) "/",
|
|
|
|
.pw_shell = (char*) "/sbin/nologin",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct group root_group = {
|
|
|
|
.gr_name = (char*) "root",
|
|
|
|
.gr_gid = 0,
|
|
|
|
.gr_passwd = (char*) "x", /* see shadow file */
|
|
|
|
.gr_mem = (char*[]) { NULL },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct group nobody_group = {
|
|
|
|
.gr_name = (char*) NOBODY_GROUP_NAME,
|
2017-12-04 17:06:56 +01:00
|
|
|
.gr_gid = GID_NOBODY,
|
2016-07-27 13:14:01 +02:00
|
|
|
.gr_passwd = (char*) "*", /* locked */
|
|
|
|
.gr_mem = (char*[]) { NULL },
|
|
|
|
};
|
|
|
|
|
2016-07-14 19:19:49 +02:00
|
|
|
NSS_GETPW_PROTOTYPES(systemd);
|
|
|
|
NSS_GETGR_PROTOTYPES(systemd);
|
|
|
|
|
2016-08-02 12:28:51 +02:00
|
|
|
static int direct_lookup_name(const char *name, uid_t *ret) {
|
|
|
|
_cleanup_free_ char *s = NULL;
|
|
|
|
const char *path;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(name);
|
|
|
|
|
|
|
|
/* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
|
|
|
|
* namespace and subject to proper authentication. However, there's one problem: if our module is called from
|
|
|
|
* dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
|
|
|
|
* and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
|
|
|
|
|
|
|
|
path = strjoina("/run/systemd/dynamic-uid/direct:", name);
|
|
|
|
r = readlink_malloc(path, &s);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return parse_uid(s, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int direct_lookup_uid(uid_t uid, char **ret) {
|
2017-12-14 19:02:29 +01:00
|
|
|
char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
|
2016-08-02 12:28:51 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
|
|
|
|
|
|
|
|
r = readlink_malloc(path, &s);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (!valid_user_group_name(s)) { /* extra safety check */
|
|
|
|
free(s);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret = s;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-14 19:19:49 +02:00
|
|
|
enum nss_status _nss_systemd_getpwnam_r(
|
|
|
|
const char *name,
|
|
|
|
struct passwd *pwd,
|
|
|
|
char *buffer, size_t buflen,
|
|
|
|
int *errnop) {
|
|
|
|
|
2018-03-06 13:31:16 +01:00
|
|
|
_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_flush_close_unrefp) sd_bus *bus = NULL;
|
2016-07-14 19:19:49 +02:00
|
|
|
uint32_t translated;
|
|
|
|
size_t l;
|
2017-10-04 19:29:36 +02:00
|
|
|
int bypass, r;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
|
|
|
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
|
|
|
|
|
|
|
|
assert(name);
|
|
|
|
assert(pwd);
|
|
|
|
|
nss: when we encounter an invalid user/group name or UID/GID, don't return EINVAL
It's not our business to validate invalid user/group names or UID/GID.
Ideally, libc would filter these out, but they don't, hence we have to
filter, but let's not propagate this as error, but simply as "not found"
to the caller.
User name rules are pretty vaguely defined, and the rules defined by
POSIX clash with reality quite heavily (for example, utmp doesn't offer
enough room for user name length, and /usr/bin/chown permits separating
user/group names by a single dot, even though POSIX allows dots being
used in user/group names themselves.) We enforce stricter rules than
POSIX for good reason, and hence in doing so we should not categorically
return EINVAL on stuff we don't consider valid, but other components
might.
Fixes: #4983
2016-12-27 17:59:38 +01:00
|
|
|
/* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
|
|
|
|
* generate EINVAL here, because it isn't really out business to complain about invalid user names. */
|
|
|
|
if (!valid_user_group_name(name))
|
|
|
|
goto not_found;
|
2016-07-27 13:14:01 +02:00
|
|
|
|
|
|
|
/* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
|
2017-09-14 06:20:39 +02:00
|
|
|
if (streq(name, root_passwd.pw_name)) {
|
|
|
|
*pwd = root_passwd;
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2018-01-10 18:26:03 +01:00
|
|
|
if (synthesize_nobody() &&
|
|
|
|
streq(name, nobody_passwd.pw_name)) {
|
2017-09-14 06:20:39 +02:00
|
|
|
*pwd = nobody_passwd;
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2016-07-27 13:14:01 +02:00
|
|
|
}
|
|
|
|
|
2016-07-14 19:19:49 +02:00
|
|
|
/* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
2016-07-14 19:19:49 +02:00
|
|
|
goto not_found;
|
|
|
|
|
2017-10-04 19:29:36 +02:00
|
|
|
bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
|
|
|
|
if (bypass <= 0) {
|
2016-08-02 12:28:51 +02:00
|
|
|
r = sd_bus_open_system(&bus);
|
2018-03-06 13:31:16 +01:00
|
|
|
if (r < 0)
|
2017-10-04 19:29:36 +02:00
|
|
|
bypass = 1;
|
2018-03-06 13:31:16 +01:00
|
|
|
}
|
2016-08-02 12:28:51 +02:00
|
|
|
|
2018-03-06 13:31:16 +01:00
|
|
|
if (bypass > 0) {
|
|
|
|
r = direct_lookup_name(name, (uid_t*) &translated);
|
|
|
|
if (r == -ENOENT)
|
|
|
|
goto not_found;
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
} else {
|
2016-08-02 12:28:51 +02:00
|
|
|
r = sd_bus_call_method(bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"LookupDynamicUserByName",
|
|
|
|
&error,
|
|
|
|
&reply,
|
|
|
|
"s",
|
|
|
|
name);
|
|
|
|
if (r < 0) {
|
|
|
|
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
|
|
|
|
goto not_found;
|
|
|
|
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_read(reply, "u", &translated);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
l = strlen(name);
|
|
|
|
if (buflen < l+1) {
|
2017-02-16 21:29:09 +01:00
|
|
|
*errnop = ERANGE;
|
2016-07-14 19:19:49 +02:00
|
|
|
return NSS_STATUS_TRYAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(buffer, name, l+1);
|
|
|
|
|
|
|
|
pwd->pw_name = buffer;
|
|
|
|
pwd->pw_uid = (uid_t) translated;
|
|
|
|
pwd->pw_gid = (uid_t) translated;
|
2018-03-06 07:39:26 +01:00
|
|
|
pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
|
|
|
|
pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
|
|
|
|
pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
|
|
|
|
pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
not_found:
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
*errnop = -r;
|
|
|
|
return NSS_STATUS_UNAVAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum nss_status _nss_systemd_getpwuid_r(
|
|
|
|
uid_t uid,
|
|
|
|
struct passwd *pwd,
|
|
|
|
char *buffer, size_t buflen,
|
|
|
|
int *errnop) {
|
|
|
|
|
|
|
|
_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_flush_close_unrefp) sd_bus *bus = NULL;
|
2016-08-02 12:28:51 +02:00
|
|
|
_cleanup_free_ char *direct = NULL;
|
2016-07-14 19:19:49 +02:00
|
|
|
const char *translated;
|
|
|
|
size_t l;
|
2017-10-04 19:29:36 +02:00
|
|
|
int bypass, r;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
|
|
|
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
|
|
|
|
|
nss: when we encounter an invalid user/group name or UID/GID, don't return EINVAL
It's not our business to validate invalid user/group names or UID/GID.
Ideally, libc would filter these out, but they don't, hence we have to
filter, but let's not propagate this as error, but simply as "not found"
to the caller.
User name rules are pretty vaguely defined, and the rules defined by
POSIX clash with reality quite heavily (for example, utmp doesn't offer
enough room for user name length, and /usr/bin/chown permits separating
user/group names by a single dot, even though POSIX allows dots being
used in user/group names themselves.) We enforce stricter rules than
POSIX for good reason, and hence in doing so we should not categorically
return EINVAL on stuff we don't consider valid, but other components
might.
Fixes: #4983
2016-12-27 17:59:38 +01:00
|
|
|
if (!uid_is_valid(uid))
|
|
|
|
goto not_found;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2016-07-27 13:14:01 +02:00
|
|
|
/* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
|
2017-09-14 06:20:39 +02:00
|
|
|
if (uid == root_passwd.pw_uid) {
|
|
|
|
*pwd = root_passwd;
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2018-01-10 18:26:03 +01:00
|
|
|
if (synthesize_nobody() &&
|
|
|
|
uid == nobody_passwd.pw_uid) {
|
2017-09-14 06:20:39 +02:00
|
|
|
*pwd = nobody_passwd;
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2016-07-27 13:14:01 +02:00
|
|
|
}
|
|
|
|
|
2017-12-02 13:07:18 +01:00
|
|
|
if (!uid_is_dynamic(uid))
|
2016-07-14 19:19:49 +02:00
|
|
|
goto not_found;
|
|
|
|
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
2016-07-14 19:19:49 +02:00
|
|
|
goto not_found;
|
|
|
|
|
2017-10-04 19:29:36 +02:00
|
|
|
bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
|
|
|
|
if (bypass <= 0) {
|
2016-08-02 12:28:51 +02:00
|
|
|
r = sd_bus_open_system(&bus);
|
2018-03-06 13:31:16 +01:00
|
|
|
if (r < 0)
|
2017-10-04 19:29:36 +02:00
|
|
|
bypass = 1;
|
2018-03-06 13:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bypass > 0) {
|
|
|
|
r = direct_lookup_uid(uid, &direct);
|
|
|
|
if (r == -ENOENT)
|
|
|
|
goto not_found;
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
translated = direct;
|
2016-08-02 12:28:51 +02:00
|
|
|
|
2018-03-06 13:31:16 +01:00
|
|
|
} else {
|
2016-08-02 12:28:51 +02:00
|
|
|
r = sd_bus_call_method(bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"LookupDynamicUserByUID",
|
|
|
|
&error,
|
|
|
|
&reply,
|
|
|
|
"u",
|
|
|
|
(uint32_t) uid);
|
|
|
|
if (r < 0) {
|
|
|
|
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
|
|
|
|
goto not_found;
|
|
|
|
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_read(reply, "s", &translated);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
l = strlen(translated) + 1;
|
|
|
|
if (buflen < l) {
|
2017-02-16 21:29:09 +01:00
|
|
|
*errnop = ERANGE;
|
2016-07-14 19:19:49 +02:00
|
|
|
return NSS_STATUS_TRYAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(buffer, translated, l);
|
|
|
|
|
|
|
|
pwd->pw_name = buffer;
|
|
|
|
pwd->pw_uid = uid;
|
|
|
|
pwd->pw_gid = uid;
|
2018-03-06 07:39:26 +01:00
|
|
|
pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
|
|
|
|
pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
|
|
|
|
pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
|
|
|
|
pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
not_found:
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
*errnop = -r;
|
|
|
|
return NSS_STATUS_UNAVAIL;
|
|
|
|
}
|
|
|
|
|
2018-02-05 11:07:40 +01:00
|
|
|
#pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
|
|
|
|
|
2016-07-14 19:19:49 +02:00
|
|
|
enum nss_status _nss_systemd_getgrnam_r(
|
|
|
|
const char *name,
|
|
|
|
struct group *gr,
|
|
|
|
char *buffer, size_t buflen,
|
|
|
|
int *errnop) {
|
|
|
|
|
2018-03-06 13:31:16 +01:00
|
|
|
_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_flush_close_unrefp) sd_bus *bus = NULL;
|
2016-07-14 19:19:49 +02:00
|
|
|
uint32_t translated;
|
|
|
|
size_t l;
|
2017-10-04 19:29:36 +02:00
|
|
|
int bypass, r;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
|
|
|
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
|
|
|
|
|
|
|
|
assert(name);
|
|
|
|
assert(gr);
|
|
|
|
|
nss: when we encounter an invalid user/group name or UID/GID, don't return EINVAL
It's not our business to validate invalid user/group names or UID/GID.
Ideally, libc would filter these out, but they don't, hence we have to
filter, but let's not propagate this as error, but simply as "not found"
to the caller.
User name rules are pretty vaguely defined, and the rules defined by
POSIX clash with reality quite heavily (for example, utmp doesn't offer
enough room for user name length, and /usr/bin/chown permits separating
user/group names by a single dot, even though POSIX allows dots being
used in user/group names themselves.) We enforce stricter rules than
POSIX for good reason, and hence in doing so we should not categorically
return EINVAL on stuff we don't consider valid, but other components
might.
Fixes: #4983
2016-12-27 17:59:38 +01:00
|
|
|
if (!valid_user_group_name(name))
|
|
|
|
goto not_found;
|
2016-07-27 13:14:01 +02:00
|
|
|
|
|
|
|
/* Synthesize records for root and nobody, in case they are missing form /etc/group */
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
|
2017-09-14 06:20:39 +02:00
|
|
|
if (streq(name, root_group.gr_name)) {
|
|
|
|
*gr = root_group;
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2018-01-10 18:26:03 +01:00
|
|
|
if (synthesize_nobody() &&
|
|
|
|
streq(name, nobody_group.gr_name)) {
|
2017-09-14 06:20:39 +02:00
|
|
|
*gr = nobody_group;
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2016-07-27 13:14:01 +02:00
|
|
|
}
|
|
|
|
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
2016-07-14 19:19:49 +02:00
|
|
|
goto not_found;
|
|
|
|
|
2017-10-04 19:29:36 +02:00
|
|
|
bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
|
|
|
|
if (bypass <= 0) {
|
2016-08-02 12:28:51 +02:00
|
|
|
r = sd_bus_open_system(&bus);
|
2018-03-06 13:31:16 +01:00
|
|
|
if (r < 0)
|
2017-10-04 19:29:36 +02:00
|
|
|
bypass = 1;
|
2018-03-06 13:31:16 +01:00
|
|
|
}
|
2016-08-02 12:28:51 +02:00
|
|
|
|
2018-03-06 13:31:16 +01:00
|
|
|
if (bypass > 0) {
|
|
|
|
r = direct_lookup_name(name, (uid_t*) &translated);
|
|
|
|
if (r == -ENOENT)
|
|
|
|
goto not_found;
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
} else {
|
2016-08-02 12:28:51 +02:00
|
|
|
r = sd_bus_call_method(bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"LookupDynamicUserByName",
|
|
|
|
&error,
|
|
|
|
&reply,
|
|
|
|
"s",
|
|
|
|
name);
|
|
|
|
if (r < 0) {
|
|
|
|
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
|
|
|
|
goto not_found;
|
|
|
|
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_read(reply, "u", &translated);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
l = sizeof(char*) + strlen(name) + 1;
|
|
|
|
if (buflen < l) {
|
2017-02-16 21:29:09 +01:00
|
|
|
*errnop = ERANGE;
|
2016-07-14 19:19:49 +02:00
|
|
|
return NSS_STATUS_TRYAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
memzero(buffer, sizeof(char*));
|
|
|
|
strcpy(buffer + sizeof(char*), name);
|
|
|
|
|
|
|
|
gr->gr_name = buffer + sizeof(char*);
|
|
|
|
gr->gr_gid = (gid_t) translated;
|
2018-03-06 07:39:26 +01:00
|
|
|
gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
|
2016-07-14 19:19:49 +02:00
|
|
|
gr->gr_mem = (char**) buffer;
|
|
|
|
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
not_found:
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
*errnop = -r;
|
|
|
|
return NSS_STATUS_UNAVAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum nss_status _nss_systemd_getgrgid_r(
|
|
|
|
gid_t gid,
|
|
|
|
struct group *gr,
|
|
|
|
char *buffer, size_t buflen,
|
|
|
|
int *errnop) {
|
|
|
|
|
|
|
|
_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_flush_close_unrefp) sd_bus *bus = NULL;
|
2016-08-02 12:28:51 +02:00
|
|
|
_cleanup_free_ char *direct = NULL;
|
2016-07-14 19:19:49 +02:00
|
|
|
const char *translated;
|
|
|
|
size_t l;
|
2017-10-04 19:29:36 +02:00
|
|
|
int bypass, r;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
|
|
|
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
|
|
|
|
|
nss: when we encounter an invalid user/group name or UID/GID, don't return EINVAL
It's not our business to validate invalid user/group names or UID/GID.
Ideally, libc would filter these out, but they don't, hence we have to
filter, but let's not propagate this as error, but simply as "not found"
to the caller.
User name rules are pretty vaguely defined, and the rules defined by
POSIX clash with reality quite heavily (for example, utmp doesn't offer
enough room for user name length, and /usr/bin/chown permits separating
user/group names by a single dot, even though POSIX allows dots being
used in user/group names themselves.) We enforce stricter rules than
POSIX for good reason, and hence in doing so we should not categorically
return EINVAL on stuff we don't consider valid, but other components
might.
Fixes: #4983
2016-12-27 17:59:38 +01:00
|
|
|
if (!gid_is_valid(gid))
|
|
|
|
goto not_found;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2016-07-27 13:14:01 +02:00
|
|
|
/* Synthesize records for root and nobody, in case they are missing from /etc/group */
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
|
2017-09-14 06:20:39 +02:00
|
|
|
if (gid == root_group.gr_gid) {
|
|
|
|
*gr = root_group;
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2018-01-10 18:26:03 +01:00
|
|
|
if (synthesize_nobody() &&
|
|
|
|
gid == nobody_group.gr_gid) {
|
2017-09-14 06:20:39 +02:00
|
|
|
*gr = nobody_group;
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2016-07-27 13:14:01 +02:00
|
|
|
}
|
|
|
|
|
2017-12-02 13:07:18 +01:00
|
|
|
if (!gid_is_dynamic(gid))
|
2016-07-14 19:19:49 +02:00
|
|
|
goto not_found;
|
|
|
|
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
2016-07-14 19:19:49 +02:00
|
|
|
goto not_found;
|
|
|
|
|
2017-10-04 19:29:36 +02:00
|
|
|
bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
|
|
|
|
if (bypass <= 0) {
|
2016-08-02 12:28:51 +02:00
|
|
|
r = sd_bus_open_system(&bus);
|
2018-03-06 13:31:16 +01:00
|
|
|
if (r < 0)
|
2017-10-04 19:29:36 +02:00
|
|
|
bypass = 1;
|
2018-03-06 13:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bypass > 0) {
|
|
|
|
r = direct_lookup_uid(gid, &direct);
|
|
|
|
if (r == -ENOENT)
|
|
|
|
goto not_found;
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
translated = direct;
|
2016-08-02 12:28:51 +02:00
|
|
|
|
2018-03-06 13:31:16 +01:00
|
|
|
} else {
|
2016-08-02 12:28:51 +02:00
|
|
|
r = sd_bus_call_method(bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"LookupDynamicUserByUID",
|
|
|
|
&error,
|
|
|
|
&reply,
|
|
|
|
"u",
|
|
|
|
(uint32_t) gid);
|
|
|
|
if (r < 0) {
|
|
|
|
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
|
|
|
|
goto not_found;
|
|
|
|
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_read(reply, "s", &translated);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
l = sizeof(char*) + strlen(translated) + 1;
|
|
|
|
if (buflen < l) {
|
2017-02-16 21:29:09 +01:00
|
|
|
*errnop = ERANGE;
|
2016-07-14 19:19:49 +02:00
|
|
|
return NSS_STATUS_TRYAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
memzero(buffer, sizeof(char*));
|
|
|
|
strcpy(buffer + sizeof(char*), translated);
|
|
|
|
|
|
|
|
gr->gr_name = buffer + sizeof(char*);
|
|
|
|
gr->gr_gid = gid;
|
2018-03-06 07:39:26 +01:00
|
|
|
gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
|
2016-07-14 19:19:49 +02:00
|
|
|
gr->gr_mem = (char**) buffer;
|
|
|
|
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
not_found:
|
|
|
|
*errnop = 0;
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
*errnop = -r;
|
|
|
|
return NSS_STATUS_UNAVAIL;
|
|
|
|
}
|