5 * Copyright (C) 2013 Intel Corporation. All rights reserved.
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.
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.
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
30 #include <arpa/inet.h>
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>
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"
50 #define AGENT_PATH "/net/connman/neard_handover_agent"
51 #define AGENT_TYPE "wifi"
53 #define NEARD_SERVICE_GROUP "service_neard_provision"
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"
61 struct data_elements {
64 gboolean fixed_length;
68 DE_AUTHENTICATION_TYPE = 0,
74 static const struct data_elements DEs[DE_MAX] = {
76 { 0x1027, 64, FALSE },
77 { 0x1045, 32, FALSE },
80 #define DE_VAL_OPEN 0x0001
81 #define DE_VAL_PSK 0x0022
88 static DBusConnection *connection = NULL;
89 DBusPendingCall *register_call = NULL;
90 gboolean agent_registered = FALSE;
91 static guint watch_id = 0;
93 static int set_2b_into_tlv(uint8_t *tlv_msg, uint16_t val)
98 ins = (uint8_t *) &ne_val;
107 static int set_byte_array_into_tlv(uint8_t *tlv_msg,
108 uint8_t *array, int length)
110 memcpy((void *)tlv_msg, (void *)array, length);
114 static uint8_t *encode_to_tlv(const char *ssid, const char *psk, int *length)
116 uint16_t ssid_len, psk_len;
120 if (ssid == NULL || length == NULL)
123 ssid_len = strlen(ssid);
125 *length = 6 + 4 + ssid_len;
127 psk_len = strlen(psk);
128 *length += 4 + psk_len;
132 tlv_msg = g_try_malloc0(sizeof(uint8_t) * (*length));
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);
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);
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);
150 pos += set_2b_into_tlv(tlv_msg+pos, DE_VAL_OPEN);
155 static DBusMessage *get_reply_on_error(DBusMessage *message, int error)
157 if (error == ENOTSUP)
158 return g_dbus_create_error(message,
159 NEARD_ERROR_INTERFACE ".NotSupported",
160 "Operation is not supported");
162 return g_dbus_create_error(message,
163 NEARD_ERROR_INTERFACE ".Failed", "Invalid parameters");
166 static int parse_request_oob_params(DBusMessage *message,
167 const uint8_t **tlv_msg, int *length)
169 DBusMessageIter iter;
170 DBusMessageIter array;
171 DBusMessageIter dict_entry;
175 dbus_message_iter_init(message, &iter);
177 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
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)
185 if (tlv_msg == NULL && length == NULL)
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) !=
194 dbus_message_iter_get_basic(&dict_entry, &key);
195 if (g_strcmp0(key, "WSC") == 0) {
196 DBusMessageIter value;
197 DBusMessageIter fixed_array;
199 dbus_message_iter_next(&dict_entry);
200 dbus_message_iter_recurse(&dict_entry, &value);
202 if (dbus_message_iter_get_arg_type(&value) !=
206 dbus_message_iter_recurse(&value, &fixed_array);
207 dbus_message_iter_get_fixed_array(&fixed_array,
212 dbus_message_iter_next(&array);
213 arg_type = dbus_message_iter_get_arg_type(&array);
219 static DBusMessage *create_request_oob_reply(DBusMessage *message)
222 DBusMessageIter iter;
223 DBusMessageIter dict;
224 const char *ssid, *psk;
228 if (connman_technology_get_wifi_tethering(&ssid, &psk) == FALSE)
229 return get_reply_on_error(message, ENOTSUP);
231 tlv_msg = encode_to_tlv(ssid, psk, &length);
233 return get_reply_on_error(message, ENOTSUP);
235 reply = dbus_message_new_method_return(message);
239 dbus_message_iter_init_append(reply, &iter);
241 connman_dbus_dict_open(&iter, &dict);
243 connman_dbus_dict_append_fixed_array(&dict, "WSC",
244 DBUS_TYPE_BYTE, &tlv_msg, length);
246 dbus_message_iter_close_container(&iter, &dict);
254 static DBusMessage *request_oob_method(DBusConnection *dbus_conn,
255 DBusMessage *message, void *user_data)
259 if (parse_request_oob_params(message, NULL, NULL) != 0)
260 return get_reply_on_error(message, EINVAL);
262 return create_request_oob_reply(message);
265 static void free_wifi_sc(struct wifi_sc *wsc)
271 g_free(wsc->passphrase);
276 static inline int get_2b_from_tlv(const uint8_t *tlv_msg, uint16_t *val)
278 *val = ntohs(tlv_msg[0] | (tlv_msg[1] << 8));
283 static uint8_t *get_byte_array_from_tlv(const uint8_t *tlv_msg, int length)
287 array = g_try_malloc0(sizeof(uint8_t) * length);
291 memcpy((void *)array, (void *)tlv_msg, length*sizeof(uint8_t));
296 static char *get_string_from_tlv(const uint8_t *tlv_msg, int length)
300 str = g_try_malloc0((sizeof(char) * length) + 1);
304 memcpy((void *)str, (void *)tlv_msg, length*sizeof(char));
309 static inline DEid get_de_id(uint16_t attr)
313 for (i = 0; i < DE_MAX && DEs[i].value != 0; i++) {
314 if (attr == DEs[i].value)
321 static inline gboolean is_de_length_fine(DEid id, uint16_t length)
323 if (DEs[id].fixed_length == TRUE)
324 return (length == DEs[id].length);
326 return (length <= DEs[id].length);
329 static char *get_hexstr_from_byte_array(const uint8_t *bt, int length)
334 hex_str = g_try_malloc0(((length*2)+1) * sizeof(char));
338 for (i = 0, j = 0; i < length; i++, j += 2)
339 snprintf(hex_str+j, 3, "%02x", bt[i]);
344 static struct wifi_sc *decode_from_tlv(const uint8_t *tlv_msg, int length)
352 if (tlv_msg == NULL || length == 0)
355 wsc = g_try_malloc0(sizeof(struct wifi_sc));
360 while (pos < length) {
361 pos += get_2b_from_tlv(tlv_msg+pos, &attr);
362 pos += get_2b_from_tlv(tlv_msg+pos, &len);
364 if (len > length - pos)
367 id = get_de_id(attr);
373 if (is_de_length_fine(id, len) == FALSE)
377 case DE_AUTHENTICATION_TYPE:
380 wsc->passphrase = get_string_from_tlv(tlv_msg+pos,
384 bt = get_byte_array_from_tlv(tlv_msg+pos, len);
385 wsc->ssid = get_hexstr_from_byte_array(bt, len);
390 /* Falling back. Should never happen though. */
405 static int handle_wcs_data(const uint8_t *tlv_msg, int length)
407 struct wifi_sc *wsc = NULL;
408 GKeyFile *keyfile = NULL;
411 wsc = decode_from_tlv(tlv_msg, length);
415 if (wsc->ssid == NULL)
418 keyfile = g_key_file_new();
419 if (keyfile == NULL) {
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);
431 if (wsc->passphrase != NULL)
432 g_key_file_set_string(keyfile, NEARD_SERVICE_GROUP,
433 SERVICE_KEY_PASSPHRASE, wsc->passphrase);
435 ret = connman_config_provision_mutable_service(keyfile);
438 g_key_file_free(keyfile);
443 static DBusMessage *push_oob_method(DBusConnection *dbus_conn,
444 DBusMessage *message, void *user_data)
446 const uint8_t *tlv_msg;
452 if (parse_request_oob_params(message, &tlv_msg, &length) != 0)
453 return get_reply_on_error(message, err);
455 err = handle_wcs_data(tlv_msg, length);
457 return get_reply_on_error(message, err);
459 return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
462 static DBusMessage *release_method(DBusConnection *dbus_conn,
463 DBusMessage *message, void *user_data)
467 agent_registered = FALSE;
468 g_dbus_unregister_interface(connection,
469 AGENT_PATH, NEARD_AGENT_INTERFACE);
471 return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
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) },
484 static void cleanup_register_call(void)
486 if (register_call != NULL) {
487 dbus_pending_call_cancel(register_call);
488 dbus_pending_call_unref(register_call);
489 register_call = NULL;
493 static void register_agent_cb(DBusPendingCall *pending, void *user_data)
497 if (dbus_pending_call_get_completed(pending) == FALSE)
500 register_call = NULL;
502 reply = dbus_pending_call_steal_reply(pending);
506 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
507 g_dbus_unregister_interface(connection,
508 AGENT_PATH, NEARD_AGENT_INTERFACE);
510 agent_registered = TRUE;
512 dbus_message_unref(reply);
514 dbus_pending_call_unref(pending);
517 static void register_agent(void)
519 const char *path = AGENT_PATH;
520 const char *type = AGENT_TYPE;
521 DBusMessage *message;
523 message = dbus_message_new_method_call(NEARD_SERVICE, NEARD_PATH,
524 NEARD_MANAGER_INTERFACE,
525 "RegisterHandoverAgent");
529 dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH,
530 &path, DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID);
532 if (dbus_connection_send_with_reply(connection, message,
533 ®ister_call, TIMEOUT) == FALSE) {
534 dbus_message_unref(message);
538 if (dbus_pending_call_set_notify(register_call, register_agent_cb,
539 NULL, NULL) == FALSE)
540 cleanup_register_call();
543 dbus_message_unref(message);
546 static void unregister_agent(void)
548 const char *path = AGENT_PATH;
549 const char *type = AGENT_TYPE;
550 DBusMessage *message;
552 if (agent_registered == FALSE)
553 return cleanup_register_call();
555 agent_registered = FALSE;
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);
566 g_dbus_unregister_interface(connection,
567 AGENT_PATH, NEARD_AGENT_INTERFACE);
570 static void neard_is_present(DBusConnection *conn, void *user_data)
574 if (agent_registered == TRUE)
577 if (g_dbus_register_interface(connection, AGENT_PATH,
578 NEARD_AGENT_INTERFACE, neard_methods,
579 NULL, NULL, NULL, NULL) == TRUE)
583 static void neard_is_out(DBusConnection *conn, void *user_data)
587 if (agent_registered == TRUE) {
588 g_dbus_unregister_interface(connection,
589 AGENT_PATH, NEARD_AGENT_INTERFACE);
590 agent_registered = FALSE;
593 cleanup_register_call();
596 static int neard_init(void)
598 connection = connman_dbus_get_connection();
599 if (connection == NULL)
602 watch_id = g_dbus_add_service_watch(connection, NEARD_SERVICE,
603 neard_is_present, neard_is_out,
606 dbus_connection_unref(connection);
613 static void neard_exit(void)
618 g_dbus_remove_watch(connection, watch_id);
619 if (connection != NULL)
620 dbus_connection_unref(connection);
623 CONNMAN_PLUGIN_DEFINE(neard, "Neard handover plugin", VERSION,
624 CONNMAN_PLUGIN_PRIORITY_DEFAULT, neard_init, neard_exit)