1 /* 2 * WPA Supplicant / dbus-based control interface 3 * Copyright (c) 2006, Dan Williams <dcbw (at) redhat.com> and Red Hat, Inc. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Alternatively, this software may be distributed under the terms of BSD 10 * license. 11 * 12 * See README and COPYING for more details. 13 */ 14 15 #include "includes.h" 16 17 #include <dbus/dbus.h> 18 19 #include "common.h" 20 #include "config.h" 21 #include "wpa_supplicant_i.h" 22 #include "ctrl_iface_dbus.h" 23 #include "ctrl_iface_dbus_handlers.h" 24 #include "l2_packet.h" 25 #include "eap_methods.h" 26 #include "dbus_dict_helpers.h" 27 #include "wpa.h" 28 29 30 /** 31 * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message 32 * @message: Pointer to incoming dbus message this error refers to 33 * Returns: a dbus error message 34 * 35 * Convenience function to create and return an invalid options error 36 */ 37 static DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, 38 const char *arg) 39 { 40 DBusMessage *reply; 41 42 reply = dbus_message_new_error(message, WPAS_ERROR_INVALID_OPTS, 43 "Did not receive correct message " 44 "arguments."); 45 if (arg != NULL) 46 dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, 47 DBUS_TYPE_INVALID); 48 49 return reply; 50 } 51 52 53 /** 54 * wpas_dbus_new_success_reply - Return a new success reply message 55 * @message: Pointer to incoming dbus message this reply refers to 56 * Returns: a dbus message containing a single UINT32 that indicates 57 * success (ie, a value of 1) 58 * 59 * Convenience function to create and return a success reply message 60 */ 61 static DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message) 62 { 63 DBusMessage *reply; 64 unsigned int success = 1; 65 66 reply = dbus_message_new_method_return(message); 67 dbus_message_append_args(reply, DBUS_TYPE_UINT32, &success, 68 DBUS_TYPE_INVALID); 69 return reply; 70 } 71 72 73 static void wpas_dbus_free_wpa_interface(struct wpa_interface *iface) 74 { 75 free((char *) iface->driver); 76 free((char *) iface->driver_param); 77 free((char *) iface->confname); 78 free((char *) iface->bridge_ifname); 79 } 80 81 82 /** 83 * wpas_dbus_global_add_interface - Request registration of a network interface 84 * @message: Pointer to incoming dbus message 85 * @global: %wpa_supplicant global data structure 86 * Returns: The object path of the new interface object, 87 * or a dbus error message with more information 88 * 89 * Handler function for "addInterface" method call. Handles requests 90 * by dbus clients to register a network interface that wpa_supplicant 91 * will manage. 92 */ 93 DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, 94 struct wpa_global *global) 95 { 96 struct wpa_interface iface; 97 char *ifname = NULL; 98 DBusMessage *reply = NULL; 99 DBusMessageIter iter; 100 101 memset(&iface, 0, sizeof(iface)); 102 103 dbus_message_iter_init(message, &iter); 104 105 /* First argument: interface name (DBUS_TYPE_STRING) 106 * Required; must be non-zero length 107 */ 108 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) 109 goto error; 110 dbus_message_iter_get_basic(&iter, &ifname); 111 if (!strlen(ifname)) 112 goto error; 113 iface.ifname = ifname; 114 115 /* Second argument: dict of options */ 116 if (dbus_message_iter_next(&iter)) { 117 DBusMessageIter iter_dict; 118 struct wpa_dbus_dict_entry entry; 119 120 if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) 121 goto error; 122 while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { 123 if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) 124 goto error; 125 if (!strcmp(entry.key, "driver") && 126 (entry.type == DBUS_TYPE_STRING)) { 127 iface.driver = strdup(entry.str_value); 128 if (iface.driver == NULL) 129 goto error; 130 } else if (!strcmp(entry.key, "driver-params") && 131 (entry.type == DBUS_TYPE_STRING)) { 132 iface.driver_param = strdup(entry.str_value); 133 if (iface.driver_param == NULL) 134 goto error; 135 } else if (!strcmp(entry.key, "config-file") && 136 (entry.type == DBUS_TYPE_STRING)) { 137 iface.confname = strdup(entry.str_value); 138 if (iface.confname == NULL) 139 goto error; 140 } else if (!strcmp(entry.key, "bridge-ifname") && 141 (entry.type == DBUS_TYPE_STRING)) { 142 iface.bridge_ifname = strdup(entry.str_value); 143 if (iface.bridge_ifname == NULL) 144 goto error; 145 } else { 146 wpa_dbus_dict_entry_clear(&entry); 147 goto error; 148 } 149 wpa_dbus_dict_entry_clear(&entry); 150 } 151 } 152 153 /* 154 * Try to get the wpa_supplicant record for this iface, return 155 * an error if we already control it. 156 */ 157 if (wpa_supplicant_get_iface(global, iface.ifname) != 0) { 158 reply = dbus_message_new_error(message, 159 WPAS_ERROR_EXISTS_ERROR, 160 "wpa_supplicant already " 161 "controls this interface."); 162 } else { 163 struct wpa_supplicant *wpa_s; 164 /* Otherwise, have wpa_supplicant attach to it. */ 165 if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) { 166 const char *path = wpa_supplicant_get_dbus_path(wpa_s); 167 reply = dbus_message_new_method_return(message); 168 dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, 169 &path, DBUS_TYPE_INVALID); 170 } else { 171 reply = dbus_message_new_error(message, 172 WPAS_ERROR_ADD_ERROR, 173 "wpa_supplicant " 174 "couldn't grab this " 175 "interface."); 176 } 177 } 178 wpas_dbus_free_wpa_interface(&iface); 179 return reply; 180 181 error: 182 wpas_dbus_free_wpa_interface(&iface); 183 return wpas_dbus_new_invalid_opts_error(message, NULL); 184 } 185 186 187 /** 188 * wpas_dbus_global_remove_interface - Request deregistration of an interface 189 * @message: Pointer to incoming dbus message 190 * @global: wpa_supplicant global data structure 191 * Returns: a dbus message containing a UINT32 indicating success (1) or 192 * failure (0), or returns a dbus error message with more information 193 * 194 * Handler function for "removeInterface" method call. Handles requests 195 * by dbus clients to deregister a network interface that wpa_supplicant 196 * currently manages. 197 */ 198 DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, 199 struct wpa_global *global) 200 { 201 struct wpa_supplicant *wpa_s; 202 char *path; 203 DBusMessage *reply = NULL; 204 205 if (!dbus_message_get_args(message, NULL, 206 DBUS_TYPE_OBJECT_PATH, &path, 207 DBUS_TYPE_INVALID)) { 208 reply = wpas_dbus_new_invalid_opts_error(message, NULL); 209 goto out; 210 } 211 212 wpa_s = wpa_supplicant_get_iface_by_dbus_path(global, path); 213 if (wpa_s == NULL) { 214 reply = wpas_dbus_new_invalid_iface_error(message); 215 goto out; 216 } 217 218 if (!wpa_supplicant_remove_iface(global, wpa_s)) { 219 reply = wpas_dbus_new_success_reply(message); 220 } else { 221 reply = dbus_message_new_error(message, 222 WPAS_ERROR_REMOVE_ERROR, 223 "wpa_supplicant couldn't " 224 "remove this interface."); 225 } 226 227 out: 228 return reply; 229 } 230 231 232 /** 233 * wpas_dbus_global_get_interface - Get the object path for an interface name 234 * @message: Pointer to incoming dbus message 235 * @global: %wpa_supplicant global data structure 236 * Returns: The object path of the interface object, 237 * or a dbus error message with more information 238 * 239 * Handler function for "getInterface" method call. Handles requests 240 * by dbus clients for the object path of an specific network interface. 241 */ 242 DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, 243 struct wpa_global *global) 244 { 245 DBusMessage *reply = NULL; 246 const char *ifname; 247 const char *path; 248 struct wpa_supplicant *wpa_s; 249 250 if (!dbus_message_get_args(message, NULL, 251 DBUS_TYPE_STRING, &ifname, 252 DBUS_TYPE_INVALID)) { 253 reply = wpas_dbus_new_invalid_opts_error(message, NULL); 254 goto out; 255 } 256 257 wpa_s = wpa_supplicant_get_iface(global, ifname); 258 if (wpa_s == NULL) { 259 reply = wpas_dbus_new_invalid_iface_error(message); 260 goto out; 261 } 262 263 path = wpa_supplicant_get_dbus_path(wpa_s); 264 if (path == NULL) { 265 reply = dbus_message_new_error(message, 266 WPAS_ERROR_INTERNAL_ERROR, 267 "an internal error occurred " 268 "getting the interface."); 269 goto out; 270 } 271 272 reply = dbus_message_new_method_return(message); 273 dbus_message_append_args(reply, 274 DBUS_TYPE_OBJECT_PATH, &path, 275 DBUS_TYPE_INVALID); 276 277 out: 278 return reply; 279 } 280 281 282 /** 283 * wpas_dbus_iface_scan - Request a wireless scan on an interface 284 * @message: Pointer to incoming dbus message 285 * @wpa_s: wpa_supplicant structure for a network interface 286 * Returns: a dbus message containing a UINT32 indicating success (1) or 287 * failure (0) 288 * 289 * Handler function for "scan" method call of a network device. Requests 290 * that wpa_supplicant perform a wireless scan as soon as possible 291 * on a particular wireless interface. 292 */ 293 DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, 294 struct wpa_supplicant *wpa_s) 295 { 296 wpa_s->scan_req = 2; 297 wpa_supplicant_req_scan(wpa_s, 0, 0); 298 return wpas_dbus_new_success_reply(message); 299 } 300 301 302 /** 303 * wpas_dbus_iface_scan_results - Get the results of a recent scan request 304 * @message: Pointer to incoming dbus message 305 * @wpa_s: wpa_supplicant structure for a network interface 306 * Returns: a dbus message containing a dbus array of objects paths, or returns 307 * a dbus error message if not scan results could be found 308 * 309 * Handler function for "scanResults" method call of a network device. Returns 310 * a dbus message containing the object paths of wireless networks found. 311 */ 312 DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, 313 struct wpa_supplicant *wpa_s) 314 { 315 DBusMessage *reply = NULL; 316 DBusMessageIter iter; 317 DBusMessageIter sub_iter; 318 int i; 319 320 /* Ensure we've actually got scan results to return */ 321 if (wpa_s->scan_results == NULL && 322 wpa_supplicant_get_scan_results(wpa_s) < 0) { 323 reply = dbus_message_new_error(message, WPAS_ERROR_SCAN_ERROR, 324 "An error ocurred getting scan " 325 "results."); 326 goto out; 327 } 328 329 /* Create and initialize the return message */ 330 reply = dbus_message_new_method_return(message); 331 dbus_message_iter_init_append(reply, &iter); 332 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, 333 DBUS_TYPE_OBJECT_PATH_AS_STRING, 334 &sub_iter); 335 336 /* Loop through scan results and append each result's object path */ 337 for (i = 0; i < wpa_s->num_scan_results; i++) { 338 struct wpa_scan_result *res = &wpa_s->scan_results[i]; 339 char *path; 340 341 path = wpa_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); 342 if (path == NULL) { 343 perror("wpas_dbus_iface_scan_results[dbus]: out of " 344 "memory."); 345 wpa_printf(MSG_ERROR, "dbus control interface: not " 346 "enough memory to send scan results " 347 "signal."); 348 break; 349 } 350 /* Construct the object path for this network. Note that ':' 351 * is not a valid character in dbus object paths. 352 */ 353 snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, 354 "%s/" WPAS_DBUS_BSSIDS_PART "/" 355 WPAS_DBUS_BSSID_FORMAT, 356 wpa_supplicant_get_dbus_path(wpa_s), 357 MAC2STR(res->bssid)); 358 dbus_message_iter_append_basic(&sub_iter, 359 DBUS_TYPE_OBJECT_PATH, &path); 360 free(path); 361 } 362 363 dbus_message_iter_close_container(&iter, &sub_iter); 364 365 out: 366 return reply; 367 } 368 369 370 /** 371 * wpas_dbus_bssid_properties - Return the properties of a scanned network 372 * @message: Pointer to incoming dbus message 373 * @wpa_s: wpa_supplicant structure for a network interface 374 * @res: wpa_supplicant scan result for which to get properties 375 * Returns: a dbus message containing the properties for the requested network 376 * 377 * Handler function for "properties" method call of a scanned network. 378 * Returns a dbus message containing the the properties. 379 */ 380 DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, 381 struct wpa_supplicant *wpa_s, 382 struct wpa_scan_result *res) 383 { 384 DBusMessage *reply = NULL; 385 char *bssid_data, *ssid_data, *wpa_ie_data, *rsn_ie_data; 386 DBusMessageIter iter, iter_dict; 387 388 /* dbus needs the address of a pointer to the actual value 389 * for array types, not the address of the value itself. 390 */ 391 bssid_data = (char *) &res->bssid; 392 ssid_data = (char *) &res->ssid; 393 wpa_ie_data = (char *) &res->wpa_ie; 394 rsn_ie_data = (char *) &res->rsn_ie; 395 396 /* Dump the properties into a dbus message */ 397 reply = dbus_message_new_method_return(message); 398 399 dbus_message_iter_init_append(reply, &iter); 400 if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) 401 goto error; 402 403 if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", 404 bssid_data, ETH_ALEN)) 405 goto error; 406 if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", 407 ssid_data, res->ssid_len)) 408 goto error; 409 if (res->wpa_ie_len) { 410 if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", 411 wpa_ie_data, 412 res->wpa_ie_len)) { 413 goto error; 414 } 415 } 416 if (res->rsn_ie_len) { 417 if (!wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", 418 rsn_ie_data, 419 res->rsn_ie_len)) { 420 goto error; 421 } 422 } 423 if (res->freq) { 424 if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency", 425 res->freq)) 426 goto error; 427 } 428 if (!wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", 429 res->caps)) 430 goto error; 431 if (!wpa_dbus_dict_append_int32(&iter_dict, "quality", res->qual)) 432 goto error; 433 if (!wpa_dbus_dict_append_int32(&iter_dict, "noise", res->noise)) 434 goto error; 435 if (!wpa_dbus_dict_append_int32(&iter_dict, "level", res->level)) 436 goto error; 437 if (!wpa_dbus_dict_append_int32(&iter_dict, "maxrate", res->maxrate)) 438 goto error; 439 440 if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) 441 goto error; 442 443 return reply; 444 445 error: 446 if (reply) 447 dbus_message_unref(reply); 448 return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, 449 "an internal error occurred returning " 450 "BSSID properties."); 451 } 452 453 454 /** 455 * wpas_dbus_iface_capabilities - Return interface capabilities 456 * @message: Pointer to incoming dbus message 457 * @wpa_s: wpa_supplicant structure for a network interface 458 * Returns: A dbus message containing a dict of strings 459 * 460 * Handler function for "capabilities" method call of an interface. 461 */ 462 DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, 463 struct wpa_supplicant *wpa_s) 464 { 465 DBusMessage *reply = NULL; 466 struct wpa_driver_capa capa; 467 int res; 468 DBusMessageIter iter, iter_dict; 469 char **eap_methods; 470 size_t num_items; 471 dbus_bool_t strict = FALSE; 472 DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; 473 474 if (!dbus_message_get_args(message, NULL, 475 DBUS_TYPE_BOOLEAN, &strict, 476 DBUS_TYPE_INVALID)) 477 strict = FALSE; 478 479 reply = dbus_message_new_method_return(message); 480 481 dbus_message_iter_init_append(reply, &iter); 482 if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) 483 goto error; 484 485 /* EAP methods */ 486 eap_methods = eap_get_names_as_string_array(&num_items); 487 if (eap_methods) { 488 dbus_bool_t success = FALSE; 489 size_t i = 0; 490 491 success = wpa_dbus_dict_append_string_array( 492 &iter_dict, "eap", (const char **) eap_methods, 493 num_items); 494 495 /* free returned method array */ 496 while (eap_methods[i]) 497 free(eap_methods[i++]); 498 free(eap_methods); 499 500 if (!success) 501 goto error; 502 } 503 504 res = wpa_drv_get_capa(wpa_s, &capa); 505 506 /***** pairwise cipher */ 507 if (res < 0) { 508 if (!strict) { 509 const char *args[] = {"CCMP", "TKIP", "NONE"}; 510 if (!wpa_dbus_dict_append_string_array( 511 &iter_dict, "pairwise", args, 512 sizeof(args) / sizeof(char*))) 513 goto error; 514 } 515 } else { 516 if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise", 517 &iter_dict_entry, 518 &iter_dict_val, 519 &iter_array)) 520 goto error; 521 522 if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { 523 if (!wpa_dbus_dict_string_array_add_element( 524 &iter_array, "CCMP")) 525 goto error; 526 } 527 528 if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { 529 if (!wpa_dbus_dict_string_array_add_element( 530 &iter_array, "TKIP")) 531 goto error; 532 } 533 534 if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { 535 if (!wpa_dbus_dict_string_array_add_element( 536 &iter_array, "NONE")) 537 goto error; 538 } 539 540 if (!wpa_dbus_dict_end_string_array(&iter_dict, 541 &iter_dict_entry, 542 &iter_dict_val, 543 &iter_array)) 544 goto error; 545 } 546 547 /***** group cipher */ 548 if (res < 0) { 549 if (!strict) { 550 const char *args[] = { 551 "CCMP", "TKIP", "WEP104", "WEP40" 552 }; 553 if (!wpa_dbus_dict_append_string_array( 554 &iter_dict, "group", args, 555 sizeof(args) / sizeof(char*))) 556 goto error; 557 } 558 } else { 559 if (!wpa_dbus_dict_begin_string_array(&iter_dict, "group", 560 &iter_dict_entry, 561 &iter_dict_val, 562 &iter_array)) 563 goto error; 564 565 if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { 566 if (!wpa_dbus_dict_string_array_add_element( 567 &iter_array, "CCMP")) 568 goto error; 569 } 570 571 if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { 572 if (!wpa_dbus_dict_string_array_add_element( 573 &iter_array, "TKIP")) 574 goto error; 575 } 576 577 if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) { 578 if (!wpa_dbus_dict_string_array_add_element( 579 &iter_array, "WEP104")) 580 goto error; 581 } 582 583 if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) { 584 if (!wpa_dbus_dict_string_array_add_element( 585 &iter_array, "WEP40")) 586 goto error; 587 } 588 589 if (!wpa_dbus_dict_end_string_array(&iter_dict, 590 &iter_dict_entry, 591 &iter_dict_val, 592 &iter_array)) 593 goto error; 594 } 595 596 /***** key management */ 597 if (res < 0) { 598 if (!strict) { 599 const char *args[] = { 600 "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE", 601 "NONE" 602 }; 603 if (!wpa_dbus_dict_append_string_array( 604 &iter_dict, "key_mgmt", args, 605 sizeof(args) / sizeof(char*))) 606 goto error; 607 } 608 } else { 609 if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt", 610 &iter_dict_entry, 611 &iter_dict_val, 612 &iter_array)) 613 goto error; 614 615 if (!wpa_dbus_dict_string_array_add_element(&iter_array, 616 "NONE")) 617 goto error; 618 619 if (!wpa_dbus_dict_string_array_add_element(&iter_array, 620 "IEEE8021X")) 621 goto error; 622 623 if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | 624 WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { 625 if (!wpa_dbus_dict_string_array_add_element( 626 &iter_array, "WPA-EAP")) 627 goto error; 628 } 629 630 if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | 631 WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { 632 if (!wpa_dbus_dict_string_array_add_element( 633 &iter_array, "WPA-PSK")) 634 goto error; 635 } 636 637 if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { 638 if (!wpa_dbus_dict_string_array_add_element( 639 &iter_array, "WPA-NONE")) 640 goto error; 641 } 642 643 if (!wpa_dbus_dict_end_string_array(&iter_dict, 644 &iter_dict_entry, 645 &iter_dict_val, 646 &iter_array)) 647 goto error; 648 } 649 650 /***** WPA protocol */ 651 if (res < 0) { 652 if (!strict) { 653 const char *args[] = { "RSN", "WPA" }; 654 if (!wpa_dbus_dict_append_string_array( 655 &iter_dict, "proto", args, 656 sizeof(args) / sizeof(char*))) 657 goto error; 658 } 659 } else { 660 if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto", 661 &iter_dict_entry, 662 &iter_dict_val, 663 &iter_array)) 664 goto error; 665 666 if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | 667 WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { 668 if (!wpa_dbus_dict_string_array_add_element( 669 &iter_array, "RSN")) 670 goto error; 671 } 672 673 if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | 674 WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { 675 if (!wpa_dbus_dict_string_array_add_element( 676 &iter_array, "WPA")) 677 goto error; 678 } 679 680 if (!wpa_dbus_dict_end_string_array(&iter_dict, 681 &iter_dict_entry, 682 &iter_dict_val, 683 &iter_array)) 684 goto error; 685 } 686 687 /***** auth alg */ 688 if (res < 0) { 689 if (!strict) { 690 const char *args[] = { "OPEN", "SHARED", "LEAP" }; 691 if (!wpa_dbus_dict_append_string_array( 692 &iter_dict, "auth_alg", args, 693 sizeof(args) / sizeof(char*))) 694 goto error; 695 } 696 } else { 697 if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg", 698 &iter_dict_entry, 699 &iter_dict_val, 700 &iter_array)) 701 goto error; 702 703 if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) { 704 if (!wpa_dbus_dict_string_array_add_element( 705 &iter_array, "OPEN")) 706 goto error; 707 } 708 709 if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) { 710 if (!wpa_dbus_dict_string_array_add_element( 711 &iter_array, "SHARED")) 712 goto error; 713 } 714 715 if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) { 716 if (!wpa_dbus_dict_string_array_add_element( 717 &iter_array, "LEAP")) 718 goto error; 719 } 720 721 if (!wpa_dbus_dict_end_string_array(&iter_dict, 722 &iter_dict_entry, 723 &iter_dict_val, 724 &iter_array)) 725 goto error; 726 } 727 728 if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) 729 goto error; 730 731 return reply; 732 733 error: 734 if (reply) 735 dbus_message_unref(reply); 736 return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, 737 "an internal error occurred returning " 738 "interface capabilities."); 739 } 740 741 742 /** 743 * wpas_dbus_iface_add_network - Add a new configured network 744 * @message: Pointer to incoming dbus message 745 * @wpa_s: wpa_supplicant structure for a network interface 746 * Returns: A dbus message containing the object path of the new network 747 * 748 * Handler function for "addNetwork" method call of a network interface. 749 */ 750 DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, 751 struct wpa_supplicant *wpa_s) 752 { 753 DBusMessage *reply = NULL; 754 struct wpa_ssid *ssid; 755 char *path = NULL; 756 757 path = wpa_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); 758 if (path == NULL) { 759 perror("wpas_dbus_iface_scan_results[dbus]: out of " 760 "memory."); 761 wpa_printf(MSG_ERROR, "dbus control interface: not " 762 "enough memory to send scan results " 763 "signal."); 764 goto out; 765 } 766 767 ssid = wpa_config_add_network(wpa_s->conf); 768 if (ssid == NULL) { 769 reply = dbus_message_new_error(message, 770 WPAS_ERROR_ADD_NETWORK_ERROR, 771 "wpa_supplicant could not add " 772 "a network on this interface."); 773 goto out; 774 } 775 ssid->disabled = 1; 776 wpa_config_set_network_defaults(ssid); 777 778 /* Construct the object path for this network. */ 779 snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, 780 "%s/" WPAS_DBUS_NETWORKS_PART "/%d", 781 wpa_supplicant_get_dbus_path(wpa_s), 782 ssid->id); 783 784 reply = dbus_message_new_method_return(message); 785 dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, 786 &path, DBUS_TYPE_INVALID); 787 788 out: 789 free(path); 790 return reply; 791 } 792 793 794 /** 795 * wpas_dbus_iface_remove_network - Remove a configured network 796 * @message: Pointer to incoming dbus message 797 * @wpa_s: wpa_supplicant structure for a network interface 798 * Returns: A dbus message containing a UINT32 indicating success (1) or 799 * failure (0) 800 * 801 * Handler function for "removeNetwork" method call of a network interface. 802 */ 803 DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, 804 struct wpa_supplicant *wpa_s) 805 { 806 DBusMessage *reply = NULL; 807 const char *op; 808 char *iface = NULL, *net_id = NULL; 809 int id; 810 struct wpa_ssid *ssid; 811 812 if (!dbus_message_get_args(message, NULL, 813 DBUS_TYPE_OBJECT_PATH, &op, 814 DBUS_TYPE_INVALID)) { 815 reply = wpas_dbus_new_invalid_opts_error(message, NULL); 816 goto out; 817 } 818 819 /* Extract the network ID */ 820 iface = wpas_dbus_decompose_object_path(op, &net_id, NULL); 821 if (iface == NULL) { 822 reply = wpas_dbus_new_invalid_network_error(message); 823 goto out; 824 } 825 /* Ensure the network is actually a child of this interface */ 826 if (strcmp(iface, wpa_supplicant_get_dbus_path(wpa_s)) != 0) { 827 reply = wpas_dbus_new_invalid_network_error(message); 828 goto out; 829 } 830 831 id = strtoul(net_id, NULL, 10); 832 ssid = wpa_config_get_network(wpa_s->conf, id); 833 if (ssid == NULL) { 834 reply = wpas_dbus_new_invalid_network_error(message); 835 goto out; 836 } 837 838 if (wpa_config_remove_network(wpa_s->conf, id) < 0) { 839 reply = dbus_message_new_error(message, 840 WPAS_ERROR_REMOVE_NETWORK_ERROR, 841 "error removing the specified " 842 "on this interface."); 843 goto out; 844 } 845 846 if (ssid == wpa_s->current_ssid) 847 wpa_supplicant_disassociate(wpa_s, REASON_DEAUTH_LEAVING); 848 reply = wpas_dbus_new_success_reply(message); 849 850 out: 851 free(iface); 852 free(net_id); 853 return reply; 854 } 855 856 857 static const char *dont_quote[] = { 858 "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", 859 "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", 860 "bssid", NULL 861 }; 862 863 static dbus_bool_t should_quote_opt(const char *key) 864 { 865 int i = 0; 866 while (dont_quote[i] != NULL) { 867 if (strcmp(key, dont_quote[i]) == 0) 868 return FALSE; 869 i++; 870 } 871 return TRUE; 872 } 873 874 /** 875 * wpas_dbus_iface_set_network - Set options for a configured network 876 * @message: Pointer to incoming dbus message 877 * @wpa_s: wpa_supplicant structure for a network interface 878 * @ssid: wpa_ssid structure for a configured network 879 * Returns: a dbus message containing a UINT32 indicating success (1) or 880 * failure (0) 881 * 882 * Handler function for "set" method call of a configured network. 883 */ 884 DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, 885 struct wpa_supplicant *wpa_s, 886 struct wpa_ssid *ssid) 887 { 888 DBusMessage *reply = NULL; 889 struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; 890 DBusMessageIter iter, iter_dict; 891 892 dbus_message_iter_init(message, &iter); 893 894 if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) { 895 reply = wpas_dbus_new_invalid_opts_error(message, NULL); 896 goto out; 897 } 898 899 while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { 900 char *value = NULL; 901 size_t size = 50; 902 int ret; 903 904 if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { 905 reply = wpas_dbus_new_invalid_opts_error(message, 906 NULL); 907 goto out; 908 } 909 910 /* Type conversions, since wpa_supplicant wants strings */ 911 if (entry.type == DBUS_TYPE_ARRAY && 912 entry.array_type == DBUS_TYPE_BYTE) { 913 if (entry.array_len <= 0) 914 goto error; 915 916 size = entry.array_len * 2 + 1; 917 value = wpa_zalloc(size); 918 if (value == NULL) 919 goto error; 920 ret = wpa_snprintf_hex(value, size, 921 (u8 *) entry.bytearray_value, 922 entry.array_len); 923 if (ret <= 0) 924 goto error; 925 } else if (entry.type == DBUS_TYPE_STRING) { 926 if (should_quote_opt(entry.key)) { 927 size = strlen(entry.str_value); 928 /* Zero-length option check */ 929 if (size <= 0) 930 goto error; 931 size += 3; /* For quotes and terminator */ 932 value = wpa_zalloc(size); 933 if (value == NULL) 934 goto error; 935 ret = snprintf(value, size, "\"%s\"", 936 entry.str_value); 937 if (ret < 0 || (size_t) ret != (size - 1)) 938 goto error; 939 } else { 940 value = strdup(entry.str_value); 941 if (value == NULL) 942 goto error; 943 } 944 } else if (entry.type == DBUS_TYPE_UINT32) { 945 value = wpa_zalloc(size); 946 if (value == NULL) 947 goto error; 948 ret = snprintf(value, size, "%u", entry.uint32_value); 949 if (ret <= 0) 950 goto error; 951 } else if (entry.type == DBUS_TYPE_INT32) { 952 value = wpa_zalloc(size); 953 if (value == NULL) 954 goto error; 955 ret = snprintf(value, size, "%d", entry.int32_value); 956 if (ret <= 0) 957 goto error; 958 } else 959 goto error; 960 961 if (wpa_config_set(ssid, entry.key, value, 0) < 0) 962 goto error; 963 964 if ((strcmp(entry.key, "psk") == 0 && 965 value[0] == '"' && ssid->ssid_len) || 966 (strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) 967 wpa_config_update_psk(ssid); 968 969 free(value); 970 wpa_dbus_dict_entry_clear(&entry); 971 continue; 972 973 error: 974 free(value); 975 reply = wpas_dbus_new_invalid_opts_error(message, entry.key); 976 wpa_dbus_dict_entry_clear(&entry); 977 break; 978 } 979 980 if (!reply) 981 reply = wpas_dbus_new_success_reply(message); 982 983 out: 984 return reply; 985 } 986 987 988 /** 989 * wpas_dbus_iface_enable_network - Mark a configured network as enabled 990 * @message: Pointer to incoming dbus message 991 * @wpa_s: wpa_supplicant structure for a network interface 992 * @ssid: wpa_ssid structure for a configured network 993 * Returns: A dbus message containing a UINT32 indicating success (1) or 994 * failure (0) 995 * 996 * Handler function for "enable" method call of a configured network. 997 */ 998 DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message, 999 struct wpa_supplicant *wpa_s, 1000 struct wpa_ssid *ssid) 1001 { 1002 if (wpa_s->current_ssid == NULL && ssid->disabled) { 1003 /* 1004 * Try to reassociate since there is no current configuration 1005 * and a new network was made available. 1006 */ 1007 wpa_s->reassociate = 1; 1008 wpa_supplicant_req_scan(wpa_s, 0, 0); 1009 } 1010 ssid->disabled = 0; 1011 1012 return wpas_dbus_new_success_reply(message); 1013 } 1014 1015 1016 /** 1017 * wpas_dbus_iface_disable_network - Mark a configured network as disabled 1018 * @message: Pointer to incoming dbus message 1019 * @wpa_s: wpa_supplicant structure for a network interface 1020 * @ssid: wpa_ssid structure for a configured network 1021 * Returns: A dbus message containing a UINT32 indicating success (1) or 1022 * failure (0) 1023 * 1024 * Handler function for "disable" method call of a configured network. 1025 */ 1026 DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, 1027 struct wpa_supplicant *wpa_s, 1028 struct wpa_ssid *ssid) 1029 { 1030 if (ssid == wpa_s->current_ssid) 1031 wpa_supplicant_disassociate(wpa_s, REASON_DEAUTH_LEAVING); 1032 ssid->disabled = 1; 1033 1034 return wpas_dbus_new_success_reply(message); 1035 } 1036 1037 1038 /** 1039 * wpas_dbus_iface_select_network - Attempt association with a configured network 1040 * @message: Pointer to incoming dbus message 1041 * @wpa_s: wpa_supplicant structure for a network interface 1042 * Returns: A dbus message containing a UINT32 indicating success (1) or 1043 * failure (0) 1044 * 1045 * Handler function for "selectNetwork" method call of network interface. 1046 */ 1047 DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, 1048 struct wpa_supplicant *wpa_s) 1049 { 1050 DBusMessage *reply = NULL; 1051 const char *op; 1052 struct wpa_ssid *ssid; 1053 char *iface_obj_path = NULL; 1054 char *network = NULL; 1055 1056 if (strlen(dbus_message_get_signature(message)) == 0) { 1057 /* Any network */ 1058 ssid = wpa_s->conf->ssid; 1059 while (ssid) { 1060 ssid->disabled = 0; 1061 ssid = ssid->next; 1062 } 1063 wpa_s->reassociate = 1; 1064 wpa_supplicant_req_scan(wpa_s, 0, 0); 1065 } else { 1066 const char *obj_path; 1067 int nid; 1068 1069 if (!dbus_message_get_args(message, NULL, 1070 DBUS_TYPE_OBJECT_PATH, &op, 1071 DBUS_TYPE_INVALID)) { 1072 reply = wpas_dbus_new_invalid_opts_error(message, 1073 NULL); 1074 goto out; 1075 } 1076 1077 /* Extract the network number */ 1078 iface_obj_path = wpas_dbus_decompose_object_path(op, 1079 &network, 1080 NULL); 1081 if (iface_obj_path == NULL) { 1082 reply = wpas_dbus_new_invalid_iface_error(message); 1083 goto out; 1084 } 1085 /* Ensure the object path really points to this interface */ 1086 obj_path = wpa_supplicant_get_dbus_path(wpa_s); 1087 if (strcmp(iface_obj_path, obj_path) != 0) { 1088 reply = wpas_dbus_new_invalid_network_error(message); 1089 goto out; 1090 } 1091 1092 nid = strtoul(network, NULL, 10); 1093 if (errno == EINVAL) { 1094 reply = wpas_dbus_new_invalid_network_error(message); 1095 goto out; 1096 } 1097 1098 ssid = wpa_config_get_network(wpa_s->conf, nid); 1099 if (ssid == NULL) { 1100 reply = wpas_dbus_new_invalid_network_error(message); 1101 goto out; 1102 } 1103 1104 /* Finally, associate with the network */ 1105 if (ssid != wpa_s->current_ssid && wpa_s->current_ssid) 1106 wpa_supplicant_disassociate(wpa_s, 1107 REASON_DEAUTH_LEAVING); 1108 1109 /* Mark all other networks disabled and trigger reassociation 1110 */ 1111 ssid = wpa_s->conf->ssid; 1112 while (ssid) { 1113 ssid->disabled = (nid != ssid->id); 1114 ssid = ssid->next; 1115 } 1116 wpa_s->disconnected = 0; 1117 wpa_s->reassociate = 1; 1118 wpa_supplicant_req_scan(wpa_s, 0, 0); 1119 } 1120 1121 reply = wpas_dbus_new_success_reply(message); 1122 1123 out: 1124 free(iface_obj_path); 1125 free(network); 1126 return reply; 1127 } 1128 1129 1130 /** 1131 * wpas_dbus_iface_disconnect - Terminate the current connection 1132 * @message: Pointer to incoming dbus message 1133 * @wpa_s: wpa_supplicant structure for a network interface 1134 * Returns: A dbus message containing a UINT32 indicating success (1) or 1135 * failure (0) 1136 * 1137 * Handler function for "disconnect" method call of network interface. 1138 */ 1139 DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, 1140 struct wpa_supplicant *wpa_s) 1141 { 1142 wpa_s->disconnected = 1; 1143 wpa_supplicant_disassociate(wpa_s, REASON_DEAUTH_LEAVING); 1144 1145 return wpas_dbus_new_success_reply(message); 1146 } 1147 1148 1149 /** 1150 * wpas_dbus_iface_set_ap_scan - Control roaming mode 1151 * @message: Pointer to incoming dbus message 1152 * @wpa_s: wpa_supplicant structure for a network interface 1153 * Returns: A dbus message containing a UINT32 indicating success (1) or 1154 * failure (0) 1155 * 1156 * Handler function for "setAPScan" method call. 1157 */ 1158 DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, 1159 struct wpa_supplicant *wpa_s) 1160 { 1161 DBusMessage *reply = NULL; 1162 dbus_uint32_t ap_scan = 1; 1163 1164 if (!dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &ap_scan, 1165 DBUS_TYPE_INVALID)) { 1166 reply = wpas_dbus_new_invalid_opts_error(message, NULL); 1167 goto out; 1168 } 1169 1170 if (ap_scan > 2) { 1171 reply = wpas_dbus_new_invalid_opts_error(message, NULL); 1172 goto out; 1173 } 1174 wpa_s->conf->ap_scan = ap_scan; 1175 reply = wpas_dbus_new_success_reply(message); 1176 1177 out: 1178 return reply; 1179 } 1180 1181 1182 /** 1183 * wpas_dbus_iface_get_state - Get interface state 1184 * @message: Pointer to incoming dbus message 1185 * @wpa_s: wpa_supplicant structure for a network interface 1186 * Returns: A dbus message containing a STRING representing the current 1187 * interface state 1188 * 1189 * Handler function for "state" method call. 1190 */ 1191 DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, 1192 struct wpa_supplicant *wpa_s) 1193 { 1194 DBusMessage *reply = NULL; 1195 const char *str_state; 1196 1197 reply = dbus_message_new_method_return(message); 1198 if (reply != NULL) { 1199 str_state = wpa_supplicant_state_txt(wpa_s->wpa_state); 1200 dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_state, 1201 DBUS_TYPE_INVALID); 1202 } 1203 1204 return reply; 1205 } 1206 1207 1208 /** 1209 * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates) 1210 * @message: Pointer to incoming dbus message 1211 * @global: %wpa_supplicant global data structure 1212 * Returns: A dbus message containing a UINT32 indicating success (1) or 1213 * failure (0) 1214 * 1215 * Asks wpa_supplicant to internally store a one or more binary blobs. 1216 */ 1217 DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, 1218 struct wpa_supplicant *wpa_s) 1219 { 1220 DBusMessage *reply = NULL; 1221 struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; 1222 DBusMessageIter iter, iter_dict; 1223 1224 dbus_message_iter_init(message, &iter); 1225 1226 if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) 1227 return wpas_dbus_new_invalid_opts_error(message, NULL); 1228 1229 while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { 1230 struct wpa_config_blob *blob; 1231 1232 if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { 1233 reply = wpas_dbus_new_invalid_opts_error(message, 1234 NULL); 1235 break; 1236 } 1237 1238 if (entry.type != DBUS_TYPE_ARRAY || 1239 entry.array_type != DBUS_TYPE_BYTE) { 1240 reply = wpas_dbus_new_invalid_opts_error( 1241 message, "Byte array expected."); 1242 break; 1243 } 1244 1245 if ((entry.array_len <= 0) || (entry.array_len > 65536) || 1246 !strlen(entry.key)) { 1247 reply = wpas_dbus_new_invalid_opts_error( 1248 message, "Invalid array size."); 1249 break; 1250 } 1251 1252 blob = os_zalloc(sizeof(*blob)); 1253 if (blob == NULL) { 1254 reply = dbus_message_new_error( 1255 message, WPAS_ERROR_ADD_ERROR, 1256 "Not enough memory to add blob."); 1257 break; 1258 } 1259 blob->data = os_zalloc(entry.array_len); 1260 if (blob->data == NULL) { 1261 reply = dbus_message_new_error( 1262 message, WPAS_ERROR_ADD_ERROR, 1263 "Not enough memory to add blob data."); 1264 os_free(blob); 1265 break; 1266 } 1267 1268 blob->name = os_strdup(entry.key); 1269 blob->len = entry.array_len; 1270 os_memcpy(blob->data, (u8 *) entry.bytearray_value, 1271 entry.array_len); 1272 if (blob->name == NULL || blob->data == NULL) { 1273 wpa_config_free_blob(blob); 1274 reply = dbus_message_new_error( 1275 message, WPAS_ERROR_ADD_ERROR, 1276 "Error adding blob."); 1277 break; 1278 } 1279 1280 /* Success */ 1281 wpa_config_remove_blob(wpa_s->conf, blob->name); 1282 wpa_config_set_blob(wpa_s->conf, blob); 1283 wpa_dbus_dict_entry_clear(&entry); 1284 } 1285 wpa_dbus_dict_entry_clear(&entry); 1286 1287 return reply ? reply : wpas_dbus_new_success_reply(message); 1288 } 1289 1290 1291 /** 1292 * wpas_dbus_iface_remove_blob - Remove named binary blobs 1293 * @message: Pointer to incoming dbus message 1294 * @global: %wpa_supplicant global data structure 1295 * Returns: A dbus message containing a UINT32 indicating success (1) or 1296 * failure (0) 1297 * 1298 * Asks wpa_supplicant to remove one or more previously stored binary blobs. 1299 */ 1300 DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, 1301 struct wpa_supplicant *wpa_s) 1302 { 1303 DBusMessageIter iter, array; 1304 char *err_msg = NULL; 1305 1306 dbus_message_iter_init(message, &iter); 1307 1308 if ((dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) || 1309 (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING)) 1310 return wpas_dbus_new_invalid_opts_error(message, NULL); 1311 1312 dbus_message_iter_recurse(&iter, &array); 1313 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { 1314 const char *name; 1315 1316 dbus_message_iter_get_basic(&array, &name); 1317 if (!strlen(name)) 1318 err_msg = "Invalid blob name."; 1319 1320 if (wpa_config_remove_blob(wpa_s->conf, name) != 0) 1321 err_msg = "Error removing blob."; 1322 dbus_message_iter_next(&array); 1323 } 1324 1325 if (err_msg) { 1326 return dbus_message_new_error(message, WPAS_ERROR_REMOVE_ERROR, 1327 err_msg); 1328 } 1329 1330 return wpas_dbus_new_success_reply(message); 1331 } 1332