Fix gcc compiler warning with -Wformat-security.
[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 static bool agent_registered = false;
42 static DBusMessage *agent_message = NULL;
43 static struct {
44         DBusMessage *reply;
45         DBusMessageIter iter;
46         DBusMessageIter dict;
47 } agent_reply = { };
48
49 #define AGENT_INTERFACE      "net.connman.Agent"
50
51 static void request_input_ssid_return(char *input);
52 static void request_input_passphrase_return(char *input);
53 static void request_input_string_return(char *input);
54
55 static int confirm_input(char *input)
56 {
57         int i;
58
59         if (input == NULL)
60                 return -1;
61
62         for (i = 0; input[i] != '\0'; i++)
63                 if (isspace(input[i]) == 0)
64                         break;
65
66         if (strcasecmp(&input[i], "yes") == 0 ||
67                         strcasecmp(&input[i], "y") == 0)
68                 return 1;
69
70         if (strcasecmp(&input[i], "no") == 0 ||
71                         strcasecmp(&input[i], "n") == 0)
72                 return 0;
73
74         return -1;
75 }
76
77 static char *strip_path(char *path)
78 {
79         char *name = strrchr(path, '/');
80         if (name != NULL)
81                 name++;
82         else
83                 name = path;
84
85         return name;
86 }
87
88 static char *agent_path(void)
89 {
90         static char *path = NULL;
91
92         if (path == NULL)
93                 path = g_strdup_printf("/net/connman/connmanctl%d", getpid());
94
95         return path;
96 }
97
98 static void pending_message_remove()
99 {
100         if (agent_message != NULL) {
101                 dbus_message_unref(agent_message);
102                 agent_message = NULL;
103         }
104
105         if (agent_reply.reply != NULL) {
106                 dbus_message_unref(agent_reply.reply);
107                 agent_reply.reply = NULL;
108         }
109 }
110
111 static void pending_command_complete(char *message)
112 {
113         __connmanctl_save_rl();
114
115         fprintf(stdout, "%s", message);
116
117         __connmanctl_redraw_rl();
118
119         if (__connmanctl_is_interactive() == true)
120                 __connmanctl_command_mode();
121         else
122                 __connmanctl_agent_mode("", NULL);
123 }
124
125 static DBusMessage *agent_release(DBusConnection *connection,
126                 DBusMessage *message, void *user_data)
127 {
128         g_dbus_unregister_interface(connection, agent_path(), AGENT_INTERFACE);
129         agent_registered = false;
130
131         pending_message_remove();
132         pending_command_complete("Agent unregistered by ConnMan\n");
133
134         if (__connmanctl_is_interactive() == false)
135                 __connmanctl_quit();
136
137         return dbus_message_new_method_return(message);
138 }
139
140 static DBusMessage *agent_cancel(DBusConnection *connection,
141                 DBusMessage *message, void *user_data)
142 {
143         pending_message_remove();
144         pending_command_complete("Agent request cancelled by ConnMan\n");
145
146         return dbus_message_new_method_return(message);
147 }
148
149 static DBusConnection *agent_connection = NULL;
150
151 static void request_browser_return(char *input)
152 {
153         switch (confirm_input(input)) {
154         case 1:
155                 g_dbus_send_reply(agent_connection, agent_message,
156                                 DBUS_TYPE_INVALID);
157                 break;
158         case 0:
159                 g_dbus_send_error(agent_connection, agent_message,
160                                 "net.connman.Agent.Error.Canceled", NULL);
161                 break;
162         default:
163                 return;
164         }
165
166         pending_message_remove();
167         pending_command_complete("");
168 }
169
170 static DBusMessage *agent_request_browser(DBusConnection *connection,
171                 DBusMessage *message, void *user_data)
172 {
173         DBusMessageIter iter;
174         char *service, *url;
175
176         dbus_message_iter_init(message, &iter);
177
178         dbus_message_iter_get_basic(&iter, &service);
179         dbus_message_iter_next(&iter);
180         dbus_message_iter_get_basic(&iter, &url);
181
182         __connmanctl_save_rl();
183         fprintf(stdout, "Agent RequestBrowser %s\n", strip_path(service));
184         fprintf(stdout, "  %s\n", url);
185         __connmanctl_redraw_rl();
186
187         agent_connection = connection;
188         agent_message = dbus_message_ref(message);
189         __connmanctl_agent_mode("Connected (yes/no)? ",
190                         request_browser_return);
191
192         return NULL;
193 }
194
195 static void report_error_return(char *input)
196 {
197         switch (confirm_input(input)) {
198         case 1:
199                 g_dbus_send_error(agent_connection, agent_message,
200                                 "net.connman.Agent.Error.Retry", NULL);
201                 break;
202         case 0:
203                 g_dbus_send_reply(agent_connection, agent_message,
204                                 DBUS_TYPE_INVALID);
205                 break;
206         default:
207                 return;
208         }
209
210         pending_message_remove();
211         pending_command_complete("");
212 }
213
214 static DBusMessage *agent_report_error(DBusConnection *connection,
215                 DBusMessage *message, void *user_data)
216 {
217         DBusMessageIter iter;
218         char *path, *service, *error;
219
220         dbus_message_iter_init(message, &iter);
221
222         dbus_message_iter_get_basic(&iter, &path);
223         service = strip_path(path);
224
225         dbus_message_iter_next(&iter);
226         dbus_message_iter_get_basic(&iter, &error);
227
228         __connmanctl_save_rl();
229         fprintf(stdout, "Agent ReportError %s\n", service);
230         fprintf(stdout, "  %s\n", error);
231         __connmanctl_redraw_rl();
232
233         agent_connection = connection;
234         agent_message = dbus_message_ref(message);
235         __connmanctl_agent_mode("Retry (yes/no)? ", report_error_return);
236
237         return NULL;
238 }
239
240 enum requestinput {
241         SSID                    = 0,
242         IDENTITY                = 1,
243         PASSPHRASE              = 2,
244         WPS                     = 3,
245         WISPR_USERNAME          = 4,
246         WISPR_PASSPHRASE        = 5,
247         REQUEST_INPUT_MAX       = 6,
248 };
249
250 static struct {
251         const char *attribute;
252         bool requested;
253         char *prompt;
254         connmanctl_input_func_t *func;
255 } agent_input[] = {
256         { "Name", false, "Hidden SSID name? ", request_input_ssid_return },
257         { "Identity", false, "EAP username? ", request_input_string_return },
258         { "Passphrase", false, "Passphrase? ",
259           request_input_passphrase_return },
260         { "WPS", false, "WPS PIN (empty line for pushbutton)? " ,
261           request_input_string_return },
262         { "Username", false, "WISPr username? ", request_input_string_return },
263         { "Password", false, "WISPr password? ", request_input_string_return },
264         { },
265 };
266
267 static void request_input_next(void)
268 {
269         int i;
270
271         for (i = 0; agent_input[i].attribute != NULL; i++) {
272                 if (agent_input[i].requested == true) {
273                         if(agent_input[i].func != NULL)
274                                 __connmanctl_agent_mode(agent_input[i].prompt,
275                                                 agent_input[i].func);
276                         else
277                                 agent_input[i].requested = false;
278                         return;
279                 }
280         }
281
282         dbus_message_iter_close_container(&agent_reply.iter,
283                         &agent_reply.dict);
284
285         g_dbus_send_message(agent_connection, agent_reply.reply);
286         agent_reply.reply = NULL;
287
288         pending_message_remove();
289         pending_command_complete("");
290 }
291
292 static void request_input_append(const char *attribute, char *value)
293 {
294         __connmanctl_dbus_append_dict_entry(&agent_reply.dict, attribute,
295                         DBUS_TYPE_STRING, &value);
296 }
297
298 static void request_input_ssid_return(char *input)
299 {
300         int len = 0;
301
302         if (input != NULL)
303                 len = strlen(input);
304
305         if (len > 0 && len <= 32) {
306                 agent_input[SSID].requested = false;
307                 request_input_append(agent_input[SSID].attribute, input);
308
309                 request_input_next();
310         }
311 }
312
313 static void request_input_passphrase_return(char *input)
314 {
315         /* TBD passphrase length checking */
316
317         if (input != NULL && strlen(input) > 0) {
318                 agent_input[PASSPHRASE].requested = false;
319                 request_input_append(agent_input[PASSPHRASE].attribute, input);
320
321                 agent_input[WPS].requested = false;
322
323                 request_input_next();
324         }
325 }
326
327 static void request_input_string_return(char *input)
328 {
329         int i;
330
331         for (i = 0; agent_input[i].attribute != NULL; i++) {
332                 if (agent_input[i].requested == true) {
333                         request_input_append(agent_input[i].attribute, input);
334                         agent_input[i].requested = false;
335                         break;
336                 }
337         }
338
339         request_input_next();
340 }
341
342 static DBusMessage *agent_request_input(DBusConnection *connection,
343                 DBusMessage *message, void *user_data)
344 {
345         DBusMessageIter iter, dict, entry, variant;
346         char *service, *str, *field;
347         DBusMessageIter dict_entry, field_entry, field_value;
348         char *argument, *value, *attr_type;
349
350         int i;
351
352         dbus_message_iter_init(message, &iter);
353
354         dbus_message_iter_get_basic(&iter, &str);
355         service = strip_path(str);
356
357         dbus_message_iter_next(&iter);
358         dbus_message_iter_recurse(&iter, &dict);
359
360         __connmanctl_save_rl();
361         fprintf(stdout, "Agent RequestInput %s\n", service);
362         __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
363         fprintf(stdout, "\n");
364         __connmanctl_redraw_rl();
365
366         dbus_message_iter_recurse(&iter, &dict);
367
368         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
369
370                 dbus_message_iter_recurse(&dict, &entry);
371
372                 dbus_message_iter_get_basic(&entry, &field);
373
374                 dbus_message_iter_next(&entry);
375
376                 dbus_message_iter_recurse(&entry, &variant);
377                 dbus_message_iter_recurse(&variant, &dict_entry);
378
379                 while (dbus_message_iter_get_arg_type(&dict_entry)
380                                 == DBUS_TYPE_DICT_ENTRY) {
381                         dbus_message_iter_recurse(&dict_entry, &field_entry);
382
383                         dbus_message_iter_get_basic(&field_entry, &argument);
384
385                         dbus_message_iter_next(&field_entry);
386
387                         dbus_message_iter_recurse(&field_entry, &field_value);
388
389                         dbus_message_iter_get_basic(&field_value, &value);
390
391                         if (strcmp(argument, "Type") == 0)
392                                 attr_type = g_strdup(value);
393
394                         dbus_message_iter_next(&dict_entry);
395                 }
396
397                 for (i = 0; agent_input[i].attribute != NULL; i++) {
398                         if (strcmp(field, agent_input[i].attribute) == 0) {
399                                 agent_input[i].requested = true;
400                                 break;
401                         }
402                 }
403
404                 g_free(attr_type);
405                 attr_type = NULL;
406
407                 dbus_message_iter_next(&dict);
408         }
409
410         agent_connection = connection;
411         agent_reply.reply = dbus_message_new_method_return(message);
412         dbus_message_iter_init_append(agent_reply.reply, &agent_reply.iter);
413
414         dbus_message_iter_open_container(&agent_reply.iter, DBUS_TYPE_ARRAY,
415                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
416                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
417                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &agent_reply.dict);
418
419         request_input_next();
420
421         return NULL;
422 }
423
424 static const GDBusMethodTable agent_methods[] = {
425         { GDBUS_METHOD("Release", NULL, NULL, agent_release) },
426         { GDBUS_METHOD("Cancel", NULL, NULL, agent_cancel) },
427         { GDBUS_ASYNC_METHOD("RequestBrowser",
428                                 GDBUS_ARGS({ "service", "o" },
429                                         { "url", "s" }),
430                                 NULL, agent_request_browser) },
431         { GDBUS_ASYNC_METHOD("ReportError",
432                                 GDBUS_ARGS({ "service", "o" },
433                                         { "error", "s" }),
434                                 NULL, agent_report_error) },
435         { GDBUS_ASYNC_METHOD("RequestInput",
436                                 GDBUS_ARGS({ "service", "o" },
437                                         { "fields", "a{sv}" }),
438                                 GDBUS_ARGS({ "fields", "a{sv}" }),
439                                 agent_request_input) },
440         { },
441 };
442
443 static int agent_register_return(DBusMessageIter *iter, const char *error,
444                 void *user_data)
445 {
446         DBusConnection *connection = user_data;
447
448         if (error != NULL) {
449                 g_dbus_unregister_interface(connection, agent_path(),
450                                 AGENT_INTERFACE);
451                 fprintf(stderr, "Error registering Agent: %s\n", error);
452                 return 0;
453         }
454
455         agent_registered = true;
456         fprintf(stdout, "Agent registered\n");
457
458         return -EINPROGRESS;
459 }
460
461 int __connmanctl_agent_register(DBusConnection *connection)
462 {
463         char *path = agent_path();
464         int result;
465
466         if (agent_registered == true) {
467                 fprintf(stderr, "Agent already registered\n");
468                 return -EALREADY;
469         }
470
471         if (g_dbus_register_interface(connection, path,
472                                         AGENT_INTERFACE, agent_methods,
473                                         NULL, NULL, connection,
474                                         NULL) == FALSE) {
475                 fprintf(stderr, "Error: Failed to register Agent callbacks\n");
476                 return 0;
477         }
478
479         result = __connmanctl_dbus_method_call(connection, "/",
480                         "net.connman.Manager", "RegisterAgent",
481                         agent_register_return, connection,
482                         DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
483
484         if (result != -EINPROGRESS) {
485                 g_dbus_unregister_interface(connection, agent_path(),
486                                 AGENT_INTERFACE);
487
488                 fprintf(stderr, "Error: Failed to register Agent\n");
489         }
490
491         return result;
492 }
493
494 static int agent_unregister_return(DBusMessageIter *iter, const char *error,
495                 void *user_data)
496 {
497         if (error != NULL) {
498                 fprintf(stderr, "Error unregistering Agent: %s\n", error);
499                 return 0;
500         }
501
502         agent_registered = false;
503         fprintf(stdout, "Agent unregistered\n");
504
505         return 0;
506 }
507
508 int __connmanctl_agent_unregister(DBusConnection *connection)
509 {
510         char *path = agent_path();
511         int result;
512
513         if (agent_registered == false) {
514                 fprintf(stderr, "Agent not registered\n");
515                 return -EALREADY;
516         }
517
518         g_dbus_unregister_interface(connection, agent_path(), AGENT_INTERFACE);
519
520         result = __connmanctl_dbus_method_call(connection, "/",
521                         "net.connman.Manager", "UnregisterAgent",
522                         agent_unregister_return, NULL,
523                         DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
524
525         if (result != -EINPROGRESS)
526                 fprintf(stderr, "Error: Failed to unregister Agent\n");
527
528         return result;
529 }