1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* cynara.c Cynara runtime privilege checking
4 * Copyright (c) 2014 Samsung Electronics, Ltd.
6 * Licensed under the Academic Free License version 2.1
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.
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.
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 Street, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <dbus/dbus.h>
32 #include <dbus/dbus-watch.h>
33 #include <bus/connection.h>
34 #ifdef DBUS_ENABLE_CYNARA
35 #include <cynara-client-async.h>
36 #include <dbus/dbus-connection-internal.h>
39 #define USE_CYNARA_CACHE 1
41 #ifdef DBUS_ENABLE_CYNARA
42 typedef struct BusCynara
49 DBusWatch *cynara_watch;
52 typedef struct BusCynaraCheckData
54 BusDeferredMessage *deferred_message;
55 BusDeferredMessageStatus check_type;
58 static dbus_bool_t bus_cynara_watch_callback(DBusWatch *watch,
62 static void status_callback(int old_fd,
64 cynara_async_status status,
65 void *user_status_data);
66 static void bus_cynara_check_response_callback (cynara_check_id check_id,
67 cynara_async_call_cause cause,
69 void *user_response_data);
71 static BusCynaraCheckData *
72 bus_cynara_check_data_new(BusDeferredMessage *deferred_message, BusDeferredMessageStatus check_type)
74 BusCynaraCheckData *data;
76 data = dbus_new(BusCynaraCheckData, 1);
80 data->deferred_message = deferred_message;
81 data->check_type = check_type;
88 bus_cynara_new(BusCheck *check, DBusError *error)
90 #ifdef DBUS_ENABLE_CYNARA
95 cynara = dbus_new(BusCynara, 1);
102 context = bus_check_get_context(check);
104 cynara->refcount = 1;
105 cynara->check = check;
106 cynara->context = context;
107 cynara->cynara_watch = NULL;
109 ret = cynara_async_initialize(&cynara->cynara, NULL, &status_callback, cynara);
110 if (ret != CYNARA_API_SUCCESS)
112 dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to initialize Cynara client");
124 bus_cynara_ref (BusCynara *cynara)
126 #ifdef DBUS_ENABLE_CYNARA
127 _dbus_assert (cynara->refcount > 0);
128 cynara->refcount += 1;
137 bus_cynara_unref (BusCynara *cynara)
139 #ifdef DBUS_ENABLE_CYNARA
140 _dbus_assert (cynara->refcount > 0);
142 cynara->refcount -= 1;
144 if (cynara->refcount == 0)
146 if (cynara->cynara != NULL)
147 cynara_async_finish(cynara->cynara);
154 bus_cynara_check_privilege (BusCynara *cynara,
155 DBusMessage *message,
156 DBusConnection *sender,
157 DBusConnection *addressed_recipient,
158 DBusConnection *proposed_recipient,
159 const char *privilege,
160 BusDeferredMessageStatus check_type,
161 BusDeferredMessage **deferred_message_param)
163 #ifdef DBUS_ENABLE_CYNARA
167 const char *session_id;
169 cynara_check_id check_id;
170 DBusConnection *connection = check_type == BUS_DEFERRED_MESSAGE_CHECK_RECEIVE ? proposed_recipient : sender;
171 BusDeferredMessage *deferred_message = NULL;
172 BusResult return_result;
173 BusCynaraCheckData *callback_data;
175 _dbus_assert(connection != NULL);
177 if (deferred_message_param)
178 deferred_message = *deferred_message_param;
180 if (dbus_connection_get_unix_user(connection, &uid) == FALSE)
181 return BUS_RESULT_FALSE;
183 if (_dbus_connection_get_linux_security_label (connection, (char **)&label) == FALSE)
184 return BUS_RESULT_FALSE;
186 session_id = bus_connection_get_cynara_session_id (connection);
187 if (session_id == NULL)
189 dbus_free ((char*)label);
190 return BUS_RESULT_FALSE;
193 snprintf(user, sizeof(user), "%lu", uid);
196 result = cynara_async_check_cache(cynara->cynara, label, session_id, user, privilege);
198 result = CYNARA_API_CACHE_MISS;
203 case CYNARA_API_ACCESS_ALLOWED:
204 _dbus_verbose("Cynara: got ALLOWED answer from cache (client=%s session_id=%s user=%s privilege=%s)\n",
205 label, session_id, user, privilege);
206 return_result = BUS_RESULT_TRUE;
209 case CYNARA_API_ACCESS_DENIED:
210 _dbus_verbose("Cynara: got DENIED answer from cache (client=%s session_id=%s user=%s privilege=%s)\n",
211 label, session_id, user, privilege);
212 return_result = BUS_RESULT_FALSE;
215 case CYNARA_API_CACHE_MISS:
216 if (deferred_message == NULL)
218 deferred_message = bus_deferred_message_new(message, sender, addressed_recipient,
219 proposed_recipient, BUS_RESULT_LATER);
220 if (deferred_message == NULL)
222 _dbus_verbose("Failed to allocate memory for deferred message\n");
223 return_result = BUS_RESULT_FALSE;
229 bus_deferred_message_ref(deferred_message);
232 callback_data = bus_cynara_check_data_new(deferred_message, check_type);
233 if (callback_data == NULL)
235 _dbus_verbose("Failed to allocate memory for deferred message callback data\n");
236 return_result = BUS_RESULT_FALSE;
239 /* callback is supposed to unref deferred_message and free callback_data */
240 result = cynara_async_create_request(cynara->cynara, label, session_id, user, privilege, &check_id,
241 &bus_cynara_check_response_callback, callback_data);
242 if (result == CYNARA_API_SUCCESS)
244 _dbus_verbose("Created Cynara request: client=%s session_id=%s user=%s privilege=%s check_id=%u "
245 "deferred_message=%p\n", label, session_id, user, privilege, (unsigned int)check_id, deferred_message);
246 if (deferred_message_param != NULL)
247 *deferred_message_param = deferred_message;
248 return_result = BUS_RESULT_LATER;
253 _dbus_verbose("Error on cynara request create: %i\n", result);
254 bus_deferred_message_unref(deferred_message);
255 return_result = BUS_RESULT_FALSE;
260 _dbus_verbose("Error when accessing Cynara cache: %i\n", result);
261 return_result = BUS_RESULT_FALSE;
265 dbus_free ((char*)label);
266 return return_result;
269 return BUS_RESULT_FALSE;
275 #ifdef DBUS_ENABLE_CYNARA
277 status_callback(int old_fd, int new_fd, cynara_async_status status,
278 void *user_status_data)
280 BusCynara *cynara = (BusCynara *)user_status_data;
281 DBusLoop *loop = bus_context_get_loop(cynara->context);
283 if (cynara->cynara_watch != NULL)
285 _dbus_loop_remove_watch(loop, cynara->cynara_watch);
286 _dbus_watch_invalidate(cynara->cynara_watch);
287 _dbus_watch_unref(cynara->cynara_watch);
288 cynara->cynara_watch = NULL;
298 case CYNARA_STATUS_FOR_READ:
299 flags = DBUS_WATCH_READABLE;
301 case CYNARA_STATUS_FOR_RW:
302 flags = DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE;
305 /* Cynara passed unknown status - warn and add RW watch */
306 _dbus_verbose("Cynara passed unknown status value: 0x%08X\n", (unsigned int)status);
307 flags = DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE;
311 watch = _dbus_watch_new(new_fd, flags, TRUE, &bus_cynara_watch_callback, cynara, NULL);
314 if (_dbus_loop_add_watch(loop, watch) == TRUE)
316 cynara->cynara_watch = watch;
320 _dbus_watch_invalidate(watch);
321 _dbus_watch_unref(watch);
324 /* It seems like not much can be done at this point. Cynara events won't be processed
325 * until next Cynara function call triggering status callback */
326 _dbus_verbose("Failed to add dbus watch\n");
331 bus_cynara_watch_callback(DBusWatch *watch,
335 BusCynara *cynara = (BusCynara *)data;
336 int result = cynara_async_process(cynara->cynara);
337 if (result != CYNARA_API_SUCCESS)
338 _dbus_verbose("cynara_async_process returned %d\n", result);
340 return result != CYNARA_API_OUT_OF_MEMORY ? TRUE : FALSE;
343 static inline const char *
344 call_cause_to_string(cynara_async_call_cause cause)
348 case CYNARA_CALL_CAUSE_ANSWER:
350 case CYNARA_CALL_CAUSE_CANCEL:
352 case CYNARA_CALL_CAUSE_FINISH:
354 case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
355 return "SERVICE NOT AVAILABLE";
362 bus_cynara_check_response_callback (cynara_check_id check_id,
363 cynara_async_call_cause cause,
365 void *user_response_data)
367 BusCynaraCheckData *check_data = user_response_data;
368 BusDeferredMessage *deferred_message;
370 BusDeferredMessageStatus check_type;
372 _dbus_verbose("Cynara callback: check_id=%u, cause=%s response=%i response_data=%p\n",
373 (unsigned int)check_id, call_cause_to_string(cause), response, user_response_data);
375 if (check_data == NULL)
378 deferred_message = check_data->deferred_message;
379 check_type = check_data->check_type;
381 dbus_free(check_data);
383 if (deferred_message == NULL)
386 if (cause == CYNARA_CALL_CAUSE_ANSWER && response == CYNARA_API_ACCESS_ALLOWED)
387 result = BUS_RESULT_TRUE;
389 result = BUS_RESULT_FALSE;
391 bus_deferred_message_response_received(deferred_message, check_type, result);
392 bus_deferred_message_unref(deferred_message);
395 #endif /* DBUS_ENABLE_CYNARA */