9ccba72e0224307013b4902e4dd6d1defb792b36
[platform/upstream/connman.git] / src / agent-connman.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012  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 version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <gdbus.h>
31 #include <connman/agent.h>
32 #include <connman/setting.h>
33 #include <connman/service.h>
34
35 #include "connman.h"
36
37 static connman_bool_t check_reply_has_dict(DBusMessage *reply)
38 {
39         const char *signature = DBUS_TYPE_ARRAY_AS_STRING
40                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
41                 DBUS_TYPE_STRING_AS_STRING
42                 DBUS_TYPE_VARIANT_AS_STRING
43                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
44
45         if (dbus_message_has_signature(reply, signature) == TRUE)
46                 return TRUE;
47
48         connman_warn("Reply %s to %s from %s has wrong signature %s",
49                         signature,
50                         dbus_message_get_interface(reply),
51                         dbus_message_get_sender(reply),
52                         dbus_message_get_signature(reply));
53
54         return FALSE;
55 }
56
57 struct request_input_reply {
58         struct connman_service *service;
59         authentication_cb_t callback;
60         void *user_data;
61 };
62
63 static void request_input_passphrase_reply(DBusMessage *reply, void *user_data)
64 {
65         struct request_input_reply *passphrase_reply = user_data;
66         connman_bool_t values_received = FALSE;
67         connman_bool_t wps = FALSE;
68         const char *error = NULL;
69         char *identity = NULL;
70         char *passphrase = NULL;
71         char *wpspin = NULL;
72         char *key;
73         char *name = NULL;
74         int name_len = 0;
75         DBusMessageIter iter, dict;
76
77         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
78                 error = dbus_message_get_error_name(reply);
79                 goto done;
80         }
81
82         if (check_reply_has_dict(reply) == FALSE)
83                 goto done;
84
85         values_received = TRUE;
86
87         dbus_message_iter_init(reply, &iter);
88         dbus_message_iter_recurse(&iter, &dict);
89         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
90                 DBusMessageIter entry, value;
91
92                 dbus_message_iter_recurse(&dict, &entry);
93                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
94                         break;
95
96                 dbus_message_iter_get_basic(&entry, &key);
97
98                 if (g_str_equal(key, "Identity")) {
99                         dbus_message_iter_next(&entry);
100                         if (dbus_message_iter_get_arg_type(&entry)
101                                                         != DBUS_TYPE_VARIANT)
102                                 break;
103                         dbus_message_iter_recurse(&entry, &value);
104                         dbus_message_iter_get_basic(&value, &identity);
105
106                 } else if (g_str_equal(key, "Passphrase")) {
107                         dbus_message_iter_next(&entry);
108                         if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
109                                 break;
110                         dbus_message_iter_recurse(&entry, &value);
111                         dbus_message_iter_get_basic(&value, &passphrase);
112
113                 } else if (g_str_equal(key, "WPS")) {
114                         wps = TRUE;
115
116                         dbus_message_iter_next(&entry);
117                         if (dbus_message_iter_get_arg_type(&entry)
118                                                         != DBUS_TYPE_VARIANT)
119                                 break;
120                         dbus_message_iter_recurse(&entry, &value);
121                         dbus_message_iter_get_basic(&value, &wpspin);
122                         break;
123                 } else if (g_str_equal(key, "Name")) {
124                         dbus_message_iter_next(&entry);
125                         if (dbus_message_iter_get_arg_type(&entry)
126                                                         != DBUS_TYPE_VARIANT)
127                                 break;
128                         dbus_message_iter_recurse(&entry, &value);
129                         dbus_message_iter_get_basic(&value, &name);
130                         name_len = strlen(name);
131                 } else if (g_str_equal(key, "SSID")) {
132                         dbus_message_iter_next(&entry);
133                         if (dbus_message_iter_get_arg_type(&entry)
134                                                         != DBUS_TYPE_VARIANT)
135                                 break;
136                         dbus_message_iter_recurse(&entry, &value);
137                         if (dbus_message_iter_get_arg_type(&value)
138                                                         != DBUS_TYPE_VARIANT)
139                                 break;
140                         if (dbus_message_iter_get_element_type(&value)
141                                                         != DBUS_TYPE_VARIANT)
142                                 break;
143                         dbus_message_iter_get_fixed_array(&value, &name,
144                                                         &name_len);
145                 }
146                 dbus_message_iter_next(&dict);
147         }
148
149 done:
150         passphrase_reply->callback(passphrase_reply->service, values_received,
151                                 name, name_len,
152                                 identity, passphrase,
153                                 wps, wpspin, error,
154                                 passphrase_reply->user_data);
155         g_free(passphrase_reply);
156 }
157
158 static void request_input_append_alternates(DBusMessageIter *iter,
159                                                         void *user_data)
160 {
161         const char *str = user_data;
162         char **alternates, **alternative;
163
164         if (str == NULL)
165                 return;
166
167         alternates = g_strsplit(str, ",", 0);
168         if (alternates == NULL)
169                 return;
170
171         for (alternative = alternates; *alternative != NULL; alternative++)
172                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
173                                                                 alternative);
174
175         g_strfreev(alternates);
176 }
177
178 static void request_input_append_identity(DBusMessageIter *iter,
179                                                         void *user_data)
180 {
181         char *str = "string";
182
183         connman_dbus_dict_append_basic(iter, "Type",
184                                 DBUS_TYPE_STRING, &str);
185         str = "mandatory";
186         connman_dbus_dict_append_basic(iter, "Requirement",
187                                 DBUS_TYPE_STRING, &str);
188 }
189
190 static void request_input_append_passphrase(DBusMessageIter *iter,
191                                                         void *user_data)
192 {
193         struct connman_service *service = user_data;
194         char *value;
195         const char *phase2;
196
197         switch (__connman_service_get_security(service)) {
198         case CONNMAN_SERVICE_SECURITY_WEP:
199                 value = "wep";
200                 break;
201         case CONNMAN_SERVICE_SECURITY_PSK:
202                 value = "psk";
203                 break;
204         case CONNMAN_SERVICE_SECURITY_8021X:
205                 phase2 = __connman_service_get_phase2(service);
206
207                 if (phase2 != NULL && (
208                                 g_str_has_suffix(phase2, "GTC") == TRUE ||
209                                 g_str_has_suffix(phase2, "OTP") == TRUE))
210                         value = "response";
211                 else
212                         value = "passphrase";
213
214                 break;
215         default:
216                 value = "string";
217                 break;
218         }
219         connman_dbus_dict_append_basic(iter, "Type",
220                                 DBUS_TYPE_STRING, &value);
221         value = "mandatory";
222         connman_dbus_dict_append_basic(iter, "Requirement",
223                                 DBUS_TYPE_STRING, &value);
224
225         if (__connman_service_wps_enabled(service) == TRUE) {
226                 connman_dbus_dict_append_array(iter, "Alternates",
227                                         DBUS_TYPE_STRING,
228                                         request_input_append_alternates,
229                                         "WPS");
230         }
231 }
232
233 static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
234 {
235         const char *str = "wpspin";
236
237         connman_dbus_dict_append_basic(iter, "Type",
238                                 DBUS_TYPE_STRING, &str);
239         str = "alternate";
240         connman_dbus_dict_append_basic(iter, "Requirement",
241                                 DBUS_TYPE_STRING, &str);
242 }
243
244 static void request_input_append_name(DBusMessageIter *iter, void *user_data)
245 {
246         const char *str = "string";
247
248         connman_dbus_dict_append_basic(iter, "Type",
249                                 DBUS_TYPE_STRING, &str);
250         str = "mandatory";
251         connman_dbus_dict_append_basic(iter, "Requirement",
252                                 DBUS_TYPE_STRING, &str);
253         connman_dbus_dict_append_array(iter, "Alternates",
254                                 DBUS_TYPE_STRING,
255                                 request_input_append_alternates,
256                                 "SSID");
257 }
258
259 static void request_input_append_ssid(DBusMessageIter *iter, void *user_data)
260 {
261         const char *str = "ssid";
262
263         connman_dbus_dict_append_basic(iter, "Type",
264                                 DBUS_TYPE_STRING, &str);
265         str = "alternate";
266         connman_dbus_dict_append_basic(iter, "Requirement",
267                                 DBUS_TYPE_STRING, &str);
268 }
269
270 static void request_input_append_password(DBusMessageIter *iter,
271                                                         void *user_data)
272 {
273         char *str = "passphrase";
274
275         connman_dbus_dict_append_basic(iter, "Type",
276                                 DBUS_TYPE_STRING, &str);
277         str = "mandatory";
278         connman_dbus_dict_append_basic(iter, "Requirement",
279                                 DBUS_TYPE_STRING, &str);
280 }
281
282 struct previous_passphrase_data {
283         const char *passphrase;
284         const char *type;
285 };
286
287 static void request_input_append_previouspassphrase(DBusMessageIter *iter,
288                                                         void *user_data)
289 {
290         struct previous_passphrase_data *data = user_data;
291         const char *requirement = "informational";
292
293         connman_dbus_dict_append_basic(iter, "Type",
294                                 DBUS_TYPE_STRING, &data->type);
295
296         connman_dbus_dict_append_basic(iter, "Requirement",
297                                 DBUS_TYPE_STRING, &requirement);
298
299         connman_dbus_dict_append_basic(iter, "Value",
300                                 DBUS_TYPE_STRING, &data->passphrase);
301 }
302
303 static void previous_passphrase_handler(DBusMessageIter *iter,
304                                         struct connman_service *service)
305 {
306         enum connman_service_security security;
307         struct previous_passphrase_data data;
308         struct connman_network *network;
309
310         network = __connman_service_get_network(service);
311         data.passphrase = connman_network_get_string(network, "WiFi.PinWPS");
312
313         if (connman_network_get_bool(network, "WiFi.UseWPS") == TRUE &&
314                                                 data.passphrase != NULL) {
315                 data.type = "wpspin";
316         } else {
317                 data.passphrase = __connman_service_get_passphrase(service);
318                 if (data.passphrase == NULL)
319                         return;
320
321                 security = __connman_service_get_security(service);
322                 switch (security) {
323                 case CONNMAN_SERVICE_SECURITY_WEP:
324                         data.type = "wep";
325                         break;
326                 case CONNMAN_SERVICE_SECURITY_PSK:
327                         data.type  = "psk";
328                         break;
329                 /*
330                  * This should never happen: no passphrase is set if security
331                  * is not one of the above. */
332                 default:
333                         break;
334                 }
335         }
336
337         connman_dbus_dict_append_dict(iter, "PreviousPassphrase",
338                         request_input_append_previouspassphrase, &data);
339 }
340
341 static void request_input_login_reply(DBusMessage *reply, void *user_data)
342 {
343         struct request_input_reply *username_password_reply = user_data;
344         const char *error = NULL;
345         connman_bool_t values_received = FALSE;
346         char *username = NULL;
347         char *password = NULL;
348         char *key;
349         DBusMessageIter iter, dict;
350
351         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
352                 error = dbus_message_get_error_name(reply);
353                 goto done;
354         }
355
356         if (check_reply_has_dict(reply) == FALSE)
357                 goto done;
358
359         values_received = TRUE;
360
361         dbus_message_iter_init(reply, &iter);
362         dbus_message_iter_recurse(&iter, &dict);
363         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
364                 DBusMessageIter entry, value;
365
366                 dbus_message_iter_recurse(&dict, &entry);
367                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
368                         break;
369
370                 dbus_message_iter_get_basic(&entry, &key);
371
372                 if (g_str_equal(key, "Username")) {
373                         dbus_message_iter_next(&entry);
374                         if (dbus_message_iter_get_arg_type(&entry)
375                                                         != DBUS_TYPE_VARIANT)
376                                 break;
377                         dbus_message_iter_recurse(&entry, &value);
378                         dbus_message_iter_get_basic(&value, &username);
379
380                 } else if (g_str_equal(key, "Password")) {
381                         dbus_message_iter_next(&entry);
382                         if (dbus_message_iter_get_arg_type(&entry) !=
383                                                         DBUS_TYPE_VARIANT)
384                                 break;
385                         dbus_message_iter_recurse(&entry, &value);
386                         dbus_message_iter_get_basic(&value, &password);
387                 }
388
389                 dbus_message_iter_next(&dict);
390         }
391
392 done:
393         username_password_reply->callback(username_password_reply->service,
394                                         values_received, NULL, 0,
395                                         username, password,
396                                         FALSE, NULL, error,
397                                         username_password_reply->user_data);
398         g_free(username_password_reply);
399 }
400
401 int __connman_agent_request_passphrase_input(struct connman_service *service,
402                                 authentication_cb_t callback, void *user_data)
403 {
404         DBusMessage *message;
405         const char *path, *agent_sender, *agent_path;
406         DBusMessageIter iter;
407         DBusMessageIter dict;
408         struct request_input_reply *passphrase_reply;
409         int err;
410
411         connman_agent_get_info(&agent_sender, &agent_path);
412
413         if (service == NULL || agent_path == NULL || callback == NULL)
414                 return -ESRCH;
415
416         message = dbus_message_new_method_call(agent_sender, agent_path,
417                                         CONNMAN_AGENT_INTERFACE,
418                                         "RequestInput");
419         if (message == NULL)
420                 return -ENOMEM;
421
422         dbus_message_iter_init_append(message, &iter);
423
424         path = __connman_service_get_path(service);
425         dbus_message_iter_append_basic(&iter,
426                                 DBUS_TYPE_OBJECT_PATH, &path);
427
428         connman_dbus_dict_open(&iter, &dict);
429
430         if (__connman_service_is_hidden(service)) {
431                 connman_dbus_dict_append_dict(&dict, "Name",
432                                         request_input_append_name, NULL);
433                 connman_dbus_dict_append_dict(&dict, "SSID",
434                                         request_input_append_ssid, NULL);
435         }
436
437         if (__connman_service_get_security(service) ==
438                         CONNMAN_SERVICE_SECURITY_8021X) {
439                 connman_dbus_dict_append_dict(&dict, "Identity",
440                                         request_input_append_identity, service);
441         }
442
443         if (__connman_service_get_security(service) !=
444                         CONNMAN_SERVICE_SECURITY_NONE) {
445                 connman_dbus_dict_append_dict(&dict, "Passphrase",
446                                         request_input_append_passphrase, service);
447
448                 previous_passphrase_handler(&dict, service);
449         }
450
451         if (__connman_service_wps_enabled(service) == TRUE) {
452             connman_dbus_dict_append_dict(&dict, "WPS",
453                                 request_input_append_wps, NULL);
454         }
455
456         connman_dbus_dict_close(&iter, &dict);
457
458         passphrase_reply = g_try_new0(struct request_input_reply, 1);
459         if (passphrase_reply == NULL) {
460                 dbus_message_unref(message);
461                 return -ENOMEM;
462         }
463
464         passphrase_reply->service = service;
465         passphrase_reply->callback = callback;
466         passphrase_reply->user_data = user_data;
467
468         err = connman_agent_queue_message(service, message,
469                         connman_timeout_input_request(),
470                         request_input_passphrase_reply,
471                         passphrase_reply);
472
473         if (err < 0 && err != -EBUSY) {
474                 DBG("error %d sending agent message", err);
475                 dbus_message_unref(message);
476                 g_free(passphrase_reply);
477                 return err;
478         }
479
480         dbus_message_unref(message);
481
482         return -EINPROGRESS;
483 }
484
485 int __connman_agent_request_login_input(struct connman_service *service,
486                                 authentication_cb_t callback, void *user_data)
487 {
488         DBusMessage *message;
489         const char *path, *agent_sender, *agent_path;
490         DBusMessageIter iter;
491         DBusMessageIter dict;
492         struct request_input_reply *username_password_reply;
493         int err;
494
495         connman_agent_get_info(&agent_sender, &agent_path);
496
497         if (service == NULL || agent_path == NULL || callback == NULL)
498                 return -ESRCH;
499
500         message = dbus_message_new_method_call(agent_sender, agent_path,
501                                         CONNMAN_AGENT_INTERFACE,
502                                         "RequestInput");
503         if (message == NULL)
504                 return -ENOMEM;
505
506         dbus_message_iter_init_append(message, &iter);
507
508         path = __connman_service_get_path(service);
509         dbus_message_iter_append_basic(&iter,
510                                 DBUS_TYPE_OBJECT_PATH, &path);
511
512         connman_dbus_dict_open(&iter, &dict);
513
514         connman_dbus_dict_append_dict(&dict, "Username",
515                                 request_input_append_identity, service);
516
517         connman_dbus_dict_append_dict(&dict, "Password",
518                                 request_input_append_password, service);
519
520         connman_dbus_dict_close(&iter, &dict);
521
522         username_password_reply = g_try_new0(struct request_input_reply, 1);
523         if (username_password_reply == NULL) {
524                 dbus_message_unref(message);
525                 return -ENOMEM;
526         }
527
528         username_password_reply->service = service;
529         username_password_reply->callback = callback;
530         username_password_reply->user_data = user_data;
531
532         err = connman_agent_queue_message(service, message,
533                         connman_timeout_input_request(),
534                         request_input_login_reply, username_password_reply);
535         if (err < 0 && err != -EBUSY) {
536                 DBG("error %d sending agent request", err);
537                 dbus_message_unref(message);
538                 g_free(username_password_reply);
539                 return err;
540         }
541
542         dbus_message_unref(message);
543
544         return -EINPROGRESS;
545 }
546
547 struct request_browser_reply_data {
548         struct connman_service *service;
549         browser_authentication_cb_t callback;
550         void *user_data;
551 };
552
553 static void request_browser_reply(DBusMessage *reply, void *user_data)
554 {
555         struct request_browser_reply_data *browser_reply_data = user_data;
556         connman_bool_t result = FALSE;
557         const char *error = NULL;
558
559         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
560                 error = dbus_message_get_error_name(reply);
561                 goto done;
562         }
563
564         result = TRUE;
565
566 done:
567         browser_reply_data->callback(browser_reply_data->service, result,
568                                         error, browser_reply_data->user_data);
569         g_free(browser_reply_data);
570 }
571
572 int __connman_agent_request_browser(struct connman_service *service,
573                                 browser_authentication_cb_t callback,
574                                 const char *url, void *user_data)
575 {
576         struct request_browser_reply_data *browser_reply_data;
577         DBusMessage *message;
578         DBusMessageIter iter;
579         const char *path, *agent_sender, *agent_path;
580         int err;
581
582         connman_agent_get_info(&agent_sender, &agent_path);
583
584         if (service == NULL || agent_path == NULL || callback == NULL)
585                 return -ESRCH;
586
587         if (url == NULL)
588                 url = "";
589
590         message = dbus_message_new_method_call(agent_sender, agent_path,
591                                         CONNMAN_AGENT_INTERFACE,
592                                         "RequestBrowser");
593         if (message == NULL)
594                 return -ENOMEM;
595
596         dbus_message_iter_init_append(message, &iter);
597
598         path = __connman_service_get_path(service);
599         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
600
601         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &url);
602
603         browser_reply_data = g_try_new0(struct request_browser_reply_data, 1);
604         if (browser_reply_data == NULL) {
605                 dbus_message_unref(message);
606                 return -ENOMEM;
607         }
608
609         browser_reply_data->service = service;
610         browser_reply_data->callback = callback;
611         browser_reply_data->user_data = user_data;
612
613         err = connman_agent_queue_message(service, message,
614                                 connman_timeout_browser_launch(),
615                                 request_browser_reply, browser_reply_data);
616
617         if (err < 0 && err != -EBUSY) {
618                 DBG("error %d sending browser request", err);
619                 dbus_message_unref(message);
620                 g_free(browser_reply_data);
621                 return err;
622         }
623
624         dbus_message_unref(message);
625
626         return -EINPROGRESS;
627 }