Remove boost-log dependency
[platform/core/security/device-certificate-manager.git] / src / dcm-daemon / dcmsession.cpp
1 /******************************************************************
2  *
3  * Copyright 2017 - 2020 Samsung Electronics All Rights Reserved.
4  *
5  * Author: Jaroslaw Pelczar <j.pelczar@samsung.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ******************************************************************/
20
21 #include <iostream>
22 #include <cassert>
23 #include <map>
24 #include <mutex>
25
26 #include <cynara-client.h>
27 #include <cynara-creds-socket.h>
28 #include <cynara-session.h>
29
30 #include "dcmsession.h"
31 #include "log.h"
32 #include "exception_translator.h"
33 #include "dcmserver.h"
34
35 extern cynara * gGlobalCynaraInstance;
36
37 static inline std::string cynara_error_to_string(int error) {
38         char buffer[256];
39         int ret = cynara_strerror(error, buffer, sizeof(buffer));
40         if(ret == CYNARA_API_SUCCESS)
41                 return std::string(buffer);
42         return std::string("Can't translate error");
43 }
44
45 unsigned int globalSessionCounter = 0;
46
47 dcm_session::dcm_session(boost::asio::io_service& io_service,
48                 boost::asio::deadline_timer& timer,
49                 const std::shared_ptr<dcm_server>& server,
50                 std::shared_ptr<so_resolver> soResolver) :
51         fService(io_service),
52         fTimer(timer),
53         fSocket(io_service),
54         fServer(server),
55         fSoResolver(soResolver)
56 {
57         LOGD("Create new session object " << this);
58         globalSessionCounter++;
59 }
60
61 dcm_session::~dcm_session()
62 {
63         LOGD("Destroy session object " << this);
64 }
65
66 struct string_free_deleter {
67         void operator()(char * p) const {
68                 free(p);
69         }
70 };
71
72 bool dcm_session::verify_privileges(int handle)
73 {
74         int ret = 0;
75         char * tmp_str;
76         pid_t pid = 0;
77
78         std::unique_ptr<char, string_free_deleter> user;
79         std::unique_ptr<char, string_free_deleter> client;
80         std::unique_ptr<char, string_free_deleter> client_session;
81
82         /* Get user info */
83         tmp_str = nullptr;
84         ret = cynara_creds_socket_get_user(handle, USER_METHOD_DEFAULT, &tmp_str);
85         if(ret != CYNARA_API_SUCCESS) {
86                 LOGE("Can't get user from socket : " << ret << " - " << cynara_error_to_string(ret));
87                 return false;
88         }
89         user.reset(tmp_str);
90
91         /* Get client info */
92         tmp_str = nullptr;
93         ret = cynara_creds_socket_get_client(handle, CLIENT_METHOD_DEFAULT, &tmp_str);
94         if(ret != CYNARA_API_SUCCESS) {
95                 LOGE("Can't get client from socket : " << ret << " - " << cynara_error_to_string(ret));
96                 return false;
97         }
98         client.reset(tmp_str);
99
100
101         /* Get client PID from socket */
102         ret = cynara_creds_socket_get_pid(handle, &pid);
103         if(ret != CYNARA_API_SUCCESS) {
104                 LOGE("Can't get PID from socket : " << ret << " - " << cynara_error_to_string(ret));
105                 return false;
106         }
107
108         client_session.reset(cynara_session_from_pid(pid));
109         if(!client_session) {
110                 LOGE("Can't get session identifier from PID");
111                 return false;
112         }
113
114         LOGD("Got new session from " << pid << " with user " << user.get() <<
115                 ", client ID " << client.get() << " and session ID " << client_session.get());
116
117         ret = cynara_check(gGlobalCynaraInstance,
118                         client.get(),
119                         client_session.get(),
120                         user.get(),
121                         "http://tizen.org/privilege/devicecertificate");
122
123         if(ret != CYNARA_API_ACCESS_ALLOWED) {
124                 LOGE("Application access denied for " << pid << " - " << cynara_error_to_string(ret));
125                 return false;
126         }
127
128         LOGD("Access granted for " << pid);
129
130         return true;
131 }
132
133 void dcm_session::start()
134 {
135         int handle = fSocket.native_handle();
136         LOGD("Accepted connection with socket " << fSocket.native_handle());
137
138         if(verify_privileges(handle)) {
139                 stop_timer();
140                 do_receive();
141         } else {
142                 LOGE("Client privilege check failure. Disconnect");
143                 start_timer();
144         }
145 }
146
147 void dcm_session::start_timer()
148 {
149         globalSessionCounter--;
150         LOGD("Number of active connections: " << globalSessionCounter);
151
152         if(globalSessionCounter == 0) {
153                 LOGD("No active connections, server will be closed after few seconds");
154                 fTimer.expires_from_now(boost::posix_time::seconds(10));
155                 /* operation_aborted error is returned whenever we cancel the timer or we reset the timer using expires_from_now function */
156                 fTimer.async_wait([this](const boost::system::error_code &error) {
157                         if(error != boost::asio::error::operation_aborted) {
158                                 LOGD("No active connections, server will be closed");
159                                 fService.stop();
160                         }
161                 });
162         }
163 }
164
165 void dcm_session::stop_timer()
166 {
167         globalSessionCounter++;
168         LOGD("Number of active connections: " << globalSessionCounter);
169
170         fTimer.cancel();
171         LOGD("Timer cancelled");
172 }
173
174 void dcm_session::do_receive() noexcept
175 {
176         try {
177                 auto self(shared_from_this());
178
179                 LOGD("Read new message");
180
181                 fDeserializer.read_message(fSocket,
182                         [self, this](const boost::system::error_code& error, std::size_t bytes_read) {
183                                         if(!error) {
184                                                 LOGD("Received " << bytes_read << " bytes from client");
185                                                 decode_message();
186                                         } else {
187                                                 LOGE("Client disconnected: " << error);
188                                                 // Connection object will be released by shared ptr
189                                                 start_timer();
190                                         }
191                         });
192         } catch(std::exception& ex) {
193                 LOGE("Caught exception while trying to read message : " << ex.what());
194                 // Connection object will be released by shared ptr
195         } catch(...) {
196                 LOGE("Caught unknown exception while trying to read message");
197                 // Connection object will be released by shared ptr
198         }
199 }
200
201 void dcm_session::decode_message() noexcept
202 {
203         try {
204                 // Try to decode whole message
205                 RequestMessage requestMessage;
206
207                 if(!fDeserializer.decode_received_message(requestMessage)) {
208                         LOGE("Can't parse message from stream");
209                         // This will terminate connection
210                         return;
211                 }
212
213                 switch(requestMessage.request_oneof_case())
214                 {
215                 case RequestMessage::kAssociateContext:
216                         handle_context_association(requestMessage.associate_context());
217                         break;
218                 case RequestMessage::kRequestChain:
219                         handle_cert_chain(requestMessage.request_chain());
220                         break;
221                 case RequestMessage::kSignData:
222                         handle_sign_request(requestMessage.sign_data());
223                         break;
224                 default:
225                         LOGE("Incorrect request message type");
226                         // This will terminate connection
227                         return;
228                 }
229         } catch(std::exception& ex) {
230                 LOGE("Caught exception while parsing message : " << ex.what());
231                 // Connection object will be released by shared ptr
232         } catch(...) {
233                 LOGE("Caught unknown exception while parsing message");
234                 // Connection object will be released by shared ptr
235         }
236 }
237
238 void dcm_session::reply(const ResponseMessage& resp) noexcept
239 {
240         try {
241                 auto self(shared_from_this());
242
243                 fSerializer.encodeMessage(resp);
244
245                 fSerializer.async_write(fSocket,
246                                 [self, this](const boost::system::error_code& error, std::size_t bytes_written)
247                         {
248                                 if(!error) {
249                                         LOGD("Written " << bytes_written << " to socket");
250                                         do_receive();
251                                 } else {
252                                         LOGE("Server disconnected: " << error);
253                                         // Connection object will be released by shared ptr
254                                 }
255                         });
256         } catch(std::exception& ex) {
257                 LOGE("Caught exception while sending message : " << ex.what());
258                 // Connection object will be released by shared ptr
259         } catch(...) {
260                 LOGE("Caught unknown exception while sending message");
261                 // Connection object will be released by shared ptr
262         }
263 }
264
265 void dcm_session::handle_context_association(const AssociateKeyContext& message)
266 {
267         LOGD("Associate context");
268
269         ResponseMessage msg;
270         auto * contextResponse = msg.mutable_associate_context();
271
272         if(fBackendContext) {
273                 contextResponse->set_result(EEXIST);
274                 reply(msg);
275                 return;
276         }
277
278         LOGD("Associate context from service " << message.service() <<
279                 " with usage " << message.usage() << " and key type " << message.key_type());
280
281         auto server = fServer.lock();
282
283         if(!server) {
284                 LOGE("Server object gone while handling message");
285                 return;
286         }
287
288         int error = run_with_exception_handler([&]() {
289
290                 bool loaded = fSoResolver->ensure_loaded();
291                 if (loaded) {
292                         fBackendContext = std::unique_ptr<dcm_backend_context>(new dcm_backend_context);
293
294                         fSoResolver->invoke<void, dcm_backend_context&, const std::string&>(
295                                 "dcm_backend_create_key_context",
296                                 *fBackendContext,
297                                 message.key_type()
298                         );
299
300                         CryptoKeyType crypto_key_type = fSoResolver->invoke<CryptoKeyType, dcm_backend_context&>(
301                                 "dcm_backend_key_type",
302                                 *fBackendContext
303                         );
304                         contextResponse->set_key_type(crypto_key_type);
305
306                         unsigned int crypto_key_length = fSoResolver->invoke<unsigned int, dcm_backend_context&>(
307                                 "dcm_backend_key_length",
308                                 *fBackendContext
309                         );
310                         contextResponse->set_key_length(crypto_key_length);
311                 }
312                 else {
313                         LOGE("No usable backend available");
314                         throw std::invalid_argument("Unable to find backend");
315                 }
316                 fCookie = (uintptr_t)fBackendContext.get();
317                 contextResponse->set_context_cookie(fCookie);
318         });
319
320         contextResponse->set_result(error);
321
322         reply(msg);
323 }
324
325 static const std::string sPEMHeader("-----BEGIN CERTIFICATE-----\n");
326
327 void dcm_session::handle_cert_chain(const RequestCertificateChain& message)
328 {
329         ResponseMessage msg;
330         auto * certificateResponse = msg.mutable_request_chain();
331
332         LOGD("Request certificate chain");
333
334         if(message.context_cookie() != fCookie) {
335                 LOGE("Received unknown context cookie");
336                 certificateResponse->set_result(-EINVAL);
337                 reply(msg);
338                 return;
339         }
340
341         if(!fBackendContext) {
342                 LOGE("Context not associated with connection");
343                 certificateResponse->set_result(-EINVAL);
344                 reply(msg);
345                 return;
346         }
347
348         std::string cert_chain;
349
350         int error = 0;
351         bool loaded = fSoResolver->ensure_loaded();
352         if (loaded) {
353                 error = fSoResolver->invoke<int, dcm_backend_context&, std::string&>(
354                         "dcm_backend_request_certificate_chain",
355                         *fBackendContext,
356                         cert_chain
357                 );
358         }
359
360         if(error != 0) {
361                 certificateResponse->set_result(error);
362                 reply(msg);
363                 return;
364         }
365
366         if(cert_chain.length() >= sPEMHeader.length() &&
367                         !memcmp(sPEMHeader.c_str(), cert_chain.c_str(), sPEMHeader.size()) &&
368                         cert_chain[cert_chain.size() - 1] != '\0')
369         {
370                 // Add missing 0
371                 cert_chain.push_back(0);
372         }
373
374         *certificateResponse->mutable_cert_chain() = cert_chain;
375         certificateResponse->set_result(0);
376         reply(msg);
377 }
378
379 void dcm_session::handle_sign_request(const SignRequest& message)
380 {
381         LOGD("Request data signing");
382
383         ResponseMessage msg;
384         auto * signingResponse = msg.mutable_sign_data();
385
386         if(message.context_cookie() != fCookie) {
387                 LOGE("Received unknown context cookie");
388                 signingResponse->set_result(-EINVAL);
389                 reply(msg);
390                 return;
391         }
392
393         if(!fBackendContext) {
394                 LOGE("Context not associated with connection");
395                 signingResponse->set_result(-EINVAL);
396                 reply(msg);
397                 return;
398         }
399
400         if(message.data_to_sign().size() == 0) {
401                 LOGE("Data to sign is empty and hash type is NONE");
402                 signingResponse->set_result(-EINVAL);
403                 return;
404         }
405
406         int error = 0;
407         bool loaded = fSoResolver->ensure_loaded();
408         if (loaded) {
409                 error = fSoResolver->invoke<int, dcm_backend_context&, MessageDigestType, const std::string&, std::string&>(
410                         "dcm_backend_sign_crypto_data",
411                         *fBackendContext,
412                         message.digest_type(),
413                         message.data_to_sign(),
414                         *signingResponse->mutable_signature()
415                 );
416         }
417         signingResponse->set_result(error);
418
419         reply(msg);
420 }