polkit: temporarily spawn of a polkit agent in terminals for possibly authenticated operations

This commit is contained in:
Lennart Poettering 2012-04-11 18:50:16 +02:00
parent f25626edf4
commit 6bb92a169e
12 changed files with 322 additions and 144 deletions

View File

@ -120,6 +120,7 @@ AM_CPPFLAGS = \
-DSYSTEMD_KBD_MODEL_MAP=\"$(pkgdatadir)/kbd-model-map\" \
-DX_SERVER=\"$(bindir)/X\" \
-DUDEVLIBEXECDIR=\""$(libexecdir)/udev"\" \
-DPOLKIT_AGENT_BINARY_PATH=\"$(bindir)/pkttyagent\" \
-I $(top_srcdir)/src \
-I $(top_srcdir)/src/shared \
-I $(top_srcdir)/src/readahead \
@ -766,8 +767,7 @@ libsystemd_core_la_SOURCES = \
src/core/ask-password-api.h \
src/core/sysfs-show.h \
src/core/polkit.h \
src/core/dbus-loop.h \
src/core/spawn-agent.h
src/core/dbus-loop.h
nodist_libsystemd_core_la_SOURCES = \
src/load-fragment-gperf.c \
@ -1078,7 +1078,10 @@ systemctl_SOURCES = \
src/cgroup-show.h \
src/unit-name.c \
src/install.c \
src/spawn-agent.c \
src/spawn-ask-password-agent.c \
src/spawn-ask-password-agent.h \
src/spawn-polkit-agent.c \
src/spawn-polkit-agent.h \
src/logs-show.c \
src/logs-show.h
@ -2589,6 +2592,8 @@ loginctl_SOURCES = \
src/login/loginctl.c \
src/login/sysfs-show.c \
src/dbus-common.c \
src/spawn-polkit-agent.c \
src/spawn-polkit-agent.h \
src/cgroup-show.c \
src/cgroup-show.h

View File

@ -118,6 +118,14 @@
pager.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--no-ask-password</option></term>
<listitem><para>Don't query the user
for authentication for privileged
operations.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--kill-who=</option></term>

View File

