xref: /plan9-contrib/sys/src/cmd/aux/wpa.c (revision 1bbb2cb28bd9faa18a4a0c27d58656ba5679737b)
1*1bbb2cb2SDavid du Colombier #include <u.h>
2*1bbb2cb2SDavid du Colombier #include <libc.h>
3*1bbb2cb2SDavid du Colombier #include <ip.h>
4*1bbb2cb2SDavid du Colombier #include <mp.h>
5*1bbb2cb2SDavid du Colombier #include <libsec.h>
6*1bbb2cb2SDavid du Colombier #include <auth.h>
7*1bbb2cb2SDavid du Colombier 
8*1bbb2cb2SDavid du Colombier enum {
9*1bbb2cb2SDavid du Colombier 	PMKlen = 256/8,
10*1bbb2cb2SDavid du Colombier 	PTKlen = 512/8,
11*1bbb2cb2SDavid du Colombier 	GTKlen = 256/8,
12*1bbb2cb2SDavid du Colombier 
13*1bbb2cb2SDavid du Colombier 	MIClen = 16,
14*1bbb2cb2SDavid du Colombier 
15*1bbb2cb2SDavid du Colombier 	Noncelen = 32,
16*1bbb2cb2SDavid du Colombier 	Eaddrlen = 6,
17*1bbb2cb2SDavid du Colombier };
18*1bbb2cb2SDavid du Colombier 
19*1bbb2cb2SDavid du Colombier enum {
20*1bbb2cb2SDavid du Colombier 	Fptk	= 1<<3,
21*1bbb2cb2SDavid du Colombier 	Fins	= 1<<6,
22*1bbb2cb2SDavid du Colombier 	Fack	= 1<<7,
23*1bbb2cb2SDavid du Colombier 	Fmic	= 1<<8,
24*1bbb2cb2SDavid du Colombier 	Fsec	= 1<<9,
25*1bbb2cb2SDavid du Colombier 	Ferr	= 1<<10,
26*1bbb2cb2SDavid du Colombier 	Freq	= 1<<11,
27*1bbb2cb2SDavid du Colombier 	Fenc	= 1<<12,
28*1bbb2cb2SDavid du Colombier 
29*1bbb2cb2SDavid du Colombier 	Keydescrlen = 1+2+2+8+32+16+8+8+16+2,
30*1bbb2cb2SDavid du Colombier };
31*1bbb2cb2SDavid du Colombier 
32*1bbb2cb2SDavid du Colombier typedef struct Keydescr Keydescr;
33*1bbb2cb2SDavid du Colombier struct Keydescr
34*1bbb2cb2SDavid du Colombier {
35*1bbb2cb2SDavid du Colombier 	uchar	type[1];
36*1bbb2cb2SDavid du Colombier 	uchar	flags[2];
37*1bbb2cb2SDavid du Colombier 	uchar	keylen[2];
38*1bbb2cb2SDavid du Colombier 	uchar	repc[8];
39*1bbb2cb2SDavid du Colombier 	uchar	nonce[32];
40*1bbb2cb2SDavid du Colombier 	uchar	eapoliv[16];
41*1bbb2cb2SDavid du Colombier 	uchar	rsc[8];
42*1bbb2cb2SDavid du Colombier 	uchar	id[8];
43*1bbb2cb2SDavid du Colombier 	uchar	mic[16];
44*1bbb2cb2SDavid du Colombier 	uchar	datalen[2];
45*1bbb2cb2SDavid du Colombier 	uchar	data[];
46*1bbb2cb2SDavid du Colombier };
47*1bbb2cb2SDavid du Colombier 
48*1bbb2cb2SDavid du Colombier typedef struct Cipher Cipher;
49*1bbb2cb2SDavid du Colombier struct Cipher
50*1bbb2cb2SDavid du Colombier {
51*1bbb2cb2SDavid du Colombier 	char	*name;
52*1bbb2cb2SDavid du Colombier 	int	keylen;
53*1bbb2cb2SDavid du Colombier };
54*1bbb2cb2SDavid du Colombier 
55*1bbb2cb2SDavid du Colombier typedef struct Eapconn Eapconn;
56*1bbb2cb2SDavid du Colombier typedef struct TLStunn TLStunn;
57*1bbb2cb2SDavid du Colombier 
58*1bbb2cb2SDavid du Colombier struct Eapconn
59*1bbb2cb2SDavid du Colombier {
60*1bbb2cb2SDavid du Colombier 	int	fd;
61*1bbb2cb2SDavid du Colombier 	int	version;
62*1bbb2cb2SDavid du Colombier 
63*1bbb2cb2SDavid du Colombier 	uchar	type;
64*1bbb2cb2SDavid du Colombier 	uchar	smac[Eaddrlen];
65*1bbb2cb2SDavid du Colombier 	uchar	amac[Eaddrlen];
66*1bbb2cb2SDavid du Colombier 
67*1bbb2cb2SDavid du Colombier 	TLStunn	*tunn;
68*1bbb2cb2SDavid du Colombier 
69*1bbb2cb2SDavid du Colombier 	void	(*write)(Eapconn*, uchar *data, int datalen);
70*1bbb2cb2SDavid du Colombier };
71*1bbb2cb2SDavid du Colombier 
72*1bbb2cb2SDavid du Colombier struct TLStunn
73*1bbb2cb2SDavid du Colombier {
74*1bbb2cb2SDavid du Colombier 	int	fd;
75*1bbb2cb2SDavid du Colombier 
76*1bbb2cb2SDavid du Colombier 	int	clientpid;
77*1bbb2cb2SDavid du Colombier 	int	readerpid;
78*1bbb2cb2SDavid du Colombier 
79*1bbb2cb2SDavid du Colombier 	uchar	id;
80*1bbb2cb2SDavid du Colombier 	uchar	tp;
81*1bbb2cb2SDavid du Colombier };
82*1bbb2cb2SDavid du Colombier 
83*1bbb2cb2SDavid du Colombier Cipher	tkip = { "tkip", 32 };
84*1bbb2cb2SDavid du Colombier Cipher	ccmp = { "ccmp", 16 };
85*1bbb2cb2SDavid du Colombier 
86*1bbb2cb2SDavid du Colombier Cipher	*peercipher;
87*1bbb2cb2SDavid du Colombier Cipher	*groupcipher;
88*1bbb2cb2SDavid du Colombier 
89*1bbb2cb2SDavid du Colombier int	forked;
90*1bbb2cb2SDavid du Colombier int	prompt;
91*1bbb2cb2SDavid du Colombier int	debug;
92*1bbb2cb2SDavid du Colombier int	fd, cfd;
93*1bbb2cb2SDavid du Colombier char	*dev;
94*1bbb2cb2SDavid du Colombier enum {
95*1bbb2cb2SDavid du Colombier 	AuthNone,
96*1bbb2cb2SDavid du Colombier 	AuthPSK,
97*1bbb2cb2SDavid du Colombier 	AuthWPA,
98*1bbb2cb2SDavid du Colombier };
99*1bbb2cb2SDavid du Colombier int	authtype;
100*1bbb2cb2SDavid du Colombier char	devdir[40];
101*1bbb2cb2SDavid du Colombier uchar	ptk[PTKlen];
102*1bbb2cb2SDavid du Colombier char	essid[32+1];
103*1bbb2cb2SDavid du Colombier uvlong	lastrepc;
104*1bbb2cb2SDavid du Colombier 
105*1bbb2cb2SDavid du Colombier uchar rsntkipoui[4] = {0x00, 0x0F, 0xAC, 0x02};
106*1bbb2cb2SDavid du Colombier uchar rsnccmpoui[4] = {0x00, 0x0F, 0xAC, 0x04};
107*1bbb2cb2SDavid du Colombier uchar rsnapskoui[4] = {0x00, 0x0F, 0xAC, 0x02};
108*1bbb2cb2SDavid du Colombier uchar rsnawpaoui[4] = {0x00, 0x0F, 0xAC, 0x01};
109*1bbb2cb2SDavid du Colombier 
110*1bbb2cb2SDavid du Colombier uchar	rsnie[] = {
111*1bbb2cb2SDavid du Colombier 	0x30,			/* RSN */
112*1bbb2cb2SDavid du Colombier 	0x14,			/* length */
113*1bbb2cb2SDavid du Colombier 	0x01, 0x00,		/* version 1 */
114*1bbb2cb2SDavid du Colombier 	0x00, 0x0F, 0xAC, 0x04,	/* group cipher suite CCMP */
115*1bbb2cb2SDavid du Colombier 	0x01, 0x00,		/* pairwise cipher suite count 1 */
116*1bbb2cb2SDavid du Colombier 	0x00, 0x0F, 0xAC, 0x04,	/* pairwise cipher suite CCMP */
117*1bbb2cb2SDavid du Colombier 	0x01, 0x00,		/* authentication suite count 1 */
118*1bbb2cb2SDavid du Colombier 	0x00, 0x0F, 0xAC, 0x02,	/* authentication suite PSK */
119*1bbb2cb2SDavid du Colombier 	0x00, 0x00,		/* capabilities */
120*1bbb2cb2SDavid du Colombier };
121*1bbb2cb2SDavid du Colombier 
122*1bbb2cb2SDavid du Colombier uchar wpa1oui[4]    = {0x00, 0x50, 0xF2, 0x01};
123*1bbb2cb2SDavid du Colombier uchar wpatkipoui[4] = {0x00, 0x50, 0xF2, 0x02};
124*1bbb2cb2SDavid du Colombier uchar wpaapskoui[4] = {0x00, 0x50, 0xF2, 0x02};
125*1bbb2cb2SDavid du Colombier uchar wpaawpaoui[4] = {0x00, 0x50, 0xF2, 0x01};
126*1bbb2cb2SDavid du Colombier 
127*1bbb2cb2SDavid du Colombier uchar	wpaie[] = {
128*1bbb2cb2SDavid du Colombier 	0xdd,			/* vendor specific */
129*1bbb2cb2SDavid du Colombier 	0x16,			/* length */
130*1bbb2cb2SDavid du Colombier 	0x00, 0x50, 0xf2, 0x01,	/* WPAIE type 1 */
131*1bbb2cb2SDavid du Colombier 	0x01, 0x00,		/* version 1 */
132*1bbb2cb2SDavid du Colombier 	0x00, 0x50, 0xf2, 0x02,	/* group cipher suite TKIP */
133*1bbb2cb2SDavid du Colombier 	0x01, 0x00,		/* pairwise cipher suite count 1 */
134*1bbb2cb2SDavid du Colombier 	0x00, 0x50, 0xf2, 0x02,	/* pairwise cipher suite TKIP */
135*1bbb2cb2SDavid du Colombier 	0x01, 0x00,		/* authentication suite count 1 */
136*1bbb2cb2SDavid du Colombier 	0x00, 0x50, 0xf2, 0x02,	/* authentication suite PSK */
137*1bbb2cb2SDavid du Colombier };
138*1bbb2cb2SDavid du Colombier 
139*1bbb2cb2SDavid du Colombier void*
emalloc(int len)140*1bbb2cb2SDavid du Colombier emalloc(int len)
141*1bbb2cb2SDavid du Colombier {
142*1bbb2cb2SDavid du Colombier 	void *v;
143*1bbb2cb2SDavid du Colombier 
144*1bbb2cb2SDavid du Colombier 	if((v = mallocz(len, 1)) == nil)
145*1bbb2cb2SDavid du Colombier 		sysfatal("malloc: %r");
146*1bbb2cb2SDavid du Colombier 	return v;
147*1bbb2cb2SDavid du Colombier }
148*1bbb2cb2SDavid du Colombier 
149*1bbb2cb2SDavid du Colombier int
hextob(char * s,char ** sp,uchar * b,int n)150*1bbb2cb2SDavid du Colombier hextob(char *s, char **sp, uchar *b, int n)
151*1bbb2cb2SDavid du Colombier {
152*1bbb2cb2SDavid du Colombier 	int r;
153*1bbb2cb2SDavid du Colombier 
154*1bbb2cb2SDavid du Colombier 	n <<= 1;
155*1bbb2cb2SDavid du Colombier 	for(r = 0; r < n && *s; s++){
156*1bbb2cb2SDavid du Colombier 		*b <<= 4;
157*1bbb2cb2SDavid du Colombier 		if(*s >= '0' && *s <= '9')
158*1bbb2cb2SDavid du Colombier 			*b |= (*s - '0');
159*1bbb2cb2SDavid du Colombier 		else if(*s >= 'a' && *s <= 'f')
160*1bbb2cb2SDavid du Colombier 			*b |= 10+(*s - 'a');
161*1bbb2cb2SDavid du Colombier 		else if(*s >= 'A' && *s <= 'F')
162*1bbb2cb2SDavid du Colombier 			*b |= 10+(*s - 'A');
163*1bbb2cb2SDavid du Colombier 		else break;
164*1bbb2cb2SDavid du Colombier 		if((++r & 1) == 0)
165*1bbb2cb2SDavid du Colombier 			b++;
166*1bbb2cb2SDavid du Colombier 	}
167*1bbb2cb2SDavid du Colombier 	if(sp != nil)
168*1bbb2cb2SDavid du Colombier 		*sp = s;
169*1bbb2cb2SDavid du Colombier 	return r >> 1;
170*1bbb2cb2SDavid du Colombier }
171*1bbb2cb2SDavid du Colombier 
172*1bbb2cb2SDavid du Colombier char*
getifstats(char * key,char * val,int nval)173*1bbb2cb2SDavid du Colombier getifstats(char *key, char *val, int nval)
174*1bbb2cb2SDavid du Colombier {
175*1bbb2cb2SDavid du Colombier 	char buf[8*1024], *f[2], *p, *e;
176*1bbb2cb2SDavid du Colombier 	int fd, n;
177*1bbb2cb2SDavid du Colombier 
178*1bbb2cb2SDavid du Colombier 	snprint(buf, sizeof(buf), "%s/ifstats", devdir);
179*1bbb2cb2SDavid du Colombier 	if((fd = open(buf, OREAD)) < 0)
180*1bbb2cb2SDavid du Colombier 		return nil;
181*1bbb2cb2SDavid du Colombier 	n = readn(fd, buf, sizeof(buf)-1);
182*1bbb2cb2SDavid du Colombier 	close(fd);
183*1bbb2cb2SDavid du Colombier 	if(n <= 0)
184*1bbb2cb2SDavid du Colombier 		return nil;
185*1bbb2cb2SDavid du Colombier 	buf[n] = 0;
186*1bbb2cb2SDavid du Colombier 	for(p = buf; (e = strchr(p, '\n')) != nil; p = e){
187*1bbb2cb2SDavid du Colombier 		*e++ = 0;
188*1bbb2cb2SDavid du Colombier 		if(gettokens(p, f, 2, "\t\r\n ") != 2)
189*1bbb2cb2SDavid du Colombier 			continue;
190*1bbb2cb2SDavid du Colombier 		if(strcmp(f[0], key) != 0)
191*1bbb2cb2SDavid du Colombier 			continue;
192*1bbb2cb2SDavid du Colombier 		strncpy(val, f[1], nval);
193*1bbb2cb2SDavid du Colombier 		val[nval-1] = 0;
194*1bbb2cb2SDavid du Colombier 		return val;
195*1bbb2cb2SDavid du Colombier 	}
196*1bbb2cb2SDavid du Colombier 	return nil;
197*1bbb2cb2SDavid du Colombier }
198*1bbb2cb2SDavid du Colombier 
199*1bbb2cb2SDavid du Colombier char*
getessid(void)200*1bbb2cb2SDavid du Colombier getessid(void)
201*1bbb2cb2SDavid du Colombier {
202*1bbb2cb2SDavid du Colombier 	return getifstats("essid:", essid, sizeof(essid));
203*1bbb2cb2SDavid du Colombier }
204*1bbb2cb2SDavid du Colombier 
205*1bbb2cb2SDavid du Colombier int
getbssid(uchar mac[Eaddrlen])206*1bbb2cb2SDavid du Colombier getbssid(uchar mac[Eaddrlen])
207*1bbb2cb2SDavid du Colombier {
208*1bbb2cb2SDavid du Colombier 	char buf[64];
209*1bbb2cb2SDavid du Colombier 
210*1bbb2cb2SDavid du Colombier 	if(getifstats("bssid:", buf, sizeof(buf)) != nil)
211*1bbb2cb2SDavid du Colombier 		return parseether(mac, buf);
212*1bbb2cb2SDavid du Colombier 	return -1;
213*1bbb2cb2SDavid du Colombier }
214*1bbb2cb2SDavid du Colombier 
215*1bbb2cb2SDavid du Colombier int
connected(void)216*1bbb2cb2SDavid du Colombier connected(void)
217*1bbb2cb2SDavid du Colombier {
218*1bbb2cb2SDavid du Colombier 	char status[1024];
219*1bbb2cb2SDavid du Colombier 
220*1bbb2cb2SDavid du Colombier 	if(getifstats("status:", status, sizeof(status)) == nil)
221*1bbb2cb2SDavid du Colombier 		return 0;
222*1bbb2cb2SDavid du Colombier 	if(strcmp(status, "connecting") == 0)
223*1bbb2cb2SDavid du Colombier 		return 0;
224*1bbb2cb2SDavid du Colombier 	if(strcmp(status, "unauthenticated") == 0)
225*1bbb2cb2SDavid du Colombier 		return 0;
226*1bbb2cb2SDavid du Colombier 	if(debug)
227*1bbb2cb2SDavid du Colombier 		fprint(2, "status: %s\n", status);
228*1bbb2cb2SDavid du Colombier 	return 1;
229*1bbb2cb2SDavid du Colombier }
230*1bbb2cb2SDavid du Colombier 
231*1bbb2cb2SDavid du Colombier int
buildrsne(uchar rsne[258])232*1bbb2cb2SDavid du Colombier buildrsne(uchar rsne[258])
233*1bbb2cb2SDavid du Colombier {
234*1bbb2cb2SDavid du Colombier 	char buf[1024];
235*1bbb2cb2SDavid du Colombier 	uchar brsne[258];
236*1bbb2cb2SDavid du Colombier 	int brsnelen;
237*1bbb2cb2SDavid du Colombier 	uchar *p, *w, *e;
238*1bbb2cb2SDavid du Colombier 	int i, n;
239*1bbb2cb2SDavid du Colombier 
240*1bbb2cb2SDavid du Colombier 	if(getifstats("brsne:", buf, sizeof(buf)) == nil)
241*1bbb2cb2SDavid du Colombier 		return 0;	/* not an error, might be old kernel */
242*1bbb2cb2SDavid du Colombier 
243*1bbb2cb2SDavid du Colombier 	brsnelen = hextob(buf, nil, brsne, sizeof(brsne));
244*1bbb2cb2SDavid du Colombier 	if(brsnelen <= 4){
245*1bbb2cb2SDavid du Colombier trunc:		sysfatal("invalid or truncated RSNE; brsne: %s", buf);
246*1bbb2cb2SDavid du Colombier 		return 0;
247*1bbb2cb2SDavid du Colombier 	}
248*1bbb2cb2SDavid du Colombier 
249*1bbb2cb2SDavid du Colombier 	w = rsne;
250*1bbb2cb2SDavid du Colombier 	p = brsne;
251*1bbb2cb2SDavid du Colombier 	e = p + brsnelen;
252*1bbb2cb2SDavid du Colombier 	if(p[0] == 0x30){
253*1bbb2cb2SDavid du Colombier 		p += 2;
254*1bbb2cb2SDavid du Colombier 
255*1bbb2cb2SDavid du Colombier 		/* RSN */
256*1bbb2cb2SDavid du Colombier 		*w++ = 0x30;
257*1bbb2cb2SDavid du Colombier 		*w++ = 0;	/* length */
258*1bbb2cb2SDavid du Colombier 	} else if(p[0] == 0xDD){
259*1bbb2cb2SDavid du Colombier 		p += 2;
260*1bbb2cb2SDavid du Colombier 		if((e - p) < 4 || memcmp(p, wpa1oui, 4) != 0){
261*1bbb2cb2SDavid du Colombier 			sysfatal("unrecognized WPAIE type; brsne: %s", buf);
262*1bbb2cb2SDavid du Colombier 			return 0;
263*1bbb2cb2SDavid du Colombier 		}
264*1bbb2cb2SDavid du Colombier 
265*1bbb2cb2SDavid du Colombier 		/* WPA */
266*1bbb2cb2SDavid du Colombier 		*w++ = 0xDD;
267*1bbb2cb2SDavid du Colombier 		*w++ = 0;	/* length */
268*1bbb2cb2SDavid du Colombier 
269*1bbb2cb2SDavid du Colombier 		memmove(w, wpa1oui, 4);
270*1bbb2cb2SDavid du Colombier 		w += 4;
271*1bbb2cb2SDavid du Colombier 		p += 4;
272*1bbb2cb2SDavid du Colombier 	} else {
273*1bbb2cb2SDavid du Colombier 		sysfatal("unrecognized RSNE type; brsne: %s", buf);
274*1bbb2cb2SDavid du Colombier 		return 0;
275*1bbb2cb2SDavid du Colombier 	}
276*1bbb2cb2SDavid du Colombier 
277*1bbb2cb2SDavid du Colombier 	if((e - p) < 6)
278*1bbb2cb2SDavid du Colombier 		goto trunc;
279*1bbb2cb2SDavid du Colombier 
280*1bbb2cb2SDavid du Colombier 	*w++ = *p++;		/* version */
281*1bbb2cb2SDavid du Colombier 	*w++ = *p++;
282*1bbb2cb2SDavid du Colombier 
283*1bbb2cb2SDavid du Colombier 	if(rsne[0] == 0x30){
284*1bbb2cb2SDavid du Colombier 		if(memcmp(p, rsnccmpoui, 4) == 0)
285*1bbb2cb2SDavid du Colombier 			groupcipher = &ccmp;
286*1bbb2cb2SDavid du Colombier 		else if(memcmp(p, rsntkipoui, 4) == 0)
287*1bbb2cb2SDavid du Colombier 			groupcipher = &tkip;
288*1bbb2cb2SDavid du Colombier 		else {
289*1bbb2cb2SDavid du Colombier 			sysfatal("unrecognized RSN group cipher; brsne: %s", buf);
290*1bbb2cb2SDavid du Colombier 			return 0;
291*1bbb2cb2SDavid du Colombier 		}
292*1bbb2cb2SDavid du Colombier 	} else {
293*1bbb2cb2SDavid du Colombier 		if(memcmp(p, wpatkipoui, 4) != 0){
294*1bbb2cb2SDavid du Colombier 			sysfatal("unrecognized WPA group cipher; brsne: %s", buf);
295*1bbb2cb2SDavid du Colombier 			return 0;
296*1bbb2cb2SDavid du Colombier 		}
297*1bbb2cb2SDavid du Colombier 		groupcipher = &tkip;
298*1bbb2cb2SDavid du Colombier 	}
299*1bbb2cb2SDavid du Colombier 
300*1bbb2cb2SDavid du Colombier 	memmove(w, p, 4);	/* group cipher */
301*1bbb2cb2SDavid du Colombier 	w += 4;
302*1bbb2cb2SDavid du Colombier 	p += 4;
303*1bbb2cb2SDavid du Colombier 
304*1bbb2cb2SDavid du Colombier 	if((e - p) < 6)
305*1bbb2cb2SDavid du Colombier 		goto trunc;
306*1bbb2cb2SDavid du Colombier 
307*1bbb2cb2SDavid du Colombier 	*w++ = 0x01;		/* # of peer ciphers */
308*1bbb2cb2SDavid du Colombier 	*w++ = 0x00;
309*1bbb2cb2SDavid du Colombier 	n = *p++;
310*1bbb2cb2SDavid du Colombier 	n |= *p++ << 8;
311*1bbb2cb2SDavid du Colombier 
312*1bbb2cb2SDavid du Colombier 	if(n <= 0)
313*1bbb2cb2SDavid du Colombier 		goto trunc;
314*1bbb2cb2SDavid du Colombier 
315*1bbb2cb2SDavid du Colombier 	peercipher = &tkip;
316*1bbb2cb2SDavid du Colombier 	for(i=0; i<n; i++){
317*1bbb2cb2SDavid du Colombier 		if((e - p) < 4)
318*1bbb2cb2SDavid du Colombier 			goto trunc;
319*1bbb2cb2SDavid du Colombier 
320*1bbb2cb2SDavid du Colombier 		if(rsne[0] == 0x30 && memcmp(p, rsnccmpoui, 4) == 0 && peercipher == &tkip)
321*1bbb2cb2SDavid du Colombier 			peercipher = &ccmp;
322*1bbb2cb2SDavid du Colombier 		p += 4;
323*1bbb2cb2SDavid du Colombier 	}
324*1bbb2cb2SDavid du Colombier 	if(peercipher == &ccmp)
325*1bbb2cb2SDavid du Colombier 		memmove(w, rsnccmpoui, 4);
326*1bbb2cb2SDavid du Colombier 	else if(rsne[0] == 0x30)
327*1bbb2cb2SDavid du Colombier 		memmove(w, rsntkipoui, 4);
328*1bbb2cb2SDavid du Colombier 	else
329*1bbb2cb2SDavid du Colombier 		memmove(w, wpatkipoui, 4);
330*1bbb2cb2SDavid du Colombier 	w += 4;
331*1bbb2cb2SDavid du Colombier 
332*1bbb2cb2SDavid du Colombier 	if((e - p) < 6)
333*1bbb2cb2SDavid du Colombier 		goto trunc;
334*1bbb2cb2SDavid du Colombier 
335*1bbb2cb2SDavid du Colombier 	*w++ = 0x01;		/* # of auth suites */
336*1bbb2cb2SDavid du Colombier 	*w++ = 0x00;
337*1bbb2cb2SDavid du Colombier 	n = *p++;
338*1bbb2cb2SDavid du Colombier 	n |= *p++ << 8;
339*1bbb2cb2SDavid du Colombier 
340*1bbb2cb2SDavid du Colombier 	if(n <= 0)
341*1bbb2cb2SDavid du Colombier 		goto trunc;
342*1bbb2cb2SDavid du Colombier 
343*1bbb2cb2SDavid du Colombier 	for(i=0; i<n; i++){
344*1bbb2cb2SDavid du Colombier 		if((e - p) < 4)
345*1bbb2cb2SDavid du Colombier 			goto trunc;
346*1bbb2cb2SDavid du Colombier 
347*1bbb2cb2SDavid du Colombier 		if(rsne[0] == 0x30){
348*1bbb2cb2SDavid du Colombier 			/* look for PSK oui */
349*1bbb2cb2SDavid du Colombier 			if(memcmp(p, rsnapskoui, 4) == 0)
350*1bbb2cb2SDavid du Colombier 				break;
351*1bbb2cb2SDavid du Colombier 			/* look for WPA oui */
352*1bbb2cb2SDavid du Colombier 			if(memcmp(p, rsnawpaoui, 4) == 0){
353*1bbb2cb2SDavid du Colombier 				authtype = AuthWPA;
354*1bbb2cb2SDavid du Colombier 				break;
355*1bbb2cb2SDavid du Colombier 			}
356*1bbb2cb2SDavid du Colombier 		} else {
357*1bbb2cb2SDavid du Colombier 			/* look for PSK oui */
358*1bbb2cb2SDavid du Colombier 			if(memcmp(p, wpaapskoui, 4) == 0)
359*1bbb2cb2SDavid du Colombier 				break;
360*1bbb2cb2SDavid du Colombier 			/* look for WPA oui */
361*1bbb2cb2SDavid du Colombier 			if(memcmp(p, wpaawpaoui, 4) == 0){
362*1bbb2cb2SDavid du Colombier 				authtype = AuthWPA;
363*1bbb2cb2SDavid du Colombier 				break;
364*1bbb2cb2SDavid du Colombier 			}
365*1bbb2cb2SDavid du Colombier 		}
366*1bbb2cb2SDavid du Colombier 		p += 4;
367*1bbb2cb2SDavid du Colombier 	}
368*1bbb2cb2SDavid du Colombier 	if(i >= n){
369*1bbb2cb2SDavid du Colombier 		sysfatal("auth suite is not PSK or WPA; brsne: %s", buf);
370*1bbb2cb2SDavid du Colombier 		return 0;
371*1bbb2cb2SDavid du Colombier 	}
372*1bbb2cb2SDavid du Colombier 
373*1bbb2cb2SDavid du Colombier 	memmove(w, p, 4);
374*1bbb2cb2SDavid du Colombier 	w += 4;
375*1bbb2cb2SDavid du Colombier 
376*1bbb2cb2SDavid du Colombier 	if(rsne[0] == 0x30){
377*1bbb2cb2SDavid du Colombier 		/* RSN caps */
378*1bbb2cb2SDavid du Colombier 		*w++ = 0x00;
379*1bbb2cb2SDavid du Colombier 		*w++ = 0x00;
380*1bbb2cb2SDavid du Colombier 	}
381*1bbb2cb2SDavid du Colombier 
382*1bbb2cb2SDavid du Colombier 	rsne[1] = (w - rsne) - 2;
383*1bbb2cb2SDavid du Colombier 	return w - rsne;
384*1bbb2cb2SDavid du Colombier }
385*1bbb2cb2SDavid du Colombier 
386*1bbb2cb2SDavid du Colombier char*
factotumattr(char * attr,char * fmt,...)387*1bbb2cb2SDavid du Colombier factotumattr(char *attr, char *fmt, ...)
388*1bbb2cb2SDavid du Colombier {
389*1bbb2cb2SDavid du Colombier 	char buf[1024];
390*1bbb2cb2SDavid du Colombier 	va_list list;
391*1bbb2cb2SDavid du Colombier 	AuthRpc *rpc;
392*1bbb2cb2SDavid du Colombier 	char *val;
393*1bbb2cb2SDavid du Colombier 	Attr *a;
394*1bbb2cb2SDavid du Colombier 	int afd;
395*1bbb2cb2SDavid du Colombier 
396*1bbb2cb2SDavid du Colombier 	if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
397*1bbb2cb2SDavid du Colombier 		return nil;
398*1bbb2cb2SDavid du Colombier 	if((rpc = auth_allocrpc(afd)) == nil){
399*1bbb2cb2SDavid du Colombier 		close(afd);
400*1bbb2cb2SDavid du Colombier 		return nil;
401*1bbb2cb2SDavid du Colombier 	}
402*1bbb2cb2SDavid du Colombier 	va_start(list, fmt);
403*1bbb2cb2SDavid du Colombier 	vsnprint(buf, sizeof(buf), fmt, list);
404*1bbb2cb2SDavid du Colombier 	va_end(list);
405*1bbb2cb2SDavid du Colombier 	val = nil;
406*1bbb2cb2SDavid du Colombier 	if(auth_rpc(rpc, "start", buf, strlen(buf)) == 0){
407*1bbb2cb2SDavid du Colombier 		if((a = auth_attr(rpc)) != nil){
408*1bbb2cb2SDavid du Colombier 			if((val = _strfindattr(a, attr)) != nil)
409*1bbb2cb2SDavid du Colombier 				val = strdup(val);
410*1bbb2cb2SDavid du Colombier 			_freeattr(a);
411*1bbb2cb2SDavid du Colombier 		}
412*1bbb2cb2SDavid du Colombier 	}
413*1bbb2cb2SDavid du Colombier 	auth_freerpc(rpc);
414*1bbb2cb2SDavid du Colombier 	close(afd);
415*1bbb2cb2SDavid du Colombier 
416*1bbb2cb2SDavid du Colombier 	return val;
417*1bbb2cb2SDavid du Colombier }
418*1bbb2cb2SDavid du Colombier 
419*1bbb2cb2SDavid du Colombier void
freeup(UserPasswd * up)420*1bbb2cb2SDavid du Colombier freeup(UserPasswd *up)
421*1bbb2cb2SDavid du Colombier {
422*1bbb2cb2SDavid du Colombier 	memset(up->user, 0, strlen(up->user));
423*1bbb2cb2SDavid du Colombier 	memset(up->passwd, 0, strlen(up->passwd));
424*1bbb2cb2SDavid du Colombier 	free(up);
425*1bbb2cb2SDavid du Colombier }
426*1bbb2cb2SDavid du Colombier 
427*1bbb2cb2SDavid du Colombier char*
getidentity(void)428*1bbb2cb2SDavid du Colombier getidentity(void)
429*1bbb2cb2SDavid du Colombier {
430*1bbb2cb2SDavid du Colombier 	static char *identity;
431*1bbb2cb2SDavid du Colombier 	char *s;
432*1bbb2cb2SDavid du Colombier 
433*1bbb2cb2SDavid du Colombier 	s = nil;
434*1bbb2cb2SDavid du Colombier 	for(;;){
435*1bbb2cb2SDavid du Colombier 		if(getessid() == nil)
436*1bbb2cb2SDavid du Colombier 			break;
437*1bbb2cb2SDavid du Colombier 		if((s = factotumattr("user", "proto=pass service=wpa essid=%q", essid)) != nil)
438*1bbb2cb2SDavid du Colombier 			break;
439*1bbb2cb2SDavid du Colombier 		if((s = factotumattr("user", "proto=mschapv2 role=client service=wpa essid=%q", essid)) != nil)
440*1bbb2cb2SDavid du Colombier 			break;
441*1bbb2cb2SDavid du Colombier 		break;
442*1bbb2cb2SDavid du Colombier 	}
443*1bbb2cb2SDavid du Colombier 	if(s != nil){
444*1bbb2cb2SDavid du Colombier 		free(identity);
445*1bbb2cb2SDavid du Colombier 		identity = s;
446*1bbb2cb2SDavid du Colombier 	} else if(identity == nil)
447*1bbb2cb2SDavid du Colombier 		identity = strdup("anonymous");
448*1bbb2cb2SDavid du Colombier 	if(debug)
449*1bbb2cb2SDavid du Colombier 		fprint(2, "identity: %s\n", identity);
450*1bbb2cb2SDavid du Colombier 	return identity;
451*1bbb2cb2SDavid du Colombier }
452*1bbb2cb2SDavid du Colombier 
453*1bbb2cb2SDavid du Colombier int
factotumctl(char * fmt,...)454*1bbb2cb2SDavid du Colombier factotumctl(char *fmt, ...)
455*1bbb2cb2SDavid du Colombier {
456*1bbb2cb2SDavid du Colombier 	va_list list;
457*1bbb2cb2SDavid du Colombier 	int fd, r, n;
458*1bbb2cb2SDavid du Colombier 	char *s;
459*1bbb2cb2SDavid du Colombier 
460*1bbb2cb2SDavid du Colombier 	r = -1;
461*1bbb2cb2SDavid du Colombier 	if((fd = open("/mnt/factotum/ctl", OWRITE)) >= 0){
462*1bbb2cb2SDavid du Colombier 		va_start(list, fmt);
463*1bbb2cb2SDavid du Colombier 		s = vsmprint(fmt, list);
464*1bbb2cb2SDavid du Colombier 		va_end(list);
465*1bbb2cb2SDavid du Colombier 		if(s != nil){
466*1bbb2cb2SDavid du Colombier 			n = strlen(s);
467*1bbb2cb2SDavid du Colombier 			r = write(fd, s, n);
468*1bbb2cb2SDavid du Colombier 			memset(s, 0, n);
469*1bbb2cb2SDavid du Colombier 			free(s);
470*1bbb2cb2SDavid du Colombier 		}
471*1bbb2cb2SDavid du Colombier 		close(fd);
472*1bbb2cb2SDavid du Colombier 	}
473*1bbb2cb2SDavid du Colombier 	return r;
474*1bbb2cb2SDavid du Colombier }
475*1bbb2cb2SDavid du Colombier 
476*1bbb2cb2SDavid du Colombier int
setpmk(uchar pmk[PMKlen])477*1bbb2cb2SDavid du Colombier setpmk(uchar pmk[PMKlen])
478*1bbb2cb2SDavid du Colombier {
479*1bbb2cb2SDavid du Colombier 	if(getessid() == nil)
480*1bbb2cb2SDavid du Colombier 		return -1;
481*1bbb2cb2SDavid du Colombier 	return factotumctl("key proto=wpapsk role=client essid=%q !password=%.*H\n", essid, PMKlen, pmk);
482*1bbb2cb2SDavid du Colombier }
483*1bbb2cb2SDavid du Colombier 
484*1bbb2cb2SDavid du Colombier int
getptk(AuthGetkey * getkey,uchar smac[Eaddrlen],uchar amac[Eaddrlen],uchar snonce[Noncelen],uchar anonce[Noncelen],uchar ptk[PTKlen])485*1bbb2cb2SDavid du Colombier getptk(AuthGetkey *getkey,
486*1bbb2cb2SDavid du Colombier 	uchar smac[Eaddrlen], uchar amac[Eaddrlen],
487*1bbb2cb2SDavid du Colombier 	uchar snonce[Noncelen],  uchar anonce[Noncelen],
488*1bbb2cb2SDavid du Colombier 	uchar ptk[PTKlen])
489*1bbb2cb2SDavid du Colombier {
490*1bbb2cb2SDavid du Colombier 	uchar buf[2*Eaddrlen + 2*Noncelen], *p;
491*1bbb2cb2SDavid du Colombier 	AuthRpc *rpc;
492*1bbb2cb2SDavid du Colombier 	int afd, ret;
493*1bbb2cb2SDavid du Colombier 	char *s;
494*1bbb2cb2SDavid du Colombier 
495*1bbb2cb2SDavid du Colombier 	ret = -1;
496*1bbb2cb2SDavid du Colombier 	s = nil;
497*1bbb2cb2SDavid du Colombier 	rpc = nil;
498*1bbb2cb2SDavid du Colombier 	if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
499*1bbb2cb2SDavid du Colombier 		goto out;
500*1bbb2cb2SDavid du Colombier 	if((rpc = auth_allocrpc(afd)) == nil)
501*1bbb2cb2SDavid du Colombier 		goto out;
502*1bbb2cb2SDavid du Colombier 	if((s = getessid()) == nil)
503*1bbb2cb2SDavid du Colombier 		goto out;
504*1bbb2cb2SDavid du Colombier 	if((s = smprint("proto=wpapsk role=client essid=%q", s)) == nil)
505*1bbb2cb2SDavid du Colombier 		goto out;
506*1bbb2cb2SDavid du Colombier 	if((ret = auth_rpc(rpc, "start", s, strlen(s))) != ARok)
507*1bbb2cb2SDavid du Colombier 		goto out;
508*1bbb2cb2SDavid du Colombier 	p = buf;
509*1bbb2cb2SDavid du Colombier 	memmove(p, smac, Eaddrlen); p += Eaddrlen;
510*1bbb2cb2SDavid du Colombier 	memmove(p, amac, Eaddrlen); p += Eaddrlen;
511*1bbb2cb2SDavid du Colombier 	memmove(p, snonce, Noncelen); p += Noncelen;
512*1bbb2cb2SDavid du Colombier 	memmove(p, anonce, Noncelen); p += Noncelen;
513*1bbb2cb2SDavid du Colombier 	if((ret = auth_rpc(rpc, "write", buf, p - buf)) != ARok)
514*1bbb2cb2SDavid du Colombier 		goto out;
515*1bbb2cb2SDavid du Colombier 	if((ret = auth_rpc(rpc, "read", nil, 0)) != ARok)
516*1bbb2cb2SDavid du Colombier 		goto out;
517*1bbb2cb2SDavid du Colombier 	if(rpc->narg != PTKlen){
518*1bbb2cb2SDavid du Colombier 		ret = -1;
519*1bbb2cb2SDavid du Colombier 		goto out;
520*1bbb2cb2SDavid du Colombier 	}
521*1bbb2cb2SDavid du Colombier 	memmove(ptk, rpc->arg, PTKlen);
522*1bbb2cb2SDavid du Colombier 	ret = 0;
523*1bbb2cb2SDavid du Colombier out:
524*1bbb2cb2SDavid du Colombier 	if(getkey != nil){
525*1bbb2cb2SDavid du Colombier 		switch(ret){
526*1bbb2cb2SDavid du Colombier 		case ARneedkey:
527*1bbb2cb2SDavid du Colombier 		case ARbadkey:
528*1bbb2cb2SDavid du Colombier 			(*getkey)(rpc->arg);
529*1bbb2cb2SDavid du Colombier 			break;
530*1bbb2cb2SDavid du Colombier 		}
531*1bbb2cb2SDavid du Colombier 	}
532*1bbb2cb2SDavid du Colombier 	free(s);
533*1bbb2cb2SDavid du Colombier 	if(afd >= 0) close(afd);
534*1bbb2cb2SDavid du Colombier 	if(rpc != nil) auth_freerpc(rpc);
535*1bbb2cb2SDavid du Colombier 	return ret;
536*1bbb2cb2SDavid du Colombier }
537*1bbb2cb2SDavid du Colombier 
538*1bbb2cb2SDavid du Colombier void
dumpkeydescr(Keydescr * kd)539*1bbb2cb2SDavid du Colombier dumpkeydescr(Keydescr *kd)
540*1bbb2cb2SDavid du Colombier {
541*1bbb2cb2SDavid du Colombier 	static struct {
542*1bbb2cb2SDavid du Colombier 		int	flag;
543*1bbb2cb2SDavid du Colombier 		char	*name;
544*1bbb2cb2SDavid du Colombier 	} flags[] = {
545*1bbb2cb2SDavid du Colombier 		Fptk,	"ptk",
546*1bbb2cb2SDavid du Colombier 		Fins,	"ins",
547*1bbb2cb2SDavid du Colombier 		Fack,	"ack",
548*1bbb2cb2SDavid du Colombier 		Fmic,	"mic",
549*1bbb2cb2SDavid du Colombier 		Fsec,	"sec",
550*1bbb2cb2SDavid du Colombier 		Ferr,	"err",
551*1bbb2cb2SDavid du Colombier 		Freq,	"req",
552*1bbb2cb2SDavid du Colombier 		Fenc,	"enc",
553*1bbb2cb2SDavid du Colombier 	};
554*1bbb2cb2SDavid du Colombier 	int i, f;
555*1bbb2cb2SDavid du Colombier 
556*1bbb2cb2SDavid du Colombier 	f = kd->flags[0]<<8 | kd->flags[1];
557*1bbb2cb2SDavid du Colombier 	fprint(2, "type=%.*H vers=%d flags=%.*H ( ",
558*1bbb2cb2SDavid du Colombier 		sizeof(kd->type), kd->type, kd->flags[1] & 7,
559*1bbb2cb2SDavid du Colombier 		sizeof(kd->flags), kd->flags);
560*1bbb2cb2SDavid du Colombier 	for(i=0; i<nelem(flags); i++)
561*1bbb2cb2SDavid du Colombier 		if(flags[i].flag & f)
562*1bbb2cb2SDavid du Colombier 			fprint(2, "%s ", flags[i].name);
563*1bbb2cb2SDavid du Colombier 	fprint(2, ") len=%.*H\nrepc=%.*H nonce=%.*H\neapoliv=%.*H rsc=%.*H id=%.*H mic=%.*H\n",
564*1bbb2cb2SDavid du Colombier 		sizeof(kd->keylen), kd->keylen,
565*1bbb2cb2SDavid du Colombier 		sizeof(kd->repc), kd->repc,
566*1bbb2cb2SDavid du Colombier 		sizeof(kd->nonce), kd->nonce,
567*1bbb2cb2SDavid du Colombier 		sizeof(kd->eapoliv), kd->eapoliv,
568*1bbb2cb2SDavid du Colombier 		sizeof(kd->rsc), kd->rsc,
569*1bbb2cb2SDavid du Colombier 		sizeof(kd->id), kd->id,
570*1bbb2cb2SDavid du Colombier 		sizeof(kd->mic), kd->mic);
571*1bbb2cb2SDavid du Colombier 	i = kd->datalen[0]<<8 | kd->datalen[1];
572*1bbb2cb2SDavid du Colombier 	fprint(2, "data[%.4x]=%.*H\n", i, i, kd->data);
573*1bbb2cb2SDavid du Colombier }
574*1bbb2cb2SDavid du Colombier 
575*1bbb2cb2SDavid du Colombier int
rc4unwrap(uchar key[16],uchar iv[16],uchar * data,int len)576*1bbb2cb2SDavid du Colombier rc4unwrap(uchar key[16], uchar iv[16], uchar *data, int len)
577*1bbb2cb2SDavid du Colombier {
578*1bbb2cb2SDavid du Colombier 	uchar seed[32];
579*1bbb2cb2SDavid du Colombier 	RC4state rs;
580*1bbb2cb2SDavid du Colombier 
581*1bbb2cb2SDavid du Colombier 	memmove(seed, iv, 16);
582*1bbb2cb2SDavid du Colombier 	memmove(seed+16, key, 16);
583*1bbb2cb2SDavid du Colombier 	setupRC4state(&rs, seed, sizeof(seed));
584*1bbb2cb2SDavid du Colombier 	rc4skip(&rs, 256);
585*1bbb2cb2SDavid du Colombier 	rc4(&rs, data, len);
586*1bbb2cb2SDavid du Colombier 	return len;
587*1bbb2cb2SDavid du Colombier }
588*1bbb2cb2SDavid du Colombier 
589*1bbb2cb2SDavid du Colombier int
aesunwrap(uchar * key,int nkey,uchar * data,int len)590*1bbb2cb2SDavid du Colombier aesunwrap(uchar *key, int nkey, uchar *data, int len)
591*1bbb2cb2SDavid du Colombier {
592*1bbb2cb2SDavid du Colombier 	static uchar IV[8] = { 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, };
593*1bbb2cb2SDavid du Colombier 	uchar B[16], *R;
594*1bbb2cb2SDavid du Colombier 	AESstate s;
595*1bbb2cb2SDavid du Colombier 	uint t;
596*1bbb2cb2SDavid du Colombier 	int n;
597*1bbb2cb2SDavid du Colombier 
598*1bbb2cb2SDavid du Colombier 	len -= 8;
599*1bbb2cb2SDavid du Colombier 	if(len < 16 || (len % 8) != 0)
600*1bbb2cb2SDavid du Colombier 		return -1;
601*1bbb2cb2SDavid du Colombier 	n = len/8;
602*1bbb2cb2SDavid du Colombier 	t = n*6;
603*1bbb2cb2SDavid du Colombier 	setupAESstate(&s, key, nkey, 0);
604*1bbb2cb2SDavid du Colombier 	memmove(B, data, 8);
605*1bbb2cb2SDavid du Colombier 	memmove(data, data+8, n*8);
606*1bbb2cb2SDavid du Colombier 	do {
607*1bbb2cb2SDavid du Colombier 		for(R = data + (n - 1)*8; R >= data; t--, R -= 8){
608*1bbb2cb2SDavid du Colombier 			memmove(B+8, R, 8);
609*1bbb2cb2SDavid du Colombier 			B[7] ^= (t >> 0);
610*1bbb2cb2SDavid du Colombier 			B[6] ^= (t >> 8);
611*1bbb2cb2SDavid du Colombier 			B[5] ^= (t >> 16);
612*1bbb2cb2SDavid du Colombier 			B[4] ^= (t >> 24);
613*1bbb2cb2SDavid du Colombier 			aes_decrypt(s.dkey, s.rounds, B, B);
614*1bbb2cb2SDavid du Colombier 			memmove(R, B+8, 8);
615*1bbb2cb2SDavid du Colombier 		}
616*1bbb2cb2SDavid du Colombier 	} while(t > 0);
617*1bbb2cb2SDavid du Colombier 	if(memcmp(B, IV, 8) != 0)
618*1bbb2cb2SDavid du Colombier 		return -1;
619*1bbb2cb2SDavid du Colombier 	return n*8;
620*1bbb2cb2SDavid du Colombier }
621*1bbb2cb2SDavid du Colombier 
622*1bbb2cb2SDavid du Colombier int
calcmic(Keydescr * kd,uchar * msg,int msglen)623*1bbb2cb2SDavid du Colombier calcmic(Keydescr *kd, uchar *msg, int msglen)
624*1bbb2cb2SDavid du Colombier {
625*1bbb2cb2SDavid du Colombier 	int vers;
626*1bbb2cb2SDavid du Colombier 
627*1bbb2cb2SDavid du Colombier 	vers = kd->flags[1] & 7;
628*1bbb2cb2SDavid du Colombier 	memset(kd->mic, 0, MIClen);
629*1bbb2cb2SDavid du Colombier 	if(vers == 1){
630*1bbb2cb2SDavid du Colombier 		uchar digest[MD5dlen];
631*1bbb2cb2SDavid du Colombier 
632*1bbb2cb2SDavid du Colombier 		hmac_md5(msg, msglen, ptk, 16, digest, nil);
633*1bbb2cb2SDavid du Colombier 		memmove(kd->mic, digest, MIClen);
634*1bbb2cb2SDavid du Colombier 		return 0;
635*1bbb2cb2SDavid du Colombier 	}
636*1bbb2cb2SDavid du Colombier 	if(vers == 2){
637*1bbb2cb2SDavid du Colombier 		uchar digest[SHA1dlen];
638*1bbb2cb2SDavid du Colombier 
639*1bbb2cb2SDavid du Colombier 		hmac_sha1(msg, msglen, ptk, 16, digest, nil);
640*1bbb2cb2SDavid du Colombier 		memmove(kd->mic, digest, MIClen);
641*1bbb2cb2SDavid du Colombier 		return 0;
642*1bbb2cb2SDavid du Colombier 	}
643*1bbb2cb2SDavid du Colombier 	return -1;
644*1bbb2cb2SDavid du Colombier }
645*1bbb2cb2SDavid du Colombier 
646*1bbb2cb2SDavid du Colombier int
checkmic(Keydescr * kd,uchar * msg,int msglen)647*1bbb2cb2SDavid du Colombier checkmic(Keydescr *kd, uchar *msg, int msglen)
648*1bbb2cb2SDavid du Colombier {
649*1bbb2cb2SDavid du Colombier 	uchar tmp[MIClen];
650*1bbb2cb2SDavid du Colombier 
651*1bbb2cb2SDavid du Colombier 	memmove(tmp, kd->mic, MIClen);
652*1bbb2cb2SDavid du Colombier 	if(calcmic(kd, msg, msglen) != 0)
653*1bbb2cb2SDavid du Colombier 		return -1;
654*1bbb2cb2SDavid du Colombier 	return memcmp(tmp, kd->mic, MIClen) != 0;
655*1bbb2cb2SDavid du Colombier }
656*1bbb2cb2SDavid du Colombier 
657*1bbb2cb2SDavid du Colombier void
fdwrite(Eapconn * conn,uchar * data,int len)658*1bbb2cb2SDavid du Colombier fdwrite(Eapconn *conn, uchar *data, int len)
659*1bbb2cb2SDavid du Colombier {
660*1bbb2cb2SDavid du Colombier 	if(write(conn->fd, data, len) != len)
661*1bbb2cb2SDavid du Colombier 		sysfatal("write: %r");
662*1bbb2cb2SDavid du Colombier }
663*1bbb2cb2SDavid du Colombier 
664*1bbb2cb2SDavid du Colombier void
etherwrite(Eapconn * conn,uchar * data,int len)665*1bbb2cb2SDavid du Colombier etherwrite(Eapconn *conn, uchar *data, int len)
666*1bbb2cb2SDavid du Colombier {
667*1bbb2cb2SDavid du Colombier 	uchar *buf, *p;
668*1bbb2cb2SDavid du Colombier 	int n;
669*1bbb2cb2SDavid du Colombier 
670*1bbb2cb2SDavid du Colombier 	if(debug)
671*1bbb2cb2SDavid du Colombier 		fprint(2, "\nreply(v%d,t%d) %E -> %E: ", conn->version, conn->type, conn->smac, conn->amac);
672*1bbb2cb2SDavid du Colombier 	n = 2*Eaddrlen + 2 + len;
673*1bbb2cb2SDavid du Colombier 	if(n < 60) n = 60;	/* ETHERMINTU */
674*1bbb2cb2SDavid du Colombier 	p = buf = emalloc(n);
675*1bbb2cb2SDavid du Colombier 	/* ethernet header */
676*1bbb2cb2SDavid du Colombier 	memmove(p, conn->amac, Eaddrlen); p += Eaddrlen;
677*1bbb2cb2SDavid du Colombier 	memmove(p, conn->smac, Eaddrlen); p += Eaddrlen;
678*1bbb2cb2SDavid du Colombier 	*p++ = 0x88;
679*1bbb2cb2SDavid du Colombier 	*p++ = 0x8e;
680*1bbb2cb2SDavid du Colombier 	/* eapol data */
681*1bbb2cb2SDavid du Colombier 	memmove(p, data, len);
682*1bbb2cb2SDavid du Colombier 	fdwrite(conn, buf, n);
683*1bbb2cb2SDavid du Colombier 	free(buf);
684*1bbb2cb2SDavid du Colombier }
685*1bbb2cb2SDavid du Colombier 
686*1bbb2cb2SDavid du Colombier void
eapwrite(Eapconn * conn,uchar * data,int len)687*1bbb2cb2SDavid du Colombier eapwrite(Eapconn *conn, uchar *data, int len)
688*1bbb2cb2SDavid du Colombier {
689*1bbb2cb2SDavid du Colombier 	uchar *buf, *p;
690*1bbb2cb2SDavid du Colombier 
691*1bbb2cb2SDavid du Colombier 	p = buf = emalloc(len + 4);
692*1bbb2cb2SDavid du Colombier 	/* eapol header */
693*1bbb2cb2SDavid du Colombier 	*p++ = conn->version;
694*1bbb2cb2SDavid du Colombier 	*p++ = conn->type;
695*1bbb2cb2SDavid du Colombier 	*p++ = len >> 8;
696*1bbb2cb2SDavid du Colombier 	*p++ = len;
697*1bbb2cb2SDavid du Colombier 	/* eap data */
698*1bbb2cb2SDavid du Colombier 	memmove(p, data, len); p += len;
699*1bbb2cb2SDavid du Colombier 	etherwrite(conn, buf, p - buf);
700*1bbb2cb2SDavid du Colombier 	free(buf);
701*1bbb2cb2SDavid du Colombier }
702*1bbb2cb2SDavid du Colombier 
703*1bbb2cb2SDavid du Colombier void
replykey(Eapconn * conn,int flags,Keydescr * kd,uchar * data,int datalen)704*1bbb2cb2SDavid du Colombier replykey(Eapconn *conn, int flags, Keydescr *kd, uchar *data, int datalen)
705*1bbb2cb2SDavid du Colombier {
706*1bbb2cb2SDavid du Colombier 	uchar buf[4096], *p = buf;
707*1bbb2cb2SDavid du Colombier 
708*1bbb2cb2SDavid du Colombier 	/* eapol hader */
709*1bbb2cb2SDavid du Colombier 	*p++ = conn->version;
710*1bbb2cb2SDavid du Colombier 	*p++ = conn->type;
711*1bbb2cb2SDavid du Colombier 	datalen += Keydescrlen;
712*1bbb2cb2SDavid du Colombier 	*p++ = datalen >> 8;
713*1bbb2cb2SDavid du Colombier 	*p++ = datalen;
714*1bbb2cb2SDavid du Colombier 	datalen -= Keydescrlen;
715*1bbb2cb2SDavid du Colombier 	/* key header */
716*1bbb2cb2SDavid du Colombier 	memmove(p, kd, Keydescrlen);
717*1bbb2cb2SDavid du Colombier 	kd = (Keydescr*)p;
718*1bbb2cb2SDavid du Colombier 	kd->flags[0] = flags >> 8;
719*1bbb2cb2SDavid du Colombier 	kd->flags[1] = flags;
720*1bbb2cb2SDavid du Colombier 	kd->datalen[0] = datalen >> 8;
721*1bbb2cb2SDavid du Colombier 	kd->datalen[1] = datalen;
722*1bbb2cb2SDavid du Colombier 	/* key data */
723*1bbb2cb2SDavid du Colombier 	p = kd->data;
724*1bbb2cb2SDavid du Colombier 	memmove(p, data, datalen);
725*1bbb2cb2SDavid du Colombier 	p += datalen;
726*1bbb2cb2SDavid du Colombier 	/* mic */
727*1bbb2cb2SDavid du Colombier 	memset(kd->mic, 0, MIClen);
728*1bbb2cb2SDavid du Colombier 	if(flags & Fmic)
729*1bbb2cb2SDavid du Colombier 		calcmic(kd, buf, p - buf);
730*1bbb2cb2SDavid du Colombier 	etherwrite(conn, buf, p - buf);
731*1bbb2cb2SDavid du Colombier 	if(debug)
732*1bbb2cb2SDavid du Colombier 		dumpkeydescr(kd);
733*1bbb2cb2SDavid du Colombier }
734*1bbb2cb2SDavid du Colombier 
735*1bbb2cb2SDavid du Colombier void
eapresp(Eapconn * conn,int code,int id,uchar * data,int len)736*1bbb2cb2SDavid du Colombier eapresp(Eapconn *conn, int code, int id, uchar *data, int len)
737*1bbb2cb2SDavid du Colombier {
738*1bbb2cb2SDavid du Colombier 	uchar *buf, *p;
739*1bbb2cb2SDavid du Colombier 
740*1bbb2cb2SDavid du Colombier 	len += 4;
741*1bbb2cb2SDavid du Colombier 	p = buf = emalloc(len);
742*1bbb2cb2SDavid du Colombier 	/* eap header */
743*1bbb2cb2SDavid du Colombier 	*p++ = code;
744*1bbb2cb2SDavid du Colombier 	*p++ = id;
745*1bbb2cb2SDavid du Colombier 	*p++ = len >> 8;
746*1bbb2cb2SDavid du Colombier 	*p++ = len;
747*1bbb2cb2SDavid du Colombier 	memmove(p, data, len-4);
748*1bbb2cb2SDavid du Colombier 	(*conn->write)(conn, buf, len);
749*1bbb2cb2SDavid du Colombier 	free(buf);
750*1bbb2cb2SDavid du Colombier 
751*1bbb2cb2SDavid du Colombier 	if(debug)
752*1bbb2cb2SDavid du Colombier 		fprint(2, "eapresp(code=%d, id=%d, data=%.*H)\n", code, id, len-4, data);
753*1bbb2cb2SDavid du Colombier }
754*1bbb2cb2SDavid du Colombier 
755*1bbb2cb2SDavid du Colombier void
tlsreader(TLStunn * tunn,Eapconn * conn)756*1bbb2cb2SDavid du Colombier tlsreader(TLStunn *tunn, Eapconn *conn)
757*1bbb2cb2SDavid du Colombier {
758*1bbb2cb2SDavid du Colombier 	enum {
759*1bbb2cb2SDavid du Colombier 		Tlshdrsz = 5,
760*1bbb2cb2SDavid du Colombier 		TLStunnhdrsz = 6,
761*1bbb2cb2SDavid du Colombier 	};
762*1bbb2cb2SDavid du Colombier 	uchar *rec, *w, *p;
763*1bbb2cb2SDavid du Colombier 	int fd, n, css;
764*1bbb2cb2SDavid du Colombier 
765*1bbb2cb2SDavid du Colombier 	fd = tunn->fd;
766*1bbb2cb2SDavid du Colombier 	rec = nil;
767*1bbb2cb2SDavid du Colombier 	css = 0;
768*1bbb2cb2SDavid du Colombier Reset:
769*1bbb2cb2SDavid du Colombier 	w = rec;
770*1bbb2cb2SDavid du Colombier 	w += TLStunnhdrsz;
771*1bbb2cb2SDavid du Colombier 	for(;;w += n){
772*1bbb2cb2SDavid du Colombier 		if((p = realloc(rec, (w - rec) + Tlshdrsz)) == nil)
773*1bbb2cb2SDavid du Colombier 			break;
774*1bbb2cb2SDavid du Colombier 		w = p + (w - rec), rec = p;
775*1bbb2cb2SDavid du Colombier 		if(readn(fd, w, Tlshdrsz) != Tlshdrsz)
776*1bbb2cb2SDavid du Colombier 			break;
777*1bbb2cb2SDavid du Colombier 		n = w[3]<<8 | w[4];
778*1bbb2cb2SDavid du Colombier 		if(n < 1)
779*1bbb2cb2SDavid du Colombier 			break;
780*1bbb2cb2SDavid du Colombier 		if((p = realloc(rec, (w - rec) + Tlshdrsz+n)) == nil)
781*1bbb2cb2SDavid du Colombier 			break;
782*1bbb2cb2SDavid du Colombier 		w = p + (w - rec), rec = p;
783*1bbb2cb2SDavid du Colombier 		if(readn(fd, w+Tlshdrsz, n) != n)
784*1bbb2cb2SDavid du Colombier 			break;
785*1bbb2cb2SDavid du Colombier 		n += Tlshdrsz;
786*1bbb2cb2SDavid du Colombier 
787*1bbb2cb2SDavid du Colombier 		/* batch records that need to be send together */
788*1bbb2cb2SDavid du Colombier 		if(!css){
789*1bbb2cb2SDavid du Colombier 			/* Client Certificate */
790*1bbb2cb2SDavid du Colombier 			if(w[0] == 22 && w[5] == 11)
791*1bbb2cb2SDavid du Colombier 				continue;
792*1bbb2cb2SDavid du Colombier 			/* Client Key Exchange */
793*1bbb2cb2SDavid du Colombier 			if(w[0] == 22 && w[5] == 16)
794*1bbb2cb2SDavid du Colombier 				continue;
795*1bbb2cb2SDavid du Colombier 			/* Change Cipher Spec */
796*1bbb2cb2SDavid du Colombier 			if(w[0] == 20){
797*1bbb2cb2SDavid du Colombier 				css = 1;
798*1bbb2cb2SDavid du Colombier 				continue;
799*1bbb2cb2SDavid du Colombier 			}
800*1bbb2cb2SDavid du Colombier 		}
801*1bbb2cb2SDavid du Colombier 
802*1bbb2cb2SDavid du Colombier 		/* do not forward alert, close connection */
803*1bbb2cb2SDavid du Colombier 		if(w[0] == 21)
804*1bbb2cb2SDavid du Colombier 			break;
805*1bbb2cb2SDavid du Colombier 
806*1bbb2cb2SDavid du Colombier 		/* check if we'r still the tunnel for this connection */
807*1bbb2cb2SDavid du Colombier 		if(conn->tunn != tunn)
808*1bbb2cb2SDavid du Colombier 			break;
809*1bbb2cb2SDavid du Colombier 
810*1bbb2cb2SDavid du Colombier 		/* flush records in encapsulation */
811*1bbb2cb2SDavid du Colombier 		p = rec + TLStunnhdrsz;
812*1bbb2cb2SDavid du Colombier 		w += n;
813*1bbb2cb2SDavid du Colombier 		n = w - p;
814*1bbb2cb2SDavid du Colombier 		*(--p) = n;
815*1bbb2cb2SDavid du Colombier 		*(--p) = n >> 8;
816*1bbb2cb2SDavid du Colombier 		*(--p) = n >> 16;
817*1bbb2cb2SDavid du Colombier 		*(--p) = n >> 24;
818*1bbb2cb2SDavid du Colombier 		*(--p) = 0x80;	/* flags: Length included */
819*1bbb2cb2SDavid du Colombier 		*(--p) = tunn->tp;
820*1bbb2cb2SDavid du Colombier 
821*1bbb2cb2SDavid du Colombier 		eapresp(conn, 2, tunn->id, p, w - p);
822*1bbb2cb2SDavid du Colombier 		goto Reset;
823*1bbb2cb2SDavid du Colombier 	}
824*1bbb2cb2SDavid du Colombier 	free(rec);
825*1bbb2cb2SDavid du Colombier }
826*1bbb2cb2SDavid du Colombier 
827*1bbb2cb2SDavid du Colombier void ttlsclient(int);
828*1bbb2cb2SDavid du Colombier void peapclient(int);
829*1bbb2cb2SDavid du Colombier 
830*1bbb2cb2SDavid du Colombier void
eapreset(Eapconn * conn)831*1bbb2cb2SDavid du Colombier eapreset(Eapconn *conn)
832*1bbb2cb2SDavid du Colombier {
833*1bbb2cb2SDavid du Colombier 	TLStunn *tunn;
834*1bbb2cb2SDavid du Colombier 
835*1bbb2cb2SDavid du Colombier 	tunn = conn->tunn;
836*1bbb2cb2SDavid du Colombier 	if(tunn == nil)
837*1bbb2cb2SDavid du Colombier 		return;
838*1bbb2cb2SDavid du Colombier 	if(debug)
839*1bbb2cb2SDavid du Colombier 		fprint(2, "eapreset: kill client %d\n", tunn->clientpid);
840*1bbb2cb2SDavid du Colombier 	conn->tunn = nil;
841*1bbb2cb2SDavid du Colombier 	postnote(PNPROC, tunn->clientpid, "kill");
842*1bbb2cb2SDavid du Colombier }
843*1bbb2cb2SDavid du Colombier 
844*1bbb2cb2SDavid du Colombier int
tlswrap(int fd,char * label)845*1bbb2cb2SDavid du Colombier tlswrap(int fd, char *label)
846*1bbb2cb2SDavid du Colombier {
847*1bbb2cb2SDavid du Colombier 	TLSconn *tls;
848*1bbb2cb2SDavid du Colombier 
849*1bbb2cb2SDavid du Colombier 	tls = emalloc(sizeof(TLSconn));
850*1bbb2cb2SDavid du Colombier 	if(debug)
851*1bbb2cb2SDavid du Colombier 		tls->trace = print;
852*1bbb2cb2SDavid du Colombier 	if(label != nil){
853*1bbb2cb2SDavid du Colombier 		/* tls client computes the 1024 bit MSK for us */
854*1bbb2cb2SDavid du Colombier 		tls->sessionType = "ttls";
855*1bbb2cb2SDavid du Colombier 		tls->sessionConst = label;
856*1bbb2cb2SDavid du Colombier 		tls->sessionKeylen = 128;
857*1bbb2cb2SDavid du Colombier 		tls->sessionKey = emalloc(tls->sessionKeylen);
858*1bbb2cb2SDavid du Colombier 	}
859*1bbb2cb2SDavid du Colombier 	fd = tlsClient(fd, tls);
860*1bbb2cb2SDavid du Colombier 	if(fd < 0)
861*1bbb2cb2SDavid du Colombier 		sysfatal("tlsClient: %r");
862*1bbb2cb2SDavid du Colombier 	if(label != nil && tls->sessionKey != nil){
863*1bbb2cb2SDavid du Colombier 		/*
864*1bbb2cb2SDavid du Colombier 		 * PMK is derived from MSK by taking the first 256 bits.
865*1bbb2cb2SDavid du Colombier 		 * we store the PMK into factotum with setpmk() associated
866*1bbb2cb2SDavid du Colombier 		 * with the current essid.
867*1bbb2cb2SDavid du Colombier 		 */
868*1bbb2cb2SDavid du Colombier 		if(setpmk(tls->sessionKey) < 0)
869*1bbb2cb2SDavid du Colombier 			sysfatal("setpmk: %r");
870*1bbb2cb2SDavid du Colombier 
871*1bbb2cb2SDavid du Colombier 		/* destroy session key */
872*1bbb2cb2SDavid du Colombier 		memset(tls->sessionKey, 0, tls->sessionKeylen);
873*1bbb2cb2SDavid du Colombier 	}
874*1bbb2cb2SDavid du Colombier 	free(tls->cert);	/* TODO: check cert */
875*1bbb2cb2SDavid du Colombier 	free(tls->sessionID);
876*1bbb2cb2SDavid du Colombier 	free(tls->sessionKey);
877*1bbb2cb2SDavid du Colombier 	free(tls);
878*1bbb2cb2SDavid du Colombier 	return fd;
879*1bbb2cb2SDavid du Colombier }
880*1bbb2cb2SDavid du Colombier 
881*1bbb2cb2SDavid du Colombier void
eapreq(Eapconn * conn,int code,int id,uchar * data,int datalen)882*1bbb2cb2SDavid du Colombier eapreq(Eapconn *conn, int code, int id, uchar *data, int datalen)
883*1bbb2cb2SDavid du Colombier {
884*1bbb2cb2SDavid du Colombier 	TLStunn *tunn;
885*1bbb2cb2SDavid du Colombier 	int tp, frag;
886*1bbb2cb2SDavid du Colombier 	char *user;
887*1bbb2cb2SDavid du Colombier 
888*1bbb2cb2SDavid du Colombier 	if(debug)
889*1bbb2cb2SDavid du Colombier 		fprint(2, "eapreq(code=%d, id=%d, data=%.*H)\n", code, id, datalen, data);
890*1bbb2cb2SDavid du Colombier 
891*1bbb2cb2SDavid du Colombier 	switch(code){
892*1bbb2cb2SDavid du Colombier 	case 1:	/* Request */
893*1bbb2cb2SDavid du Colombier 		break;
894*1bbb2cb2SDavid du Colombier 	case 4:	/* NAK */
895*1bbb2cb2SDavid du Colombier 	case 3:	/* Success */
896*1bbb2cb2SDavid du Colombier 		eapreset(conn);
897*1bbb2cb2SDavid du Colombier 		if(code == 4 || debug)
898*1bbb2cb2SDavid du Colombier 			fprint(2, "%s: eap code %s\n", argv0, code == 3 ? "Success" : "NAK");
899*1bbb2cb2SDavid du Colombier 		return;
900*1bbb2cb2SDavid du Colombier 	default:
901*1bbb2cb2SDavid du Colombier 	unhandled:
902*1bbb2cb2SDavid du Colombier 		if(debug)
903*1bbb2cb2SDavid du Colombier 			fprint(2, "unhandled: %.*H\n", datalen < 0 ? 0 : datalen, data);
904*1bbb2cb2SDavid du Colombier 		return;
905*1bbb2cb2SDavid du Colombier 	}
906*1bbb2cb2SDavid du Colombier 	if(datalen < 1)
907*1bbb2cb2SDavid du Colombier 		goto unhandled;
908*1bbb2cb2SDavid du Colombier 
909*1bbb2cb2SDavid du Colombier 	tp = data[0];
910*1bbb2cb2SDavid du Colombier 	switch(tp){
911*1bbb2cb2SDavid du Colombier 	case 1:		/* Identity */
912*1bbb2cb2SDavid du Colombier 		user = getidentity();
913*1bbb2cb2SDavid du Colombier 		datalen = 1+strlen(user);
914*1bbb2cb2SDavid du Colombier 		memmove(data+1, user, datalen-1);
915*1bbb2cb2SDavid du Colombier 		eapresp(conn, 2, id, data, datalen);
916*1bbb2cb2SDavid du Colombier 		return;
917*1bbb2cb2SDavid du Colombier 	case 2:
918*1bbb2cb2SDavid du Colombier 		fprint(2, "%s: eap error: %.*s\n",
919*1bbb2cb2SDavid du Colombier 			argv0, utfnlen((char*)data+1, datalen-1), (char*)data+1);
920*1bbb2cb2SDavid du Colombier 		return;
921*1bbb2cb2SDavid du Colombier 	case 33:	/* EAP Extensions (AVP) */
922*1bbb2cb2SDavid du Colombier 		if(debug)
923*1bbb2cb2SDavid du Colombier 			fprint(2, "eap extension: %.*H\n", datalen, data);
924*1bbb2cb2SDavid du Colombier 		eapresp(conn, 2, id, data, datalen);
925*1bbb2cb2SDavid du Colombier 		return;
926*1bbb2cb2SDavid du Colombier 	case 26:	/* MS-CHAP-V2 */
927*1bbb2cb2SDavid du Colombier 		data++;
928*1bbb2cb2SDavid du Colombier 		datalen--;
929*1bbb2cb2SDavid du Colombier 		if(datalen < 1)
930*1bbb2cb2SDavid du Colombier 			break;
931*1bbb2cb2SDavid du Colombier 
932*1bbb2cb2SDavid du Colombier 		/* OpCode */
933*1bbb2cb2SDavid du Colombier 		switch(data[0]){
934*1bbb2cb2SDavid du Colombier 		case 1:	/* Challenge */
935*1bbb2cb2SDavid du Colombier 			if(datalen > 4) {
936*1bbb2cb2SDavid du Colombier 				uchar cid, chal[16], resp[48];
937*1bbb2cb2SDavid du Colombier 				char user[256+1];
938*1bbb2cb2SDavid du Colombier 				int len;
939*1bbb2cb2SDavid du Colombier 
940*1bbb2cb2SDavid du Colombier 				cid = data[1];
941*1bbb2cb2SDavid du Colombier 				len = data[2]<<8 | data[3];
942*1bbb2cb2SDavid du Colombier 				if(data[4] != sizeof(chal))
943*1bbb2cb2SDavid du Colombier 					break;
944*1bbb2cb2SDavid du Colombier 				if(len > datalen || (5 + data[4]) > len)
945*1bbb2cb2SDavid du Colombier 					break;
946*1bbb2cb2SDavid du Colombier 				memmove(chal, data+5, sizeof(chal));
947*1bbb2cb2SDavid du Colombier 				memset(user, 0, sizeof(user));
948*1bbb2cb2SDavid du Colombier 				memset(resp, 0, sizeof(resp));
949*1bbb2cb2SDavid du Colombier 				if(auth_respond(chal, sizeof(chal), user, sizeof(user), resp, sizeof(resp), nil,
950*1bbb2cb2SDavid du Colombier 					"proto=mschapv2 role=client service=wpa essid=%q", essid) < 0){
951*1bbb2cb2SDavid du Colombier 					fprint(2, "%s: eap mschapv2: auth_respond: %r\n", argv0);
952*1bbb2cb2SDavid du Colombier 					break;
953*1bbb2cb2SDavid du Colombier 				}
954*1bbb2cb2SDavid du Colombier 				len = 5 + sizeof(resp) + 1 + strlen(user);
955*1bbb2cb2SDavid du Colombier 				data[0] = 2;		/* OpCode - Response */
956*1bbb2cb2SDavid du Colombier 				data[1] = cid;		/* Identifier */
957*1bbb2cb2SDavid du Colombier 				data[2] = len >> 8;
958*1bbb2cb2SDavid du Colombier 				data[3] = len;
959*1bbb2cb2SDavid du Colombier 				data[4] = sizeof(resp)+1;	/* ValueSize */
960*1bbb2cb2SDavid du Colombier 				memmove(data+5, resp, sizeof(resp));
961*1bbb2cb2SDavid du Colombier 				data[5 + sizeof(resp)] = 0;	/* flags */
962*1bbb2cb2SDavid du Colombier 				strcpy((char*)&data[5 + sizeof(resp) + 1], user);
963*1bbb2cb2SDavid du Colombier 
964*1bbb2cb2SDavid du Colombier 				*(--data) = tp, len++;
965*1bbb2cb2SDavid du Colombier 				eapresp(conn, 2, id, data, len);
966*1bbb2cb2SDavid du Colombier 				return;
967*1bbb2cb2SDavid du Colombier 			}
968*1bbb2cb2SDavid du Colombier 			break;
969*1bbb2cb2SDavid du Colombier 
970*1bbb2cb2SDavid du Colombier 		case 3:	/* Success */
971*1bbb2cb2SDavid du Colombier 		case 4:	/* Failure */
972*1bbb2cb2SDavid du Colombier 			if(debug || data[0] == 4)
973*1bbb2cb2SDavid du Colombier 				fprint(2, "%s: eap mschapv2 %s: %.*s\n", argv0,
974*1bbb2cb2SDavid du Colombier 					data[0] == 3 ? "Success" : "Failure",
975*1bbb2cb2SDavid du Colombier 					datalen < 4 ? 0 : utfnlen((char*)data+4, datalen-4), (char*)data+4);
976*1bbb2cb2SDavid du Colombier 			*(--data) = tp;
977*1bbb2cb2SDavid du Colombier 			eapresp(conn, 2, id, data, 2);
978*1bbb2cb2SDavid du Colombier 			return;
979*1bbb2cb2SDavid du Colombier 		}
980*1bbb2cb2SDavid du Colombier 		break;
981*1bbb2cb2SDavid du Colombier 
982*1bbb2cb2SDavid du Colombier 	case 21:	/* EAP-TTLS */
983*1bbb2cb2SDavid du Colombier 	case 25:	/* PEAP */
984*1bbb2cb2SDavid du Colombier 		if(datalen < 2)
985*1bbb2cb2SDavid du Colombier 			break;
986*1bbb2cb2SDavid du Colombier 		datalen -= 2;
987*1bbb2cb2SDavid du Colombier 		data++;
988*1bbb2cb2SDavid du Colombier 		tunn = conn->tunn;
989*1bbb2cb2SDavid du Colombier 		if(*data & 0x20){	/* flags: start */
990*1bbb2cb2SDavid du Colombier 			int p[2], pid;
991*1bbb2cb2SDavid du Colombier 
992*1bbb2cb2SDavid du Colombier 			if(tunn != nil){
993*1bbb2cb2SDavid du Colombier 				if(tunn->id == id && tunn->tp == tp)
994*1bbb2cb2SDavid du Colombier 					break;	/* is retransmit, ignore */
995*1bbb2cb2SDavid du Colombier 				eapreset(conn);
996*1bbb2cb2SDavid du Colombier 			}
997*1bbb2cb2SDavid du Colombier 			if(pipe(p) < 0)
998*1bbb2cb2SDavid du Colombier 				sysfatal("pipe: %r");
999*1bbb2cb2SDavid du Colombier 			if((pid = fork()) == -1)
1000*1bbb2cb2SDavid du Colombier 				sysfatal("fork: %r");
1001*1bbb2cb2SDavid du Colombier 			if(pid == 0){
1002*1bbb2cb2SDavid du Colombier 				close(p[0]);
1003*1bbb2cb2SDavid du Colombier 				switch(tp){
1004*1bbb2cb2SDavid du Colombier 				case 21:
1005*1bbb2cb2SDavid du Colombier 					ttlsclient(p[1]);
1006*1bbb2cb2SDavid du Colombier 					break;
1007*1bbb2cb2SDavid du Colombier 				case 25:
1008*1bbb2cb2SDavid du Colombier 					peapclient(p[1]);
1009*1bbb2cb2SDavid du Colombier 					break;
1010*1bbb2cb2SDavid du Colombier 				}
1011*1bbb2cb2SDavid du Colombier 				exits(nil);
1012*1bbb2cb2SDavid du Colombier 			}
1013*1bbb2cb2SDavid du Colombier 			close(p[1]);
1014*1bbb2cb2SDavid du Colombier 			tunn = emalloc(sizeof(TLStunn));
1015*1bbb2cb2SDavid du Colombier 			tunn->tp = tp;
1016*1bbb2cb2SDavid du Colombier 			tunn->id = id;
1017*1bbb2cb2SDavid du Colombier 			tunn->fd = p[0];
1018*1bbb2cb2SDavid du Colombier 			tunn->clientpid = pid;
1019*1bbb2cb2SDavid du Colombier 			conn->tunn = tunn;
1020*1bbb2cb2SDavid du Colombier 			if((pid = rfork(RFPROC|RFMEM)) == -1)
1021*1bbb2cb2SDavid du Colombier 				sysfatal("fork: %r");
1022*1bbb2cb2SDavid du Colombier 			if(pid == 0){
1023*1bbb2cb2SDavid du Colombier 				tunn->readerpid = getpid();
1024*1bbb2cb2SDavid du Colombier 				tlsreader(tunn, conn);
1025*1bbb2cb2SDavid du Colombier 				if(conn->tunn == tunn)
1026*1bbb2cb2SDavid du Colombier 					conn->tunn = nil;
1027*1bbb2cb2SDavid du Colombier 				close(tunn->fd);
1028*1bbb2cb2SDavid du Colombier 				free(tunn);
1029*1bbb2cb2SDavid du Colombier 				exits(nil);
1030*1bbb2cb2SDavid du Colombier 			}
1031*1bbb2cb2SDavid du Colombier 			return;
1032*1bbb2cb2SDavid du Colombier 		}
1033*1bbb2cb2SDavid du Colombier 		if(tunn == nil)
1034*1bbb2cb2SDavid du Colombier 			break;
1035*1bbb2cb2SDavid du Colombier 		if(id <= tunn->id || tunn->tp != tp)
1036*1bbb2cb2SDavid du Colombier 			break;
1037*1bbb2cb2SDavid du Colombier 		tunn->id = id;
1038*1bbb2cb2SDavid du Colombier 		frag = *data & 0x40;	/* flags: more fragments */
1039*1bbb2cb2SDavid du Colombier 		if(*data & 0x80){	/* flags: length included */
1040*1bbb2cb2SDavid du Colombier 			datalen -= 4;
1041*1bbb2cb2SDavid du Colombier 			data += 4;
1042*1bbb2cb2SDavid du Colombier 		}
1043*1bbb2cb2SDavid du Colombier 		data++;
1044*1bbb2cb2SDavid du Colombier 		if(datalen > 0)
1045*1bbb2cb2SDavid du Colombier 			write(tunn->fd, data, datalen);
1046*1bbb2cb2SDavid du Colombier 		if(frag || (tp == 25 && data[0] == 20)){	/* ack change cipher spec */
1047*1bbb2cb2SDavid du Colombier 			data -= 2;
1048*1bbb2cb2SDavid du Colombier 			data[0] = tp;
1049*1bbb2cb2SDavid du Colombier 			data[1] = 0;
1050*1bbb2cb2SDavid du Colombier 			eapresp(conn, 2, id, data, 2);
1051*1bbb2cb2SDavid du Colombier 		}
1052*1bbb2cb2SDavid du Colombier 		return;
1053*1bbb2cb2SDavid du Colombier 	}
1054*1bbb2cb2SDavid du Colombier 	goto unhandled;
1055*1bbb2cb2SDavid du Colombier }
1056*1bbb2cb2SDavid du Colombier 
1057*1bbb2cb2SDavid du Colombier int
avp(uchar * p,int n,int code,void * val,int len,int pad)1058*1bbb2cb2SDavid du Colombier avp(uchar *p, int n, int code, void *val, int len, int pad)
1059*1bbb2cb2SDavid du Colombier {
1060*1bbb2cb2SDavid du Colombier 	pad = 8 + ((len + pad) & ~pad);	/* header + data + data pad */
1061*1bbb2cb2SDavid du Colombier 	assert(((pad + 3) & ~3) <= n);
1062*1bbb2cb2SDavid du Colombier 	p[0] = code >> 24;
1063*1bbb2cb2SDavid du Colombier 	p[1] = code >> 16;
1064*1bbb2cb2SDavid du Colombier 	p[2] = code >> 8;
1065*1bbb2cb2SDavid du Colombier 	p[3] = code;
1066*1bbb2cb2SDavid du Colombier 	p[4] = 2;
1067*1bbb2cb2SDavid du Colombier 	p[5] = pad >> 16;
1068*1bbb2cb2SDavid du Colombier 	p[6] = pad >> 8;
1069*1bbb2cb2SDavid du Colombier 	p[7] = pad;
1070*1bbb2cb2SDavid du Colombier 	memmove(p+8, val, len);
1071*1bbb2cb2SDavid du Colombier 	len += 8;
1072*1bbb2cb2SDavid du Colombier 	pad = (pad + 3) & ~3;	/* packet padding */
1073*1bbb2cb2SDavid du Colombier 	memset(p+len, 0, pad - len);
1074*1bbb2cb2SDavid du Colombier 	return pad;
1075*1bbb2cb2SDavid du Colombier }
1076*1bbb2cb2SDavid du Colombier 
1077*1bbb2cb2SDavid du Colombier enum {
1078*1bbb2cb2SDavid du Colombier 	/* Avp Code */
1079*1bbb2cb2SDavid du Colombier 	AvpUserName = 1,
1080*1bbb2cb2SDavid du Colombier 	AvpUserPass = 2,
1081*1bbb2cb2SDavid du Colombier 	AvpChapPass = 3,
1082*1bbb2cb2SDavid du Colombier 	AvpChapChal = 60,
1083*1bbb2cb2SDavid du Colombier };
1084*1bbb2cb2SDavid du Colombier 
1085*1bbb2cb2SDavid du Colombier void
ttlsclient(int fd)1086*1bbb2cb2SDavid du Colombier ttlsclient(int fd)
1087*1bbb2cb2SDavid du Colombier {
1088*1bbb2cb2SDavid du Colombier 	uchar buf[4096];
1089*1bbb2cb2SDavid du Colombier 	UserPasswd *up;
1090*1bbb2cb2SDavid du Colombier 	int n;
1091*1bbb2cb2SDavid du Colombier 
1092*1bbb2cb2SDavid du Colombier 	fd = tlswrap(fd, "ttls keying material");
1093*1bbb2cb2SDavid du Colombier 	if((up = auth_getuserpasswd(nil, "proto=pass service=wpa essid=%q", essid)) == nil)
1094*1bbb2cb2SDavid du Colombier 		sysfatal("auth_getuserpasswd: %r");
1095*1bbb2cb2SDavid du Colombier 	n = avp(buf, sizeof(buf), AvpUserName, up->user, strlen(up->user), 0);
1096*1bbb2cb2SDavid du Colombier 	n += avp(buf+n, sizeof(buf)-n, AvpUserPass, up->passwd, strlen(up->passwd), 15);
1097*1bbb2cb2SDavid du Colombier 	freeup(up);
1098*1bbb2cb2SDavid du Colombier 	write(fd, buf, n);
1099*1bbb2cb2SDavid du Colombier 	memset(buf, 0, n);
1100*1bbb2cb2SDavid du Colombier }
1101*1bbb2cb2SDavid du Colombier 
1102*1bbb2cb2SDavid du Colombier void
peapwrite(Eapconn * conn,uchar * data,int len)1103*1bbb2cb2SDavid du Colombier peapwrite(Eapconn *conn, uchar *data, int len)
1104*1bbb2cb2SDavid du Colombier {
1105*1bbb2cb2SDavid du Colombier 	assert(len >= 4);
1106*1bbb2cb2SDavid du Colombier 	fdwrite(conn, data + 4, len - 4);
1107*1bbb2cb2SDavid du Colombier }
1108*1bbb2cb2SDavid du Colombier 
1109*1bbb2cb2SDavid du Colombier void
peapclient(int fd)1110*1bbb2cb2SDavid du Colombier peapclient(int fd)
1111*1bbb2cb2SDavid du Colombier {
1112*1bbb2cb2SDavid du Colombier 	static Eapconn conn;
1113*1bbb2cb2SDavid du Colombier 	uchar buf[4096], *p;
1114*1bbb2cb2SDavid du Colombier 	int n, id, code;
1115*1bbb2cb2SDavid du Colombier 
1116*1bbb2cb2SDavid du Colombier 	conn.fd = fd = tlswrap(fd, "client EAP encryption");
1117*1bbb2cb2SDavid du Colombier 	while((n = read(fd, p = buf, sizeof(buf))) > 0){
1118*1bbb2cb2SDavid du Colombier 		if(n > 4 && (p[2] << 8 | p[3]) == n && p[4] == 33){
1119*1bbb2cb2SDavid du Colombier 			code = p[0];
1120*1bbb2cb2SDavid du Colombier 			id = p[1];
1121*1bbb2cb2SDavid du Colombier 			p += 4, n -= 4;
1122*1bbb2cb2SDavid du Colombier 			conn.write = fdwrite;
1123*1bbb2cb2SDavid du Colombier 		} else {
1124*1bbb2cb2SDavid du Colombier 			code = 1;
1125*1bbb2cb2SDavid du Colombier 			id = 0;
1126*1bbb2cb2SDavid du Colombier 			conn.write = peapwrite;
1127*1bbb2cb2SDavid du Colombier 		}
1128*1bbb2cb2SDavid du Colombier 		eapreq(&conn, code, id, p, n);
1129*1bbb2cb2SDavid du Colombier 	}
1130*1bbb2cb2SDavid du Colombier }
1131*1bbb2cb2SDavid du Colombier 
1132*1bbb2cb2SDavid du Colombier void
usage(void)1133*1bbb2cb2SDavid du Colombier usage(void)
1134*1bbb2cb2SDavid du Colombier {
1135*1bbb2cb2SDavid du Colombier 	fprint(2, "%s: [-dp12] [-s essid] dev\n", argv0);
1136*1bbb2cb2SDavid du Colombier 	exits("usage");
1137*1bbb2cb2SDavid du Colombier }
1138*1bbb2cb2SDavid du Colombier 
1139*1bbb2cb2SDavid du Colombier void
background(void)1140*1bbb2cb2SDavid du Colombier background(void)
1141*1bbb2cb2SDavid du Colombier {
1142*1bbb2cb2SDavid du Colombier 	if(forked || debug)
1143*1bbb2cb2SDavid du Colombier 		return;
1144*1bbb2cb2SDavid du Colombier 	switch(rfork(RFNOTEG|RFREND|RFPROC|RFNOWAIT)){
1145*1bbb2cb2SDavid du Colombier 	default:
1146*1bbb2cb2SDavid du Colombier 		exits(nil);
1147*1bbb2cb2SDavid du Colombier 	case -1:
1148*1bbb2cb2SDavid du Colombier 		sysfatal("fork: %r");
1149*1bbb2cb2SDavid du Colombier 		return;
1150*1bbb2cb2SDavid du Colombier 	case 0:
1151*1bbb2cb2SDavid du Colombier 		break;
1152*1bbb2cb2SDavid du Colombier 	}
1153*1bbb2cb2SDavid du Colombier 	forked = 1;
1154*1bbb2cb2SDavid du Colombier }
1155*1bbb2cb2SDavid du Colombier 
1156*1bbb2cb2SDavid du Colombier void
main(int argc,char * argv[])1157*1bbb2cb2SDavid du Colombier main(int argc, char *argv[])
1158*1bbb2cb2SDavid du Colombier {
1159*1bbb2cb2SDavid du Colombier 	uchar mac[Eaddrlen], buf[4096], snonce[Noncelen], anonce[Noncelen];
1160*1bbb2cb2SDavid du Colombier 	static uchar brsne[258];
1161*1bbb2cb2SDavid du Colombier 	static Eapconn conn;
1162*1bbb2cb2SDavid du Colombier 	char addr[128];
1163*1bbb2cb2SDavid du Colombier 	uchar *rsne;
1164*1bbb2cb2SDavid du Colombier 	int newptk; /* gate key reinstallation */
1165*1bbb2cb2SDavid du Colombier 	int rsnelen;
1166*1bbb2cb2SDavid du Colombier 	int n, try;
1167*1bbb2cb2SDavid du Colombier 
1168*1bbb2cb2SDavid du Colombier 	quotefmtinstall();
1169*1bbb2cb2SDavid du Colombier 	fmtinstall('H', encodefmt);
1170*1bbb2cb2SDavid du Colombier 	fmtinstall('E', eipfmt);
1171*1bbb2cb2SDavid du Colombier 
1172*1bbb2cb2SDavid du Colombier 	rsne = nil;
1173*1bbb2cb2SDavid du Colombier 	rsnelen = -1;
1174*1bbb2cb2SDavid du Colombier 	peercipher = nil;
1175*1bbb2cb2SDavid du Colombier 	groupcipher = nil;
1176*1bbb2cb2SDavid du Colombier 
1177*1bbb2cb2SDavid du Colombier 	ARGBEGIN {
1178*1bbb2cb2SDavid du Colombier 	case 'd':
1179*1bbb2cb2SDavid du Colombier 		debug = 1;
1180*1bbb2cb2SDavid du Colombier 		break;
1181*1bbb2cb2SDavid du Colombier 	case 'p':
1182*1bbb2cb2SDavid du Colombier 		prompt = 1;
1183*1bbb2cb2SDavid du Colombier 		break;
1184*1bbb2cb2SDavid du Colombier 	case 's':
1185*1bbb2cb2SDavid du Colombier 		strncpy(essid, EARGF(usage()), 32);
1186*1bbb2cb2SDavid du Colombier 		break;
1187*1bbb2cb2SDavid du Colombier 	case '1':
1188*1bbb2cb2SDavid du Colombier 		rsne = wpaie;
1189*1bbb2cb2SDavid du Colombier 		rsnelen = sizeof(wpaie);
1190*1bbb2cb2SDavid du Colombier 		peercipher = &tkip;
1191*1bbb2cb2SDavid du Colombier 		groupcipher = &tkip;
1192*1bbb2cb2SDavid du Colombier 		break;
1193*1bbb2cb2SDavid du Colombier 	case '2':
1194*1bbb2cb2SDavid du Colombier 		rsne = rsnie;
1195*1bbb2cb2SDavid du Colombier 		rsnelen = sizeof(rsnie);
1196*1bbb2cb2SDavid du Colombier 		peercipher = &ccmp;
1197*1bbb2cb2SDavid du Colombier 		groupcipher = &ccmp;
1198*1bbb2cb2SDavid du Colombier 		break;
1199*1bbb2cb2SDavid du Colombier 	default:
1200*1bbb2cb2SDavid du Colombier 		usage();
1201*1bbb2cb2SDavid du Colombier 	} ARGEND;
1202*1bbb2cb2SDavid du Colombier 
1203*1bbb2cb2SDavid du Colombier 	if(*argv != nil)
1204*1bbb2cb2SDavid du Colombier 		dev = *argv++;
1205*1bbb2cb2SDavid du Colombier 
1206*1bbb2cb2SDavid du Colombier 	if(*argv != nil || dev == nil)
1207*1bbb2cb2SDavid du Colombier 		usage();
1208*1bbb2cb2SDavid du Colombier 
1209*1bbb2cb2SDavid du Colombier 	if(myetheraddr(mac, dev) < 0)
1210*1bbb2cb2SDavid du Colombier 		sysfatal("can't get mac address: %r");
1211*1bbb2cb2SDavid du Colombier 
1212*1bbb2cb2SDavid du Colombier 	snprint(addr, sizeof(addr), "%s!0x888e", dev);
1213*1bbb2cb2SDavid du Colombier 	if((fd = dial(addr, nil, devdir, &cfd)) < 0)
1214*1bbb2cb2SDavid du Colombier 		sysfatal("dial: %r");
1215*1bbb2cb2SDavid du Colombier 
1216*1bbb2cb2SDavid du Colombier 	if(essid[0] != 0){
1217*1bbb2cb2SDavid du Colombier 		if(fprint(cfd, "essid %q", essid) < 0)
1218*1bbb2cb2SDavid du Colombier 			sysfatal("write essid: %r");
1219*1bbb2cb2SDavid du Colombier 	} else if(prompt) {
1220*1bbb2cb2SDavid du Colombier 		getessid();
1221*1bbb2cb2SDavid du Colombier 		if(essid[0] == 0)
1222*1bbb2cb2SDavid du Colombier 			sysfatal("no essid set");
1223*1bbb2cb2SDavid du Colombier 	}
1224*1bbb2cb2SDavid du Colombier 	if(!prompt)
1225*1bbb2cb2SDavid du Colombier 		background();
1226*1bbb2cb2SDavid du Colombier 
1227*1bbb2cb2SDavid du Colombier Connect:
1228*1bbb2cb2SDavid du Colombier  	/* bss scan might not be complete yet, so check for 10 seconds.	*/
1229*1bbb2cb2SDavid du Colombier 	for(try = 100; (forked || try >= 0) && !connected(); try--)
1230*1bbb2cb2SDavid du Colombier 		sleep(100);
1231*1bbb2cb2SDavid du Colombier 
1232*1bbb2cb2SDavid du Colombier 	authtype = AuthPSK;
1233*1bbb2cb2SDavid du Colombier 	if(rsnelen <= 0 || rsne == brsne){
1234*1bbb2cb2SDavid du Colombier 		rsne = brsne;
1235*1bbb2cb2SDavid du Colombier 		rsnelen = buildrsne(rsne);
1236*1bbb2cb2SDavid du Colombier 	}
1237*1bbb2cb2SDavid du Colombier 	if(rsnelen > 0){
1238*1bbb2cb2SDavid du Colombier 		if(debug)
1239*1bbb2cb2SDavid du Colombier 			fprint(2, "rsne: %.*H\n", rsnelen, rsne);
1240*1bbb2cb2SDavid du Colombier 		/*
1241*1bbb2cb2SDavid du Colombier 		 * we use write() instead of fprint so the message gets written
1242*1bbb2cb2SDavid du Colombier 		 * at once and not chunked up on fprint buffer.
1243*1bbb2cb2SDavid du Colombier 		 */
1244*1bbb2cb2SDavid du Colombier 		n = sprint((char*)buf, "auth %.*H", rsnelen, rsne);
1245*1bbb2cb2SDavid du Colombier 		if(write(cfd, buf, n) != n)
1246*1bbb2cb2SDavid du Colombier 			sysfatal("write auth: %r");
1247*1bbb2cb2SDavid du Colombier 	} else {
1248*1bbb2cb2SDavid du Colombier 		authtype = AuthNone;
1249*1bbb2cb2SDavid du Colombier 	}
1250*1bbb2cb2SDavid du Colombier 
1251*1bbb2cb2SDavid du Colombier 	conn.fd = fd;
1252*1bbb2cb2SDavid du Colombier 	conn.write = eapwrite;
1253*1bbb2cb2SDavid du Colombier 	conn.type = 1;	/* Start */
1254*1bbb2cb2SDavid du Colombier 	conn.version = 1;
1255*1bbb2cb2SDavid du Colombier 	memmove(conn.smac, mac, Eaddrlen);
1256*1bbb2cb2SDavid du Colombier 	getbssid(conn.amac);
1257*1bbb2cb2SDavid du Colombier 
1258*1bbb2cb2SDavid du Colombier 	if(prompt){
1259*1bbb2cb2SDavid du Colombier 		UserPasswd *up;
1260*1bbb2cb2SDavid du Colombier 		prompt = 0;
1261*1bbb2cb2SDavid du Colombier 		switch(authtype){
1262*1bbb2cb2SDavid du Colombier 		case AuthNone:
1263*1bbb2cb2SDavid du Colombier 			print("no authentication required\n");
1264*1bbb2cb2SDavid du Colombier 			break;
1265*1bbb2cb2SDavid du Colombier 		case AuthPSK:
1266*1bbb2cb2SDavid du Colombier 			/* dummy to for factotum keyprompt */
1267*1bbb2cb2SDavid du Colombier 			genrandom(anonce, sizeof(anonce));
1268*1bbb2cb2SDavid du Colombier 			genrandom(snonce, sizeof(snonce));
1269*1bbb2cb2SDavid du Colombier 			getptk(auth_getkey, conn.smac, conn.amac, snonce, anonce, ptk);
1270*1bbb2cb2SDavid du Colombier 			break;
1271*1bbb2cb2SDavid du Colombier 		case AuthWPA:
1272*1bbb2cb2SDavid du Colombier 			up = auth_getuserpasswd(auth_getkey, "proto=pass service=wpa essid=%q", essid);
1273*1bbb2cb2SDavid du Colombier 			if(up != nil){
1274*1bbb2cb2SDavid du Colombier 				factotumctl("key proto=mschapv2 role=client service=wpa"
1275*1bbb2cb2SDavid du Colombier 					" essid=%q user=%q !password=%q\n",
1276*1bbb2cb2SDavid du Colombier 					essid, up->user, up->passwd);
1277*1bbb2cb2SDavid du Colombier 				freeup(up);
1278*1bbb2cb2SDavid du Colombier 			}
1279*1bbb2cb2SDavid du Colombier 			break;
1280*1bbb2cb2SDavid du Colombier 		}
1281*1bbb2cb2SDavid du Colombier 		background();
1282*1bbb2cb2SDavid du Colombier 	}
1283*1bbb2cb2SDavid du Colombier 
1284*1bbb2cb2SDavid du Colombier 	genrandom(ptk, sizeof(ptk));
1285*1bbb2cb2SDavid du Colombier 	newptk = 0;
1286*1bbb2cb2SDavid du Colombier 
1287*1bbb2cb2SDavid du Colombier 	lastrepc = 0ULL;
1288*1bbb2cb2SDavid du Colombier 	for(;;){
1289*1bbb2cb2SDavid du Colombier 		uchar *p, *e, *m;
1290*1bbb2cb2SDavid du Colombier 		int proto, flags, vers, datalen;
1291*1bbb2cb2SDavid du Colombier 		uvlong repc, rsc, tsc;
1292*1bbb2cb2SDavid du Colombier 		Keydescr *kd;
1293*1bbb2cb2SDavid du Colombier 
1294*1bbb2cb2SDavid du Colombier 		if((n = read(fd, buf, sizeof(buf))) < 0)
1295*1bbb2cb2SDavid du Colombier 			sysfatal("read: %r");
1296*1bbb2cb2SDavid du Colombier 
1297*1bbb2cb2SDavid du Colombier 		if(n == 0){
1298*1bbb2cb2SDavid du Colombier 			if(debug)
1299*1bbb2cb2SDavid du Colombier 				fprint(2, "got deassociation\n");
1300*1bbb2cb2SDavid du Colombier 			eapreset(&conn);
1301*1bbb2cb2SDavid du Colombier 			goto Connect;
1302*1bbb2cb2SDavid du Colombier 		}
1303*1bbb2cb2SDavid du Colombier 
1304*1bbb2cb2SDavid du Colombier 		p = buf;
1305*1bbb2cb2SDavid du Colombier 		e = buf+n;
1306*1bbb2cb2SDavid du Colombier 		if(n < 2*Eaddrlen + 2)
1307*1bbb2cb2SDavid du Colombier 			continue;
1308*1bbb2cb2SDavid du Colombier 
1309*1bbb2cb2SDavid du Colombier 		memmove(conn.smac, p, Eaddrlen); p += Eaddrlen;
1310*1bbb2cb2SDavid du Colombier 		memmove(conn.amac, p, Eaddrlen); p += Eaddrlen;
1311*1bbb2cb2SDavid du Colombier 		proto = p[0]<<8 | p[1]; p += 2;
1312*1bbb2cb2SDavid du Colombier 
1313*1bbb2cb2SDavid du Colombier 		if(proto != 0x888e || memcmp(conn.smac, mac, Eaddrlen) != 0)
1314*1bbb2cb2SDavid du Colombier 			continue;
1315*1bbb2cb2SDavid du Colombier 
1316*1bbb2cb2SDavid du Colombier 		m = p;
1317*1bbb2cb2SDavid du Colombier 		n = e - p;
1318*1bbb2cb2SDavid du Colombier 		if(n < 4)
1319*1bbb2cb2SDavid du Colombier 			continue;
1320*1bbb2cb2SDavid du Colombier 
1321*1bbb2cb2SDavid du Colombier 		conn.version = p[0];
1322*1bbb2cb2SDavid du Colombier 		if(conn.version != 0x01 && conn.version != 0x02)
1323*1bbb2cb2SDavid du Colombier 			continue;
1324*1bbb2cb2SDavid du Colombier 		conn.type = p[1];
1325*1bbb2cb2SDavid du Colombier 		n = p[2]<<8 | p[3];
1326*1bbb2cb2SDavid du Colombier 		p += 4;
1327*1bbb2cb2SDavid du Colombier 		if(p+n > e)
1328*1bbb2cb2SDavid du Colombier 			continue;
1329*1bbb2cb2SDavid du Colombier 		e = p + n;
1330*1bbb2cb2SDavid du Colombier 
1331*1bbb2cb2SDavid du Colombier 		if(debug)
1332*1bbb2cb2SDavid du Colombier 			fprint(2, "\nrecv(v%d,t%d) %E <- %E: ", conn.version, conn.type, conn.smac, conn.amac);
1333*1bbb2cb2SDavid du Colombier 
1334*1bbb2cb2SDavid du Colombier 		if(authtype == AuthNone)
1335*1bbb2cb2SDavid du Colombier 			continue;
1336*1bbb2cb2SDavid du Colombier 
1337*1bbb2cb2SDavid du Colombier 		if(conn.type == 0x00 && authtype == AuthWPA){
1338*1bbb2cb2SDavid du Colombier 			uchar code, id;
1339*1bbb2cb2SDavid du Colombier 
1340*1bbb2cb2SDavid du Colombier 			if(n < 4)
1341*1bbb2cb2SDavid du Colombier 				continue;
1342*1bbb2cb2SDavid du Colombier 			code = p[0];
1343*1bbb2cb2SDavid du Colombier 			id = p[1];
1344*1bbb2cb2SDavid du Colombier 			n = p[3] | p[2]<<8;
1345*1bbb2cb2SDavid du Colombier 			if(n < 4 || p + n > e)
1346*1bbb2cb2SDavid du Colombier 				continue;
1347*1bbb2cb2SDavid du Colombier 			p += 4, n -= 4;
1348*1bbb2cb2SDavid du Colombier 			eapreq(&conn, code, id, p, n);
1349*1bbb2cb2SDavid du Colombier 			continue;
1350*1bbb2cb2SDavid du Colombier 		}
1351*1bbb2cb2SDavid du Colombier 
1352*1bbb2cb2SDavid du Colombier 		if(conn.type != 0x03)
1353*1bbb2cb2SDavid du Colombier 			continue;
1354*1bbb2cb2SDavid du Colombier 
1355*1bbb2cb2SDavid du Colombier 		if(n < Keydescrlen){
1356*1bbb2cb2SDavid du Colombier 			if(debug)
1357*1bbb2cb2SDavid du Colombier 				fprint(2, "bad kd size\n");
1358*1bbb2cb2SDavid du Colombier 			continue;
1359*1bbb2cb2SDavid du Colombier 		}
1360*1bbb2cb2SDavid du Colombier 		kd = (Keydescr*)p;
1361*1bbb2cb2SDavid du Colombier 		if(debug)
1362*1bbb2cb2SDavid du Colombier 			dumpkeydescr(kd);
1363*1bbb2cb2SDavid du Colombier 
1364*1bbb2cb2SDavid du Colombier 		if(kd->type[0] != 0xFE && kd->type[0] != 0x02)
1365*1bbb2cb2SDavid du Colombier 			continue;
1366*1bbb2cb2SDavid du Colombier 
1367*1bbb2cb2SDavid du Colombier 		vers = kd->flags[1] & 7;
1368*1bbb2cb2SDavid du Colombier 		flags = kd->flags[0]<<8 | kd->flags[1];
1369*1bbb2cb2SDavid du Colombier 		datalen = kd->datalen[0]<<8 | kd->datalen[1];
1370*1bbb2cb2SDavid du Colombier 		if(kd->data + datalen > e)
1371*1bbb2cb2SDavid du Colombier 			continue;
1372*1bbb2cb2SDavid du Colombier 
1373*1bbb2cb2SDavid du Colombier 		if((flags & Fmic) == 0){
1374*1bbb2cb2SDavid du Colombier 			if((flags & (Fptk|Fack)) != (Fptk|Fack))
1375*1bbb2cb2SDavid du Colombier 				continue;
1376*1bbb2cb2SDavid du Colombier 
1377*1bbb2cb2SDavid du Colombier 			memmove(anonce, kd->nonce, sizeof(anonce));
1378*1bbb2cb2SDavid du Colombier 			genrandom(snonce, sizeof(snonce));
1379*1bbb2cb2SDavid du Colombier 			if(getptk(nil, conn.smac, conn.amac, snonce, anonce, ptk) != 0){
1380*1bbb2cb2SDavid du Colombier 				if(debug)
1381*1bbb2cb2SDavid du Colombier 					fprint(2, "getptk: %r\n");
1382*1bbb2cb2SDavid du Colombier 				continue;
1383*1bbb2cb2SDavid du Colombier 			}
1384*1bbb2cb2SDavid du Colombier 			/* allow installation of new keys */
1385*1bbb2cb2SDavid du Colombier 			newptk = 1;
1386*1bbb2cb2SDavid du Colombier 
1387*1bbb2cb2SDavid du Colombier 			/* ack key exchange with mic */
1388*1bbb2cb2SDavid du Colombier 			memset(kd->rsc, 0, sizeof(kd->rsc));
1389*1bbb2cb2SDavid du Colombier 			memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
1390*1bbb2cb2SDavid du Colombier 			memmove(kd->nonce, snonce, sizeof(kd->nonce));
1391*1bbb2cb2SDavid du Colombier 			replykey(&conn, (flags & ~(Fack|Fins)) | Fmic, kd, rsne, rsnelen);
1392*1bbb2cb2SDavid du Colombier 		} else {
1393*1bbb2cb2SDavid du Colombier 			uchar gtk[GTKlen];
1394*1bbb2cb2SDavid du Colombier 			int gtklen, gtkkid;
1395*1bbb2cb2SDavid du Colombier 
1396*1bbb2cb2SDavid du Colombier 			if(checkmic(kd, m, e - m) != 0){
1397*1bbb2cb2SDavid du Colombier 				if(debug)
1398*1bbb2cb2SDavid du Colombier 					fprint(2, "bad mic\n");
1399*1bbb2cb2SDavid du Colombier 				continue;
1400*1bbb2cb2SDavid du Colombier 			}
1401*1bbb2cb2SDavid du Colombier 
1402*1bbb2cb2SDavid du Colombier 			repc =	(uvlong)kd->repc[7] |
1403*1bbb2cb2SDavid du Colombier 				(uvlong)kd->repc[6]<<8 |
1404*1bbb2cb2SDavid du Colombier 				(uvlong)kd->repc[5]<<16 |
1405*1bbb2cb2SDavid du Colombier 				(uvlong)kd->repc[4]<<24 |
1406*1bbb2cb2SDavid du Colombier 				(uvlong)kd->repc[3]<<32 |
1407*1bbb2cb2SDavid du Colombier 				(uvlong)kd->repc[2]<<40 |
1408*1bbb2cb2SDavid du Colombier 				(uvlong)kd->repc[1]<<48 |
1409*1bbb2cb2SDavid du Colombier 				(uvlong)kd->repc[0]<<56;
1410*1bbb2cb2SDavid du Colombier 			if(repc <= lastrepc){
1411*1bbb2cb2SDavid du Colombier 				if(debug)
1412*1bbb2cb2SDavid du Colombier 					fprint(2, "bad repc: %llux <= %llux\n", repc, lastrepc);
1413*1bbb2cb2SDavid du Colombier 				continue;
1414*1bbb2cb2SDavid du Colombier 			}
1415*1bbb2cb2SDavid du Colombier 			lastrepc = repc;
1416*1bbb2cb2SDavid du Colombier 
1417*1bbb2cb2SDavid du Colombier 			rsc =	(uvlong)kd->rsc[0] |
1418*1bbb2cb2SDavid du Colombier 				(uvlong)kd->rsc[1]<<8 |
1419*1bbb2cb2SDavid du Colombier 				(uvlong)kd->rsc[2]<<16 |
1420*1bbb2cb2SDavid du Colombier 				(uvlong)kd->rsc[3]<<24 |
1421*1bbb2cb2SDavid du Colombier 				(uvlong)kd->rsc[4]<<32 |
1422*1bbb2cb2SDavid du Colombier 				(uvlong)kd->rsc[5]<<40;
1423*1bbb2cb2SDavid du Colombier 
1424*1bbb2cb2SDavid du Colombier 			if(datalen > 0 && (flags & Fenc) != 0){
1425*1bbb2cb2SDavid du Colombier 				if(vers == 1)
1426*1bbb2cb2SDavid du Colombier 					datalen = rc4unwrap(ptk+16, kd->eapoliv, kd->data, datalen);
1427*1bbb2cb2SDavid du Colombier 				else
1428*1bbb2cb2SDavid du Colombier 					datalen = aesunwrap(ptk+16, 16, kd->data, datalen);
1429*1bbb2cb2SDavid du Colombier 				if(datalen <= 0){
1430*1bbb2cb2SDavid du Colombier 					if(debug)
1431*1bbb2cb2SDavid du Colombier 						fprint(2, "bad keywrap\n");
1432*1bbb2cb2SDavid du Colombier 					continue;
1433*1bbb2cb2SDavid du Colombier 				}
1434*1bbb2cb2SDavid du Colombier 				if(debug)
1435*1bbb2cb2SDavid du Colombier 					fprint(2, "unwraped keydata[%.4x]=%.*H\n", datalen, datalen, kd->data);
1436*1bbb2cb2SDavid du Colombier 			}
1437*1bbb2cb2SDavid du Colombier 
1438*1bbb2cb2SDavid du Colombier 			gtklen = 0;
1439*1bbb2cb2SDavid du Colombier 			gtkkid = -1;
1440*1bbb2cb2SDavid du Colombier 
1441*1bbb2cb2SDavid du Colombier 			if(kd->type[0] != 0xFE || (flags & (Fptk|Fack)) == (Fptk|Fack)){
1442*1bbb2cb2SDavid du Colombier 				uchar *p, *x, *e;
1443*1bbb2cb2SDavid du Colombier 
1444*1bbb2cb2SDavid du Colombier 				p = kd->data;
1445*1bbb2cb2SDavid du Colombier 				e = p + datalen;
1446*1bbb2cb2SDavid du Colombier 				for(; p+2 <= e; p = x){
1447*1bbb2cb2SDavid du Colombier 					if((x = p+2+p[1]) > e)
1448*1bbb2cb2SDavid du Colombier 						break;
1449*1bbb2cb2SDavid du Colombier 					if(debug)
1450*1bbb2cb2SDavid du Colombier 						fprint(2, "ie=%.2x data[%.2x]=%.*H\n", p[0], p[1], p[1], p+2);
1451*1bbb2cb2SDavid du Colombier 					if(p[0] == 0x30){ /* RSN */
1452*1bbb2cb2SDavid du Colombier 					}
1453*1bbb2cb2SDavid du Colombier 					if(p[0] == 0xDD){ /* WPA */
1454*1bbb2cb2SDavid du Colombier 						static uchar oui[] = { 0x00, 0x0f, 0xac, 0x01, };
1455*1bbb2cb2SDavid du Colombier 
1456*1bbb2cb2SDavid du Colombier 						if(p+2+sizeof(oui) > x || memcmp(p+2, oui, sizeof(oui)) != 0)
1457*1bbb2cb2SDavid du Colombier 							continue;
1458*1bbb2cb2SDavid du Colombier 						if((flags & Fenc) == 0)
1459*1bbb2cb2SDavid du Colombier 							continue;	/* ignore gorup key if unencrypted */
1460*1bbb2cb2SDavid du Colombier 						gtklen = x - (p + 8);
1461*1bbb2cb2SDavid du Colombier 						if(gtklen <= 0)
1462*1bbb2cb2SDavid du Colombier 							continue;
1463*1bbb2cb2SDavid du Colombier 						if(gtklen > sizeof(gtk))
1464*1bbb2cb2SDavid du Colombier 							gtklen = sizeof(gtk);
1465*1bbb2cb2SDavid du Colombier 						memmove(gtk, p + 8, gtklen);
1466*1bbb2cb2SDavid du Colombier 						gtkkid = p[6] & 3;
1467*1bbb2cb2SDavid du Colombier 					}
1468*1bbb2cb2SDavid du Colombier 				}
1469*1bbb2cb2SDavid du Colombier 			}
1470*1bbb2cb2SDavid du Colombier 
1471*1bbb2cb2SDavid du Colombier 			if((flags & (Fptk|Fack)) == (Fptk|Fack)){
1472*1bbb2cb2SDavid du Colombier 				if(!newptk)	/* a retransmit, already installed PTK */
1473*1bbb2cb2SDavid du Colombier 					continue;
1474*1bbb2cb2SDavid du Colombier 				if(vers != 1)	/* in WPA2, RSC is for group key only */
1475*1bbb2cb2SDavid du Colombier 					tsc = 0LL;
1476*1bbb2cb2SDavid du Colombier 				else {
1477*1bbb2cb2SDavid du Colombier 					tsc = rsc;
1478*1bbb2cb2SDavid du Colombier 					rsc = 0LL;
1479*1bbb2cb2SDavid du Colombier 				}
1480*1bbb2cb2SDavid du Colombier 				/* install pairwise receive key (PTK) */
1481*1bbb2cb2SDavid du Colombier 				if(fprint(cfd, "rxkey %E %s:%.*H@%llux", conn.amac,
1482*1bbb2cb2SDavid du Colombier 					peercipher->name, peercipher->keylen, ptk+32, tsc) < 0)
1483*1bbb2cb2SDavid du Colombier 					sysfatal("write rxkey: %r");
1484*1bbb2cb2SDavid du Colombier 
1485*1bbb2cb2SDavid du Colombier 				memset(kd->rsc, 0, sizeof(kd->rsc));
1486*1bbb2cb2SDavid du Colombier 				memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
1487*1bbb2cb2SDavid du Colombier 				memset(kd->nonce, 0, sizeof(kd->nonce));
1488*1bbb2cb2SDavid du Colombier 				replykey(&conn, flags & ~(Fack|Fenc|Fins), kd, nil, 0);
1489*1bbb2cb2SDavid du Colombier 				sleep(100);
1490*1bbb2cb2SDavid du Colombier 
1491*1bbb2cb2SDavid du Colombier 				tsc = 0LL;
1492*1bbb2cb2SDavid du Colombier 				/* install pairwise transmit key (PTK) */
1493*1bbb2cb2SDavid du Colombier 				if(fprint(cfd, "txkey %E %s:%.*H@%llux", conn.amac,
1494*1bbb2cb2SDavid du Colombier 					peercipher->name, peercipher->keylen, ptk+32, tsc) < 0)
1495*1bbb2cb2SDavid du Colombier 					sysfatal("write txkey: %r");
1496*1bbb2cb2SDavid du Colombier 				newptk = 0; /* prevent PTK re-installation on (replayed) retransmits */
1497*1bbb2cb2SDavid du Colombier 			} else
1498*1bbb2cb2SDavid du Colombier 			if((flags & (Fptk|Fsec|Fack)) == (Fsec|Fack)){
1499*1bbb2cb2SDavid du Colombier 				if(kd->type[0] == 0xFE){
1500*1bbb2cb2SDavid du Colombier 					/* WPA always RC4 encrypts the GTK, even tho the flag isnt set */
1501*1bbb2cb2SDavid du Colombier 					if((flags & Fenc) == 0)
1502*1bbb2cb2SDavid du Colombier 						datalen = rc4unwrap(ptk+16, kd->eapoliv, kd->data, datalen);
1503*1bbb2cb2SDavid du Colombier 					gtklen = datalen;
1504*1bbb2cb2SDavid du Colombier 					if(gtklen > sizeof(gtk))
1505*1bbb2cb2SDavid du Colombier 						gtklen = sizeof(gtk);
1506*1bbb2cb2SDavid du Colombier 					memmove(gtk, kd->data, gtklen);
1507*1bbb2cb2SDavid du Colombier 					gtkkid = (flags >> 4) & 3;
1508*1bbb2cb2SDavid du Colombier 				}
1509*1bbb2cb2SDavid du Colombier 				memset(kd->rsc, 0, sizeof(kd->rsc));
1510*1bbb2cb2SDavid du Colombier 				memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
1511*1bbb2cb2SDavid du Colombier 				memset(kd->nonce, 0, sizeof(kd->nonce));
1512*1bbb2cb2SDavid du Colombier 				replykey(&conn, flags & ~(Fenc|Fack), kd, nil, 0);
1513*1bbb2cb2SDavid du Colombier 			} else
1514*1bbb2cb2SDavid du Colombier 				continue;
1515*1bbb2cb2SDavid du Colombier 			/* install group key (GTK) */
1516*1bbb2cb2SDavid du Colombier 			if(gtklen >= groupcipher->keylen && gtkkid != -1)
1517*1bbb2cb2SDavid du Colombier 				if(fprint(cfd, "rxkey%d %E %s:%.*H@%llux",
1518*1bbb2cb2SDavid du Colombier 					gtkkid, conn.amac,
1519*1bbb2cb2SDavid du Colombier 					groupcipher->name, groupcipher->keylen, gtk, rsc) < 0)
1520*1bbb2cb2SDavid du Colombier 					sysfatal("write rxkey%d: %r", gtkkid);
1521*1bbb2cb2SDavid du Colombier 		}
1522*1bbb2cb2SDavid du Colombier 	}
1523*1bbb2cb2SDavid du Colombier }
1524