diff options
author | Adrian Vovk <adrianvovk@gmail.com> | 2024-01-09 00:11:43 +0100 |
---|---|---|
committer | Luca Boccassi <bluca@debian.org> | 2024-02-19 12:18:11 +0100 |
commit | 1b466c09401fe4896948b0a727ed670488a3cb07 (patch) | |
tree | 768257c65224046dfa1713b434e5c351a178376f /src/shared/user-record.c | |
parent | Document blob directory behavior (diff) | |
download | systemd-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.c | 63 |
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", |