Use dbus for event notification
[platform/core/security/ode.git] / server / server.cpp
1 /*
2  *  Copyright (c) 2015 - 2019 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 #include <cynara-client.h>
17 #include <sys/smack.h>
18
19 #include <stdexcept>
20 #include <chrono>
21 #include <cassert>
22 #include <thread>
23
24 #include "logger.h"
25 #include "file-sink.h"
26 #include "secure-erase.h"
27 #include "internal-encryption.h"
28 #include "external-encryption.h"
29 #include "luks.h"
30 #include "key-server.h"
31 #include "key-manager/key-generator.h"
32 #include "server.h"
33
34 using namespace std::placeholders;
35
36 audit::LogSink *SINK = nullptr;
37
38 namespace ode {
39
40 namespace {
41
42 std::unique_ptr<FileLogSink> _sink = nullptr;
43
44 void dbusBusAcquired(GDBusConnection *connection, const gchar *, gpointer user_data)
45 {
46         DEBUG(SINK, "dbusBusAcquired");
47
48         auto self = static_cast<ServerContext*>(user_data);
49         self->dbusRegisterObjects(connection);
50 }
51
52 /*
53  * Test:
54  * dbus-send --system --dest=org.freedesktop.DBus --type=method_call --print-reply
55  * /org/freedesktop/DBus org.freedesktop.DBus.ListNames
56  */
57 void dbusNameAcquired(GDBusConnection *, const gchar *, gpointer )
58 {
59         DEBUG(SINK, "dbusNameAcquired");
60 }
61
62 void dbusNameLost(GDBusConnection *, const gchar *, gpointer user_data)
63 {
64         DEBUG(SINK, "dbusNameLost");
65         static_cast<ServerContext*>(user_data)->stop();
66 }
67
68 } // namespace
69
70 void ServerContext::dbusRegisterObjects(GDBusConnection *connection)
71 {
72         externalEncryption->dbusRegisterObject(connection);
73 }
74
75 /*
76  * Timer thread fuction responsible for detecting a server inactivity period after which it will be
77  * stopped.
78  *
79  * Assumptions & acknowledgements:
80  * - Currently there are only 2 ways of stopping the oded server:
81  *   - Timeout expiration implemented in the function below
82  *   - Terminating with a signal (e.g. SIGTERM from systemd)
83  * - There's a risk of race condition for a request that has just been received (the program is
84  *   somewhere between klay's epoll_wait() and mutex lock in ServerContext::requestStarted) while
85  *   the timerFunc below detects the timeout and stops the server. In such case the newly received
86  *   request will not be processed and client will be disconnected. For now this risk is accepted.
87  *   To reduce it, the klay's socket & thread handling would have to be modified or replaced.
88  * - Request processing callbacks are currently called from one of klay's worker threads.
89  * - For requests that require longer processing additional threads are spawned by the server. The
90  *   server is considered active as long as the request processing thread is running.
91  * - For debugging purpose, a manually started server is not considered inactive until a request
92  *   arrives. In other words, the inactivity time measurement starts after a request is processed.
93  * - Spurious wakeups will not occur in sleep_for().
94  */
95 void ServerContext::timerFunc()
96 {
97         DEBUG(SINK, "Timer thread started");
98         bool requestArrived = false;
99         for(;;) {
100                 std::this_thread::sleep_for(std::chrono::seconds(5));
101
102                 std::lock_guard<std::mutex> lock(timerMutex);
103                 if (!requestArrived) {
104                         if (!newRequests)
105                                 continue;
106
107                         requestArrived = true;
108                 }
109                 if (!newRequests && currentRequests == 0)
110                         break;
111
112                 newRequests = false;
113         }
114         INFO(SINK, "Timeout reached. ODE server stopping.");
115         stop();
116 }
117
118
119 // This method may be called from different threads
120 void ServerContext::requestStarted()
121 {
122         std::lock_guard<std::mutex> lock(timerMutex);
123         currentRequests++;
124         newRequests = true;
125         DEBUG(SINK, "New request received. Current count: " << currentRequests);
126 }
127
128 // This method may be called from different threads
129 void ServerContext::requestFinished()
130 {
131         std::lock_guard<std::mutex> lock(timerMutex);
132         assert(currentRequests > 0);
133         currentRequests--;
134         DEBUG(SINK, "Request processed. Current count:" << currentRequests);
135 }
136
137 ServerContext::ServerContext() : rmi::Service(SOCKET_PATH), currentRequests(0), newRequests(false)
138 {
139         _sink.reset(new FileLogSink("ode.log"));
140         SINK = _sink.get();
141
142         INFO(SINK, "ODE server starting.");
143
144         setPrivilegeChecker(std::bind(&ServerContext::checkPeerPrivilege, this, _1, _2));
145
146         keys.reset(new KeyServer(*this));
147
148         secureErase.reset(new SecureEraseServer(*this));
149         internalEncryption.reset(new InternalEncryptionServer(*this, *keys));
150         externalEncryption.reset(new ExternalEncryptionServer(*this, *keys));
151         luks.reset(new LuksServer(*this, *keys));
152
153         KeyGenerator::init();
154
155         guint dbusOwnerId = g_bus_own_name(G_BUS_TYPE_SYSTEM,
156                                            DBUS_NAME,
157                                            G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE,
158                                            dbusBusAcquired,
159                                            dbusNameAcquired,
160                                            dbusNameLost,
161                                            this,
162                                            NULL);
163
164         if (dbusOwnerId == 0)
165                 throw runtime::Exception("Dbus failed to own name");
166
167         EventNotifier::init();
168
169         std::thread timerThread(&ServerContext::timerFunc, this);
170         timerThread.detach();
171 }
172
173 ServerContext::~ServerContext()
174 {
175         KeyGenerator::cleanup();
176 }
177
178 bool ServerContext::checkPeerPrivilege(const rmi::Credentials& cred, const std::string& privilege)
179 {
180         cynara *p_cynara;
181
182         if (privilege.empty()) {
183                 return true;
184         }
185
186         if (::cynara_initialize(&p_cynara, NULL) != CYNARA_API_SUCCESS) {
187                 ERROR(SINK, "Failure in cynara API");
188                 return false;
189         }
190
191         std::string uid = std::to_string(cred.uid);
192         if (::cynara_check(p_cynara, cred.security.c_str(), "",
193                                            uid.c_str(),
194                                            privilege.c_str()) != CYNARA_API_ACCESS_ALLOWED) {
195                 ::cynara_finish(p_cynara);
196                 ERROR(SINK, "Access denied: " + cred.security + " : " + privilege);
197                 return false;
198         }
199
200         ::cynara_finish(p_cynara);
201
202         return true;
203 }
204
205 } // namespace ode