agent: Implement EAP identity and passphrase query
[framework/connectivity/connman.git] / src / agent.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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
32 #include "connman.h"
33
34 static DBusConnection *connection = NULL;
35 static guint agent_watch = 0;
36 static gchar *agent_path = NULL;
37 static gchar *agent_sender = NULL;
38
39 static void agent_free(void)
40 {
41         agent_watch = 0;
42
43         g_free(agent_sender);
44         agent_sender = NULL;
45
46         g_free(agent_path);
47         agent_path = NULL;
48 }
49
50 static void agent_disconnect(DBusConnection *connection, void *data)
51 {
52         DBG("data %p", data);
53
54         agent_free();
55 }
56
57 int __connman_agent_register(const char *sender, const char *path)
58 {
59         DBG("sender %s path %s", sender, path);
60
61         if (agent_path != NULL)
62                 return -EEXIST;
63
64         agent_sender = g_strdup(sender);
65         agent_path = g_strdup(path);
66
67         agent_watch = g_dbus_add_disconnect_watch(connection, sender,
68                                                 agent_disconnect, NULL, NULL);
69
70         return 0;
71 }
72
73 int __connman_agent_unregister(const char *sender, const char *path)
74 {
75         DBG("sender %s path %s", sender, path);
76
77         if (agent_path == NULL)
78                 return -ESRCH;
79
80         if (agent_watch > 0)
81                 g_dbus_remove_watch(connection, agent_watch);
82
83         agent_free();
84
85         return 0;
86 }
87
88 struct request_input_reply {
89         struct connman_service *service;
90         passphrase_cb_t callback;
91         void *user_data;
92 };
93
94 static void request_input_passphrase_reply(DBusPendingCall *call, void *user_data)
95 {
96         struct request_input_reply *passphrase_reply = user_data;
97         connman_bool_t wps = FALSE;
98         char *identity = NULL;
99         char *passphrase = NULL;
100         char *wpspin = NULL;
101         char *key;
102         DBusMessageIter iter, dict;
103         DBusMessage *reply = dbus_pending_call_steal_reply(call);
104
105         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
106                 goto done;
107
108         dbus_message_iter_init(reply, &iter);
109         dbus_message_iter_recurse(&iter, &dict);
110         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
111                 DBusMessageIter entry, value;
112
113                 dbus_message_iter_recurse(&dict, &entry);
114                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
115                         break;
116
117                 dbus_message_iter_get_basic(&entry, &key);
118
119                 if (g_str_equal(key, "Identity")) {
120                         dbus_message_iter_next(&entry);
121                         if (dbus_message_iter_get_arg_type(&entry)
122                                                         != DBUS_TYPE_VARIANT)
123                                 break;
124                         dbus_message_iter_recurse(&entry, &value);
125                         dbus_message_iter_get_basic(&value, &identity);
126
127                 } else if (g_str_equal(key, "Passphrase")) {
128                         dbus_message_iter_next(&entry);
129                         if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
130                                 break;
131                         dbus_message_iter_recurse(&entry, &value);
132                         dbus_message_iter_get_basic(&value, &passphrase);
133
134                 } else if (g_str_equal(key, "WPS")) {
135                         wps = TRUE;
136
137                         dbus_message_iter_next(&entry);
138                         if (dbus_message_iter_get_arg_type(&entry)
139                                                         != DBUS_TYPE_VARIANT)
140                                 break;
141                         dbus_message_iter_recurse(&entry, &value);
142                         dbus_message_iter_get_basic(&value, &wpspin);
143                         break;
144                 }
145                 dbus_message_iter_next(&dict);
146         }
147
148         if (wps == TRUE) {
149                 struct connman_network *network;
150
151                 network = __connman_service_get_network(
152                                                 passphrase_reply->service);
153                 if (network == NULL)
154                         goto done;
155
156                 connman_network_set_bool(network, "WiFi.UseWPS", wps);
157
158                 if (wpspin != NULL && strlen(wpspin) > 0)
159                         connman_network_set_string(network,
160                                                 "WiFi.PinWPS", wpspin);
161                 else
162                         connman_network_set_string(network,
163                                                 "WiFi.PinWPS", NULL);
164         }
165
166 done:
167         passphrase_reply->callback(passphrase_reply->service, identity,
168                                 passphrase, passphrase_reply->user_data);
169         connman_service_unref(passphrase_reply->service);
170         dbus_message_unref(reply);
171         g_free(passphrase_reply);
172 }
173
174 static void request_input_append_alternates(DBusMessageIter *iter,
175                                                         void *user_data)
176 {
177         const char *str = user_data;
178         char **alternates, **alternative;
179
180         if (str == NULL)
181                 return;
182
183         alternates = g_strsplit(str, ",", 0);
184         if (alternates == NULL)
185                 return;
186
187         for (alternative = alternates; *alternative != NULL; alternative++)
188                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
189                                                                 alternative);
190
191         g_strfreev(alternates);
192 }
193
194 static void request_input_append_identity(DBusMessageIter *iter,
195                                                         void *user_data)
196 {
197         char *str = "string";
198
199         connman_dbus_dict_append_basic(iter, "Type",
200                                 DBUS_TYPE_STRING, &str);
201         str = "Mandatory";
202         connman_dbus_dict_append_basic(iter, "Requirement",
203                                 DBUS_TYPE_STRING, &str);
204 }
205
206 static void request_input_append_passphrase(DBusMessageIter *iter,
207                                                         void *user_data)
208 {
209         struct connman_service *service = user_data;
210         char *value;
211         const char *phase2;
212
213         switch (__connman_service_get_security(service)) {
214         case CONNMAN_SERVICE_SECURITY_WEP:
215                 value = "wep";
216                 break;
217         case CONNMAN_SERVICE_SECURITY_PSK:
218                 value = "psk";
219                 break;
220         case CONNMAN_SERVICE_SECURITY_8021X:
221                 phase2 = __connman_service_get_phase2(service);
222
223                 if (phase2 != NULL && (
224                                 g_str_has_suffix(phase2, "GTC") == TRUE ||
225                                 g_str_has_suffix(phase2, "OTP") == TRUE))
226                         value = "response";
227                 else
228                         value = "passphrase";
229
230                 break;
231         default:
232                 value = "string";
233                 break;
234         }
235         connman_dbus_dict_append_basic(iter, "Type",
236                                 DBUS_TYPE_STRING, &value);
237         value = "Mandatory";
238         connman_dbus_dict_append_basic(iter, "Requirement",
239                                 DBUS_TYPE_STRING, &value);
240
241         if (__connman_service_wps_enabled(service) == TRUE) {
242                 connman_dbus_dict_append_array(iter, "Alternates",
243                                         DBUS_TYPE_STRING,
244                                         request_input_append_alternates,
245                                         "WPS");
246         }
247 }
248
249 static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
250 {
251         const char *str = "wpspin";
252
253         connman_dbus_dict_append_basic(iter, "Type",
254                                 DBUS_TYPE_STRING, &str);
255         str = "alternate";
256         connman_dbus_dict_append_basic(iter, "Requirement",
257                                 DBUS_TYPE_STRING, &str);
258 }
259
260 int __connman_agent_request_input(struct connman_service *service,
261                                 passphrase_cb_t callback, void *user_data)
262 {
263         DBusMessage *message;
264         const char *path;
265         DBusMessageIter iter;
266         DBusMessageIter dict;
267         DBusPendingCall *call;
268         struct request_input_reply *passphrase_reply;
269
270         if (service == NULL || agent_path == NULL || callback == NULL)
271                 return -ESRCH;
272
273         message = dbus_message_new_method_call(agent_sender, agent_path,
274                                         CONNMAN_AGENT_INTERFACE,
275                                         "RequestInput");
276         if (message == NULL)
277                 return -ENOMEM;
278
279         dbus_message_iter_init_append(message, &iter);
280
281         path = __connman_service_get_path(service);
282         dbus_message_iter_append_basic(&iter,
283                                 DBUS_TYPE_OBJECT_PATH, &path);
284
285         connman_dbus_dict_open(&iter, &dict);
286
287         if (__connman_service_get_security(service) ==
288                         CONNMAN_SERVICE_SECURITY_8021X) {
289                 connman_dbus_dict_append_dict(&dict, "Identity",
290                                         request_input_append_identity, service);
291         }
292
293         connman_dbus_dict_append_dict(&dict, "Passphrase",
294                                 request_input_append_passphrase, service);
295
296         if (__connman_service_wps_enabled(service) == TRUE) {
297             connman_dbus_dict_append_dict(&dict, "WPS",
298                                 request_input_append_wps, NULL);
299         }
300
301         connman_dbus_dict_close(&iter, &dict);
302
303         passphrase_reply = g_try_new0(struct request_input_reply, 1);
304         if (passphrase_reply == NULL) {
305                 dbus_message_unref(message);
306                 return -ENOMEM;
307         }
308
309         if (dbus_connection_send_with_reply(connection, message,
310                                                 &call, -1) == FALSE) {
311                 dbus_message_unref(message);
312                 g_free(passphrase_reply);
313                 return -ESRCH;
314         }
315
316         if (call == NULL) {
317                 dbus_message_unref(message);
318                 g_free(passphrase_reply);
319                 return -ESRCH;
320         }
321
322         passphrase_reply->service = connman_service_ref(service);
323         passphrase_reply->callback = callback;
324         passphrase_reply->user_data = user_data;
325
326         dbus_pending_call_set_notify(call, request_input_passphrase_reply,
327                                 passphrase_reply, NULL);
328
329         dbus_message_unref(message);
330
331         return -EIO;
332 }
333
334 struct report_error_data {
335         struct connman_service *service;
336         report_error_cb_t callback;
337         void *user_data;
338 };
339
340 static void report_error_reply(DBusPendingCall *call, void *user_data)
341 {
342         struct report_error_data *report_error = user_data;
343         DBusMessage *reply = dbus_pending_call_steal_reply(call);
344         gboolean retry = FALSE;
345         const char *dbus_err;
346
347         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
348                 dbus_err = dbus_message_get_error_name(reply);
349                 if (dbus_err != NULL &&
350                         strcmp(dbus_err,
351                                 CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
352                         retry = TRUE;
353         }
354
355         report_error->callback(report_error->service, retry,
356                         report_error->user_data);
357         connman_service_unref(report_error->service);
358         g_free(report_error);
359         dbus_message_unref(reply);
360 }
361
362 int __connman_agent_report_error(struct connman_service *service,
363                                 const char *error,
364                                 report_error_cb_t callback, void *user_data)
365 {
366         DBusMessage *message;
367         DBusMessageIter iter;
368         const char *path;
369         struct report_error_data *report_error;
370         DBusPendingCall *call;
371
372         if (service == NULL || agent_path == NULL || error == NULL ||
373                 callback == NULL)
374                 return -ESRCH;
375
376         message = dbus_message_new_method_call(agent_sender, agent_path,
377                                         CONNMAN_AGENT_INTERFACE,
378                                         "ReportError");
379         if (message == NULL)
380                 return -ENOMEM;
381
382         dbus_message_iter_init_append(message, &iter);
383
384         path = __connman_service_get_path(service);
385         dbus_message_iter_append_basic(&iter,
386                                 DBUS_TYPE_OBJECT_PATH, &path);
387         dbus_message_iter_append_basic(&iter,
388                                 DBUS_TYPE_STRING, &error);
389
390         report_error = g_try_new0(struct report_error_data, 1);
391         if (report_error == NULL) {
392                 dbus_message_unref(message);
393                 return -ENOMEM;
394         }
395
396         if (dbus_connection_send_with_reply(connection, message,
397                                                 &call, -1) == FALSE) {
398                 dbus_message_unref(message);
399                 g_free(report_error);
400                 return -ESRCH;
401         }
402
403         if (call == NULL) {
404                 dbus_message_unref(message);
405                 g_free(report_error);
406                 return -ESRCH;
407         }
408
409         report_error->service = connman_service_ref(service);
410         report_error->callback = callback;
411         report_error->user_data = user_data;
412         dbus_pending_call_set_notify(call, report_error_reply,
413                                 report_error, NULL);
414         dbus_message_unref(message);
415
416         return -EIO;
417 }
418
419 int __connman_agent_init(void)
420 {
421         DBG("");
422
423         connection = connman_dbus_get_connection();
424         if (connection == NULL)
425                 return -1;
426
427         return 0;
428 }
429
430 void __connman_agent_cleanup(void)
431 {
432         DBusMessage *message;
433
434         DBG("");
435
436         if (connection == NULL)
437                 return;
438
439         if (agent_watch > 0)
440                 g_dbus_remove_watch(connection, agent_watch);
441
442         if (agent_path == NULL)
443                 return;
444
445         message = dbus_message_new_method_call(agent_sender, agent_path,
446                                         CONNMAN_AGENT_INTERFACE, "Release");
447         if (message == NULL)
448                 return;
449
450         dbus_message_set_no_reply(message, TRUE);
451
452         g_dbus_send_message(connection, message);
453
454         agent_free();
455
456         dbus_connection_unref(connection);
457 }