diff --git a/TODO b/TODO index f848b9741e..0079b35963 100644 --- a/TODO +++ b/TODO @@ -181,10 +181,6 @@ Features: * add bpf-based implementation of devices cgroup controller logic for compat with cgroupsv2 as supported by newest kernel -* introduce sd_id128_get_boot_app_specific() which is like - sd_id128_get_machine_app_specific(). After all on long-running systems both - IDs have similar properties. - * sd-bus: add vtable flag, that may be used to request client creds implicitly and asynchronously before dispatching the operation diff --git a/man/rules/meson.build b/man/rules/meson.build index 99915c5e9f..303b584654 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -438,6 +438,7 @@ manpages = [ ['sd_id128_get_machine', '3', ['sd_id128_get_boot', + 'sd_id128_get_boot_app_specific', 'sd_id128_get_invocation', 'sd_id128_get_machine_app_specific'], ''], @@ -658,6 +659,7 @@ manpages = [ 'ENABLE_HIBERNATE'], ['systemd-hostnamed.service', '8', ['systemd-hostnamed'], 'ENABLE_HOSTNAMED'], ['systemd-hwdb', '8', [], 'ENABLE_HWDB'], + ['systemd-id128', '1', [], ''], ['systemd-importd.service', '8', ['systemd-importd'], 'ENABLE_IMPORTD'], ['systemd-inhibit', '1', [], ''], ['systemd-initctl.service', diff --git a/man/sd_id128_get_machine.xml b/man/sd_id128_get_machine.xml index 9587aa7d66..0884838324 100644 --- a/man/sd_id128_get_machine.xml +++ b/man/sd_id128_get_machine.xml @@ -22,6 +22,7 @@ sd_id128_get_machine sd_id128_get_machine_app_specific sd_id128_get_boot + sd_id128_get_boot_app_specific sd_id128_get_invocation Retrieve 128-bit IDs @@ -46,6 +47,12 @@ sd_id128_t *ret + + int sd_id128_get_boot_app_specific + sd_id128_t app_id + sd_id128_t *ret + + int sd_id128_get_invocation sd_id128_t *ret @@ -69,19 +76,25 @@ sd_id128_get_machine(), but retrieves a machine ID that is specific to the application that is identified by the indicated application ID. It is recommended to use this function instead of sd_id128_get_machine() when passing an ID to untrusted environments, in order to make sure - that the original machine ID may not be determined externally. The application-specific ID should be generated via - a tool like journalctl --new-id128, and may be compiled into the application. This function will - return the same application-specific ID for each combination of machine ID and application ID. Internally, this - function calculates HMAC-SHA256 of the application ID, keyed by the machine ID. + that the original machine ID may not be determined externally. This way, the ID used by the application remains + stable on a given machine, but cannot be easily correlated with IDs used in other applications on the same + machine. The application-specific ID should be generated via a tool like systemd-id128 new, + and may be compiled into the application. This function will return the same application-specific ID for each + combination of machine ID and application ID. Internally, this function calculates HMAC-SHA256 of the application + ID, keyed by the machine ID. - sd_id128_get_boot() returns the boot ID - of the executing kernel. This reads and parses the - /proc/sys/kernel/random/boot_id file exposed - by the kernel. It is randomly generated early at boot and is - unique for every running kernel instance. See - random4 - for more information. This function also internally caches the - returned ID to make this call a cheap operation. + sd_id128_get_boot() returns the boot ID of the executing kernel. This reads and parses + the /proc/sys/kernel/random/boot_id file exposed by the kernel. It is randomly generated early + at boot and is unique for every running kernel instance. See random4 for more + information. This function also internally caches the returned ID to make this call a cheap operation. It is + recommended to use this ID as-is only in trusted environments. In untrusted environments it is recommended to + derive an application specific ID using sd_id128_get_machine_app_specific(), see below. + + sd_id128_get_boot_app_specific() is analogous to + sd_id128_get_machine_app_specific() but returns an ID that changes between boots. Some + machines may be used for a long time without rebooting, hence the boot ID may remain constant for a long time, and + has properties similar to the machine ID during that time. sd_id128_get_invocation() returns the invocation ID of the currently executed service. In its current implementation, this reads and parses the $INVOCATION_ID environment @@ -89,10 +102,11 @@ systemd.exec5 for details. The ID is cached internally. In future a different mechanism to determine the invocation ID may be added. - Note that sd_id128_get_machine_app_specific(), sd_id128_get_boot() - and sd_id128_get_invocation() always return UUID v4 compatible IDs. - sd_id128_get_machine() will also return a UUID v4-compatible ID on new installations but might - not on older. It is possible to convert the machine ID into a UUID v4-compatible one. For more information, see + Note that sd_id128_get_machine_app_specific(), sd_id128_get_boot(), + sd_id128_get_boot_app_specific(), and sd_id128_get_invocation() always + return UUID v4 compatible IDs. sd_id128_get_machine() will also return a UUID v4-compatible + ID on new installations but might not on older. It is possible to convert the machine ID into a UUID v4-compatible + one. For more information, see machine-id5. For more information about the sd_id128_t @@ -104,10 +118,12 @@ Return Value Those calls return 0 on success (in which case ret is filled in), - or a negative errno-style error code. In particular, sd_id128_get_machine() - and sd_id128_get_machine_app_specific() return -ENOENT - if /etc/machine-id is missing, and -ENOMEDIUM if is - empty or all zeros. + or a negative errno-style error code. In particular, + sd_id128_get_machine(), + sd_id128_get_machine_app_specific(), and + sd_id128_get_boot_app_specific() return -ENOENT if + /etc/machine-id is missing, and -ENOMEDIUM if is empty + or all zeros. @@ -118,7 +134,20 @@ Application-specific machine ID - Here's a simple example for an application specific machine ID: + First, generate the application ID: + $ systemd-id128 -p new +As string: +c273277323db454ea63bb96e79b53e97 + +As UUID: +c2732773-23db-454e-a63b-b96e79b53e97 + +As man:sd-id128(3) macro: +#define MESSAGE_XYZ SD_ID128_MAKE(c2,73,27,73,23,db,45,4e,a6,3b,b9,6e,79,b5,3e,97) +... + + + Then use the new identifier in an example application: @@ -129,6 +158,7 @@ systemd1, + systemd-id1281, sd-id1283, machine-id5, systemd.exec5, diff --git a/man/systemd-id128.xml b/man/systemd-id128.xml new file mode 100644 index 0000000000..8a76cccd86 --- /dev/null +++ b/man/systemd-id128.xml @@ -0,0 +1,122 @@ + + + + + + + + systemd-id128 + systemd + + + + systemd-id128 + 1 + + + + systemd-id128 + Generate and print sd-128 identifiers + + + + + systemd-id128 + OPTIONS + new + + + + systemd-id128 + OPTIONS + machine-id + + + + systemd-id128 + OPTIONS + boot-id + + + + systemd-id128 + OPTIONS + invocation-id + + + + + Description + + id128 may be used to conveniently print + sd-id1283 + UUIDs. What identifier is printed depends on the specific verb. + + With new, a new random identifier will be generated. + + With machine-id, the identifier of the current machine will be + printed. See + machine-id5. + + + With boot-id, the identifier of the current boot will be + printed. + + Both machine-id and boot-id may be combined + with the switch to + generate application-specific IDs. See + sd_id128_get_machine3 + for the discussion when this is useful. + + With invocation-id, the identifier of the current service invocation + will be printed. This is available in systemd services. See + systemd.exec5. + + + + + Options + + The following options are understood: + + + + + + + Generate output as programming language snippets. + + + + + + + With this option, an identifier that is the result of hashing the + application identifier app-id and the machine identifier will be + printed. The app-id argument must be a valid sd-id128 string + identifying the application. + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code otherwise. + + + + See Also + + systemd1, + sd-id1283, + sd_id128_get_machine3 + + + + diff --git a/meson.build b/meson.build index 02e230415d..554e67e5dd 100644 --- a/meson.build +++ b/meson.build @@ -2324,6 +2324,14 @@ executable('systemd-cgroups-agent', install : true, install_dir : rootlibexecdir) +exe = executable('systemd-id128', + 'src/id128/id128.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) +public_programs += exe + exe = executable('systemd-path', 'src/path/path.c', include_directories : includes, diff --git a/src/id128/id128.c b/src/id128/id128.c new file mode 100644 index 0000000000..a6391313be --- /dev/null +++ b/src/id128/id128.c @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include + +#include "alloc-util.h" +#include "id128-print.h" +#include "terminal-util.h" +#include "util.h" +#include "verbs.h" + +static bool arg_pretty = false; +static sd_id128_t arg_app = {}; + +static int verb_new(int argc, char **argv, void *userdata) { + return id128_print_new(arg_pretty); +} + +static int verb_machine_id(int argc, char **argv, void *userdata) { + sd_id128_t id; + int r; + + if (sd_id128_is_null(arg_app)) + r = sd_id128_get_machine(&id); + else + r = sd_id128_get_machine_app_specific(arg_app, &id); + if (r < 0) + return log_error_errno(r, "Failed to get %smachine-ID: %m", + sd_id128_is_null(arg_app) ? "" : "app-specific "); + + return id128_pretty_print(id, arg_pretty); +} + +static int verb_boot_id(int argc, char **argv, void *userdata) { + sd_id128_t id; + int r; + + if (sd_id128_is_null(arg_app)) + r = sd_id128_get_boot(&id); + else + r = sd_id128_get_boot_app_specific(arg_app, &id); + if (r < 0) + return log_error_errno(r, "Failed to get %sboot-ID: %m", + sd_id128_is_null(arg_app) ? "" : "app-specific "); + + return id128_pretty_print(id, arg_pretty); +} + +static int verb_invocation_id(int argc, char **argv, void *userdata) { + sd_id128_t id; + int r; + + if (!sd_id128_is_null(arg_app)) + return log_error_errno(EINVAL, "Verb \"invocation-id\" cannot be combined with --app-specific=."); + + r = sd_id128_get_invocation(&id); + if (r < 0) + return log_error_errno(r, "Failed to get invocation-ID: %m"); + + return id128_pretty_print(id, arg_pretty); +} + +static int help(void) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("systemd-id128", "1", &link); + if (r < 0) + return log_oom(); + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Generate and print id128 strings.\n\n" + " -h --help Show this help\n\n" + " -p --pretty Generate samples of program code\n\n" + " -a --app-specific=ID Generate app-specific IDs\n\n" + "Commands:\n" + " new Generate a new id128 string\n" + " machine-id Print the ID of current machine\n" + " boot-id Print the ID of current boot\n" + " invocation-id Print the ID of current invocation\n" + " help Show this help\n" + "\nSee the %s for details.\n" + , program_invocation_short_name + , link + ); + + return 0; +} + +static int verb_help(int argc, char **argv, void *userdata) { + return help(); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "app-specific", required_argument, NULL, 'a' }, + {}, + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hpa:", options, NULL)) >= 0) + switch (c) { + + case 'h': + return help(); + + case ARG_VERSION: + return version(); + + case 'p': + arg_pretty = true; + break; + + case 'a': + r = sd_id128_from_string(optarg, &arg_app); + if (r < 0) + return log_error_errno(r, "Failed to parse \"%s\" as application-ID: %m", optarg); + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +static int id128_main(int argc, char *argv[]) { + static const Verb verbs[] = { + { "new", VERB_ANY, 1, 0, verb_new }, + { "machine-id", VERB_ANY, 1, 0, verb_machine_id }, + { "boot-id", VERB_ANY, 1, 0, verb_boot_id }, + { "invocation-id", VERB_ANY, 1, 0, verb_invocation_id }, + { "help", VERB_ANY, VERB_ANY, 0, verb_help }, + {} + }; + + return dispatch_verb(argc, argv, verbs, NULL); +} + +int main(int argc, char *argv[]) { + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = id128_main(argc, argv); + + finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index a9e4af1dd8..b42f676bae 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -38,6 +38,7 @@ #include "fsprg.h" #include "glob-util.h" #include "hostname-util.h" +#include "id128-print.h" #include "io-util.h" #include "journal-def.h" #include "journal-internal.h" @@ -1007,35 +1008,6 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int generate_new_id128(void) { - sd_id128_t id; - int r; - unsigned i; - - r = sd_id128_randomize(&id); - if (r < 0) - return log_error_errno(r, "Failed to generate ID: %m"); - - printf("As string:\n" - SD_ID128_FORMAT_STR "\n\n" - "As UUID:\n" - "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n" - "As man:sd-id128(3) macro:\n" - "#define MESSAGE_XYZ SD_ID128_MAKE(", - SD_ID128_FORMAT_VAL(id), - SD_ID128_FORMAT_VAL(id)); - for (i = 0; i < 16; i++) - printf("%02x%s", id.bytes[i], i != 15 ? "," : ""); - fputs(")\n\n", stdout); - - printf("As Python constant:\n" - ">>> import uuid\n" - ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n", - SD_ID128_FORMAT_VAL(id)); - - return 0; -} - static int add_matches(sd_journal *j, char **args) { char **i; bool have_term = false; @@ -2092,7 +2064,7 @@ int main(int argc, char *argv[]) { switch (arg_action) { case ACTION_NEW_ID128: - r = generate_new_id128(); + r = id128_print_new(true); goto finish; case ACTION_SETUP_KEYS: diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index d06e95638e..480ef39c7a 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -648,4 +648,6 @@ global: sd_hwdb_enumerate; sd_hwdb_unrefp; + + sd_id128_get_boot_app_specific; } LIBSYSTEMD_239; diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index 9d93732d7e..b7cca832d8 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -284,19 +284,15 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { return 0; } -_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) { +static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) { _cleanup_(khash_unrefp) khash *h = NULL; - sd_id128_t m, result; + sd_id128_t result; const void *p; int r; - assert_return(ret, -EINVAL); + assert(ret); - r = sd_id128_get_machine(&m); - if (r < 0) - return r; - - r = khash_new_with_key(&h, "hmac(sha256)", &m, sizeof(m)); + r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base)); if (r < 0) return r; @@ -314,3 +310,29 @@ _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *re *ret = make_v4_uuid(result); return 0; } + +_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) { + sd_id128_t id; + int r; + + assert_return(ret, -EINVAL); + + r = sd_id128_get_machine(&id); + if (r < 0) + return r; + + return get_app_specific(id, app_id, ret); +} + +_public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) { + sd_id128_t id; + int r; + + assert_return(ret, -EINVAL); + + r = sd_id128_get_boot(&id); + if (r < 0) + return r; + + return get_app_specific(id, app_id, ret); +} diff --git a/src/shared/id128-print.c b/src/shared/id128-print.c new file mode 100644 index 0000000000..eea3ee5ca8 --- /dev/null +++ b/src/shared/id128-print.c @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "sd-id128.h" + +#include "alloc-util.h" +#include "id128-print.h" +#include "log.h" +#include "terminal-util.h" + +int id128_pretty_print(sd_id128_t id, bool pretty) { + unsigned i; + _cleanup_free_ char *man_link = NULL, *mod_link = NULL; + const char *on, *off; + + if (!pretty) { + printf(SD_ID128_FORMAT_STR "\n", + SD_ID128_FORMAT_VAL(id)); + return 0; + } + + on = ansi_highlight(); + off = ansi_normal(); + + if (terminal_urlify("man:systemd-id128(1)", "systemd-id128(1)", &man_link) < 0) + return log_oom(); + + if (terminal_urlify("https://docs.python.org/3/library/uuid.html", "uuid", &mod_link) < 0) + return log_oom(); + + printf("As string:\n" + "%s" SD_ID128_FORMAT_STR "%s\n\n" + "As UUID:\n" + "%s%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%s\n\n" + "As %s macro:\n" + "%s#define MESSAGE_XYZ SD_ID128_MAKE(", + on, SD_ID128_FORMAT_VAL(id), off, + on, SD_ID128_FORMAT_VAL(id), off, + man_link, + on); + for (i = 0; i < 16; i++) + printf("%02x%s", id.bytes[i], i != 15 ? "," : ""); + printf(")%s\n\n", off); + + printf("As Python constant:\n" + ">>> import %s\n" + ">>> %sMESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')%s\n", + mod_link, + on, SD_ID128_FORMAT_VAL(id), off); + + return 0; +} + +int id128_print_new(bool pretty) { + sd_id128_t id; + int r; + + r = sd_id128_randomize(&id); + if (r < 0) + return log_error_errno(r, "Failed to generate ID: %m"); + + return id128_pretty_print(id, pretty); +} diff --git a/src/shared/id128-print.h b/src/shared/id128-print.h new file mode 100644 index 0000000000..5d50de0fc8 --- /dev/null +++ b/src/shared/id128-print.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#pragma once + +#include + +#include "sd-id128.h" + +int id128_pretty_print(sd_id128_t id, bool pretty); +int id128_print_new(bool pretty); diff --git a/src/shared/meson.build b/src/shared/meson.build index 17370f4048..498b73c750 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -44,6 +44,8 @@ shared_sources = files(''' generator.c generator.h gpt.h + id128-print.c + id128-print.h ima-util.c ima-util.h import-util.c diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h index 143a0ffb5e..78cf9462b0 100644 --- a/src/systemd/sd-id128.h +++ b/src/systemd/sd-id128.h @@ -42,6 +42,7 @@ int sd_id128_randomize(sd_id128_t *ret); int sd_id128_get_machine(sd_id128_t *ret); int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret); +int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret); int sd_id128_get_boot(sd_id128_t *ret); int sd_id128_get_invocation(sd_id128_t *ret);