1 /*
2  * wpa_supplicant - D-Bus introspection
3  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
4  * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
5  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
6  *
7  * This software may be distributed under the terms of the BSD license.
8  * See README for more details.
9  */
10 
11 #include "utils/includes.h"
12 
13 #include "utils/common.h"
14 #include "utils/list.h"
15 #include "utils/wpabuf.h"
16 #include "dbus_common_i.h"
17 #include "dbus_new_helpers.h"
18 
19 
20 struct interfaces {
21 	struct dl_list list;
22 	char *dbus_interface;
23 	struct wpabuf *xml;
24 };
25 
26 
27 static struct interfaces * add_interface(struct dl_list *list,
28 					 const char *dbus_interface)
29 {
30 	struct interfaces *iface;
31 
32 	dl_list_for_each(iface, list, struct interfaces, list) {
33 		if (os_strcmp(iface->dbus_interface, dbus_interface) == 0)
34 			return iface; /* already in the list */
35 	}
36 
37 	iface = os_zalloc(sizeof(struct interfaces));
38 	if (!iface)
39 		return NULL;
40 	iface->xml = wpabuf_alloc(6000);
41 	if (iface->xml == NULL) {
42 		os_free(iface);
43 		return NULL;
44 	}
45 	wpabuf_printf(iface->xml, "<interface name=\"%s\">", dbus_interface);
46 	dl_list_add_tail(list, &iface->list);
47 	iface->dbus_interface = os_strdup(dbus_interface);
48 	return iface;
49 }
50 
51 
52 static void add_arg(struct wpabuf *xml, const char *name, const char *type,
53 		    const char *direction)
54 {
55 	wpabuf_printf(xml, "<arg name=\"%s\"", name);
56 	if (type)
57 		wpabuf_printf(xml, " type=\"%s\"", type);
58 	if (direction)
59 		wpabuf_printf(xml, " direction=\"%s\"", direction);
60 	wpabuf_put_str(xml, "/>");
61 }
62 
63 
64 static void add_entry(struct wpabuf *xml, const char *type, const char *name,
65 		      const struct wpa_dbus_argument *args, int include_dir)
66 {
67 	const struct wpa_dbus_argument *arg;
68 
69 	if (args == NULL || args->name == NULL) {
70 		wpabuf_printf(xml, "<%s name=\"%s\"/>", type, name);
71 		return;
72 	}
73 	wpabuf_printf(xml, "<%s name=\"%s\">", type, name);
74 	for (arg = args; arg && arg->name; arg++) {
75 		add_arg(xml, arg->name, arg->type,
76 			include_dir ? (arg->dir == ARG_IN ? "in" : "out") :
77 			NULL);
78 	}
79 	wpabuf_printf(xml, "</%s>", type);
80 }
81 
82 
83 static void add_property(struct wpabuf *xml,
84 			 const struct wpa_dbus_property_desc *dsc)
85 {
86 	wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" "
87 		      "access=\"%s%s\"/>",
88 		      dsc->dbus_property, dsc->type,
89 		      dsc->getter ? "read" : "",
90 		      dsc->setter ? "write" : "");
91 }
92 
93 
94 static void extract_interfaces_methods(
95 	struct dl_list *list, const struct wpa_dbus_method_desc *methods)
96 {
97 	const struct wpa_dbus_method_desc *dsc;
98 	struct interfaces *iface;
99 	for (dsc = methods; dsc && dsc->dbus_method; dsc++) {
100 		iface = add_interface(list, dsc->dbus_interface);
101 		if (iface)
102 			add_entry(iface->xml, "method", dsc->dbus_method,
103 				  dsc->args, 1);
104 	}
105 }
106 
107 
108 static void extract_interfaces_signals(
109 	struct dl_list *list, const struct wpa_dbus_signal_desc *signals)
110 {
111 	const struct wpa_dbus_signal_desc *dsc;
112 	struct interfaces *iface;
113 	for (dsc = signals; dsc && dsc->dbus_signal; dsc++) {
114 		iface = add_interface(list, dsc->dbus_interface);
115 		if (iface)
116 			add_entry(iface->xml, "signal", dsc->dbus_signal,
117 				  dsc->args, 0);
118 	}
119 }
120 
121 
122 static void extract_interfaces_properties(
123 	struct dl_list *list, const struct wpa_dbus_property_desc *properties)
124 {
125 	const struct wpa_dbus_property_desc *dsc;
126 	struct interfaces *iface;
127 	for (dsc = properties; dsc && dsc->dbus_property; dsc++) {
128 		iface = add_interface(list, dsc->dbus_interface);
129 		if (iface)
130 			add_property(iface->xml, dsc);
131 	}
132 }
133 
134 
135 /**
136  * extract_interfaces - Extract interfaces from methods, signals and props
137  * @list: Interface list to be filled
138  * @obj_dsc: Description of object from which interfaces will be extracted
139  *
140  * Iterates over all methods, signals, and properties registered with an
141  * object and collects all declared DBus interfaces and create interfaces'
142  * node in XML root node for each. Returned list elements contain interface
143  * name and XML node of corresponding interface.
144  */
145 static void extract_interfaces(struct dl_list *list,
146 			       struct wpa_dbus_object_desc *obj_dsc)
147 {
148 	extract_interfaces_methods(list, obj_dsc->methods);
149 	extract_interfaces_signals(list, obj_dsc->signals);
150 	extract_interfaces_properties(list, obj_dsc->properties);
151 }
152 
153 
154 static void add_interfaces(struct dl_list *list, struct wpabuf *xml)
155 {
156 	struct interfaces *iface, *n;
157 	dl_list_for_each_safe(iface, n, list, struct interfaces, list) {
158 		if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) {
159 			wpabuf_put_buf(xml, iface->xml);
160 			wpabuf_put_str(xml, "</interface>");
161 		} else {
162 			wpa_printf(MSG_DEBUG, "dbus: Not enough room for "
163 				   "add_interfaces inspect data: tailroom %u, "
164 				   "add %u",
165 				   (unsigned int) wpabuf_tailroom(xml),
166 				   (unsigned int) wpabuf_len(iface->xml));
167 		}
168 		dl_list_del(&iface->list);
169 		wpabuf_free(iface->xml);
170 		os_free(iface->dbus_interface);
171 		os_free(iface);
172 	}
173 }
174 
175 
176 static void add_child_nodes(struct wpabuf *xml, DBusConnection *con,
177 			    const char *path)
178 {
179 	char **children;
180 	int i;
181 
182 	/* add child nodes to introspection tree */
183 	dbus_connection_list_registered(con, path, &children);
184 	for (i = 0; children[i]; i++)
185 		wpabuf_printf(xml, "<node name=\"%s\"/>", children[i]);
186 	dbus_free_string_array(children);
187 }
188 
189 
190 static void add_introspectable_interface(struct wpabuf *xml)
191 {
192 	wpabuf_printf(xml, "<interface name=\"%s\">"
193 		      "<method name=\"%s\">"
194 		      "<arg name=\"data\" type=\"s\" direction=\"out\"/>"
195 		      "</method>"
196 		      "</interface>",
197 		      WPA_DBUS_INTROSPECTION_INTERFACE,
198 		      WPA_DBUS_INTROSPECTION_METHOD);
199 }
200 
201 
202 static void add_properties_interface(struct wpabuf *xml)
203 {
204 	wpabuf_printf(xml, "<interface name=\"%s\">",
205 		      WPA_DBUS_PROPERTIES_INTERFACE);
206 
207 	wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GET);
208 	add_arg(xml, "interface", "s", "in");
209 	add_arg(xml, "propname", "s", "in");
210 	add_arg(xml, "value", "v", "out");
211 	wpabuf_put_str(xml, "</method>");
212 
213 	wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GETALL);
214 	add_arg(xml, "interface", "s", "in");
215 	add_arg(xml, "props", "a{sv}", "out");
216 	wpabuf_put_str(xml, "</method>");
217 
218 	wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_SET);
219 	add_arg(xml, "interface", "s", "in");
220 	add_arg(xml, "propname", "s", "in");
221 	add_arg(xml, "value", "v", "in");
222 	wpabuf_put_str(xml, "</method>");
223 
224 	wpabuf_put_str(xml, "</interface>");
225 }
226 
227 
228 static void add_wpas_interfaces(struct wpabuf *xml,
229 				struct wpa_dbus_object_desc *obj_dsc)
230 {
231 	struct dl_list ifaces;
232 	dl_list_init(&ifaces);
233 	extract_interfaces(&ifaces, obj_dsc);
234 	add_interfaces(&ifaces, xml);
235 }
236 
237 
238 /**
239  * wpa_dbus_introspect - Responds for Introspect calls on object
240  * @message: Message with Introspect call
241  * @obj_dsc: Object description on which Introspect was called
242  * Returns: Message with introspection result XML string as only argument
243  *
244  * Iterates over all methods, signals and properties registered with
245  * object and generates introspection data for the object as XML string.
246  */
247 DBusMessage * wpa_dbus_introspect(DBusMessage *message,
248 				  struct wpa_dbus_object_desc *obj_dsc)
249 {
250 
251 	DBusMessage *reply;
252 	struct wpabuf *xml;
253 
254 	xml = wpabuf_alloc(10000);
255 	if (xml == NULL)
256 		return NULL;
257 
258 	wpabuf_put_str(xml, "<?xml version=\"1.0\"?>\n");
259 	wpabuf_put_str(xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
260 	wpabuf_put_str(xml, "<node>");
261 
262 	add_introspectable_interface(xml);
263 	add_properties_interface(xml);
264 	add_wpas_interfaces(xml, obj_dsc);
265 	add_child_nodes(xml, obj_dsc->connection,
266 			dbus_message_get_path(message));
267 
268 	wpabuf_put_str(xml, "</node>\n");
269 
270 	reply = dbus_message_new_method_return(message);
271 	if (reply) {
272 		const char *intro_str = wpabuf_head(xml);
273 		dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str,
274 					 DBUS_TYPE_INVALID);
275 	}
276 	wpabuf_free(xml);
277 
278 	return reply;
279 }
280