2 * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
18 * @file privacy_privilege_manager.c
19 * @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
20 * @brief This file contains test of CAPI for Privacy Privilege Manager
27 #include <sys/types.h>
31 #include <attributes/attributes.h>
32 #include <client-channel.h>
33 #include <server-channel.h>
34 #include <common-types.h>
35 #include <connection-exception.h>
36 #include <privacy_privilege_manager.h>
38 using namespace AskUser::Protocol;
40 struct ServerPrivilegeRequest {
42 ServerPrivilegeRequest(RequestId reqId)
46 ServerPrivilegeRequest(RequestId reqId, const Privilege &privilege)
48 , m_privilege(privilege)
51 bool operator<(const ServerPrivilegeRequest &other) const {
52 return m_id < other.m_id;
56 Privilege m_privilege;
59 typedef std::set<ServerPrivilegeRequest> PrivilegeRequestSet;
61 struct ClientRequest {
63 GMainLoop *m_mainLoop;
64 bool m_stopAppAfterDeny;
69 GIOChannel *gio_channel;
70 GIOCondition condition;
80 const GIOCondition GIO_ERROR_CONDITION = static_cast<GIOCondition>(G_IO_ERR | G_IO_HUP | G_IO_NVAL);
82 GIOCondition maskToGIOCondition(int mask)
84 int ret = ((mask & FdMask::READ) ? G_IO_IN : 0) |
85 ((mask & FdMask::WRITE) ? G_IO_OUT : 0);
86 return static_cast<GIOCondition>(ret);
89 GIOCondition makeCondition(GIOCondition condition)
91 return static_cast<GIOCondition>(condition | GIO_ERROR_CONDITION);
94 bool isErrorCondition(GIOCondition condition)
96 return !!(GIO_ERROR_CONDITION & condition);
99 void printPrompt(Mode mode)
117 const char *popupResultToString(ppm_popup_result_e result)
120 case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_ALLOW_FOREVER:
121 return "ALLOW_FOREVER";
122 case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_FOREVER:
123 return "DENY_FOREVER";
124 case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_ONCE:
131 void printClientHelp()
133 printf("c <privilege> check privilege status\n");
134 printf("r <privilege> send popup request\n");
135 printf("s <privilege> send popup request, additionally after receiving DENY it finishes this app\n");
136 printf("h print help\n");
138 printPrompt(Mode::CLIENT);
141 void printClientError(ppm_error_e error)
144 case PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE:
146 case PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR:
147 printf("I/O error\n");
149 case PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER:
150 printf("Invalid parameters\n");
152 case PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY:
153 printf("Out of memory\n");
155 case PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN:
156 printf("Unknown error\n");
158 case PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS:
159 printf("Operation already in progress\n");
164 void printClientCheckResult(ppm_check_result_e result)
169 case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW:
172 case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY:
175 case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ASK:
180 printPrompt(Mode::CLIENT);
183 void printAppHelp(const char *programName)
185 printf("Usage: %s [OPTIONS]\n", programName);
186 printf("Allows to test CAPI for Privacy Privilege Manager.\n");
187 printf("This program can run in two modes: as a client and as a server.\n");
188 printf("You can use it in two scenarios:\n");
189 printf("1. Run as a client (%s -c) to trigger popups in the askuser-notification daemon.\n", programName);
190 printf("2. Run one part as a client (%s -c) and other one as a server (%s -s).\n",
191 programName, programName);
192 printf("If you want to run this program as a root you need to create /var/run/user_ext/0 folder.\n");
193 printf("To run this program in context of some Tizen's application, type in the console:\n");
194 printf("#echo <SMACK label of the application> > /proc/self/attr/current\n");
195 printf("#su - <user name>\n\n");
196 printf("Options:\n");
197 printf("-s run as a server\n");
198 printf("-c run as a client (uses CAPI)\n");
199 printf("-h display this help end exit\n");
202 void printServerHelp()
204 printf("l list all connections\n");
205 printf("s list all requests\n");
206 printf("ra <fd> <request id> send ALLOW FOREVER response\n");
207 printf("rd <fd> <request id> send DENY FOREVER response\n");
208 printf("ro <fd> <request id> send DENY ONCE response\n");
209 printf("c <fd> close connection\n");
210 printf("h print help\n");
212 printPrompt(Mode::SERVER);
215 struct ServerSimulator : public IServerCallbacks {
221 virtual void newConnection(ConnectionFd fd, const Credentials &creds) {
222 printf("\nnew connection fd: %d credentials = { label: \"%s\" uid: %s }",
223 fd, creds.label.c_str(), creds.uid.c_str());
226 static gboolean serverGIOCallback(GIOChannel *src, GIOCondition cond, gpointer data) {
227 ServerSimulator *handle = static_cast<ServerSimulator *>(data);
229 int fd = g_io_channel_unix_get_fd(src);
230 int events = ((cond & G_IO_IN) ? FdMask::READ : 0) |
231 ((cond & G_IO_OUT) ? FdMask::WRITE : 0);
233 handle->m_channel->process(fd, isErrorCondition(cond) ? 0 : events);
238 virtual void updateConnection(ConnectionFd fd, int mask) {
240 printf("ERROR: updateConnection() called with fd == -1\n");
244 GIOCondition gio_condition = maskToGIOCondition(mask);
246 auto it = m_connections.find(fd);
249 if (it == m_connections.end())
252 auto &connection = it->second;
254 g_source_remove(connection.watch_id);
255 g_io_channel_unref(connection.gio_channel);
257 m_connections.erase(fd);
258 m_requests.erase(fd);
260 printf("Connection for fd: %d has been closed\n", fd);
261 printPrompt(Mode::SERVER);
265 if (it == m_connections.end()) {
266 GIOChannel *gio_channel = g_io_channel_unix_new(fd);
268 g_io_channel_set_encoding (gio_channel, nullptr, nullptr);
269 g_io_channel_set_close_on_unref(gio_channel, FALSE);
271 guint watch_id = g_io_add_watch(gio_channel,
272 makeCondition(gio_condition),
276 m_connections.insert({ fd, Connection{gio_channel, gio_condition, watch_id }});
281 auto &connection = it->second;
283 if (connection.condition != gio_condition) {
284 connection.condition = gio_condition;
285 g_source_remove(connection.watch_id);
286 connection.watch_id = g_io_add_watch(connection.gio_channel,
287 makeCondition(connection.condition),
293 virtual void popup(ConnectionFd fd, RequestId id, Privilege &&privilege) {
294 printf("\npopup request fd: %d requestId: %d privilege: \"%s\"\n", fd, id, privilege.c_str());
295 addRequest(fd, id, privilege);
296 printPrompt(Mode::SERVER);
299 void addRequest(ConnectionFd fd, RequestId id, const Privilege &privilege) {
300 auto &s = m_requests[fd];
302 auto reqIt = s.find(id);
303 if (reqIt != s.end()) {
304 printf("ERROR: We already have request id: %d for fd: %d\n", id, fd);
308 s.insert(ServerPrivilegeRequest(id, privilege));
311 void sendResponse(ConnectionFd fd, RequestId id, int response) {
312 auto fdIt = m_requests.find(fd);
313 if (fdIt == m_requests.end()) {
314 printf("ERROR: Connection for fd: %d doesn't exist!\n", fd);
318 auto &s = fdIt->second;
320 auto reqIt = s.find(id);
321 if (reqIt == s.end()) {
322 printf("ERROR: Request %d doesn't exist for fd: %d\n", id, fd);
327 m_channel->popupResponse(fd, id, response);
332 m_requests.erase(fdIt);
337 void showPrivilegeRequests() {
338 printf("fd requestId privilege\n");
339 for (const auto &con : m_requests) {
340 for (const auto &req : con.second) {
341 printf("%-11d %-11d %-20s\n", con.first, req.m_id, req.m_privilege.c_str());
346 void closeConnection(int fd) {
347 auto it = m_connections.find(fd);
348 if (it == m_connections.end()) {
349 printf("ERROR: Connection fd: %d not found\n", fd);
350 printPrompt(Mode::SERVER);
354 m_requests.erase(fd);
355 m_channel->process(fd, 0);
358 void showConnections() {
360 for (const auto &con : m_connections) {
361 printf("%d\n", con.first);
365 void setServerChannel(ServerChannel *ptr) {
370 ServerChannel *m_channel;
371 std::map<ConnectionFd, Connection> m_connections;
372 std::map<ConnectionFd, PrivilegeRequestSet> m_requests;
378 : m_mainloop(g_main_loop_new(nullptr, FALSE))
379 , m_serverSimulator(nullptr)
381 , m_mode(Mode::UNKNOWN)
385 g_main_loop_unref(m_mainloop);
387 if (m_channel != nullptr) {
392 void initialize(int argc, char **argv) {
394 throw std::logic_error("AppContext::initialize called with argc < 1");
399 while ((c = getopt (argc, argv, "sch")) != -1) {
402 m_mode = Mode::SERVER;
405 m_mode = Mode::CLIENT;
415 m_serverSimulator = new ServerSimulator();
416 m_channel = new ServerChannel(ServerCallbacksPtr(m_serverSimulator));
417 m_serverSimulator->setServerChannel(m_channel);
425 printAppHelp(argv[0]);
429 GIOChannel *gio_channel = g_io_channel_unix_new(0);
430 g_io_add_watch(gio_channel, static_cast<GIOCondition>(G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL),
431 &AppContext::mainLoopCallback, this);
435 if (m_mode == Mode::UNKNOWN) {
436 throw std::logic_error("AppContext is not initialized");
439 g_main_loop_run(m_mainloop);
444 static void popupResponseCallback(ppm_call_cause_e cause, ppm_popup_result_e result,
445 const char *privilege, void *user_data) {
446 ClientRequest *request = static_cast<ClientRequest *>(user_data);
447 if (request == nullptr) {
448 printf("ERROR: User data is nullptr\n");
452 printf("localId: %d privilege: \"%s\" ", request->m_id, privilege);
455 case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER:
456 printf("popup response ANSWER: %s\n", popupResultToString(result));
458 case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR:
459 printf("popup response ERROR cause: PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR\n");
463 if (result == PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_FOREVER && request->m_stopAppAfterDeny) {
464 g_main_loop_quit(request->m_mainLoop);
469 printPrompt(Mode::CLIENT);
472 void handleClient(char *buffer) {
473 ppm_check_result_e result;
474 ppm_error_e err = PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
475 ClientRequest *request = nullptr;
476 char *privilege = nullptr;
477 static RequestId clientRequestId;
478 bool stopAppAfterDeny = false;
482 if (strlen(buffer) < 3) {
486 err = static_cast<ppm_error_e>(ppm_check_privilege(&buffer[2], &result));
487 if (err == PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
488 printClientCheckResult(result);
492 stopAppAfterDeny = true;
494 if (strlen(buffer) < 3) {
499 buffer[strlen(buffer)] = '\0'; // erase new line
500 privilege = &buffer[2];
502 printf("sending localId: %d privilege: \"%s\"\n", clientRequestId, privilege);
504 request = new ClientRequest{ clientRequestId++, m_mainloop, stopAppAfterDeny };
505 err = static_cast<ppm_error_e>(ppm_popup_request(privilege, &AppContext::popupResponseCallback, request));
506 if (err != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
512 g_main_loop_quit(m_mainloop);
520 printClientError(err);
521 printPrompt(Mode::CLIENT);
524 void handleServer(const char *buffer) {
531 m_serverSimulator->showConnections();
534 m_serverSimulator->showPrivilegeRequests();
537 res = sscanf(&buffer[2], "%d %d", &fd, &requestId);
544 m_serverSimulator->sendResponse(fd, requestId, ASKUSER_ALLOW_FOREVER);
547 m_serverSimulator->sendResponse(fd, requestId, ASKUSER_DENY_FOREVER);
550 m_serverSimulator->sendResponse(fd, requestId, ASKUSER_DENY_ONCE);
558 res = sscanf(&buffer[1], "%d", &fd);
563 m_serverSimulator->closeConnection(fd);
569 g_main_loop_quit(m_mainloop);
573 printPrompt(Mode::SERVER);
576 static gboolean mainLoopCallback(UNUSED GIOChannel *channel, UNUSED GIOCondition cond, gpointer user_data) {
579 int ret = read(0, buffer, sizeof(buffer));
581 printf("read from stdin failed\n");
585 buffer[ret - 1] = '\0';
587 AppContext *ctx = static_cast<AppContext *>(user_data);
588 switch (ctx->m_mode) {
590 ctx->handleClient(buffer);
593 ctx->handleServer(buffer);
597 printf("Mode not set, exiting.\n");
604 GMainLoop *m_mainloop;
605 ServerSimulator *m_serverSimulator;
606 ServerChannel *m_channel;
610 int main(int argc, char **argv)
615 appCtx.initialize(argc, argv);
618 catch (const ConnectionException &e) {
619 printf("Connection exception occured during initialization: %s, exiting.\n", e.what());
620 printf("Check if the /var/run/user_ext/%u directory exists.\n", geteuid());
623 catch (const std::exception &e) {
624 printf("Standard exception occured during initialization: %s, exiting.\n", e.what());
628 printf("Unknwon exception occured during initialization, exiting.\n");