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