Fix coverity issue
[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         expose(this, "", (runtime::FileDescriptor)(ServerContext::registerNotificationSubscriber)(std::string));
147         expose(this, "", (int)(ServerContext::unregisterNotificationSubscriber)(std::string, int));
148
149         keys.reset(new KeyServer(*this));
150
151         secureErase.reset(new SecureEraseServer(*this));
152         internalEncryption.reset(new InternalEncryptionServer(*this, *keys));
153         externalEncryption.reset(new ExternalEncryptionServer(*this, *keys));
154         luks.reset(new LuksServer(*this, *keys));
155
156         KeyGenerator::init();
157
158         guint dbusOwnerId = g_bus_own_name(G_BUS_TYPE_SYSTEM,
159                                            DBUS_NAME,
160                                            G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE,
161                                            dbusBusAcquired,
162                                            dbusNameAcquired,
163                                            dbusNameLost,
164                                            this,
165                                            NULL);
166
167         if (dbusOwnerId == 0)
168                 throw runtime::Exception("Dbus failed to own name");
169
170         std::thread timerThread(&ServerContext::timerFunc, this);
171         timerThread.detach();
172 }
173
174 ServerContext::~ServerContext()
175 {
176         KeyGenerator::cleanup();
177 }
178
179 bool ServerContext::checkPeerPrivilege(const rmi::Credentials& cred, const std::string& privilege)
180 {
181         cynara *p_cynara;
182
183         if (privilege.empty()) {
184                 return true;
185         }
186
187         if (::cynara_initialize(&p_cynara, NULL) != CYNARA_API_SUCCESS) {
188                 ERROR(SINK, "Failure in cynara API");
189                 return false;
190         }
191
192         std::string uid = std::to_string(cred.uid);
193         if (::cynara_check(p_cynara, cred.security.c_str(), "",
194                                            uid.c_str(),
195                                            privilege.c_str()) != CYNARA_API_ACCESS_ALLOWED) {
196                 ::cynara_finish(p_cynara);
197                 ERROR(SINK, "Access denied: " + cred.security + " : " + privilege);
198                 return false;
199         }
200
201         ::cynara_finish(p_cynara);
202
203         return true;
204 }
205
206 runtime::FileDescriptor ServerContext::registerNotificationSubscriber(const std::string& name)
207 {
208         INFO(SINK, "registerNotificationSubscriber");
209         INFO(SINK, name);
210
211         RequestLifetime rl(*this);
212
213         int fd = subscribeNotification(name);
214
215         /**
216          *  Set @ label so that smack_file_receive() in kernel succeeds in checking
217          *  'w' access between the client and the IPOUT of the socket.
218          */
219         if (smack_fsetlabel(fd, "@", SMACK_LABEL_IPOUT) != 0) {
220                 ERROR(SINK, "Setting IPOUT label failed");
221                 throw runtime::Exception("Setting IPOUT label failed");
222         }
223
224         return runtime::FileDescriptor(fd, true);
225 }
226
227 int ServerContext::unregisterNotificationSubscriber(const std::string& name, int id)
228 {
229         RequestLifetime rl(*this);
230
231         return unsubscribeNotification(name, id);
232 }
233
234 } // namespace ode