Add the 'privilege' parameter to popup response callback
[platform/core/security/askuser.git] / src / capi / test / privacy_privilege_manager_test.cpp
1 /*
2  *  Copyright (c) 2017 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 /**
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
21  */
22
23 #include <map>
24 #include <set>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <glib.h>
29 #include <unistd.h>
30
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>
37
38 using namespace AskUser::Protocol;
39
40 struct ServerPrivilegeRequest {
41
42     ServerPrivilegeRequest(RequestId reqId)
43     : m_id(reqId)
44     {}
45
46     ServerPrivilegeRequest(RequestId reqId, const Privilege &privilege)
47     : m_id(reqId)
48     , m_privilege(privilege)
49     {}
50
51     bool operator<(const ServerPrivilegeRequest &other) const {
52         return m_id < other.m_id;
53     }
54
55     RequestId m_id;
56     Privilege m_privilege;
57 };
58
59 typedef std::set<ServerPrivilegeRequest> PrivilegeRequestSet;
60
61 struct ClientRequest {
62     RequestId m_id;
63     GMainLoop *m_mainLoop;
64     bool m_stopAppAfterDeny;
65 };
66
67 struct Connection
68 {
69     GIOChannel *gio_channel;
70     GIOCondition condition;
71     guint watch_id;
72 };
73
74 enum class Mode {
75     CLIENT,
76     SERVER,
77     UNKNOWN
78 };
79
80 const GIOCondition GIO_ERROR_CONDITION = static_cast<GIOCondition>(G_IO_ERR | G_IO_HUP | G_IO_NVAL);
81
82 GIOCondition maskToGIOCondition(int mask)
83 {
84     int ret = ((mask & FdMask::READ) ? G_IO_IN : 0) |
85               ((mask & FdMask::WRITE) ? G_IO_OUT : 0);
86     return static_cast<GIOCondition>(ret);
87 }
88
89 GIOCondition makeCondition(GIOCondition condition)
90 {
91     return static_cast<GIOCondition>(condition | GIO_ERROR_CONDITION);
92 }
93
94 bool isErrorCondition(GIOCondition condition)
95 {
96     return !!(GIO_ERROR_CONDITION & condition);
97 }
98
99 void printPrompt(Mode mode)
100 {
101     switch (mode) {
102         case Mode::CLIENT:
103             printf("Client ");
104             break;
105         case Mode::SERVER:
106             printf("Server ");
107             break;
108         case Mode::UNKNOWN:
109             printf("Unknown ");
110             break;
111     }
112
113     printf("> ");
114     fflush(stdout);
115 }
116
117 const char *popupResultToString(ppm_popup_result_e result)
118 {
119     switch (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:
125             return "DENY_ONCE";
126     }
127
128     return "UNKNOWN";
129 }
130
131 void printClientHelp()
132 {
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");
137     printf("q               quit\n");
138     printPrompt(Mode::CLIENT);
139 }
140
141 void printClientError(ppm_error_e error)
142 {
143     switch (error) {
144         case PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE:
145             break;
146         case PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR:
147             printf("I/O error\n");
148             break;
149         case  PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER:
150             printf("Invalid parameters\n");
151             break;
152         case PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY:
153             printf("Out of memory\n");
154             break;
155         case PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN:
156             printf("Unknown error\n");
157             break;
158         case PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS:
159             printf("Operation already in progress\n");
160             break;
161     }
162 }
163
164 void printClientCheckResult(ppm_check_result_e result)
165 {
166     printf("res: ");
167
168     switch (result) {
169         case  PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW:
170             printf("ALLOW\n");
171             break;
172         case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY:
173             printf("DENY\n");
174             break;
175         case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ASK:
176             printf("ASK\n");
177             break;
178     }
179
180     printPrompt(Mode::CLIENT);
181 }
182
183 void printAppHelp(const char *programName)
184 {
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");
200 }
201
202 void printServerHelp()
203 {
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");
211     printf("q                       quit\n");
212     printPrompt(Mode::SERVER);
213 }
214
215 struct ServerSimulator : public IServerCallbacks {
216
217     ServerSimulator()
218     : m_channel(nullptr)
219     {}
220
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());
224     }
225
226     static gboolean serverGIOCallback(GIOChannel *src, GIOCondition cond, gpointer data) {
227         ServerSimulator *handle = static_cast<ServerSimulator *>(data);
228
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);
232
233         handle->m_channel->process(fd, isErrorCondition(cond) ? 0 : events);
234
235         return TRUE;
236     }
237
238     virtual void updateConnection(ConnectionFd fd, int mask) {
239         if (fd == -1) {
240             printf("ERROR: updateConnection() called with fd == -1\n");
241             return;
242         }
243
244         GIOCondition gio_condition = maskToGIOCondition(mask);
245
246         auto it = m_connections.find(fd);
247
248         if (mask == 0) {
249             if (it == m_connections.end())
250                 return;
251
252             auto &connection = it->second;
253
254             g_source_remove(connection.watch_id);
255             g_io_channel_unref(connection.gio_channel);
256
257             m_connections.erase(fd);
258             m_requests.erase(fd);
259
260             printf("Connection for fd: %d has been closed\n", fd);
261             printPrompt(Mode::SERVER);
262             return;
263         }
264
265         if (it == m_connections.end()) {
266             GIOChannel *gio_channel = g_io_channel_unix_new(fd);
267
268             g_io_channel_set_encoding (gio_channel, nullptr, nullptr);
269             g_io_channel_set_close_on_unref(gio_channel, FALSE);
270
271             guint watch_id = g_io_add_watch(gio_channel,
272                                       makeCondition(gio_condition),
273                                       serverGIOCallback,
274                                       this);
275
276             m_connections.insert({ fd, Connection{gio_channel, gio_condition, watch_id }});
277
278             return;
279         }
280
281         auto &connection = it->second;
282
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),
288                                                  serverGIOCallback,
289                                                  this);
290         }
291     }
292
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);
297     }
298
299     void addRequest(ConnectionFd fd, RequestId id, const Privilege &privilege) {
300         auto &s = m_requests[fd];
301
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);
305             return;
306         }
307
308         s.insert(ServerPrivilegeRequest(id, privilege));
309     }
310
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);
315             return;
316         }
317
318         auto &s = fdIt->second;
319
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);
323             return;
324         }
325
326         if (m_channel) {
327             m_channel->popupResponse(fd, id, response);
328
329             // delete request
330             s.erase(reqIt);
331             if (s.empty()) {
332                 m_requests.erase(fdIt);
333             }
334         }
335     }
336
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());
342             }
343         }
344     }
345
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);
351             return;
352         }
353
354         m_requests.erase(fd);
355         m_channel->process(fd, 0);
356     }
357
358     void showConnections() {
359         printf("fd\n");
360         for (const auto &con : m_connections) {
361             printf("%d\n", con.first);
362         }
363     }
364
365     void setServerChannel(ServerChannel *ptr) {
366         m_channel = ptr;
367     }
368
369 private:
370     ServerChannel *m_channel;
371     std::map<ConnectionFd, Connection> m_connections;
372     std::map<ConnectionFd,  PrivilegeRequestSet> m_requests;
373 };
374
375 class AppContext {
376 public:
377     AppContext()
378     : m_mainloop(g_main_loop_new(nullptr, FALSE))
379     , m_serverSimulator(nullptr)
380     , m_channel(nullptr)
381     , m_mode(Mode::UNKNOWN)
382     {}
383
384     ~AppContext() {
385         g_main_loop_unref(m_mainloop);
386
387         if (m_channel != nullptr) {
388             delete m_channel;
389         }
390     }
391
392     void initialize(int argc, char **argv) {
393         if (argc < 1) {
394             throw std::logic_error("AppContext::initialize called with argc < 1");
395         }
396
397         int c;
398         opterr = 0;
399         while ((c = getopt (argc, argv, "sch")) != -1) {
400             switch (c) {
401                 case 's':
402                     m_mode = Mode::SERVER;
403                     break;
404                 case 'c':
405                     m_mode = Mode::CLIENT;
406                     break;
407                 case 'h':
408                 default:
409                     break;
410             }
411         }
412
413         switch (m_mode) {
414             case Mode::SERVER:
415                 m_serverSimulator = new ServerSimulator();
416                 m_channel = new ServerChannel(ServerCallbacksPtr(m_serverSimulator));
417                 m_serverSimulator->setServerChannel(m_channel);
418                 printServerHelp();
419                 break;
420             case Mode::CLIENT:
421                 printClientHelp();
422                 break;
423             case Mode::UNKNOWN:
424             default:
425                 printAppHelp(argv[0]);
426                 exit(EXIT_SUCCESS);
427         }
428
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);
432     }
433
434     void run() {
435         if (m_mode == Mode::UNKNOWN) {
436             throw std::logic_error("AppContext is not initialized");
437         }
438
439         g_main_loop_run(m_mainloop);
440     }
441
442 private:
443
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");
449             return;
450         }
451
452         printf("localId: %d privilege: \"%s\" ", request->m_id, privilege);
453
454         switch (cause) {
455             case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER:
456                 printf("popup response ANSWER: %s\n", popupResultToString(result));
457                 break;
458             case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR:
459                 printf("popup response ERROR cause: PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR\n");
460                 break;
461         }
462
463         if (result == PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_FOREVER && request->m_stopAppAfterDeny) {
464             g_main_loop_quit(request->m_mainLoop);
465         }
466
467         delete request;
468
469         printPrompt(Mode::CLIENT);
470     }
471
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;
479
480         switch (buffer[0]) {
481             case 'c':
482                 if (strlen(buffer) < 3) {
483                     printClientHelp();
484                     break;
485                 }
486                 err = static_cast<ppm_error_e>(ppm_check_privilege(&buffer[2], &result));
487                 if (err == PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
488                     printClientCheckResult(result);
489                 }
490                 break;
491             case 's':
492                 stopAppAfterDeny = true;
493             case 'r':
494                 if (strlen(buffer) < 3) {
495                     printClientHelp();
496                     break;
497                 }
498
499                 buffer[strlen(buffer)] = '\0'; // erase new line
500                 privilege = &buffer[2];
501
502                 printf("sending localId: %d privilege: \"%s\"\n", clientRequestId, privilege);
503
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) {
507                     delete request;
508                 }
509
510                 break;
511             case 'q':
512                 g_main_loop_quit(m_mainloop);
513                 return;
514             case 'h':
515             default:
516                 printClientHelp();
517                 return;
518         }
519
520         printClientError(err);
521         printPrompt(Mode::CLIENT);
522     }
523
524     void handleServer(const char *buffer) {
525         int res;
526         ConnectionFd fd;
527         RequestId requestId;
528
529         switch (buffer[0]) {
530             case 'l':
531                 m_serverSimulator->showConnections();
532                 break;
533             case 's':
534                 m_serverSimulator->showPrivilegeRequests();
535                 break;
536             case 'r':
537                 res = sscanf(&buffer[2], "%d %d", &fd, &requestId);
538                 if (res != 2) {
539                     printServerHelp();
540                     return;
541                 }
542                 switch (buffer[1]) {
543                     case 'a':
544                         m_serverSimulator->sendResponse(fd, requestId, ASKUSER_ALLOW_FOREVER);
545                         break;
546                     case 'd':
547                         m_serverSimulator->sendResponse(fd, requestId, ASKUSER_DENY_FOREVER);
548                         break;
549                     case 'o':
550                         m_serverSimulator->sendResponse(fd, requestId, ASKUSER_DENY_ONCE);
551                         break;
552                     default:
553                         printServerHelp();
554                         return;
555                 }
556                 break;
557             case 'c':
558                 res = sscanf(&buffer[1], "%d", &fd);
559                 if (res != 1) {
560                     printServerHelp();
561                     return;
562                 }
563                 m_serverSimulator->closeConnection(fd);
564                 return;
565             case 'h':
566                 printServerHelp();
567                 return;
568             case 'q':
569                 g_main_loop_quit(m_mainloop);
570                 return;
571         }
572
573         printPrompt(Mode::SERVER);
574     }
575
576     static gboolean mainLoopCallback(UNUSED GIOChannel *channel, UNUSED GIOCondition cond, gpointer user_data) {
577         char buffer[4096];
578
579         int ret = read(0, buffer, sizeof(buffer));
580         if (ret <= 0) {
581             printf("read from stdin failed\n");
582             exit(EXIT_FAILURE);
583         }
584
585         buffer[ret - 1] = '\0';
586
587         AppContext *ctx = static_cast<AppContext *>(user_data);
588         switch (ctx->m_mode) {
589             case Mode::CLIENT:
590                 ctx->handleClient(buffer);
591                 break;
592             case Mode::SERVER:
593                 ctx->handleServer(buffer);
594                 break;
595             case Mode::UNKNOWN:
596             default:
597                 printf("Mode not set, exiting.\n");
598                 exit(EXIT_FAILURE);
599         }
600
601         return TRUE;
602     }
603
604     GMainLoop *m_mainloop;
605     ServerSimulator *m_serverSimulator;
606     ServerChannel *m_channel;
607     Mode m_mode;
608 };
609
610 int main(int argc, char **argv)
611 {
612     AppContext appCtx;
613
614     try {
615         appCtx.initialize(argc, argv);
616         appCtx.run();
617     }
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());
621         exit(EXIT_FAILURE);
622     }
623     catch (const std::exception &e) {
624         printf("Standard exception occured during initialization: %s, exiting.\n", e.what());
625         exit(EXIT_FAILURE);
626     }
627     catch (...) {
628         printf("Unknwon exception occured during initialization, exiting.\n");
629         exit(EXIT_FAILURE);
630     }
631
632     return 0;
633 }