1/*
2 * WPA Supplicant / dbus-based control interface
3 * Copyright (c) 2006, Dan Williams <dcbw@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 */
37static 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 */
61static 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
73static 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 */
93DBusMessage * 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
181error:
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 */
198DBusMessage * 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
227out:
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 */
242DBusMessage * 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
277out:
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 */
293DBusMessage * 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 */
312DBusMessage * 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
365out:
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 */
380DBusMessage * 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
445error:
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 */
462DBusMessage * 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
733error:
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 */
750DBusMessage * 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
788out:
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 */
803DBusMessage * 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
850out:
851	free(iface);
852	free(net_id);
853	return reply;
854}
855
856
857static 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
863static 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 */
884DBusMessage * 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
983out:
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 */
998DBusMessage * 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 */
1026DBusMessage * 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 */
1047DBusMessage * 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
1123out:
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 */
1139DBusMessage * 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 */
1158DBusMessage * 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
1177out:
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 */
1191DBusMessage * 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 */
1217DBusMessage * 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 */
1300DBusMessage * 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