iptables: Drop support for xtables < 1.4.11
[platform/upstream/connman.git] / plugins / neard.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 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 <stdio.h>
28 #include <stdint.h>
29 #include <string.h>
30 #include <arpa/inet.h>
31
32 #include <glib.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/plugin.h>
36 #include <connman/dbus.h>
37 #include <connman/technology.h>
38 #include <connman/provision.h>
39
40 #include <gdbus.h>
41
42 #define TIMEOUT 30000
43
44 #define NEARD_SERVICE "org.neard"
45 #define NEARD_PATH "/"
46 #define NEARD_MANAGER_INTERFACE "org.neard.Manager"
47 #define NEARD_AGENT_INTERFACE "org.neard.HandoverAgent"
48 #define NEARD_ERROR_INTERFACE "org.neard.HandoverAgent.Error"
49
50 #define AGENT_PATH "/net/connman/neard_handover_agent"
51 #define AGENT_TYPE "wifi"
52
53 #define NEARD_SERVICE_GROUP "service_neard_provision"
54
55 /* Configuration keys */
56 #define SERVICE_KEY_TYPE               "Type"
57 #define SERVICE_KEY_SSID               "SSID"
58 #define SERVICE_KEY_PASSPHRASE         "Passphrase"
59 #define SERVICE_KEY_HIDDEN             "Hidden"
60
61 struct data_elements {
62         unsigned int value;
63         unsigned int length;
64         gboolean fixed_length;
65 };
66
67 typedef enum {
68         DE_AUTHENTICATION_TYPE = 0,
69         DE_NETWORK_KEY         = 1,
70         DE_SSID                = 2,
71         DE_MAX                 = 3,
72 } DEid;
73
74 static const struct data_elements  DEs[DE_MAX] = {
75         { 0x1003, 2,  TRUE  },
76         { 0x1027, 64, FALSE },
77         { 0x1045, 32, FALSE },
78 };
79
80 #define DE_VAL_OPEN                 0x0001
81 #define DE_VAL_PSK                  0x0022
82
83 struct wifi_sc {
84         gchar *ssid;
85         gchar *passphrase;
86 };
87
88 static DBusConnection *connection = NULL;
89 DBusPendingCall *register_call = NULL;
90 gboolean agent_registered = FALSE;
91 static guint watch_id = 0;
92
93 static int set_2b_into_tlv(uint8_t *tlv_msg, uint16_t val)
94 {
95         uint16_t ne_val;
96         uint8_t *ins;
97
98         ins = (uint8_t *) &ne_val;
99
100         ne_val = htons(val);
101         tlv_msg[0] = ins[0];
102         tlv_msg[1] = ins[1];
103
104         return 2;
105 }
106
107 static int set_byte_array_into_tlv(uint8_t *tlv_msg,
108                                         uint8_t *array, int length)
109 {
110         memcpy((void *)tlv_msg, (void *)array, length);
111         return length;
112 }
113
114 static uint8_t *encode_to_tlv(const char *ssid, const char *psk, int *length)
115 {
116         uint16_t ssid_len, psk_len;
117         uint8_t *tlv_msg;
118         int pos = 0;
119
120         if (ssid == NULL || length == NULL)
121                 return NULL;
122
123         ssid_len = strlen(ssid);
124
125         *length = 6 + 4 + ssid_len;
126         if (psk != NULL) {
127                 psk_len = strlen(psk);
128                 *length += 4 + psk_len;
129         } else
130                 psk_len = 0;
131
132         tlv_msg = g_try_malloc0(sizeof(uint8_t) * (*length));
133         if (tlv_msg == NULL)
134                 return NULL;
135
136         pos += set_2b_into_tlv(tlv_msg+pos, DEs[DE_SSID].value);
137         pos += set_2b_into_tlv(tlv_msg+pos, ssid_len);
138         pos += set_byte_array_into_tlv(tlv_msg+pos, (uint8_t *)ssid, ssid_len);
139
140         pos += set_2b_into_tlv(tlv_msg+pos, DEs[DE_AUTHENTICATION_TYPE].value);
141         pos += set_2b_into_tlv(tlv_msg+pos,
142                                         DEs[DE_AUTHENTICATION_TYPE].length);
143         if (psk != NULL) {
144                 pos += set_2b_into_tlv(tlv_msg+pos, DE_VAL_PSK);
145                 pos += set_2b_into_tlv(tlv_msg+pos, DEs[DE_NETWORK_KEY].value);
146                 pos += set_2b_into_tlv(tlv_msg+pos, psk_len);
147                 pos += set_byte_array_into_tlv(tlv_msg+pos,
148                                                 (uint8_t *)psk, psk_len);
149         } else
150                 pos += set_2b_into_tlv(tlv_msg+pos, DE_VAL_OPEN);
151
152         return tlv_msg;
153 }
154
155 static DBusMessage *get_reply_on_error(DBusMessage *message, int error)
156 {
157         if (error == ENOTSUP)
158                 return g_dbus_create_error(message,
159                                         NEARD_ERROR_INTERFACE ".NotSupported",
160                                         "Operation is not supported");
161
162         return g_dbus_create_error(message,
163                 NEARD_ERROR_INTERFACE ".Failed", "Invalid parameters");
164 }
165
166 static int parse_request_oob_params(DBusMessage *message,
167                                 const uint8_t **tlv_msg, int *length)
168 {
169         DBusMessageIter iter;
170         DBusMessageIter array;
171         DBusMessageIter dict_entry;
172         const char *key;
173         int arg_type;
174
175         dbus_message_iter_init(message, &iter);
176
177         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
178                 return -EINVAL;
179
180         dbus_message_iter_recurse(&iter, &array);
181         arg_type = dbus_message_iter_get_arg_type(&array);
182         if (arg_type != DBUS_TYPE_DICT_ENTRY)
183                 return -EINVAL;
184
185         if (tlv_msg == NULL && length == NULL)
186                 return 0;
187
188         while (arg_type != DBUS_TYPE_INVALID) {
189                 dbus_message_iter_recurse(&array, &dict_entry);
190                 if (dbus_message_iter_get_arg_type(&dict_entry) !=
191                                                         DBUS_TYPE_STRING)
192                         return -EINVAL;
193
194                 dbus_message_iter_get_basic(&dict_entry, &key);
195                 if (g_strcmp0(key, "WSC") == 0) {
196                         DBusMessageIter value;
197                         DBusMessageIter fixed_array;
198
199                         dbus_message_iter_next(&dict_entry);
200                         dbus_message_iter_recurse(&dict_entry, &value);
201
202                         if (dbus_message_iter_get_arg_type(&value) !=
203                                                         DBUS_TYPE_ARRAY)
204                                 return -EINVAL;
205
206                         dbus_message_iter_recurse(&value, &fixed_array);
207                         dbus_message_iter_get_fixed_array(&fixed_array,
208                                                         tlv_msg, length);
209                         return 0;
210                 }
211
212                 dbus_message_iter_next(&array);
213                 arg_type = dbus_message_iter_get_arg_type(&array);
214         }
215
216         return -EINVAL;
217 }
218
219 static DBusMessage *create_request_oob_reply(DBusMessage *message)
220 {
221         DBusMessage *reply;
222         DBusMessageIter iter;
223         DBusMessageIter dict;
224         const char *ssid, *psk;
225         uint8_t *tlv_msg;
226         int length;
227
228         if (connman_technology_get_wifi_tethering(&ssid, &psk) == FALSE)
229                 return get_reply_on_error(message, ENOTSUP);
230
231         tlv_msg = encode_to_tlv(ssid, psk, &length);
232         if (tlv_msg == NULL)
233                 return get_reply_on_error(message, ENOTSUP);
234
235         reply = dbus_message_new_method_return(message);
236         if (reply == NULL)
237                 goto out;
238
239         dbus_message_iter_init_append(reply, &iter);
240
241         connman_dbus_dict_open(&iter, &dict);
242
243         connman_dbus_dict_append_fixed_array(&dict, "WSC",
244                                         DBUS_TYPE_BYTE, &tlv_msg, length);
245
246         dbus_message_iter_close_container(&iter, &dict);
247
248 out:
249         g_free(tlv_msg);
250
251         return reply;
252 }
253
254 static DBusMessage *request_oob_method(DBusConnection *dbus_conn,
255                                         DBusMessage *message, void *user_data)
256 {
257         DBG("");
258
259         if (parse_request_oob_params(message, NULL, NULL) != 0)
260                 return get_reply_on_error(message, EINVAL);
261
262         return create_request_oob_reply(message);
263 }
264
265 static void free_wifi_sc(struct wifi_sc *wsc)
266 {
267         if (wsc == NULL)
268                 return;
269
270         g_free(wsc->ssid);
271         g_free(wsc->passphrase);
272
273         g_free(wsc);
274 }
275
276 static inline int get_2b_from_tlv(const uint8_t *tlv_msg, uint16_t *val)
277 {
278         *val = ntohs(tlv_msg[0] | (tlv_msg[1] << 8));
279
280         return 2;
281 }
282
283 static uint8_t *get_byte_array_from_tlv(const uint8_t *tlv_msg, int length)
284 {
285         uint8_t *array;
286
287         array = g_try_malloc0(sizeof(uint8_t) * length);
288         if (array == NULL)
289                 return NULL;
290
291         memcpy((void *)array, (void *)tlv_msg, length*sizeof(uint8_t));
292
293         return array;
294 }
295
296 static char *get_string_from_tlv(const uint8_t *tlv_msg, int length)
297 {
298         char *str;
299
300         str = g_try_malloc0((sizeof(char) * length) + 1);
301         if (str == NULL)
302                 return NULL;
303
304         memcpy((void *)str, (void *)tlv_msg, length*sizeof(char));
305
306         return str;
307 }
308
309 static inline DEid get_de_id(uint16_t attr)
310 {
311         int i;
312
313         for (i = 0; i < DE_MAX && DEs[i].value != 0; i++) {
314                 if (attr == DEs[i].value)
315                         return i;
316         }
317
318         return DE_MAX;
319 }
320
321 static inline gboolean is_de_length_fine(DEid id, uint16_t length)
322 {
323         if (DEs[id].fixed_length == TRUE)
324                 return (length == DEs[id].length);
325
326         return (length <= DEs[id].length);
327 }
328
329 static char *get_hexstr_from_byte_array(const uint8_t *bt, int length)
330 {
331         char *hex_str;
332         int i, j;
333
334         hex_str = g_try_malloc0(((length*2)+1) * sizeof(char));
335         if (hex_str == NULL)
336                 return NULL;
337
338         for (i = 0, j = 0; i < length; i++, j += 2)
339                 snprintf(hex_str+j, 3, "%02x", bt[i]);
340
341         return hex_str;
342 }
343
344 static struct wifi_sc *decode_from_tlv(const uint8_t *tlv_msg, int length)
345 {
346         struct wifi_sc *wsc;
347         uint16_t attr, len;
348         uint8_t *bt;
349         int pos;
350         DEid id;
351
352         if (tlv_msg == NULL || length == 0)
353                 return NULL;
354
355         wsc = g_try_malloc0(sizeof(struct wifi_sc));
356         if (wsc == NULL)
357                 return NULL;
358
359         pos = 0;
360         while (pos < length) {
361                 pos += get_2b_from_tlv(tlv_msg+pos, &attr);
362                 pos += get_2b_from_tlv(tlv_msg+pos, &len);
363
364                 if (len > length - pos)
365                         goto error;
366
367                 id = get_de_id(attr);
368                 if (id == DE_MAX) {
369                         pos += len;
370                         continue;
371                 }
372
373                 if (is_de_length_fine(id, len) == FALSE)
374                         goto error;
375
376                 switch (id) {
377                 case DE_AUTHENTICATION_TYPE:
378                         break;
379                 case DE_NETWORK_KEY:
380                         wsc->passphrase = get_string_from_tlv(tlv_msg+pos,
381                                                                         len);
382                         break;
383                 case DE_SSID:
384                         bt = get_byte_array_from_tlv(tlv_msg+pos, len);
385                         wsc->ssid = get_hexstr_from_byte_array(bt, len);
386                         g_free(bt);
387
388                         break;
389                 case DE_MAX:
390                         /* Falling back. Should never happen though. */
391                 default:
392                         break;
393                 }
394
395                 pos += len;
396         }
397
398         return wsc;
399
400 error:
401         free_wifi_sc(wsc);
402         return NULL;
403 }
404
405 static int handle_wcs_data(const uint8_t *tlv_msg, int length)
406 {
407         struct wifi_sc *wsc = NULL;
408         GKeyFile *keyfile = NULL;
409         int ret = -EINVAL;
410
411         wsc = decode_from_tlv(tlv_msg, length);
412         if (wsc == NULL)
413                 return -EINVAL;
414
415         if (wsc->ssid == NULL)
416                 goto out;
417
418         keyfile = g_key_file_new();
419         if (keyfile == NULL) {
420                 ret = -ENOMEM;
421                 goto out;
422         }
423
424         g_key_file_set_string(keyfile, NEARD_SERVICE_GROUP,
425                                         SERVICE_KEY_TYPE, "wifi");
426         g_key_file_set_string(keyfile, NEARD_SERVICE_GROUP,
427                                         SERVICE_KEY_SSID, wsc->ssid);
428         g_key_file_set_boolean(keyfile, NEARD_SERVICE_GROUP,
429                                         SERVICE_KEY_HIDDEN, TRUE);
430
431         if (wsc->passphrase != NULL)
432                 g_key_file_set_string(keyfile, NEARD_SERVICE_GROUP,
433                                 SERVICE_KEY_PASSPHRASE, wsc->passphrase);
434
435         ret = connman_config_provision_mutable_service(keyfile);
436
437 out:
438         g_key_file_free(keyfile);
439         free_wifi_sc(wsc);
440         return ret;
441 }
442
443 static DBusMessage *push_oob_method(DBusConnection *dbus_conn,
444                                         DBusMessage *message, void *user_data)
445 {
446         const uint8_t *tlv_msg;
447         int err = EINVAL;
448         int length;
449
450         DBG("");
451
452         if (parse_request_oob_params(message, &tlv_msg, &length) != 0)
453                 return get_reply_on_error(message, err);
454
455         err = handle_wcs_data(tlv_msg, length);
456         if (err != 0)
457                 return get_reply_on_error(message, err);
458
459         return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
460 }
461
462 static DBusMessage *release_method(DBusConnection *dbus_conn,
463                                         DBusMessage *message, void *user_data)
464 {
465         DBG("");
466
467         agent_registered = FALSE;
468         g_dbus_unregister_interface(connection,
469                                         AGENT_PATH, NEARD_AGENT_INTERFACE);
470
471         return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
472 }
473
474 static const GDBusMethodTable neard_methods[] = {
475 { GDBUS_ASYNC_METHOD("RequestOOB",
476                 GDBUS_ARGS({ "data", "a{sv}" }),
477                 GDBUS_ARGS({ "data", "a{sv}" }), request_oob_method) },
478         { GDBUS_ASYNC_METHOD("PushOOB",
479                 GDBUS_ARGS({ "data", "a{sv}"}), NULL, push_oob_method) },
480         { GDBUS_METHOD("Release", NULL, NULL, release_method) },
481         { },
482 };
483
484 static void cleanup_register_call(void)
485 {
486         if (register_call != NULL) {
487                 dbus_pending_call_cancel(register_call);
488                 dbus_pending_call_unref(register_call);
489                 register_call = NULL;
490         }
491 }
492
493 static void register_agent_cb(DBusPendingCall *pending, void *user_data)
494 {
495         DBusMessage *reply;
496
497         if (dbus_pending_call_get_completed(pending) == FALSE)
498                 return;
499
500         register_call = NULL;
501
502         reply = dbus_pending_call_steal_reply(pending);
503         if (reply == NULL)
504                 goto out;
505
506         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
507                 g_dbus_unregister_interface(connection,
508                                         AGENT_PATH, NEARD_AGENT_INTERFACE);
509         } else
510                 agent_registered = TRUE;
511
512         dbus_message_unref(reply);
513 out:
514         dbus_pending_call_unref(pending);
515 }
516
517 static void register_agent(void)
518 {
519         const char *path = AGENT_PATH;
520         const char *type = AGENT_TYPE;
521         DBusMessage *message;
522
523         message = dbus_message_new_method_call(NEARD_SERVICE, NEARD_PATH,
524                                                 NEARD_MANAGER_INTERFACE,
525                                                 "RegisterHandoverAgent");
526         if (message == NULL)
527                 return;
528
529         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH,
530                         &path, DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID);
531
532         if (dbus_connection_send_with_reply(connection, message,
533                                         &register_call, TIMEOUT) == FALSE) {
534                 dbus_message_unref(message);
535                 goto out;
536         }
537
538         if (dbus_pending_call_set_notify(register_call, register_agent_cb,
539                                                         NULL, NULL) == FALSE)
540                 cleanup_register_call();
541
542 out:
543         dbus_message_unref(message);
544 }
545
546 static void unregister_agent(void)
547 {
548         const char *path = AGENT_PATH;
549         const char *type = AGENT_TYPE;
550         DBusMessage *message;
551
552         if (agent_registered == FALSE)
553                 return cleanup_register_call();
554
555         agent_registered = FALSE;
556
557         message = dbus_message_new_method_call(NEARD_SERVICE, NEARD_PATH,
558                                                 NEARD_MANAGER_INTERFACE,
559                                                 "UnregisterHandoverAgent");
560         if (message != NULL) {
561                 dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH,
562                         &path, DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID);
563                 g_dbus_send_message(connection, message);
564         }
565
566         g_dbus_unregister_interface(connection,
567                                         AGENT_PATH, NEARD_AGENT_INTERFACE);
568 }
569
570 static void neard_is_present(DBusConnection *conn, void *user_data)
571 {
572         DBG("");
573
574         if (agent_registered == TRUE)
575                 return;
576
577         if (g_dbus_register_interface(connection, AGENT_PATH,
578                                         NEARD_AGENT_INTERFACE, neard_methods,
579                                         NULL, NULL, NULL, NULL) == TRUE)
580                 register_agent();
581 }
582
583 static void neard_is_out(DBusConnection *conn, void *user_data)
584 {
585         DBG("");
586
587         if (agent_registered == TRUE) {
588                 g_dbus_unregister_interface(connection,
589                                         AGENT_PATH, NEARD_AGENT_INTERFACE);
590                 agent_registered = FALSE;
591         }
592
593         cleanup_register_call();
594 }
595
596 static int neard_init(void)
597 {
598         connection = connman_dbus_get_connection();
599         if (connection == NULL)
600                 return -EIO;
601
602         watch_id = g_dbus_add_service_watch(connection, NEARD_SERVICE,
603                                                 neard_is_present, neard_is_out,
604                                                 NULL, NULL);
605         if (watch_id == 0) {
606                 dbus_connection_unref(connection);
607                 return -ENOMEM;
608         }
609
610         return 0;
611 }
612
613 static void neard_exit(void)
614 {
615         unregister_agent();
616
617         if (watch_id != 0)
618                 g_dbus_remove_watch(connection, watch_id);
619         if (connection != NULL)
620                 dbus_connection_unref(connection);
621 }
622
623 CONNMAN_PLUGIN_DEFINE(neard, "Neard handover plugin", VERSION,
624                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, neard_init, neard_exit)