@ -299,11 +299,13 @@
<command>systemctl</command> will
query the user on the terminal for the
necessary secrets. Use this option to
switch this behavior off. In this
case the password must be supplied by
some other means (for example
graphical password agents) or the
service might fail.</para></listitem>
switch this behavior off. In this case
the password must be supplied by some
other means (for example graphical
password agents) or the service might
fail. This also disables querying the
user for authentication for privileged
operations.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -35,6 +35,7 @@
#include "strv.h"
#include "cgroup-show.h"
#include "sysfs-show.h"
#include "spawn-polkit-agent.h"
static char **arg_property = NULL;
static bool arg_all = false;
@ -46,6 +47,7 @@ static enum transport {
TRANSPORT_SSH,
TRANSPORT_POLKIT
} arg_transport = TRANSPORT_NORMAL;
static bool arg_ask_password = true;
static const char *arg_host = NULL;
static bool on_tty(void) {
@ -68,8 +70,20 @@ static void pager_open_if_enabled(void) {
/* Cache result before we open the pager */
on_tty();
if (!arg_no_pager)
pager_open();
if (arg_no_pager)
return;
pager_open();
}
static void polkit_agent_open_if_enabled(void) {
/* Open the polkit agent as a child process if necessary */
if (!arg_ask_password)
return;
polkit_agent_open();
}
static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
@ -1286,6 +1300,8 @@ static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
dbus_error_init(&error);
polkit_agent_open_if_enabled();
b = streq(args[0], "enable-linger");
for (i = 1; i < n; i++) {
@ -1490,6 +1506,8 @@ static int attach(DBusConnection *bus, char **args, unsigned n) {
dbus_error_init(&error);
polkit_agent_open_if_enabled();
for (i = 2; i < n; i++) {
DBusMessage *reply;
@ -1546,6 +1564,8 @@ static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
dbus_error_init(&error);
polkit_agent_open_if_enabled();
m = dbus_message_new_method_call(
"org.freedesktop.login1",
"/org/freedesktop/login1",
@ -1684,7 +1704,8 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_KILL_WHO
ARG_KILL_WHO,
ARG_NO_ASK_PASSWORD
};
static const struct option options[] = {
@ -1697,6 +1718,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "signal", required_argument, NULL, 's' },
{ "host", required_argument, NULL, 'H' },
{ "privileged",no_argument, NULL, 'P' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ NULL, 0, NULL, 0 }
};
@ -1744,6 +1766,9 @@ static int parse_argv(int argc, char *argv[]) {
arg_no_pager = true;
break;
case ARG_NO_ASK_PASSWORD:
arg_ask_password = false;
case ARG_KILL_WHO:
arg_kill_who = optarg;
break;

View File

@ -6035,3 +6035,88 @@ int fd_inc_rcvbuf(int fd, size_t n) {
return 1;
}
int fork_agent(pid_t *pid, const char *path, ...) {
pid_t parent_pid, agent_pid;
int fd;
bool stdout_is_tty, stderr_is_tty;
unsigned n, i;
va_list ap;
char **l;
assert(pid);
assert(path);
parent_pid = getpid();
/* Spawns a temporary TTY agent, making sure it goes away when
* we go away */
agent_pid = fork();
if (agent_pid < 0)
return -errno;
if (agent_pid != 0) {
*pid = agent_pid;
return 0;
}
/* In the child:
*
* Make sure the agent goes away when the parent dies */
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
_exit(EXIT_FAILURE);
/* Check whether our parent died before we were able
* to set the death signal */
if (getppid() != parent_pid)
_exit(EXIT_SUCCESS);
/* Don't leak fds to the agent */
close_all_fds(NULL, 0);
stdout_is_tty = isatty(STDOUT_FILENO);
stderr_is_tty = isatty(STDERR_FILENO);
if (!stdout_is_tty || !stderr_is_tty) {
/* Detach from stdout/stderr. and reopen
* /dev/tty for them. This is important to
* ensure that when systemctl is started via
* popen() or a similar call that expects to
* read EOF we actually do generate EOF and
* not delay this indefinitely by because we
* keep an unused copy of stdin around. */
fd = open("/dev/tty", O_WRONLY);
if (fd < 0) {
log_error("Failed to open /dev/tty: %m");
_exit(EXIT_FAILURE);
}
if (!stdout_is_tty)
dup2(fd, STDOUT_FILENO);
if (!stderr_is_tty)
dup2(fd, STDERR_FILENO);
if (fd > 2)
close(fd);
}
/* Count arguments */
va_start(ap, path);
for (n = 0; va_arg(ap, char*); n++)
;
va_end(ap);
/* Allocate strv */
l = alloca(sizeof(char *) * (n + 1));
/* Fill in arguments */
va_start(ap, path);
for (i = 0; i <= n; i++)
l[i] = va_arg(ap, char*);
va_end(ap);
execv(path, l);
_exit(EXIT_FAILURE);
}

View File

@ -528,4 +528,7 @@ int is_kernel_thread(pid_t pid);
int fd_inc_sndbuf(int fd, size_t n);
int fd_inc_rcvbuf(int fd, size_t n);
int fork_agent(pid_t *pid, const char *path, ...);
#endif

View File

@ -1,120 +0,0 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 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/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/prctl.h>
#include <signal.h>
#include <fcntl.h>
#include "log.h"
#include "util.h"
#include "spawn-agent.h"
static pid_t agent_pid = 0;
void agent_open(void) {
pid_t parent_pid;
if (agent_pid > 0)
return;
/* We check STDIN here, not STDOUT, since this is about input,
* not output */
if (!isatty(STDIN_FILENO))
return;
parent_pid = getpid();
/* Spawns a temporary TTY agent, making sure it goes away when
* we go away */
agent_pid = fork();
if (agent_pid < 0) {
log_error("Failed to fork agent: %m");
return;
}
if (agent_pid == 0) {
/* In the child */
int fd;
bool stdout_is_tty, stderr_is_tty;
/* Make sure the agent goes away when the parent dies */
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
_exit(EXIT_FAILURE);
/* Check whether our parent died before we were able
* to set the death signal */
if (getppid() != parent_pid)
_exit(EXIT_SUCCESS);
/* Don't leak fds to the agent */
close_all_fds(NULL, 0);
stdout_is_tty = isatty(STDOUT_FILENO);
stderr_is_tty = isatty(STDERR_FILENO);
if (!stdout_is_tty || !stderr_is_tty) {
/* Detach from stdout/stderr. and reopen
* /dev/tty for them. This is important to
* ensure that when systemctl is started via
* popen() or a similar call that expects to
* read EOF we actually do generate EOF and
* not delay this indefinitely by because we
* keep an unused copy of stdin around. */
fd = open("/dev/tty", O_WRONLY);
if (fd < 0) {
log_error("Failed to open /dev/tty: %m");
_exit(EXIT_FAILURE);
}
if (!stdout_is_tty)
dup2(fd, STDOUT_FILENO);
if (!stderr_is_tty)
dup2(fd, STDERR_FILENO);
if (fd > 2)
close(fd);
}
execl(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
log_error("Unable to execute agent: %m");
_exit(EXIT_FAILURE);
}
}
void agent_close(void) {
if (agent_pid <= 0)
return;
/* Inform agent that we are done */
kill(agent_pid, SIGTERM);
kill(agent_pid, SIGCONT);
wait_for_terminate(agent_pid, NULL);
agent_pid = 0;
}

View File

@ -0,0 +1,64 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 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/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/prctl.h>
#include <signal.h>
#include <fcntl.h>
#include "log.h"
#include "util.h"
#include "spawn-ask-password-agent.h"
static pid_t agent_pid = 0;
int ask_password_agent_open(void) {
int r;
if (agent_pid > 0)
return 0;
/* We check STDIN here, not STDOUT, since this is about input,
* not output */
if (!isatty(STDIN_FILENO))
return 0;
r = fork_agent(&agent_pid, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
if (r < 0)
log_error("Failed to fork TTY ask password agent: %s", strerror(-r));
return r;
}
void ask_password_agent_close(void) {
if (agent_pid <= 0)
return;
/* Inform agent that we are done */
kill(agent_pid, SIGTERM);
kill(agent_pid, SIGCONT);
wait_for_terminate(agent_pid, NULL);
agent_pid = 0;
}

View File

@ -1,7 +1,7 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foospawnagenthfoo
#define foospawnagenthfoo
#ifndef foospawnaskpasswordagenthfoo
#define foospawnaskpasswordagenthfoo
/***
This file is part of systemd.
@ -22,7 +22,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
void agent_open(void);
void agent_close(void);
int ask_password_agent_open(void);
void ask_password_agent_close(void);
#endif

64
src/spawn-polkit-agent.c Normal file
View File

@ -0,0 +1,64 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 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/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/prctl.h>
#include <signal.h>
#include <fcntl.h>
#include "log.h"
#include "util.h"
#include "spawn-polkit-agent.h"
static pid_t agent_pid = 0;
int polkit_agent_open(void) {
int r;
if (agent_pid > 0)
return 0;
/* We check STDIN here, not STDOUT, since this is about input,
* not output */
if (!isatty(STDIN_FILENO))
return 0;
r = fork_agent(&agent_pid, POLKIT_AGENT_BINARY_PATH, POLKIT_AGENT_BINARY_PATH, NULL);
if (r < 0)
log_error("Failed to fork TTY ask password agent: %s", strerror(-r));
return r;
}
void polkit_agent_close(void) {
if (agent_pid <= 0)
return;
/* Inform agent that we are done */
kill(agent_pid, SIGTERM);
kill(agent_pid, SIGCONT);
wait_for_terminate(agent_pid, NULL);
agent_pid = 0;
}

28
src/spawn-polkit-agent.h Normal file
View File

@ -0,0 +1,28 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foospawnpolkitagenthfoo
#define foospawnpolkitagenthfoo
/***
This file is part of systemd.
Copyright 2012 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/>.
***/
int polkit_agent_open(void);
void polkit_agent_close(void);
#endif

View File

@ -57,7 +57,8 @@
#include "build.h"
#include "unit-name.h"
#include "pager.h"
#include "spawn-agent.h"
#include "spawn-ask-password-agent.h"
#include "spawn-polkit-agent.h"
#include "install.h"
#include "logs-show.h"
@ -78,7 +79,7 @@ static bool arg_dry = false;
static bool arg_quiet = false;
static bool arg_full = false;
static int arg_force = 0;
static bool arg_ask_password = false;
static bool arg_ask_password = true;
static bool arg_failed = false;
static bool arg_runtime = false;
static char **arg_wall = NULL;
@ -154,7 +155,7 @@ static void pager_open_if_enabled(void) {
pager_open();
}
static void agent_open_if_enabled(void) {
static void ask_password_agent_open_if_enabled(void) {
/* Open the password agent as a child process if necessary */
@ -164,7 +165,20 @@ static void agent_open_if_enabled(void) {
if (arg_scope != UNIT_FILE_SYSTEM)
return;
agent_open();
ask_password_agent_open();
}
static void polkit_agent_open_if_enabled(void) {
/* Open the polkit agent as a child process if necessary */
if (!arg_ask_password)
return;
if (arg_scope != UNIT_FILE_SYSTEM)
return;
polkit_agent_open();
}
static const char *ansi_highlight_red(bool b) {
@ -1601,7 +1615,7 @@ static int start_unit(DBusConnection *bus, char **args) {
assert(bus);
agent_open_if_enabled();
ask_password_agent_open_if_enabled();
if (arg_action == ACTION_SYSTEMCTL) {
method =
@ -1695,6 +1709,8 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) {
dbus_error_init(&error);
polkit_agent_open_if_enabled();
switch (a) {
case ACTION_REBOOT:
@ -4290,9 +4306,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
/* Only when running as systemctl we ask for passwords */
arg_ask_password = true;
while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) {
switch (c) {
@ -5503,7 +5516,8 @@ finish:
strv_free(arg_property);
pager_close();
agent_close();
ask_password_agent_close();
polkit_agent_close();
return retval;
}