The only supported recovery key type at the moment is `modhex64`, for details see the description of `recoveryKey` below.
An account may have any number of recovery keys defined, and the array should have one entry for each.
+`selfModifiableFields` → An array of strings, each corresponding to a field name that can appear
+in the `regular` or `perMachine` sections. The user may be allowed to edit any field in this list
+without authenticating as an administrator. Note that the user will only be allowed to edit fields
+in `perMachine` sections that match the machine the user is performing the edit from.
+
+`selfModifiableBlobs` → Similar to `selfModifiableFields`, but it lists blobs that the user
+is allowed to edit.
+
+`selfModifiablePrivileged` → Similar to `selfModifiableFields`, but it lists fields in
+the `privileged` section that the user is allowed to edit.
+
`privileged` → An object, which contains the fields of the `privileged` section
of the user record, see below.
`autoLogin`, `preferredSessionType`, `preferredSessionLauncher`, `stopDelayUSec`, `killProcesses`,
`passwordChangeMinUSec`, `passwordChangeMaxUSec`, `passwordChangeWarnUSec`,
`passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`,
-`fido2HmacCredential`.
+`fido2HmacCredential`, `selfModifiableFields`, `selfModifiableBlobs`, `selfModifiablePrivileged`.
## Fields in the `binding` section
return NULL;
}
+static void dump_self_modifiable(const char *heading, char **field, const char **value) {
+ assert(heading);
+
+ /* Helper function for printing the various self_modifiable_* fields from the user record */
+
+ if (strv_isempty((char**) value))
+ /* Case 1: the array is explicitly set to be empty by the administrator */
+ printf("%13s %sDisabled by Administrator%s\n", heading, ansi_highlight_red(), ansi_normal());
+ else if (!field)
+ /* Case 2: we have values, but the field is NULL. This means that we're using the defaults.
+ * We list them anyways, because they're security-sensitive to the administrator */
+ STRV_FOREACH(i, value)
+ printf("%13s %s%s%s\n", i == value ? heading : "", ansi_grey(), *i, ansi_normal());
+ else
+ /* Case 3: we have a list provided by the administrator */
+ STRV_FOREACH(i, value)
+ printf("%13s %s\n", i == value ? heading : "", *i);
+}
+
void user_record_show(UserRecord *hr, bool show_full_group_info) {
_cleanup_strv_free_ char **langs = NULL;
const char *hd, *ip, *shell;
if (hr->service)
printf(" Service: %s\n", hr->service);
+
+ dump_self_modifiable("Self Modify:",
+ hr->self_modifiable_fields,
+ user_record_self_modifiable_fields(hr));
+ dump_self_modifiable("(Blobs)",
+ hr->self_modifiable_blobs,
+ user_record_self_modifiable_blobs(hr));
+ dump_self_modifiable("(Privileged)",
+ hr->self_modifiable_privileged,
+ user_record_self_modifiable_privileged(hr));
}
void group_record_show(GroupRecord *gr, bool show_full_user_info) {
for (size_t i = 0; i < h->n_recovery_key; i++)
recovery_key_done(h->recovery_key + i);
+ strv_free(h->self_modifiable_fields);
+ strv_free(h->self_modifiable_blobs);
+ strv_free(h->self_modifiable_privileged);
+
sd_json_variant_unref(h->json);
return mfree(h);
{ "passwordChangeNow", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(UserRecord, password_change_now), 0 },
{ "pkcs11TokenUri", SD_JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 },
{ "fido2HmacCredential", SD_JSON_VARIANT_ARRAY, dispatch_fido2_hmac_credential_array, 0, 0 },
+ { "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 },
{},
};
{ "pkcs11TokenUri", SD_JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 },
{ "fido2HmacCredential", SD_JSON_VARIANT_ARRAY, dispatch_fido2_hmac_credential_array, 0, 0 },
{ "recoveryKeyType", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, recovery_key_type), 0 },
+ { "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 },
{ "secret", SD_JSON_VARIANT_OBJECT, dispatch_secret, 0, 0 },
{ "privileged", SD_JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 },
return 0;
}
+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
+ * a given system. */
+ static const char *const default_fields[] = {
+ /* For display purposes */
+ "realName",
+ "emailAddress", /* Just the $EMAIL env var */
+ "iconName",
+ "location",
+
+ /* Basic account settings */
+ "shell",
+ "umask",
+ "environment",
+ "timeZone",
+ "preferredLanguage",
+ "additionalLanguages",
+ "preferredSessionLauncher",
+ "preferredSessionType",
+
+ /* Authentication methods */
+ "pkcs11TokenUri",
+ "fido2HmacCredential",
+ "recoveryKeyType",
+
+ "lastChangeUSec", /* Necessary to be able to change record at all */
+ "lastPasswordChangeUSec", /* Ditto, but for authentication methods */
+ NULL
+ };
+
+ assert(h);
+
+ /* Note that we intentionally distinguish between NULL and an empty array here */
+ return (const char**) h->self_modifiable_fields ?: (const char**) default_fields;
+}
+
+const char** user_record_self_modifiable_blobs(UserRecord *h) {
+ static const char *const default_blobs[] = {
+ /* For display purposes */
+ "avatar",
+ "login-background",
+ NULL
+ };
+
+ assert(h);
+
+ /* Note that we intentionally distinguish between NULL and an empty array here */
+ return (const char**) h->self_modifiable_blobs ?: (const char**) default_blobs;
+}
+
+const char** user_record_self_modifiable_privileged(UserRecord *h) {
+ static const char *const default_fields[] = {
+ /* For display purposes */
+ "passwordHint",
+
+ /* Authentication methods */
+ "hashedPassword"
+ "pkcs11EncryptedKey",
+ "fido2HmacSalt",
+ "recoveryKey",
+
+ "sshAuthorizedKeys", /* Basically just ~/.ssh/authorized_keys */
+ NULL
+ };
+
+ assert(h);
+
+ /* Note that we intentionally distinguish between NULL and an empty array here */
+ return (const char**) h->self_modifiable_privileged ?: (const char**) default_fields;
+}
+
uint64_t user_record_ratelimit_next_try(UserRecord *h) {
assert(h);