Command aux/wpa which acts as a supplicant for wireless authentication protocol WPA-PSK (wpa1 or wpa2), and factotum support for the wpapsk challenge-response protocol. Adopted from an earlier 9front implementation. Reference: /n/sources/patch/wpa-psk Date: Mon Nov 7 09:18:45 CET 2016 Signed-off-by: miller@hamnavoe.com --- /sys/src/cmd/auth/factotum/dat.h Sun Nov 6 21:45:01 2016 +++ /sys/src/cmd/auth/factotum/dat.h Sun Nov 6 21:44:59 2016 @@ -236,3 +236,4 @@ extern Proto wep; /* wep.c */ /* extern Proto srs; /* srs.c */ extern Proto httpdigest; /* httpdigest.c */ +extern Proto wpapsk; /* wpapsk.c */ --- /sys/src/cmd/auth/factotum/fs.c Sun Nov 6 21:45:06 2016 +++ /sys/src/cmd/auth/factotum/fs.c Sun Nov 6 21:45:03 2016 @@ -40,6 +40,7 @@ &rsa, &vnc, &wep, + &wpapsk, nil, }; --- /sys/src/cmd/auth/factotum/mkfile Sun Nov 6 21:45:10 2016 +++ /sys/src/cmd/auth/factotum/mkfile Sun Nov 6 21:45:08 2016 @@ -13,6 +13,7 @@ pass.$O\ rsa.$O\ wep.$O\ + wpapsk.$O\ FOFILES=\ $PROTO\ --- /sys/src/cmd/auth/factotum/wpapsk.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/wpapsk.c Sun Nov 6 21:45:12 2016 @@ -0,0 +1,237 @@ +/* + * WPA-PSK + * + * Client protocol: + * write challenge: smac[6] + amac[6] + snonce[32] + anonce[32] + * read response: ptk[64] + * + * Server protocol: + * unimplemented + */ +#include "dat.h" + +enum { + PMKlen = 256/8, + PTKlen = 512/8, + + Eaddrlen = 6, + Noncelen = 32, +}; + +enum +{ + CNeedChal, + CHaveResp, + Maxphase, +}; + +static char *phasenames[Maxphase] = { +[CNeedChal] "CNeedChal", +[CHaveResp] "CHaveResp", +}; + +struct State +{ + uchar resp[PTKlen]; +}; + +static void +pbkdf2(uchar *p, ulong plen, uchar *s, ulong slen, ulong rounds, uchar *d, ulong dlen) +{ + uchar block[SHA1dlen], tmp[SHA1dlen], tmp2[SHA1dlen]; + ulong i, j, k, n; + DigestState *ds; + + for(i = 1; dlen > 0; i++, d += n, dlen -= n){ + tmp[3] = i; + tmp[2] = i >> 8; + tmp[1] = i >> 16; + tmp[0] = i >> 24; + ds = hmac_sha1(s, slen, p, plen, nil, nil); + hmac_sha1(tmp, 4, p, plen, block, ds); + memmove(tmp, block, sizeof(tmp)); + for(j = 1; j < rounds; j++){ + hmac_sha1(tmp, sizeof(tmp), p, plen, tmp2, nil); + memmove(tmp, tmp2, sizeof(tmp)); + for(k=0; k sizeof(block) ? sizeof(block) : dlen; + memmove(d, block, n); + } +} + +static int +hextob(char *s, char **sp, uchar *b, int n) +{ + int r; + + n <<= 1; + for(r = 0; r < n && *s; s++){ + *b <<= 4; + if(*s >= '0' && *s <= '9') + *b |= (*s - '0'); + else if(*s >= 'a' && *s <= 'f') + *b |= 10+(*s - 'a'); + else if(*s >= 'A' && *s <= 'F') + *b |= 10+(*s - 'A'); + else break; + if((++r & 1) == 0) + b++; + } + if(sp != nil) + *sp = s; + return r >> 1; +} + +static void +pass2pmk(char *pass, char *ssid, uchar pmk[PMKlen]) +{ + if(hextob(pass, nil, pmk, PMKlen) == PMKlen) + return; + pbkdf2((uchar*)pass, strlen(pass), (uchar*)ssid, strlen(ssid), 4096, pmk, PMKlen); +} + +static void +prfn(uchar *k, int klen, char *a, uchar *b, int blen, uchar *d, int dlen) +{ + uchar r[SHA1dlen], i; + DigestState *ds; + int n; + + i = 0; + while(dlen > 0){ + ds = hmac_sha1((uchar*)a, strlen(a)+1, k, klen, nil, nil); + hmac_sha1(b, blen, k, klen, nil, ds); + hmac_sha1(&i, 1, k, klen, r, ds); + i++; + n = dlen; + if(n > sizeof(r)) + n = sizeof(r); + memmove(d, r, n); d += n; + dlen -= n; + } +} + +static void +calcptk(uchar pmk[PMKlen], uchar smac[Eaddrlen], uchar amac[Eaddrlen], + uchar snonce[Noncelen], uchar anonce[Noncelen], + uchar ptk[PTKlen]) +{ + uchar b[2*Eaddrlen + 2*Noncelen]; + + if(memcmp(smac, amac, Eaddrlen) > 0){ + memmove(b + Eaddrlen*0, amac, Eaddrlen); + memmove(b + Eaddrlen*1, smac, Eaddrlen); + } else { + memmove(b + Eaddrlen*0, smac, Eaddrlen); + memmove(b + Eaddrlen*1, amac, Eaddrlen); + } + if(memcmp(snonce, anonce, Eaddrlen) > 0){ + memmove(b + Eaddrlen*2 + Noncelen*0, anonce, Noncelen); + memmove(b + Eaddrlen*2 + Noncelen*1, snonce, Noncelen); + } else { + memmove(b + Eaddrlen*2 + Noncelen*0, snonce, Noncelen); + memmove(b + Eaddrlen*2 + Noncelen*1, anonce, Noncelen); + } + prfn(pmk, PMKlen, "Pairwise key expansion", b, sizeof(b), ptk, PTKlen); +} + +static int +wpapskinit(Proto *p, Fsstate *fss) +{ + int iscli; + State *s; + + if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) + return failure(fss, nil); + if(!iscli) + return failure(fss, "%s server not supported", p->name); + + s = emalloc(sizeof *s); + fss->phasename = phasenames; + fss->maxphase = Maxphase; + fss->phase = CNeedChal; + fss->ps = s; + return RpcOk; +} + +static int +wpapskwrite(Fsstate *fss, void *va, uint n) +{ + uchar pmk[PMKlen], *smac, *amac, *snonce, *anonce; + char *pass, *essid; + State *s; + int ret; + Key *k; + Keyinfo ki; + Attr *attr; + + s = fss->ps; + + if(fss->phase != CNeedChal) + return phaseerror(fss, "write"); + if(n != (2*Eaddrlen + 2*Noncelen)) + return phaseerror(fss, "bad write size"); + + attr = _delattr(_copyattr(fss->attr), "role"); + mkkeyinfo(&ki, fss, attr); + ret = findkey(&k, &ki, "%s", fss->proto->keyprompt); + _freeattr(attr); + if(ret != RpcOk) + return ret; + + pass = _strfindattr(k->privattr, "!password"); + if(pass == nil) + return failure(fss, "key has no password"); + essid = _strfindattr(k->attr, "essid"); + if(essid == nil) + return failure(fss, "key has no essid"); + setattrs(fss->attr, k->attr); + closekey(k); + + pass2pmk(pass, essid, pmk); + + smac = va; + amac = smac + Eaddrlen; + snonce = amac + Eaddrlen; + anonce = snonce + Noncelen; + calcptk(pmk, smac, amac, snonce, anonce, s->resp); + + fss->phase = CHaveResp; + return RpcOk; +} + +static int +wpapskread(Fsstate *fss, void *va, uint *n) +{ + State *s; + + s = fss->ps; + if(fss->phase != CHaveResp) + return phaseerror(fss, "read"); + if(*n > sizeof(s->resp)) + *n = sizeof(s->resp); + memmove(va, s->resp, *n); + fss->phase = Established; + fss->haveai = 0; + return RpcOk; +} + +static void +wpapskclose(Fsstate *fss) +{ + State *s; + s = fss->ps; + free(s); +} + +Proto wpapsk = { +.name= "wpapsk", +.init= wpapskinit, +.write= wpapskwrite, +.read= wpapskread, +.close= wpapskclose, +.addkey= replacekey, +.keyprompt= "!password? essid?" +}; --- /sys/src/cmd/aux/wpa.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/aux/wpa.c Sun Nov 6 21:45:14 2016 @@ -0,0 +1,672 @@ +/* + * aux/wpa + * + * adapted from an earlier 9front version + */ + +#include +#include +#include +#include +#include +#include + +enum { + PTKlen = 512/8, + GTKlen = 256/8, + + MIClen = 16, + + Noncelen = 32, + Eaddrlen = 6, +}; + +enum { + Fptk = 1<<3, + Fins = 1<<6, + Fack = 1<<7, + Fmic = 1<<8, + Fsec = 1<<9, + Ferr = 1<<10, + Freq = 1<<11, + Fenc = 1<<12, + + Keydescrlen = 1+2+2+8+32+16+8+8+16+2, +}; + +typedef struct Keydescr Keydescr; +struct Keydescr +{ + uchar type[1]; + uchar flags[2]; + uchar keylen[2]; + uchar repc[8]; + uchar nonce[32]; + uchar eapoliv[16]; + uchar rsc[8]; + uchar id[8]; + uchar mic[16]; + uchar datalen[2]; + uchar data[]; +}; + +typedef struct Cipher Cipher; +struct Cipher +{ + char *name; + int keylen; +}; + +Cipher tkip = { "tkip", 32 }; +Cipher ccmp = { "ccmp", 16 }; + +Cipher *peercipher; +Cipher *groupcipher; +uchar version; + +int prompt; +int debug; +int fd, cfd; +char *dev; +char devdir[40]; +uchar ptk[PTKlen]; +char essid[32+1]; +uvlong lastrepc; +uchar lastrecv[1024]; +int lastrecvlen; +uchar replybuf[4096]; +int replylen; + +uchar rsnie[] = { + 0x30, /* RSN */ + 0x14, /* length */ + 0x01, 0x00, /* version 1 */ + 0x00, 0x0F, 0xAC, 0x04, /* group cipher suite CCMP */ + 0x01, 0x00, /* peerwise cipher suite count 1 */ + 0x00, 0x0F, 0xAC, 0x04, /* peerwise cipher suite CCMP */ + 0x01, 0x00, /* authentication suite count 1 */ + 0x00, 0x0F, 0xAC, 0x02, /* authentication suite PSK */ + 0x00, 0x00, /* capabilities */ +}; + +uchar wpaie[] = { + 0xdd, /* vendor specific */ + 0x16, /* length */ + 0x00, 0x50, 0xf2, 0x01, /* WPAIE type 1 */ + 0x01, 0x00, /* version 1 */ + 0x00, 0x50, 0xf2, 0x02, /* group cipher suite TKIP */ + 0x01, 0x00, /* peerwise cipher suite count 1 */ + 0x00, 0x50, 0xf2, 0x02, /* peerwise cipher suite TKIP */ + 0x01, 0x00, /* authentication suite count 1 */ + 0x00, 0x50, 0xf2, 0x02, /* authentication suite PSK */ +}; + +char* +getessid(void) +{ + char buf[8*1024], *f[2], *p, *e; + int fd, n; + + snprint(buf, sizeof(buf), "%s/ifstats", devdir); + if((fd = open(buf, OREAD)) < 0) + return nil; + n = read(fd, buf, sizeof(buf)-1); + close(fd); + if(n > 0){ + buf[n] = 0; + for(p = buf; (e = strchr(p, '\n')) != nil; p = e){ + *e++ = 0; + if(tokenize(p, f, 2) != 2) + continue; + if(strcmp(f[0], "essid:") != 0) + continue; + strncpy(essid, f[1], 32); + return essid; + } + } + return nil; +} + +int +getptk( uchar smac[Eaddrlen], uchar amac[Eaddrlen], + uchar snonce[Noncelen], uchar anonce[Noncelen], + uchar ptk[PTKlen]) +{ + uchar buf[2*Eaddrlen + 2*Noncelen], *p; + AuthRpc *rpc; + int afd, ret; + char *s; + + ret = -1; + s = nil; + rpc = nil; + if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0) + goto out1; + if((rpc = auth_allocrpc(afd)) == nil) + goto out2; + if((s = getessid()) == nil) + goto out3; + if((s = smprint("proto=wpapsk role=client essid=%q", s)) == nil) + goto out4; + if((ret = auth_rpc(rpc, "start", s, strlen(s))) != ARok) + goto out5; + p = buf; + memmove(p, smac, Eaddrlen); p += Eaddrlen; + memmove(p, amac, Eaddrlen); p += Eaddrlen; + memmove(p, snonce, Noncelen); p += Noncelen; + memmove(p, anonce, Noncelen); p += Noncelen; + if((ret = auth_rpc(rpc, "write", buf, p - buf)) != ARok) + goto out6; + if((ret = auth_rpc(rpc, "read", nil, 0)) != ARok) + goto out7; + if(rpc->narg != PTKlen) + goto out8; + memmove(ptk, rpc->arg, PTKlen); + ret = 0; + goto out; +out8: ret--; +out7: ret--; +out6: ret--; +out5: ret--; +out4: ret--; +out3: ret--; +out2: ret--; +out1: +out: + free(s); + if(afd >= 0) close(afd); + if(rpc != nil) auth_freerpc(rpc); + return ret; +} + +int +Hfmt(Fmt *f) +{ + uchar *p, *e; + + p = va_arg(f->args, uchar*); + e = p; + if(f->prec >= 0) + e += f->prec; + for(; p != e; p++) + if(fmtprint(f, "%.2x", *p) < 0) + return -1; + return 0; +} + +void +dumpkeydescr(Keydescr *kd) +{ + static struct { + int flag; + char *name; + } flags[] = { + Fptk, "ptk", + Fins, "ins", + Fack, "ack", + Fmic, "mic", + Fsec, "sec", + Ferr, "err", + Freq, "req", + Fenc, "enc", + }; + int i, f; + + f = kd->flags[0]<<8 | kd->flags[1]; + fprint(2, "type=%.*H vers=%d flags=%.*H ( ", + sizeof(kd->type), kd->type, kd->flags[1] & 7, + sizeof(kd->flags), kd->flags); + for(i=0; ikeylen), kd->keylen, + sizeof(kd->repc), kd->repc, + sizeof(kd->nonce), kd->nonce, + sizeof(kd->eapoliv), kd->eapoliv, + sizeof(kd->rsc), kd->rsc, + sizeof(kd->id), kd->id, + sizeof(kd->mic), kd->mic); + i = kd->datalen[0]<<8 | kd->datalen[1]; + fprint(2, "data[%.4x]=%.*H\n", i, i, kd->data); +} + +int +rc4unwrap(uchar key[16], uchar iv[16], uchar *data, int len) +{ + uchar seed[32]; + RC4state rs; + + memmove(seed, iv, 16); + memmove(seed+16, key, 16); + setupRC4state(&rs, seed, sizeof(seed)); + rc4skip(&rs, 256); + rc4(&rs, data, len); + return len; +} + +int +aesunwrap(uchar *key, int nkey, uchar *data, int len) +{ + static uchar IV[8] = { 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, }; + uchar B[16], *R; + AESstate s; + uint t; + int n; + + len -= 8; + if(len < 16 || (len % 8) != 0) + return -1; + n = len/8; + t = n*6; + setupAESstate(&s, key, nkey, 0); + memmove(B, data, 8); + memmove(data, data+8, n*8); + do { + for(R = data + (n - 1)*8; R >= data; t--, R -= 8){ + memmove(B+8, R, 8); + B[7] ^= (t >> 0); + B[6] ^= (t >> 8); + B[5] ^= (t >> 16); + B[4] ^= (t >> 24); + aes_decrypt(s.dkey, s.rounds, B, B); + memmove(R, B+8, 8); + } + } while(t > 0); + if(memcmp(B, IV, 8) != 0) + return -1; + return n*8; +} + +int +calcmic(Keydescr *kd, uchar *msg, int msglen) +{ + int vers; + + vers = kd->flags[1] & 7; + memset(kd->mic, 0, MIClen); + if(vers == 1){ + uchar digest[MD5dlen]; + + hmac_md5(msg, msglen, ptk, 16, digest, nil); + memmove(kd->mic, digest, MIClen); + return 0; + } + if(vers == 2){ + uchar digest[SHA1dlen]; + + hmac_sha1(msg, msglen, ptk, 16, digest, nil); + memmove(kd->mic, digest, MIClen); + return 0; + } + return -1; +} + +int +checkmic(Keydescr *kd, uchar *msg, int msglen) +{ + uchar tmp[MIClen]; + + memmove(tmp, kd->mic, MIClen); + if(calcmic(kd, msg, msglen) != 0) + return -1; + return memcmp(tmp, kd->mic, MIClen) != 0; +} + +void +reply(uchar smac[Eaddrlen], uchar amac[Eaddrlen], int flags, Keydescr *kd, uchar *data, int datalen) +{ + uchar *m, *p = replybuf; + + memmove(p, amac, Eaddrlen); p += Eaddrlen; + memmove(p, smac, Eaddrlen); p += Eaddrlen; + *p++ = 0x88; + *p++ = 0x8e; + + m = p; + *p++ = version; + *p++ = 0x03; + datalen += Keydescrlen; + *p++ = datalen >> 8; + *p++ = datalen; + datalen -= Keydescrlen; + + memmove(p, kd, Keydescrlen); + kd = (Keydescr*)p; + kd->flags[0] = flags >> 8; + kd->flags[1] = flags; + kd->datalen[0] = datalen >> 8; + kd->datalen[1] = datalen; + p = kd->data; + memmove(p, data, datalen); + p += datalen; + + memset(kd->mic, 0, MIClen); + if(flags & Fmic) + calcmic(kd, m, p - m); + if(debug > 1){ + fprint(2, "\nreply %E -> %E: ", smac, amac); + dumpkeydescr(kd); + } + replylen = p - replybuf; + if(write(fd, replybuf, replylen) != replylen) + sysfatal("write: %r"); +} + +void +usage(void) +{ + fprint(2, "%s: [-dp12] [-s essid] dev\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + uchar mac[Eaddrlen], buf[1024]; + char addr[128]; + uchar *rsne; + int background, connecting; + int rsnelen; + int n; + + quotefmtinstall(); + fmtinstall('H', Hfmt); + fmtinstall('E', eipfmt); + + /* default is WPA */ + rsne = wpaie; + rsnelen = sizeof(wpaie); + peercipher = &tkip; + groupcipher = &tkip; + + ARGBEGIN { + case 'd': + debug++; + break; + case 'p': + prompt = 1; + break; + case 's': + strncpy(essid, EARGF(usage()), 32); + break; + case '1': + rsne = wpaie; + rsnelen = sizeof(wpaie); + peercipher = &tkip; + groupcipher = &tkip; + break; + case '2': + rsne = rsnie; + rsnelen = sizeof(rsnie); + peercipher = &ccmp; + groupcipher = &ccmp; + break; + default: + usage(); + } ARGEND; + + if(*argv != nil) + dev = *argv++; + + if(*argv != nil || dev == nil) + usage(); + + if(myetheraddr(mac, dev) < 0) + sysfatal("can't get mac address: %r"); + + snprint(addr, sizeof(addr), "%s!0x888e", dev); + if((fd = dial(addr, nil, devdir, &cfd)) < 0) + sysfatal("dial: %r"); + + if(essid[0] != 0) + if(fprint(cfd, "essid %s", essid) < 0) + fprint(2, "write essid: %r"); + + background = 0; + connecting = 0; + for(;;){ + uchar smac[Eaddrlen], amac[Eaddrlen], snonce[Noncelen], anonce[Noncelen], *p, *e, *m; + int proto, flags, vers, datalen; + uvlong repc, rsc, tsc; + Keydescr *kd; + + if(!connecting){ + connecting = 1; + if(!background && prompt){ + char *s; + + if(essid[0] == 0) + getessid(); + if(essid[0] != 0) + s = smprint("proto=wpapsk essid=%q !password?", essid); + else + s = smprint("proto=wpapsk essid? !password?"); + auth_getkey(s); + free(s); + } + /* + * we use write() instead of fprint so message gets written + * at once and not chunked up on fprint buffer. + */ + n = sprint((char*)buf, "auth %.*H", rsnelen, rsne); + if(write(cfd, buf, n) != n) + sysfatal("write auth: %r"); + } + + if((n = read(fd, buf, sizeof(buf))) < 0) + sysfatal("read: %r"); + if(debug > 2) + fprint(2, "?%.*H\n", n, buf); + if(n == 0){ + connecting = 0; + lastrepc = 0; + continue; + } + if(n == lastrecvlen && memcmp(buf, lastrecv, n) == 0){ + if(write(fd, replybuf, replylen) != replylen) + sysfatal("write: %r"); + continue; + } + memcpy(lastrecv, buf, n); + lastrecvlen = n; + p = buf; + e = buf+n; + if(n < 2*Eaddrlen + 2) + continue; + memmove(smac, p, Eaddrlen); p += Eaddrlen; + memmove(amac, p, Eaddrlen); p += Eaddrlen; + proto = p[0]<<8 | p[1]; p += 2; + if(proto != 0x888e || memcmp(smac, mac, Eaddrlen) != 0) + continue; + + m = p; + n = e - p; + if(n < 4 || (p[0] != 0x01 && p[0] != 0x02) || p[1] != 0x03) + continue; + version = p[0]; + n = p[2]<<8 | p[3]; + p += 4; + if(n < Keydescrlen || p + n > e) + continue; + e = p + n; + kd = (Keydescr*)p; + if(debug > 1){ + fprint(2, "\nrecv %E <- %E: ", smac, amac); + dumpkeydescr(kd); + } + + if(kd->type[0] != 0xFE && kd->type[0] != 0x02) + continue; + + vers = kd->flags[1] & 7; + flags = kd->flags[0]<<8 | kd->flags[1]; + datalen = kd->datalen[0]<<8 | kd->datalen[1]; + if(kd->data + datalen > e) + continue; + + if((flags & Fmic) == 0){ + if((flags & (Fptk|Fack)) != (Fptk|Fack)) + continue; + + memmove(anonce, kd->nonce, sizeof(anonce)); + genrandom(snonce, sizeof(snonce)); + if((n = getptk(smac, amac, snonce, anonce, ptk)) != 0){ + if(debug) + fprint(2, "getptk error: %d\n", n); + continue; + } + + /* ack key exchange with mic */ + memset(kd->rsc, 0, sizeof(kd->rsc)); + memset(kd->eapoliv, 0, sizeof(kd->eapoliv)); + memmove(kd->nonce, snonce, sizeof(kd->nonce)); + reply(smac, amac, (flags & ~(Fack|Fins)) | Fmic, kd, rsne, rsnelen); + } else { + uchar gtk[GTKlen]; + int gtklen, gtkkid; + + if(checkmic(kd, m, e - m) != 0){ + if(debug != 0) + fprint(2, "bad mic\n"); + continue; + } + + repc = (uvlong)kd->repc[7] | + (uvlong)kd->repc[6]<<8 | + (uvlong)kd->repc[5]<<16 | + (uvlong)kd->repc[4]<<24 | + (uvlong)kd->repc[3]<<32 | + (uvlong)kd->repc[2]<<40 | + (uvlong)kd->repc[1]<<48 | + (uvlong)kd->repc[0]<<56; + if(repc <= lastrepc){ + if(debug != 0) + fprint(2, "bad repc: %llux <= %llux\n", repc, lastrepc); + continue; + } + lastrepc = repc; + + rsc = (uvlong)kd->rsc[0] | + (uvlong)kd->rsc[1]<<8 | + (uvlong)kd->rsc[2]<<16 | + (uvlong)kd->rsc[3]<<24 | + (uvlong)kd->rsc[4]<<32 | + (uvlong)kd->rsc[5]<<40; + + if(datalen > 0 && (flags & Fenc) != 0){ + if(vers == 1) + datalen = rc4unwrap(ptk+16, kd->eapoliv, kd->data, datalen); + else + datalen = aesunwrap(ptk+16, 16, kd->data, datalen); + if(datalen <= 0){ + if(debug != 0) + fprint(2, "bad keywrap\n"); + continue; + } + if(debug > 1) + fprint(2, "unwrapped keydata[%.4x]=%.*H\n", datalen, datalen, kd->data); + } + + gtklen = 0; + gtkkid = -1; + + if(kd->type[0] != 0xFE || (flags & (Fptk|Fack)) == (Fptk|Fack)){ + uchar *p, *x, *e; + + p = kd->data; + e = p + datalen; + for(; p+2 <= e; p = x){ + if((x = p+2+p[1]) > e) + break; + if(debug > 1) + fprint(2, "ie=%.2x data[%.2x]=%.*H\n", p[0], p[1], p[1], p+2); + if(p[0] == 0x30){ /* RSN */ + } + if(p[0] == 0xDD){ /* WPA */ + static uchar oui[] = { 0x00, 0x0f, 0xac, 0x01, }; + + if(p+2+sizeof(oui) > x || memcmp(p+2, oui, sizeof(oui)) != 0) + continue; + if((flags & Fenc) == 0) + continue; /* ignore gorup key if unencrypted */ + gtklen = x - (p + 8); + if(gtklen <= 0) + continue; + if(gtklen > sizeof(gtk)) + gtklen = sizeof(gtk); + memmove(gtk, p + 8, gtklen); + gtkkid = p[6] & 3; + } + } + } + + if((flags & (Fptk|Fack)) == (Fptk|Fack)){ + if(vers != 1) /* in WPA2, RSC is for group key only */ + tsc = 0LL; + else { + tsc = rsc; + rsc = 0LL; + } + /* install peerwise receive key */ + if(fprint(cfd, "rxkey %.*H %s:%.*H@%llux", Eaddrlen, amac, + peercipher->name, peercipher->keylen, ptk+32, tsc) < 0) + sysfatal("write rxkey: %r"); + + /* pick random 16bit tsc value for transmit */ + tsc = 1 + (truerand() & 0x7fff); + memset(kd->rsc, 0, sizeof(kd->rsc)); + kd->rsc[0] = tsc; + kd->rsc[1] = tsc>>8; + memset(kd->eapoliv, 0, sizeof(kd->eapoliv)); + memset(kd->nonce, 0, sizeof(kd->nonce)); + reply(smac, amac, flags & ~(Fack|Fenc|Fsec), kd, nil, 0); + sleep(100); + + /* install peerwise transmit key */ + if(fprint(cfd, "txkey %.*H %s:%.*H@%llux", Eaddrlen, amac, + peercipher->name, peercipher->keylen, ptk+32, tsc) < 0) + sysfatal("write txkey: %r"); + + if(!background && !debug){ + background = 1; + switch(rfork(RFNOTEG|RFREND|RFPROC|RFNOWAIT)){ + default: + exits(nil); + case -1: + sysfatal("fork: %r"); + return; + case 0: + break; + } + } + } else + if((flags & (Fptk|Fsec|Fack)) == (Fsec|Fack)){ + if(kd->type[0] == 0xFE){ + /* WPA always RC4 encrypts the GTK, even tho the flag isnt set */ + if((flags & Fenc) == 0) + datalen = rc4unwrap(ptk+16, kd->eapoliv, kd->data, datalen); + gtklen = datalen; + if(gtklen > sizeof(gtk)) + gtklen = sizeof(gtk); + memmove(gtk, kd->data, gtklen); + gtkkid = (flags >> 4) & 3; + } + + memset(kd->rsc, 0, sizeof(kd->rsc)); + memset(kd->eapoliv, 0, sizeof(kd->eapoliv)); + memset(kd->nonce, 0, sizeof(kd->nonce)); + reply(smac, amac, flags & ~(Fenc|Fack), kd, nil, 0); + } else + continue; + + if(gtklen >= groupcipher->keylen && gtkkid != -1){ + /* install group key */ + if(fprint(cfd, "rxkey%d %.*H %s:%.*H@%llux", + gtkkid, Eaddrlen, amac, + groupcipher->name, groupcipher->keylen, gtk, rsc) < 0) + sysfatal("write rxkey%d: %r", gtkkid); + } + } + } +} --- /sys/src/cmd/aux/mkfile Sun Nov 6 21:45:18 2016 +++ /sys/src/cmd/aux/mkfile Sun Nov 6 21:45:16 2016 @@ -35,6 +35,7 @@ trampoline\ usage\ watchdog\ + wpa\ write\ zerotrunc\ --- /sys/man/8/wpa Thu Jan 1 00:00:00 1970 +++ /sys/man/8/wpa Sun Nov 6 21:45:19 2016 @@ -0,0 +1,72 @@ +.TH WPA 8 +.SH NAME +wpa \- Wi-Fi Protected Access setup +.SH SYNOPSIS +.B aux/wpa +[ +.B -dp12 +] [ +.B -s +.I essid +] +.I dev +.SH DESCRIPTION +.I Wpa +handles the authentication and key exchange with WPA +protected wireless networks. +.PP +The +.I dev +parameter specifies the network interface that needs +to be setup with WPA. +The +.I essid +can be set with the +.I -s +option. Otherwise, the previously configured essid +on the interface will be used. +The +.B -p +option will prompt and install the preshared key or +pap/chap credentials into factotum, otherwise the key has to be +already present in factotum or an interactive key prompter like +auth/fgui (see +.IR factotum (4)) +needs to provide it. +.PP +The authentication protocol is initiated by the +wireless access point so +.I wpa +will background itself after the keyprompt and establish +the encryption automatically as needed. +The optional +.B -1 +or +.B -2 +arguments can be used to select between WPA1/TKIP +(default) or WPA2/CCMP encryption with WPA PSK. +.PP +The +.B -d +option enables debugging and causes +.I wpa +to stay in foreground writing protocol messages to +standard error. +.SH EXAMPLES +Setup wireless encryption: +.EX +% bind -a '#l1' /net +% aux/wpa -s 9HAL -p /net/ether1 +!Adding key: proto=wpapsk essid=9HAL +password: ***** +! +% ip/ipconfig ether /net/ether1 +.EE +.SH SOURCE +.B /sys/src/cmd/aux/wpa.c +.SH SEE ALSO +.IR factotum (4), +.IR ipconfig (8) +.SH HISTORY +.I Wpa +first appeared in 9front (March, 2013). --- /sys/man/4/factotum Sun Nov 6 21:45:25 2016 +++ /sys/man/4/factotum Sun Nov 6 21:45:21 2016 @@ -133,6 +133,10 @@ .IR vnc (1)'s challenge/response. .TP +.B wpapsk +the WPA-PSK challenge/response protocol used in wireless networks +(client side only). +.TP .B wep WEP passwords for wireless ethernet cards. .PD @@ -321,6 +325,14 @@ .BR sshserve , or .BR tls . +.PP +.B Wpapsk +requires a key with +.B proto=wpapsk +and attributes +.B essid +and +.BR !password . .PP .B Wep requires a