summaryrefslogtreecommitdiffstats
path: root/src/shared/user-record.c
diff options
context:
space:
mode:
authorAdrian Vovk <adrianvovk@gmail.com>2024-01-09 00:11:43 +0100
committerLuca Boccassi <bluca@debian.org>2024-02-19 12:18:11 +0100
commit1b466c09401fe4896948b0a727ed670488a3cb07 (patch)
tree768257c65224046dfa1713b434e5c351a178376f /src/shared/user-record.c
parentDocument blob directory behavior (diff)
downloadsystemd-1b466c09401fe4896948b0a727ed670488a3cb07.tar.xz
systemd-1b466c09401fe4896948b0a727ed670488a3cb07.zip
user-record: Add blobDirectory and blobManifest
These fields are used to connect a JSON user record to its blob directory, and to include the directory's contents in the record's signature
Diffstat (limited to 'src/shared/user-record.c')
-rw-r--r--src/shared/user-record.c63
1 files changed, 63 insertions, 0 deletions
diff --git a/src/shared/user-record.c b/src/shared/user-record.c
index 38e5f01c23..fc39194ac5 100644
--- a/src/shared/user-record.c
+++ b/src/shared/user-record.c
@@ -15,11 +15,13 @@
#include "path-util.h"
#include "pkcs11-util.h"
#include "rlimit-util.h"
+#include "sha256.h"
#include "string-table.h"
#include "strv.h"
#include "uid-classification.h"
#include "user-record.h"
#include "user-util.h"
+#include "utf8.h"
#define DEFAULT_RATELIMIT_BURST 30
#define DEFAULT_RATELIMIT_INTERVAL_USEC (1*USEC_PER_MINUTE)
@@ -142,6 +144,9 @@ static UserRecord* user_record_free(UserRecord *h) {
free(h->location);
free(h->icon_name);
+ free(h->blob_directory);
+ hashmap_free(h->blob_manifest);
+
free(h->shell);
strv_free(h->environment);
@@ -1074,6 +1079,7 @@ static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispa
static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
static const JsonDispatch binding_dispatch_table[] = {
+ { "blobDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, blob_directory), 0 },
{ "imagePath", JSON_VARIANT_STRING, json_dispatch_image_path, offsetof(UserRecord, image_path), 0 },
{ "homeDirectory", JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, home_directory), 0 },
{ "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 },
@@ -1110,6 +1116,52 @@ static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatch
return json_dispatch(m, binding_dispatch_table, flags, userdata);
}
+static int dispatch_blob_manifest(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ _cleanup_hashmap_free_ Hashmap *manifest = NULL;
+ Hashmap **ret = ASSERT_PTR(userdata);
+ JsonVariant *value;
+ const char *key;
+ int r;
+
+ if (!variant)
+ return 0;
+
+ if (!json_variant_is_object(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+ JSON_VARIANT_OBJECT_FOREACH(key, value, variant) {
+ _cleanup_free_ char *filename = NULL;
+ _cleanup_free_ uint8_t *hash = NULL;
+
+ if (!json_variant_is_string(value))
+ return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Blob entry '%s' has invalid hash.", key);
+
+ if (!suitable_blob_filename(key))
+ return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Blob entry '%s' has invalid filename.", key);
+
+ filename = strdup(key);
+ if (!filename)
+ return json_log_oom(value, flags);
+
+ hash = malloc(SHA256_DIGEST_SIZE);
+ if (!hash)
+ return json_log_oom(value, flags);
+
+ r = parse_sha256(json_variant_string(value), hash);
+ if (r < 0)
+ return json_log(value, flags, r, "Blob entry '%s' has invalid hash: %s", filename, json_variant_string(value));
+
+ r = hashmap_ensure_put(&manifest, &path_hash_ops_free_free, filename, hash);
+ if (r < 0)
+ return json_log(value, flags, r, "Failed to insert blob manifest entry '%s': %m", filename);
+ TAKE_PTR(filename); /* Ownership transfers to hashmap */
+ TAKE_PTR(hash);
+ }
+
+ hashmap_free_and_replace(*ret, manifest);
+ return 0;
+}
+
int per_machine_id_match(JsonVariant *ids, JsonDispatchFlags flags) {
sd_id128_t mid;
int r;
@@ -1226,6 +1278,8 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp
static const JsonDispatch per_machine_dispatch_table[] = {
{ "matchMachineId", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
{ "matchHostname", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
+ { "blobDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, blob_directory), 0 },
+ { "blobManifest", JSON_VARIANT_OBJECT, dispatch_blob_manifest, offsetof(UserRecord, blob_manifest), 0 },
{ "iconName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, icon_name), JSON_SAFE },
{ "location", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, location), 0 },
{ "shell", JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
@@ -1560,6 +1614,8 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla
static const JsonDispatch user_dispatch_table[] = {
{ "userName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(UserRecord, user_name), JSON_RELAX},
{ "realm", JSON_VARIANT_STRING, json_dispatch_realm, offsetof(UserRecord, realm), 0 },
+ { "blobDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, blob_directory), 0 },
+ { "blobManifest", JSON_VARIANT_OBJECT, dispatch_blob_manifest, offsetof(UserRecord, blob_manifest), 0 },
{ "realName", JSON_VARIANT_STRING, json_dispatch_gecos, offsetof(UserRecord, real_name), 0 },
{ "emailAddress", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, email_address), JSON_SAFE },
{ "iconName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, icon_name), JSON_SAFE },
@@ -2373,6 +2429,13 @@ int user_record_test_password_change_required(UserRecord *h) {
return change_permitted ? 0 : -EROFS;
}
+int suitable_blob_filename(const char *name) {
+ /* Enforces filename requirements as described in docs/USER_RECORD_BULK_DIRS.md */
+ return filename_is_valid(name) &&
+ in_charset(name, URI_UNRESERVED) &&
+ name[0] != '.';
+}
+
static const char* const user_storage_table[_USER_STORAGE_MAX] = {
[USER_CLASSIC] = "classic",
[USER_LUKS] = "luks",