2 * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 * @author Uladzislau Harbuz (u.harbuz@samsung.com)
20 * @brief SecurityContext class
24 #include "SecurityContext.h"
25 #include <cynara/cynara-creds-socket.h>
26 #include <cynara/cynara-session.h>
27 #include <security-manager/app-runtime.h>
30 #include <boost/filesystem.hpp>
33 #include <tzplatform_config.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
38 using p_char = std::unique_ptr<char, std::function<void(void*)>>;
39 using p_cynara_conf = std::unique_ptr<cynara_configuration, std::function<void(cynara_configuration*)>>;
40 using p_tzplatform_context = std::unique_ptr<tzplatform_context, std::function<void(tzplatform_context*)>>;
42 namespace fs = boost::filesystem;
44 constexpr const char* SecurityContext::sysTaPaths[];
46 cynara* SecurityContext::_cynara = SecurityContext::initCynara();
48 pthread_mutex_t cynara_mutex = PTHREAD_MUTEX_INITIALIZER;
50 #define FILE_WAS_FOUND 1
51 #define MAX_OPENED_FD 5
52 #define MAX_PATH_LENGTH 100
53 #define BOOST_FILESYSTEM_VERSION 3
54 #define RETURN_UNLOCK(ret, mtx) {pthread_mutex_unlock(&mtx); return ret;}
57 std::string SecurityContext::getCaFullPathFromPkgId(char* pkgid) {
61 tzplatform_variable ids[3] = {TZ_USER_APP, TZ_SYS_RW_APP, TZ_SYS_RO_APP};
62 tzplatform_context *ctx;
63 if (tzplatform_context_create(&ctx) != 0) {
64 LOGE(SIM_DAEMON, "Can't create tizen context");
68 p_tzplatform_context p_ctx(ctx, &tzplatform_context_destroy);
70 auto len = sizeof(struct ucred);
73 if (getsockopt(connFd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
74 LOGE(SIM_DAEMON, "Can't get uid of client");
78 auto clientUID = ucred.uid;
80 if (tzplatform_context_set_user(p_ctx.get(), clientUID) != 0) {
81 LOGE(SIM_DAEMON, "Can not set user for context");
85 for (auto &id : ids) {
86 path = std::move(tzplatform_context_getenv(p_ctx.get(), id));
87 LOGD(SIM_DAEMON, "Path is : %s", path.c_str());
88 if (!path.empty()) break;
91 if (!fs::exists(path)) {
92 LOGE(SIM_DAEMON, "Path doesn't exist: %s", path.c_str());
95 if (fs::is_symlink(path)) {
96 path = fs::read_symlink(path).string();
99 LOGE(SIM_DAEMON, "Bad CA path. Does this directory exist: %s ?", path.c_str());
102 path += "/" + std::string(pkgid) + TA_LOCAL_PATH;
103 LOGD(SIM_DAEMON, "Path: %s", path.c_str());
109 bool SecurityContext::findRequestedTa(const std::string &ta_name, std::string &allowed_path) {
113 LOGD(SIM_DAEMON, "Entry");
114 ret = security_manager_identify_app_from_socket(connFd, &pkg_id_ca, NULL);
116 if (ret == SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT) {
117 LOGD(SIM_DAEMON, "Owner of socket has no pkgid");
119 std::string ta_full_path;
121 /* Check if any of system ta directories contains our ta */
122 for (const std::string& path : sysTaPaths) {
123 ta_full_path = path + ta_name;
125 if (fs::exists(ta_full_path)){
134 if (ret != SECURITY_MANAGER_SUCCESS) {
135 LOGE(SIM_DAEMON, "security_manager_identify_app_from_socket failed with CA");
139 /* We can free it only if security_manager_identify_app_from_socket return success */
140 p_char p_pkg_id_ca(pkg_id_ca, &free);
142 std::string ca_pkg_path = getCaFullPathFromPkgId(pkg_id_ca);
143 if (!fs::exists(ca_pkg_path)) {
144 LOGE(SIM_DAEMON, "Error while loading client's path");
148 /* Now it searches client’s TA recursively in subdirectories of it. Maybe
149 * in future we will know exact predefined subdirectories or even
150 * map Client <-> allowed directories for it*/
151 for(fs::recursive_directory_iterator end, dir(ca_pkg_path); dir != end; ++dir){
152 std::string ta_full_path = dir->path().filename().string() + "/" + ta_name;
153 if(fs::exists(ta_full_path)){
154 allowed_path = dir->path().filename().string();
163 bool SecurityContext::clientHasCynaraPermission(const std::string &privelege) {
166 pthread_mutex_lock(&cynara_mutex);
168 char *label = nullptr;
169 ret = cynara_creds_socket_get_client(connFd, CLIENT_METHOD_SMACK, &label);
170 if (ret != CYNARA_API_SUCCESS) {
171 LOGE(SIM_DAEMON, "Couldn't get smack label of the client. Error code: %d", ret);
172 RETURN_UNLOCK(false, cynara_mutex);
174 p_char p_label(label, &free);
177 ret = cynara_creds_socket_get_pid(connFd, &ca_pid);
178 if (ret != CYNARA_API_SUCCESS) {
179 LOGE(SIM_DAEMON, "Couldn't get pid of the client. Error code: %d", ret);
180 RETURN_UNLOCK(false, cynara_mutex);
183 char *session = nullptr;
184 session = cynara_session_from_pid(ca_pid);
186 LOGE(SIM_DAEMON, "Couldn't get client's cynara session.");
187 RETURN_UNLOCK(false, cynara_mutex);
189 p_char p_session(session, &free);
191 char *user = nullptr;
192 ret = cynara_creds_socket_get_user(connFd, USER_METHOD_DEFAULT, &user);
193 if (ret != CYNARA_API_SUCCESS) {
194 LOGE(SIM_DAEMON, "Couldn't get user. Error code: %d", ret);
195 RETURN_UNLOCK(false, cynara_mutex);
197 p_char p_user(user, &free);
199 ret = cynara_check(_cynara, p_label.get(), p_session.get(), p_user.get(), privelege.c_str());
201 if (ret == CYNARA_API_ACCESS_DENIED) {
202 LOGE(SIM_DAEMON, "Cynara access denied.");
203 RETURN_UNLOCK(false, cynara_mutex);
205 if (ret != CYNARA_API_ACCESS_ALLOWED) {
206 LOGE(SIM_DAEMON, "Error during cynara_check(). Error code: %d", ret);
207 RETURN_UNLOCK(false, cynara_mutex);
210 RETURN_UNLOCK(true, cynara_mutex);
214 cynara* SecurityContext::initCynara() {
216 cynara_configuration *p_conf = nullptr;
217 cynara* result = nullptr;
219 ret = cynara_configuration_create(&p_conf);
220 if (ret != CYNARA_API_SUCCESS) {
221 LOGE(SIM_DAEMON, "Cynara configuration creation failed");
225 p_cynara_conf conf(p_conf, &cynara_configuration_destroy);
227 ret = cynara_configuration_set_cache_size(p_conf, CYNARA_CACHE_SIZE);
228 if (ret != CYNARA_API_SUCCESS) {
229 LOGE(SIM_DAEMON, "Set cynara cache size failed");
233 ret = cynara_initialize(&result, conf.get());
234 if (ret != CYNARA_API_SUCCESS) {
235 LOGE(SIM_DAEMON, "Cynara initialize failed");
243 SecurityContext::SecurityContext():
244 SecurityContext(-1) {
248 SecurityContext::SecurityContext(int connFd):
250 if (_cynara == nullptr) throw std::runtime_error("Cynara is not initialized");
254 SecurityContext::~SecurityContext() {