[SVACE Issue Fixes]
[platform/core/pim/contacts-service.git] / server / db / ctsvc_db_access_control.c
1 /*
2  * Contacts Service
3  *
4  * Copyright (c) 2010 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19 #include <unistd.h>
20 #include <pthread.h>
21 #include <sys/smack.h>
22 #include <pims-ipc-svc.h>
23
24 #include "contacts.h"
25 #include "ctsvc_internal.h"
26 #include "ctsvc_db_schema.h"
27 #include "ctsvc_db_sqlite.h"
28 #include "ctsvc_db_access_control.h"
29 #include "ctsvc_mutex.h"
30 #include "ctsvc_server_service.h"
31
32 enum {
33         CTSVC_TYPE_NONE,
34         CTSVC_TYPE_PERMISSION,
35         CTSVC_TYPE_BOOK_READONLY,
36 };
37
38 typedef struct {
39         int id;
40         int type;
41 } ctsvc_writable_info_s;
42
43 typedef struct {
44         unsigned int thread_id;
45         pims_ipc_h ipc;
46         char *smack;
47         GList *writable_list; /* ctsvc_writable_info_s */
48 } ctsvc_permission_info_s;
49
50 static GList *__thread_list = NULL;
51
52 static int have_smack = -1;
53
54 /* check SMACK enable or disable */
55 static int __ctsvc_have_smack(void)
56 {
57         if (-1 == have_smack) {
58                 if (NULL == smack_smackfs_path())
59                         have_smack = 0;
60                 else
61                         have_smack = 1;
62         }
63         return have_smack;
64 }
65
66 /* this function is called in mutex lock */
67 static ctsvc_permission_info_s * __ctsvc_find_access_info(unsigned int thread_id)
68 {
69         GList *cursor;
70
71         DBG("thread id : %08x", thread_id);
72
73         for (cursor = __thread_list; cursor; cursor = cursor->next) {
74                 ctsvc_permission_info_s *info = NULL;
75                 info = cursor->data;
76                 if (info->thread_id == thread_id)
77                         return info;
78         }
79         return NULL;
80 }
81
82 /*
83  * Check the client has read permission of the file(path)
84  * success : CONTACTS_ERROR_NONE
85  * Fail: return negative value
86  */
87 int ctsvc_have_file_read_permission(const char *path)
88 {
89         CTS_FN_CALL;
90         RETV_IF(NULL == path, CONTACTS_ERROR_INVALID_PARAMETER);
91
92         if (0 != access(path, F_OK|R_OK)) {
93                 /* LCOV_EXCL_START */
94                 ERR("access(%s) Fail(%d)", path, errno);
95                 switch (errno) {
96                 case EACCES:
97                         return CONTACTS_ERROR_PERMISSION_DENIED;
98                 default:
99                         return CONTACTS_ERROR_SYSTEM;
100                 }
101                 /* LCOV_EXCL_STOP */
102         }
103
104         return CONTACTS_ERROR_NONE;
105 }
106
107 static bool __ctsvc_is_allowed_client(const char *client_label, const char *addressbook_label)
108 {
109         const char *device_addressbook = "org.tizen.contacts";
110         const char *preload_contacts = "User::Pkg::org.tizen.contacts";
111         const char *user_daemon = "User";
112         const char *system_daemon = "System";
113
114         if (STRING_EQUAL == strncmp(client_label, preload_contacts, strlen(preload_contacts))
115                 && STRING_EQUAL == strcmp(addressbook_label, device_addressbook)) /*preload contacts && device addressbook*/
116                 return true;
117         else if ((STRING_EQUAL == strcmp(client_label, user_daemon) || STRING_EQUAL == strcmp(client_label, system_daemon))
118                 && STRING_EQUAL != strcmp(addressbook_label, device_addressbook)) /*daemon && !device addressbook*/
119                 return true;
120
121         return false;
122 }
123
124 /* this function is called in mutex lock */
125 static void __ctsvc_set_permission_info(ctsvc_permission_info_s *info)
126 {
127         int ret;
128         int count;
129         cts_stmt stmt;
130         char query[CTS_SQL_MAX_LEN] = {0};
131         bool smack_enabled = false;
132
133         if (__ctsvc_have_smack() == 1)
134                 smack_enabled = true;
135         else
136                 INFO("SAMCK disabled");
137
138         /* white listing : core module */
139         g_list_free_full(info->writable_list, free);
140         info->writable_list = NULL;
141
142         /* don't have write permission */
143         if (!ctsvc_have_permission(info->ipc, CTSVC_PERMISSION_CONTACT_WRITE))
144                 return;
145
146         snprintf(query, sizeof(query),
147                         "SELECT count(addressbook_id) FROM "CTS_TABLE_ADDRESSBOOKS);
148         ret = ctsvc_query_get_first_int_result(query, &count);
149         if (CONTACTS_ERROR_NONE != ret) {
150                 /* LCOV_EXCL_START */
151                 ERR(" ctsvc_query_get_first_int_result() Fail(%d)", ret);
152                 return;
153                 /* LCOV_EXCL_STOP */
154         }
155
156         snprintf(query, sizeof(query),
157                         "SELECT addressbook_id, mode, smack_label FROM "CTS_TABLE_ADDRESSBOOKS);
158         ret = ctsvc_query_prepare(query, &stmt);
159         if (NULL == stmt) {
160                 /* LCOV_EXCL_START */
161                 ERR("ctsvc_query_prepare() Fail(%d)", ret);
162                 return;
163                 /* LCOV_EXCL_STOP */
164         }
165
166         while ((ret = ctsvc_stmt_step(stmt))) {
167                 int id;
168                 int mode;
169                 char *temp = NULL;
170
171                 if (1 != ret) {
172                         /* LCOV_EXCL_START */
173                         ERR("ctsvc_stmt_step() Fail(%d)", ret);
174                         ctsvc_stmt_finalize(stmt);
175                         return;
176                         /* LCOV_EXCL_STOP */
177                 }
178
179                 id = ctsvc_stmt_get_int(stmt, 0);
180                 mode = ctsvc_stmt_get_int(stmt, 1);
181                 temp = ctsvc_stmt_get_text(stmt, 2);
182
183                 ctsvc_writable_info_s *wi = calloc(1, sizeof(ctsvc_writable_info_s));
184                 if (NULL == wi) {
185                         /* LCOV_EXCL_START */
186                         ERR("calloc() Fail");
187                         break;
188                         /* LCOV_EXCL_STOP */
189                 }
190
191                 if (!smack_enabled) { /* smack disabled */
192                         wi->id = id;
193                         wi->type = CTSVC_TYPE_PERMISSION;
194                 } else if (NULL == info->ipc) { /* contacts-service daemon */
195                         wi->id = id;
196                         wi->type = CTSVC_TYPE_PERMISSION;
197                 } else if (info->smack && temp &&
198                         ((STRING_EQUAL == strcmp(temp, info->smack)) || __ctsvc_is_allowed_client(info->smack, temp))) { /* owner || allow client*/
199                         wi->id = id;
200                         wi->type = CTSVC_TYPE_PERMISSION;
201                 } else if (CONTACTS_ADDRESS_BOOK_MODE_NONE == mode) {
202                         wi->id = id;
203                         wi->type = CTSVC_TYPE_PERMISSION;
204                 } else if (CONTACTS_ADDRESS_BOOK_MODE_READONLY == mode) {
205                         wi->id = id;
206                         wi->type = CTSVC_TYPE_BOOK_READONLY;
207                 } else {
208                         DBG("Unable to check permission");
209                 }
210
211                 if (wi->type == CTSVC_TYPE_NONE)
212                         free(wi);
213                 else
214                         info->writable_list = g_list_append(info->writable_list, wi);
215         }
216         ctsvc_stmt_finalize(stmt);
217 }
218
219 void ctsvc_unset_client_access_info(void)
220 {
221         ctsvc_permission_info_s *find = NULL;
222
223         ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
224         find = __ctsvc_find_access_info(pthread_self());
225         if (find) {
226                 free(find->smack);
227                 g_list_free_full(find->writable_list, free);
228                 __thread_list = g_list_remove(__thread_list, find);
229                 free(find);
230         }
231         ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
232 }
233
234 /*
235  * release permission info resource when disconnecting client
236  * It is set as callback function using pims-ipc API
237  */
238 static void __ctsvc_client_disconnected_cb(pims_ipc_h ipc, void *user_data)
239 {
240         ctsvc_permission_info_s *info;
241
242         ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
243         info = (ctsvc_permission_info_s*)user_data;
244         if (info) {
245                 INFO("Thread(0x%x), info(%p)", info->thread_id, info);
246                 free(info->smack);
247                 g_list_free_full(info->writable_list, free);
248                 __thread_list = g_list_remove(__thread_list, info);
249                 free(info);
250         }
251
252         ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
253
254         /*
255          * if client did not call disconnect function
256          * such as contacts_disconnect, contacts_disconnect_on_thread
257          * DB will be closed in ctsvc_contacts_internal_disconnect()
258          */
259         ctsvc_contacts_internal_disconnect();
260 }
261
262 void ctsvc_set_client_access_info(pims_ipc_h ipc, const char *smack)
263 {
264         unsigned int thread_id;
265         ctsvc_permission_info_s *info = NULL;
266
267         ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
268         thread_id = (unsigned int)pthread_self();
269         info = __ctsvc_find_access_info(thread_id);
270         if (NULL == info) {
271                 info = calloc(1, sizeof(ctsvc_permission_info_s));
272                 if (NULL == info) {
273                         /* LCOV_EXCL_START */
274                         ERR("Thread(0x%x), calloc() Fail", thread_id);
275                         ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
276                         return;
277                         /* LCOV_EXCL_STOP */
278                 }
279                 __thread_list  = g_list_append(__thread_list, info);
280         }
281         info->thread_id = thread_id;
282         info->ipc = ipc;
283
284         FREEandSTRDUP(info->smack, smack);
285         WARN_IF(NULL == info->smack, "strdup() Fail");
286         __ctsvc_set_permission_info(info);
287
288         if (info->ipc)
289                 pims_ipc_svc_set_client_disconnected_cb(__ctsvc_client_disconnected_cb, info);
290
291         ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
292 }
293
294 /*
295  * Whenever changing addressbook this function will be called
296  * to reset read/write permssion info of each addressbook
297  */
298 void ctsvc_reset_all_client_access_info()
299 {
300         GList *cursor;
301         ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
302         for (cursor = __thread_list; cursor; cursor = cursor->next) {
303                 ctsvc_permission_info_s *info = cursor->data;
304                 if (info == NULL)
305                         continue;
306                 __ctsvc_set_permission_info(info);
307         }
308         ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
309 }
310
311 bool ctsvc_have_permission(pims_ipc_h ipc, int permission)
312 {
313         have_smack = __ctsvc_have_smack();
314         if (have_smack != 1)   /* smack disable */
315                 return true;
316
317         if (NULL == ipc) /* contacts-service daemon */
318                 return true;
319
320         if ((CTSVC_PERMISSION_CONTACT_READ & permission) &&
321                         !pims_ipc_svc_check_privilege(ipc, CTSVC_PRIVILEGE_CONTACT_READ))
322                 return false;
323
324         if ((CTSVC_PERMISSION_CONTACT_WRITE & permission) &&
325                         !pims_ipc_svc_check_privilege(ipc, CTSVC_PRIVILEGE_CONTACT_WRITE))
326                 return false;
327
328         if ((CTSVC_PERMISSION_PHONELOG_READ & permission) &&
329                         !pims_ipc_svc_check_privilege(ipc, CTSVC_PRIVILEGE_CALLHISTORY_READ))
330                 return false;
331
332         if ((CTSVC_PERMISSION_PHONELOG_WRITE & permission) &&
333                         !pims_ipc_svc_check_privilege(ipc, CTSVC_PRIVILEGE_CALLHISTORY_WRITE))
334                 return false;
335
336         return true;
337 }
338
339 bool ctsvc_have_ab_write_permission(int addressbook_id, bool allow_readonly)
340 {
341         unsigned int thread_id;
342         ctsvc_permission_info_s *find = NULL;
343
344         have_smack = __ctsvc_have_smack();
345         if (have_smack != 1)   /* smack disable */
346                 return true;
347
348         ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
349         thread_id = (unsigned int)pthread_self();
350         find = __ctsvc_find_access_info(thread_id);
351         if (NULL == find) {
352                 /* LCOV_EXCL_START */
353                 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
354                 ERR("can not found access info");
355                 return false;
356                 /* LCOV_EXCL_STOP */
357         }
358
359         if (NULL == find->writable_list) {
360                 /* LCOV_EXCL_START */
361                 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
362                 ERR("there is no write access info");
363                 return false;
364                 /* LCOV_EXCL_STOP */
365         }
366
367         GList *cursor = find->writable_list;
368         while (cursor) {
369                 ctsvc_writable_info_s *wi = cursor->data;
370                 if (NULL == wi) {
371                         cursor = g_list_next(cursor);
372                         continue;
373                 }
374                 if (addressbook_id == wi->id) {
375                         ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
376                         if (CTSVC_TYPE_BOOK_READONLY == wi->type)
377                                 return allow_readonly;
378                         return true;
379                 }
380                 cursor = g_list_next(cursor);
381         }
382
383         /* LCOV_EXCL_START */
384         ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
385         ERR("Thread(0x%x), Does not have write permission of addressbook(%d)", thread_id, addressbook_id);
386         return false;
387         /* LCOV_EXCL_STOP */
388 }
389
390 int ctsvc_get_write_permitted_addressbook_ids(int **addressbook_ids, int *count)
391 {
392         unsigned int thread_id;
393         ctsvc_permission_info_s *find = NULL;
394         *count = 0;
395         *addressbook_ids = NULL;
396
397         ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
398         thread_id = (unsigned int)pthread_self();
399         find = __ctsvc_find_access_info(thread_id);
400         if (NULL == find) {
401                 /* LCOV_EXCL_START */
402                 ERR("__ctsvc_find_access_info() Fail");
403                 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
404                 return CONTACTS_ERROR_PERMISSION_DENIED;
405                 /* LCOV_EXCL_STOP */
406         }
407
408         if (NULL == find->writable_list) {
409                 /* LCOV_EXCL_START */
410                 ERR("No permission info");
411                 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
412                 return CONTACTS_ERROR_PERMISSION_DENIED;
413                 /* LCOV_EXCL_STOP */
414         }
415
416         int book_count = g_list_length(find->writable_list);
417         int *book_ids = calloc(book_count, sizeof(int));
418         if (NULL == book_ids) {
419                 /* LCOV_EXCL_START */
420                 ERR("Thread(0x%x), calloc() Fail", thread_id);
421                 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
422                 return CONTACTS_ERROR_OUT_OF_MEMORY;
423                 /* LCOV_EXCL_STOP */
424         }
425         int i = 0;
426         GList *cursor = find->writable_list;
427         while (cursor) {
428                 ctsvc_writable_info_s *wi = cursor->data;
429                 if (NULL == wi) {
430                         cursor = g_list_next(cursor);
431                         continue;
432                 }
433                 if (CTSVC_TYPE_BOOK_READONLY == wi->type) {
434                         cursor = g_list_next(cursor);
435                         continue;
436                 }
437                 book_ids[i] = wi->id;
438                 i++;
439
440                 cursor = g_list_next(cursor);
441         }
442
443         *count = i;
444         *addressbook_ids = book_ids;
445
446         ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
447         return CONTACTS_ERROR_NONE;
448 }
449
450 char* ctsvc_get_client_smack_label(void)
451 {
452         ctsvc_permission_info_s *find = NULL;
453         char *smack = NULL;
454
455         ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
456         find = __ctsvc_find_access_info(pthread_self());
457         if (find && find->smack) {
458                 smack = strdup(find->smack);
459                 WARN_IF(NULL == smack, "strdup() Fail");
460         }
461         ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
462         return smack;
463 }
464
465 int ctsvc_is_owner(int addressbook_id)
466 {
467         int ret;
468         cts_stmt stmt = NULL;
469         char query[CTS_SQL_MAX_LEN] = {0};
470         char *smack = NULL;
471         char *saved_smack = NULL;
472
473         snprintf(query, sizeof(query),
474                         "SELECT addressbook_name, smack_label FROM "CTS_TABLE_ADDRESSBOOKS" "
475                         "WHERE addressbook_id = %d", addressbook_id);
476         ret = ctsvc_query_prepare(query, &stmt);
477         if (NULL == stmt) {
478                 /* LCOV_EXCL_START */
479                 ERR("ctsvc_query_prepare() Fail(%d)", ret);
480                 return ret;
481                 /* LCOV_EXCL_STOP */
482         }
483
484         ret = ctsvc_stmt_step(stmt);
485         if (1 != ret) {
486                 /* LCOV_EXCL_START */
487                 ERR("ctsvc_stmt_step() Fail(%d)", ret);
488                 ctsvc_stmt_finalize(stmt);
489                 return ret;
490                 /* LCOV_EXCL_STOP */
491         }
492
493         ret = CONTACTS_ERROR_PERMISSION_DENIED;
494
495         saved_smack = ctsvc_stmt_get_text(stmt, 1);
496         smack = ctsvc_get_client_smack_label();
497
498         if (smack && saved_smack) {
499                 if (STRING_EQUAL == strcmp(smack, saved_smack)) /*owner*/
500                         ret = CONTACTS_ERROR_NONE;
501                 else if (__ctsvc_is_allowed_client(smack, saved_smack))
502                         ret = CONTACTS_ERROR_NONE;
503         }
504         ctsvc_stmt_finalize(stmt);
505
506         free(smack);
507         return ret;
508 }
509