mirror of
https://github.com/systemd/systemd.git
synced 2024-11-23 02:03:37 +08:00
user-record: Introduce selfModifiable fields
Allows the system administrator to configure what fields the user is allowed to edit about themself, along with hard-coded defaults.
This commit is contained in:
parent
5310cf3354
commit
ad03f2d5f0
@ -597,6 +597,17 @@ The salt to pass to the FIDO2 device is found in `fido2HmacSalt`.
|
||||
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.
|
||||
|
||||
@ -754,7 +765,7 @@ All other fields that may be used in this section are identical to the equally n
|
||||
`autoLogin`, `preferredSessionType`, `preferredSessionLauncher`, `stopDelayUSec`, `killProcesses`,
|
||||
`passwordChangeMinUSec`, `passwordChangeMaxUSec`, `passwordChangeWarnUSec`,
|
||||
`passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`,
|
||||
`fido2HmacCredential`.
|
||||
`fido2HmacCredential`, `selfModifiableFields`, `selfModifiableBlobs`, `selfModifiablePrivileged`.
|
||||
|
||||
## Fields in the `binding` section
|
||||
|
||||
|
@ -28,6 +28,25 @@ const char* user_record_state_color(const char *state) {
|
||||
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;
|
||||
@ -585,6 +604,16 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
|
||||
|
||||
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) {
|
||||
|
@ -207,6 +207,10 @@ static UserRecord* user_record_free(UserRecord *h) {
|
||||
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);
|
||||
@ -1300,6 +1304,9 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j
|
||||
{ "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 },
|
||||
{},
|
||||
};
|
||||
|
||||
@ -1646,6 +1653,9 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load
|
||||
{ "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 },
|
||||
@ -2156,6 +2166,78 @@ int user_record_languages(UserRecord *h, char ***ret) {
|
||||
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);
|
||||
|
||||
|
@ -383,6 +383,10 @@ typedef struct UserRecord {
|
||||
char **capability_bounding_set;
|
||||
char **capability_ambient_set;
|
||||
|
||||
char **self_modifiable_fields; /* fields a user can change about themself w/o auth */
|
||||
char **self_modifiable_blobs;
|
||||
char **self_modifiable_privileged;
|
||||
|
||||
sd_json_variant *json;
|
||||
} UserRecord;
|
||||
|
||||
@ -431,6 +435,10 @@ 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);
|
||||
|
||||
const char **user_record_self_modifiable_fields(UserRecord *h);
|
||||
const char **user_record_self_modifiable_blobs(UserRecord *h);
|
||||
const char **user_record_self_modifiable_privileged(UserRecord *h);
|
||||
|
||||
int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret);
|
||||
|
||||
bool user_record_equal(UserRecord *a, UserRecord *b);
|
||||
|
Loading…
Reference in New Issue
Block a user