Skip to content

Commit d3dcf4e

Browse files
committed
fileio: beef up READ_FULL_FILE_CONNECT_SOCKET to allow setting sender socket name
This beefs up the READ_FULL_FILE_CONNECT_SOCKET logic of read_full_file_full() a bit: when used a sender socket name may be specified. If specified as NULL behaviour is as before: the client socket name is picked by the kernel. But if specified as non-NULL the client can pick a socket name to use when connecting. This is useful to communicate a minimal amount of metainformation from client to server, outside of the transport payload. Specifically, these beefs up the service credential logic to pass an abstract AF_UNIX socket name as client socket name when connecting via READ_FULL_FILE_CONNECT_SOCKET, that includes the requesting unit name and the eventual credential name. This allows servers implementing the trivial credential socket logic to distinguish clients: via a simple getpeername() it can be determined which unit is requesting a credential, and which credential specifically. Example: with this patch in place, in a unit file "waldo.service" a configuration line like the following: LoadCredential=foo:/run/quux/creds.sock will result in a connection to the AF_UNIX socket /run/quux/creds.sock, originating from an abstract namespace AF_UNIX socket: @$RANDOM/unit/waldo.service/foo (The $RANDOM is replaced by some randomized string. This is included in the socket name order to avoid namespace squatting issues: the abstract socket namespace is open to unprivileged users after all, and care needs to be taken not to use guessable names) The services listening on the /run/quux/creds.sock socket may thus easily retrieve the name of the unit the credential is requested for plus the credential name, via a simpler getpeername(), discarding the random preifx and the /unit/ string. This logic uses "/" as separator between the fields, since both unit names and credential names appear in the file system, and thus are designed to use "/" as outer separators. Given that it's a good safe choice to use as separators here, too avoid any conflicts. This is a minimal patch only: the new logic is used only for the unit file credential logic. For other places where we use READ_FULL_FILE_CONNECT_SOCKET it is probably a good idea to use this scheme too, but this should be done carefully in later patches, since the socket names become API that way, and we should determine the right amount of info to pass over.
1 parent ff640bd commit d3dcf4e

File tree

14 files changed

+95
-34
lines changed

14 files changed

+95
-34
lines changed

man/systemd.exec.xml

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2706,15 +2706,16 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
27062706
credential plus a file system path. The ID must be a short ASCII string suitable as filename in the
27072707
filesystem, and may be chosen freely by the user. If the specified path is absolute it is opened as
27082708
regular file and the credential data is read from it. If the absolute path refers to an
2709-
<constant>AF_UNIX</constant> stream socket in the file system a connection is made to it and the
2710-
credential data read from the connection, providing an easy IPC integration point for dynamically
2711-
providing credentials from other services. If the specified path is not absolute and itself qualifies
2712-
as valid credential identifier it is understood to refer to a credential that the service manager
2713-
itself received via the <varname>$CREDENTIALS_DIRECTORY</varname> environment variable, which may be
2714-
used to propagate credentials from an invoking environment (e.g. a container manager that invoked the
2715-
service manager) into a service. The contents of the file/socket may be arbitrary binary or textual
2716-
data, including newline characters and NUL bytes. This option may be used multiple times, each time
2717-
defining an additional credential to pass to the unit.</para>
2709+
<constant>AF_UNIX</constant> stream socket in the file system a connection is made to it (only once
2710+
at unit start-up) and the credential data read from the connection, providing an easy IPC integration
2711+
point for dynamically providing credentials from other services. If the specified path is not
2712+
absolute and itself qualifies as valid credential identifier it is understood to refer to a
2713+
credential that the service manager itself received via the <varname>$CREDENTIALS_DIRECTORY</varname>
2714+
environment variable, which may be used to propagate credentials from an invoking environment (e.g. a
2715+
container manager that invoked the service manager) into a service. The contents of the file/socket
2716+
may be arbitrary binary or textual data, including newline characters and <constant>NUL</constant>
2717+
bytes. This option may be used multiple times, each time defining an additional credential to pass to
2718+
the unit.</para>
27182719

