diff --git a/src/basic/recurse-dir.c b/src/basic/recurse-dir.c index 9eef4e1d8c..f7f8e4a7ab 100644 --- a/src/basic/recurse-dir.c +++ b/src/basic/recurse-dir.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include +#include #include "alloc-util.h" #include "dirent-util.h" @@ -433,30 +434,25 @@ int recurse_dir( i, statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */ userdata); - if (r == RECURSE_DIR_LEAVE_DIRECTORY) - break; - if (r == RECURSE_DIR_SKIP_ENTRY) - continue; - if (r != RECURSE_DIR_CONTINUE) - return r; + if (r == RECURSE_DIR_CONTINUE) { + r = recurse_dir(subdir_fd, + p, + statx_mask, + n_depth_max - 1, + flags & ~RECURSE_DIR_TOPLEVEL, /* we already called the callback for this entry */ + func, + userdata); + if (r != 0) + return r; - r = recurse_dir(subdir_fd, - p, - statx_mask, - n_depth_max - 1, - flags & ~RECURSE_DIR_TOPLEVEL, /* we already called the callback for this entry */ - func, - userdata); - if (r != 0) - return r; - - r = func(RECURSE_DIR_LEAVE, - p, - dir_fd, - subdir_fd, - i, - statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */ - userdata); + r = func(RECURSE_DIR_LEAVE, + p, + dir_fd, + subdir_fd, + i, + statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */ + userdata); + } } else /* Non-directory inode */ r = func(RECURSE_DIR_ENTRY, @@ -469,7 +465,21 @@ int recurse_dir( if (r == RECURSE_DIR_LEAVE_DIRECTORY) break; - if (!IN_SET(r, RECURSE_DIR_SKIP_ENTRY, RECURSE_DIR_CONTINUE)) + + if (IN_SET(r, RECURSE_DIR_UNLINK, RECURSE_DIR_UNLINK_GRACEFUL)) { + int f = subdir_fd >= 0 ? AT_REMOVEDIR : 0; + + /* Close inodes before we try to delete them */ + subdir_fd = safe_close(subdir_fd); + inode_fd = safe_close(inode_fd); + + if (unlinkat(dir_fd, i->d_name, f) < 0) { + if (r != RECURSE_DIR_UNLINK_GRACEFUL) + return -errno; + + log_debug_errno(errno, "Unable to remove '%s', ignoring: %m", p); + } + } else if (!IN_SET(r, RECURSE_DIR_SKIP_ENTRY, RECURSE_DIR_CONTINUE)) return r; } diff --git a/src/basic/recurse-dir.h b/src/basic/recurse-dir.h index 68abb279f4..af33c239f7 100644 --- a/src/basic/recurse-dir.h +++ b/src/basic/recurse-dir.h @@ -53,6 +53,8 @@ typedef enum RecurseDirEvent { #define RECURSE_DIR_CONTINUE 0 #define RECURSE_DIR_LEAVE_DIRECTORY INT_MIN #define RECURSE_DIR_SKIP_ENTRY (INT_MIN+1) +#define RECURSE_DIR_UNLINK (INT_MIN+2) +#define RECURSE_DIR_UNLINK_GRACEFUL (INT_MIN+3) /* Make sure that the negative errno range and these two special returns don't overlap */ assert_cc(RECURSE_DIR_LEAVE_DIRECTORY < -ERRNO_MAX); diff --git a/src/fundamental/iovec-util-fundamental.h b/src/fundamental/iovec-util-fundamental.h index fbdd76398d..707274f86e 100644 --- a/src/fundamental/iovec-util-fundamental.h +++ b/src/fundamental/iovec-util-fundamental.h @@ -31,6 +31,15 @@ static inline void iovec_done(struct iovec *iovec) { iovec->iov_len = 0; } +static inline void iovec_done_many_and_free(struct iovec *iovec, size_t n) { + if (n > 0) { + assert(iovec); + FOREACH_ARRAY(j, iovec, n) + iovec_done(j); + } + free(iovec); +} + static inline bool iovec_is_set(const struct iovec *iovec) { /* Checks if the iovec points to a non-empty chunk of memory */ return iovec && iovec->iov_len > 0 && iovec->iov_base; diff --git a/src/import/import-tar.c b/src/import/import-tar.c index 5aa3a98640..ed96e27222 100644 --- a/src/import/import-tar.c +++ b/src/import/import-tar.c @@ -257,7 +257,7 @@ static int tar_import_fork_tar(TarImport *i) { return r; _cleanup_close_ int directory_fd = -EBADF; - r = mountfsd_make_directory(d, /* flags= */ 0, &directory_fd); + r = mountfsd_make_directory(d, MODE_INVALID, /* flags= */ 0, &directory_fd); if (r < 0) return r; diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index fe28d99d30..25b07b1192 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -297,7 +297,7 @@ static int tar_pull_make_local_copy(TarPull *p) { } _cleanup_close_ int directory_fd = -EBADF; - r = mountfsd_make_directory(t, /* flags= */ 0, &directory_fd); + r = mountfsd_make_directory(t, MODE_INVALID, /* flags= */ 0, &directory_fd); if (r < 0) return r; @@ -609,7 +609,7 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) { return r; _cleanup_close_ int directory_fd = -EBADF; - r = mountfsd_make_directory(where, /* flags= */ 0, &directory_fd); + r = mountfsd_make_directory(where, MODE_INVALID, /* flags= */ 0, &directory_fd); if (r < 0) return r; diff --git a/src/libsystemd/sd-json/json-util.c b/src/libsystemd/sd-json/json-util.c index 6f42239b91..30dbfde37b 100644 --- a/src/libsystemd/sd-json/json-util.c +++ b/src/libsystemd/sd-json/json-util.c @@ -701,3 +701,45 @@ int json_variant_new_fd_info(sd_json_variant **ret, int fd) { JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE("mountId", mntid), SD_JSON_BUILD_PAIR_VARIANT("fileHandle", w)); } + +int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + mode_t *m = ASSERT_PTR(userdata); + int r; + + if (sd_json_variant_is_null(variant)) { + *m = MODE_INVALID; + return 0; + } + + /* Let the SD_JSON_STRICT determine if we allow suid/sgid/sticky or not */ + mode_t limit = FLAGS_SET(flags, SD_JSON_STRICT) ? 0777 : 07777; + + if (sd_json_variant_is_string(variant)) { + /* NB: we parse the mode in the usual octal if a string is specified. */ + + mode_t mode; + r = parse_mode(sd_json_variant_string(variant), &mode); + if (r < 0) + return json_log(variant, flags, r, "JSON field '%s' is not a valid access mode string.", strna(name)); + + if (mode > limit) + return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), + "JSON field '%s' outside of valid range 0%s0%o.", + strna(name), glyph(GLYPH_ELLIPSIS), limit); + + *m = mode; + + } else if (sd_json_variant_is_unsigned(variant)) { + + uint64_t k = sd_json_variant_unsigned(variant); + if (k > (uint64_t) limit) + return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), + "JSON field '%s' outside of valid range 0%s0%o.", + strna(name), glyph(GLYPH_ELLIPSIS), limit); + + *m = (mode_t) k; + } else + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is neither a number nor a string.", strna(name)); + + return 0; +} diff --git a/src/libsystemd/sd-json/json-util.h b/src/libsystemd/sd-json/json-util.h index 753eb30f65..12c11a316a 100644 --- a/src/libsystemd/sd-json/json-util.h +++ b/src/libsystemd/sd-json/json-util.h @@ -127,6 +127,7 @@ int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dis int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_log_level(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_strv_environment(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); +int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); static inline int json_variant_unbase64_iovec(sd_json_variant *v, struct iovec *ret) { return sd_json_variant_unbase64(v, ret ? &ret->iov_base : NULL, ret ? &ret->iov_len : NULL); diff --git a/src/mountfsd/mountwork.c b/src/mountfsd/mountwork.c index 24f6e23e23..7de8784e32 100644 --- a/src/mountfsd/mountwork.c +++ b/src/mountfsd/mountwork.c @@ -124,7 +124,7 @@ static int validate_image_fd(int fd, MountImageParameters *p) { return r; } - fl = fd_verify_safe_flags(fd); + fl = fd_verify_safe_flags_full(fd, O_NONBLOCK); if (fl < 0) return log_debug_errno(fl, "Image file descriptor has unsafe flags set: %m"); @@ -353,6 +353,8 @@ static int vl_method_mount_image( } r = validate_image_fd(image_fd, &p); + if (r == -EREMOTEIO) + return sd_varlink_errorbo(link, "io.systemd.MountFileSystem.BadFileDescriptorFlags", SD_JSON_BUILD_PAIR_STRING("parameter", "imageFileDescriptor")); if (r < 0) return r; @@ -1003,6 +1005,7 @@ static int vl_method_mount_directory( typedef struct MakeDirectoryParameters { unsigned parent_fd_idx; const char *name; + mode_t mode; } MakeDirectoryParameters; static int vl_method_make_directory( @@ -1012,14 +1015,16 @@ static int vl_method_make_directory( void *userdata) { static const sd_json_dispatch_field dispatch_table[] = { - { "parentFileDescriptor", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(MakeDirectoryParameters, parent_fd_idx), SD_JSON_MANDATORY }, - { "name", SD_JSON_VARIANT_STRING, json_dispatch_const_filename, offsetof(MakeDirectoryParameters, name), SD_JSON_MANDATORY }, + { "parentFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(MakeDirectoryParameters, parent_fd_idx), SD_JSON_MANDATORY }, + { "name", SD_JSON_VARIANT_STRING, json_dispatch_const_filename, offsetof(MakeDirectoryParameters, name), SD_JSON_MANDATORY }, + { "mode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(MakeDirectoryParameters, mode), SD_JSON_STRICT }, VARLINK_DISPATCH_POLKIT_FIELD, {} }; MakeDirectoryParameters p = { .parent_fd_idx = UINT_MAX, + .mode = MODE_INVALID, }; Hashmap **polkit_registry = ASSERT_PTR(userdata); int r; @@ -1028,6 +1033,11 @@ static int vl_method_make_directory( if (r != 0) return r; + if (p.mode == MODE_INVALID) + p.mode = 0700; + else + p.mode &= 0775; /* refuse generating world writable dirs */ + if (p.parent_fd_idx == UINT_MAX) return sd_varlink_error_invalid_parameter_name(link, "parentFileDescriptor"); @@ -1089,11 +1099,11 @@ static int vl_method_make_directory( if (r < 0) return r; - _cleanup_close_ int fd = open_mkdir_at(parent_fd, t, O_CLOEXEC, 0700); + _cleanup_close_ int fd = open_mkdir_at(parent_fd, t, O_CLOEXEC, p.mode); if (fd < 0) return fd; - r = RET_NERRNO(fchmod(fd, 0700)); /* Set mode explicitly, as paranoia regarding umask games */ + r = RET_NERRNO(fchmod(fd, p.mode)); /* Set mode explicitly, as paranoia regarding umask games */ if (r < 0) goto fail; diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index b09181a73a..fa92b47172 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -1491,6 +1491,7 @@ static int unpriviled_clone(Image *i, const char *new_path) { _cleanup_close_ int new_fd = -EBADF; r = mountfsd_make_directory( new_path, + MODE_INVALID, /* flags= */ 0, &new_fd); if (r < 0) diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index bb83386c5a..28e40606ae 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -630,7 +630,35 @@ static void check_partition_flags( log_debug("Unexpected partition flag %llu set on %s!", bit, node); } } +#endif +static int make_image_name(const char *path, char **ret) { + int r; + + assert(path); + assert(ret); + + _cleanup_free_ char *filename = NULL; + r = path_extract_filename(path, &filename); + if (r < 0) + return r; + + _cleanup_free_ char *name = NULL; + r = raw_strip_suffixes(filename, &name); + if (r < 0) + return r; + + if (!image_name_is_valid(name)) { + log_debug("Image name %s is not valid, ignoring.", strna(name)); + *ret = NULL; + return 0; + } + + *ret = TAKE_PTR(name); + return 1; +} + +#if HAVE_BLKID static int dissected_image_new(const char *path, DissectedImage **ret) { _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; _cleanup_free_ char *name = NULL; @@ -639,20 +667,9 @@ static int dissected_image_new(const char *path, DissectedImage **ret) { assert(ret); if (path) { - _cleanup_free_ char *filename = NULL; - - r = path_extract_filename(path, &filename); + r = make_image_name(path, &name); if (r < 0) return r; - - r = raw_strip_suffixes(filename, &name); - if (r < 0) - return r; - - if (!image_name_is_valid(name)) { - log_debug("Image name %s is not valid, ignoring.", strna(name)); - name = mfree(name); - } } m = new(DissectedImage, 1); @@ -5022,8 +5039,8 @@ static void mount_image_reply_parameters_done(MountImageReplyParameters *p) { #endif -int mountfsd_mount_image( - const char *path, +int mountfsd_mount_image_fd( + int image_fd, int userns_fd, const ImagePolicy *image_policy, const VeritySettings *verity, @@ -5043,13 +5060,13 @@ int mountfsd_mount_image( }; _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL; - _cleanup_close_ int image_fd = -EBADF, verity_data_fd = -EBADF; + _cleanup_close_ int verity_data_fd = -EBADF; _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; _cleanup_free_ char *ps = NULL; const char *error_id; int r; - assert(path); + assert(image_fd >= 0); assert(ret); r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem"); @@ -5064,9 +5081,11 @@ int mountfsd_mount_image( if (r < 0) return log_error_errno(r, "Failed to enable varlink fd passing for write: %m"); - image_fd = open(path, O_RDONLY|O_CLOEXEC); + _cleanup_close_ int reopened_fd = -EBADF; + + image_fd = fd_reopen_condition(image_fd, O_CLOEXEC|O_NOCTTY|O_NONBLOCK|(FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY) ? O_RDONLY : O_RDWR), O_PATH, &reopened_fd); if (image_fd < 0) - return log_error_errno(errno, "Failed to open '%s': %m", path); + return log_error_errno(image_fd, "Failed to reopen fd: %m"); r = sd_varlink_push_dup_fd(vl, image_fd); if (r < 0) @@ -5159,7 +5178,7 @@ int mountfsd_mount_image( assert(pp.designator >= 0); if (!di) { - r = dissected_image_new(path, &di); + r = dissected_image_new(/* path= */ NULL, &di); if (r < 0) return log_error_errno(r, "Failed to allocated new dissected image structure: %m"); } @@ -5194,6 +5213,38 @@ int mountfsd_mount_image( #endif } +int mountfsd_mount_image( + const char *path, + int userns_fd, + const ImagePolicy *image_policy, + const VeritySettings *verity, + DissectImageFlags flags, + DissectedImage **ret) { + + int r; + + assert(path); + assert(ret); + + _cleanup_close_ int image_fd = open(path, O_RDONLY|O_CLOEXEC); + if (image_fd < 0) + return log_error_errno(errno, "Failed to open '%s': %m", path); + + _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL; + r = mountfsd_mount_image_fd(image_fd, userns_fd, image_policy, verity, flags, &di); + if (r < 0) + return r; + + if (!di->image_name) { + r = make_image_name(path, &di->image_name); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(di); + return 0; +} + int mountfsd_mount_directory_fd( int directory_fd, int userns_fd, @@ -5284,6 +5335,7 @@ int mountfsd_mount_directory( int mountfsd_make_directory_fd( int parent_fd, const char *name, + mode_t mode, DissectImageFlags flags, int *ret_directory_fd) { @@ -5291,7 +5343,6 @@ int mountfsd_make_directory_fd( assert(parent_fd >= 0); assert(name); - assert(ret_directory_fd); _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem"); @@ -5319,6 +5370,7 @@ int mountfsd_make_directory_fd( &error_id, SD_JSON_BUILD_PAIR_UNSIGNED("parentFileDescriptor", 0), SD_JSON_BUILD_PAIR_STRING("name", name), + SD_JSON_BUILD_PAIR_CONDITION(!IN_SET(mode, MODE_INVALID, 0700), "mode", SD_JSON_BUILD_UNSIGNED(mode)), /* suppress this field if default/unset */ SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH))); if (r < 0) return r; @@ -5337,12 +5389,14 @@ int mountfsd_make_directory_fd( if (directory_fd < 0) return log_error_errno(directory_fd, "Failed to take directory fd from Varlink connection: %m"); - *ret_directory_fd = TAKE_FD(directory_fd); + if (ret_directory_fd) + *ret_directory_fd = TAKE_FD(directory_fd); return 0; } int mountfsd_make_directory( const char *path, + mode_t mode, DissectImageFlags flags, int *ret_directory_fd) { @@ -5362,5 +5416,5 @@ int mountfsd_make_directory( if (fd < 0) return log_error_errno(r, "Failed to open '%s': %m", parent); - return mountfsd_make_directory_fd(fd, dirname, flags, ret_directory_fd); + return mountfsd_make_directory_fd(fd, dirname, mode, flags, ret_directory_fd); } diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 9650dce2a4..e22f14c61b 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -268,9 +268,10 @@ static inline const char* dissected_partition_fstype(const DissectedPartition *m int get_common_dissect_directory(char **ret); +int mountfsd_mount_image_fd(int image_fd, int userns_fd, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret); int mountfsd_mount_image(const char *path, int userns_fd, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret); int mountfsd_mount_directory_fd(int directory_fd, int userns_fd, DissectImageFlags flags, int *ret_mount_fd); int mountfsd_mount_directory(const char *path, int userns_fd, DissectImageFlags flags, int *ret_mount_fd); -int mountfsd_make_directory_fd(int parent_fd, const char *name, DissectImageFlags flags, int *ret_directory_fd); -int mountfsd_make_directory(const char *path, DissectImageFlags flags, int *ret_directory_fd); +int mountfsd_make_directory_fd(int parent_fd, const char *name, mode_t mode, DissectImageFlags flags, int *ret_directory_fd); +int mountfsd_make_directory(const char *path, mode_t mode, DissectImageFlags flags, int *ret_directory_fd); diff --git a/src/shared/user-record.c b/src/shared/user-record.c index 6bb174b9ca..e237f6e6ca 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -462,50 +462,6 @@ static int json_dispatch_image_path(const char *name, sd_json_variant *variant, return 0; } -static int json_dispatch_umask(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { - mode_t *m = userdata; - uint64_t k; - - if (sd_json_variant_is_null(variant)) { - *m = MODE_INVALID; - return 0; - } - - if (!sd_json_variant_is_unsigned(variant)) - return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name)); - - k = sd_json_variant_unsigned(variant); - if (k > 0777) - return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), - "JSON field '%s' outside of valid range 0%s0777.", - strna(name), glyph(GLYPH_ELLIPSIS)); - - *m = (mode_t) k; - return 0; -} - -static int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { - mode_t *m = userdata; - uint64_t k; - - if (sd_json_variant_is_null(variant)) { - *m = MODE_INVALID; - return 0; - } - - if (!sd_json_variant_is_unsigned(variant)) - return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name)); - - k = sd_json_variant_unsigned(variant); - if (k > 07777) - return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), - "JSON field '%s' outside of valid range 0%s07777.", - strna(name), glyph(GLYPH_ELLIPSIS)); - - *m = (mode_t) k; - return 0; -} - static int json_dispatch_locale(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { char **s = userdata; const char *n; @@ -1273,7 +1229,7 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j { "iconName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, icon_name), SD_JSON_STRICT }, { "location", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, location), 0 }, { "shell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 }, - { "umask", SD_JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 }, + { "umask", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, umask), SD_JSON_STRICT }, { "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_environment, offsetof(UserRecord, environment), 0 }, { "timeZone", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, time_zone), SD_JSON_STRICT }, { "preferredLanguage", SD_JSON_VARIANT_STRING, json_dispatch_locale, offsetof(UserRecord, preferred_language), 0 }, @@ -1287,7 +1243,7 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j { "diskSize", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size), 0 }, { "diskSizeRelative", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 }, { "skeletonDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), SD_JSON_STRICT }, - { "accessMode", SD_JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 }, + { "accessMode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 }, { "tasksMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 }, { "memoryHigh", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 }, { "memoryMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 }, @@ -1397,7 +1353,7 @@ static int dispatch_status(const char *name, sd_json_variant *variant, sd_json_d { "rateLimitBeginUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, ratelimit_begin_usec), 0 }, { "rateLimitCount", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, ratelimit_count), 0 }, { "removable", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(UserRecord, removable), 0 }, - { "accessMode", SD_JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 }, + { "accessMode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 }, { "fileSystemType", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, file_system_type), SD_JSON_STRICT }, { "fallbackShell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, fallback_shell), 0 }, { "fallbackHomeDirectory", SD_JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, fallback_home_directory), 0 }, @@ -1633,7 +1589,7 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load { "lastChangeUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, last_change_usec), 0 }, { "lastPasswordChangeUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, last_password_change_usec), 0 }, { "shell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 }, - { "umask", SD_JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 }, + { "umask", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, umask), SD_JSON_STRICT }, { "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_environment, offsetof(UserRecord, environment), 0 }, { "timeZone", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, time_zone), SD_JSON_STRICT }, { "preferredLanguage", SD_JSON_VARIANT_STRING, json_dispatch_locale, offsetof(UserRecord, preferred_language), 0 }, @@ -1647,7 +1603,7 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load { "diskSize", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size), 0 }, { "diskSizeRelative", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 }, { "skeletonDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), SD_JSON_STRICT }, - { "accessMode", SD_JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 }, + { "accessMode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 }, { "tasksMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 }, { "memoryHigh", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 }, { "memoryMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 }, diff --git a/src/shared/varlink-io.systemd.MountFileSystem.c b/src/shared/varlink-io.systemd.MountFileSystem.c index e9c69f07d8..356f9938aa 100644 --- a/src/shared/varlink-io.systemd.MountFileSystem.c +++ b/src/shared/varlink-io.systemd.MountFileSystem.c @@ -113,6 +113,8 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_DEFINE_INPUT(parentFileDescriptor, SD_VARLINK_INT, 0), SD_VARLINK_FIELD_COMMENT("Name of the directory to create."), SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("Access mode of the directory to create. Note that the suid, sgid, sticky, world-writable bit is unconditionally masked off."), + SD_VARLINK_DEFINE_INPUT(mode, SD_VARLINK_INT, SD_VARLINK_NULLABLE), VARLINK_DEFINE_POLKIT_INPUT, SD_VARLINK_FIELD_COMMENT("File descriptor referencing the newly created directory."), SD_VARLINK_DEFINE_OUTPUT(directoryFileDescriptor, SD_VARLINK_INT, 0)); diff --git a/src/test/test-json.c b/src/test/test-json.c index 64a0de3fe9..1f0e07d7d4 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -1477,4 +1477,50 @@ TEST(unit_name) { &data), EINVAL); } +TEST(access_mode) { + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + ASSERT_OK(sd_json_parse("{" + " \"a\" : \"0755\", " + " \"b\" : 448, " + " \"c\" : null, " + " \"d\" : \"01755\" " + "}", + /* flags= */ 0, + &v, + /* reterr_line= */ NULL, + /* reterr_column= */ NULL)); + + struct { + mode_t a, b, c, d; + } mm = { 1, 2, 3, 4 }; + + ASSERT_OK(sd_json_dispatch( + v, + (const sd_json_dispatch_field[]) { + { "a", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, a), 0 }, + { "b", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, b), 0 }, + { "c", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, c), 0 }, + { "d", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, d), 0 }, + {}, + }, + /* flags= */ 0, + &mm)); + + ASSERT_EQ(mm.a, (mode_t) 0755); + ASSERT_EQ(mm.b, (mode_t) 0700); + ASSERT_EQ(mm.c, MODE_INVALID); + ASSERT_EQ(mm.d, (mode_t) 01755); + + /* retry with SD_JSON_STRICT, where 'd' should not parse anymore */ + ASSERT_ERROR(sd_json_dispatch( + v, + (const sd_json_dispatch_field[]) { + { "d", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, d), SD_JSON_STRICT }, + {}, + }, + /* flags= */ SD_JSON_ALLOW_EXTENSIONS, + &mm), ERANGE); +} + DEFINE_TEST_MAIN(LOG_DEBUG);