mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
systemctl: support remote and privileged systemctl access via SSH and pkexec
This adds support for executing systemctl operations remotely or as privileged user while still running systemctl itself unprivileged and locally. This currently requires a D-Bus patch to work properly. https://bugs.freedesktop.org/show_bug.cgi?id=35230
This commit is contained in:
parent
e75c058023
commit
a8f11321c2
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
systemd-stdio-bridge
|
||||
systemd-machine-id-setup
|
||||
systemd-detect-virt
|
||||
systemd-sysctl
|
||||
|
10
Makefile.am
10
Makefile.am
@ -58,6 +58,7 @@ AM_CPPFLAGS = \
|
||||
-DSYSTEMD_SHUTDOWN_BINARY_PATH=\"$(rootlibexecdir)/systemd-shutdown\" \
|
||||
-DSYSTEMCTL_BINARY_PATH=\"$(rootbindir)/systemctl\" \
|
||||
-DSYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH=\"$(rootbindir)/systemd-tty-ask-password-agent\" \
|
||||
-DSYSTEMD_STDIO_BRIDGE_BINARY_PATH=\"$(bindir)/systemd-stdio-bridge\" \
|
||||
-DRUNTIME_DIR=\"$(localstatedir)/run\" \
|
||||
-DRANDOM_SEED=\"$(localstatedir)/lib/random-seed\" \
|
||||
-DSYSTEMD_CRYPTSETUP_PATH=\"$(rootlibexecdir)/systemd-cryptsetup\" \
|
||||
@ -111,7 +112,8 @@ rootsbin_PROGRAMS = \
|
||||
systemd-machine-id-setup
|
||||
|
||||
bin_PROGRAMS = \
|
||||
systemd-cgls
|
||||
systemd-cgls \
|
||||
systemd-stdio-bridge
|
||||
|
||||
if HAVE_GTK
|
||||
bin_PROGRAMS += \
|
||||
@ -980,6 +982,12 @@ systemd_cgls_CFLAGS = \
|
||||
systemd_cgls_LDADD = \
|
||||
libsystemd-basic.la
|
||||
|
||||
systemd_stdio_bridge_SOURCES = \
|
||||
src/bridge.c
|
||||
|
||||
systemd_stdio_bridge_LDADD = \
|
||||
libsystemd-basic.la
|
||||
|
||||
systemadm_SOURCES = \
|
||||
src/systemadm.vala \
|
||||
src/systemd-interfaces.vala
|
||||
|
2
TODO
2
TODO
@ -26,6 +26,8 @@ Features:
|
||||
|
||||
* optionally create watched directories in .path units
|
||||
|
||||
* Support --test based on current system state
|
||||
|
||||
* consider services with no [Install] section and stored in /lib enabled by "systemctl is-enabled"
|
||||
|
||||
* consider services with any kind of link in /etc/systemd/system enabled
|
||||
|
@ -382,6 +382,27 @@
|
||||
file that shall be
|
||||
disabled.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-H</option></term>
|
||||
<term><option>--host</option></term>
|
||||
|
||||
<listitem><para>Execute operation
|
||||
remotely. Specifiy a hostname, or
|
||||
username and hostname seperated by @,
|
||||
to connect to. This will use SSH to
|
||||
talk to the remote systemd
|
||||
instance.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-P</option></term>
|
||||
<term><option>--privileged</option></term>
|
||||
|
||||
<listitem><para>Acquire privileges via
|
||||
PolicyKit before executing the
|
||||
operation.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>The following commands are understood:</para>
|
||||
|
367
src/bridge.c
Normal file
367
src/bridge.c
Normal file
@ -0,0 +1,367 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
#define BUFFER_SIZE (64*1024)
|
||||
#define EXTRA_SIZE 16
|
||||
|
||||
static bool initial_nul = false;
|
||||
static bool auth_over = false;
|
||||
|
||||
static void format_uid(char *buf, size_t l) {
|
||||
char text[20 + 1]; /* enough space for a 64bit integer plus NUL */
|
||||
unsigned j;
|
||||
|
||||
assert(l > 0);
|
||||
|
||||
snprintf(text, sizeof(text)-1, "%llu", (unsigned long long) geteuid());
|
||||
text[sizeof(text)-1] = 0;
|
||||
|
||||
memset(buf, 0, l);
|
||||
|
||||
for (j = 0; text[j] && j*2+2 < l; j++) {
|
||||
buf[j*2] = hexchar(text[j] >> 4);
|
||||
buf[j*2+1] = hexchar(text[j] & 0xF);
|
||||
}
|
||||
|
||||
buf[j*2] = 0;
|
||||
}
|
||||
|
||||
static size_t patch_in_line(char *line, size_t l, size_t left) {
|
||||
size_t r;
|
||||
|
||||
if (line[0] == 0 && !initial_nul) {
|
||||
initial_nul = true;
|
||||
line += 1;
|
||||
l -= 1;
|
||||
r = 1;
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
if (l == 5 && strncmp(line, "BEGIN", 5) == 0) {
|
||||
r += l;
|
||||
auth_over = true;
|
||||
|
||||
} else if (l == 17 && strncmp(line, "NEGOTIATE_UNIX_FD", 17) == 0) {
|
||||
memmove(line + 13, line + 17, left);
|
||||
memcpy(line, "NEGOTIATE_NOP", 13);
|
||||
r += 13;
|
||||
|
||||
} else if (l >= 14 && strncmp(line, "AUTH EXTERNAL ", 14) == 0) {
|
||||
char uid[20*2 + 1];
|
||||
size_t len;
|
||||
|
||||
format_uid(uid, sizeof(uid));
|
||||
len = strlen(uid);
|
||||
assert(len <= EXTRA_SIZE);
|
||||
|
||||
memmove(line + 14 + len, line + l, left);
|
||||
memcpy(line + 14, uid, len);
|
||||
|
||||
r += 14 + len;
|
||||
} else
|
||||
r += l;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static size_t patch_in_buffer(char* in_buffer, size_t *in_buffer_full) {
|
||||
size_t i, good = 0;
|
||||
|
||||
if (*in_buffer_full <= 0)
|
||||
return *in_buffer_full;
|
||||
|
||||
/* If authentication is done, we don't touch anything anymore */
|
||||
if (auth_over)
|
||||
return *in_buffer_full;
|
||||
|
||||
if (*in_buffer_full < 2)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i <= *in_buffer_full - 2; i ++) {
|
||||
|
||||
/* Fully lines can be send on */
|
||||
if (in_buffer[i] == '\r' && in_buffer[i+1] == '\n') {
|
||||
if (i > good) {
|
||||
size_t old_length, new_length;
|
||||
|
||||
old_length = i - good;
|
||||
new_length = patch_in_line(in_buffer+good, old_length, *in_buffer_full - i);
|
||||
*in_buffer_full = *in_buffer_full + new_length - old_length;
|
||||
|
||||
good += new_length + 2;
|
||||
|
||||
} else
|
||||
good = i+2;
|
||||
}
|
||||
|
||||
if (auth_over)
|
||||
break;
|
||||
}
|
||||
|
||||
return good;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int r = EXIT_FAILURE, fd = -1, ep = -1;
|
||||
union sockaddr_union sa;
|
||||
char in_buffer[BUFFER_SIZE+EXTRA_SIZE], out_buffer[BUFFER_SIZE+EXTRA_SIZE];
|
||||
size_t in_buffer_full = 0, out_buffer_full = 0;
|
||||
struct epoll_event stdin_ev, stdout_ev, fd_ev;
|
||||
bool stdin_readable = false, stdout_writable = false, fd_readable = false, fd_writable = false;
|
||||
bool stdin_rhup = false, stdout_whup = false, fd_rhup = false, fd_whup = false;
|
||||
|
||||
if (argc > 1) {
|
||||
log_error("This program takes no argument.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
|
||||
log_error("Failed to create socket: %s", strerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
zero(sa);
|
||||
sa.un.sun_family = AF_UNIX;
|
||||
strncpy(sa.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(sa.un.sun_path));
|
||||
|
||||
if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
|
||||
log_error("Failed to connect: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fd_nonblock(STDIN_FILENO, 1);
|
||||
fd_nonblock(STDOUT_FILENO, 1);
|
||||
|
||||
if ((ep = epoll_create1(EPOLL_CLOEXEC)) < 0) {
|
||||
log_error("Failed to create epoll: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
zero(stdin_ev);
|
||||
stdin_ev.events = EPOLLIN|EPOLLET;
|
||||
stdin_ev.data.fd = STDIN_FILENO;
|
||||
|
||||
zero(stdout_ev);
|
||||
stdout_ev.events = EPOLLOUT|EPOLLET;
|
||||
stdout_ev.data.fd = STDOUT_FILENO;
|
||||
|
||||
zero(fd_ev);
|
||||
fd_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
|
||||
fd_ev.data.fd = fd;
|
||||
|
||||
if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 ||
|
||||
epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 ||
|
||||
epoll_ctl(ep, EPOLL_CTL_ADD, fd, &fd_ev) < 0) {
|
||||
log_error("Failed to regiser fds in epoll: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
do {
|
||||
struct epoll_event ev[16];
|
||||
ssize_t k;
|
||||
int i, nfds;
|
||||
|
||||
if ((nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1)) < 0) {
|
||||
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
|
||||
log_error("epoll_wait(): %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
assert(nfds >= 1);
|
||||
|
||||
for (i = 0; i < nfds; i++) {
|
||||
if (ev[i].data.fd == STDIN_FILENO) {
|
||||
|
||||
if (!stdin_rhup && (ev[i].events & (EPOLLHUP|EPOLLIN)))
|
||||
stdin_readable = true;
|
||||
|
||||
} else if (ev[i].data.fd == STDOUT_FILENO) {
|
||||
|
||||
if (ev[i].events & EPOLLHUP) {
|
||||
stdout_writable = false;
|
||||
stdout_whup = true;
|
||||
}
|
||||
|
||||
if (!stdout_whup && (ev[i].events & EPOLLOUT))
|
||||
stdout_writable = true;
|
||||
|
||||
} else if (ev[i].data.fd == fd) {
|
||||
|
||||
if (ev[i].events & EPOLLHUP) {
|
||||
fd_writable = false;
|
||||
fd_whup = true;
|
||||
}
|
||||
|
||||
if (!fd_rhup && (ev[i].events & (EPOLLHUP|EPOLLIN)))
|
||||
fd_readable = true;
|
||||
|
||||
if (!fd_whup && (ev[i].events & EPOLLOUT))
|
||||
fd_writable = true;
|
||||
}
|
||||
}
|
||||
|
||||
while ((stdin_readable && in_buffer_full <= 0) ||
|
||||
(fd_writable && patch_in_buffer(in_buffer, &in_buffer_full) > 0) ||
|
||||
(fd_readable && out_buffer_full <= 0) ||
|
||||
(stdout_writable && out_buffer_full > 0)) {
|
||||
|
||||
size_t in_buffer_good = 0;
|
||||
|
||||
if (stdin_readable && in_buffer_full < BUFFER_SIZE) {
|
||||
|
||||
if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, BUFFER_SIZE - in_buffer_full)) < 0) {
|
||||
|
||||
if (errno == EAGAIN)
|
||||
stdin_readable = false;
|
||||
else if (errno == EPIPE || errno == ECONNRESET)
|
||||
k = 0;
|
||||
else {
|
||||
log_error("read(): %m");
|
||||
goto finish;
|
||||
}
|
||||
} else
|
||||
in_buffer_full += (size_t) k;
|
||||
|
||||
if (k == 0) {
|
||||
stdin_rhup = true;
|
||||
stdin_readable = false;
|
||||
shutdown(STDIN_FILENO, SHUT_RD);
|
||||
close_nointr_nofail(STDIN_FILENO);
|
||||
}
|
||||
}
|
||||
|
||||
in_buffer_good = patch_in_buffer(in_buffer, &in_buffer_full);
|
||||
|
||||
if (fd_writable && in_buffer_good > 0) {
|
||||
|
||||
if ((k = write(fd, in_buffer, in_buffer_good)) < 0) {
|
||||
|
||||
if (errno == EAGAIN)
|
||||
fd_writable = false;
|
||||
else if (errno == EPIPE || errno == ECONNRESET) {
|
||||
fd_whup = true;
|
||||
fd_writable = false;
|
||||
shutdown(fd, SHUT_WR);
|
||||
} else {
|
||||
log_error("write(): %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
} else {
|
||||
assert(in_buffer_full >= (size_t) k);
|
||||
memmove(in_buffer, in_buffer + k, in_buffer_full - k);
|
||||
in_buffer_full -= k;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd_readable && out_buffer_full < BUFFER_SIZE) {
|
||||
|
||||
if ((k = read(fd, out_buffer + out_buffer_full, BUFFER_SIZE - out_buffer_full)) < 0) {
|
||||
|
||||
if (errno == EAGAIN)
|
||||
fd_readable = false;
|
||||
else if (errno == EPIPE || errno == ECONNRESET)
|
||||
k = 0;
|
||||
else {
|
||||
log_error("read(): %m");
|
||||
goto finish;
|
||||
}
|
||||
} else
|
||||
out_buffer_full += (size_t) k;
|
||||
|
||||
if (k == 0) {
|
||||
fd_rhup = true;
|
||||
fd_readable = false;
|
||||
shutdown(fd, SHUT_RD);
|
||||
}
|
||||
}
|
||||
|
||||
if (stdout_writable && out_buffer_full > 0) {
|
||||
|
||||
if ((k = write(STDOUT_FILENO, out_buffer, out_buffer_full)) < 0) {
|
||||
|
||||
if (errno == EAGAIN)
|
||||
stdout_writable = false;
|
||||
else if (errno == EPIPE || errno == ECONNRESET) {
|
||||
stdout_whup = true;
|
||||
stdout_writable = false;
|
||||
shutdown(STDOUT_FILENO, SHUT_WR);
|
||||
close_nointr(STDOUT_FILENO);
|
||||
} else {
|
||||
log_error("write(): %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
} else {
|
||||
assert(out_buffer_full >= (size_t) k);
|
||||
memmove(out_buffer, out_buffer + k, out_buffer_full - k);
|
||||
out_buffer_full -= k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stdin_rhup && in_buffer_full <= 0 && !fd_whup) {
|
||||
fd_whup = true;
|
||||
fd_writable = false;
|
||||
shutdown(fd, SHUT_WR);
|
||||
}
|
||||
|
||||
if (fd_rhup && out_buffer_full <= 0 && !stdout_whup) {
|
||||
stdout_whup = true;
|
||||
stdout_writable = false;
|
||||
shutdown(STDOUT_FILENO, SHUT_WR);
|
||||
close_nointr(STDOUT_FILENO);
|
||||
}
|
||||
|
||||
} while (!stdout_whup || !fd_whup);
|
||||
|
||||
r = EXIT_SUCCESS;
|
||||
|
||||
finish:
|
||||
if (fd >= 0)
|
||||
close_nointr_nofail(fd);
|
||||
|
||||
if (ep >= 0)
|
||||
close_nointr_nofail(ep);
|
||||
|
||||
return r;
|
||||
}
|
@ -23,6 +23,8 @@
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include "log.h"
|
||||
@ -55,20 +57,58 @@ int bus_check_peercred(DBusConnection *c) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define TIMEOUT_USEC (60*USEC_PER_SEC)
|
||||
|
||||
static int sync_auth(DBusConnection *bus, DBusError *error) {
|
||||
usec_t begin, tstamp;
|
||||
|
||||
assert(bus);
|
||||
|
||||
/* This complexity should probably move into D-Bus itself:
|
||||
*
|
||||
* https://bugs.freedesktop.org/show_bug.cgi?id=35189 */
|
||||
|
||||
begin = tstamp = now(CLOCK_MONOTONIC);
|
||||
for (;;) {
|
||||
|
||||
if (tstamp > begin + TIMEOUT_USEC)
|
||||
break;
|
||||
|
||||
if (dbus_connection_get_is_authenticated(bus))
|
||||
break;
|
||||
|
||||
if (!dbus_connection_read_write_dispatch(bus, ((begin + TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC))
|
||||
break;
|
||||
|
||||
tstamp = now(CLOCK_MONOTONIC);
|
||||
}
|
||||
|
||||
if (!dbus_connection_get_is_connected(bus)) {
|
||||
dbus_set_error_const(error, DBUS_ERROR_NO_SERVER, "Connection terminated during authentication.");
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
|
||||
if (!dbus_connection_get_is_authenticated(bus)) {
|
||||
dbus_set_error_const(error, DBUS_ERROR_TIMEOUT, "Failed to authenticate in time.");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private, DBusError *error) {
|
||||
DBusConnection *bus;
|
||||
int r;
|
||||
|
||||
assert(_bus);
|
||||
|
||||
#define TIMEOUT_USEC (60*USEC_PER_SEC)
|
||||
|
||||
/* If we are root, then let's not go via the bus */
|
||||
if (geteuid() == 0 && t == DBUS_BUS_SYSTEM) {
|
||||
usec_t begin, tstamp;
|
||||
|
||||
if (!(bus = dbus_connection_open_private("unix:abstract=/org/freedesktop/systemd1/private", error)))
|
||||
return -EIO;
|
||||
|
||||
dbus_connection_set_exit_on_disconnect(bus, FALSE);
|
||||
|
||||
if (bus_check_peercred(bus) < 0) {
|
||||
dbus_connection_close(bus);
|
||||
dbus_connection_unref(bus);
|
||||
@ -77,40 +117,6 @@ int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private, DBusError *
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/* This complexity should probably move into D-Bus itself:
|
||||
*
|
||||
* https://bugs.freedesktop.org/show_bug.cgi?id=35189 */
|
||||
begin = tstamp = now(CLOCK_MONOTONIC);
|
||||
for (;;) {
|
||||
|
||||
if (tstamp > begin + TIMEOUT_USEC)
|
||||
break;
|
||||
|
||||
if (dbus_connection_get_is_authenticated(bus))
|
||||
break;
|
||||
|
||||
if (!dbus_connection_read_write_dispatch(bus, ((begin + TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC))
|
||||
break;
|
||||
|
||||
tstamp = now(CLOCK_MONOTONIC);
|
||||
}
|
||||
|
||||
if (!dbus_connection_get_is_connected(bus)) {
|
||||
dbus_connection_close(bus);
|
||||
dbus_connection_unref(bus);
|
||||
|
||||
dbus_set_error_const(error, DBUS_ERROR_NO_SERVER, "Connection terminated during authentication.");
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
|
||||
if (!dbus_connection_get_is_authenticated(bus)) {
|
||||
dbus_connection_close(bus);
|
||||
dbus_connection_unref(bus);
|
||||
|
||||
dbus_set_error_const(error, DBUS_ERROR_TIMEOUT, "Failed to authenticate in time.");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (private)
|
||||
*private = true;
|
||||
|
||||
@ -118,12 +124,93 @@ int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private, DBusError *
|
||||
if (!(bus = dbus_bus_get_private(t, error)))
|
||||
return -EIO;
|
||||
|
||||
dbus_connection_set_exit_on_disconnect(bus, FALSE);
|
||||
|
||||
if (private)
|
||||
*private = false;
|
||||
}
|
||||
|
||||
if ((r = sync_auth(bus, error)) < 0) {
|
||||
dbus_connection_close(bus);
|
||||
dbus_connection_unref(bus);
|
||||
return r;
|
||||
}
|
||||
|
||||
*_bus = bus;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error) {
|
||||
DBusConnection *bus;
|
||||
char *p = NULL;
|
||||
int r;
|
||||
|
||||
assert(_bus);
|
||||
assert(user || host);
|
||||
|
||||
if (user && host)
|
||||
asprintf(&p, "exec:path=ssh,argv1=-xT,argv2=%s@%s,argv3=systemd-stdio-bridge", user, host);
|
||||
else if (user)
|
||||
asprintf(&p, "exec:path=ssh,argv1=-xT,argv2=%s@localhost,argv3=systemd-stdio-bridge", user);
|
||||
else if (host)
|
||||
asprintf(&p, "exec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host);
|
||||
|
||||
if (!p) {
|
||||
dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bus = dbus_connection_open_private(p, error);
|
||||
free(p);
|
||||
|
||||
if (!bus)
|
||||
return -EIO;
|
||||
|
||||
dbus_connection_set_exit_on_disconnect(bus, FALSE);
|
||||
|
||||
if ((r = sync_auth(bus, error)) < 0) {
|
||||
dbus_connection_close(bus);
|
||||
dbus_connection_unref(bus);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!dbus_bus_register(bus, error)) {
|
||||
dbus_connection_close(bus);
|
||||
dbus_connection_unref(bus);
|
||||
return r;
|
||||
}
|
||||
|
||||
*_bus = bus;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error) {
|
||||
DBusConnection *bus;
|
||||
int r;
|
||||
|
||||
assert(_bus);
|
||||
|
||||
/* Don't bother with PolicyKit if we are root */
|
||||
if (geteuid() == 0)
|
||||
return bus_connect(DBUS_BUS_SYSTEM, _bus, NULL, error);
|
||||
|
||||
if (!(bus = dbus_connection_open_private("exec:path=pkexec,argv1=" SYSTEMD_STDIO_BRIDGE_BINARY_PATH, error)))
|
||||
return -EIO;
|
||||
|
||||
dbus_connection_set_exit_on_disconnect(bus, FALSE);
|
||||
|
||||
if ((r = sync_auth(bus, error)) < 0) {
|
||||
dbus_connection_close(bus);
|
||||
dbus_connection_unref(bus);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!dbus_bus_register(bus, error)) {
|
||||
dbus_connection_close(bus);
|
||||
dbus_connection_unref(bus);
|
||||
return r;
|
||||
}
|
||||
|
||||
*_bus = bus;
|
||||
return 0;
|
||||
}
|
||||
|
@ -28,6 +28,9 @@ int bus_check_peercred(DBusConnection *c);
|
||||
|
||||
int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private_bus, DBusError *error);
|
||||
|
||||
int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error);
|
||||
int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error);
|
||||
|
||||
const char *bus_error_message(const DBusError *error);
|
||||
|
||||
#endif
|
||||
|
@ -27,4 +27,15 @@
|
||||
<annotate key="org.freedesktop.policykit.exec.path">/lib/systemd/systemd-reply-password</annotate>
|
||||
</action>
|
||||
|
||||
<action id="org.freedesktop.systemd1.BusAccess">
|
||||
<description>Privileged system and service manager access</description>
|
||||
<message>Authentication is required to access the system and service manager.</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>auth_admin_keep</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">/usr/bin/systemd-stdio-bridge</annotate>
|
||||
</action>
|
||||
|
||||
</policyconfig>
|
||||
|
@ -108,6 +108,12 @@ static enum dot {
|
||||
DOT_ORDER,
|
||||
DOT_REQUIRE
|
||||
} arg_dot = DOT_ALL;
|
||||
static enum transport {
|
||||
TRANSPORT_NORMAL,
|
||||
TRANSPORT_SSH,
|
||||
TRANSPORT_POLKIT
|
||||
} arg_transport = TRANSPORT_NORMAL;
|
||||
static const char *arg_host = NULL;
|
||||
|
||||
static bool private_bus = false;
|
||||
|
||||
@ -2061,12 +2067,14 @@ static void print_status_info(UnitStatusInfo *i) {
|
||||
|
||||
printf("\t CGroup: %s\n", i->default_control_group);
|
||||
|
||||
if ((c = columns()) > 18)
|
||||
c -= 18;
|
||||
else
|
||||
c = 0;
|
||||
if (arg_transport != TRANSPORT_SSH) {
|
||||
if ((c = columns()) > 18)
|
||||
c -= 18;
|
||||
else
|
||||
c = 0;
|
||||
|
||||
show_cgroup_by_path(i->default_control_group, "\t\t ", c);
|
||||
show_cgroup_by_path(i->default_control_group, "\t\t ", c);
|
||||
}
|
||||
}
|
||||
|
||||
if (i->need_daemon_reload)
|
||||
@ -4290,22 +4298,25 @@ static int systemctl_help(void) {
|
||||
" pending\n"
|
||||
" --ignore-dependencies\n"
|
||||
" When queueing a new job, ignore all its dependencies\n"
|
||||
" -q --quiet Suppress output\n"
|
||||
" --no-block Do not wait until operation finished\n"
|
||||
" --no-pager Do not pipe output into a pager.\n"
|
||||
" --system Connect to system manager\n"
|
||||
" --user Connect to user service manager\n"
|
||||
" --order When generating graph for dot, show only order\n"
|
||||
" --require When generating graph for dot, show only requirement\n"
|
||||
" --no-wall Don't send wall message before halt/power-off/reboot\n"
|
||||
" --global Enable/disable unit files globally\n"
|
||||
" --no-reload When enabling/disabling unit files, don't reload daemon\n"
|
||||
" configuration\n"
|
||||
" --no-ask-password\n"
|
||||
" Do not ask for system passwords\n"
|
||||
" --kill-mode=MODE How to send signal\n"
|
||||
" --kill-who=WHO Who to send signal to\n"
|
||||
" -s --signal=SIGNAL Which signal to send\n"
|
||||
" -H --host=[user@]host\n"
|
||||
" Show information for remote host\n"
|
||||
" -P --privileged Acquire privileges before execution\n"
|
||||
" -q --quiet Suppress output\n"
|
||||
" --no-block Do not wait until operation finished\n"
|
||||
" --no-wall Don't send wall message before halt/power-off/reboot\n"
|
||||
" --no-reload When enabling/disabling unit files, don't reload daemon\n"
|
||||
" configuration\n"
|
||||
" --no-pager Do not pipe output into a pager.\n"
|
||||
" --no-ask-password\n"
|
||||
" Do not ask for system passwords\n"
|
||||
" --order When generating graph for dot, show only order\n"
|
||||
" --require When generating graph for dot, show only requirement\n"
|
||||
" --system Connect to system manager\n"
|
||||
" --user Connect to user service manager\n"
|
||||
" --global Enable/disable unit files globally\n"
|
||||
" -f --force When enabling unit files, override existing symlinks\n"
|
||||
" When shutting down, execute action immediately\n"
|
||||
" --defaults When disabling unit files, remove default symlinks only\n\n"
|
||||
@ -4472,6 +4483,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
{ "kill-who", required_argument, NULL, ARG_KILL_WHO },
|
||||
{ "signal", required_argument, NULL, 's' },
|
||||
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
||||
{ "host", required_argument, NULL, 'H' },
|
||||
{ "privileged",no_argument, NULL, 'P' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
@ -4483,7 +4496,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
/* Only when running as systemctl we ask for passwords */
|
||||
arg_ask_password = true;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "ht:p:aqfs:", options, NULL)) >= 0) {
|
||||
while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:P", options, NULL)) >= 0) {
|
||||
|
||||
switch (c) {
|
||||
|
||||
@ -4605,6 +4618,15 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
arg_ask_password = false;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
arg_transport = TRANSPORT_POLKIT;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
arg_transport = TRANSPORT_SSH;
|
||||
arg_host = optarg;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -4614,6 +4636,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_transport != TRANSPORT_NORMAL && arg_user) {
|
||||
log_error("Cannot access user instance remotely.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -5622,7 +5649,16 @@ int main(int argc, char*argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
bus_connect(arg_user ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &private_bus, &error);
|
||||
if (arg_transport == TRANSPORT_NORMAL)
|
||||
bus_connect(arg_user ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &private_bus, &error);
|
||||
else if (arg_transport == TRANSPORT_POLKIT) {
|
||||
bus_connect_system_polkit(&bus, &error);
|
||||
private_bus = false;
|
||||
} else if (arg_transport == TRANSPORT_SSH) {
|
||||
bus_connect_system_ssh(NULL, arg_host, &bus, &error);
|
||||
private_bus = false;
|
||||
} else
|
||||
assert_not_reached("Uh, invalid transport...");
|
||||
|
||||
switch (arg_action) {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user