Add CynaraThread
[platform/core/appfw/rpc-port.git] / src / stub-internal.cc
1 /*
2  * Copyright (c) 2017 - 2021 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 #include <aul.h>
18 #include <aul_rpc_port.h>
19 #include <dlog.h>
20 #include <sys/socket.h>
21 #include <sys/types.h>
22
23 #include <utility>
24 #include <vector>
25
26 #include "aul-internal.hh"
27 #include "debug-port-internal.hh"
28 #include "include/rpc-port.h"
29 #include "log-private.hh"
30 #include "peer-cred-internal.hh"
31 #include "request-internal.hh"
32 #include "response-internal.hh"
33 #include "stub-internal.hh"
34
35 namespace rpc_port {
36 namespace internal {
37 namespace {
38
39 constexpr const char kPortTypeMain[] = "main";
40 constexpr const char kPortTypeDelegate[] = "delegate";
41 constexpr uid_t kRegularUidMin = 5000;
42
43 int ReceiveRequest(ClientSocket* client, Request** request) {
44   size_t size = 0;
45   int ret = client->Receive(reinterpret_cast<void*>(&size), sizeof(size));
46   if (ret != 0) {
47     _E("Receive() is failed. error(%d)", ret);
48     return -1;
49   }
50
51   std::vector<uint8_t> buf(size);
52   ret = client->Receive(buf.data(), size);
53   if (ret != 0) {
54     _E("Receive() is failed. error(%d)", ret);
55     return -1;
56   }
57
58   tizen_base::Parcel parcel(buf.data(), buf.size());
59   *request = new (std::nothrow) Request();
60   if (*request == nullptr) {
61     _E("Out of memory");
62     return -1;
63   }
64
65   parcel.ReadParcelable(*request);
66   return 0;
67 }
68
69 int SendResponse(ClientSocket* client, const Response& response) {
70   tizen_base::Parcel parcel;
71   parcel.WriteParcelable(const_cast<Response&>(response));
72   size_t size = parcel.GetDataSize();
73   int ret = client->Send(reinterpret_cast<void*>(&size), sizeof(size));
74   if (ret != 0) {
75     _E("Send() is failed. error(%d)", ret);
76     return -1;
77   }
78
79   ret = client->Send(parcel.GetData(), size);
80   if (ret != 0) {
81     _E("Send() is failed. error(%d)", ret);
82     return -1;
83   }
84
85   return 0;
86 }
87
88 }  // namespace
89
90 std::unordered_set<Stub*> Stub::freed_stubs_;
91
92 Stub::Stub(std::string port_name) : port_name_(std::move(port_name)) {
93   _D("Stub::Stub()");
94   freed_stubs_.erase(this);
95 }
96
97 Stub::~Stub() {
98   std::lock_guard<std::recursive_mutex> lock(GetMutex());
99   _D("Stub::~Stub");
100   for (auto& p : ports_) {
101     if (!p->IsDelegate())
102       DebugPort::RemoveSession(p->GetFd());
103   }
104
105   listener_ = nullptr;
106   server_.reset();
107   freed_stubs_.insert(this);
108 }
109
110 int Stub::Listen(IEventListener* ev, int fd) {
111   if (ev == nullptr)
112     return RPC_PORT_ERROR_INVALID_PARAMETER;
113
114   if (listener_ != nullptr) {
115     _E("Already listening!");
116     return RPC_PORT_ERROR_INVALID_PARAMETER;
117   }
118
119   std::lock_guard<std::recursive_mutex> lock(GetMutex());
120   listener_ = ev;
121   server_.reset(new Server(fd, this));
122   return server_->Listen();
123 }
124
125 void Stub::Ignore() {
126   std::lock_guard<std::recursive_mutex> lock(GetMutex());
127   listener_ = nullptr;
128 }
129
130 void Stub::AddPrivilege(const std::string& privilege) {
131   std::lock_guard<std::recursive_mutex> lock(GetMutex());
132   access_controller_.AddPrivilege(privilege);
133 }
134
135 void Stub::SetTrusted(const bool trusted) {
136   std::lock_guard<std::recursive_mutex> lock(GetMutex());
137   access_controller_.SetTrusted(trusted);
138 }
139
140 std::shared_ptr<Port> Stub::FindPort(const std::string& instance) const {
141   std::lock_guard<std::recursive_mutex> lock(GetMutex());
142   for (auto& p : ports_) {
143     if (p->GetInstance() == instance && !p->IsDelegate()) {
144       return p;
145     }
146   }
147
148   return {};
149 }
150
151 std::shared_ptr<Port> Stub::FindDelegatePort(
152     const std::string& instance) const {
153   std::lock_guard<std::recursive_mutex> lock(GetMutex());
154   for (auto& p : ports_) {
155     if (p->GetInstance() == instance && p->IsDelegate()) {
156       return p;
157     }
158   }
159
160   return {};
161 }
162
163 const std::string& Stub::GetPortName() const {
164   return port_name_;
165 }
166
167 void Stub::RemoveAcceptedPorts(std::string instance) {
168   std::lock_guard<std::recursive_mutex> lock(GetMutex());
169   auto iter = ports_.begin();
170   while (iter != ports_.end()) {
171     if ((*iter)->GetInstance().compare(instance) == 0) {
172       LOGI("Close: fd(%d)", (*iter)->GetFd());
173       DebugPort::RemoveSession((*iter)->GetFd());
174       iter = ports_.erase(iter);
175     } else {
176       iter++;
177     }
178   }
179 }
180
181 gboolean Stub::AcceptedPort::OnDataReceived(GIOChannel* channel,
182     GIOCondition cond, gpointer user_data) {
183   auto* stub = static_cast<Stub*>(user_data);
184   std::lock_guard<std::recursive_mutex> lock(stub->GetMutex());
185   auto* listener = stub->listener_;
186   if (listener == nullptr) {
187     _E("Invalid context");
188     return G_SOURCE_REMOVE;
189   }
190
191   int fd = g_io_channel_unix_get_fd(channel);
192   for (auto& p : stub->ports_) {
193     if (p->GetFd() == fd && !p->IsDelegate()) {
194       char buffer[4];
195       if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
196         _W("Socket was disconnected from proxy. fd(%d)", fd);
197         listener->OnDisconnected(p->GetId(), p->GetInstance());
198         stub->RemoveAcceptedPorts(p->GetInstance());
199         Aul::NotifyRpcFinished();
200         return G_SOURCE_CONTINUE;
201       }
202
203       int ret = stub->listener_->OnReceived(p->GetId(), p->GetInstance(),
204           p.get());
205       if (ret != 0) {
206         _W("Invalid protocol");
207         listener->OnDisconnected(p->GetId(), p->GetInstance());
208         stub->RemoveAcceptedPorts(p->GetInstance());
209         Aul::NotifyRpcFinished();
210         return G_SOURCE_CONTINUE;
211       }
212
213       break;
214     }
215   }
216
217   return G_SOURCE_CONTINUE;
218 }
219
220 gboolean Stub::AcceptedPort::OnSocketDisconnected(GIOChannel* channel,
221     GIOCondition cond, gpointer user_data) {
222   auto* stub = static_cast<Stub*>(user_data);
223   std::lock_guard<std::recursive_mutex> lock(stub->GetMutex());
224   auto* listener = stub->listener_;
225   if (listener == nullptr) {
226     _E("Invalid context");
227     return G_SOURCE_REMOVE;
228   }
229
230   int fd = g_io_channel_unix_get_fd(channel);
231   _W("Socket was disconnected. fd(%d)", fd);
232   for (auto& p : stub->ports_) {
233     if (p->GetFd() == fd) {
234       listener->OnDisconnected(p->GetId(), p->GetInstance());
235       stub->RemoveAcceptedPorts(p->GetInstance());
236       Aul::NotifyRpcFinished();
237       break;
238     }
239   }
240
241   return G_SOURCE_REMOVE;
242 }
243
244 void Stub::AddAcceptedPort(const std::string& sender_appid,
245     const std::string& instance, const std::string& port_type, int fd) {
246   std::lock_guard<std::recursive_mutex> lock(GetMutex());
247   if (port_type == kPortTypeMain) {
248     auto* main_port = new AcceptedPort(this, false, fd, sender_appid,
249         instance, true);
250     ports_.emplace_back(main_port);
251     return;
252   }
253
254   auto* delegate_port = new AcceptedPort(this, true, fd, sender_appid,
255       instance, false);
256   ports_.emplace_back(delegate_port);
257
258   int main_fd = -1;
259   for (auto& p : ports_) {
260     if (p->GetId() == delegate_port->GetId() &&
261         p->GetInstance() == delegate_port->GetInstance() &&
262         p->GetFd() != delegate_port->GetFd()) {
263       main_fd = p->GetFd();
264       break;
265     }
266   }
267
268   _W("sender_appid(%s), instance(%s), main_fd(%d), delegate_fd(%d)",
269       sender_appid.c_str(), instance.c_str(), main_fd, fd);
270   DebugPort::AddSession(port_name_, sender_appid, main_fd, fd);
271   listener_->OnConnected(sender_appid, instance);
272 }
273
274 std::recursive_mutex& Stub::GetMutex() const {
275   return mutex_;
276 }
277
278 Stub::AcceptedPort::AcceptedPort(Stub* parent, bool isDelegate, int fd,
279     std::string id, std::string inst, bool watch)
280     : Port(fd, std::move(id), std::move(inst)), parent_(parent),
281       is_delegate_(isDelegate) {
282   Watch(watch);
283 }
284
285 Stub::AcceptedPort::AcceptedPort(Stub* parent, bool isDelegate, int fd,
286     std::string id, bool watch)
287     : Port(fd, std::move(id)), parent_(parent),
288       is_delegate_(isDelegate) {
289   Watch(watch);
290 }
291
292 Stub::AcceptedPort::~AcceptedPort() {
293   if (disconn_source_ > 0)
294     g_source_remove(disconn_source_);
295
296   if (source_ > 0)
297     g_source_remove(source_);
298
299   if (channel_ != nullptr)
300     g_io_channel_unref(channel_);
301 }
302
303 int Stub::AcceptedPort::Watch(bool receive) {
304   channel_ = g_io_channel_unix_new(GetFd());
305   if (channel_ == nullptr) {
306     _E("g_io_channel_unix_new() is failed");
307     return -1;
308   }
309
310   disconn_source_ = g_io_add_watch(channel_,
311       static_cast<GIOCondition>(G_IO_ERR | G_IO_HUP | G_IO_NVAL),
312       OnSocketDisconnected, parent_);
313   if (disconn_source_ == 0) {
314     _E("g_io_add_watch() is failed");
315     return -1;
316   }
317
318   if (!receive)
319     return 0;
320
321   source_ = g_io_add_watch(channel_, static_cast<GIOCondition>(G_IO_IN),
322       OnDataReceived, parent_);
323   if (source_ == 0) {
324     _E("g_io_add_watch() is failed");
325     return -1;
326   }
327
328   return 0;
329 }
330
331 Stub::Server::Server(int fd, Stub* parent)
332     : ServerSocket(fd),
333       parent_(parent) {
334 }
335
336 Stub::Server::~Server() {
337   if (channel_)
338     g_io_channel_unref(channel_);
339
340   if (source_ > 0)
341     g_source_remove(source_);
342 }
343
344 int Stub::Server::Listen() {
345   channel_ = g_io_channel_unix_new(GetFd());
346   if (channel_ == nullptr) {
347     _E("g_io_channel_unix_new() is failed");
348     return -1;
349   }
350
351   source_ = g_io_add_watch(channel_, static_cast<GIOCondition>(G_IO_IN),
352       OnRequestReceived, parent_);
353   if (source_ == 0) {
354     _E("g_io_add_watch() is failed");
355     return -1;
356   }
357
358   return 0;
359 }
360
361 gboolean Stub::Server::OnRequestReceived(GIOChannel* channel, GIOCondition cond,
362     gpointer user_data) {
363   auto* stub = static_cast<Stub*>(user_data);
364   std::lock_guard<std::recursive_mutex> lock(stub->GetMutex());
365   if (stub->listener_ == nullptr) {
366     _E("Invalid context");
367     return G_SOURCE_REMOVE;
368   }
369
370   std::shared_ptr<ClientSocket> client(stub->server_->Accept());
371   if (client.get() == nullptr) {
372     _E("Out of memory");
373     return G_SOURCE_CONTINUE;
374   }
375
376   Request* request = nullptr;
377   int ret = ReceiveRequest(client.get(), &request);
378   if (ret != 0)
379     return G_SOURCE_CONTINUE;
380
381   std::shared_ptr<Request> request_auto(request);
382   std::shared_ptr<PeerCred> cred(PeerCred::Get(client->GetFd()));
383   if (cred.get() == nullptr) {
384     _E("Failed to create peer credentials");
385     return G_SOURCE_CONTINUE;
386   }
387
388   std::string app_id = Aul::GetAppId(cred->GetPid());
389   auto response_func = [=](int res) -> void {
390     if (freed_stubs_.find(stub) != freed_stubs_.end())
391       return;
392
393     Response response(res);
394     int ret = SendResponse(client.get(), response);
395     if (ret != 0)
396       return;
397
398     if (res != 0) {
399       _E("Access denied. fd(%d), pid(%d)", client->GetFd(), cred->GetPid());
400       return;
401     }
402
403     client->SetNonblock();
404     int client_fd = client->RemoveFd();
405     stub->AddAcceptedPort(app_id, request_auto->GetInstance(),
406         request_auto->GetPortType(), client_fd);
407   };
408
409   int res;
410   if (cred->GetUid() >= kRegularUidMin) {
411     if (cred->GetUid() != getuid() && getuid() >= kRegularUidMin) {
412       _E("Reject request. %u:%u", cred->GetUid(), getuid());
413       res = -1;
414     } else {
415       stub->access_controller_.CheckAsync(client->GetFd(), app_id,
416           response_func);
417       return G_SOURCE_CONTINUE;
418     }
419   } else {
420     _W("Bypass access control. pid(%d), uid(%u)",
421         cred->GetPid(), cred->GetUid());
422     res = 0;
423   }
424
425   response_func(res);
426   return G_SOURCE_CONTINUE;
427 }
428
429 }  // namespace internal
430 }  // namespace rpc_port