Add a new "pfd:" passive stream class that accepts a pre-opened file descriptor number. This is the core building block for systemd socket activation, where systemd opens and binds the listening socket before starting the service.
The pfd_open() function validates that the file descriptor refers to a listening stream socket via getsockopt(SO_TYPE) and getsockopt(SO_ACCEPTCONN), sets it non-blocking, and wraps it in an fd_pstream. Unlike punix:, the unlink_path is NULL because the service does not own the socket file. For security, pfd: remotes are restricted to the command line (--remote=pfd:N). Runtime addition via ovsdb-server/add-remote or the database is rejected at three entry points (ovsdb_server_add_remote, add_manager_options, query_db_remotes), preventing an attacker with database write access from hijacking arbitrary file descriptors. Reported-at: https://issues.redhat.com/browse/FDP-3413 Co-authored-by: Lubomir Rintel <[email protected]> Signed-off-by: Lubomir Rintel <[email protected]> Signed-off-by: Timothy Redaelli <[email protected]> --- Documentation/ref/ovsdb.7.rst | 12 ++++++++ lib/stream-provider.h | 1 + lib/stream-unix.c | 53 +++++++++++++++++++++++++++++++++++ lib/stream.c | 5 ++++ ovsdb/ovsdb-server.c | 23 ++++++++++++++- 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/Documentation/ref/ovsdb.7.rst b/Documentation/ref/ovsdb.7.rst index 42541dd7e..cf1ef3736 100644 --- a/Documentation/ref/ovsdb.7.rst +++ b/Documentation/ref/ovsdb.7.rst @@ -709,6 +709,18 @@ punix:<file> <file> to mimic the behavior of a Unix domain socket. The ACLs of the named pipe include LocalSystem, Administrators, and Creator Owner. +pfd:<fd> + Listen on a pre-opened file descriptor <fd>. The file descriptor must + refer to a bound, listening Unix domain stream socket. This is intended + for use with systemd socket activation, where systemd opens the socket + and passes it to the service. + + For security, ``pfd:`` may only be specified on the command line + (``--remote=pfd:<fd>``). It is rejected if added at runtime via + ``ovsdb-server/add-remote`` or through the database. + + This connection method is not supported on Windows. + All IP-based connection methods accept IPv4 and IPv6 addresses. To specify an IPv6 address, wrap it in square brackets, e.g. ``ssl:[::1]:6640``. Passive IP-based connection methods by default listen for IPv4 connections only; use diff --git a/lib/stream-provider.h b/lib/stream-provider.h index 44e3c6431..ddd468b09 100644 --- a/lib/stream-provider.h +++ b/lib/stream-provider.h @@ -195,6 +195,7 @@ extern const struct pstream_class ptcp_pstream_class; #ifndef _WIN32 extern const struct stream_class unix_stream_class; extern const struct pstream_class punix_pstream_class; +extern const struct pstream_class pfd_pstream_class; #else extern const struct stream_class windows_stream_class; extern const struct pstream_class pwindows_pstream_class; diff --git a/lib/stream-unix.c b/lib/stream-unix.c index d265efb83..5cb4e93d2 100644 --- a/lib/stream-unix.c +++ b/lib/stream-unix.c @@ -136,3 +136,56 @@ const struct pstream_class punix_pstream_class = { NULL, }; +/* Pre-opened file descriptor passive stream. + * + * Used for systemd socket activation: systemd opens and binds the socket, + * then passes it to the service as a pre-opened file descriptor. */ + +static int +pfd_open(const char *name, char *suffix, + struct pstream **pstreamp, uint8_t dscp OVS_UNUSED) +{ + char *end; + long fd; + + fd = strtol(suffix, &end, 10); + if (*end != '\0' || fd < 0 || fd > INT_MAX) { + VLOG_ERR("%s: bad file descriptor", name); + return ERANGE; + } + + /* Verify it is a listening stream socket. */ + int sock_type; + socklen_t len = sizeof sock_type; + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &sock_type, &len)) { + VLOG_ERR("%s: not a socket (%s)", name, ovs_strerror(errno)); + return errno; + } + if (sock_type != SOCK_STREAM) { + VLOG_ERR("%s: not a stream socket (type %d)", name, sock_type); + return EINVAL; + } + int listening; + len = sizeof listening; + if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &listening, &len) + || !listening) { + VLOG_ERR("%s: not a listening socket", name); + return EINVAL; + } + + int error = set_nonblocking(fd); + if (error) { + return error; + } + + return new_fd_pstream(xstrdup(name), fd, punix_accept, NULL, pstreamp); +} + +const struct pstream_class pfd_pstream_class = { + "pfd", + false, + pfd_open, + NULL, + NULL, + NULL, +}; diff --git a/lib/stream.c b/lib/stream.c index feaa1cb2d..b3b21588a 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -69,6 +69,7 @@ static const struct pstream_class *pstream_classes[] = { &ptcp_pstream_class, #ifndef _WIN32 &punix_pstream_class, + &pfd_pstream_class, #else &pwindows_pstream_class, #endif @@ -147,6 +148,10 @@ stream_usage(const char *name, bool active, bool passive, #endif printf(" punix:FILE " "listen on Unix domain socket FILE\n"); +#ifndef _WIN32 + printf(" pfd:FD " + "listen on pre-opened file descriptor FD\n"); +#endif } #ifdef HAVE_OPENSSL diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c index 7c3a5ef11..2af62071e 100644 --- a/ovsdb/ovsdb-server.c +++ b/ovsdb/ovsdb-server.c @@ -1425,6 +1425,12 @@ add_manager_options(struct shash *remotes, const struct ovsdb_row *row) return; } + if (!strncmp("pfd:", target, 4)) { + VLOG_WARN_RL(&rl, "pfd: remotes can only be specified on the " + "command line; ignoring \"%s\" from database", target); + return; + } + options = add_remote(remotes, target, NULL); if (ovsdb_util_read_integer_column(row, "max_backoff", &max_backoff)) { options->rpc.max_backoff = max_backoff; @@ -1485,7 +1491,16 @@ query_db_remotes(const char *name, const struct shash *all_dbs, datum = &row->fields[column->index]; for (i = 0; i < datum->n; i++) { - add_remote(remotes, json_string(datum->keys[i].s), NULL); + const char *t = json_string(datum->keys[i].s); + if (!strncmp("pfd:", t, 4)) { + static struct vlog_rate_limit pfd_rl + = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&pfd_rl, "pfd: remotes can only be " + "specified on the command line; ignoring " + "\"%s\" from database", t); + continue; + } + add_remote(remotes, t, NULL); } } } else if (column->type.key.type == OVSDB_TYPE_UUID @@ -2291,6 +2306,12 @@ ovsdb_server_add_remote(struct unixctl_conn *conn, int argc OVS_UNUSED, return; } + if (!strncmp("pfd:", remote, 4)) { + unixctl_command_reply_error(conn, + "pfd: remotes can only be specified on the command line"); + return; + } + retval = (strncmp("db:", remote, 3) ? NULL : parse_db_column(config->all_dbs, remote, -- 2.53.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
