1/*
2 * WPA Supplicant - Layer2 packet handling with privilege separation
3 * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10#include <sys/un.h>
11
12#include "common.h"
13#include "eloop.h"
14#include "l2_packet.h"
15#include "common/privsep_commands.h"
16
17
18struct l2_packet_data {
19	int fd; /* UNIX domain socket for privsep access */
20	void (*rx_callback)(void *ctx, const u8 *src_addr,
21			    const u8 *buf, size_t len);
22	void *rx_callback_ctx;
23	u8 own_addr[ETH_ALEN];
24	char *own_socket_path;
25	struct sockaddr_un priv_addr;
26};
27
28
29static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd,
30			const void *data, size_t data_len)
31{
32	struct msghdr msg;
33	struct iovec io[2];
34
35	io[0].iov_base = &cmd;
36	io[0].iov_len = sizeof(cmd);
37	io[1].iov_base = (u8 *) data;
38	io[1].iov_len = data_len;
39
40	os_memset(&msg, 0, sizeof(msg));
41	msg.msg_iov = io;
42	msg.msg_iovlen = data ? 2 : 1;
43	msg.msg_name = &l2->priv_addr;
44	msg.msg_namelen = sizeof(l2->priv_addr);
45
46	if (sendmsg(l2->fd, &msg, 0) < 0) {
47		perror("L2: sendmsg(cmd)");
48		return -1;
49	}
50
51	return 0;
52}
53
54
55int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
56{
57	os_memcpy(addr, l2->own_addr, ETH_ALEN);
58	return 0;
59}
60
61
62int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
63		   const u8 *buf, size_t len)
64{
65	struct msghdr msg;
66	struct iovec io[4];
67	int cmd = PRIVSEP_CMD_L2_SEND;
68
69	io[0].iov_base = &cmd;
70	io[0].iov_len = sizeof(cmd);
71	io[1].iov_base = &dst_addr;
72	io[1].iov_len = ETH_ALEN;
73	io[2].iov_base = &proto;
74	io[2].iov_len = 2;
75	io[3].iov_base = (u8 *) buf;
76	io[3].iov_len = len;
77
78	os_memset(&msg, 0, sizeof(msg));
79	msg.msg_iov = io;
80	msg.msg_iovlen = 4;
81	msg.msg_name = &l2->priv_addr;
82	msg.msg_namelen = sizeof(l2->priv_addr);
83
84	if (sendmsg(l2->fd, &msg, 0) < 0) {
85		perror("L2: sendmsg(packet_send)");
86		return -1;
87	}
88
89	return 0;
90}
91
92
93static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
94{
95	struct l2_packet_data *l2 = eloop_ctx;
96	u8 buf[2300];
97	int res;
98	struct sockaddr_un from;
99	socklen_t fromlen = sizeof(from);
100
101	os_memset(&from, 0, sizeof(from));
102	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
103		       &fromlen);
104	if (res < 0) {
105		perror("l2_packet_receive - recvfrom");
106		return;
107	}
108	if (res < ETH_ALEN) {
109		wpa_printf(MSG_DEBUG, "L2: Too show packet received");
110		return;
111	}
112
113	if (from.sun_family != AF_UNIX ||
114	    os_strncmp(from.sun_path, l2->priv_addr.sun_path,
115		       sizeof(from.sun_path)) != 0) {
116		wpa_printf(MSG_DEBUG, "L2: Received message from unexpected "
117			   "source");
118		return;
119	}
120
121	l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN,
122			res - ETH_ALEN);
123}
124
125
126struct l2_packet_data * l2_packet_init(
127	const char *ifname, const u8 *own_addr, unsigned short protocol,
128	void (*rx_callback)(void *ctx, const u8 *src_addr,
129			    const u8 *buf, size_t len),
130	void *rx_callback_ctx, int l2_hdr)
131{
132	struct l2_packet_data *l2;
133	char *own_dir = "/tmp";
134	char *priv_dir = "/var/run/wpa_priv";
135	size_t len;
136	static unsigned int counter = 0;
137	struct sockaddr_un addr;
138	fd_set rfds;
139	struct timeval tv;
140	int res;
141	u8 reply[ETH_ALEN + 1];
142	int reg_cmd[2];
143
144	l2 = os_zalloc(sizeof(struct l2_packet_data));
145	if (l2 == NULL)
146		return NULL;
147	l2->rx_callback = rx_callback;
148	l2->rx_callback_ctx = rx_callback_ctx;
149
150	len = os_strlen(own_dir) + 50;
151	l2->own_socket_path = os_malloc(len);
152	if (l2->own_socket_path == NULL) {
153		os_free(l2);
154		return NULL;
155	}
156	os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d",
157		    own_dir, getpid(), counter++);
158
159	l2->priv_addr.sun_family = AF_UNIX;
160	os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path),
161		    "%s/%s", priv_dir, ifname);
162
163	l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
164	if (l2->fd < 0) {
165		perror("socket(PF_UNIX)");
166		os_free(l2->own_socket_path);
167		l2->own_socket_path = NULL;
168		os_free(l2);
169		return NULL;
170	}
171
172	os_memset(&addr, 0, sizeof(addr));
173	addr.sun_family = AF_UNIX;
174	os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path));
175	if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
176		perror("l2-pkt-privsep: bind(PF_UNIX)");
177		goto fail;
178	}
179
180	reg_cmd[0] = protocol;
181	reg_cmd[1] = l2_hdr;
182	if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd))
183	    < 0) {
184		wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv");
185		goto fail;
186	}
187
188	FD_ZERO(&rfds);
189	FD_SET(l2->fd, &rfds);
190	tv.tv_sec = 5;
191	tv.tv_usec = 0;
192	res = select(l2->fd + 1, &rfds, NULL, NULL, &tv);
193	if (res < 0 && errno != EINTR) {
194		perror("select");
195		goto fail;
196	}
197
198	if (FD_ISSET(l2->fd, &rfds)) {
199		res = recv(l2->fd, reply, sizeof(reply), 0);
200		if (res < 0) {
201			perror("recv");
202			goto fail;
203		}
204	} else {
205		wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for "
206			   "registration reply");
207		goto fail;
208	}
209
210	if (res != ETH_ALEN) {
211		wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply "
212			   "(len=%d)", res);
213	}
214	os_memcpy(l2->own_addr, reply, ETH_ALEN);
215
216	eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
217
218	return l2;
219
220fail:
221	close(l2->fd);
222	l2->fd = -1;
223	unlink(l2->own_socket_path);
224	os_free(l2->own_socket_path);
225	l2->own_socket_path = NULL;
226	os_free(l2);
227	return NULL;
228}
229
230
231void l2_packet_deinit(struct l2_packet_data *l2)
232{
233	if (l2 == NULL)
234		return;
235
236	if (l2->fd >= 0) {
237		wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0);
238		eloop_unregister_read_sock(l2->fd);
239		close(l2->fd);
240	}
241
242	if (l2->own_socket_path) {
243		unlink(l2->own_socket_path);
244		os_free(l2->own_socket_path);
245	}
246
247	os_free(l2);
248}
249
250
251int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
252{
253	/* TODO */
254	return -1;
255}
256
257
258void l2_packet_notify_auth_start(struct l2_packet_data *l2)
259{
260	wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0);
261}
262