27192720
<para>The credential files/IPC sockets must be accessible to the service manager, but don't have to
27202721
be directly accessible to the unit's processes: the credential data is read and copied into separate,
@@ -2728,7 +2729,22 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
27282729
e.g. <literal>ExecStart=cat ${CREDENTIALS_DIRECTORY}/mycred</literal>.</para>
27292730

27302731
<para>Currently, an accumulated credential size limit of 1M bytes per unit is
2731-
enforced.</para></listitem>
2732+
enforced.</para>
2733+
2734+
<para>If referencing an <constant>AF_UNIX</constant> stream socket to connect to, the connection will
2735+
originate from an abstract namespace socket, that includes information about the unit and the
2736+
credential ID in its socket name. Use <citerefentry
2737+
project='man-pages'><refentrytitle>getpeername</refentrytitle><manvolnum>2</manvolnum></citerefentry>
2738+
to query this information. The returned socket name is formatted as <constant>NUL</constant>
2739+
<replaceable>RANDOM</replaceable> <literal>/unit/</literal> <replaceable>UNIT</replaceable>
2740+
<literal>/</literal> <replaceable>ID</replaceable>, i.e. a <constant>NUL</constant> byte (as required
2741+
for abstract namespace socket names), followed by a random string (consisting of alphadecimal
2742+
characters), followed by the literal string <literal>/unit/</literal>, followed by the requesting
2743+
unit name, followed by the literal character <literal>/</literal>, followed by the textual credential
2744+
ID requested. Example: <literal>\0adf9d86b6eda275e/unit/foobar.service/credx</literal> in case the
2745+
credential <literal>credx</literal> is requested for a unit <literal>foobar.service</literal>. This
2746+
functionality is useful for using a single listening socket to serve credentials to multiple
2747+
consumers.</para></listitem>
27322748
</varlistentry>
27332749

27342750
<varlistentry>

src/basic/fileio.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,13 @@ int read_full_stream_full(
602602
return r;
603603
}
604604

605-
int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) {
605+
int read_full_file_full(
606+
int dir_fd,
607+
const char *filename,
608+
ReadFullFileFlags flags,
609+
const char *bind_name,
610+
char **contents, size_t *size) {
611+
606612
_cleanup_fclose_ FILE *f = NULL;
607613
int r;
608614

@@ -645,6 +651,20 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag
645651
if (sk < 0)
646652
return -errno;
647653

654+
if (bind_name) {
655+
/* If the caller specified a socket name to bind to, do so before connecting. This is
656+
* useful to communicate some minor, short meta-information token from the client to
657+
* the server. */
658+
union sockaddr_union bsa;
659+
660+
r = sockaddr_un_set_path(&bsa.un, bind_name);
661+
if (r < 0)
662+
return r;
663+
664+
if (bind(sk, &bsa.sa, r) < 0)
665+
return r;
666+
}
667+
648668
if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
649669
return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is
650670
* not a socket after all */

src/basic/fileio.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin
6060
int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4);
6161

6262
int read_one_line_file(const char *filename, char **line);
63-
int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size);
63+
int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, const char *bind_name, char **contents, size_t *size);
6464
static inline int read_full_file(const char *filename, char **contents, size_t *size) {
65-
return read_full_file_full(AT_FDCWD, filename, 0, contents, size);
65+
return read_full_file_full(AT_FDCWD, filename, 0, NULL, contents, size);
6666
}
6767
int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size);
6868
int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size);

