Merge pull request #9898 from keszybz/id128

Add a new tool 'systemd-id128'
This commit is contained in:
Lennart Poettering 2018-10-02 17:43:37 +02:00 committed by GitHub
commit 29088d374e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 461 additions and 63 deletions

4
TODO
View File

@ -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

View File

@ -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',

View File

@ -22,6 +22,7 @@
<refname>sd_id128_get_machine</refname>
<refname>sd_id128_get_machine_app_specific</refname>
<refname>sd_id128_get_boot</refname>
<refname>sd_id128_get_boot_app_specific</refname>
<refname>sd_id128_get_invocation</refname>
<refpurpose>Retrieve 128-bit IDs</refpurpose>
</refnamediv>
@ -46,6 +47,12 @@
<paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_id128_get_boot_app_specific</function></funcdef>
<paramdef>sd_id128_t <parameter>app_id</parameter></paramdef>
<paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_id128_get_invocation</function></funcdef>
<paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
@ -69,19 +76,25 @@
<function>sd_id128_get_machine()</function>, 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
<function>sd_id128_get_machine()</function> 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 <command>journalctl --new-id128</command>, 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.</para>
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 <command>systemd-id128 new</command>,
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.</para>
<para><function>sd_id128_get_boot()</function> returns the boot ID
of the executing kernel. This reads and parses the
<filename>/proc/sys/kernel/random/boot_id</filename> file exposed
by the kernel. It is randomly generated early at boot and is
unique for every running kernel instance. See
<citerefentry project='man-pages'><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>
for more information. This function also internally caches the
returned ID to make this call a cheap operation.</para>
<para><function>sd_id128_get_boot()</function> returns the boot ID of the executing kernel. This reads and parses
the <filename>/proc/sys/kernel/random/boot_id</filename> file exposed by the kernel. It is randomly generated early
at boot and is unique for every running kernel instance. See <citerefentry
project='man-pages'><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry> 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 <function>sd_id128_get_machine_app_specific()</function>, see below.</para>
<para><function>sd_id128_get_boot_app_specific()</function> is analogous to
<function>sd_id128_get_machine_app_specific()</function> 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.</para>
<para><function>sd_id128_get_invocation()</function> returns the invocation ID of the currently executed
service. In its current implementation, this reads and parses the <varname>$INVOCATION_ID</varname> environment
@ -89,10 +102,11 @@
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details. The
ID is cached internally. In future a different mechanism to determine the invocation ID may be added.</para>
<para>Note that <function>sd_id128_get_machine_app_specific()</function>, <function>sd_id128_get_boot()</function>
and <function>sd_id128_get_invocation()</function> always return UUID v4 compatible IDs.
<function>sd_id128_get_machine()</function> 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
<para>Note that <function>sd_id128_get_machine_app_specific()</function>, <function>sd_id128_get_boot()</function>,
<function>sd_id128_get_boot_app_specific()</function>, and <function>sd_id128_get_invocation()</function> always
return UUID v4 compatible IDs. <function>sd_id128_get_machine()</function> 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
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<para>For more information about the <literal>sd_id128_t</literal>
@ -104,10 +118,12 @@
<title>Return Value</title>
<para>Those calls return 0 on success (in which case <parameter>ret</parameter> is filled in),
or a negative errno-style error code. In particular, <function>sd_id128_get_machine()</function>
and <function>sd_id128_get_machine_app_specific()</function> return <constant>-ENOENT</constant>
if <filename>/etc/machine-id</filename> is missing, and <constant>-ENOMEDIUM</constant> if is
empty or all zeros.</para>
or a negative errno-style error code. In particular,
<function>sd_id128_get_machine()</function>,
<function>sd_id128_get_machine_app_specific()</function>, and
<function>sd_id128_get_boot_app_specific()</function> return <constant>-ENOENT</constant> if
<filename>/etc/machine-id</filename> is missing, and <constant>-ENOMEDIUM</constant> if is empty
or all zeros.</para>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
@ -118,7 +134,20 @@
<example>
<title>Application-specific machine ID</title>
<para>Here's a simple example for an application specific machine ID:</para>
<para>First, generate the application ID:</para>
<programlisting>$ 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)
...
</programlisting>
<para>Then use the new identifier in an example application:</para>
<programlisting><xi:include href="id128-app-specific.c" parse="text" /></programlisting>
</example>
@ -129,6 +158,7 @@
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-id128</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,

122
man/systemd-id128.xml Normal file
View File

@ -0,0 +1,122 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
<refentry id="systemd-id128" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-id128</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-id128</refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-id128</refname>
<refpurpose>Generate and print sd-128 identifiers</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>systemd-id128</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">new</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-id128</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">machine-id</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-id128</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">boot-id</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-id128</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">invocation-id</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><command>id128</command> may be used to conveniently print
<citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>
UUIDs. What identifier is printed depends on the specific verb.</para>
<para>With <command>new</command>, a new random identifier will be generated.</para>
<para>With <command>machine-id</command>, the identifier of the current machine will be
printed. See
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
<para>With <command>boot-id</command>, the identifier of the current boot will be
printed.</para>
<para>Both <command>machine-id</command> and <command>boot-id</command> may be combined
with the <option>--app-specific=<replaceable>app-id</replaceable></option> switch to
generate application-specific IDs. See
<citerefentry><refentrytitle>sd_id128_get_machine</refentrytitle><manvolnum>3</manvolnum></citerefentry>
for the discussion when this is useful.</para>
<para>With <command>invocation-id</command>, the identifier of the current service invocation
will be printed. This is available in systemd services. See
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<para>The following options are understood:</para>
<variablelist>
<varlistentry>
<term><option>-p</option></term>
<term><option>--pretty</option></term>
<listitem><para>Generate output as programming language snippets.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-a <replaceable>app-id</replaceable></option></term>
<term><option>--app-specific=<replaceable>app-id</replaceable></option></term>
<listitem><para>With this option, an identifier that is the result of hashing the
application identifier <replaceable>app-id</replaceable> and the machine identifier will be
printed. The <replaceable>app-id</replaceable> argument must be a valid sd-id128 string
identifying the application.</para>
</listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
</refsect1>
<refsect1>
<title>Exit status</title>
<para>On success, 0 is returned, a non-zero failure code otherwise.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_id128_get_machine</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View File

@ -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,

167
src/id128/id128.c Normal file
View File

@ -0,0 +1,167 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <getopt.h>
#include <stdio.h>
#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;
}

View File

@ -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:

View File

@ -648,4 +648,6 @@ global:
sd_hwdb_enumerate;
sd_hwdb_unrefp;
sd_id128_get_boot_app_specific;
} LIBSYSTEMD_239;

View File

@ -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);
}

64
src/shared/id128-print.c Normal file
View File

@ -0,0 +1,64 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <stdio.h>
#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);
}

10
src/shared/id128-print.h Normal file
View File

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include "sd-id128.h"
int id128_pretty_print(sd_id128_t id, bool pretty);
int id128_print_new(bool pretty);

View File

@ -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

View File

@ -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);