1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-18 10:04:04 +03:00

nsresourced: add ability to mangle specified name if necessary

Let's optionally mangle any passed name on the server side so that it is
useful for identifying a userns, if it isn't suitable for that
right-away. This mostl means truncating it if too long.

It's just too nasty to leave this to the client side, since they'd have
to understand the precise rules for naming userns then.

While we are at it, add full Varlink IDL comments.
This commit is contained in:
Lennart Poettering 2024-11-27 10:23:02 +01:00
parent 86895e29c8
commit c448f9c015
4 changed files with 85 additions and 13 deletions

View File

@ -7,6 +7,7 @@
#include <sys/eventfd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <utmpx.h>
#include "sd-daemon.h"
#include "sd-netlink.h"
@ -608,8 +609,28 @@ static int test_userns_api_support(sd_varlink *link) {
return 0;
}
static int validate_name(sd_varlink *link, const char *name, char **ret) {
_cleanup_free_ char *un = NULL;
static char *random_name(void) {
char *s = NULL;
if (asprintf(&s, "r%016" PRIx64, random_u64()) < 0)
return NULL;
return s;
}
static char *shorten_name(const char *name) {
char *n = strdup(name);
if (!n)
return NULL;
/* make sure it fits into utmpx even if prefixed with "ns-" and suffixed by "-65535" */
strshorten(n, sizeof_field(struct utmpx, ut_user) - 3 - 6);
return n;
}
static int validate_name(sd_varlink *link, const char *name, bool mangle, char **ret) {
int r;
assert(link);
@ -621,13 +642,25 @@ static int validate_name(sd_varlink *link, const char *name, char **ret) {
if (r < 0)
return r;
_cleanup_free_ char *un = NULL;
if (peer_uid == 0) {
if (!userns_name_is_valid(name))
return sd_varlink_error_invalid_parameter_name(link, "name");
if (userns_name_is_valid(name)) {
un = strdup(name);
if (!un)
return -ENOMEM;
} else if (mangle) {
un = shorten_name(name);
if (!un)
return -ENOMEM;
un = strdup(name);
if (!un)
return -ENOMEM;
if (!userns_name_is_valid(un)) {
free(un);
un = random_name();
if (!un)
return -ENOMEM;
}
}
} else {
/* The the client is not root then prefix the name with the UID of the peer, so that they
* live in separate namespaces and cannot steal each other's names. */
@ -635,10 +668,26 @@ static int validate_name(sd_varlink *link, const char *name, char **ret) {
if (asprintf(&un, UID_FMT "-%s", peer_uid, name) < 0)
return -ENOMEM;
if (!userns_name_is_valid(un))
return sd_varlink_error_invalid_parameter_name(link, "name");
if (!userns_name_is_valid(un) && mangle) {
_cleanup_free_ char *c = shorten_name(un);
if (!c)
return -ENOMEM;
if (userns_name_is_valid(c))
free_and_replace(un, c);
else {
_cleanup_free_ char *rnd = random_name();
un = mfree(un);
if (asprintf(&un, UID_FMT "-%s", peer_uid, rnd) < 0)
return -ENOMEM;
}
}
}
if (!userns_name_is_valid(un))
return sd_varlink_error_invalid_parameter_name(link, "name");
*ret = TAKE_PTR(un);
return 0;
}
@ -727,6 +776,7 @@ typedef struct AllocateParameters {
unsigned size;
unsigned target;
unsigned userns_fd_idx;
bool mangle_name;
} AllocateParameters;
static int vl_method_allocate_user_range(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
@ -736,6 +786,7 @@ static int vl_method_allocate_user_range(sd_varlink *link, sd_json_variant *para
{ "size", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(AllocateParameters, size), SD_JSON_MANDATORY },
{ "target", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(AllocateParameters, target), 0 },
{ "userNamespaceFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(AllocateParameters, userns_fd_idx), SD_JSON_MANDATORY },
{ "mangleName", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(AllocateParameters, mangle_name), 0 },
{}
};
@ -761,7 +812,7 @@ static int vl_method_allocate_user_range(sd_varlink *link, sd_json_variant *para
if (r != 0)
return r;
r = validate_name(link, p.name, &userns_name);
r = validate_name(link, p.name, p.mangle_name, &userns_name);
if (r != 0)
return r;
@ -928,6 +979,7 @@ static int validate_userns_is_safe(sd_varlink *link, int userns_fd) {
typedef struct RegisterParameters {
const char *name;
unsigned userns_fd_idx;
bool mangle_name;
} RegisterParameters;
static int vl_method_register_user_namespace(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
@ -935,6 +987,7 @@ static int vl_method_register_user_namespace(sd_varlink *link, sd_json_variant *
static const sd_json_dispatch_field dispatch_table[] = {
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(RegisterParameters, name), SD_JSON_MANDATORY },
{ "userNamespaceFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(RegisterParameters, userns_fd_idx), SD_JSON_MANDATORY },
{ "mangleName", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(AllocateParameters, mangle_name), 0 },
{}
};
@ -959,7 +1012,7 @@ static int vl_method_register_user_namespace(sd_varlink *link, sd_json_variant *
if (r != 0)
return r;
r = validate_name(link, p.name, &userns_name);
r = validate_name(link, p.name, p.mangle_name, &userns_name);
if (r != 0)
return r;

View File

@ -592,6 +592,9 @@ bool userns_name_is_valid(const char *name) {
/* Checks if the specified string is suitable as user namespace name. */
if (isempty(name))
return false;
if (strlen(name) > NAME_MAX) /* before we use alloca(), let's check for size */
return false;

View File

@ -83,6 +83,7 @@ int nsresource_allocate_userns(const char *name, uint64_t size) {
&reply,
&error_id,
SD_JSON_BUILD_PAIR("name", SD_JSON_BUILD_STRING(name)),
SD_JSON_BUILD_PAIR("mangleName", SD_JSON_BUILD_BOOLEAN(true)),
SD_JSON_BUILD_PAIR("size", SD_JSON_BUILD_UNSIGNED(size)),
SD_JSON_BUILD_PAIR("userNamespaceFileDescriptor", SD_JSON_BUILD_UNSIGNED(userns_fd_idx)));
if (r < 0)
@ -139,6 +140,7 @@ int nsresource_register_userns(const char *name, int userns_fd) {
&reply,
&error_id,
SD_JSON_BUILD_PAIR("name", SD_JSON_BUILD_STRING(name)),
SD_JSON_BUILD_PAIR("mangleName", SD_JSON_BUILD_BOOLEAN(true)),
SD_JSON_BUILD_PAIR("userNamespaceFileDescriptor", SD_JSON_BUILD_UNSIGNED(userns_fd_idx)));
if (r < 0)
return log_debug_errno(r, "Failed to call RegisterUserNamespace() varlink call: %m");

View File

@ -4,15 +4,29 @@
static SD_VARLINK_DEFINE_METHOD(
AllocateUserRange,
SD_VARLINK_FIELD_COMMENT("The name for the user namespce, a short string that must be fit to be included in a file name and in a user name"),
SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Controls whether to mangle the provided name if needed so that it is suitable for naming a user namespace. If true this will shorten the name as necessary or randomize it if that's not sufficient. If null defaults to false."),
SD_VARLINK_DEFINE_INPUT(mangleName, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The number of UIDs to assign. Must be 1 or 65536"),
SD_VARLINK_DEFINE_INPUT(size, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("The target UID inside the user namespace"),
SD_VARLINK_DEFINE_INPUT(target, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, SD_VARLINK_INT, 0));
SD_VARLINK_FIELD_COMMENT("A file descriptor to an allocated userns with no current UID range assignments"),
SD_VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("The name assigned to the user namespace"),
SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
RegisterUserNamespace,
SD_VARLINK_FIELD_COMMENT("The name for the user namespce, a short string that must be fit to be included in a file name and in a user name"),
SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, SD_VARLINK_INT, 0));
SD_VARLINK_FIELD_COMMENT("Controls whether to mangle the provided name if needed so that it is suitable for naming a user namespace. If true this will shorten the name as necessary or randomize it if that's not sufficient. If null defaults to false."),
SD_VARLINK_DEFINE_INPUT(mangleName, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("A user namespace file descriptor that is fully initialized"),
SD_VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("The name assigned to the user namespace"),
SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
AddMountToUserNamespace,