1 /*-
2  * Copyright (c) 2005
3  *      Bill Paul <wpaul@windriver.com>.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Bill Paul.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30  * THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD: stable/9/usr.sbin/wpa/wpa_supplicant/Packet32.c 223492 2011-06-24 07:05:20Z kevlo $");
35 
36 /*
37  * This file implements a small portion of the Winpcap API for the
38  * Windows NDIS interface in wpa_supplicant. It provides just enough
39  * routines to fool wpa_supplicant into thinking it's really running
40  * in a Windows environment.
41  */
42 
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/socket.h>
46 #include <sys/ioctl.h>
47 #include <sys/errno.h>
48 #include <sys/sysctl.h>
49 #include <sys/fcntl.h>
50 #include <net/if.h>
51 #include <net/if_dl.h>
52 #include <net/if_var.h>
53 
54 #include <netinet/in.h>
55 #include <arpa/inet.h>
56 #include <netdb.h>
57 #include <net/route.h>
58 
59 #include <net80211/ieee80211_ioctl.h>
60 
61 #include <stdio.h>
62 #include <string.h>
63 #include <stdlib.h>
64 #include <unistd.h>
65 #include <pcap.h>
66 
67 #include "Packet32.h"
68 
69 #define OID_802_11_ADD_KEY      0x0d01011D
70 
71 typedef ULONGLONG NDIS_802_11_KEY_RSC;
72 typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
73 
74 typedef struct NDIS_802_11_KEY {
75 	ULONG Length;
76 	ULONG KeyIndex;
77 	ULONG KeyLength;
78 	NDIS_802_11_MAC_ADDRESS BSSID;
79 	NDIS_802_11_KEY_RSC KeyRSC;
80 	UCHAR KeyMaterial[1];
81 } NDIS_802_11_KEY;
82 
83 typedef struct NDIS_802_11_KEY_COMPAT {
84 	ULONG Length;
85 	ULONG KeyIndex;
86 	ULONG KeyLength;
87 	NDIS_802_11_MAC_ADDRESS BSSID;
88 	UCHAR Pad[6]; /* Make struct layout match Windows. */
89 	NDIS_802_11_KEY_RSC KeyRSC;
90 #ifdef notdef
91 	UCHAR KeyMaterial[1];
92 #endif
93 } NDIS_802_11_KEY_COMPAT;
94 
95 #define TRUE 1
96 #define FALSE 0
97 
98 struct adapter {
99 	int			socket;
100 	char			name[IFNAMSIZ];
101 	int			prev_roaming;
102 };
103 
104 PCHAR
PacketGetVersion(void)105 PacketGetVersion(void)
106 {
107 	return("FreeBSD WinPcap compatibility shim v1.0");
108 }
109 
110 void *
PacketOpenAdapter(CHAR * iface)111 PacketOpenAdapter(CHAR *iface)
112 {
113 	struct adapter		*a;
114 	int			s;
115 	int			ifflags;
116 	struct ifreq		ifr;
117 	struct ieee80211req	ireq;
118 
119 	s = socket(PF_INET, SOCK_DGRAM, 0);
120 
121 	if (s == -1)
122 		return(NULL);
123 
124 	a = malloc(sizeof(struct adapter));
125 	if (a == NULL)
126 		return(NULL);
127 
128 	a->socket = s;
129 	if (strncmp(iface, "\\Device\\NPF_", 12) == 0)
130 		iface += 12;
131 	else if (strncmp(iface, "\\DEVICE\\", 8) == 0)
132 		iface += 8;
133 	snprintf(a->name, IFNAMSIZ, "%s", iface);
134 
135 	/* Turn off net80211 roaming */
136 	bzero((char *)&ireq, sizeof(ireq));
137 	strncpy(ireq.i_name, iface, sizeof (ifr.ifr_name));
138 	ireq.i_type = IEEE80211_IOC_ROAMING;
139 	if (ioctl(a->socket, SIOCG80211, &ireq) == 0) {
140 		a->prev_roaming = ireq.i_val;
141 		ireq.i_val = IEEE80211_ROAMING_MANUAL;
142 		if (ioctl(a->socket, SIOCS80211, &ireq) < 0)
143 			fprintf(stderr,
144 			    "Could not set IEEE80211_ROAMING_MANUAL\n");
145 	}
146 
147 	bzero((char *)&ifr, sizeof(ifr));
148         strncpy(ifr.ifr_name, iface, sizeof (ifr.ifr_name));
149         if (ioctl(a->socket, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
150 		free(a);
151 		close(s);
152 		return(NULL);
153         }
154         ifr.ifr_flags |= IFF_UP;
155         if (ioctl(a->socket, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
156 		free(a);
157 		close(s);
158 		return(NULL);
159         }
160 
161 	return(a);
162 }
163 
164 int
PacketRequest(void * iface,BOOLEAN set,PACKET_OID_DATA * oid)165 PacketRequest(void *iface, BOOLEAN set, PACKET_OID_DATA *oid)
166 {
167 	struct adapter		*a;
168 	uint32_t		retval;
169 	struct ifreq		ifr;
170 	NDIS_802_11_KEY		*old;
171 	NDIS_802_11_KEY_COMPAT	*new;
172 	PACKET_OID_DATA		*o = NULL;
173 
174 	if (iface == NULL)
175 		return(-1);
176 
177 	a = iface;
178 	bzero((char *)&ifr, sizeof(ifr));
179 
180 	/*
181 	 * This hack is necessary to work around a difference
182 	 * betwee the GNU C and Microsoft C compilers. The NDIS_802_11_KEY
183 	 * structure has a uint64_t in it, right after an array of
184 	 * chars. The Microsoft compiler inserts padding right before
185 	 * the 64-bit value to align it on a 64-bit boundary, but
186 	 * GCC only aligns it on a 32-bit boundary. Trying to pass
187 	 * the GCC-formatted structure to an NDIS binary driver
188 	 * fails because some of the fields appear to be at the
189 	 * wrong offsets.
190 	 *
191 	 * To get around this, if we detect someone is trying to do
192 	 * a set operation on OID_802_11_ADD_KEY, we shuffle the data
193 	 * into a properly padded structure and pass that into the
194 	 * driver instead. This allows the driver_ndis.c code supplied
195 	 * with wpa_supplicant to work unmodified.
196 	 */
197 
198 	if (set == TRUE && oid->Oid == OID_802_11_ADD_KEY) {
199 		old = (NDIS_802_11_KEY *)&oid->Data;
200 		o = malloc(sizeof(PACKET_OID_DATA) +
201 		    sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength);
202 		if (o == NULL)
203 			return(0);
204 		bzero((char *)o, sizeof(PACKET_OID_DATA) +
205 		    sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength);
206 		o->Oid = oid->Oid;
207 		o->Length = sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength;
208 		new = (NDIS_802_11_KEY_COMPAT *)&o->Data;
209 		new->KeyRSC = old->KeyRSC;
210 		new->Length = o->Length;
211 		new->KeyIndex = old->KeyIndex;
212 		new->KeyLength = old->KeyLength;
213 		bcopy(old->BSSID, new->BSSID, sizeof(NDIS_802_11_MAC_ADDRESS));
214 		bcopy(old->KeyMaterial, (char *)new +
215 		    sizeof(NDIS_802_11_KEY_COMPAT), new->KeyLength);
216         	ifr.ifr_data = (caddr_t)o;
217 	} else
218         	ifr.ifr_data = (caddr_t)oid;
219 
220         strlcpy(ifr.ifr_name, a->name, sizeof(ifr.ifr_name));
221 
222 	if (set == TRUE)
223 		retval = ioctl(a->socket, SIOCSDRVSPEC, &ifr);
224 	else
225 		retval = ioctl(a->socket, SIOCGDRVSPEC, &ifr);
226 
227 	if (o != NULL)
228 		free(o);
229 
230 	if (retval)
231 		return(0);
232 
233 	return(1);
234 }
235 
236 int
PacketGetAdapterNames(CHAR * namelist,ULONG * len)237 PacketGetAdapterNames(CHAR *namelist, ULONG *len)
238 {
239 	int			mib[6];
240 	size_t			needed;
241 	struct if_msghdr	*ifm;
242 	struct sockaddr_dl	*sdl;
243 	char			*buf, *lim, *next;
244 	char			*plist;
245 	int			spc;
246 	int			i, ifcnt = 0;
247 
248 	plist = namelist;
249 	spc = 0;
250 
251 	bzero(plist, *len);
252 
253 	needed = 0;
254 	mib[0] = CTL_NET;
255 	mib[1] = PF_ROUTE;
256 	mib[2] = 0;             /* protocol */
257 	mib[3] = 0;             /* wildcard address family */
258 	mib[4] = NET_RT_IFLIST;
259 	mib[5] = 0;             /* no flags */
260 
261 	if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
262 		return(FALSE);
263 
264 	buf = malloc (needed);
265 	if (buf == NULL)
266 		return(FALSE);
267 
268 	if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) {
269 		free(buf);
270 		return(FALSE);
271 	}
272 
273 	lim = buf + needed;
274 
275 	/* Generate interface name list. */
276 
277 	next = buf;
278 	while (next < lim) {
279 		ifm = (struct if_msghdr *)next;
280 		if (ifm->ifm_type == RTM_IFINFO) {
281 			sdl = (struct sockaddr_dl *)(ifm + 1);
282 			if (strnstr(sdl->sdl_data, "wlan", sdl->sdl_nlen)) {
283 				if ((spc + sdl->sdl_nlen) > *len) {
284 					free(buf);
285 					return(FALSE);
286 				}
287 				strncpy(plist, sdl->sdl_data, sdl->sdl_nlen);
288 				plist += (sdl->sdl_nlen + 1);
289 				spc += (sdl->sdl_nlen + 1);
290 				ifcnt++;
291 			}
292 		}
293 		next += ifm->ifm_msglen;
294 	}
295 
296 
297 	/* Insert an extra "" as a spacer */
298 
299 	plist++;
300 	spc++;
301 
302 	/*
303 	 * Now generate the interface description list. There
304 	 * must be a unique description for each interface, and
305 	 * they have to match what the ndis_events program will
306 	 * feed in later. To keep this simple, we just repeat
307 	 * the interface list over again.
308 	 */
309 
310 	next = buf;
311 	while (next < lim) {
312 		ifm = (struct if_msghdr *)next;
313 		if (ifm->ifm_type == RTM_IFINFO) {
314 			sdl = (struct sockaddr_dl *)(ifm + 1);
315 			if (strnstr(sdl->sdl_data, "wlan", sdl->sdl_nlen)) {
316 				if ((spc + sdl->sdl_nlen) > *len) {
317 					free(buf);
318 					return(FALSE);
319 				}
320 				strncpy(plist, sdl->sdl_data, sdl->sdl_nlen);
321 				plist += (sdl->sdl_nlen + 1);
322 				spc += (sdl->sdl_nlen + 1);
323 				ifcnt++;
324 			}
325 		}
326 		next += ifm->ifm_msglen;
327 	}
328 
329 	free (buf);
330 
331 	*len = spc + 1;
332 
333 	return(TRUE);
334 }
335 
336 void
PacketCloseAdapter(void * iface)337 PacketCloseAdapter(void *iface)
338 {
339 	struct adapter		*a;
340 	struct ifreq		ifr;
341 	struct ieee80211req	ireq;
342 
343 	if (iface == NULL)
344 		return;
345 
346 	a = iface;
347 
348 	/* Reset net80211 roaming */
349 	bzero((char *)&ireq, sizeof(ireq));
350 	strncpy(ireq.i_name, a->name, sizeof (ifr.ifr_name));
351 	ireq.i_type = IEEE80211_IOC_ROAMING;
352 	ireq.i_val = a->prev_roaming;
353 	ioctl(a->socket, SIOCS80211, &ireq);
354 
355 	bzero((char *)&ifr, sizeof(ifr));
356         strncpy(ifr.ifr_name, a->name, sizeof (ifr.ifr_name));
357         ioctl(a->socket, SIOCGIFFLAGS, (caddr_t)&ifr);
358         ifr.ifr_flags &= ~IFF_UP;
359         ioctl(a->socket, SIOCSIFFLAGS, (caddr_t)&ifr);
360 	close(a->socket);
361 	free(a);
362 
363 	return;
364 }
365 
366 #if __FreeBSD_version < 600000
367 
368 /*
369  * The version of libpcap in FreeBSD 5.2.1 doesn't have these routines.
370  * Call me insane if you will, but I still run 5.2.1 on my laptop, and
371  * I'd like to use WPA there.
372  */
373 
374 int
pcap_get_selectable_fd(pcap_t * p)375 pcap_get_selectable_fd(pcap_t *p)
376 {
377 	return(pcap_fileno(p));
378 }
379 
380 /*
381  * The old version of libpcap opens its BPF descriptor in read-only
382  * mode. We need to temporarily create a new one we can write to.
383  */
384 
385 int
pcap_inject(pcap_t * p,const void * buf,size_t len)386 pcap_inject(pcap_t *p, const void *buf, size_t len)
387 {
388 	int			fd;
389 	int			res, n = 0;
390 	char			device[sizeof "/dev/bpf0000000000"];
391 	struct ifreq		ifr;
392 
393         /*
394          * Go through all the minors and find one that isn't in use.
395          */
396 	do {
397 		(void)snprintf(device, sizeof(device), "/dev/bpf%d", n++);
398 		fd = open(device, O_RDWR);
399 	} while (fd < 0 && errno == EBUSY);
400 
401 	if (fd == -1)
402 		return(-1);
403 
404 	bzero((char *)&ifr, sizeof(ifr));
405 	ioctl(pcap_fileno(p), BIOCGETIF, (caddr_t)&ifr);
406 	ioctl(fd, BIOCSETIF, (caddr_t)&ifr);
407 
408 	res = write(fd, buf, len);
409 
410 	close(fd);
411 
412 	return(res);
413 }
414 #endif
415