Fix path to TA checking
[platform/core/security/tef-simulator.git] / simulatordaemon / src / SecurityContext.cpp
1 /**
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /**
18  * @file
19  * @author Uladzislau Harbuz (u.harbuz@samsung.com)
20  * @brief  SecurityContext class
21  */
22
23
24 #include "SecurityContext.h"
25 #include <cynara/cynara-creds-socket.h>
26 #include <cynara/cynara-session.h>
27 #include <security-manager/app-runtime.h>
28 #include <memory>
29 #include "log.h"
30 #include <boost/filesystem.hpp>
31 #include <pthread.h>
32 #include <stdexcept>
33 #include <tzplatform_config.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37
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*)>>;
41
42 namespace fs = boost::filesystem;
43
44 constexpr const char* SecurityContext::sysTaPaths[];
45
46 cynara* SecurityContext::_cynara = SecurityContext::initCynara();
47
48 pthread_mutex_t cynara_mutex = PTHREAD_MUTEX_INITIALIZER;
49
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;}
55
56
57 std::string SecurityContext::getCaFullPathFromPkgId(char* pkgid) {
58
59         std::string path;
60
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");
65                 return "";
66         }
67
68         p_tzplatform_context p_ctx(ctx, &tzplatform_context_destroy);
69
70         auto len = sizeof(struct ucred);
71         struct ucred ucred;
72
73         if (getsockopt(connFd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
74                 LOGE(SIM_DAEMON, "Can't get uid of client");
75                 return "";
76         }
77
78         auto clientUID = ucred.uid;
79
80         if (tzplatform_context_set_user(p_ctx.get(), clientUID) != 0) {
81                 LOGE(SIM_DAEMON, "Can not set user for context");
82                 return "";
83         }
84
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;
89         }
90
91         if (!fs::exists(path)) {
92                 LOGE(SIM_DAEMON, "Path doesn't exist: %s", path.c_str());
93                 return "";
94         }
95         if (fs::is_symlink(path)) {
96                 path = fs::read_symlink(path).string();
97         }
98         if (path.empty()) {
99                 LOGE(SIM_DAEMON, "Bad CA path. Does this directory exist: %s ?", path.c_str());
100         }
101
102         path += "/" + std::string(pkgid) + TA_LOCAL_PATH;
103         LOGD(SIM_DAEMON, "Path: %s", path.c_str());
104
105         return path;
106 }
107
108
109 bool SecurityContext::findRequestedTa(const std::string &ta_name, std::string &allowed_path) {
110         int ret;
111         char* pkg_id_ca;
112
113         LOGD(SIM_DAEMON, "Entry");
114         ret = security_manager_identify_app_from_socket(connFd, &pkg_id_ca, NULL);
115
116         if (ret == SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT) {
117                 LOGD(SIM_DAEMON, "Owner of socket has no pkgid");
118
119                 std::string ta_full_path;
120
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;
124
125                         if (fs::exists(ta_full_path)){
126                                 allowed_path = path;
127                                 return true;
128                         }
129                 }
130
131                 return false;
132         }
133
134         if (ret != SECURITY_MANAGER_SUCCESS) {
135                 LOGE(SIM_DAEMON, "security_manager_identify_app_from_socket failed with CA");
136                 return false;
137         }
138
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);
141
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");
145                 return false;
146         }
147
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();
155                         return true;
156                 }
157         }
158
159         return false;
160 }
161
162
163 bool SecurityContext::clientHasCynaraPermission(const std::string &privelege) {
164         int ret = -1;
165
166         pthread_mutex_lock(&cynara_mutex);
167
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);
173         }
174         p_char p_label(label, &free);
175
176         pid_t ca_pid = -1;
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);
181         }
182
183         char *session = nullptr;
184         session = cynara_session_from_pid(ca_pid);
185         if (!session) {
186                 LOGE(SIM_DAEMON, "Couldn't get client's cynara session.");
187                 RETURN_UNLOCK(false, cynara_mutex);
188         }
189         p_char p_session(session, &free);
190
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);
196         }
197         p_char p_user(user, &free);
198
199         ret = cynara_check(_cynara, p_label.get(), p_session.get(), p_user.get(), privelege.c_str());
200
201         if (ret == CYNARA_API_ACCESS_DENIED) {
202                 LOGE(SIM_DAEMON, "Cynara access denied.");
203                 RETURN_UNLOCK(false, cynara_mutex);
204         }
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);
208         }
209
210         RETURN_UNLOCK(true, cynara_mutex);
211 }
212
213
214 cynara* SecurityContext::initCynara() {
215         int ret = -1;
216         cynara_configuration *p_conf = nullptr;
217         cynara* result = nullptr;
218
219         ret = cynara_configuration_create(&p_conf);
220         if (ret != CYNARA_API_SUCCESS) {
221                 LOGE(SIM_DAEMON, "Cynara configuration creation failed");
222                 return nullptr;
223         }
224
225         p_cynara_conf conf(p_conf, &cynara_configuration_destroy);
226
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");
230                 return nullptr;
231         }
232
233         ret = cynara_initialize(&result, conf.get());
234         if (ret != CYNARA_API_SUCCESS) {
235                 LOGE(SIM_DAEMON, "Cynara initialize failed");
236                 return nullptr;
237         }
238
239         return result;
240 }
241
242
243 SecurityContext::SecurityContext():
244         SecurityContext(-1) {
245 }
246
247
248 SecurityContext::SecurityContext(int connFd):
249         connFd(connFd) {
250         if (_cynara == nullptr) throw std::runtime_error("Cynara is not initialized");
251 }
252
253
254 SecurityContext::~SecurityContext() {
255 }