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