1/*
2 *
3 *  D-Bus helper library
4 *
5 *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
6 *
7 *
8 *  This program is free software; you can redistribute it and/or modify
9 *  it under the terms of the GNU General Public License as published by
10 *  the Free Software Foundation; either version 2 of the License, or
11 *  (at your option) any later version.
12 *
13 *  This program is distributed in the hope that it will be useful,
14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *  GNU General Public License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with this program; if not, write to the Free Software
20 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <errno.h>
29
30#include <dbus/dbus.h>
31
32#include <glib.h>
33
34int polkit_check_authorization(DBusConnection *conn,
35				const char *action, gboolean interaction,
36				void (*function) (dbus_bool_t authorized,
37							void *user_data),
38						void *user_data, int timeout);
39
40static void add_dict_with_string_value(DBusMessageIter *iter,
41					const char *key, const char *str)
42{
43	DBusMessageIter dict, entry, value;
44
45	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
46			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
47			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
48			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
49	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
50								NULL, &entry);
51
52	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
53
54	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
55					DBUS_TYPE_STRING_AS_STRING, &value);
56	dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
57	dbus_message_iter_close_container(&entry, &value);
58
59	dbus_message_iter_close_container(&dict, &entry);
60	dbus_message_iter_close_container(iter, &dict);
61}
62
63static void add_empty_string_dict(DBusMessageIter *iter)
64{
65	DBusMessageIter dict;
66
67	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
68			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
69			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
70			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
71
72	dbus_message_iter_close_container(iter, &dict);
73}
74
75static void add_arguments(DBusConnection *conn, DBusMessageIter *iter,
76				const char *action, dbus_uint32_t flags)
77{
78	const char *busname = dbus_bus_get_unique_name(conn);
79	const char *kind = "system-bus-name";
80	const char *cancel = "";
81	DBusMessageIter subject;
82
83	dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
84							NULL, &subject);
85	dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind);
86	add_dict_with_string_value(&subject, "name", busname);
87	dbus_message_iter_close_container(iter, &subject);
88
89	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action);
90	add_empty_string_dict(iter);
91	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
92	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel);
93}
94
95static dbus_bool_t parse_result(DBusMessageIter *iter)
96{
97	DBusMessageIter result;
98	dbus_bool_t authorized, challenge;
99
100	dbus_message_iter_recurse(iter, &result);
101
102	dbus_message_iter_get_basic(&result, &authorized);
103	dbus_message_iter_get_basic(&result, &challenge);
104
105	return authorized;
106}
107
108struct authorization_data {
109	void (*function) (dbus_bool_t authorized, void *user_data);
110	void *user_data;
111};
112
113static void authorization_reply(DBusPendingCall *call, void *user_data)
114{
115	struct authorization_data *data = user_data;
116	DBusMessage *reply;
117	DBusMessageIter iter;
118	dbus_bool_t authorized = FALSE;
119
120	reply = dbus_pending_call_steal_reply(call);
121
122	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
123		goto done;
124
125	if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE)
126		goto done;
127
128	dbus_message_iter_init(reply, &iter);
129
130	authorized = parse_result(&iter);
131
132done:
133	if (data->function != NULL)
134		data->function(authorized, data->user_data);
135
136	dbus_message_unref(reply);
137
138	dbus_pending_call_unref(call);
139}
140
141#define AUTHORITY_DBUS	"org.freedesktop.PolicyKit1"
142#define AUTHORITY_INTF	"org.freedesktop.PolicyKit1.Authority"
143#define AUTHORITY_PATH	"/org/freedesktop/PolicyKit1/Authority"
144
145int polkit_check_authorization(DBusConnection *conn,
146				const char *action, gboolean interaction,
147				void (*function) (dbus_bool_t authorized,
148							void *user_data),
149						void *user_data, int timeout)
150{
151	struct authorization_data *data;
152	DBusMessage *msg;
153	DBusMessageIter iter;
154	DBusPendingCall *call;
155	dbus_uint32_t flags = 0x00000000;
156
157	if (conn == NULL)
158		return -EINVAL;
159
160	data = dbus_malloc0(sizeof(*data));
161	if (data == NULL)
162		return -ENOMEM;
163
164	msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH,
165				AUTHORITY_INTF, "CheckAuthorization");
166	if (msg == NULL) {
167		dbus_free(data);
168		return -ENOMEM;
169	}
170
171	if (interaction == TRUE)
172		flags |= 0x00000001;
173
174	if (action == NULL)
175		action = "org.freedesktop.policykit.exec";
176
177	dbus_message_iter_init_append(msg, &iter);
178	add_arguments(conn, &iter, action, flags);
179
180	if (dbus_connection_send_with_reply(conn, msg,
181						&call, timeout) == FALSE) {
182		dbus_message_unref(msg);
183		dbus_free(data);
184		return -EIO;
185	}
186
187	if (call == NULL) {
188		dbus_message_unref(msg);
189		dbus_free(data);
190		return -EIO;
191	}
192
193	data->function = function;
194	data->user_data = user_data;
195
196	dbus_pending_call_set_notify(call, authorization_reply,
197							data, dbus_free);
198
199	dbus_message_unref(msg);
200
201	return 0;
202}
203