]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
user-record: add fields for setting limits on /tmp/ and /dev/shm/
authorLennart Poettering <lennart@poettering.net>
Fri, 10 Jan 2025 10:33:59 +0000 (11:33 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 23 Jan 2025 21:16:24 +0000 (22:16 +0100)
docs/USER_RECORD.md
src/shared/user-record-show.c
src/shared/user-record.c
src/shared/user-record.h

index a8e02b2c5ec4d47e6285205e457b6f9dc5c16f89..ae1173c560556e220773a1107a3dea93c2bfa1ae 100644 (file)
@@ -619,6 +619,19 @@ is allowed to edit.
 `selfModifiablePrivileged` →  Similar to `selfModifiableFields`, but it lists fields in
 the `privileged` section that the user is allowed to edit.
 
+`tmpLimit` → A numeric value encoding a disk quota limit in bytes enforced on
+`/tmp/` on login, in case it is backed by volatile file system (such as
+`tmpfs`).
+
+`tmpLimitScale` → Similar, but encodes a relative value, normalized to
+`UINT32_MAX` as 100%. This value is applied relative to the file system
+size. If both `tmpLimit` and `tmpLimitScale` are set, the lower of the two
+should be enforced. If neither field is set the implementation might apply a
+default limit.
+
+`devShmLimit`, `devShmLimitScale` → Similar to the previous two, but apply to
+`/dev/shm/` rather than `/tmp/`.
+
 `privileged` → An object, which contains the fields of the `privileged` section
 of the user record, see below.
 
@@ -761,22 +774,26 @@ These two are the only two fields specific to this section.
 All other fields that may be used in this section are identical to the equally named ones in the
 `regular` section (i.e. at the top-level object). Specifically, these are:
 
-`blobDirectory`, `blobManifest`, `iconName`, `location`, `shell`, `umask`, `environment`, `timeZone`,
-`preferredLanguage`, `additionalLanguages`, `niceLevel`, `resourceLimits`, `locked`, `notBeforeUSec`,
-`notAfterUSec`, `storage`, `diskSize`, `diskSizeRelative`, `skeletonDirectory`,
-`accessMode`, `tasksMax`, `memoryHigh`, `memoryMax`, `cpuWeight`, `ioWeight`,
+`blobDirectory`, `blobManifest`, `iconName`, `location`, `shell`, `umask`,
+`environment`, `timeZone`, `preferredLanguage`, `additionalLanguages`,
+`niceLevel`, `resourceLimits`, `locked`, `notBeforeUSec`, `notAfterUSec`,
+`storage`, `diskSize`, `diskSizeRelative`, `skeletonDirectory`, `accessMode`,
+`tasksMax`, `memoryHigh`, `memoryMax`, `cpuWeight`, `ioWeight`,
 `mountNoDevices`, `mountNoSuid`, `mountNoExecute`, `cifsDomain`,
 `cifsUserName`, `cifsService`, `cifsExtraMountOptions`, `imagePath`, `uid`,
 `gid`, `memberOf`, `fileSystemType`, `partitionUuid`, `luksUuid`,
 `fileSystemUuid`, `luksDiscard`, `luksOfflineDiscard`, `luksCipher`,
 `luksCipherMode`, `luksVolumeKeySize`, `luksPbkdfHashAlgorithm`,
-`luksPbkdfType`, `luksPbkdfForceIterations`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`,
-`luksPbkdfParallelThreads`, `luksSectorSize`, `autoResizeMode`, `rebalanceWeight`,
-`rateLimitIntervalUSec`, `rateLimitBurst`, `enforcePasswordPolicy`,
-`autoLogin`, `preferredSessionType`, `preferredSessionLauncher`, `stopDelayUSec`, `killProcesses`,
+`luksPbkdfType`, `luksPbkdfForceIterations`, `luksPbkdfTimeCostUSec`,
+`luksPbkdfMemoryCost`, `luksPbkdfParallelThreads`, `luksSectorSize`,
+`autoResizeMode`, `rebalanceWeight`, `rateLimitIntervalUSec`, `rateLimitBurst`,
+`enforcePasswordPolicy`, `autoLogin`, `preferredSessionType`,
+`preferredSessionLauncher`, `stopDelayUSec`, `killProcesses`,
 `passwordChangeMinUSec`, `passwordChangeMaxUSec`, `passwordChangeWarnUSec`,
 `passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`,
-`fido2HmacCredential`, `selfModifiableFields`, `selfModifiableBlobs`, `selfModifiablePrivileged`.
+`fido2HmacCredential`, `selfModifiableFields`, `selfModifiableBlobs`,
+`selfModifiablePrivileged`, `tmpLimit`, `tmpLimitScale`, `devShmLimit`,
+`devShmLimitScale`.
 
 ## Fields in the `binding` section
 
index acff25d0712a7abb81db31117a1c7b94530db5c8..a9c635a47831c503391aa972a9ff89a03533bd93 100644 (file)
@@ -7,6 +7,7 @@
 #include "hashmap.h"
 #include "hexdecoct.h"
 #include "path-util.h"
+#include "percent-util.h"
 #include "pretty-print.h"
 #include "process-util.h"
 #include "rlimit-util.h"
@@ -54,6 +55,26 @@ static void show_self_modifiable(
                         printf("%13s %s\n", i == value ? heading : "", *i);
 }
 
+static void show_tmpfs_limit(const char *tmpfs, const TmpfsLimit *limit, uint32_t scale) {
+        assert(tmpfs);
+        assert(limit);
+
+        if (!limit->is_set)
+                return;
+
+        printf("   %s Limit:", tmpfs);
+
+        if (limit->limit != UINT64_MAX)
+                printf(" %s", FORMAT_BYTES(limit->limit));
+        if (limit->limit == UINT64_MAX || limit->limit_scale != UINT32_MAX) {
+                if (limit->limit != UINT64_MAX)
+                        printf(" or");
+
+                printf(" %i%%", UINT32_SCALE_TO_PERCENT(scale));
+        }
+        printf("\n");
+}
+
 void user_record_show(UserRecord *hr, bool show_full_group_info) {
         _cleanup_strv_free_ char **langs = NULL;
         const char *hd, *ip, *shell;
@@ -368,6 +389,9 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
         if (hr->io_weight != UINT64_MAX)
                 printf("   IO Weight: %" PRIu64 "\n", hr->io_weight);
 
+        show_tmpfs_limit("TMP", &hr->tmp_limit, user_record_tmp_limit_scale(hr));
+        show_tmpfs_limit("SHM", &hr->dev_shm_limit, user_record_dev_shm_limit_scale(hr));
+
         if (hr->access_mode != MODE_INVALID)
                 printf(" Access Mode: 0%03o\n", user_record_access_mode(hr));
 
index 9feac30933ff9b341d15fd61bd2cdd9fa57b01bc..ddfcbf7d60599caf1b2d7a9df4325f8cfcd2cdd1 100644 (file)
@@ -15,6 +15,7 @@
 #include "locale-util.h"
 #include "memory-util.h"
 #include "path-util.h"
+#include "percent-util.h"
 #include "pkcs11-util.h"
 #include "rlimit-util.h"
 #include "sha256.h"
@@ -95,6 +96,8 @@ UserRecord* user_record_new(void) {
                 .drop_caches = -1,
                 .auto_resize_mode = _AUTO_RESIZE_MODE_INVALID,
                 .rebalance_weight = REBALANCE_WEIGHT_UNSET,
+                .tmp_limit = TMPFS_LIMIT_NULL,
+                .dev_shm_limit = TMPFS_LIMIT_NULL,
         };
 
         return h;
@@ -982,6 +985,40 @@ static int dispatch_rebalance_weight(const char *name, sd_json_variant *variant,
         return 0;
 }
 
+static int dispatch_tmpfs_limit(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
+        TmpfsLimit *limit = ASSERT_PTR(userdata);
+        int r;
+
+        if (sd_json_variant_is_null(variant)) {
+                *limit = TMPFS_LIMIT_NULL;
+                return 0;
+        }
+
+        r = sd_json_dispatch_uint64(name, variant, flags, &limit->limit);
+        if (r < 0)
+                return r;
+
+        limit->is_set = true;
+        return 0;
+}
+
+static int dispatch_tmpfs_limit_scale(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
+        TmpfsLimit *limit = ASSERT_PTR(userdata);
+        int r;
+
+        if (sd_json_variant_is_null(variant)) {
+                *limit = TMPFS_LIMIT_NULL;
+                return 0;
+        }
+
+        r = sd_json_dispatch_uint32(name, variant, flags, &limit->limit_scale);
+        if (r < 0)
+                return r;
+
+        limit->is_set = true;
+        return 0;
+}
+
 static int dispatch_privileged(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
 
         static const sd_json_dispatch_field privileged_dispatch_table[] = {
@@ -1275,6 +1312,10 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j
                 { "selfModifiableFields",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_fields),        SD_JSON_STRICT },
                 { "selfModifiableBlobs",        SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_blobs),         SD_JSON_STRICT },
                 { "selfModifiablePrivileged",   SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_privileged),    SD_JSON_STRICT },
+                { "tmpLimit",                   _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, tmp_limit),                     0,             },
+                { "tmpLimitScale",              _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, tmp_limit),                     0,             },
+                { "devShmLimit",                _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, dev_shm_limit),                 0,             },
+                { "devShmLimitScale",           _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, dev_shm_limit),                 0,             },
                 {},
         };
 
@@ -1625,6 +1666,10 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load
                 { "selfModifiableFields",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_fields),        SD_JSON_STRICT },
                 { "selfModifiableBlobs",        SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_blobs),         SD_JSON_STRICT },
                 { "selfModifiablePrivileged",   SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_privileged),    SD_JSON_STRICT },
+                { "tmpLimit",                   _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, tmp_limit),                     0,             },
+                { "tmpLimitScale",              _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, tmp_limit),                     0,             },
+                { "devShmLimit",                _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, dev_shm_limit),                 0,             },
+                { "devShmLimitScale",           _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, dev_shm_limit),                 0,             },
 
                 { "secret",                     SD_JSON_VARIANT_OBJECT,        dispatch_secret,                      0,                                                   0              },
                 { "privileged",                 SD_JSON_VARIANT_OBJECT,        dispatch_privileged,                  0,                                                   0              },
@@ -2138,6 +2183,32 @@ int user_record_languages(UserRecord *h, char ***ret) {
         return 0;
 }
 
+uint32_t user_record_tmp_limit_scale(UserRecord *h) {
+        assert(h);
+
+        if (h->tmp_limit.is_set)
+                return h->tmp_limit.limit_scale;
+
+        /* By default grant regular users only 80% quota */
+        if (user_record_disposition(h) == USER_REGULAR)
+                return UINT32_SCALE_FROM_PERCENT(80);
+
+        return UINT32_MAX;
+}
+
+uint32_t user_record_dev_shm_limit_scale(UserRecord *h) {
+        assert(h);
+
+        if (h->dev_shm_limit.is_set)
+                return h->dev_shm_limit.limit_scale;
+
+        /* By default grant regular users only 80% quota */
+        if (user_record_disposition(h) == USER_REGULAR)
+                return UINT32_SCALE_FROM_PERCENT(80);
+
+        return UINT32_MAX;
+}
+
 const char** user_record_self_modifiable_fields(UserRecord *h) {
         /* As a rule of thumb: a setting is safe if it cannot be used by a
          * user to give themselves some unfair advantage over other users on
index d80a46130a9b1e734f1795d6205f3dda0870641a..fc8510c074050ca82e91b8c5f8b422ae6e3a2927 100644 (file)
@@ -230,6 +230,19 @@ typedef enum AutoResizeMode {
 #define REBALANCE_WEIGHT_MAX UINT64_C(10000)
 #define REBALANCE_WEIGHT_UNSET UINT64_MAX
 
+typedef struct TmpfsLimit {
+        /* Absolute and relative tmpfs limits */
+        uint64_t limit;
+        uint32_t limit_scale;
+        bool is_set;
+} TmpfsLimit;
+
+#define TMPFS_LIMIT_NULL                        \
+        (TmpfsLimit) {                          \
+                .limit = UINT64_MAX,            \
+                .limit_scale = UINT32_MAX,      \
+        }                                       \
+
 typedef struct UserRecord {
         /* The following three fields are not part of the JSON record */
         unsigned n_ref;
@@ -389,6 +402,8 @@ typedef struct UserRecord {
         char **self_modifiable_blobs;
         char **self_modifiable_privileged;
 
+        TmpfsLimit tmp_limit, dev_shm_limit;
+
         sd_json_variant *json;
 } UserRecord;
 
@@ -436,6 +451,8 @@ uint64_t user_record_rebalance_weight(UserRecord *h);
 uint64_t user_record_capability_bounding_set(UserRecord *h);
 uint64_t user_record_capability_ambient_set(UserRecord *h);
 int user_record_languages(UserRecord *h, char ***ret);
+uint32_t user_record_tmp_limit_scale(UserRecord *h);
+uint32_t user_record_dev_shm_limit_scale(UserRecord *h);
 
 const char **user_record_self_modifiable_fields(UserRecord *h);
 const char **user_record_self_modifiable_blobs(UserRecord *h);