1 /******************************************************************
3 * Copyright 2017 - 2020 Samsung Electronics All Rights Reserved.
5 * Author: Jaroslaw Pelczar <j.pelczar@samsung.com>
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 ******************************************************************/
26 #include <cynara-client.h>
27 #include <cynara-creds-socket.h>
28 #include <cynara-session.h>
30 #include "dcmsession.h"
32 #include "exception_translator.h"
33 #include "dcmserver.h"
35 extern cynara * gGlobalCynaraInstance;
37 static inline std::string cynara_error_to_string(int error) {
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");
45 unsigned int globalSessionCounter = 0;
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) :
55 fSoResolver(soResolver)
57 LOGD("Create new session object " << this);
58 globalSessionCounter++;
61 dcm_session::~dcm_session()
63 LOGD("Destroy session object " << this);
66 struct string_free_deleter {
67 void operator()(char * p) const {
72 bool dcm_session::verify_privileges(int handle)
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;
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));
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));
98 client.reset(tmp_str);
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));
108 client_session.reset(cynara_session_from_pid(pid));
109 if(!client_session) {
110 LOGE("Can't get session identifier from PID");
114 LOGD("Got new session from " << pid << " with user " << user.get() <<
115 ", client ID " << client.get() << " and session ID " << client_session.get());
117 ret = cynara_check(gGlobalCynaraInstance,
119 client_session.get(),
121 "http://tizen.org/privilege/devicecertificate");
123 if(ret != CYNARA_API_ACCESS_ALLOWED) {
124 LOGE("Application access denied for " << pid << " - " << cynara_error_to_string(ret));
128 LOGD("Access granted for " << pid);
133 void dcm_session::start()
135 int handle = fSocket.native_handle();
136 LOGD("Accepted connection with socket " << fSocket.native_handle());
138 if(verify_privileges(handle)) {
142 LOGE("Client privilege check failure. Disconnect");
147 void dcm_session::start_timer()
149 globalSessionCounter--;
150 LOGD("Number of active connections: " << globalSessionCounter);
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");
165 void dcm_session::stop_timer()
167 globalSessionCounter++;
168 LOGD("Number of active connections: " << globalSessionCounter);
171 LOGD("Timer cancelled");
174 void dcm_session::do_receive() noexcept
177 auto self(shared_from_this());
179 LOGD("Read new message");
181 fDeserializer.read_message(fSocket,
182 [self, this](const boost::system::error_code& error, std::size_t bytes_read) {
184 LOGD("Received " << bytes_read << " bytes from client");
187 LOGE("Client disconnected: " << error);
188 // Connection object will be released by shared ptr
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
196 LOGE("Caught unknown exception while trying to read message");
197 // Connection object will be released by shared ptr
201 void dcm_session::decode_message() noexcept
204 // Try to decode whole message
205 RequestMessage requestMessage;
207 if(!fDeserializer.decode_received_message(requestMessage)) {
208 LOGE("Can't parse message from stream");
209 // This will terminate connection
213 switch(requestMessage.request_oneof_case())
215 case RequestMessage::kAssociateContext:
216 handle_context_association(requestMessage.associate_context());
218 case RequestMessage::kRequestChain:
219 handle_cert_chain(requestMessage.request_chain());
221 case RequestMessage::kSignData:
222 handle_sign_request(requestMessage.sign_data());
225 LOGE("Incorrect request message type");
226 // This will terminate connection
229 } catch(std::exception& ex) {
230 LOGE("Caught exception while parsing message : " << ex.what());
231 // Connection object will be released by shared ptr
233 LOGE("Caught unknown exception while parsing message");
234 // Connection object will be released by shared ptr
238 void dcm_session::reply(const ResponseMessage& resp) noexcept
241 auto self(shared_from_this());
243 fSerializer.encodeMessage(resp);
245 fSerializer.async_write(fSocket,
246 [self, this](const boost::system::error_code& error, std::size_t bytes_written)
249 LOGD("Written " << bytes_written << " to socket");
252 LOGE("Server disconnected: " << error);
253 // Connection object will be released by shared ptr
256 } catch(std::exception& ex) {
257 LOGE("Caught exception while sending message : " << ex.what());
258 // Connection object will be released by shared ptr
260 LOGE("Caught unknown exception while sending message");
261 // Connection object will be released by shared ptr
265 void dcm_session::handle_context_association(const AssociateKeyContext& message)
267 LOGD("Associate context");
270 auto * contextResponse = msg.mutable_associate_context();
272 if(fBackendContext) {
273 contextResponse->set_result(EEXIST);
278 LOGD("Associate context from service " << message.service() <<
279 " with usage " << message.usage() << " and key type " << message.key_type());
281 auto server = fServer.lock();
284 LOGE("Server object gone while handling message");
288 int error = run_with_exception_handler([&]() {
290 bool loaded = fSoResolver->ensure_loaded();
292 fBackendContext = std::unique_ptr<dcm_backend_context>(new dcm_backend_context);
294 fSoResolver->invoke<void, dcm_backend_context&, const std::string&>(
295 "dcm_backend_create_key_context",
300 CryptoKeyType crypto_key_type = fSoResolver->invoke<CryptoKeyType, dcm_backend_context&>(
301 "dcm_backend_key_type",
304 contextResponse->set_key_type(crypto_key_type);
306 unsigned int crypto_key_length = fSoResolver->invoke<unsigned int, dcm_backend_context&>(
307 "dcm_backend_key_length",
310 contextResponse->set_key_length(crypto_key_length);
313 LOGE("No usable backend available");
314 throw std::invalid_argument("Unable to find backend");
316 fCookie = (uintptr_t)fBackendContext.get();
317 contextResponse->set_context_cookie(fCookie);
320 contextResponse->set_result(error);
325 static const std::string sPEMHeader("-----BEGIN CERTIFICATE-----\n");
327 void dcm_session::handle_cert_chain(const RequestCertificateChain& message)
330 auto * certificateResponse = msg.mutable_request_chain();
332 LOGD("Request certificate chain");
334 if(message.context_cookie() != fCookie) {
335 LOGE("Received unknown context cookie");
336 certificateResponse->set_result(-EINVAL);
341 if(!fBackendContext) {
342 LOGE("Context not associated with connection");
343 certificateResponse->set_result(-EINVAL);
348 std::string cert_chain;
351 bool loaded = fSoResolver->ensure_loaded();
353 error = fSoResolver->invoke<int, dcm_backend_context&, std::string&>(
354 "dcm_backend_request_certificate_chain",
361 certificateResponse->set_result(error);
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')
371 cert_chain.push_back(0);
374 *certificateResponse->mutable_cert_chain() = cert_chain;
375 certificateResponse->set_result(0);
379 void dcm_session::handle_sign_request(const SignRequest& message)
381 LOGD("Request data signing");
384 auto * signingResponse = msg.mutable_sign_data();
386 if(message.context_cookie() != fCookie) {
387 LOGE("Received unknown context cookie");
388 signingResponse->set_result(-EINVAL);
393 if(!fBackendContext) {
394 LOGE("Context not associated with connection");
395 signingResponse->set_result(-EINVAL);
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);
407 bool loaded = fSoResolver->ensure_loaded();
409 error = fSoResolver->invoke<int, dcm_backend_context&, MessageDigestType, const std::string&, std::string&>(
410 "dcm_backend_sign_crypto_data",
412 message.digest_type(),
413 message.data_to_sign(),
414 *signingResponse->mutable_signature()
417 signingResponse->set_result(error);