Clarify license declaration
[platform/upstream/dbus.git] / bus / cynara.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* cynara.c  Cynara runtime privilege checking
3  *
4  * Copyright (c) 2014 Samsung Electronics, Ltd.
5  *
6  * Licensed under the Academic Free License version 2.1
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 Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #include <config.h>
25 #include "cynara.h"
26 #include "check.h"
27 #include "utils.h"
28
29 #include <stdio.h>
30
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>
37 #endif
38
39 #define USE_CYNARA_CACHE 1
40
41 #ifdef DBUS_ENABLE_CYNARA
42 typedef struct BusCynara
43 {
44   int refcount;
45
46   BusContext   *context;
47   BusCheck     *check;
48   cynara_async *cynara;
49   DBusWatch    *cynara_watch;
50 } BusCynara;
51
52 typedef struct BusCynaraCheckData
53 {
54   BusDeferredMessage *deferred_message;
55   BusDeferredMessageStatus check_type;
56 } BusCynaraCheckData;
57
58 static dbus_bool_t bus_cynara_watch_callback(DBusWatch *watch,
59                                              unsigned int flags,
60                                              void *data);
61
62 static void status_callback(int old_fd,
63                             int new_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,
68                                                 int response,
69                                                 void *user_response_data);
70
71 static BusCynaraCheckData *
72 bus_cynara_check_data_new(BusDeferredMessage *deferred_message, BusDeferredMessageStatus check_type)
73 {
74   BusCynaraCheckData *data;
75
76   data = dbus_new(BusCynaraCheckData, 1);
77   if (data == NULL)
78     return NULL;
79
80   data->deferred_message = deferred_message;
81   data->check_type = check_type;
82
83   return data;
84 }
85 #endif
86
87 BusCynara *
88 bus_cynara_new(BusCheck *check, DBusError *error)
89 {
90 #ifdef DBUS_ENABLE_CYNARA
91   BusContext *context;
92   BusCynara *cynara;
93   int ret;
94
95   cynara = dbus_new(BusCynara, 1);
96   if (cynara == NULL)
97     {
98       BUS_SET_OOM(error);
99       return NULL;
100     }
101
102   context = bus_check_get_context(check);
103
104   cynara->refcount = 1;
105   cynara->check = check;
106   cynara->context = context;
107   cynara->cynara_watch = NULL;
108
109   ret = cynara_async_initialize(&cynara->cynara, NULL, &status_callback, cynara);
110   if (ret != CYNARA_API_SUCCESS)
111     {
112       dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to initialize Cynara client");
113       dbus_free(cynara);
114       return NULL;
115     }
116
117   return cynara;
118 #else
119   return NULL;
120 #endif
121 }
122
123 BusCynara *
124 bus_cynara_ref (BusCynara *cynara)
125 {
126 #ifdef DBUS_ENABLE_CYNARA
127   _dbus_assert (cynara->refcount > 0);
128   cynara->refcount += 1;
129
130   return cynara;
131 #else
132   return NULL;
133 #endif
134 }
135
136 void
137 bus_cynara_unref (BusCynara *cynara)
138 {
139 #ifdef DBUS_ENABLE_CYNARA
140   _dbus_assert (cynara->refcount > 0);
141
142   cynara->refcount -= 1;
143
144   if (cynara->refcount == 0)
145     {
146       if (cynara->cynara != NULL)
147         cynara_async_finish(cynara->cynara);
148       dbus_free(cynara);
149     }
150 #endif
151 }
152
153 BusResult
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)
162 {
163 #ifdef DBUS_ENABLE_CYNARA
164   int result;
165   unsigned long uid;
166   const char *label;
167   const char *session_id;
168   char user[32];
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;
174
175   _dbus_assert(connection != NULL);
176
177   if (deferred_message_param)
178     deferred_message = *deferred_message_param;
179
180   if (dbus_connection_get_unix_user(connection, &uid) == FALSE)
181       return BUS_RESULT_FALSE;
182
183   if (_dbus_connection_get_linux_security_label (connection, (char **)&label) == FALSE)
184       return BUS_RESULT_FALSE;
185
186   session_id = bus_connection_get_cynara_session_id (connection);
187   if (session_id == NULL)
188     {
189       dbus_free ((char*)label);
190       return BUS_RESULT_FALSE;
191     }
192
193   snprintf(user, sizeof(user), "%lu", uid);
194
195 #if USE_CYNARA_CACHE
196   result = cynara_async_check_cache(cynara->cynara, label, session_id, user, privilege);
197 #else
198   result = CYNARA_API_CACHE_MISS;
199 #endif
200
201   switch (result)
202   {
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;
207     break;
208
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;
213     break;
214
215   case CYNARA_API_CACHE_MISS:
216     if (deferred_message == NULL)
217       {
218          deferred_message = bus_deferred_message_new(message, sender, addressed_recipient,
219              proposed_recipient, BUS_RESULT_LATER);
220          if (deferred_message == NULL)
221            {
222              _dbus_verbose("Failed to allocate memory for deferred message\n");
223              return_result = BUS_RESULT_FALSE;
224              break;
225            }
226       }
227     else
228       {
229         bus_deferred_message_ref(deferred_message);
230       }
231
232     callback_data = bus_cynara_check_data_new(deferred_message, check_type);
233     if (callback_data == NULL)
234       {
235            _dbus_verbose("Failed to allocate memory for deferred message callback data\n");
236            return_result = BUS_RESULT_FALSE;
237       }
238
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)
243       {
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;
249         break;
250       }
251     else
252       {
253         _dbus_verbose("Error on cynara request create: %i\n", result);
254         bus_deferred_message_unref(deferred_message);
255         return_result = BUS_RESULT_FALSE;
256         break;
257       }
258     break;
259   default:
260     _dbus_verbose("Error when accessing Cynara cache: %i\n", result);
261     return_result = BUS_RESULT_FALSE;
262     break;
263   }
264
265   dbus_free ((char*)label);
266   return return_result;
267
268 #else
269   return BUS_RESULT_FALSE;
270 #endif
271 }
272
273
274
275 #ifdef DBUS_ENABLE_CYNARA
276 static void
277 status_callback(int old_fd, int new_fd, cynara_async_status status,
278                 void *user_status_data)
279 {
280   BusCynara *cynara = (BusCynara *)user_status_data;
281   DBusLoop *loop = bus_context_get_loop(cynara->context);
282
283   if (cynara->cynara_watch != NULL)
284     {
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;
289     }
290
291   if (new_fd != -1)
292     {
293       unsigned int flags;
294       DBusWatch *watch;
295
296       switch (status)
297       {
298       case CYNARA_STATUS_FOR_READ:
299         flags = DBUS_WATCH_READABLE;
300         break;
301       case CYNARA_STATUS_FOR_RW:
302         flags = DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE;
303         break;
304       default:
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;
308         break;
309       }
310
311       watch = _dbus_watch_new(new_fd, flags, TRUE, &bus_cynara_watch_callback, cynara, NULL);
312       if (watch != NULL)
313         {
314           if (_dbus_loop_add_watch(loop, watch) == TRUE)
315             {
316               cynara->cynara_watch = watch;
317               return;
318             }
319
320           _dbus_watch_invalidate(watch);
321           _dbus_watch_unref(watch);
322         }
323
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");
327     }
328 }
329
330 static dbus_bool_t
331 bus_cynara_watch_callback(DBusWatch    *watch,
332                           unsigned int  flags,
333                           void         *data)
334 {
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);
339
340   return result != CYNARA_API_OUT_OF_MEMORY ? TRUE : FALSE;
341 }
342
343 static inline const char *
344 call_cause_to_string(cynara_async_call_cause cause)
345 {
346   switch (cause)
347   {
348   case CYNARA_CALL_CAUSE_ANSWER:
349     return "ANSWER";
350   case CYNARA_CALL_CAUSE_CANCEL:
351     return "CANCEL";
352   case CYNARA_CALL_CAUSE_FINISH:
353     return "FINSIH";
354   case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
355     return "SERVICE NOT AVAILABLE";
356   default:
357     return "INVALID";
358   }
359 }
360
361 static void
362 bus_cynara_check_response_callback (cynara_check_id check_id,
363                                     cynara_async_call_cause cause,
364                                     int response,
365                                     void *user_response_data)
366 {
367   BusCynaraCheckData *check_data = user_response_data;
368   BusDeferredMessage *deferred_message;
369   BusResult result;
370   BusDeferredMessageStatus check_type;
371
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);
374
375   if (check_data == NULL)
376     return;
377
378   deferred_message = check_data->deferred_message;
379   check_type = check_data->check_type;
380
381   dbus_free(check_data);
382
383   if (deferred_message == NULL)
384     return;
385
386   if (cause == CYNARA_CALL_CAUSE_ANSWER && response == CYNARA_API_ACCESS_ALLOWED)
387     result = BUS_RESULT_TRUE;
388   else
389     result = BUS_RESULT_FALSE;
390
391   bus_deferred_message_response_received(deferred_message, check_type, result);
392   bus_deferred_message_unref(deferred_message);
393 }
394
395 #endif /* DBUS_ENABLE_CYNARA */