src/core/execute.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
#include "parse-util.h"
7474
#include "path-util.h"
7575
#include "process-util.h"
76+
#include "random-util.h"
7677
#include "rlimit-util.h"
7778
#include "rm-rf.h"
7879
#if HAVE_SECCOMP
@@ -2509,6 +2510,7 @@ static int write_credential(
25092510
static int acquire_credentials(
25102511
const ExecContext *context,
25112512
const ExecParameters *params,
2513+
const char *unit,
25122514
const char *p,
25132515
uid_t uid,
25142516
bool ownership_ok) {
@@ -2546,14 +2548,20 @@ static int acquire_credentials(
25462548
STRV_FOREACH_PAIR(id, fn, context->load_credentials) {
25472549
ReadFullFileFlags flags = READ_FULL_FILE_SECURE;
25482550
_cleanup_(erase_and_freep) char *data = NULL;
2549-
_cleanup_free_ char *j = NULL;
2551+
_cleanup_free_ char *j = NULL, *bindname = NULL;
25502552
const char *source;
25512553
size_t size, add;
25522554

25532555
if (path_is_absolute(*fn)) {
25542556
/* If this is an absolute path, read the data directly from it, and support AF_UNIX sockets */
25552557
source = *fn;
25562558
flags |= READ_FULL_FILE_CONNECT_SOCKET;
2559+
2560+
/* Pass some minimal info about the unit and the credential name we are looking to acquire
2561+
* via the source socket address in case we read off an AF_UNIX socket. */
2562+
if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, *id) < 0)
2563+
return -ENOMEM;
2564+
25572565
} else if (params->received_credentials) {
25582566
/* If this is a relative path, take it relative to the credentials we received
25592567
* ourselves. We don't support the AF_UNIX stuff in this mode, since we are operating
@@ -2566,8 +2574,9 @@ static int acquire_credentials(
25662574
} else
25672575
source = NULL;
25682576

2577+
25692578
if (source)
2570-
r = read_full_file_full(AT_FDCWD, source, flags, &data, &size);
2579+
r = read_full_file_full(AT_FDCWD, source, flags, bindname, &data, &size);
25712580
else
25722581
r = -ENOENT;
25732582
if (r == -ENOENT &&
@@ -2613,6 +2622,7 @@ static int acquire_credentials(
26132622
static int setup_credentials_internal(
26142623
const ExecContext *context,
26152624
const ExecParameters *params,
2625+
const char *unit,
26162626
const char *final, /* This is where the credential store shall eventually end up at */
26172627
const char *workspace, /* This is where we can prepare it before moving it to the final place */
26182628
bool reuse_workspace, /* Whether to reuse any existing workspace mount if it already is a mount */
@@ -2724,7 +2734,7 @@ static int setup_credentials_internal(
27242734
assert(!must_mount || workspace_mounted > 0);
27252735
where = workspace_mounted ? workspace : final;
27262736

2727-
r = acquire_credentials(context, params, where, uid, workspace_mounted);
2737+
r = acquire_credentials(context, params, unit, where, uid, workspace_mounted);
27282738
if (r < 0)
27292739
return r;
27302740

@@ -2824,6 +2834,7 @@ static int setup_credentials(
28242834
r = setup_credentials_internal(
28252835
context,
28262836
params,
2837+
unit,
28272838
p, /* final mount point */
28282839
u, /* temporary workspace to overmount */
28292840
true, /* reuse the workspace if it is already a mount */
@@ -2861,6 +2872,7 @@ static int setup_credentials(
28612872
r = setup_credentials_internal(
28622873
context,
28632874
params,
2875+
unit,
28642876
p, /* final mount point */
28652877
"/dev/shm", /* temporary workspace to overmount */
28662878
false, /* do not reuse /dev/shm if it is already a mount, under no circumstances */

src/journal-remote/journal-gatewayd.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,7 @@ static int parse_argv(int argc, char *argv[]) {
896896
if (arg_key_pem)
897897
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
898898
"Key file specified twice");
899-
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_key_pem, NULL);
899+
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_key_pem, NULL);
900900
if (r < 0)
901901
return log_error_errno(r, "Failed to read key file: %m");
902902
assert(arg_key_pem);
@@ -906,7 +906,7 @@ static int parse_argv(int argc, char *argv[]) {
906906
if (arg_cert_pem)
907907
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
908908
"Certificate file specified twice");
909-
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_cert_pem, NULL);
909+
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_cert_pem, NULL);
910910
if (r < 0)
911911
return log_error_errno(r, "Failed to read certificate file: %m");
912912
assert(arg_cert_pem);
@@ -917,7 +917,7 @@ static int parse_argv(int argc, char *argv[]) {
917917
if (arg_trust_pem)
918918
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
919919
"CA certificate file specified twice");
920-
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_trust_pem, NULL);
920+
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_trust_pem, NULL);
921921
if (r < 0)
922922
return log_error_errno(r, "Failed to read CA certificate file: %m");
923923
assert(arg_trust_pem);

src/journal-remote/journal-remote-main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,20 +1077,20 @@ static int parse_argv(int argc, char *argv[]) {
10771077
static int load_certificates(char **key, char **cert, char **trust) {
10781078
int r;
10791079

1080-
r = read_full_file_full(AT_FDCWD, arg_key ?: PRIV_KEY_FILE, READ_FULL_FILE_CONNECT_SOCKET, key, NULL);
1080+
r = read_full_file_full(AT_FDCWD, arg_key ?: PRIV_KEY_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, key, NULL);
10811081
if (r < 0)
10821082
return log_error_errno(r, "Failed to read key from file '%s': %m",
10831083
arg_key ?: PRIV_KEY_FILE);
10841084

1085-
r = read_full_file_full(AT_FDCWD, arg_cert ?: CERT_FILE, READ_FULL_FILE_CONNECT_SOCKET, cert, NULL);
1085+
r = read_full_file_full(AT_FDCWD, arg_cert ?: CERT_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, cert, NULL);
10861086
if (r < 0)
10871087
return log_error_errno(r, "Failed to read certificate from file '%s': %m",
10881088
arg_cert ?: CERT_FILE);
10891089

10901090
if (arg_trust_all)
10911091
log_info("Certificate checking disabled.");
10921092
else {
1093-
r = read_full_file_full(AT_FDCWD, arg_trust ?: TRUST_FILE, READ_FULL_FILE_CONNECT_SOCKET, trust, NULL);
1093+
r = read_full_file_full(AT_FDCWD, arg_trust ?: TRUST_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, trust, NULL);
10941094
if (r < 0)
10951095
return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
10961096
arg_trust ?: TRUST_FILE);

src/network/netdev/macsec.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) {
989989
r = read_full_file_full(
990990
AT_FDCWD, sa->key_file,
991991
READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
992-
(char **) &key, &key_len);
992+
NULL, (char **) &key, &key_len);
993993
if (r < 0)
994994
return log_netdev_error_errno(netdev, r,
995995
"Failed to read key from '%s', ignoring: %m",

src/network/netdev/wireguard.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_
871871
r = read_full_file_full(
872872
AT_FDCWD, filename,
873873
READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
874-
&key, &key_len);
874+
NULL, &key, &key_len);
875875
if (r < 0)
876876
return r;
877877

src/nspawn/nspawn.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ static int parse_argv(int argc, char *argv[]) {
15891589
return log_oom();
15901590
}
15911591

1592-
r = read_full_file_full(AT_FDCWD, j ?: p, flags, &data, &size);
1592+
r = read_full_file_full(AT_FDCWD, j ?: p, flags, NULL, &data, &size);
15931593
if (r < 0)
15941594
return log_error_errno(r, "Failed to read credential '%s': %m", j ?: p);
15951595

src/partition/repart.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3630,7 +3630,7 @@ static int parse_argv(int argc, char *argv[]) {
36303630
_cleanup_(erase_and_freep) char *k = NULL;
36313631
size_t n = 0;
36323632

3633-
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_SECURE|READ_FULL_FILE_CONNECT_SOCKET, &k, &n);
3633+
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_SECURE|READ_FULL_FILE_CONNECT_SOCKET, NULL, &k, &n);
36343634
if (r < 0)
36353635
return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
36363636

0 commit comments

Comments
 (0)