Remove build warnings
[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 static dbus_bool_t bus_cynara_watch_callback(DBusWatch *watch,
53                                              unsigned int flags,
54                                              void *data);
55
56 static void status_callback(int old_fd,
57                             int new_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,
62                                                 int response,
63                                                 void *user_response_data);
64 #endif
65
66
67 BusCynara *
68 bus_cynara_new(BusCheck *check, DBusError *error)
69 {
70 #ifdef DBUS_ENABLE_CYNARA
71   BusContext *context;
72   BusCynara *cynara;
73   int ret;
74
75   cynara = dbus_new(BusCynara, 1);
76   if (cynara == NULL)
77     {
78       BUS_SET_OOM(error);
79       return NULL;
80     }
81
82   context = bus_check_get_context(check);
83
84   cynara->refcount = 1;
85   cynara->check = check;
86   cynara->context = context;
87   cynara->cynara_watch = NULL;
88
89   ret = cynara_async_initialize(&cynara->cynara, NULL, &status_callback, cynara);
90   if (ret != CYNARA_API_SUCCESS)
91     {
92       dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to initialize Cynara client");
93       dbus_free(cynara);
94       return NULL;
95     }
96
97   return cynara;
98 #else
99   return NULL;
100 #endif
101 }
102
103 BusCynara *
104 bus_cynara_ref (BusCynara *cynara)
105 {
106 #ifdef DBUS_ENABLE_CYNARA
107   _dbus_assert (cynara->refcount > 0);
108   cynara->refcount += 1;
109
110   return cynara;
111 #else
112   return NULL;
113 #endif
114 }
115
116 void
117 bus_cynara_unref (BusCynara *cynara)
118 {
119 #ifdef DBUS_ENABLE_CYNARA
120   _dbus_assert (cynara->refcount > 0);
121
122   cynara->refcount -= 1;
123
124   if (cynara->refcount == 0)
125     {
126       if (cynara->cynara != NULL)
127         cynara_async_finish(cynara->cynara);
128       dbus_free(cynara);
129     }
130 #endif
131 }
132
133 BusResult
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)
142 {
143 #ifdef DBUS_ENABLE_CYNARA
144   int result;
145   unsigned long uid;
146   const char *label;
147   const char *session_id;
148   char user[32];
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;
153
154   _dbus_assert(connection != NULL);
155
156   if (dbus_connection_get_unix_user(connection, &uid) == FALSE)
157       return BUS_RESULT_FALSE;
158
159   if (_dbus_connection_get_linux_security_label (connection, (char **)&label) == FALSE)
160       return BUS_RESULT_FALSE;
161
162   session_id = bus_connection_get_cynara_session_id (connection);
163   if (session_id == NULL)
164     {
165       dbus_free ((char*)label);
166       return BUS_RESULT_FALSE;
167     }
168
169   snprintf(user, sizeof(user), "%lu", uid);
170
171 #if USE_CYNARA_CACHE
172   result = cynara_async_check_cache(cynara->cynara, label, session_id, user, privilege);
173 #else
174   result = CYNARA_API_CACHE_MISS;
175 #endif
176
177   switch (result)
178   {
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;
183     break;
184
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;
189     break;
190
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)
195        {
196          _dbus_verbose("Failed to allocate memory for deferred message\n");
197          return_result = BUS_RESULT_FALSE;
198          break;
199        }
200
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)
205       {
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;
211         break;
212       }
213     else
214       {
215         _dbus_verbose("Error on cynara request create: %i\n", result);
216         bus_deferred_message_unref(deferred_message);
217         return_result = BUS_RESULT_FALSE;
218         break;
219       }
220     break;
221   default:
222     _dbus_verbose("Error when accessing Cynara cache: %i\n", result);
223     return_result = BUS_RESULT_FALSE;
224     break;
225   }
226
227   dbus_free ((char*)label);
228   return return_result;
229
230 #else
231   return BUS_RESULT_FALSE;
232 #endif
233 }
234
235
236
237 #ifdef DBUS_ENABLE_CYNARA
238 static void
239 status_callback(int old_fd, int new_fd, cynara_async_status status,
240                 void *user_status_data)
241 {
242   BusCynara *cynara = (BusCynara *)user_status_data;
243   DBusLoop *loop = bus_context_get_loop(cynara->context);
244
245   if (cynara->cynara_watch != NULL)
246     {
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;
251     }
252
253   if (new_fd != -1)
254     {
255       unsigned int flags;
256       DBusWatch *watch;
257
258       switch (status)
259       {
260       case CYNARA_STATUS_FOR_READ:
261         flags = DBUS_WATCH_READABLE;
262         break;
263       case CYNARA_STATUS_FOR_RW:
264         flags = DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE;
265         break;
266       default:
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;
270         break;
271       }
272
273       watch = _dbus_watch_new(new_fd, flags, TRUE, &bus_cynara_watch_callback, cynara, NULL);
274       if (watch != NULL)
275         {
276           if (_dbus_loop_add_watch(loop, watch) == TRUE)
277             {
278               cynara->cynara_watch = watch;
279               return;
280             }
281
282           _dbus_watch_invalidate(watch);
283           _dbus_watch_unref(watch);
284         }
285
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");
289     }
290 }
291
292 static dbus_bool_t
293 bus_cynara_watch_callback(DBusWatch    *watch,
294                           unsigned int  flags,
295                           void         *data)
296 {
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);
301
302   return result != CYNARA_API_OUT_OF_MEMORY ? TRUE : FALSE;
303 }
304
305 static inline const char *
306 call_cause_to_string(cynara_async_call_cause cause)
307 {
308   switch (cause)
309   {
310   case CYNARA_CALL_CAUSE_ANSWER:
311     return "ANSWER";
312   case CYNARA_CALL_CAUSE_CANCEL:
313     return "CANCEL";
314   case CYNARA_CALL_CAUSE_FINISH:
315     return "FINSIH";
316   case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
317     return "SERVICE NOT AVAILABLE";
318   default:
319     return "INVALID";
320   }
321 }
322
323 static void
324 bus_cynara_check_response_callback (cynara_check_id check_id,
325                                     cynara_async_call_cause cause,
326                                     int response,
327                                     void *user_response_data)
328 {
329   BusDeferredMessage *deferred_message = user_response_data;
330   BusResult result;
331
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);
334
335   if (deferred_message == NULL)
336     return;
337
338   if (cause == CYNARA_CALL_CAUSE_ANSWER && response == CYNARA_API_ACCESS_ALLOWED)
339     result = BUS_RESULT_TRUE;
340   else
341     result = BUS_RESULT_FALSE;
342
343   bus_deferred_message_response_received(deferred_message, result);
344   bus_deferred_message_unref(deferred_message);
345 }
346
347 #endif /* DBUS_ENABLE_CYNARA */