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 static dbus_bool_t bus_cynara_watch_callback(DBusWatch *watch,
56 static void status_callback(int old_fd,
58 cynara_async_status status,
59 void *user_status_data);
60 static void bus_cynara_check_response_callback (cynara_check_id check_id,
61 cynara_async_call_cause cause,
63 void *user_response_data);
68 bus_cynara_new(BusCheck *check, DBusError *error)
70 #ifdef DBUS_ENABLE_CYNARA
75 cynara = dbus_new(BusCynara, 1);
82 context = bus_check_get_context(check);
85 cynara->check = check;
86 cynara->context = context;
87 cynara->cynara_watch = NULL;
89 ret = cynara_async_initialize(&cynara->cynara, NULL, &status_callback, cynara);
90 if (ret != CYNARA_API_SUCCESS)
92 dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to initialize Cynara client");
104 bus_cynara_ref (BusCynara *cynara)
106 #ifdef DBUS_ENABLE_CYNARA
107 _dbus_assert (cynara->refcount > 0);
108 cynara->refcount += 1;
117 bus_cynara_unref (BusCynara *cynara)
119 #ifdef DBUS_ENABLE_CYNARA
120 _dbus_assert (cynara->refcount > 0);
122 cynara->refcount -= 1;
124 if (cynara->refcount == 0)
126 if (cynara->cynara != NULL)
127 cynara_async_finish(cynara->cynara);
134 bus_cynara_check_privilege (BusCynara *cynara,
135 DBusMessage *message,
136 DBusConnection *sender,
137 DBusConnection *addressed_recipient,
138 DBusConnection *proposed_recipient,
139 const char *privilege,
140 BusDeferredMessageStatus check_type,
141 BusDeferredMessage **deferred_message_param)
143 #ifdef DBUS_ENABLE_CYNARA
147 const char *session_id;
149 cynara_check_id check_id;
150 DBusConnection *connection = check_type == BUS_DEFERRED_MESSAGE_CHECK_RECEIVE ? proposed_recipient : sender;
151 BusDeferredMessage *deferred_message;
152 BusResult return_result;
154 _dbus_assert(connection != NULL);
156 if (dbus_connection_get_unix_user(connection, &uid) == FALSE)
157 return BUS_RESULT_FALSE;
159 if (_dbus_connection_get_linux_security_label (connection, (char **)&label) == FALSE)
160 return BUS_RESULT_FALSE;
162 session_id = bus_connection_get_cynara_session_id (connection);
163 if (session_id == NULL)
165 dbus_free ((char*)label);
166 return BUS_RESULT_FALSE;
169 snprintf(user, sizeof(user), "%lu", uid);
172 result = cynara_async_check_cache(cynara->cynara, label, session_id, user, privilege);
174 result = CYNARA_API_CACHE_MISS;
179 case CYNARA_API_ACCESS_ALLOWED:
180 _dbus_verbose("Cynara: got ALLOWED answer from cache (client=%s session_id=%s user=%s privilege=%s)\n",
181 label, session_id, user, privilege);
182 return_result = BUS_RESULT_TRUE;
185 case CYNARA_API_ACCESS_DENIED:
186 _dbus_verbose("Cynara: got DENIED answer from cache (client=%s session_id=%s user=%s privilege=%s)\n",
187 label, session_id, user, privilege);
188 return_result = BUS_RESULT_FALSE;
191 case CYNARA_API_CACHE_MISS:
192 deferred_message = bus_deferred_message_new(message, sender, addressed_recipient,
193 proposed_recipient, BUS_RESULT_LATER);
194 if (deferred_message == NULL)
196 _dbus_verbose("Failed to allocate memory for deferred message\n");
197 return_result = BUS_RESULT_FALSE;
201 /* callback is supposed to unref deferred_message*/
202 result = cynara_async_create_request(cynara->cynara, label, session_id, user, privilege, &check_id,
203 &bus_cynara_check_response_callback, deferred_message);
204 if (result == CYNARA_API_SUCCESS)
206 _dbus_verbose("Created Cynara request: client=%s session_id=%s user=%s privilege=%s check_id=%u "
207 "deferred_message=%p\n", label, session_id, user, privilege, (unsigned int)check_id, deferred_message);
208 if (deferred_message_param != NULL)
209 *deferred_message_param = deferred_message;
210 return_result = BUS_RESULT_LATER;
215 _dbus_verbose("Error on cynara request create: %i\n", result);
216 bus_deferred_message_unref(deferred_message);
217 return_result = BUS_RESULT_FALSE;
222 _dbus_verbose("Error when accessing Cynara cache: %i\n", result);
223 return_result = BUS_RESULT_FALSE;
227 dbus_free ((char*)label);
228 return return_result;
231 return BUS_RESULT_FALSE;
237 #ifdef DBUS_ENABLE_CYNARA
239 status_callback(int old_fd, int new_fd, cynara_async_status status,
240 void *user_status_data)
242 BusCynara *cynara = (BusCynara *)user_status_data;
243 DBusLoop *loop = bus_context_get_loop(cynara->context);
245 if (cynara->cynara_watch != NULL)
247 _dbus_loop_remove_watch(loop, cynara->cynara_watch);
248 _dbus_watch_invalidate(cynara->cynara_watch);
249 _dbus_watch_unref(cynara->cynara_watch);
250 cynara->cynara_watch = NULL;
260 case CYNARA_STATUS_FOR_READ:
261 flags = DBUS_WATCH_READABLE;
263 case CYNARA_STATUS_FOR_RW:
264 flags = DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE;
267 /* Cynara passed unknown status - warn and add RW watch */
268 _dbus_verbose("Cynara passed unknown status value: 0x%08X\n", (unsigned int)status);
269 flags = DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE;
273 watch = _dbus_watch_new(new_fd, flags, TRUE, &bus_cynara_watch_callback, cynara, NULL);
276 if (_dbus_loop_add_watch(loop, watch) == TRUE)
278 cynara->cynara_watch = watch;
282 _dbus_watch_invalidate(watch);
283 _dbus_watch_unref(watch);
286 /* It seems like not much can be done at this point. Cynara events won't be processed
287 * until next Cynara function call triggering status callback */
288 _dbus_verbose("Failed to add dbus watch\n");
293 bus_cynara_watch_callback(DBusWatch *watch,
297 BusCynara *cynara = (BusCynara *)data;
298 int result = cynara_async_process(cynara->cynara);
299 if (result != CYNARA_API_SUCCESS)
300 _dbus_verbose("cynara_async_process returned %d\n", result);
302 return result != CYNARA_API_OUT_OF_MEMORY ? TRUE : FALSE;
305 static inline const char *
306 call_cause_to_string(cynara_async_call_cause cause)
310 case CYNARA_CALL_CAUSE_ANSWER:
312 case CYNARA_CALL_CAUSE_CANCEL:
314 case CYNARA_CALL_CAUSE_FINISH:
316 case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
317 return "SERVICE NOT AVAILABLE";
324 bus_cynara_check_response_callback (cynara_check_id check_id,
325 cynara_async_call_cause cause,
327 void *user_response_data)
329 BusDeferredMessage *deferred_message = user_response_data;
332 _dbus_verbose("Cynara callback: check_id=%u, cause=%s response=%i response_data=%p\n",
333 (unsigned int)check_id, call_cause_to_string(cause), response, user_response_data);
335 if (deferred_message == NULL)
338 if (cause == CYNARA_CALL_CAUSE_ANSWER && response == CYNARA_API_ACCESS_ALLOWED)
339 result = BUS_RESULT_TRUE;
341 result = BUS_RESULT_FALSE;
343 bus_deferred_message_response_received(deferred_message, result);
344 bus_deferred_message_unref(deferred_message);
347 #endif /* DBUS_ENABLE_CYNARA */