summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKay Sievers <kay.sievers@vrfy.org>2012-02-29 22:22:15 +0100
committerKay Sievers <kay.sievers@vrfy.org>2012-02-29 22:28:33 +0100
commit4c80c73c2b804576b1de27e644c1da4dab2f9026 (patch)
tree1c405f271d25d6a0b7b16bf111ef14bcee35404e
parent8dc6b88fd2dad113d7dab776d623d7b301e4b754 (diff)
systemctl: forward untrusted user reboot, poweroff requests to logind
-rw-r--r--configure.ac1
-rw-r--r--src/systemctl.c133
2 files changed, 117 insertions, 17 deletions
diff --git a/configure.ac b/configure.ac
index 62e8cdf582..fe1771c360 100644
--- a/configure.ac
+++ b/configure.ac
@@ -335,6 +335,7 @@ if test "x$enable_logind" != "xno"; then
have_logind=yes
fi
AM_CONDITIONAL(ENABLE_LOGIND, [test "$have_logind" = "yes"])
+AS_IF([test "$have_logind" = "yes"], [ AC_DEFINE(HAVE_LOGIND, [1], [Logind support available]) ])
have_hostnamed=no
AC_ARG_ENABLE(hostnamed, AS_HELP_STRING([--disable-hostnamed], [disable hostname daemon]))
diff --git a/src/systemctl.c b/src/systemctl.c
index dc3703050e..8f99a724b8 100644
--- a/src/systemctl.c
+++ b/src/systemctl.c
@@ -126,7 +126,7 @@ static OutputMode arg_output = OUTPUT_SHORT;
static bool private_bus = false;
static int daemon_reload(DBusConnection *bus, char **args);
-static void halt_now(int action);
+static void halt_now(enum action a);
static bool on_tty(void) {
static int t = -1;
@@ -226,7 +226,7 @@ static int translate_bus_error_to_exit_status(int r, const DBusError *error) {
return EXIT_FAILURE;
}
-static void warn_wall(enum action action) {
+static void warn_wall(enum action a) {
static const char *table[_ACTION_MAX] = {
[ACTION_HALT] = "The system is going down for system halt NOW!",
[ACTION_REBOOT] = "The system is going down for reboot NOW!",
@@ -256,10 +256,10 @@ static void warn_wall(enum action action) {
free(p);
}
- if (!table[action])
+ if (!table[a])
return;
- utmp_wall(table[action], NULL);
+ utmp_wall(table[a], NULL);
}
static bool avoid_bus(void) {
@@ -1682,33 +1682,125 @@ finish:
return ret;
}
+/* ask systemd-logind, which might grant access to unprivileged users through polkit */
+static int reboot_with_logind(DBusConnection *bus, enum action a) {
+#ifdef HAVE_LOGIND
+ const char *method;
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ dbus_bool_t interactive = true;
+ int r;
+
+ dbus_error_init(&error);
+
+ switch (a) {
+
+ case ACTION_REBOOT:
+ method = "Reboot";
+ break;
+
+ case ACTION_POWEROFF:
+ method = "PowerOff";
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ method);
+ if (!m) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_BOOLEAN, &interactive,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+ if (!reply) {
+ if (error_is_no_service(&error)) {
+ log_debug("Failed to issue method call: %s", bus_error_message(&error));
+ r = -ENOENT;
+ goto finish;
+ }
+
+ if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
+ log_debug("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EACCES;
+ goto finish;
+ }
+
+ log_info("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+#else
+ return -ENOSYS;
+#endif
+}
+
static int start_special(DBusConnection *bus, char **args) {
+ enum action a;
int r;
assert(bus);
assert(args);
- if (arg_force >= 2 && streq(args[0], "halt"))
+ a = verb_to_action(args[0]);
+
+ if (arg_force >= 2 && a == ACTION_HALT)
halt_now(ACTION_HALT);
- if (arg_force >= 2 && streq(args[0], "poweroff"))
+ if (arg_force >= 2 && a == ACTION_POWEROFF)
halt_now(ACTION_POWEROFF);
- if (arg_force >= 2 && streq(args[0], "reboot"))
- halt_now(ACTION_POWEROFF);
+ if (arg_force >= 2 && a == ACTION_REBOOT)
+ halt_now(ACTION_REBOOT);
if (arg_force &&
- (streq(args[0], "halt") ||
- streq(args[0], "poweroff") ||
- streq(args[0], "reboot") ||
- streq(args[0], "kexec") ||
- streq(args[0], "exit")))
+ (a == ACTION_HALT ||
+ a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT ||
+ a == ACTION_KEXEC ||
+ a == ACTION_EXIT))
return daemon_reload(bus, args);
- r = start_unit(bus, args);
+ if (geteuid() != 0) {
+ /* first try logind, to allow authentication with polkit */
+ if (a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT) {
+ r = reboot_with_logind(bus, a);
+ if (r >= 0)
+ return r;
+ }
+ }
+ r = start_unit(bus, args);
if (r >= 0)
- warn_wall(verb_to_action(args[0]));
+ warn_wall(a);
return r;
}
@@ -5181,13 +5273,13 @@ done:
return 0;
}
-static void halt_now(int action) {
+static void halt_now(enum action a) {
/* Make sure C-A-D is handled by the kernel from this
* point on... */
reboot(RB_ENABLE_CAD);
- switch (action) {
+ switch (a) {
case ACTION_HALT:
log_info("Halting.");
@@ -5215,6 +5307,13 @@ static int halt_main(DBusConnection *bus) {
int r;
if (geteuid() != 0) {
+ if (arg_action == ACTION_POWEROFF ||
+ arg_action == ACTION_REBOOT) {
+ r = reboot_with_logind(bus, arg_action);
+ if (r >= 0)
+ return r;
+ }
+
log_error("Must be root.");
return -EPERM;
}