804d4659418d13e560747bb5736af33f8879b961
[openwrt/staging/jow.git] / package / network / services / hostapd / src / wpa_supplicant / ubus.c
1 /*
2 * wpa_supplicant / ubus support
3 * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
4 * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
5 *
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
8 */
9
10 #include "utils/includes.h"
11 #include "utils/common.h"
12 #include "utils/eloop.h"
13 #include "utils/wpabuf.h"
14 #include "common/ieee802_11_defs.h"
15 #include "wpa_supplicant_i.h"
16 #include "wps_supplicant.h"
17 #include "ubus.h"
18
19 static struct ubus_context *ctx;
20 static struct blob_buf b;
21 static int ctx_ref;
22
23 static inline struct wpa_global *get_wpa_global_from_object(struct ubus_object *obj)
24 {
25 return container_of(obj, struct wpa_global, ubus_global);
26 }
27
28 static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *obj)
29 {
30 return container_of(obj, struct wpa_supplicant, ubus.obj);
31 }
32
33 static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
34 {
35 if (ubus_reconnect(ctx, NULL)) {
36 eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
37 return;
38 }
39
40 ubus_add_uloop(ctx);
41 }
42
43 static void wpas_ubus_connection_lost(struct ubus_context *ctx)
44 {
45 uloop_fd_delete(&ctx->sock);
46 eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
47 }
48
49 static bool wpas_ubus_init(void)
50 {
51 if (ctx)
52 return true;
53
54 eloop_add_uloop();
55 ctx = ubus_connect(NULL);
56 if (!ctx)
57 return false;
58
59 ctx->connection_lost = wpas_ubus_connection_lost;
60 ubus_add_uloop(ctx);
61
62 return true;
63 }
64
65 static void wpas_ubus_ref_inc(void)
66 {
67 ctx_ref++;
68 }
69
70 static void wpas_ubus_ref_dec(void)
71 {
72 ctx_ref--;
73 if (!ctx)
74 return;
75
76 if (ctx_ref)
77 return;
78
79 uloop_fd_delete(&ctx->sock);
80 ubus_free(ctx);
81 ctx = NULL;
82 }
83
84 static int
85 wpas_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
86 struct ubus_request_data *req, const char *method,
87 struct blob_attr *msg)
88 {
89 struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
90
91 blob_buf_init(&b, 0);
92 blobmsg_add_u8(&b, "ht_supported", ht_supported(wpa_s->hw.modes));
93 blobmsg_add_u8(&b, "vht_supported", vht_supported(wpa_s->hw.modes));
94 ubus_send_reply(ctx, req, b.head);
95
96 return 0;
97 }
98
99 static int
100 wpas_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
101 struct ubus_request_data *req, const char *method,
102 struct blob_attr *msg)
103 {
104 struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
105
106 if (wpa_supplicant_reload_configuration(wpa_s))
107 return UBUS_STATUS_UNKNOWN_ERROR;
108 else
109 return 0;
110 }
111
112 #ifdef CONFIG_WPS
113 enum {
114 WPS_START_MULTI_AP,
115 __WPS_START_MAX
116 };
117
118 static const struct blobmsg_policy wps_start_policy[] = {
119 [WPS_START_MULTI_AP] = { "multi_ap", BLOBMSG_TYPE_BOOL },
120 };
121
122 static int
123 wpas_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
124 struct ubus_request_data *req, const char *method,
125 struct blob_attr *msg)
126 {
127 int rc;
128 struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
129 struct blob_attr *tb[__WPS_START_MAX], *cur;
130 int multi_ap = 0;
131
132 blobmsg_parse(wps_start_policy, __WPS_START_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
133
134 if (tb[WPS_START_MULTI_AP])
135 multi_ap = blobmsg_get_bool(tb[WPS_START_MULTI_AP]);
136
137 rc = wpas_wps_start_pbc(wpa_s, NULL, 0, multi_ap);
138
139 if (rc != 0)
140 return UBUS_STATUS_NOT_SUPPORTED;
141
142 return 0;
143 }
144
145 static int
146 wpas_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
147 struct ubus_request_data *req, const char *method,
148 struct blob_attr *msg)
149 {
150 int rc;
151 struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
152
153 rc = wpas_wps_cancel(wpa_s);
154
155 if (rc != 0)
156 return UBUS_STATUS_NOT_SUPPORTED;
157
158 return 0;
159 }
160 #endif
161
162 static const struct ubus_method bss_methods[] = {
163 UBUS_METHOD_NOARG("reload", wpas_bss_reload),
164 UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
165 #ifdef CONFIG_WPS
166 UBUS_METHOD_NOARG("wps_start", wpas_bss_wps_start),
167 UBUS_METHOD_NOARG("wps_cancel", wpas_bss_wps_cancel),
168 #endif
169 };
170
171 static struct ubus_object_type bss_object_type =
172 UBUS_OBJECT_TYPE("wpas_bss", bss_methods);
173
174 void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
175 {
176 struct ubus_object *obj = &wpa_s->ubus.obj;
177 char *name;
178 int ret;
179
180 if (!wpas_ubus_init())
181 return;
182
183 if (asprintf(&name, "wpa_supplicant.%s", wpa_s->ifname) < 0)
184 return;
185
186 obj->name = name;
187 obj->type = &bss_object_type;
188 obj->methods = bss_object_type.methods;
189 obj->n_methods = bss_object_type.n_methods;
190 ret = ubus_add_object(ctx, obj);
191 wpas_ubus_ref_inc();
192 }
193
194 void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
195 {
196 struct ubus_object *obj = &wpa_s->ubus.obj;
197 char *name = (char *) obj->name;
198
199 if (!ctx)
200 return;
201
202 if (obj->id) {
203 ubus_remove_object(ctx, obj);
204 wpas_ubus_ref_dec();
205 }
206
207 free(name);
208 }
209
210 enum {
211 WPAS_CONFIG_DRIVER,
212 WPAS_CONFIG_IFACE,
213 WPAS_CONFIG_BRIDGE,
214 WPAS_CONFIG_HOSTAPD_CTRL,
215 WPAS_CONFIG_CTRL,
216 WPAS_CONFIG_FILE,
217 __WPAS_CONFIG_MAX
218 };
219
220 static const struct blobmsg_policy wpas_config_add_policy[__WPAS_CONFIG_MAX] = {
221 [WPAS_CONFIG_DRIVER] = { "driver", BLOBMSG_TYPE_STRING },
222 [WPAS_CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
223 [WPAS_CONFIG_BRIDGE] = { "bridge", BLOBMSG_TYPE_STRING },
224 [WPAS_CONFIG_HOSTAPD_CTRL] = { "hostapd_ctrl", BLOBMSG_TYPE_STRING },
225 [WPAS_CONFIG_CTRL] = { "ctrl", BLOBMSG_TYPE_STRING },
226 [WPAS_CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
227 };
228
229 static int
230 wpas_config_add(struct ubus_context *ctx, struct ubus_object *obj,
231 struct ubus_request_data *req, const char *method,
232 struct blob_attr *msg)
233 {
234 struct blob_attr *tb[__WPAS_CONFIG_MAX];
235 struct wpa_global *global = get_wpa_global_from_object(obj);
236 struct wpa_interface *iface;
237
238 blobmsg_parse(wpas_config_add_policy, __WPAS_CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
239
240 if (!tb[WPAS_CONFIG_FILE] || !tb[WPAS_CONFIG_IFACE] || !tb[WPAS_CONFIG_DRIVER])
241 return UBUS_STATUS_INVALID_ARGUMENT;
242
243 iface = os_zalloc(sizeof(struct wpa_interface));
244 if (iface == NULL)
245 return UBUS_STATUS_UNKNOWN_ERROR;
246
247 iface->driver = blobmsg_get_string(tb[WPAS_CONFIG_DRIVER]);
248 iface->ifname = blobmsg_get_string(tb[WPAS_CONFIG_IFACE]);
249 iface->confname = blobmsg_get_string(tb[WPAS_CONFIG_FILE]);
250
251 if (tb[WPAS_CONFIG_BRIDGE])
252 iface->bridge_ifname = blobmsg_get_string(tb[WPAS_CONFIG_BRIDGE]);
253
254 if (tb[WPAS_CONFIG_CTRL])
255 iface->ctrl_interface = blobmsg_get_string(tb[WPAS_CONFIG_CTRL]);
256
257 if (tb[WPAS_CONFIG_HOSTAPD_CTRL])
258 iface->hostapd_ctrl = blobmsg_get_string(tb[WPAS_CONFIG_HOSTAPD_CTRL]);
259
260 if (!wpa_supplicant_add_iface(global, iface, NULL))
261 return UBUS_STATUS_INVALID_ARGUMENT;
262
263 blob_buf_init(&b, 0);
264 blobmsg_add_u32(&b, "pid", getpid());
265 ubus_send_reply(ctx, req, b.head);
266
267 return UBUS_STATUS_OK;
268 }
269
270 enum {
271 WPAS_CONFIG_REM_IFACE,
272 __WPAS_CONFIG_REM_MAX
273 };
274
275 static const struct blobmsg_policy wpas_config_remove_policy[__WPAS_CONFIG_REM_MAX] = {
276 [WPAS_CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
277 };
278
279 static int
280 wpas_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
281 struct ubus_request_data *req, const char *method,
282 struct blob_attr *msg)
283 {
284 struct blob_attr *tb[__WPAS_CONFIG_REM_MAX];
285 struct wpa_global *global = get_wpa_global_from_object(obj);
286 struct wpa_supplicant *wpa_s = NULL;
287 unsigned int found = 0;
288
289 blobmsg_parse(wpas_config_remove_policy, __WPAS_CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
290
291 if (!tb[WPAS_CONFIG_REM_IFACE])
292 return UBUS_STATUS_INVALID_ARGUMENT;
293
294 /* find wpa_s object for to-be-removed interface */
295 for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
296 if (!strncmp(wpa_s->ifname,
297 blobmsg_get_string(tb[WPAS_CONFIG_REM_IFACE]),
298 sizeof(wpa_s->ifname)))
299 {
300 found = 1;
301 break;
302 }
303 }
304
305 if (!found)
306 return UBUS_STATUS_INVALID_ARGUMENT;
307
308 if (wpa_supplicant_remove_iface(global, wpa_s, 0))
309 return UBUS_STATUS_INVALID_ARGUMENT;
310
311 return UBUS_STATUS_OK;
312 }
313
314 static const struct ubus_method wpas_daemon_methods[] = {
315 UBUS_METHOD("config_add", wpas_config_add, wpas_config_add_policy),
316 UBUS_METHOD("config_remove", wpas_config_remove, wpas_config_remove_policy),
317 };
318
319 static struct ubus_object_type wpas_daemon_object_type =
320 UBUS_OBJECT_TYPE("wpa_supplicant", wpas_daemon_methods);
321
322 void wpas_ubus_add(struct wpa_global *global)
323 {
324 struct ubus_object *obj = &global->ubus_global;
325 int ret;
326
327 if (!wpas_ubus_init())
328 return;
329
330 obj->name = strdup("wpa_supplicant");
331
332 obj->type = &wpas_daemon_object_type;
333 obj->methods = wpas_daemon_object_type.methods;
334 obj->n_methods = wpas_daemon_object_type.n_methods;
335 ret = ubus_add_object(ctx, obj);
336 wpas_ubus_ref_inc();
337 }
338
339 void wpas_ubus_free(struct wpa_global *global)
340 {
341 struct ubus_object *obj = &global->ubus_global;
342 char *name = (char *) obj->name;
343
344 if (!ctx)
345 return;
346
347 if (obj->id) {
348 ubus_remove_object(ctx, obj);
349 wpas_ubus_ref_dec();
350 }
351
352 free(name);
353 }
354
355
356 #ifdef CONFIG_WPS
357 void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
358 {
359 u16 auth_type;
360 char *ifname, *encryption, *ssid, *key;
361 size_t ifname_len;
362
363 if (!cred)
364 return;
365
366 auth_type = cred->auth_type;
367
368 if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))
369 auth_type = WPS_AUTH_WPA2PSK;
370
371 if (auth_type != WPS_AUTH_OPEN &&
372 auth_type != WPS_AUTH_WPAPSK &&
373 auth_type != WPS_AUTH_WPA2PSK) {
374 wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
375 "unsupported authentication type 0x%x",
376 auth_type);
377 return;
378 }
379
380 if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
381 if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
382 wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
383 "invalid Network Key length %lu",
384 (unsigned long) cred->key_len);
385 return;
386 }
387 }
388
389 blob_buf_init(&b, 0);
390
391 ifname_len = strlen(wpa_s->ifname);
392 ifname = blobmsg_alloc_string_buffer(&b, "ifname", ifname_len + 1);
393 memcpy(ifname, wpa_s->ifname, ifname_len + 1);
394 ifname[ifname_len] = '\0';
395 blobmsg_add_string_buffer(&b);
396
397 switch (auth_type) {
398 case WPS_AUTH_WPA2PSK:
399 encryption = "psk2";
400 break;
401 case WPS_AUTH_WPAPSK:
402 encryption = "psk";
403 break;
404 default:
405 encryption = "none";
406 break;
407 }
408
409 blobmsg_add_string(&b, "encryption", encryption);
410
411 ssid = blobmsg_alloc_string_buffer(&b, "ssid", cred->ssid_len + 1);
412 memcpy(ssid, cred->ssid, cred->ssid_len);
413 ssid[cred->ssid_len] = '\0';
414 blobmsg_add_string_buffer(&b);
415
416 if (cred->key_len > 0) {
417 key = blobmsg_alloc_string_buffer(&b, "key", cred->key_len + 1);
418 memcpy(key, cred->key, cred->key_len);
419 key[cred->key_len] = '\0';
420 blobmsg_add_string_buffer(&b);
421 }
422
423 // ubus_notify(ctx, &wpa_s->ubus.obj, "wps_credentials", b.head, -1);
424 ubus_send_event(ctx, "wps_credentials", b.head);
425 }
426 #endif /* CONFIG_WPS */