1189251Ssam/*
2189251Ssam * WPA Supplicant / privileged helper program
3189251Ssam * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam#ifdef __linux__
11189251Ssam#include <fcntl.h>
12189251Ssam#endif /* __linux__ */
13189251Ssam#include <sys/un.h>
14189251Ssam#include <sys/stat.h>
15189251Ssam
16189251Ssam#include "common.h"
17189251Ssam#include "eloop.h"
18214734Srpaulo#include "common/version.h"
19189251Ssam#include "drivers/driver.h"
20189251Ssam#include "l2_packet/l2_packet.h"
21214734Srpaulo#include "common/privsep_commands.h"
22214734Srpaulo#include "common/ieee802_11_defs.h"
23189251Ssam
24189251Ssam
25189251Ssamstruct wpa_priv_interface {
26189251Ssam	struct wpa_priv_interface *next;
27189251Ssam	char *driver_name;
28189251Ssam	char *ifname;
29189251Ssam	char *sock_name;
30189251Ssam	int fd;
31189251Ssam
32189251Ssam	struct wpa_driver_ops *driver;
33189251Ssam	void *drv_priv;
34189251Ssam	struct sockaddr_un drv_addr;
35189251Ssam	int wpas_registered;
36189251Ssam
37189251Ssam	/* TODO: add support for multiple l2 connections */
38189251Ssam	struct l2_packet_data *l2;
39189251Ssam	struct sockaddr_un l2_addr;
40189251Ssam};
41189251Ssam
42189251Ssam
43189251Ssamstatic void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
44189251Ssam				  struct sockaddr_un *from)
45189251Ssam{
46189251Ssam	if (iface->drv_priv) {
47189251Ssam		wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance");
48189251Ssam		if (iface->driver->deinit)
49189251Ssam			iface->driver->deinit(iface->drv_priv);
50189251Ssam		iface->drv_priv = NULL;
51189251Ssam		iface->wpas_registered = 0;
52189251Ssam	}
53189251Ssam
54189251Ssam	if (iface->l2) {
55189251Ssam		wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
56189251Ssam			   "instance");
57189251Ssam		l2_packet_deinit(iface->l2);
58189251Ssam		iface->l2 = NULL;
59189251Ssam	}
60189251Ssam
61189251Ssam	if (iface->driver->init == NULL)
62189251Ssam		return;
63189251Ssam
64189251Ssam	iface->drv_priv = iface->driver->init(iface, iface->ifname);
65189251Ssam	if (iface->drv_priv == NULL) {
66189251Ssam		wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper");
67189251Ssam		return;
68189251Ssam	}
69189251Ssam
70189251Ssam	wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface "
71189251Ssam		   "'%s'", iface->driver_name, iface->ifname);
72189251Ssam
73189251Ssam	os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr));
74189251Ssam	iface->wpas_registered = 1;
75189251Ssam
76189251Ssam	if (iface->driver->set_param &&
77189251Ssam	    iface->driver->set_param(iface->drv_priv, NULL) < 0) {
78189251Ssam		wpa_printf(MSG_ERROR, "Driver interface rejected param");
79189251Ssam	}
80189251Ssam}
81189251Ssam
82189251Ssam
83189251Ssamstatic void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface,
84189251Ssam				    struct sockaddr_un *from)
85189251Ssam{
86189251Ssam	if (iface->drv_priv) {
87189251Ssam		if (iface->driver->deinit)
88189251Ssam			iface->driver->deinit(iface->drv_priv);
89189251Ssam		iface->drv_priv = NULL;
90189251Ssam		iface->wpas_registered = 0;
91189251Ssam	}
92189251Ssam}
93189251Ssam
94189251Ssam
95189251Ssamstatic void wpa_priv_cmd_scan(struct wpa_priv_interface *iface,
96189251Ssam			      char *buf, size_t len)
97189251Ssam{
98214734Srpaulo	struct wpa_driver_scan_params params;
99214734Srpaulo
100189251Ssam	if (iface->drv_priv == NULL)
101189251Ssam		return;
102189251Ssam
103214734Srpaulo	os_memset(&params, 0, sizeof(params));
104214734Srpaulo	if (len) {
105214734Srpaulo		params.ssids[0].ssid = (u8 *) buf;
106214734Srpaulo		params.ssids[0].ssid_len = len;
107214734Srpaulo		params.num_ssids = 1;
108214734Srpaulo	}
109214734Srpaulo
110214734Srpaulo	if (iface->driver->scan2)
111214734Srpaulo		iface->driver->scan2(iface->drv_priv, &params);
112189251Ssam}
113189251Ssam
114189251Ssam
115189251Ssamstatic void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface,
116189251Ssam				       struct sockaddr_un *from)
117189251Ssam{
118189251Ssam	struct wpa_scan_results *res;
119189251Ssam	u8 *buf = NULL, *pos, *end;
120189251Ssam	int val;
121189251Ssam	size_t i;
122189251Ssam
123189251Ssam	res = iface->driver->get_scan_results2(iface->drv_priv);
124189251Ssam	if (res == NULL)
125189251Ssam		goto fail;
126189251Ssam
127189251Ssam	buf = os_malloc(60000);
128189251Ssam	if (buf == NULL)
129189251Ssam		goto fail;
130189251Ssam	pos = buf;
131189251Ssam	end = buf + 60000;
132189251Ssam	val = res->num;
133189251Ssam	os_memcpy(pos, &val, sizeof(int));
134189251Ssam	pos += sizeof(int);
135189251Ssam
136189251Ssam	for (i = 0; i < res->num; i++) {
137189251Ssam		struct wpa_scan_res *r = res->res[i];
138189251Ssam		val = sizeof(*r) + r->ie_len;
139189251Ssam		if (end - pos < (int) sizeof(int) + val)
140189251Ssam			break;
141189251Ssam		os_memcpy(pos, &val, sizeof(int));
142189251Ssam		pos += sizeof(int);
143189251Ssam		os_memcpy(pos, r, val);
144189251Ssam		pos += val;
145189251Ssam	}
146189251Ssam
147189251Ssam	sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from,
148189251Ssam	       sizeof(*from));
149189251Ssam
150189251Ssam	os_free(buf);
151209158Srpaulo	wpa_scan_results_free(res);
152189251Ssam	return;
153189251Ssam
154189251Ssamfail:
155189251Ssam	os_free(buf);
156209158Srpaulo	wpa_scan_results_free(res);
157189251Ssam	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
158189251Ssam}
159189251Ssam
160189251Ssam
161189251Ssamstatic void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface,
162189251Ssam					  struct sockaddr_un *from)
163189251Ssam{
164189251Ssam	if (iface->drv_priv == NULL)
165189251Ssam		return;
166189251Ssam
167189251Ssam	if (iface->driver->get_scan_results2)
168189251Ssam		wpa_priv_get_scan_results2(iface, from);
169189251Ssam	else
170189251Ssam		sendto(iface->fd, "", 0, 0, (struct sockaddr *) from,
171189251Ssam		       sizeof(*from));
172189251Ssam}
173189251Ssam
174189251Ssam
175189251Ssamstatic void wpa_priv_cmd_associate(struct wpa_priv_interface *iface,
176189251Ssam				   void *buf, size_t len)
177189251Ssam{
178189251Ssam	struct wpa_driver_associate_params params;
179189251Ssam	struct privsep_cmd_associate *assoc;
180189251Ssam	u8 *bssid;
181189251Ssam	int res;
182189251Ssam
183189251Ssam	if (iface->drv_priv == NULL || iface->driver->associate == NULL)
184189251Ssam		return;
185189251Ssam
186189251Ssam	if (len < sizeof(*assoc)) {
187189251Ssam		wpa_printf(MSG_DEBUG, "Invalid association request");
188189251Ssam		return;
189189251Ssam	}
190189251Ssam
191189251Ssam	assoc = buf;
192189251Ssam	if (sizeof(*assoc) + assoc->wpa_ie_len > len) {
193189251Ssam		wpa_printf(MSG_DEBUG, "Association request overflow");
194189251Ssam		return;
195189251Ssam	}
196189251Ssam
197189251Ssam	os_memset(&params, 0, sizeof(params));
198189251Ssam	bssid = assoc->bssid;
199189251Ssam	if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5])
200189251Ssam		params.bssid = bssid;
201189251Ssam	params.ssid = assoc->ssid;
202189251Ssam	if (assoc->ssid_len > 32)
203189251Ssam		return;
204189251Ssam	params.ssid_len = assoc->ssid_len;
205189251Ssam	params.freq = assoc->freq;
206189251Ssam	if (assoc->wpa_ie_len) {
207189251Ssam		params.wpa_ie = (u8 *) (assoc + 1);
208189251Ssam		params.wpa_ie_len = assoc->wpa_ie_len;
209189251Ssam	}
210189251Ssam	params.pairwise_suite = assoc->pairwise_suite;
211189251Ssam	params.group_suite = assoc->group_suite;
212189251Ssam	params.key_mgmt_suite = assoc->key_mgmt_suite;
213189251Ssam	params.auth_alg = assoc->auth_alg;
214189251Ssam	params.mode = assoc->mode;
215189251Ssam
216189251Ssam	res = iface->driver->associate(iface->drv_priv, &params);
217189251Ssam	wpa_printf(MSG_DEBUG, "drv->associate: res=%d", res);
218189251Ssam}
219189251Ssam
220189251Ssam
221189251Ssamstatic void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface,
222189251Ssam				   struct sockaddr_un *from)
223189251Ssam{
224189251Ssam	u8 bssid[ETH_ALEN];
225189251Ssam
226189251Ssam	if (iface->drv_priv == NULL)
227189251Ssam		goto fail;
228189251Ssam
229189251Ssam	if (iface->driver->get_bssid == NULL ||
230189251Ssam	    iface->driver->get_bssid(iface->drv_priv, bssid) < 0)
231189251Ssam		goto fail;
232189251Ssam
233189251Ssam	sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from,
234189251Ssam	       sizeof(*from));
235189251Ssam	return;
236189251Ssam
237189251Ssamfail:
238189251Ssam	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
239189251Ssam}
240189251Ssam
241189251Ssam
242189251Ssamstatic void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface,
243189251Ssam				  struct sockaddr_un *from)
244189251Ssam{
245189251Ssam	u8 ssid[sizeof(int) + 32];
246189251Ssam	int res;
247189251Ssam
248189251Ssam	if (iface->drv_priv == NULL)
249189251Ssam		goto fail;
250189251Ssam
251189251Ssam	if (iface->driver->get_ssid == NULL)
252189251Ssam		goto fail;
253189251Ssam
254189251Ssam	res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]);
255189251Ssam	if (res < 0 || res > 32)
256189251Ssam		goto fail;
257189251Ssam	os_memcpy(ssid, &res, sizeof(int));
258189251Ssam
259189251Ssam	sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from,
260189251Ssam	       sizeof(*from));
261189251Ssam	return;
262189251Ssam
263189251Ssamfail:
264189251Ssam	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
265189251Ssam}
266189251Ssam
267189251Ssam
268189251Ssamstatic void wpa_priv_cmd_set_key(struct wpa_priv_interface *iface,
269189251Ssam				 void *buf, size_t len)
270189251Ssam{
271189251Ssam	struct privsep_cmd_set_key *params;
272189251Ssam	int res;
273189251Ssam
274189251Ssam	if (iface->drv_priv == NULL || iface->driver->set_key == NULL)
275189251Ssam		return;
276189251Ssam
277189251Ssam	if (len != sizeof(*params)) {
278189251Ssam		wpa_printf(MSG_DEBUG, "Invalid set_key request");
279189251Ssam		return;
280189251Ssam	}
281189251Ssam
282189251Ssam	params = buf;
283189251Ssam
284214734Srpaulo	res = iface->driver->set_key(iface->ifname, iface->drv_priv,
285214734Srpaulo				     params->alg,
286189251Ssam				     params->addr, params->key_idx,
287189251Ssam				     params->set_tx,
288189251Ssam				     params->seq_len ? params->seq : NULL,
289189251Ssam				     params->seq_len,
290189251Ssam				     params->key_len ? params->key : NULL,
291189251Ssam				     params->key_len);
292189251Ssam	wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res);
293189251Ssam}
294189251Ssam
295189251Ssam
296189251Ssamstatic void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface,
297189251Ssam				  struct sockaddr_un *from)
298189251Ssam{
299189251Ssam	struct wpa_driver_capa capa;
300189251Ssam
301189251Ssam	if (iface->drv_priv == NULL)
302189251Ssam		goto fail;
303189251Ssam
304189251Ssam	if (iface->driver->get_capa == NULL ||
305189251Ssam	    iface->driver->get_capa(iface->drv_priv, &capa) < 0)
306189251Ssam		goto fail;
307189251Ssam
308189251Ssam	sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from,
309189251Ssam	       sizeof(*from));
310189251Ssam	return;
311189251Ssam
312189251Ssamfail:
313189251Ssam	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
314189251Ssam}
315189251Ssam
316189251Ssam
317189251Ssamstatic void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
318189251Ssam			   size_t len)
319189251Ssam{
320189251Ssam	struct wpa_priv_interface *iface = ctx;
321189251Ssam	struct msghdr msg;
322189251Ssam	struct iovec io[2];
323189251Ssam
324189251Ssam	io[0].iov_base = (u8 *) src_addr;
325189251Ssam	io[0].iov_len = ETH_ALEN;
326189251Ssam	io[1].iov_base = (u8 *) buf;
327189251Ssam	io[1].iov_len = len;
328189251Ssam
329189251Ssam	os_memset(&msg, 0, sizeof(msg));
330189251Ssam	msg.msg_iov = io;
331189251Ssam	msg.msg_iovlen = 2;
332189251Ssam	msg.msg_name = &iface->l2_addr;
333189251Ssam	msg.msg_namelen = sizeof(iface->l2_addr);
334189251Ssam
335189251Ssam	if (sendmsg(iface->fd, &msg, 0) < 0) {
336189251Ssam		perror("sendmsg(l2 rx)");
337189251Ssam	}
338189251Ssam}
339189251Ssam
340189251Ssam
341189251Ssamstatic void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
342189251Ssam				     struct sockaddr_un *from,
343189251Ssam				     void *buf, size_t len)
344189251Ssam{
345189251Ssam	int *reg_cmd = buf;
346189251Ssam	u8 own_addr[ETH_ALEN];
347189251Ssam	int res;
348189251Ssam	u16 proto;
349189251Ssam
350189251Ssam	if (len != 2 * sizeof(int)) {
351189251Ssam		wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu",
352189251Ssam			   (unsigned long) len);
353189251Ssam		return;
354189251Ssam	}
355189251Ssam
356189251Ssam	proto = reg_cmd[0];
357189251Ssam	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
358189251Ssam		wpa_printf(MSG_DEBUG, "Refused l2_packet connection for "
359189251Ssam			   "ethertype 0x%x", proto);
360189251Ssam		return;
361189251Ssam	}
362189251Ssam
363189251Ssam	if (iface->l2) {
364189251Ssam		wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
365189251Ssam			   "instance");
366189251Ssam		l2_packet_deinit(iface->l2);
367189251Ssam		iface->l2 = NULL;
368189251Ssam	}
369189251Ssam
370189251Ssam	os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr));
371189251Ssam
372189251Ssam	iface->l2 = l2_packet_init(iface->ifname, NULL, proto,
373189251Ssam				   wpa_priv_l2_rx, iface, reg_cmd[1]);
374189251Ssam	if (iface->l2 == NULL) {
375189251Ssam		wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet "
376189251Ssam			   "instance for protocol %d", proto);
377189251Ssam		return;
378189251Ssam	}
379189251Ssam
380189251Ssam	if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) {
381189251Ssam		wpa_printf(MSG_DEBUG, "Failed to get own address from "
382189251Ssam			   "l2_packet");
383189251Ssam		l2_packet_deinit(iface->l2);
384189251Ssam		iface->l2 = NULL;
385189251Ssam		return;
386189251Ssam	}
387189251Ssam
388189251Ssam	res = sendto(iface->fd, own_addr, ETH_ALEN, 0,
389189251Ssam		     (struct sockaddr *) from, sizeof(*from));
390189251Ssam	wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res);
391189251Ssam}
392189251Ssam
393189251Ssam
394189251Ssamstatic void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface,
395189251Ssam				       struct sockaddr_un *from)
396189251Ssam{
397189251Ssam	if (iface->l2) {
398189251Ssam		l2_packet_deinit(iface->l2);
399189251Ssam		iface->l2 = NULL;
400189251Ssam	}
401189251Ssam}
402189251Ssam
403189251Ssam
404189251Ssamstatic void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface,
405189251Ssam					      struct sockaddr_un *from)
406189251Ssam{
407189251Ssam	if (iface->l2)
408189251Ssam		l2_packet_notify_auth_start(iface->l2);
409189251Ssam}
410189251Ssam
411189251Ssam
412189251Ssamstatic void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface,
413189251Ssam				 struct sockaddr_un *from,
414189251Ssam				 void *buf, size_t len)
415189251Ssam{
416189251Ssam	u8 *dst_addr;
417189251Ssam	u16 proto;
418189251Ssam	int res;
419189251Ssam
420189251Ssam	if (iface->l2 == NULL)
421189251Ssam		return;
422189251Ssam
423189251Ssam	if (len < ETH_ALEN + 2) {
424189251Ssam		wpa_printf(MSG_DEBUG, "Too short L2 send packet (len=%lu)",
425189251Ssam			   (unsigned long) len);
426189251Ssam		return;
427189251Ssam	}
428189251Ssam
429189251Ssam	dst_addr = buf;
430189251Ssam	os_memcpy(&proto, buf + ETH_ALEN, 2);
431189251Ssam
432189251Ssam	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
433189251Ssam		wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype "
434189251Ssam			   "0x%x", proto);
435189251Ssam		return;
436189251Ssam	}
437189251Ssam
438189251Ssam	res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2,
439189251Ssam			     len - ETH_ALEN - 2);
440189251Ssam	wpa_printf(MSG_DEBUG, "L2 send: res=%d", res);
441189251Ssam}
442189251Ssam
443189251Ssam
444189251Ssamstatic void wpa_priv_cmd_set_country(struct wpa_priv_interface *iface,
445189251Ssam				     char *buf)
446189251Ssam{
447189251Ssam	if (iface->drv_priv == NULL || iface->driver->set_country == NULL ||
448189251Ssam	    *buf == '\0')
449189251Ssam		return;
450189251Ssam
451189251Ssam	iface->driver->set_country(iface->drv_priv, buf);
452189251Ssam}
453189251Ssam
454189251Ssam
455189251Ssamstatic void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
456189251Ssam{
457189251Ssam	struct wpa_priv_interface *iface = eloop_ctx;
458189251Ssam	char buf[2000], *pos;
459189251Ssam	void *cmd_buf;
460189251Ssam	size_t cmd_len;
461189251Ssam	int res, cmd;
462189251Ssam	struct sockaddr_un from;
463189251Ssam	socklen_t fromlen = sizeof(from);
464189251Ssam
465189251Ssam	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
466189251Ssam		       &fromlen);
467189251Ssam	if (res < 0) {
468189251Ssam		perror("recvfrom");
469189251Ssam		return;
470189251Ssam	}
471189251Ssam
472189251Ssam	if (res < (int) sizeof(int)) {
473189251Ssam		wpa_printf(MSG_DEBUG, "Too short command (len=%d)", res);
474189251Ssam		return;
475189251Ssam	}
476189251Ssam
477189251Ssam	os_memcpy(&cmd, buf, sizeof(int));
478189251Ssam	wpa_printf(MSG_DEBUG, "Command %d for interface %s",
479189251Ssam		   cmd, iface->ifname);
480189251Ssam	cmd_buf = &buf[sizeof(int)];
481189251Ssam	cmd_len = res - sizeof(int);
482189251Ssam
483189251Ssam	switch (cmd) {
484189251Ssam	case PRIVSEP_CMD_REGISTER:
485189251Ssam		wpa_priv_cmd_register(iface, &from);
486189251Ssam		break;
487189251Ssam	case PRIVSEP_CMD_UNREGISTER:
488189251Ssam		wpa_priv_cmd_unregister(iface, &from);
489189251Ssam		break;
490189251Ssam	case PRIVSEP_CMD_SCAN:
491189251Ssam		wpa_priv_cmd_scan(iface, cmd_buf, cmd_len);
492189251Ssam		break;
493189251Ssam	case PRIVSEP_CMD_GET_SCAN_RESULTS:
494189251Ssam		wpa_priv_cmd_get_scan_results(iface, &from);
495189251Ssam		break;
496189251Ssam	case PRIVSEP_CMD_ASSOCIATE:
497189251Ssam		wpa_priv_cmd_associate(iface, cmd_buf, cmd_len);
498189251Ssam		break;
499189251Ssam	case PRIVSEP_CMD_GET_BSSID:
500189251Ssam		wpa_priv_cmd_get_bssid(iface, &from);
501189251Ssam		break;
502189251Ssam	case PRIVSEP_CMD_GET_SSID:
503189251Ssam		wpa_priv_cmd_get_ssid(iface, &from);
504189251Ssam		break;
505189251Ssam	case PRIVSEP_CMD_SET_KEY:
506189251Ssam		wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len);
507189251Ssam		break;
508189251Ssam	case PRIVSEP_CMD_GET_CAPA:
509189251Ssam		wpa_priv_cmd_get_capa(iface, &from);
510189251Ssam		break;
511189251Ssam	case PRIVSEP_CMD_L2_REGISTER:
512189251Ssam		wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len);
513189251Ssam		break;
514189251Ssam	case PRIVSEP_CMD_L2_UNREGISTER:
515189251Ssam		wpa_priv_cmd_l2_unregister(iface, &from);
516189251Ssam		break;
517189251Ssam	case PRIVSEP_CMD_L2_NOTIFY_AUTH_START:
518189251Ssam		wpa_priv_cmd_l2_notify_auth_start(iface, &from);
519189251Ssam		break;
520189251Ssam	case PRIVSEP_CMD_L2_SEND:
521189251Ssam		wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len);
522189251Ssam		break;
523189251Ssam	case PRIVSEP_CMD_SET_COUNTRY:
524189251Ssam		pos = cmd_buf;
525189251Ssam		if (pos + cmd_len >= buf + sizeof(buf))
526189251Ssam			break;
527189251Ssam		pos[cmd_len] = '\0';
528189251Ssam		wpa_priv_cmd_set_country(iface, pos);
529189251Ssam		break;
530189251Ssam	}
531189251Ssam}
532189251Ssam
533189251Ssam
534189251Ssamstatic void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
535189251Ssam{
536189251Ssam	if (iface->drv_priv && iface->driver->deinit)
537189251Ssam		iface->driver->deinit(iface->drv_priv);
538189251Ssam
539189251Ssam	if (iface->fd >= 0) {
540189251Ssam		eloop_unregister_read_sock(iface->fd);
541189251Ssam		close(iface->fd);
542189251Ssam		unlink(iface->sock_name);
543189251Ssam	}
544189251Ssam
545189251Ssam	if (iface->l2)
546189251Ssam		l2_packet_deinit(iface->l2);
547189251Ssam
548189251Ssam	os_free(iface->ifname);
549189251Ssam	os_free(iface->driver_name);
550189251Ssam	os_free(iface->sock_name);
551189251Ssam	os_free(iface);
552189251Ssam}
553189251Ssam
554189251Ssam
555214734Srpauloextern struct wpa_driver_ops *wpa_drivers[];
556189251Ssam
557189251Ssamstatic struct wpa_priv_interface *
558189251Ssamwpa_priv_interface_init(const char *dir, const char *params)
559189251Ssam{
560189251Ssam	struct wpa_priv_interface *iface;
561189251Ssam	char *pos;
562189251Ssam	size_t len;
563189251Ssam	struct sockaddr_un addr;
564189251Ssam	int i;
565189251Ssam
566189251Ssam	pos = os_strchr(params, ':');
567189251Ssam	if (pos == NULL)
568189251Ssam		return NULL;
569189251Ssam
570189251Ssam	iface = os_zalloc(sizeof(*iface));
571189251Ssam	if (iface == NULL)
572189251Ssam		return NULL;
573189251Ssam	iface->fd = -1;
574189251Ssam
575189251Ssam	len = pos - params;
576189251Ssam	iface->driver_name = os_malloc(len + 1);
577189251Ssam	if (iface->driver_name == NULL) {
578189251Ssam		wpa_priv_interface_deinit(iface);
579189251Ssam		return NULL;
580189251Ssam	}
581189251Ssam	os_memcpy(iface->driver_name, params, len);
582189251Ssam	iface->driver_name[len] = '\0';
583189251Ssam
584214734Srpaulo	for (i = 0; wpa_drivers[i]; i++) {
585189251Ssam		if (os_strcmp(iface->driver_name,
586214734Srpaulo			      wpa_drivers[i]->name) == 0) {
587214734Srpaulo			iface->driver = wpa_drivers[i];
588189251Ssam			break;
589189251Ssam		}
590189251Ssam	}
591189251Ssam	if (iface->driver == NULL) {
592189251Ssam		wpa_printf(MSG_ERROR, "Unsupported driver '%s'",
593189251Ssam			   iface->driver_name);
594189251Ssam		wpa_priv_interface_deinit(iface);
595189251Ssam		return NULL;
596189251Ssam	}
597189251Ssam
598189251Ssam	pos++;
599189251Ssam	iface->ifname = os_strdup(pos);
600189251Ssam	if (iface->ifname == NULL) {
601189251Ssam		wpa_priv_interface_deinit(iface);
602189251Ssam		return NULL;
603189251Ssam	}
604189251Ssam
605189251Ssam	len = os_strlen(dir) + 1 + os_strlen(iface->ifname);
606189251Ssam	iface->sock_name = os_malloc(len + 1);
607189251Ssam	if (iface->sock_name == NULL) {
608189251Ssam		wpa_priv_interface_deinit(iface);
609189251Ssam		return NULL;
610189251Ssam	}
611189251Ssam
612189251Ssam	os_snprintf(iface->sock_name, len + 1, "%s/%s", dir, iface->ifname);
613189251Ssam	if (os_strlen(iface->sock_name) >= sizeof(addr.sun_path)) {
614189251Ssam		wpa_priv_interface_deinit(iface);
615189251Ssam		return NULL;
616189251Ssam	}
617189251Ssam
618189251Ssam	iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
619189251Ssam	if (iface->fd < 0) {
620189251Ssam		perror("socket(PF_UNIX)");
621189251Ssam		wpa_priv_interface_deinit(iface);
622189251Ssam		return NULL;
623189251Ssam	}
624189251Ssam
625189251Ssam	os_memset(&addr, 0, sizeof(addr));
626189251Ssam	addr.sun_family = AF_UNIX;
627189251Ssam	os_strlcpy(addr.sun_path, iface->sock_name, sizeof(addr.sun_path));
628189251Ssam
629189251Ssam	if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
630189251Ssam		wpa_printf(MSG_DEBUG, "bind(PF_UNIX) failed: %s",
631189251Ssam			   strerror(errno));
632189251Ssam		if (connect(iface->fd, (struct sockaddr *) &addr,
633189251Ssam			    sizeof(addr)) < 0) {
634189251Ssam			wpa_printf(MSG_DEBUG, "Socket exists, but does not "
635189251Ssam				   "allow connections - assuming it was "
636189251Ssam				   "leftover from forced program termination");
637189251Ssam			if (unlink(iface->sock_name) < 0) {
638189251Ssam				perror("unlink[ctrl_iface]");
639189251Ssam				wpa_printf(MSG_ERROR, "Could not unlink "
640189251Ssam					   "existing ctrl_iface socket '%s'",
641189251Ssam					   iface->sock_name);
642189251Ssam				goto fail;
643189251Ssam			}
644189251Ssam			if (bind(iface->fd, (struct sockaddr *) &addr,
645189251Ssam				 sizeof(addr)) < 0) {
646252726Srpaulo				perror("wpa-priv-iface-init: bind(PF_UNIX)");
647189251Ssam				goto fail;
648189251Ssam			}
649189251Ssam			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
650189251Ssam				   "socket '%s'", iface->sock_name);
651189251Ssam		} else {
652189251Ssam			wpa_printf(MSG_INFO, "Socket exists and seems to be "
653189251Ssam				   "in use - cannot override it");
654189251Ssam			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
655189251Ssam				   "not used anymore", iface->sock_name);
656189251Ssam			goto fail;
657189251Ssam		}
658189251Ssam	}
659189251Ssam
660189251Ssam	if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
661189251Ssam		perror("chmod");
662189251Ssam		goto fail;
663189251Ssam	}
664189251Ssam
665189251Ssam	eloop_register_read_sock(iface->fd, wpa_priv_receive, iface, NULL);
666189251Ssam
667189251Ssam	return iface;
668189251Ssam
669189251Ssamfail:
670189251Ssam	wpa_priv_interface_deinit(iface);
671189251Ssam	return NULL;
672189251Ssam}
673189251Ssam
674189251Ssam
675189251Ssamstatic int wpa_priv_send_event(struct wpa_priv_interface *iface, int event,
676189251Ssam			       const void *data, size_t data_len)
677189251Ssam{
678189251Ssam	struct msghdr msg;
679189251Ssam	struct iovec io[2];
680189251Ssam
681189251Ssam	io[0].iov_base = &event;
682189251Ssam	io[0].iov_len = sizeof(event);
683189251Ssam	io[1].iov_base = (u8 *) data;
684189251Ssam	io[1].iov_len = data_len;
685189251Ssam
686189251Ssam	os_memset(&msg, 0, sizeof(msg));
687189251Ssam	msg.msg_iov = io;
688189251Ssam	msg.msg_iovlen = data ? 2 : 1;
689189251Ssam	msg.msg_name = &iface->drv_addr;
690189251Ssam	msg.msg_namelen = sizeof(iface->drv_addr);
691189251Ssam
692189251Ssam	if (sendmsg(iface->fd, &msg, 0) < 0) {
693189251Ssam		perror("sendmsg(wpas_socket)");
694189251Ssam		return -1;
695189251Ssam	}
696189251Ssam
697189251Ssam	return 0;
698189251Ssam}
699189251Ssam
700189251Ssam
701189251Ssamstatic void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event,
702189251Ssam				union wpa_event_data *data)
703189251Ssam{
704189251Ssam	size_t buflen = 3 * sizeof(int);
705189251Ssam	u8 *buf, *pos;
706189251Ssam	int len;
707189251Ssam
708189251Ssam	if (data) {
709189251Ssam		buflen += data->assoc_info.req_ies_len +
710189251Ssam			data->assoc_info.resp_ies_len +
711189251Ssam			data->assoc_info.beacon_ies_len;
712189251Ssam	}
713189251Ssam
714189251Ssam	buf = os_malloc(buflen);
715189251Ssam	if (buf == NULL)
716189251Ssam		return;
717189251Ssam
718189251Ssam	pos = buf;
719189251Ssam
720189251Ssam	if (data && data->assoc_info.req_ies) {
721189251Ssam		len = data->assoc_info.req_ies_len;
722189251Ssam		os_memcpy(pos, &len, sizeof(int));
723189251Ssam		pos += sizeof(int);
724189251Ssam		os_memcpy(pos, data->assoc_info.req_ies, len);
725189251Ssam		pos += len;
726189251Ssam	} else {
727189251Ssam		len = 0;
728189251Ssam		os_memcpy(pos, &len, sizeof(int));
729189251Ssam		pos += sizeof(int);
730189251Ssam	}
731189251Ssam
732189251Ssam	if (data && data->assoc_info.resp_ies) {
733189251Ssam		len = data->assoc_info.resp_ies_len;
734189251Ssam		os_memcpy(pos, &len, sizeof(int));
735189251Ssam		pos += sizeof(int);
736189251Ssam		os_memcpy(pos, data->assoc_info.resp_ies, len);
737189251Ssam		pos += len;
738189251Ssam	} else {
739189251Ssam		len = 0;
740189251Ssam		os_memcpy(pos, &len, sizeof(int));
741189251Ssam		pos += sizeof(int);
742189251Ssam	}
743189251Ssam
744189251Ssam	if (data && data->assoc_info.beacon_ies) {
745189251Ssam		len = data->assoc_info.beacon_ies_len;
746189251Ssam		os_memcpy(pos, &len, sizeof(int));
747189251Ssam		pos += sizeof(int);
748189251Ssam		os_memcpy(pos, data->assoc_info.beacon_ies, len);
749189251Ssam		pos += len;
750189251Ssam	} else {
751189251Ssam		len = 0;
752189251Ssam		os_memcpy(pos, &len, sizeof(int));
753189251Ssam		pos += sizeof(int);
754189251Ssam	}
755189251Ssam
756189251Ssam	wpa_priv_send_event(iface, event, buf, buflen);
757189251Ssam
758189251Ssam	os_free(buf);
759189251Ssam}
760189251Ssam
761189251Ssam
762189251Ssamstatic void wpa_priv_send_interface_status(struct wpa_priv_interface *iface,
763189251Ssam					   union wpa_event_data *data)
764189251Ssam{
765189251Ssam	int ievent;
766189251Ssam	size_t len, maxlen;
767189251Ssam	u8 *buf;
768189251Ssam	char *ifname;
769189251Ssam
770189251Ssam	if (data == NULL)
771189251Ssam		return;
772189251Ssam
773189251Ssam	ievent = data->interface_status.ievent;
774189251Ssam	maxlen = sizeof(data->interface_status.ifname);
775189251Ssam	ifname = data->interface_status.ifname;
776189251Ssam	for (len = 0; len < maxlen && ifname[len]; len++)
777189251Ssam		;
778189251Ssam
779189251Ssam	buf = os_malloc(sizeof(int) + len);
780189251Ssam	if (buf == NULL)
781189251Ssam		return;
782189251Ssam
783189251Ssam	os_memcpy(buf, &ievent, sizeof(int));
784189251Ssam	os_memcpy(buf + sizeof(int), ifname, len);
785189251Ssam
786189251Ssam	wpa_priv_send_event(iface, PRIVSEP_EVENT_INTERFACE_STATUS,
787189251Ssam			    buf, sizeof(int) + len);
788189251Ssam
789189251Ssam	os_free(buf);
790189251Ssam
791189251Ssam}
792189251Ssam
793189251Ssam
794189251Ssamstatic void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
795189251Ssam				      union wpa_event_data *data)
796189251Ssam{
797189251Ssam	size_t len;
798189251Ssam	u8 *buf, *pos;
799189251Ssam
800189251Ssam	if (data == NULL || data->ft_ies.ies == NULL)
801189251Ssam		return;
802189251Ssam
803189251Ssam	len = sizeof(int) + ETH_ALEN + data->ft_ies.ies_len;
804189251Ssam	buf = os_malloc(len);
805189251Ssam	if (buf == NULL)
806189251Ssam		return;
807189251Ssam
808189251Ssam	pos = buf;
809189251Ssam	os_memcpy(pos, &data->ft_ies.ft_action, sizeof(int));
810189251Ssam	pos += sizeof(int);
811189251Ssam	os_memcpy(pos, data->ft_ies.target_ap, ETH_ALEN);
812189251Ssam	pos += ETH_ALEN;
813189251Ssam	os_memcpy(pos, data->ft_ies.ies, data->ft_ies.ies_len);
814189251Ssam
815189251Ssam	wpa_priv_send_event(iface, PRIVSEP_EVENT_FT_RESPONSE, buf, len);
816189251Ssam
817189251Ssam	os_free(buf);
818189251Ssam
819189251Ssam}
820189251Ssam
821189251Ssam
822252726Srpaulovoid wpa_supplicant_event(void *ctx, enum wpa_event_type event,
823189251Ssam			  union wpa_event_data *data)
824189251Ssam{
825189251Ssam	struct wpa_priv_interface *iface = ctx;
826189251Ssam
827189251Ssam	wpa_printf(MSG_DEBUG, "%s - event=%d", __func__, event);
828189251Ssam
829189251Ssam	if (!iface->wpas_registered) {
830189251Ssam		wpa_printf(MSG_DEBUG, "Driver event received, but "
831189251Ssam			   "wpa_supplicant not registered");
832189251Ssam		return;
833189251Ssam	}
834189251Ssam
835189251Ssam	switch (event) {
836189251Ssam	case EVENT_ASSOC:
837189251Ssam		wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOC, data);
838189251Ssam		break;
839189251Ssam	case EVENT_DISASSOC:
840189251Ssam		wpa_priv_send_event(iface, PRIVSEP_EVENT_DISASSOC, NULL, 0);
841189251Ssam		break;
842189251Ssam	case EVENT_ASSOCINFO:
843189251Ssam		if (data == NULL)
844189251Ssam			return;
845189251Ssam		wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOCINFO, data);
846189251Ssam		break;
847189251Ssam	case EVENT_MICHAEL_MIC_FAILURE:
848189251Ssam		if (data == NULL)
849189251Ssam			return;
850189251Ssam		wpa_priv_send_event(iface, PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
851189251Ssam				    &data->michael_mic_failure.unicast,
852189251Ssam				    sizeof(int));
853189251Ssam		break;
854189251Ssam	case EVENT_SCAN_RESULTS:
855189251Ssam		wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL,
856189251Ssam				    0);
857189251Ssam		break;
858189251Ssam	case EVENT_INTERFACE_STATUS:
859189251Ssam		wpa_priv_send_interface_status(iface, data);
860189251Ssam		break;
861189251Ssam	case EVENT_PMKID_CANDIDATE:
862189251Ssam		if (data == NULL)
863189251Ssam			return;
864189251Ssam		wpa_priv_send_event(iface, PRIVSEP_EVENT_PMKID_CANDIDATE,
865189251Ssam				    &data->pmkid_candidate,
866189251Ssam				    sizeof(struct pmkid_candidate));
867189251Ssam		break;
868189251Ssam	case EVENT_STKSTART:
869189251Ssam		if (data == NULL)
870189251Ssam			return;
871189251Ssam		wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART,
872189251Ssam				    &data->stkstart.peer, ETH_ALEN);
873189251Ssam		break;
874189251Ssam	case EVENT_FT_RESPONSE:
875189251Ssam		wpa_priv_send_ft_response(iface, data);
876189251Ssam		break;
877189251Ssam	default:
878189251Ssam		wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO",
879189251Ssam			   event);
880189251Ssam		break;
881189251Ssam	}
882189251Ssam}
883189251Ssam
884189251Ssam
885189251Ssamvoid wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
886189251Ssam			     const u8 *buf, size_t len)
887189251Ssam{
888189251Ssam	struct wpa_priv_interface *iface = ctx;
889189251Ssam	struct msghdr msg;
890189251Ssam	struct iovec io[3];
891189251Ssam	int event = PRIVSEP_EVENT_RX_EAPOL;
892189251Ssam
893189251Ssam	wpa_printf(MSG_DEBUG, "RX EAPOL from driver");
894189251Ssam	io[0].iov_base = &event;
895189251Ssam	io[0].iov_len = sizeof(event);
896189251Ssam	io[1].iov_base = (u8 *) src_addr;
897189251Ssam	io[1].iov_len = ETH_ALEN;
898189251Ssam	io[2].iov_base = (u8 *) buf;
899189251Ssam	io[2].iov_len = len;
900189251Ssam
901189251Ssam	os_memset(&msg, 0, sizeof(msg));
902189251Ssam	msg.msg_iov = io;
903189251Ssam	msg.msg_iovlen = 3;
904189251Ssam	msg.msg_name = &iface->drv_addr;
905189251Ssam	msg.msg_namelen = sizeof(iface->drv_addr);
906189251Ssam
907189251Ssam	if (sendmsg(iface->fd, &msg, 0) < 0)
908189251Ssam		perror("sendmsg(wpas_socket)");
909189251Ssam}
910189251Ssam
911189251Ssam
912252726Srpaulostatic void wpa_priv_terminate(int sig, void *signal_ctx)
913189251Ssam{
914189251Ssam	wpa_printf(MSG_DEBUG, "wpa_priv termination requested");
915189251Ssam	eloop_terminate();
916189251Ssam}
917189251Ssam
918189251Ssam
919189251Ssamstatic void wpa_priv_fd_workaround(void)
920189251Ssam{
921189251Ssam#ifdef __linux__
922189251Ssam	int s, i;
923189251Ssam	/* When started from pcmcia-cs scripts, wpa_supplicant might start with
924189251Ssam	 * fd 0, 1, and 2 closed. This will cause some issues because many
925189251Ssam	 * places in wpa_supplicant are still printing out to stdout. As a
926189251Ssam	 * workaround, make sure that fd's 0, 1, and 2 are not used for other
927189251Ssam	 * sockets. */
928189251Ssam	for (i = 0; i < 3; i++) {
929189251Ssam		s = open("/dev/null", O_RDWR);
930189251Ssam		if (s > 2) {
931189251Ssam			close(s);
932189251Ssam			break;
933189251Ssam		}
934189251Ssam	}
935189251Ssam#endif /* __linux__ */
936189251Ssam}
937189251Ssam
938189251Ssam
939189251Ssamstatic void usage(void)
940189251Ssam{
941189251Ssam	printf("wpa_priv v" VERSION_STR "\n"
942189251Ssam	       "Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> and "
943189251Ssam	       "contributors\n"
944189251Ssam	       "\n"
945189251Ssam	       "usage:\n"
946189251Ssam	       "  wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> "
947189251Ssam	       "[driver:ifname ...]\n");
948189251Ssam}
949189251Ssam
950189251Ssam
951189251Ssamextern int wpa_debug_level;
952189251Ssam
953189251Ssamint main(int argc, char *argv[])
954189251Ssam{
955189251Ssam	int c, i;
956189251Ssam	int ret = -1;
957189251Ssam	char *pid_file = NULL;
958189251Ssam	int daemonize = 0;
959189251Ssam	char *ctrl_dir = "/var/run/wpa_priv";
960189251Ssam	struct wpa_priv_interface *interfaces = NULL, *iface;
961189251Ssam
962189251Ssam	if (os_program_init())
963189251Ssam		return -1;
964189251Ssam
965189251Ssam	wpa_priv_fd_workaround();
966189251Ssam
967189251Ssam	for (;;) {
968189251Ssam		c = getopt(argc, argv, "Bc:dP:");
969189251Ssam		if (c < 0)
970189251Ssam			break;
971189251Ssam		switch (c) {
972189251Ssam		case 'B':
973189251Ssam			daemonize++;
974189251Ssam			break;
975189251Ssam		case 'c':
976189251Ssam			ctrl_dir = optarg;
977189251Ssam			break;
978189251Ssam		case 'd':
979189251Ssam			wpa_debug_level--;
980189251Ssam			break;
981189251Ssam		case 'P':
982189251Ssam			pid_file = os_rel2abs_path(optarg);
983189251Ssam			break;
984189251Ssam		default:
985189251Ssam			usage();
986189251Ssam			goto out;
987189251Ssam		}
988189251Ssam	}
989189251Ssam
990189251Ssam	if (optind >= argc) {
991189251Ssam		usage();
992189251Ssam		goto out;
993189251Ssam	}
994189251Ssam
995189251Ssam	wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir);
996189251Ssam
997214734Srpaulo	if (eloop_init()) {
998189251Ssam		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
999189251Ssam		goto out;
1000189251Ssam	}
1001189251Ssam
1002189251Ssam	for (i = optind; i < argc; i++) {
1003189251Ssam		wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]);
1004189251Ssam		iface = wpa_priv_interface_init(ctrl_dir, argv[i]);
1005189251Ssam		if (iface == NULL)
1006189251Ssam			goto out;
1007189251Ssam		iface->next = interfaces;
1008189251Ssam		interfaces = iface;
1009189251Ssam	}
1010189251Ssam
1011189251Ssam	if (daemonize && os_daemonize(pid_file))
1012189251Ssam		goto out;
1013189251Ssam
1014189251Ssam	eloop_register_signal_terminate(wpa_priv_terminate, NULL);
1015189251Ssam	eloop_run();
1016189251Ssam
1017189251Ssam	ret = 0;
1018189251Ssam
1019189251Ssamout:
1020189251Ssam	iface = interfaces;
1021189251Ssam	while (iface) {
1022189251Ssam		struct wpa_priv_interface *prev = iface;
1023189251Ssam		iface = iface->next;
1024189251Ssam		wpa_priv_interface_deinit(prev);
1025189251Ssam	}
1026189251Ssam
1027189251Ssam	eloop_destroy();
1028189251Ssam
1029189251Ssam	os_daemonize_terminate(pid_file);
1030189251Ssam	os_free(pid_file);
1031189251Ssam	os_program_deinit();
1032189251Ssam
1033189251Ssam	return ret;
1034189251Ssam}
1035