4 * Copyright (c) 2010 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include <sys/smack.h>
22 #include <pims-ipc-svc.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"
34 CTSVC_TYPE_PERMISSION,
35 CTSVC_TYPE_BOOK_READONLY,
41 } ctsvc_writable_info_s;
44 unsigned int thread_id;
47 GList *writable_list; /* ctsvc_writable_info_s */
48 } ctsvc_permission_info_s;
50 static GList *__thread_list = NULL;
52 static int have_smack = -1;
54 /* check SMACK enable or disable */
55 static int __ctsvc_have_smack(void)
57 if (-1 == have_smack) {
58 if (NULL == smack_smackfs_path())
66 /* this function is called in mutex lock */
67 static ctsvc_permission_info_s * __ctsvc_find_access_info(unsigned int thread_id)
71 DBG("thread id : %08x", thread_id);
73 for (cursor = __thread_list; cursor; cursor = cursor->next) {
74 ctsvc_permission_info_s *info = NULL;
76 if (info->thread_id == thread_id)
83 * Check the client has read permission of the file(path)
84 * success : CONTACTS_ERROR_NONE
85 * Fail: return negative value
87 int ctsvc_have_file_read_permission(const char *path)
90 RETV_IF(NULL == path, CONTACTS_ERROR_INVALID_PARAMETER);
92 if (0 != access(path, F_OK|R_OK)) {
94 ERR("access(%s) Fail(%d)", path, errno);
97 return CONTACTS_ERROR_PERMISSION_DENIED;
99 return CONTACTS_ERROR_SYSTEM;
104 return CONTACTS_ERROR_NONE;
107 static bool __ctsvc_is_allowed_client(const char *client_label, const char *addressbook_label)
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";
114 if (STRING_EQUAL == strncmp(client_label, preload_contacts, strlen(preload_contacts))
115 && STRING_EQUAL == strcmp(addressbook_label, device_addressbook)) /*preload contacts && device addressbook*/
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*/
124 /* this function is called in mutex lock */
125 static void __ctsvc_set_permission_info(ctsvc_permission_info_s *info)
130 char query[CTS_SQL_MAX_LEN] = {0};
131 bool smack_enabled = false;
133 if (__ctsvc_have_smack() == 1)
134 smack_enabled = true;
136 INFO("SAMCK disabled");
138 /* white listing : core module */
139 g_list_free_full(info->writable_list, free);
140 info->writable_list = NULL;
142 /* don't have write permission */
143 if (!ctsvc_have_permission(info->ipc, CTSVC_PERMISSION_CONTACT_WRITE))
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);
156 snprintf(query, sizeof(query),
157 "SELECT addressbook_id, mode, smack_label FROM "CTS_TABLE_ADDRESSBOOKS);
158 ret = ctsvc_query_prepare(query, &stmt);
160 /* LCOV_EXCL_START */
161 ERR("ctsvc_query_prepare() Fail(%d)", ret);
166 while ((ret = ctsvc_stmt_step(stmt))) {
172 /* LCOV_EXCL_START */
173 ERR("ctsvc_stmt_step() Fail(%d)", ret);
174 ctsvc_stmt_finalize(stmt);
179 id = ctsvc_stmt_get_int(stmt, 0);
180 mode = ctsvc_stmt_get_int(stmt, 1);
181 temp = ctsvc_stmt_get_text(stmt, 2);
183 ctsvc_writable_info_s *wi = calloc(1, sizeof(ctsvc_writable_info_s));
185 /* LCOV_EXCL_START */
186 ERR("calloc() Fail");
191 if (!smack_enabled) { /* smack disabled */
193 wi->type = CTSVC_TYPE_PERMISSION;
194 } else if (NULL == info->ipc) { /* contacts-service daemon */
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*/
200 wi->type = CTSVC_TYPE_PERMISSION;
201 } else if (CONTACTS_ADDRESS_BOOK_MODE_NONE == mode) {
203 wi->type = CTSVC_TYPE_PERMISSION;
204 } else if (CONTACTS_ADDRESS_BOOK_MODE_READONLY == mode) {
206 wi->type = CTSVC_TYPE_BOOK_READONLY;
208 DBG("Unable to check permission");
211 if (wi->type == CTSVC_TYPE_NONE)
214 info->writable_list = g_list_append(info->writable_list, wi);
216 ctsvc_stmt_finalize(stmt);
219 void ctsvc_unset_client_access_info(void)
221 ctsvc_permission_info_s *find = NULL;
223 ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
224 find = __ctsvc_find_access_info(pthread_self());
227 g_list_free_full(find->writable_list, free);
228 __thread_list = g_list_remove(__thread_list, find);
231 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
235 * release permission info resource when disconnecting client
236 * It is set as callback function using pims-ipc API
238 static void __ctsvc_client_disconnected_cb(pims_ipc_h ipc, void *user_data)
240 ctsvc_permission_info_s *info;
242 ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
243 info = (ctsvc_permission_info_s*)user_data;
245 INFO("Thread(0x%x), info(%p)", info->thread_id, info);
247 g_list_free_full(info->writable_list, free);
248 __thread_list = g_list_remove(__thread_list, info);
252 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
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()
259 ctsvc_contacts_internal_disconnect();
262 void ctsvc_set_client_access_info(pims_ipc_h ipc, const char *smack)
264 unsigned int thread_id;
265 ctsvc_permission_info_s *info = NULL;
267 ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
268 thread_id = (unsigned int)pthread_self();
269 info = __ctsvc_find_access_info(thread_id);
271 info = calloc(1, sizeof(ctsvc_permission_info_s));
273 /* LCOV_EXCL_START */
274 ERR("Thread(0x%x), calloc() Fail", thread_id);
275 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
279 __thread_list = g_list_append(__thread_list, info);
281 info->thread_id = thread_id;
284 FREEandSTRDUP(info->smack, smack);
285 WARN_IF(NULL == info->smack, "strdup() Fail");
286 __ctsvc_set_permission_info(info);
289 pims_ipc_svc_set_client_disconnected_cb(__ctsvc_client_disconnected_cb, info);
291 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
295 * Whenever changing addressbook this function will be called
296 * to reset read/write permssion info of each addressbook
298 void ctsvc_reset_all_client_access_info()
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;
306 __ctsvc_set_permission_info(info);
308 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
311 bool ctsvc_have_permission(pims_ipc_h ipc, int permission)
313 have_smack = __ctsvc_have_smack();
314 if (have_smack != 1) /* smack disable */
317 if (NULL == ipc) /* contacts-service daemon */
320 if ((CTSVC_PERMISSION_CONTACT_READ & permission) &&
321 !pims_ipc_svc_check_privilege(ipc, CTSVC_PRIVILEGE_CONTACT_READ))
324 if ((CTSVC_PERMISSION_CONTACT_WRITE & permission) &&
325 !pims_ipc_svc_check_privilege(ipc, CTSVC_PRIVILEGE_CONTACT_WRITE))
328 if ((CTSVC_PERMISSION_PHONELOG_READ & permission) &&
329 !pims_ipc_svc_check_privilege(ipc, CTSVC_PRIVILEGE_CALLHISTORY_READ))
332 if ((CTSVC_PERMISSION_PHONELOG_WRITE & permission) &&
333 !pims_ipc_svc_check_privilege(ipc, CTSVC_PRIVILEGE_CALLHISTORY_WRITE))
339 bool ctsvc_have_ab_write_permission(int addressbook_id, bool allow_readonly)
341 unsigned int thread_id;
342 ctsvc_permission_info_s *find = NULL;
344 have_smack = __ctsvc_have_smack();
345 if (have_smack != 1) /* smack disable */
348 ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
349 thread_id = (unsigned int)pthread_self();
350 find = __ctsvc_find_access_info(thread_id);
352 /* LCOV_EXCL_START */
353 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
354 ERR("can not found access info");
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");
367 GList *cursor = find->writable_list;
369 ctsvc_writable_info_s *wi = cursor->data;
371 cursor = g_list_next(cursor);
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;
380 cursor = g_list_next(cursor);
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);
390 int ctsvc_get_write_permitted_addressbook_ids(int **addressbook_ids, int *count)
392 unsigned int thread_id;
393 ctsvc_permission_info_s *find = NULL;
395 *addressbook_ids = NULL;
397 ctsvc_mutex_lock(CTS_MUTEX_ACCESS_CONTROL);
398 thread_id = (unsigned int)pthread_self();
399 find = __ctsvc_find_access_info(thread_id);
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;
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;
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;
426 GList *cursor = find->writable_list;
428 ctsvc_writable_info_s *wi = cursor->data;
430 cursor = g_list_next(cursor);
433 if (CTSVC_TYPE_BOOK_READONLY == wi->type) {
434 cursor = g_list_next(cursor);
437 book_ids[i] = wi->id;
440 cursor = g_list_next(cursor);
444 *addressbook_ids = book_ids;
446 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
447 return CONTACTS_ERROR_NONE;
450 char* ctsvc_get_client_smack_label(void)
452 ctsvc_permission_info_s *find = NULL;
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");
461 ctsvc_mutex_unlock(CTS_MUTEX_ACCESS_CONTROL);
465 int ctsvc_is_owner(int addressbook_id)
468 cts_stmt stmt = NULL;
469 char query[CTS_SQL_MAX_LEN] = {0};
471 char *saved_smack = NULL;
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);
478 /* LCOV_EXCL_START */
479 ERR("ctsvc_query_prepare() Fail(%d)", ret);
484 ret = ctsvc_stmt_step(stmt);
486 /* LCOV_EXCL_START */
487 ERR("ctsvc_stmt_step() Fail(%d)", ret);
488 ctsvc_stmt_finalize(stmt);
493 ret = CONTACTS_ERROR_PERMISSION_DENIED;
495 saved_smack = ctsvc_stmt_get_text(stmt, 1);
496 smack = ctsvc_get_client_smack_label();
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;
504 ctsvc_stmt_finalize(stmt);