]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/wpa.c
aux/wpa: check reply counter only after mic check
[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         PTKlen = 512/8,
10         GTKlen = 256/8,
11         Noncelen = 32,
12         Eaddrlen = 6,
13 };
14
15 enum {
16         Fptk    = 1<<3,
17         Fins    = 1<<6,
18         Fack    = 1<<7,
19         Fmic    = 1<<8,
20         Fsec    = 1<<9,
21         Ferr    = 1<<10,
22         Freq    = 1<<11,
23         Fenc    = 1<<12,
24
25         Keydescrlen = 1+2+2+8+32+16+8+8+16+2,
26 };
27
28 typedef struct Keydescr Keydescr;
29 struct Keydescr
30 {
31         uchar   type[1];
32         uchar   flags[2];
33         uchar   keylen[2];
34         uchar   repc[8];
35         uchar   nonce[32];
36         uchar   eapoliv[16];
37         uchar   rsc[8];
38         uchar   id[8];
39         uchar   mic[16];
40         uchar   datalen[2];
41         uchar   data[];
42 };
43
44 int     prompt;
45 int     debug;
46 int     fd, cfd;
47 char    *dev;
48 char    devdir[40];
49 uchar   ptk[PTKlen];
50 char    essid[32+1];
51 uvlong  lastrepc;
52
53 uchar   rsnie[] = {
54         0x30,                   /* RSN */
55         0x14,                   /* length */
56         0x01, 0x00,             /* version 1 */
57         0x00, 0x0F, 0xAC, 0x02, /* group cipher suite TKIP */
58         0x01, 0x00,             /* peerwise cipher suite count 1 */
59         0x00, 0x0F, 0xAC, 0x02, /* peerwise cipher suite TKIP */
60         0x01, 0x00,             /* authentication suite count 1 */
61         0x00, 0x0F, 0xAC, 0x02, /* authentication suite PSK */
62         0x00, 0x00,             /* capabilities */
63 };
64
65 uchar   wpaie[] = {
66         0xdd,                   /* vendor specific */
67         0x16,                   /* length */
68         0x00, 0x50, 0xf2, 0x01, /* WPAIE type 1 */
69         0x01, 0x00,             /* version 1 */
70         0x00, 0x50, 0xf2, 0x02, /* group cipher suite TKIP */
71         0x01, 0x00,             /* peerwise cipher suite count 1 */
72         0x00, 0x50, 0xf2, 0x02, /* peerwise cipher suite TKIP */
73         0x01, 0x00,             /* authentication suite count 1 */
74         0x00, 0x50, 0xf2, 0x02, /* authentication suite PSK */
75 };
76
77 /* only WPA for now */
78 uchar   *rsne = wpaie;
79 int     rsnelen = sizeof(wpaie);
80
81 char*
82 getessid(void)
83 {
84         char buf[8*1024], *f[2], *p, *e;
85         int fd, n;
86
87         snprint(buf, sizeof(buf), "%s/ifstats", devdir);
88         if((fd = open(buf, OREAD)) < 0)
89                 return nil;
90         n = read(fd, buf, sizeof(buf)-1);
91         close(fd);
92         if(n > 0){
93                 buf[n] = 0;
94                 for(p = buf; (e = strchr(p, '\n')) != nil; p = e){
95                         *e++ = 0;
96                         if(tokenize(p, f, 2) != 2)
97                                 continue;
98                         if(strcmp(f[0], "essid:") != 0)
99                                 continue;
100                         strncpy(essid, f[1], 32);
101                         return essid;
102                 }
103         }
104         return nil;
105 }
106
107 int
108 getptk( uchar smac[Eaddrlen], uchar amac[Eaddrlen], 
109         uchar snonce[Noncelen],  uchar anonce[Noncelen], 
110         uchar ptk[PTKlen])
111 {
112         uchar buf[2*Eaddrlen + 2*Noncelen], *p;
113         AuthRpc *rpc;
114         int afd, ret;
115         char *s;
116
117         ret = -1;
118         s = nil;
119         rpc = nil;
120         if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
121                 goto out;
122         if((rpc = auth_allocrpc(afd)) == nil)
123                 goto out;
124         if((s = getessid()) == nil)
125                 goto out;
126         if((s = smprint("proto=wpapsk role=client essid=%q", s)) == nil)
127                 goto out;
128         if((ret = auth_rpc(rpc, "start", s, strlen(s))) != ARok)
129                 goto out;
130         p = buf;
131         memmove(p, smac, Eaddrlen); p += Eaddrlen;
132         memmove(p, amac, Eaddrlen); p += Eaddrlen;
133         memmove(p, snonce, Noncelen); p += Noncelen;
134         memmove(p, anonce, Noncelen); p += Noncelen;
135         if((ret = auth_rpc(rpc, "write", buf, p - buf)) != ARok)
136                 goto out;
137         if((ret = auth_rpc(rpc, "read", nil, 0)) != ARok)
138                 goto out;
139         if(rpc->narg != PTKlen)
140                 goto out;
141         memmove(ptk, rpc->arg, PTKlen);
142         ret = 0;
143 out:
144         free(s);
145         if(afd >= 0) close(afd);
146         if(rpc != nil) auth_freerpc(rpc);
147         return ret;
148 }
149
150 int
151 Hfmt(Fmt *f)
152 {
153         uchar *p, *e;
154
155         p = va_arg(f->args, uchar*);
156         e = p;
157         if(f->prec >= 0)
158                 e += f->prec;
159         for(; p != e; p++)
160                 if(fmtprint(f, "%.2x", *p) < 0)
161                         return -1;
162         return 0;
163 }
164
165 void
166 dumpkeydescr(Keydescr *kd)
167 {
168         static struct {
169                 int     flag;
170                 char    *name;
171         } flags[] = {
172                 Fptk,   "ptk",
173                 Fins,   "ins",
174                 Fack,   "ack",
175                 Fmic,   "mic",
176                 Fsec,   "sec",
177                 Ferr,   "err",
178                 Freq,   "req",
179                 Fenc,   "enc",
180         };
181         int i, f;
182
183         f = kd->flags[0]<<8 | kd->flags[1];
184         fprint(2, "type=%.*H flags=%.*H ( ",
185                 sizeof(kd->type), kd->type,
186                 sizeof(kd->flags), kd->flags);
187         for(i=0; i<nelem(flags); i++)
188                 if(flags[i].flag & f)
189                         fprint(2, "%s ", flags[i].name);
190         fprint(2, ") len=%.*H\nrepc=%.*H nonce=%.*H\neapoliv=%.*H rsc=%.*H id=%.*H mic=%.*H\n",
191                 sizeof(kd->keylen), kd->keylen,
192                 sizeof(kd->repc), kd->repc,
193                 sizeof(kd->nonce), kd->nonce,
194                 sizeof(kd->eapoliv), kd->eapoliv,
195                 sizeof(kd->rsc), kd->rsc,
196                 sizeof(kd->id), kd->id,
197                 sizeof(kd->mic), kd->mic);
198         i = kd->datalen[0]<<8 | kd->datalen[1];
199         fprint(2, "data[%.4x]=%.*H\n\n", i, i, kd->data);
200 }
201
202 void
203 reply(uchar smac[Eaddrlen], uchar amac[Eaddrlen], int flags, Keydescr *kd, uchar *data, int datalen)
204 {
205         uchar buf[4096], mic[MD5dlen], *m, *p = buf;
206
207         memmove(p, amac, Eaddrlen); p += Eaddrlen;
208         memmove(p, smac, Eaddrlen); p += Eaddrlen;
209         *p++ = 0x88;
210         *p++ = 0x8e;
211
212         m = p;
213         *p++ = 0x01;
214         *p++ = 0x03;
215         datalen += Keydescrlen;
216         *p++ = datalen >> 8;
217         *p++ = datalen;
218         datalen -= Keydescrlen;
219
220         memmove(p, kd, Keydescrlen);
221         kd = (Keydescr*)p;
222         kd->flags[0] = flags >> 8;
223         kd->flags[1] = flags;
224         kd->datalen[0] = datalen >> 8;
225         kd->datalen[1] = datalen;
226         p = kd->data;
227         memmove(p, data, datalen);
228         p += datalen;
229
230         memset(kd->mic, 0, sizeof(kd->mic));
231         if(flags & Fmic){
232                 hmac_md5(m, p - m, ptk, 16, mic, nil);
233                 memmove(kd->mic, mic, sizeof(kd->mic));
234         }
235         if(debug != 0){
236                 fprint(2, "reply %E -> %E: ", smac, amac);
237                 dumpkeydescr(kd);
238         }
239         datalen = p - buf;
240         if(write(fd, buf, datalen) != datalen)
241                 sysfatal("write: %r");
242 }
243
244 void
245 usage(void)
246 {
247         fprint(2, "%s: [-dp] [-s essid] dev\n", argv0);
248         exits("usage");
249 }
250
251 void
252 main(int argc, char *argv[])
253 {
254         uchar mac[Eaddrlen], buf[1024];
255         char addr[128];
256         int n;
257
258         quotefmtinstall();
259         fmtinstall('H', Hfmt);
260         fmtinstall('E', eipfmt);
261
262         ARGBEGIN {
263         case 'd':
264                 debug = 1;
265                 break;
266         case 'p':
267                 prompt = 1;
268                 break;
269         case 's':
270                 strncpy(essid, EARGF(usage()), 32);
271                 break;
272         default:
273                 usage();
274         } ARGEND;
275
276         if(*argv != nil)
277                 dev = *argv++;
278
279         if(*argv != nil || dev == nil)
280                 usage();
281
282         if(myetheraddr(mac, dev) < 0)
283                 sysfatal("can't get mac address: %r");
284
285         snprint(addr, sizeof(addr), "%s!0x888e", dev);
286         if((fd = dial(addr, nil, devdir, &cfd)) < 0)
287                 sysfatal("dial: %r");
288
289         if(essid[0] != 0)
290                 if(fprint(cfd, "essid %s", essid) < 0)
291                         sysfatal("write essid: %r");
292
293         if(prompt){
294                 char *s;
295
296                 if(essid[0] == 0)
297                         getessid();
298                 if(essid[0] != 0)
299                         s = smprint("proto=wpapsk essid=%q !password?", essid);
300                 else
301                         s = smprint("proto=wpapsk essid? !password?");
302                 auth_getkey(s);
303                 free(s);
304         }
305
306         /*
307          * we use write() instead of fprint so message gets  written
308          * at once and not chunked up on fprint buffer.
309          */
310         n = sprint((char*)buf, "auth %.*H", rsnelen, rsne);
311         if(write(cfd, buf, n) != n)
312                 sysfatal("write auth: %r");
313
314         if(!debug){
315                 switch(rfork(RFFDG|RFREND|RFPROC|RFNOWAIT)){
316                 default:
317                         exits(nil);
318                 case -1:
319                         sysfatal("fork: %r");
320                         return;
321                 case 0:
322                         break;
323                 }
324         }
325         
326         for(;;){
327                 uchar smac[Eaddrlen], amac[Eaddrlen], snonce[Noncelen], anonce[Noncelen], *p, *e, *m;
328                 int proto, flags, kid;
329                 uvlong repc, rsc;
330                 Keydescr *kd;
331
332                 if((n = read(fd, buf, sizeof(buf))) < 0)
333                         sysfatal("read: %r");
334                 p = buf;
335                 e = buf+n;
336                 if(n < 2*Eaddrlen + 2)
337                         continue;
338                 memmove(smac, p, Eaddrlen); p += Eaddrlen;
339                 memmove(amac, p, Eaddrlen); p += Eaddrlen;
340                 proto = p[0]<<8 | p[1]; p += 2;
341                 if(proto != 0x888e || memcmp(smac, mac, Eaddrlen) != 0)
342                         continue;
343
344                 m = p;
345                 n = e - p;
346                 if(n < 4 || p[0] != 0x01 || p[1] != 0x03)
347                         continue;
348                 n = p[2]<<8 | p[3];
349                 p += 4;
350                 if(n < Keydescrlen || p + n > e)
351                         continue;
352                 kd = (Keydescr*)p;
353                 if(debug){
354                         fprint(2, "recv %E <- %E: ", smac, amac);
355                         dumpkeydescr(kd);
356                 }
357
358                 /* only WPA, RSN descriptor format not suppoted yet */
359                 if(kd->type[0] != 0xFE)
360                         continue;
361
362                 flags = kd->flags[0]<<8 | kd->flags[1];
363
364                 rsc =   (uvlong)kd->rsc[0] |
365                         (uvlong)kd->rsc[1]<<8 |
366                         (uvlong)kd->rsc[2]<<16 |
367                         (uvlong)kd->rsc[3]<<24 |
368                         (uvlong)kd->rsc[4]<<32 |
369                         (uvlong)kd->rsc[5]<<40;
370
371                 if((flags & Fmic) == 0){
372                         if((flags & (Fptk|Fack)) != (Fptk|Fack))
373                                 continue;
374
375                         memmove(anonce, kd->nonce, sizeof(anonce));
376                         genrandom(snonce, sizeof(snonce));
377                         if(getptk(smac, amac, snonce, anonce, ptk) < 0)
378                                 continue;
379
380                         /* ack key exchange with mic */
381                         memset(kd->rsc, 0, sizeof(kd->rsc));
382                         memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
383                         memmove(kd->nonce, snonce, sizeof(kd->nonce));
384                         reply(smac, amac, (flags & ~(Fack|Fins)) | Fmic, kd, rsne, rsnelen);
385                 } else {
386                         uchar tmp[MD5dlen], mic[MD5dlen];
387
388                         /* check mic */
389                         memmove(tmp, kd->mic, sizeof(mic));
390                         memset(kd->mic, 0, sizeof(kd->mic));
391                         hmac_md5(m, e - m, ptk, 16, mic, nil);
392                         if(memcmp(tmp, mic, sizeof(mic)) != 0)
393                                 continue;
394
395                         repc =  (uvlong)kd->repc[7] |
396                                 (uvlong)kd->repc[6]<<8 |
397                                 (uvlong)kd->repc[5]<<16 |
398                                 (uvlong)kd->repc[4]<<24 |
399                                 (uvlong)kd->repc[3]<<32 |
400                                 (uvlong)kd->repc[2]<<40 |
401                                 (uvlong)kd->repc[1]<<48 |
402                                 (uvlong)kd->repc[0]<<56;
403                         if(repc <= lastrepc)
404                                 continue;
405                         lastrepc = repc;
406
407                         if((flags & (Fptk|Fsec|Fack)) == (Fptk|Fack)){
408                                 /* install peerwise receive key */
409                                 if(fprint(cfd, "rxkey %.*H tkip:%.*H@%llux", Eaddrlen, amac, 32, ptk+32, rsc) < 0)
410                                         sysfatal("write rxkey: %r");
411
412                                 /* pick random 16bit tsc value for transmit */
413                                 rsc = 1 + (truerand() & 0x7fff);
414                                 memset(kd->rsc, 0, sizeof(kd->rsc));
415                                 kd->rsc[0] = rsc;
416                                 kd->rsc[1] = rsc>>8;
417                                 memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
418                                 memset(kd->nonce, 0, sizeof(kd->nonce));
419                                 reply(smac, amac, flags & ~Fack, kd, nil, 0);
420                                 sleep(100);
421
422                                 /* install peerwise transmit key */ 
423                                 if(fprint(cfd, "txkey %.*H tkip:%.*H@%llux", Eaddrlen, amac, 32, ptk+32, rsc) < 0)
424                                         sysfatal("write txkey: %r");
425                         } else
426                         if((flags & (Fptk|Fsec|Fack)) == (Fsec|Fack)){
427                                 uchar seed[32], gtk[GTKlen];
428                                 RC4state rs;
429                                 int len;
430
431                                 len = kd->datalen[1]<<8 | kd->datalen[0];
432                                 if(len > sizeof(gtk))
433                                         len = sizeof(gtk);
434                                 memmove(gtk, kd->data, len);
435                                 memmove(seed, kd->eapoliv, 16);
436                                 memmove(seed+16, ptk+16, 16);
437                                 setupRC4state(&rs, seed, sizeof(seed));
438                                 rc4skip(&rs, 256);
439                                 rc4(&rs, gtk, len);
440         
441                                 /* install group key */
442                                 kid = (flags >> 4) & 3;
443                                 if(fprint(cfd, "rxkey%d %.*H tkip:%.*H@%llux", kid, Eaddrlen, amac, len, gtk, rsc) < 0)
444                                         sysfatal("write rxkey%d: %r", kid);
445
446                                 memset(kd->rsc, 0, sizeof(kd->rsc));
447                                 memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
448                                 memset(kd->nonce, 0, sizeof(kd->nonce));
449                                 reply(smac, amac, flags & ~Fack, kd, nil, 0);
450                         }
451                 }
452         }
453 }