12b0a98439ff5ac6348c10292c3e2ed6ae0ca8c3
[platform/upstream/connman.git] / client / agent.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2013  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <stdbool.h>
32 #include <string.h>
33 #include <ctype.h>
34
35 #include <gdbus.h>
36
37 #include "input.h"
38 #include "dbus_helpers.h"
39 #include "agent.h"
40
41 #define AGENT_INTERFACE      "net.connman.Agent"
42 #define VPN_AGENT_INTERFACE  "net.connman.vpn.Agent"
43
44 static DBusConnection *agent_connection;
45
46 struct agent_input_data {
47         const char *attribute;
48         bool requested;
49         char *prompt;
50         connmanctl_input_func_t func;
51 };
52
53 struct agent_data {
54         struct agent_input_data *input;
55         char *interface;
56         bool registered;
57         DBusMessage *message;
58         DBusMessage *reply;
59         DBusMessageIter iter;
60         DBusMessageIter dict;
61         GDBusMethodFunction pending_function;
62 };
63
64 static void request_input_ssid_return(char *input, void *user_data);
65 static void request_input_passphrase_return(char *input, void *user_data);
66 static void request_input_string_return(char *input, void *user_data);
67
68 enum requestinput {
69         SSID                    = 0,
70         IDENTITY                = 1,
71         PASSPHRASE              = 2,
72         WPS                     = 3,
73         WISPR_USERNAME          = 4,
74         WISPR_PASSPHRASE        = 5,
75         REQUEST_INPUT_MAX       = 6,
76 };
77
78 static struct agent_input_data agent_input_handler[] = {
79         { "Name", false, "Hidden SSID name? ", request_input_ssid_return },
80         { "Identity", false, "EAP username? ", request_input_string_return },
81         { "Passphrase", false, "Passphrase? ",
82           request_input_passphrase_return },
83         { "WPS", false, "WPS PIN (empty line for pushbutton)? " ,
84           request_input_string_return },
85         { "Username", false, "WISPr username? ", request_input_string_return },
86         { "Password", false, "WISPr password? ", request_input_string_return },
87         { },
88 };
89
90 static struct agent_data agent_request = {
91         agent_input_handler,
92         AGENT_INTERFACE,
93 };
94
95 static struct agent_input_data vpnagent_input_handler[] = {
96         { "OpenConnect.Cookie", false, "OpenConnect Cookie? ",
97           request_input_string_return },
98         { "Username", false, "VPN username? ", request_input_string_return },
99         { "Password", false, "VPN password? ", request_input_string_return },
100         { },
101 };
102
103 static struct agent_data vpn_agent_request = {
104         vpnagent_input_handler,
105         VPN_AGENT_INTERFACE,
106 };
107
108 static int confirm_input(char *input)
109 {
110         int i;
111
112         if (input == NULL)
113                 return -1;
114
115         for (i = 0; input[i] != '\0'; i++)
116                 if (isspace(input[i]) == 0)
117                         break;
118
119         if (strcasecmp(&input[i], "yes") == 0 ||
120                         strcasecmp(&input[i], "y") == 0)
121                 return 1;
122
123         if (strcasecmp(&input[i], "no") == 0 ||
124                         strcasecmp(&input[i], "n") == 0)
125                 return 0;
126
127         return -1;
128 }
129
130 static char *strip_path(char *path)
131 {
132         char *name = strrchr(path, '/');
133         if (name != NULL)
134                 name++;
135         else
136                 name = path;
137
138         return name;
139 }
140
141 static char *agent_path(void)
142 {
143         static char *path = NULL;
144
145         if (path == NULL)
146                 path = g_strdup_printf("/net/connman/connmanctl%d", getpid());
147
148         return path;
149 }
150
151 static void pending_message_remove(struct agent_data *request)
152 {
153         if (request->message != NULL) {
154                 dbus_message_unref(request->message);
155                 request->message = NULL;
156         }
157
158         if (request->reply != NULL) {
159                 dbus_message_unref(request->reply);
160                 request->reply = NULL;
161         }
162 }
163
164 static void pending_command_complete(char *message)
165 {
166         struct agent_data *next_request = NULL;
167         DBusMessage *pending_message;
168         GDBusMethodFunction pending_function;
169
170         __connmanctl_save_rl();
171
172         fprintf(stdout, "%s", message);
173
174         __connmanctl_redraw_rl();
175
176         if (__connmanctl_is_interactive() == true)
177                 __connmanctl_command_mode();
178         else
179                 __connmanctl_agent_mode("", NULL, NULL);
180
181         if (agent_request.message != NULL)
182                 next_request = &agent_request;
183         else if (vpn_agent_request.message != NULL)
184                 next_request = &vpn_agent_request;
185
186         if (next_request == NULL)
187                 return;
188
189         pending_message = next_request->message;
190         pending_function = next_request->pending_function;
191         next_request->pending_function = NULL;
192
193         pending_function(agent_connection, next_request->message,
194                         next_request);
195
196         dbus_message_unref(pending_message);
197 }
198
199 static bool handle_message(DBusMessage *message, struct agent_data *request,
200                 GDBusMethodFunction function)
201 {
202         if (agent_request.pending_function == NULL &&
203                         vpn_agent_request.pending_function == NULL)
204                 return true;
205
206         request->message = dbus_message_ref(message);
207         request->pending_function = function;
208
209         return false;
210 }
211
212 static DBusMessage *agent_release(DBusConnection *connection,
213                 DBusMessage *message, void *user_data)
214 {
215         struct agent_data *request = user_data;
216
217         if (handle_message(message, request, agent_release) == false)
218                 return NULL;
219
220         g_dbus_unregister_interface(connection, agent_path(),
221                         request->interface);
222         request->registered = false;
223
224         pending_message_remove(request);
225
226         if (strcmp(request->interface, AGENT_INTERFACE) == 0)
227                 pending_command_complete("Agent unregistered by ConnMan\n");
228         else
229                 pending_command_complete("VPN Agent unregistered by ConnMan "
230                                 "VPNd\n");
231
232         if (__connmanctl_is_interactive() == false)
233                 __connmanctl_quit();
234
235         return dbus_message_new_method_return(message);
236 }
237
238 static DBusMessage *agent_cancel(DBusConnection *connection,
239                 DBusMessage *message, void *user_data)
240 {
241         struct agent_data *request = user_data;
242
243         if (handle_message(message, request, agent_cancel) == false)
244                 return NULL;
245
246         pending_message_remove(request);
247
248         if (strcmp(request->interface, AGENT_INTERFACE) == 0)
249                 pending_command_complete("Agent request cancelled by "
250                                 "ConnMan\n");
251         else
252                 pending_command_complete("VPN Agent request cancelled by "
253                                 "ConnMan VPNd\n");
254
255         return dbus_message_new_method_return(message);
256 }
257
258 static void request_browser_return(char *input, void *user_data)
259 {
260         struct agent_data *request = user_data;
261
262         switch (confirm_input(input)) {
263         case 1:
264                 g_dbus_send_reply(agent_connection, request->message,
265                                 DBUS_TYPE_INVALID);
266                 break;
267         case 0:
268                 g_dbus_send_error(agent_connection, request->message,
269                                 "net.connman.Agent.Error.Canceled", NULL);
270                 break;
271         default:
272                 return;
273         }
274
275         pending_message_remove(request);
276         pending_command_complete("");
277 }
278
279 static DBusMessage *agent_request_browser(DBusConnection *connection,
280                 DBusMessage *message, void *user_data)
281 {
282         struct agent_data *request = user_data;
283         DBusMessageIter iter;
284         char *service, *url;
285
286         if (handle_message(message, request, agent_request_browser) == false)
287                 return NULL;
288
289         dbus_message_iter_init(message, &iter);
290
291         dbus_message_iter_get_basic(&iter, &service);
292         dbus_message_iter_next(&iter);
293         dbus_message_iter_get_basic(&iter, &url);
294
295         __connmanctl_save_rl();
296         fprintf(stdout, "Agent RequestBrowser %s\n", strip_path(service));
297         fprintf(stdout, "  %s\n", url);
298         __connmanctl_redraw_rl();
299
300         request->message = dbus_message_ref(message);
301         __connmanctl_agent_mode("Connected (yes/no)? ",
302                         request_browser_return, request);
303
304         return NULL;
305 }
306
307 static void report_error_return(char *input, void *user_data)
308 {
309         struct agent_data *request = user_data;
310
311         switch (confirm_input(input)) {
312         case 1:
313                 if (strcmp(request->interface, AGENT_INTERFACE) == 0)
314                         g_dbus_send_error(agent_connection, request->message,
315                                         "net.connman.Agent.Error.Retry", NULL);
316                 else
317                         g_dbus_send_error(agent_connection, request->message,
318                                         "net.connman.vpn.Agent.Error.Retry",
319                                         NULL);
320                 break;
321         case 0:
322                 g_dbus_send_reply(agent_connection, request->message,
323                                 DBUS_TYPE_INVALID);
324                 break;
325         default:
326                 return;
327         }
328
329         pending_message_remove(request);
330         pending_command_complete("");
331 }
332
333 static DBusMessage *agent_report_error(DBusConnection *connection,
334                 DBusMessage *message, void *user_data)
335 {
336         struct agent_data *request = user_data;
337         DBusMessageIter iter;
338         char *path, *service, *error;
339
340         if (handle_message(message, request, agent_report_error) == false)
341                 return NULL;
342
343         dbus_message_iter_init(message, &iter);
344
345         dbus_message_iter_get_basic(&iter, &path);
346         service = strip_path(path);
347
348         dbus_message_iter_next(&iter);
349         dbus_message_iter_get_basic(&iter, &error);
350
351         __connmanctl_save_rl();
352         if (strcmp(request->interface, AGENT_INTERFACE) == 0)
353                 fprintf(stdout, "Agent ReportError %s\n", service);
354         else
355                 fprintf(stdout, "VPN Agent ReportError %s\n", service);
356         fprintf(stdout, "  %s\n", error);
357         __connmanctl_redraw_rl();
358
359         request->message = dbus_message_ref(message);
360         __connmanctl_agent_mode("Retry (yes/no)? ", report_error_return,
361                         request);
362
363         return NULL;
364 }
365
366 static void request_input_next(struct agent_data *request)
367 {
368         int i;
369
370         for (i = 0; request->input[i].attribute != NULL; i++) {
371                 if (request->input[i].requested == true) {
372                         if(request->input[i].func != NULL)
373                                 __connmanctl_agent_mode(request->input[i].prompt,
374                                                 request->input[i].func,
375                                                 request);
376                         else
377                                 request->input[i].requested = false;
378                         return;
379                 }
380         }
381
382         dbus_message_iter_close_container(&request->iter, &request->dict);
383
384         g_dbus_send_message(agent_connection, request->reply);
385         request->reply = NULL;
386
387         pending_message_remove(request);
388         pending_command_complete("");
389
390         __connmanctl_redraw_rl();
391 }
392
393 static void request_input_append(struct agent_data *request,
394                 const char *attribute, char *value)
395 {
396         __connmanctl_dbus_append_dict_entry(&request->dict, attribute,
397                         DBUS_TYPE_STRING, &value);
398 }
399
400 static void request_input_ssid_return(char *input,
401                 void *user_data)
402 {
403         struct agent_data *request = user_data;
404         int len = 0;
405
406         if (input != NULL)
407                 len = strlen(input);
408
409         if (len > 0 && len <= 32) {
410                 request->input[SSID].requested = false;
411                 request_input_append(request, request->input[SSID].attribute,
412                                 input);
413
414                 request_input_next(request);
415         }
416 }
417
418 static void request_input_passphrase_return(char *input, void *user_data)
419 {
420         struct agent_data *request = user_data;
421         int len = 0;
422
423         /* TBD passphrase length checking */
424
425         if (input != NULL)
426                 len = strlen(input);
427
428         if (len == 0 && request->input[WPS].requested == false)
429                 return;
430
431         request->input[PASSPHRASE].requested = false;
432
433         if (len > 0) {
434                 request_input_append(request,
435                                 request->input[PASSPHRASE].attribute, input);
436
437                 request->input[WPS].requested = false;
438         }
439
440         request_input_next(request);
441 }
442
443 static void request_input_string_return(char *input, void *user_data)
444 {
445         struct agent_data *request = user_data;
446         int i;
447
448         for (i = 0; request->input[i].attribute != NULL; i++) {
449                 if (request->input[i].requested == true) {
450                         request_input_append(request,
451                                         request->input[i].attribute, input);
452                         request->input[i].requested = false;
453                         break;
454                 }
455         }
456
457         request_input_next(request);
458 }
459
460 static DBusMessage *agent_request_input(DBusConnection *connection,
461                 DBusMessage *message, void *user_data)
462 {
463         struct agent_data *request = user_data;
464         DBusMessageIter iter, dict, entry, variant;
465         char *service, *str, *field;
466         DBusMessageIter dict_entry, field_entry, field_value;
467         char *argument, *value, *attr_type;
468
469         int i;
470
471         if (handle_message(message, request, agent_request_input) == false)
472                 return NULL;
473
474         dbus_message_iter_init(message, &iter);
475
476         dbus_message_iter_get_basic(&iter, &str);
477         service = strip_path(str);
478
479         dbus_message_iter_next(&iter);
480         dbus_message_iter_recurse(&iter, &dict);
481
482         __connmanctl_save_rl();
483         if (strcmp(request->interface, AGENT_INTERFACE) == 0)
484                 fprintf(stdout, "Agent RequestInput %s\n", service);
485         else
486                 fprintf(stdout, "VPN Agent RequestInput %s\n", service);
487         __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
488         fprintf(stdout, "\n");
489
490         dbus_message_iter_recurse(&iter, &dict);
491
492         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
493
494                 dbus_message_iter_recurse(&dict, &entry);
495
496                 dbus_message_iter_get_basic(&entry, &field);
497
498                 dbus_message_iter_next(&entry);
499
500                 dbus_message_iter_recurse(&entry, &variant);
501                 dbus_message_iter_recurse(&variant, &dict_entry);
502
503                 while (dbus_message_iter_get_arg_type(&dict_entry)
504                                 == DBUS_TYPE_DICT_ENTRY) {
505                         dbus_message_iter_recurse(&dict_entry, &field_entry);
506
507                         dbus_message_iter_get_basic(&field_entry, &argument);
508
509                         dbus_message_iter_next(&field_entry);
510
511                         dbus_message_iter_recurse(&field_entry, &field_value);
512
513                         if (strcmp(argument, "Type") == 0) {
514                                 dbus_message_iter_get_basic(&field_value,
515                                                 &value);
516                                 attr_type = g_strdup(value);
517                         }
518
519                         dbus_message_iter_next(&dict_entry);
520                 }
521
522                 for (i = 0; request->input[i].attribute != NULL; i++) {
523                         if (strcmp(field, request->input[i].attribute) == 0) {
524                                 request->input[i].requested = true;
525                                 break;
526                         }
527                 }
528
529                 g_free(attr_type);
530                 attr_type = NULL;
531
532                 dbus_message_iter_next(&dict);
533         }
534
535         request->reply = dbus_message_new_method_return(message);
536         dbus_message_iter_init_append(request->reply, &request->iter);
537
538         dbus_message_iter_open_container(&request->iter, DBUS_TYPE_ARRAY,
539                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
540                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
541                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
542                         &request->dict);
543
544         request_input_next(request);
545
546         return NULL;
547 }
548
549 static const GDBusMethodTable agent_methods[] = {
550         { GDBUS_ASYNC_METHOD("Release", NULL, NULL, agent_release) },
551         { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL, agent_cancel) },
552         { GDBUS_ASYNC_METHOD("RequestBrowser",
553                                 GDBUS_ARGS({ "service", "o" },
554                                         { "url", "s" }),
555                                 NULL, agent_request_browser) },
556         { GDBUS_ASYNC_METHOD("ReportError",
557                                 GDBUS_ARGS({ "service", "o" },
558                                         { "error", "s" }),
559                                 NULL, agent_report_error) },
560         { GDBUS_ASYNC_METHOD("RequestInput",
561                                 GDBUS_ARGS({ "service", "o" },
562                                         { "fields", "a{sv}" }),
563                                 GDBUS_ARGS({ "fields", "a{sv}" }),
564                                 agent_request_input) },
565         { },
566 };
567
568 static int agent_register_return(DBusMessageIter *iter, const char *error,
569                 void *user_data)
570 {
571         DBusConnection *connection = user_data;
572
573         if (error != NULL) {
574                 g_dbus_unregister_interface(connection, agent_path(),
575                                 AGENT_INTERFACE);
576                 fprintf(stderr, "Error registering Agent: %s\n", error);
577                 return 0;
578         }
579
580         agent_request.registered = true;
581         fprintf(stdout, "Agent registered\n");
582
583         return -EINPROGRESS;
584 }
585
586 int __connmanctl_agent_register(DBusConnection *connection)
587 {
588         char *path = agent_path();
589         int result;
590
591         if (agent_request.registered == true) {
592                 fprintf(stderr, "Agent already registered\n");
593                 return -EALREADY;
594         }
595
596         agent_connection = connection;
597
598         if (g_dbus_register_interface(connection, path,
599                                         AGENT_INTERFACE, agent_methods,
600                                         NULL, NULL, &agent_request,
601                                         NULL) == FALSE) {
602                 fprintf(stderr, "Error: Failed to register Agent callbacks\n");
603                 return 0;
604         }
605
606         result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
607                         CONNMAN_PATH, "net.connman.Manager", "RegisterAgent",
608                         agent_register_return, connection,
609                         DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
610
611         if (result != -EINPROGRESS) {
612                 g_dbus_unregister_interface(connection, agent_path(),
613                                 AGENT_INTERFACE);
614
615                 fprintf(stderr, "Error: Failed to register Agent\n");
616         }
617
618         return result;
619 }
620
621 static int agent_unregister_return(DBusMessageIter *iter, const char *error,
622                 void *user_data)
623 {
624         if (error != NULL) {
625                 fprintf(stderr, "Error unregistering Agent: %s\n", error);
626                 return 0;
627         }
628
629         agent_request.registered = false;
630         fprintf(stdout, "Agent unregistered\n");
631
632         return 0;
633 }
634
635 int __connmanctl_agent_unregister(DBusConnection *connection)
636 {
637         char *path = agent_path();
638         int result;
639
640         if (agent_request.registered == false) {
641                 fprintf(stderr, "Agent not registered\n");
642                 return -EALREADY;
643         }
644
645         g_dbus_unregister_interface(connection, agent_path(), AGENT_INTERFACE);
646
647         result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
648                         CONNMAN_PATH, "net.connman.Manager", "UnregisterAgent",
649                         agent_unregister_return, NULL,
650                         DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
651
652         if (result != -EINPROGRESS)
653                 fprintf(stderr, "Error: Failed to unregister Agent\n");
654
655         return result;
656 }
657
658 static const GDBusMethodTable vpn_agent_methods[] = {
659         { GDBUS_ASYNC_METHOD("Release", NULL, NULL, agent_release) },
660         { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL, agent_cancel) },
661         { GDBUS_ASYNC_METHOD("ReportError",
662                                 GDBUS_ARGS({ "service", "o" },
663                                         { "error", "s" }),
664                                 NULL, agent_report_error) },
665         { GDBUS_ASYNC_METHOD("RequestInput",
666                                 GDBUS_ARGS({ "service", "o" },
667                                         { "fields", "a{sv}" }),
668                                 GDBUS_ARGS({ "fields", "a{sv}" }),
669                                 agent_request_input) },
670         { },
671 };
672
673 static int vpn_agent_register_return(DBusMessageIter *iter, const char *error,
674                 void *user_data)
675 {
676         DBusConnection *connection = user_data;
677
678         if (error != NULL) {
679                 g_dbus_unregister_interface(connection, agent_path(),
680                                 VPN_AGENT_INTERFACE);
681                 fprintf(stderr, "Error registering VPN Agent: %s\n", error);
682                 return 0;
683         }
684
685         vpn_agent_request.registered = true;
686         fprintf(stdout, "VPN Agent registered\n");
687
688         return -EINPROGRESS;
689 }
690
691 int __connmanctl_vpn_agent_register(DBusConnection *connection)
692 {
693         char *path = agent_path();
694         int result;
695
696         if (vpn_agent_request.registered == true) {
697                 fprintf(stderr, "VPN Agent already registered\n");
698                 return -EALREADY;
699         }
700
701         agent_connection = connection;
702
703         if (g_dbus_register_interface(connection, path,
704                                         VPN_AGENT_INTERFACE, vpn_agent_methods,
705                                         NULL, NULL, &vpn_agent_request,
706                                         NULL) == FALSE) {
707                 fprintf(stderr, "Error: Failed to register VPN Agent "
708                                 "callbacks\n");
709                 return 0;
710         }
711
712         result = __connmanctl_dbus_method_call(connection, VPN_SERVICE,
713                         VPN_PATH, "net.connman.vpn.Manager", "RegisterAgent",
714                         vpn_agent_register_return, connection,
715                         DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
716
717         if (result != -EINPROGRESS) {
718                 g_dbus_unregister_interface(connection, agent_path(),
719                                 VPN_AGENT_INTERFACE);
720
721                 fprintf(stderr, "Error: Failed to register VPN Agent\n");
722         }
723
724         return result;
725 }
726
727 static int vpn_agent_unregister_return(DBusMessageIter *iter,
728                 const char *error, void *user_data)
729 {
730         if (error != NULL) {
731                 fprintf(stderr, "Error unregistering VPN Agent: %s\n", error);
732                 return 0;
733         }
734
735         vpn_agent_request.registered = false;
736         fprintf(stdout, "VPN Agent unregistered\n");
737
738         return 0;
739 }
740
741 int __connmanctl_vpn_agent_unregister(DBusConnection *connection)
742 {
743         char *path = agent_path();
744         int result;
745
746         if (vpn_agent_request.registered == false) {
747                 fprintf(stderr, "VPN Agent not registered\n");
748                 return -EALREADY;
749         }
750
751         g_dbus_unregister_interface(connection, agent_path(),
752                         VPN_AGENT_INTERFACE);
753
754         result = __connmanctl_dbus_method_call(connection, VPN_SERVICE,
755                         VPN_PATH, "net.connman.vpn.Manager", "UnregisterAgent",
756                         vpn_agent_unregister_return, NULL,
757                         DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
758
759         if (result != -EINPROGRESS)
760                 fprintf(stderr, "Error: Failed to unregister VPN Agent\n");
761
762         return result;
763 }