mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-23 17:34:00 +03:00
shared: add generic user/group record structures and JSON parsers
This commit is contained in:
parent
64aa2622a3
commit
71d0b9d422
346
src/shared/group-record.c
Normal file
346
src/shared/group-record.c
Normal file
@ -0,0 +1,346 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include "group-record.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
|
||||
GroupRecord* group_record_new(void) {
|
||||
GroupRecord *h;
|
||||
|
||||
h = new(GroupRecord, 1);
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
*h = (GroupRecord) {
|
||||
.n_ref = 1,
|
||||
.disposition = _USER_DISPOSITION_INVALID,
|
||||
.last_change_usec = UINT64_MAX,
|
||||
.gid = GID_INVALID,
|
||||
};
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static GroupRecord *group_record_free(GroupRecord *g) {
|
||||
if (!g)
|
||||
return NULL;
|
||||
|
||||
free(g->group_name);
|
||||
free(g->realm);
|
||||
free(g->group_name_and_realm_auto);
|
||||
|
||||
strv_free(g->members);
|
||||
free(g->service);
|
||||
strv_free(g->administrators);
|
||||
strv_free_erase(g->hashed_password);
|
||||
|
||||
json_variant_unref(g->json);
|
||||
|
||||
return mfree(g);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_REF_UNREF_FUNC(GroupRecord, group_record, group_record_free);
|
||||
|
||||
static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
|
||||
|
||||
static const JsonDispatch privileged_dispatch_table[] = {
|
||||
{ "hashedPassword", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(GroupRecord, hashed_password), JSON_SAFE },
|
||||
{},
|
||||
};
|
||||
|
||||
return json_dispatch(variant, privileged_dispatch_table, NULL, flags, userdata);
|
||||
}
|
||||
|
||||
static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
|
||||
|
||||
static const JsonDispatch binding_dispatch_table[] = {
|
||||
{ "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
char smid[SD_ID128_STRING_MAX];
|
||||
JsonVariant *m;
|
||||
sd_id128_t mid;
|
||||
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));
|
||||
|
||||
r = sd_id128_get_machine(&mid);
|
||||
if (r < 0)
|
||||
return json_log(variant, flags, r, "Failed to determine machine ID: %m");
|
||||
|
||||
m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
|
||||
if (!m)
|
||||
return 0;
|
||||
|
||||
return json_dispatch(m, binding_dispatch_table, NULL, flags, userdata);
|
||||
}
|
||||
|
||||
static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
|
||||
|
||||
static const JsonDispatch per_machine_dispatch_table[] = {
|
||||
{ "matchMachineId", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "matchHostname", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
|
||||
{ "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), 0 },
|
||||
{ "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
JsonVariant *e;
|
||||
int r;
|
||||
|
||||
if (!variant)
|
||||
return 0;
|
||||
|
||||
if (!json_variant_is_array(variant))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(e, variant) {
|
||||
bool matching = false;
|
||||
JsonVariant *m;
|
||||
|
||||
if (!json_variant_is_object(e))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
|
||||
|
||||
m = json_variant_by_key(e, "matchMachineId");
|
||||
if (m) {
|
||||
r = per_machine_id_match(m, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
matching = r > 0;
|
||||
}
|
||||
|
||||
if (!matching) {
|
||||
m = json_variant_by_key(e, "matchHostname");
|
||||
if (m) {
|
||||
r = per_machine_hostname_match(m, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
matching = r > 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matching)
|
||||
continue;
|
||||
|
||||
r = json_dispatch(e, per_machine_dispatch_table, NULL, flags, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
|
||||
|
||||
static const JsonDispatch status_dispatch_table[] = {
|
||||
{ "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(GroupRecord, service), JSON_SAFE },
|
||||
{},
|
||||
};
|
||||
|
||||
char smid[SD_ID128_STRING_MAX];
|
||||
JsonVariant *m;
|
||||
sd_id128_t mid;
|
||||
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));
|
||||
|
||||
r = sd_id128_get_machine(&mid);
|
||||
if (r < 0)
|
||||
return json_log(variant, flags, r, "Failed to determine machine ID: %m");
|
||||
|
||||
m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
|
||||
if (!m)
|
||||
return 0;
|
||||
|
||||
return json_dispatch(m, status_dispatch_table, NULL, flags, userdata);
|
||||
}
|
||||
|
||||
static int group_record_augment(GroupRecord *h, JsonDispatchFlags json_flags) {
|
||||
assert(h);
|
||||
|
||||
if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
|
||||
return 0;
|
||||
|
||||
assert(h->group_name);
|
||||
|
||||
if (!h->group_name_and_realm_auto && h->realm) {
|
||||
h->group_name_and_realm_auto = strjoin(h->group_name, "@", h->realm);
|
||||
if (!h->group_name_and_realm_auto)
|
||||
return json_log_oom(h->json, json_flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int group_record_load(
|
||||
GroupRecord *h,
|
||||
JsonVariant *v,
|
||||
UserRecordLoadFlags load_flags) {
|
||||
|
||||
static const JsonDispatch group_dispatch_table[] = {
|
||||
{ "groupName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(GroupRecord, group_name), 0 },
|
||||
{ "realm", JSON_VARIANT_STRING, json_dispatch_realm, offsetof(GroupRecord, realm), 0 },
|
||||
{ "disposition", JSON_VARIANT_STRING, json_dispatch_user_disposition, offsetof(GroupRecord, disposition), 0 },
|
||||
{ "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(GroupRecord, service), JSON_SAFE },
|
||||
{ "lastChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(GroupRecord, last_change_usec), 0 },
|
||||
{ "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
|
||||
{ "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), 0 },
|
||||
{ "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), 0 },
|
||||
|
||||
{ "privileged", JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 },
|
||||
|
||||
/* Not defined for now, for groups, but let's at least generate sensible errors about it */
|
||||
{ "secret", JSON_VARIANT_OBJECT, json_dispatch_unsupported, 0, 0 },
|
||||
|
||||
/* Ignore the perMachine, binding and status stuff here, and process it later, so that it overrides whatever is set above */
|
||||
{ "perMachine", JSON_VARIANT_ARRAY, NULL, 0, 0 },
|
||||
{ "binding", JSON_VARIANT_OBJECT, NULL, 0, 0 },
|
||||
{ "status", JSON_VARIANT_OBJECT, NULL, 0, 0 },
|
||||
|
||||
/* Ignore 'signature', we check it with explicit accessors instead */
|
||||
{ "signature", JSON_VARIANT_ARRAY, NULL, 0, 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
assert(!h->json);
|
||||
|
||||
/* Note that this call will leave a half-initialized record around on failure! */
|
||||
|
||||
if ((USER_RECORD_REQUIRE_MASK(load_flags) & (USER_RECORD_SECRET|USER_RECORD_PRIVILEGED)))
|
||||
return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Secret and privileged section currently not available for groups, refusing.");
|
||||
|
||||
r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = json_dispatch(h->json, group_dispatch_table, NULL, json_flags, h);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* During the parsing operation above we ignored the 'perMachine', 'binding' and 'status' fields, since we want
|
||||
* them to override the global options. Let's process them now. */
|
||||
|
||||
r = dispatch_per_machine("perMachine", json_variant_by_key(h->json, "perMachine"), json_flags, h);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dispatch_binding("binding", json_variant_by_key(h->json, "binding"), json_flags, h);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dispatch_status("status", json_variant_by_key(h->json, "status"), json_flags, h);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->group_name)
|
||||
return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "Group name field missing, refusing.");
|
||||
|
||||
r = group_record_augment(h, json_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int group_record_build(GroupRecord **ret, ...) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
va_start(ap, ret);
|
||||
r = json_buildv(&v, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
g = group_record_new();
|
||||
if (!g)
|
||||
return -ENOMEM;
|
||||
|
||||
r = group_record_load(g, v, USER_RECORD_LOAD_FULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(g);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *group_record_group_name_and_realm(GroupRecord *h) {
|
||||
assert(h);
|
||||
|
||||
/* Return the pre-initialized joined string if it is defined */
|
||||
if (h->group_name_and_realm_auto)
|
||||
return h->group_name_and_realm_auto;
|
||||
|
||||
/* If it's not defined then we cannot have a realm */
|
||||
assert(!h->realm);
|
||||
return h->group_name;
|
||||
}
|
||||
|
||||
UserDisposition group_record_disposition(GroupRecord *h) {
|
||||
assert(h);
|
||||
|
||||
if (h->disposition >= 0)
|
||||
return h->disposition;
|
||||
|
||||
/* If not declared, derive from GID */
|
||||
|
||||
if (!gid_is_valid(h->gid))
|
||||
return _USER_DISPOSITION_INVALID;
|
||||
|
||||
if (h->gid == 0 || h->gid == GID_NOBODY)
|
||||
return USER_INTRINSIC;
|
||||
|
||||
if (gid_is_system(h->gid))
|
||||
return USER_SYSTEM;
|
||||
|
||||
if (gid_is_dynamic(h->gid))
|
||||
return USER_DYNAMIC;
|
||||
|
||||
if (gid_is_container(h->gid))
|
||||
return USER_CONTAINER;
|
||||
|
||||
if (h->gid > INT32_MAX)
|
||||
return USER_RESERVED;
|
||||
|
||||
return USER_REGULAR;
|
||||
}
|
||||
|
||||
int group_record_clone(GroupRecord *h, UserRecordLoadFlags flags, GroupRecord **ret) {
|
||||
_cleanup_(group_record_unrefp) GroupRecord *c = NULL;
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
assert(ret);
|
||||
|
||||
c = group_record_new();
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
r = group_record_load(c, h->json, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(c);
|
||||
return 0;
|
||||
}
|
44
src/shared/group-record.h
Normal file
44
src/shared/group-record.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include "json.h"
|
||||
#include "user-record.h"
|
||||
|
||||
typedef struct GroupRecord {
|
||||
unsigned n_ref;
|
||||
UserRecordMask mask;
|
||||
bool incomplete;
|
||||
|
||||
char *group_name;
|
||||
char *realm;
|
||||
char *group_name_and_realm_auto;
|
||||
|
||||
UserDisposition disposition;
|
||||
uint64_t last_change_usec;
|
||||
|
||||
gid_t gid;
|
||||
|
||||
char **members;
|
||||
|
||||
char *service;
|
||||
|
||||
/* The following exist mostly so that we can cover the full /etc/gshadow set of fields, we currently
|
||||
* do not actually make use of these */
|
||||
char **administrators; /* maps to 'struct sgrp' .sg_adm field */
|
||||
char **hashed_password; /* maps to 'struct sgrp' .sg_passwd field */
|
||||
|
||||
JsonVariant *json;
|
||||
} GroupRecord;
|
||||
|
||||
GroupRecord* group_record_new(void);
|
||||
GroupRecord* group_record_ref(GroupRecord *g);
|
||||
GroupRecord* group_record_unref(GroupRecord *g);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(GroupRecord*, group_record_unref);
|
||||
|
||||
int group_record_load(GroupRecord *h, JsonVariant *v, UserRecordLoadFlags flags);
|
||||
int group_record_build(GroupRecord **ret, ...);
|
||||
int group_record_clone(GroupRecord *g, UserRecordLoadFlags flags, GroupRecord **ret);
|
||||
|
||||
const char *group_record_group_name_and_realm(GroupRecord *h);
|
||||
UserDisposition group_record_disposition(GroupRecord *h);
|
@ -86,6 +86,8 @@ shared_sources = files('''
|
||||
generator.c
|
||||
generator.h
|
||||
gpt.h
|
||||
group-record.c
|
||||
group-record.h
|
||||
id128-print.c
|
||||
id128-print.h
|
||||
ima-util.c
|
||||
@ -189,6 +191,8 @@ shared_sources = files('''
|
||||
uid-range.h
|
||||
unit-file.c
|
||||
unit-file.h
|
||||
user-record.c
|
||||
user-record.h
|
||||
utmp-wtmp.h
|
||||
varlink.c
|
||||
varlink.h
|
||||
|
1883
src/shared/user-record.c
Normal file
1883
src/shared/user-record.c
Normal file
File diff suppressed because it is too large
Load Diff
375
src/shared/user-record.h
Normal file
375
src/shared/user-record.h
Normal file
@ -0,0 +1,375 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "json.h"
|
||||
#include "missing_resource.h"
|
||||
#include "time-util.h"
|
||||
|
||||
/* But some limits on disk sizes: not less than 5M, not more than 5T */
|
||||
#define USER_DISK_SIZE_MIN (UINT64_C(5)*1024*1024)
|
||||
#define USER_DISK_SIZE_MAX (UINT64_C(5)*1024*1024*1024*1024)
|
||||
|
||||
/* The default disk size to use when nothing else is specified, relative to free disk space */
|
||||
#define USER_DISK_SIZE_DEFAULT_PERCENT 85
|
||||
|
||||
typedef enum UserDisposition {
|
||||
USER_INTRINSIC, /* root and nobody */
|
||||
USER_SYSTEM, /* statically allocated users for system services */
|
||||
USER_DYNAMIC, /* dynamically allocated users for system services */
|
||||
USER_REGULAR, /* regular (typically human users) */
|
||||
USER_CONTAINER, /* UID ranges allocated for container uses */
|
||||
USER_RESERVED, /* Range above 2^31 */
|
||||
_USER_DISPOSITION_MAX,
|
||||
_USER_DISPOSITION_INVALID = -1,
|
||||
} UserDisposition;
|
||||
|
||||
typedef enum UserHomeStorage {
|
||||
USER_CLASSIC,
|
||||
USER_LUKS,
|
||||
USER_DIRECTORY, /* A directory, and a .identity file in it, which USER_CLASSIC lacks */
|
||||
USER_SUBVOLUME,
|
||||
USER_FSCRYPT,
|
||||
USER_CIFS,
|
||||
_USER_STORAGE_MAX,
|
||||
_USER_STORAGE_INVALID = -1
|
||||
} UserStorage;
|
||||
|
||||
typedef enum UserRecordMask {
|
||||
/* The various sections an identity record may have, as bit mask */
|
||||
USER_RECORD_REGULAR = 1U << 0,
|
||||
USER_RECORD_SECRET = 1U << 1,
|
||||
USER_RECORD_PRIVILEGED = 1U << 2,
|
||||
USER_RECORD_PER_MACHINE = 1U << 3,
|
||||
USER_RECORD_BINDING = 1U << 4,
|
||||
USER_RECORD_STATUS = 1U << 5,
|
||||
USER_RECORD_SIGNATURE = 1U << 6,
|
||||
_USER_RECORD_MASK_MAX = (1U << 7)-1
|
||||
} UserRecordMask;
|
||||
|
||||
typedef enum UserRecordLoadFlags {
|
||||
/* A set of flags used while loading a user record from JSON data. We leave the lower 6 bits free,
|
||||
* just as a safety precaution so that we can detect borked conversions between UserRecordMask and
|
||||
* UserRecordLoadFlags. */
|
||||
|
||||
/* What to require */
|
||||
USER_RECORD_REQUIRE_REGULAR = USER_RECORD_REGULAR << 7,
|
||||
USER_RECORD_REQUIRE_SECRET = USER_RECORD_SECRET << 7,
|
||||
USER_RECORD_REQUIRE_PRIVILEGED = USER_RECORD_PRIVILEGED << 7,
|
||||
USER_RECORD_REQUIRE_PER_MACHINE = USER_RECORD_PER_MACHINE << 7,
|
||||
USER_RECORD_REQUIRE_BINDING = USER_RECORD_BINDING << 7,
|
||||
USER_RECORD_REQUIRE_STATUS = USER_RECORD_STATUS << 7,
|
||||
USER_RECORD_REQUIRE_SIGNATURE = USER_RECORD_SIGNATURE << 7,
|
||||
|
||||
/* What to allow */
|
||||
USER_RECORD_ALLOW_REGULAR = USER_RECORD_REGULAR << 14,
|
||||
USER_RECORD_ALLOW_SECRET = USER_RECORD_SECRET << 14,
|
||||
USER_RECORD_ALLOW_PRIVILEGED = USER_RECORD_PRIVILEGED << 14,
|
||||
USER_RECORD_ALLOW_PER_MACHINE = USER_RECORD_PER_MACHINE << 14,
|
||||
USER_RECORD_ALLOW_BINDING = USER_RECORD_BINDING << 14,
|
||||
USER_RECORD_ALLOW_STATUS = USER_RECORD_STATUS << 14,
|
||||
USER_RECORD_ALLOW_SIGNATURE = USER_RECORD_SIGNATURE << 14,
|
||||
|
||||
/* What to strip */
|
||||
USER_RECORD_STRIP_REGULAR = USER_RECORD_REGULAR << 21,
|
||||
USER_RECORD_STRIP_SECRET = USER_RECORD_SECRET << 21,
|
||||
USER_RECORD_STRIP_PRIVILEGED = USER_RECORD_PRIVILEGED << 21,
|
||||
USER_RECORD_STRIP_PER_MACHINE = USER_RECORD_PER_MACHINE << 21,
|
||||
USER_RECORD_STRIP_BINDING = USER_RECORD_BINDING << 21,
|
||||
USER_RECORD_STRIP_STATUS = USER_RECORD_STATUS << 21,
|
||||
USER_RECORD_STRIP_SIGNATURE = USER_RECORD_SIGNATURE << 21,
|
||||
|
||||
/* Some special combinations that deserve explicit names */
|
||||
USER_RECORD_LOAD_FULL = USER_RECORD_REQUIRE_REGULAR |
|
||||
USER_RECORD_ALLOW_SECRET |
|
||||
USER_RECORD_ALLOW_PRIVILEGED |
|
||||
USER_RECORD_ALLOW_PER_MACHINE |
|
||||
USER_RECORD_ALLOW_BINDING |
|
||||
USER_RECORD_ALLOW_STATUS |
|
||||
USER_RECORD_ALLOW_SIGNATURE,
|
||||
|
||||
USER_RECORD_LOAD_REFUSE_SECRET = USER_RECORD_REQUIRE_REGULAR |
|
||||
USER_RECORD_ALLOW_PRIVILEGED |
|
||||
USER_RECORD_ALLOW_PER_MACHINE |
|
||||
USER_RECORD_ALLOW_BINDING |
|
||||
USER_RECORD_ALLOW_STATUS |
|
||||
USER_RECORD_ALLOW_SIGNATURE,
|
||||
|
||||
USER_RECORD_LOAD_MASK_SECRET = USER_RECORD_REQUIRE_REGULAR |
|
||||
USER_RECORD_ALLOW_PRIVILEGED |
|
||||
USER_RECORD_ALLOW_PER_MACHINE |
|
||||
USER_RECORD_ALLOW_BINDING |
|
||||
USER_RECORD_ALLOW_STATUS |
|
||||
USER_RECORD_ALLOW_SIGNATURE |
|
||||
USER_RECORD_STRIP_SECRET,
|
||||
|
||||
USER_RECORD_EXTRACT_SECRET = USER_RECORD_REQUIRE_SECRET |
|
||||
USER_RECORD_STRIP_REGULAR |
|
||||
USER_RECORD_STRIP_PRIVILEGED |
|
||||
USER_RECORD_STRIP_PER_MACHINE |
|
||||
USER_RECORD_STRIP_BINDING |
|
||||
USER_RECORD_STRIP_STATUS |
|
||||
USER_RECORD_STRIP_SIGNATURE,
|
||||
|
||||
USER_RECORD_LOAD_SIGNABLE = USER_RECORD_REQUIRE_REGULAR |
|
||||
USER_RECORD_ALLOW_PRIVILEGED |
|
||||
USER_RECORD_ALLOW_PER_MACHINE,
|
||||
|
||||
USER_RECORD_EXTRACT_SIGNABLE = USER_RECORD_LOAD_SIGNABLE |
|
||||
USER_RECORD_STRIP_SECRET |
|
||||
USER_RECORD_STRIP_BINDING |
|
||||
USER_RECORD_STRIP_STATUS |
|
||||
USER_RECORD_STRIP_SIGNATURE,
|
||||
|
||||
USER_RECORD_LOAD_EMBEDDED = USER_RECORD_REQUIRE_REGULAR |
|
||||
USER_RECORD_ALLOW_PRIVILEGED |
|
||||
USER_RECORD_ALLOW_PER_MACHINE |
|
||||
USER_RECORD_ALLOW_SIGNATURE,
|
||||
|
||||
USER_RECORD_EXTRACT_EMBEDDED = USER_RECORD_LOAD_EMBEDDED |
|
||||
USER_RECORD_STRIP_SECRET |
|
||||
USER_RECORD_STRIP_BINDING |
|
||||
USER_RECORD_STRIP_STATUS,
|
||||
|
||||
/* Whether to log about loader errors beyond LOG_DEBUG */
|
||||
USER_RECORD_LOG = 1U << 28,
|
||||
|
||||
/* Whether to ignore errors and load what we can */
|
||||
USER_RECORD_PERMISSIVE = 1U << 29,
|
||||
} UserRecordLoadFlags;
|
||||
|
||||
static inline UserRecordLoadFlags USER_RECORD_REQUIRE(UserRecordMask m) {
|
||||
assert((m & ~_USER_RECORD_MASK_MAX) == 0);
|
||||
return m << 7;
|
||||
}
|
||||
|
||||
static inline UserRecordLoadFlags USER_RECORD_ALLOW(UserRecordMask m) {
|
||||
assert((m & ~_USER_RECORD_MASK_MAX) == 0);
|
||||
return m << 14;
|
||||
}
|
||||
|
||||
static inline UserRecordLoadFlags USER_RECORD_STRIP(UserRecordMask m) {
|
||||
assert((m & ~_USER_RECORD_MASK_MAX) == 0);
|
||||
return m << 21;
|
||||
}
|
||||
|
||||
static inline UserRecordMask USER_RECORD_REQUIRE_MASK(UserRecordLoadFlags f) {
|
||||
return (f >> 7) & _USER_RECORD_MASK_MAX;
|
||||
}
|
||||
|
||||
static inline UserRecordMask USER_RECORD_ALLOW_MASK(UserRecordLoadFlags f) {
|
||||
return ((f >> 14) & _USER_RECORD_MASK_MAX) | USER_RECORD_REQUIRE_MASK(f);
|
||||
}
|
||||
|
||||
static inline UserRecordMask USER_RECORD_STRIP_MASK(UserRecordLoadFlags f) {
|
||||
return (f >> 21) & _USER_RECORD_MASK_MAX;
|
||||
}
|
||||
|
||||
static inline JsonDispatchFlags USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(UserRecordLoadFlags flags) {
|
||||
return (FLAGS_SET(flags, USER_RECORD_LOG) ? JSON_LOG : 0) |
|
||||
(FLAGS_SET(flags, USER_RECORD_PERMISSIVE) ? JSON_PERMISSIVE : 0);
|
||||
}
|
||||
|
||||
typedef struct Pkcs11EncryptedKey {
|
||||
/* The encrypted passphrase, which can be decrypted with the private key indicated below */
|
||||
void *data;
|
||||
size_t size;
|
||||
|
||||
/* Where to find the private key to decrypt the encrypted passphrase above */
|
||||
char *uri;
|
||||
|
||||
/* What to test the decrypted passphrase against to allow access (classic UNIX password hash). Note
|
||||
* that the decrypted passphrase is also used for unlocking LUKS and fscrypt, and if the account is
|
||||
* backed by LUKS or fscrypt the hashed password is only an additional layer of authentication, not
|
||||
* the only. */
|
||||
char *hashed_password;
|
||||
} Pkcs11EncryptedKey;
|
||||
|
||||
typedef struct UserRecord {
|
||||
/* The following three fields are not part of the JSON record */
|
||||
unsigned n_ref;
|
||||
UserRecordMask mask;
|
||||
bool incomplete; /* incomplete due to security restrictions. */
|
||||
|
||||
char *user_name;
|
||||
char *realm;
|
||||
char *user_name_and_realm_auto; /* the user_name field concatenated with '@' and the realm, if the latter is defined */
|
||||
char *real_name;
|
||||
char *email_address;
|
||||
char *password_hint;
|
||||
char *icon_name;
|
||||
char *location;
|
||||
|
||||
UserDisposition disposition;
|
||||
uint64_t last_change_usec;
|
||||
uint64_t last_password_change_usec;
|
||||
|
||||
char *shell;
|
||||
mode_t umask;
|
||||
char **environment;
|
||||
char *time_zone;
|
||||
char *preferred_language;
|
||||
int nice_level;
|
||||
struct rlimit *rlimits[_RLIMIT_MAX];
|
||||
|
||||
int locked; /* prohibit activation in general */
|
||||
uint64_t not_before_usec; /* prohibit activation before this unix time */
|
||||
uint64_t not_after_usec; /* prohibit activation after this unix time */
|
||||
|
||||
UserStorage storage;
|
||||
uint64_t disk_size;
|
||||
uint64_t disk_size_relative; /* Disk size, relative to the free bytes of the medium, normalized to UINT32_MAX = 100% */
|
||||
char *skeleton_directory;
|
||||
mode_t access_mode;
|
||||
|
||||
uint64_t tasks_max;
|
||||
uint64_t memory_high;
|
||||
uint64_t memory_max;
|
||||
uint64_t cpu_weight;
|
||||
uint64_t io_weight;
|
||||
|
||||
bool nosuid;
|
||||
bool nodev;
|
||||
bool noexec;
|
||||
|
||||
char **hashed_password;
|
||||
char **ssh_authorized_keys;
|
||||
char **password;
|
||||
char **pkcs11_pin;
|
||||
|
||||
char *cifs_domain;
|
||||
char *cifs_user_name;
|
||||
char *cifs_service;
|
||||
|
||||
char *image_path;
|
||||
char *image_path_auto; /* when none is configured explicitly, this is where we place the implicit image */
|
||||
char *home_directory;
|
||||
char *home_directory_auto; /* when none is set explicitly, this is where we place the implicit home directory */
|
||||
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
char **member_of;
|
||||
|
||||
char *file_system_type;
|
||||
sd_id128_t partition_uuid;
|
||||
sd_id128_t luks_uuid;
|
||||
sd_id128_t file_system_uuid;
|
||||
|
||||
int luks_discard;
|
||||
char *luks_cipher;
|
||||
char *luks_cipher_mode;
|
||||
uint64_t luks_volume_key_size;
|
||||
char *luks_pbkdf_hash_algorithm;
|
||||
char *luks_pbkdf_type;
|
||||
uint64_t luks_pbkdf_time_cost_usec;
|
||||
uint64_t luks_pbkdf_memory_cost;
|
||||
uint64_t luks_pbkdf_parallel_threads;
|
||||
|
||||
uint64_t disk_usage;
|
||||
uint64_t disk_free;
|
||||
uint64_t disk_ceiling;
|
||||
uint64_t disk_floor;
|
||||
|
||||
char *state;
|
||||
char *service;
|
||||
int signed_locally;
|
||||
|
||||
uint64_t good_authentication_counter;
|
||||
uint64_t bad_authentication_counter;
|
||||
uint64_t last_good_authentication_usec;
|
||||
uint64_t last_bad_authentication_usec;
|
||||
|
||||
uint64_t ratelimit_begin_usec;
|
||||
uint64_t ratelimit_count;
|
||||
uint64_t ratelimit_interval_usec;
|
||||
uint64_t ratelimit_burst;
|
||||
|
||||
int removable;
|
||||
int enforce_password_policy;
|
||||
int auto_login;
|
||||
|
||||
uint64_t stop_delay_usec; /* How long to leave systemd --user around on log-out */
|
||||
int kill_processes; /* Whether to kill user processes forcibly on log-out */
|
||||
|
||||
/* The following exist mostly so that we can cover the full /etc/shadow set of fields */
|
||||
uint64_t password_change_min_usec; /* maps to .sp_min */
|
||||
uint64_t password_change_max_usec; /* maps to .sp_max */
|
||||
uint64_t password_change_warn_usec; /* maps to .sp_warn */
|
||||
uint64_t password_change_inactive_usec; /* maps to .sp_inact */
|
||||
int password_change_now; /* Require a password change immediately on next login (.sp_lstchg = 0) */
|
||||
|
||||
char **pkcs11_token_uri;
|
||||
Pkcs11EncryptedKey *pkcs11_encrypted_key;
|
||||
size_t n_pkcs11_encrypted_key;
|
||||
int pkcs11_protected_authentication_path_permitted;
|
||||
|
||||
JsonVariant *json;
|
||||
} UserRecord;
|
||||
|
||||
UserRecord* user_record_new(void);
|
||||
UserRecord* user_record_ref(UserRecord *h);
|
||||
UserRecord* user_record_unref(UserRecord *h);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(UserRecord*, user_record_unref);
|
||||
|
||||
int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags flags);
|
||||
int user_record_build(UserRecord **ret, ...);
|
||||
|
||||
const char *user_record_user_name_and_realm(UserRecord *h);
|
||||
UserStorage user_record_storage(UserRecord *h);
|
||||
const char *user_record_file_system_type(UserRecord *h);
|
||||
const char *user_record_skeleton_directory(UserRecord *h);
|
||||
mode_t user_record_access_mode(UserRecord *h);
|
||||
const char *user_record_home_directory(UserRecord *h);
|
||||
const char *user_record_image_path(UserRecord *h);
|
||||
unsigned long user_record_mount_flags(UserRecord *h);
|
||||
const char *user_record_cifs_user_name(UserRecord *h);
|
||||
const char *user_record_shell(UserRecord *h);
|
||||
const char *user_record_real_name(UserRecord *h);
|
||||
bool user_record_luks_discard(UserRecord *h);
|
||||
const char *user_record_luks_cipher(UserRecord *h);
|
||||
const char *user_record_luks_cipher_mode(UserRecord *h);
|
||||
uint64_t user_record_luks_volume_key_size(UserRecord *h);
|
||||
const char* user_record_luks_pbkdf_type(UserRecord *h);
|
||||
usec_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h);
|
||||
uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h);
|
||||
uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h);
|
||||
const char *user_record_luks_pbkdf_hash_algorithm(UserRecord *h);
|
||||
gid_t user_record_gid(UserRecord *h);
|
||||
UserDisposition user_record_disposition(UserRecord *h);
|
||||
int user_record_removable(UserRecord *h);
|
||||
usec_t user_record_ratelimit_interval_usec(UserRecord *h);
|
||||
uint64_t user_record_ratelimit_burst(UserRecord *h);
|
||||
bool user_record_can_authenticate(UserRecord *h);
|
||||
|
||||
bool user_record_equal(UserRecord *a, UserRecord *b);
|
||||
bool user_record_compatible(UserRecord *a, UserRecord *b);
|
||||
int user_record_compare_last_change(UserRecord *a, UserRecord *b);
|
||||
|
||||
usec_t user_record_ratelimit_next_try(UserRecord *h);
|
||||
|
||||
int user_record_clone(UserRecord *h, UserRecordLoadFlags flags, UserRecord **ret);
|
||||
int user_record_masked_equal(UserRecord *a, UserRecord *b, UserRecordMask mask);
|
||||
|
||||
int user_record_test_blocked(UserRecord *h);
|
||||
int user_record_test_password_change_required(UserRecord *h);
|
||||
|
||||
/* The following six are user by group-record.c, that's why we export them here */
|
||||
int json_dispatch_realm(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
int json_dispatch_user_group_list(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
int json_dispatch_user_disposition(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
|
||||
int per_machine_id_match(JsonVariant *ids, JsonDispatchFlags flags);
|
||||
int per_machine_hostname_match(JsonVariant *hns, JsonDispatchFlags flags);
|
||||
int user_group_record_mangle(JsonVariant *v, UserRecordLoadFlags load_flags, JsonVariant **ret_variant, UserRecordMask *ret_mask);
|
||||
|
||||
const char* user_storage_to_string(UserStorage t) _const_;
|
||||
UserStorage user_storage_from_string(const char *s) _pure_;
|
||||
|
||||
const char* user_disposition_to_string(UserDisposition t) _const_;
|
||||
UserDisposition user_disposition_from_string(const char *s) _pure_;
|
Loading…
Reference in New Issue
Block a user