| /*-*- 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 <errno.h> |
| |
| #include "util.h" |
| #include "dbus-common.h" |
| #include "polkit.h" |
| |
| /* This mimics dbus_bus_get_unix_user() */ |
| static pid_t get_unix_process_id( |
| DBusConnection *connection, |
| const char *name, |
| DBusError *error) { |
| |
| DBusMessage *m = NULL, *reply = NULL; |
| uint32_t pid = 0; |
| |
| m = dbus_message_new_method_call( |
| DBUS_SERVICE_DBUS, |
| DBUS_PATH_DBUS, |
| DBUS_INTERFACE_DBUS, |
| "GetConnectionUnixProcessID"); |
| if (!m) { |
| dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); |
| goto finish; |
| } |
| |
| if (!dbus_message_append_args( |
| m, |
| DBUS_TYPE_STRING, &name, |
| DBUS_TYPE_INVALID)) { |
| dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); |
| goto finish; |
| } |
| |
| reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error); |
| if (!reply) |
| goto finish; |
| |
| if (dbus_set_error_from_message(error, reply)) |
| goto finish; |
| |
| if (!dbus_message_get_args( |
| reply, error, |
| DBUS_TYPE_UINT32, &pid, |
| DBUS_TYPE_INVALID)) |
| goto finish; |
| |
| finish: |
| if (m) |
| dbus_message_unref(m); |
| |
| if (reply) |
| dbus_message_unref(reply); |
| |
| return (pid_t) pid; |
| } |
| |
| int verify_polkit( |
| DBusConnection *c, |
| DBusMessage *request, |
| const char *action, |
| bool interactive, |
| bool *_challenge, |
| DBusError *error) { |
| |
| DBusMessage *m = NULL, *reply = NULL; |
| const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = ""; |
| const char *sender; |
| uint32_t flags = interactive ? 1 : 0; |
| pid_t pid_raw; |
| uint32_t pid_u32; |
| unsigned long long starttime_raw; |
| uint64_t starttime_u64; |
| DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant; |
| int r; |
| dbus_bool_t authorized = FALSE, challenge = FALSE; |
| |
| assert(c); |
| assert(request); |
| |
| sender = dbus_message_get_sender(request); |
| if (!sender) |
| return -EINVAL; |
| |
| pid_raw = get_unix_process_id(c, sender, error); |
| if (pid_raw == 0) |
| return -EINVAL; |
| |
| r = get_starttime_of_pid(pid_raw, &starttime_raw); |
| if (r < 0) |
| return r; |
| |
| m = dbus_message_new_method_call( |
| "org.freedesktop.PolicyKit1", |
| "/org/freedesktop/PolicyKit1/Authority", |
| "org.freedesktop.PolicyKit1.Authority", |
| "CheckAuthorization"); |
| if (!m) |
| return -ENOMEM; |
| |
| dbus_message_iter_init_append(m, &iter_msg); |
| |
| pid_u32 = (uint32_t) pid_raw; |
| starttime_u64 = (uint64_t) starttime_raw; |
| |
| if (!dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct) || |
| !dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process) || |
| !dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array) || |
| !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) || |
| !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid) || |
| !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant) || |
| !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32) || |
| !dbus_message_iter_close_container(&iter_dict, &iter_variant) || |
| !dbus_message_iter_close_container(&iter_array, &iter_dict) || |
| !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) || |
| !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &starttime) || |
| !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant) || |
| !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &starttime_u64) || |
| !dbus_message_iter_close_container(&iter_dict, &iter_variant) || |
| !dbus_message_iter_close_container(&iter_array, &iter_dict) || |
| !dbus_message_iter_close_container(&iter_struct, &iter_array) || |
| !dbus_message_iter_close_container(&iter_msg, &iter_struct) || |
| !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action) || |
| !dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array) || |
| !dbus_message_iter_close_container(&iter_msg, &iter_array) || |
| !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags) || |
| !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id)) { |
| r = -ENOMEM; |
| goto finish; |
| } |
| |
| reply = dbus_connection_send_with_reply_and_block(c, m, -1, error); |
| if (!reply) { |
| r = -EIO; |
| goto finish; |
| } |
| |
| if (dbus_set_error_from_message(error, reply)) { |
| r = -EIO; |
| goto finish; |
| } |
| |
| if (!dbus_message_iter_init(reply, &iter_msg) || |
| dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) { |
| r = -EIO; |
| goto finish; |
| } |
| |
| dbus_message_iter_recurse(&iter_msg, &iter_struct); |
| |
| if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) { |
| r = -EIO; |
| goto finish; |
| } |
| |
| dbus_message_iter_get_basic(&iter_struct, &authorized); |
| |
| if (!dbus_message_iter_next(&iter_struct) || |
| dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) { |
| r = -EIO; |
| goto finish; |
| } |
| |
| dbus_message_iter_get_basic(&iter_struct, &challenge); |
| |
| if (authorized) |
| r = 1; |
| else if (_challenge) { |
| *_challenge = !!challenge; |
| r = 0; |
| } else |
| r = -EPERM; |
| |
| finish: |
| |
| if (m) |
| dbus_message_unref(m); |
| |
| if (reply) |
| dbus_message_unref(reply); |
| |
| return r; |
| } |