changelog shortlog tags branches files raw gz bz2 help

Mercurial > hg > plan9front / changeset: wpa: experimental wpa2 enterprise support

changeset 4226: 76e57c8daa02
parent 4225: 7780917b503d
child 4227: c3f8bf355f2e
author: cinap_lenrek@felloff.net
date: Sun, 25 Jan 2015 07:58:20 +0100
files: sys/src/cmd/aux/wpa.c
description: wpa: experimental wpa2 enterprise support

this adds support for eap-peap/mschapv2 and eap-ttls/pap.

code has only been tested with freeradius and a cheap
access point, not tested with actual eduroam network.
     1.1--- a/sys/src/cmd/aux/wpa.c
     1.2+++ b/sys/src/cmd/aux/wpa.c
     1.3@@ -6,6 +6,7 @@
     1.4 #include <auth.h>
     1.5 
     1.6 enum {
     1.7+	PMKlen = 256/8,
     1.8 	PTKlen = 512/8,
     1.9 	GTKlen = 256/8,
    1.10 
    1.11@@ -51,6 +52,34 @@ struct Cipher
    1.12 	int	keylen;
    1.13 };
    1.14 
    1.15+typedef struct Eapconn Eapconn;
    1.16+typedef struct TLStunn TLStunn;
    1.17+
    1.18+struct Eapconn
    1.19+{
    1.20+	int	fd;
    1.21+	int	version;
    1.22+
    1.23+	uchar	type;
    1.24+	uchar	smac[Eaddrlen];
    1.25+	uchar	amac[Eaddrlen];
    1.26+
    1.27+	TLStunn	*tunn;
    1.28+
    1.29+	void	(*write)(Eapconn*, uchar *data, int datalen);
    1.30+};
    1.31+
    1.32+struct TLStunn
    1.33+{
    1.34+	int	fd;
    1.35+
    1.36+	int	clientpid;
    1.37+	int	readerpid;
    1.38+
    1.39+	uchar	id;
    1.40+	uchar	tp;
    1.41+};
    1.42+
    1.43 Cipher	tkip = { "tkip", 32 };
    1.44 Cipher	ccmp = { "ccmp", 16 };
    1.45 
    1.46@@ -62,6 +91,7 @@ int	prompt;
    1.47 int	debug;
    1.48 int	fd, cfd;
    1.49 char	*dev;
    1.50+int	ispsk;
    1.51 char	devdir[40];
    1.52 uchar	ptk[PTKlen];
    1.53 char	essid[32+1];
    1.54@@ -70,6 +100,7 @@ uvlong	lastrepc;
    1.55 uchar rsntkipoui[4] = {0x00, 0x0F, 0xAC, 0x02};
    1.56 uchar rsnccmpoui[4] = {0x00, 0x0F, 0xAC, 0x04};
    1.57 uchar rsnapskoui[4] = {0x00, 0x0F, 0xAC, 0x02};
    1.58+uchar rsnawpaoui[4] = {0x00, 0x0F, 0xAC, 0x01};
    1.59 
    1.60 uchar	rsnie[] = {
    1.61 	0x30,			/* RSN */
    1.62@@ -99,6 +130,16 @@ uchar	wpaie[] = {
    1.63 	0x00, 0x50, 0xf2, 0x02,	/* authentication suite PSK */
    1.64 };
    1.65 
    1.66+void*
    1.67+emalloc(int len)
    1.68+{
    1.69+	void *v;
    1.70+
    1.71+	if((v = mallocz(len, 1)) == nil)
    1.72+		sysfatal("malloc: %r");
    1.73+	return v;
    1.74+}
    1.75+
    1.76 int
    1.77 hextob(char *s, char **sp, uchar *b, int n)
    1.78 {
    1.79@@ -156,7 +197,17 @@ getessid(void)
    1.80 }
    1.81 
    1.82 int
    1.83-connected(void)
    1.84+getbssid(uchar mac[Eaddrlen])
    1.85+{
    1.86+	char buf[64];
    1.87+
    1.88+	if(getifstats("bssid:", buf, sizeof(buf)) != nil)
    1.89+		return parseether(mac, buf);
    1.90+	return -1;
    1.91+}
    1.92+
    1.93+int
    1.94+connected(int assoc)
    1.95 {
    1.96 	char status[1024];
    1.97 
    1.98@@ -166,6 +217,10 @@ connected(void)
    1.99 		return 0;
   1.100 	if(strcmp(status, "unauthenticated") == 0)
   1.101 		return 0;
   1.102+	if(assoc){
   1.103+		if(strcmp(status, "blocked") != 0 && strcmp(status, "associated") != 0)
   1.104+			return 0;
   1.105+	}
   1.106 	if(debug)
   1.107 		fprint(2, "status: %s\n", status);
   1.108 	return 1;
   1.109@@ -287,10 +342,15 @@ trunc:		sysfatal("invalid or truncated R
   1.110 		if((e - p) < 4)
   1.111 			goto trunc;
   1.112 
   1.113-		/* look for PSK oui */
   1.114 		if(rsne[0] == 0x30){
   1.115+			/* look for PSK oui */
   1.116 			if(memcmp(p, rsnapskoui, 4) == 0)
   1.117 				break;
   1.118+			/* look for WPA oui */
   1.119+			if(memcmp(p, rsnawpaoui, 4) == 0){
   1.120+				ispsk = 0;
   1.121+				break;
   1.122+			}
   1.123 		} else {
   1.124 			if(memcmp(p, wpaapskoui, 4) == 0)
   1.125 				break;
   1.126@@ -298,7 +358,7 @@ trunc:		sysfatal("invalid or truncated R
   1.127 		p += 4;
   1.128 	}
   1.129 	if(i >= n){
   1.130-		sysfatal("auth suite is not PSK; brsne: %s", buf);
   1.131+		sysfatal("auth suite is not PSK or WPA; brsne: %s", buf);
   1.132 		return 0;
   1.133 	}
   1.134 
   1.135@@ -315,6 +375,96 @@ trunc:		sysfatal("invalid or truncated R
   1.136 	return w - rsne;
   1.137 }
   1.138 
   1.139+char*
   1.140+factotumattr(char *attr, char *fmt, ...)
   1.141+{
   1.142+	char buf[1024];
   1.143+	va_list list;
   1.144+	AuthRpc *rpc;
   1.145+	char *val;
   1.146+	Attr *a;
   1.147+	int afd;
   1.148+
   1.149+	if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
   1.150+		return nil;
   1.151+	if((rpc = auth_allocrpc(afd)) == nil){
   1.152+		close(afd);
   1.153+		return nil;
   1.154+	}
   1.155+	va_start(list, fmt);
   1.156+	vsnprint(buf, sizeof(buf), fmt, list);
   1.157+	va_end(list);
   1.158+	val = nil;
   1.159+	if(auth_rpc(rpc, "start", buf, strlen(buf)) == 0){
   1.160+		if((a = auth_attr(rpc)) != nil){
   1.161+			if((val = _strfindattr(a, attr)) != nil)
   1.162+				val = strdup(val);
   1.163+			_freeattr(a);
   1.164+		}
   1.165+	}
   1.166+	auth_freerpc(rpc);
   1.167+	close(afd);
   1.168+
   1.169+	return val;
   1.170+}
   1.171+
   1.172+void
   1.173+freeup(UserPasswd *up)
   1.174+{
   1.175+	memset(up->user, 0, strlen(up->user));
   1.176+	memset(up->passwd, 0, strlen(up->passwd));
   1.177+	free(up);
   1.178+}
   1.179+
   1.180+char*
   1.181+getidentity(void)
   1.182+{
   1.183+	static char *identity;
   1.184+	char *s;
   1.185+
   1.186+	s = nil;
   1.187+	for(;;){
   1.188+		if(getessid() == nil)
   1.189+			break;
   1.190+		if((s = factotumattr("user", "proto=pass service=wpa essid=%q", essid)) != nil)
   1.191+			break;
   1.192+		if((s = factotumattr("user", "proto=mschapv2 role=client service=wpa essid=%q", essid)) != nil)
   1.193+			break;
   1.194+		break;
   1.195+	}
   1.196+	if(s != nil){
   1.197+		free(identity);
   1.198+		identity = s;
   1.199+	} else if(identity == nil)
   1.200+		identity = strdup("anonymous");
   1.201+	if(debug)
   1.202+		fprint(2, "identity: %s\n", identity);
   1.203+	return identity;
   1.204+}
   1.205+
   1.206+int
   1.207+factotumctl(char *fmt, ...)
   1.208+{
   1.209+	va_list list;
   1.210+	int r, fd;
   1.211+
   1.212+	if((fd = open("/mnt/factotum/ctl", OWRITE)) < 0)
   1.213+		return -1;
   1.214+	va_start(list, fmt);
   1.215+	r = vfprint(fd, fmt, list);
   1.216+	va_end(list);
   1.217+	close(fd);
   1.218+	return r;
   1.219+}
   1.220+
   1.221+int
   1.222+setpmk(uchar pmk[PMKlen])
   1.223+{
   1.224+	if(getessid() == nil)
   1.225+		return -1;
   1.226+	return factotumctl("key proto=wpapsk role=client essid=%q !password=%.32H\n", essid, pmk);
   1.227+}
   1.228+
   1.229 int
   1.230 getptk(	uchar smac[Eaddrlen], uchar amac[Eaddrlen], 
   1.231 	uchar snonce[Noncelen],  uchar anonce[Noncelen], 
   1.232@@ -495,43 +645,470 @@ checkmic(Keydescr *kd, uchar *msg, int m
   1.233 }
   1.234 
   1.235 void
   1.236-reply(int eapver, uchar smac[Eaddrlen], uchar amac[Eaddrlen], int flags, Keydescr *kd, uchar *data, int datalen)
   1.237+fdwrite(Eapconn *conn, uchar *data, int len)
   1.238+{
   1.239+	if(write(conn->fd, data, len) != len)
   1.240+		sysfatal("write: %r");
   1.241+}
   1.242+
   1.243+void
   1.244+etherwrite(Eapconn *conn, uchar *data, int len)
   1.245 {
   1.246-	uchar buf[4096], *m, *p = buf;
   1.247+	uchar *buf, *p;
   1.248+	int n;
   1.249 
   1.250-	memmove(p, amac, Eaddrlen); p += Eaddrlen;
   1.251-	memmove(p, smac, Eaddrlen); p += Eaddrlen;
   1.252+	if(debug)
   1.253+		fprint(2, "\nreply(v%d,t%d) %E -> %E: ", conn->version, conn->type, conn->smac, conn->amac);
   1.254+	n = 2*Eaddrlen + 2 + len;
   1.255+	if(n < 60) n = 60;	/* ETHERMINTU */
   1.256+	p = buf = emalloc(n);
   1.257+	/* ethernet header */
   1.258+	memmove(p, conn->amac, Eaddrlen); p += Eaddrlen;
   1.259+	memmove(p, conn->smac, Eaddrlen); p += Eaddrlen;
   1.260 	*p++ = 0x88;
   1.261 	*p++ = 0x8e;
   1.262+	/* eapol data */
   1.263+	memmove(p, data, len);
   1.264+	fdwrite(conn, buf, n);
   1.265+	free(buf);
   1.266+}
   1.267 
   1.268-	m = p;
   1.269-	*p++ = eapver;
   1.270-	*p++ = 0x03;
   1.271+void
   1.272+eapwrite(Eapconn *conn, uchar *data, int len)
   1.273+{
   1.274+	uchar *buf, *p;
   1.275+
   1.276+	p = buf = emalloc(len + 4);
   1.277+	/* eapol header */
   1.278+	*p++ = conn->version;
   1.279+	*p++ = conn->type;
   1.280+	*p++ = len >> 8;
   1.281+	*p++ = len;
   1.282+	/* eap data */
   1.283+	memmove(p, data, len); p += len;
   1.284+	etherwrite(conn, buf, p - buf);
   1.285+	free(buf);
   1.286+}
   1.287+
   1.288+void
   1.289+replykey(Eapconn *conn, int flags, Keydescr *kd, uchar *data, int datalen)
   1.290+{
   1.291+	uchar buf[4096], *p = buf;
   1.292+
   1.293+	/* eapol hader */
   1.294+	*p++ = conn->version;
   1.295+	*p++ = conn->type;
   1.296 	datalen += Keydescrlen;
   1.297 	*p++ = datalen >> 8;
   1.298 	*p++ = datalen;
   1.299 	datalen -= Keydescrlen;
   1.300-
   1.301+	/* key header */
   1.302 	memmove(p, kd, Keydescrlen);
   1.303 	kd = (Keydescr*)p;
   1.304 	kd->flags[0] = flags >> 8;
   1.305 	kd->flags[1] = flags;
   1.306 	kd->datalen[0] = datalen >> 8;
   1.307 	kd->datalen[1] = datalen;
   1.308+	/* key data */
   1.309 	p = kd->data;
   1.310 	memmove(p, data, datalen);
   1.311 	p += datalen;
   1.312-
   1.313+	/* mic */
   1.314 	memset(kd->mic, 0, MIClen);
   1.315 	if(flags & Fmic)
   1.316-		calcmic(kd, m, p - m);
   1.317-	if(debug != 0){
   1.318-		fprint(2, "\nreply(v%d) %E -> %E: ", eapver, smac, amac);
   1.319+		calcmic(kd, buf, p - buf);
   1.320+	etherwrite(conn, buf, p - buf);
   1.321+	if(debug)
   1.322 		dumpkeydescr(kd);
   1.323+}
   1.324+
   1.325+void
   1.326+eapresp(Eapconn *conn, int code, int id, uchar *data, int len)
   1.327+{
   1.328+	uchar *buf, *p;
   1.329+
   1.330+	len += 4;
   1.331+	p = buf = emalloc(len);
   1.332+	/* eap header */
   1.333+	*p++ = code;
   1.334+	*p++ = id;
   1.335+	*p++ = len >> 8;
   1.336+	*p++ = len;
   1.337+	memmove(p, data, len-4);
   1.338+	(*conn->write)(conn, buf, len);
   1.339+	free(buf);
   1.340+
   1.341+	if(debug)
   1.342+		fprint(2, "eapresp(code=%d, id=%d, data=%.*H)\n", code, id, len-4, data);
   1.343+}
   1.344+
   1.345+void
   1.346+tlsreader(TLStunn *tunn, Eapconn *conn)
   1.347+{
   1.348+	enum {
   1.349+		Tlshdrsz = 5,
   1.350+		TLStunnhdrsz = 6,
   1.351+	};
   1.352+	uchar *rec, *w, *p;
   1.353+	int fd, n, css;
   1.354+
   1.355+	fd = tunn->fd;
   1.356+	rec = nil;
   1.357+	css = 0;
   1.358+Reset:
   1.359+	w = rec;
   1.360+	w += TLStunnhdrsz;
   1.361+	for(;;w += n){
   1.362+		if((p = realloc(rec, (w - rec) + Tlshdrsz)) == nil)
   1.363+			break;
   1.364+		w = p + (w - rec), rec = p;
   1.365+		if(readn(fd, w, Tlshdrsz) != Tlshdrsz)
   1.366+			break;
   1.367+		n = w[3]<<8 | w[4];
   1.368+		if(n < 1)
   1.369+			break;
   1.370+		if((p = realloc(rec, (w - rec) + Tlshdrsz+n)) == nil)
   1.371+			break;
   1.372+		w = p + (w - rec), rec = p;
   1.373+		if(readn(fd, w+Tlshdrsz, n) != n)
   1.374+			break;
   1.375+		n += Tlshdrsz;	
   1.376+		
   1.377+		/* batch records that need to be send together */
   1.378+		if(!css){
   1.379+			/* Client Certificate */
   1.380+			if(w[0] == 22 && w[5] == 11)
   1.381+				continue;
   1.382+			/* Client Key Exchange */
   1.383+			if(w[0] == 22 && w[5] == 16)
   1.384+				continue;
   1.385+			/* Change Cipher Spec */
   1.386+			if(w[0] == 20){
   1.387+				css = 1;
   1.388+				continue;
   1.389+			}
   1.390+		}
   1.391+
   1.392+		/* check if we'r still the tunnel for this connection */
   1.393+		if(conn->tunn != tunn)
   1.394+			break;
   1.395+
   1.396+		/* flush records in encapsulation */
   1.397+		p = rec + TLStunnhdrsz;
   1.398+		w += n;
   1.399+		n = w - p;
   1.400+		*(--p) = n;
   1.401+		*(--p) = n >> 8;
   1.402+		*(--p) = n >> 16;
   1.403+		*(--p) = n >> 24;
   1.404+		*(--p) = 0x80;	/* flags: Length included */
   1.405+		*(--p) = tunn->tp;
   1.406+
   1.407+		eapresp(conn, 2, tunn->id, p, w - p);
   1.408+		goto Reset;
   1.409 	}
   1.410-	datalen = p - buf;
   1.411-	if(write(fd, buf, datalen) != datalen)
   1.412-		sysfatal("write: %r");
   1.413+	free(rec);
   1.414+}
   1.415+
   1.416+void ttlsclient(int);
   1.417+void peapclient(int);
   1.418+
   1.419+void
   1.420+eapreset(Eapconn *conn)
   1.421+{
   1.422+	TLStunn *tunn;
   1.423+
   1.424+	tunn = conn->tunn;
   1.425+	if(tunn == nil)
   1.426+		return;
   1.427+	if(debug)
   1.428+		fprint(2, "eapreset: kill client %d\n", tunn->clientpid);
   1.429+	conn->tunn = nil;
   1.430+	postnote(PNPROC, tunn->clientpid, "kill");
   1.431+}
   1.432+
   1.433+int
   1.434+tlswrap(int fd, char *label)
   1.435+{
   1.436+	TLSconn *tls;
   1.437+
   1.438+	tls = emalloc(sizeof(TLSconn));
   1.439+	if(debug)
   1.440+		tls->trace = print;
   1.441+	if(label != nil){
   1.442+		/* tls client computes the 1024 bit MSK for us */
   1.443+		tls->sessionType = "ttls";
   1.444+		tls->sessionConst = label;
   1.445+		tls->sessionKeylen = 128;
   1.446+		tls->sessionKey = emalloc(tls->sessionKeylen);
   1.447+	}
   1.448+	if((fd = tlsClient(fd, tls)) < 0)
   1.449+		sysfatal("tlsClient: %r");
   1.450+	if(label != nil){
   1.451+		/*
   1.452+		 * PMK is derived from MSK by taking the first 256 bits.
   1.453+		 * we store the PMK into factotum with setpmk() associated
   1.454+		 * with the current essid.
   1.455+		 */
   1.456+		if(setpmk(tls->sessionKey) < 0)
   1.457+			sysfatal("setpmk: %r");
   1.458+	}
   1.459+	return fd;
   1.460+}
   1.461+
   1.462+void
   1.463+eapreq(Eapconn *conn, int code, int id, uchar *data, int datalen)
   1.464+{
   1.465+	TLStunn *tunn;
   1.466+	int tp, frag;
   1.467+	char *user;
   1.468+
   1.469+	if(debug)
   1.470+		fprint(2, "eapreq(code=%d, id=%d, data=%.*H)\n", code, id, datalen, data);
   1.471+
   1.472+	switch(code){
   1.473+	case 1:	/* Request */
   1.474+		break;
   1.475+	case 4:	/* NAK */
   1.476+	case 3:	/* Success */
   1.477+		eapreset(conn);
   1.478+		if(code == 4 || debug)
   1.479+			fprint(2, "%s: eap code %s\n", argv0, code == 3 ? "Success" : "NAK");
   1.480+	default:
   1.481+		return;
   1.482+	}
   1.483+
   1.484+	if(datalen < 1)
   1.485+		return;
   1.486+	tp = data[0];
   1.487+	switch(tp){
   1.488+	case 1:		/* Identity */
   1.489+		user = getidentity();
   1.490+		datalen = 1+strlen(user);
   1.491+		memmove(data+1, user, datalen-1);
   1.492+		eapresp(conn, 2, id, data, datalen);
   1.493+		break;
   1.494+	case 2:
   1.495+		fprint(2, "%s: eap error: %.*s\n", argv0, datalen-1, (char*)data+1);
   1.496+		break;
   1.497+	case 33:	/* EAP Extensions (AVP) */
   1.498+		if(debug)
   1.499+			fprint(2, "eap extension: %.*H\n", datalen, data);
   1.500+		eapresp(conn, 2, id, data, datalen);
   1.501+		break;
   1.502+	case 26:	/* MS-CHAP-V2 */
   1.503+		data++;
   1.504+		datalen--;
   1.505+		if(datalen < 1)
   1.506+			break;
   1.507+
   1.508+		/* OpCode */	
   1.509+		switch(data[0]){
   1.510+		case 1:	/* Challenge */
   1.511+			if(datalen > 4) {
   1.512+				uchar cid, chal[16], resp[48];
   1.513+				char user[256+1];
   1.514+				int len;
   1.515+
   1.516+				cid = data[1];
   1.517+				len = data[2]<<8 | data[3];
   1.518+				if(data[4] != sizeof(chal))
   1.519+					break;
   1.520+				if(len > datalen || (5 + data[4]) > len)
   1.521+					break;
   1.522+				memmove(chal, data+5, sizeof(chal));
   1.523+				memset(user, 0, sizeof(user));
   1.524+				memset(resp, 0, sizeof(resp));
   1.525+				if(auth_respond(chal, sizeof(chal), user, sizeof(user), resp, sizeof(resp), nil,
   1.526+					"proto=mschapv2 role=client service=wpa essid=%q", essid) < 0){
   1.527+					fprint(2, "%s: eap mschapv2: auth_respond: %r\n", argv0);
   1.528+					break;
   1.529+				}
   1.530+				len = 5 + sizeof(resp) + 1 + strlen(user);
   1.531+				data[0] = 2;		/* OpCode - Response */
   1.532+				data[1] = cid;		/* Identifier */
   1.533+				data[2] = len >> 8;
   1.534+				data[3] = len;
   1.535+				data[4] = sizeof(resp)+1;	/* ValueSize */
   1.536+				memmove(data+5, resp, sizeof(resp));
   1.537+				data[5 + sizeof(resp)] = 0;	/* flags */
   1.538+				strcpy((char*)&data[5 + sizeof(resp) + 1], user);
   1.539+
   1.540+				*(--data) = tp, len++;
   1.541+				eapresp(conn, 2, id, data, len);
   1.542+			}
   1.543+			break;
   1.544+
   1.545+		case 3:	/* Success */
   1.546+		case 4:	/* Failure */
   1.547+			if(debug || data[0] == 4)
   1.548+				fprint(2, "%s: eap mschapv2 %s: %.*s\n", argv0,
   1.549+					data[0] == 3 ? "Success" : "Failure",
   1.550+					datalen < 4 ? 0 : datalen-4, (char*)data+4);
   1.551+			*(--data) = tp;
   1.552+			eapresp(conn, 2, id, data, 2);
   1.553+			break;
   1.554+		}
   1.555+		break;
   1.556+
   1.557+	case 21:	/* EAP-TTLS */
   1.558+	case 25:	/* PEAP */
   1.559+		if(datalen < 2)
   1.560+			break;
   1.561+		datalen -= 2;
   1.562+		data++;
   1.563+		tunn = conn->tunn;
   1.564+		if(*data & 0x20){	/* flags: start */
   1.565+			int p[2], pid;
   1.566+
   1.567+			if(tunn != nil){
   1.568+				if(tunn->id == id && tunn->tp == tp)
   1.569+					break;	/* is retransmit, ignore */
   1.570+				eapreset(conn);
   1.571+			}
   1.572+			if(pipe(p) < 0)
   1.573+				sysfatal("pipe: %r");
   1.574+			if((pid = fork()) == -1)
   1.575+				sysfatal("fork: %r");
   1.576+			if(pid == 0){
   1.577+				close(p[0]);
   1.578+				switch(tp){
   1.579+				case 21:
   1.580+					ttlsclient(p[1]);
   1.581+					break;
   1.582+				case 25:
   1.583+					peapclient(p[1]);
   1.584+					break;
   1.585+				}
   1.586+				exits(nil);
   1.587+			}
   1.588+			close(p[1]);
   1.589+			tunn = emalloc(sizeof(TLStunn));
   1.590+			tunn->tp = tp;
   1.591+			tunn->id = id;
   1.592+			tunn->fd = p[0];
   1.593+			tunn->clientpid = pid;
   1.594+			conn->tunn = tunn;
   1.595+			if((pid = rfork(RFPROC|RFMEM)) == -1)
   1.596+				sysfatal("fork: %r");
   1.597+			if(pid == 0){
   1.598+				pid = getpid();
   1.599+				tunn->readerpid = pid;
   1.600+				tlsreader(tunn, conn);
   1.601+				if(conn->tunn == tunn)
   1.602+					conn->tunn = nil;
   1.603+				close(tunn->fd);
   1.604+				free(tunn);
   1.605+				exits(nil);
   1.606+			}
   1.607+			break;
   1.608+		}
   1.609+		if(tunn == nil)
   1.610+			break;
   1.611+		if(id <= tunn->id || tunn->tp != tp)
   1.612+			break;
   1.613+		tunn->id = id;
   1.614+		frag = *data & 0x40;	/* flags: more fragments */
   1.615+		if(*data & 0x80){	/* flags: length included */
   1.616+			datalen -= 4;
   1.617+			data += 4;
   1.618+		}
   1.619+		data++;
   1.620+		if(datalen <= 0)
   1.621+			break;
   1.622+		if(write(tunn->fd, data, datalen) != datalen)
   1.623+			break;
   1.624+		if(frag || (tp == 25 && data[0] == 20)){	/* ack change cipher spec */
   1.625+			data -= 2;
   1.626+			data[0] = tp;
   1.627+			data[1] = 0;
   1.628+			eapresp(conn, 2, id, data, 2);
   1.629+		}
   1.630+		break;
   1.631+	}
   1.632+}
   1.633+
   1.634+int
   1.635+avp(uchar *p, int code, void *val, int len)
   1.636+{
   1.637+	int n;
   1.638+
   1.639+	n = 4 + 4 + len;
   1.640+
   1.641+	*p++ = code >> 24;
   1.642+	*p++ = code >> 16;
   1.643+	*p++ = code >> 8;
   1.644+	*p++ = code;
   1.645+	*p++ = 2;
   1.646+
   1.647+	*p++ = n >> 16;
   1.648+	*p++ = n >> 8;
   1.649+	*p++ = n;
   1.650+
   1.651+	memmove(p, val, len);
   1.652+	p += len;
   1.653+
   1.654+	n = (n + 3) & ~3;
   1.655+	memset(p, 0, n - len);
   1.656+
   1.657+	return n;
   1.658+}
   1.659+
   1.660+enum {
   1.661+	/* Avp Code */
   1.662+	AvpUserName = 1,
   1.663+	AvpUserPass = 2,
   1.664+	AvpChapPass = 3,
   1.665+	AvpChapChal = 60,
   1.666+};
   1.667+
   1.668+void
   1.669+ttlsclient(int fd)
   1.670+{
   1.671+	uchar buf[4096];
   1.672+	UserPasswd *up;
   1.673+	int n;
   1.674+
   1.675+	fd = tlswrap(fd, "ttls keying material");
   1.676+
   1.677+	/* we should really do challenge response here */
   1.678+	if((up = auth_getuserpasswd(nil, "proto=pass service=wpa essid=%q", essid)) == nil)
   1.679+		sysfatal("ttlsclient %d: no user/pass for essid=%q", getpid(), essid);
   1.680+
   1.681+	n = avp(buf, AvpUserName, up->user, strlen(up->user));
   1.682+	n += avp(buf+n, AvpUserPass, up->passwd, strlen(up->passwd));
   1.683+	freeup(up);
   1.684+	if(write(fd, buf, n) != n)
   1.685+		sysfatal("ttlsclient write: %r");
   1.686+}
   1.687+
   1.688+void
   1.689+peapwrite(Eapconn *conn, uchar *data, int len)
   1.690+{
   1.691+	fdwrite(conn, data + 4, len - 4);
   1.692+}
   1.693+
   1.694+void
   1.695+peapclient(int fd)
   1.696+{
   1.697+	static Eapconn conn;
   1.698+	uchar buf[4096], *p;
   1.699+	int n, id, code;
   1.700+
   1.701+	conn.fd = fd = tlswrap(fd, "client EAP encryption");
   1.702+	while((n = read(fd, p = buf, sizeof(buf))) > 0){
   1.703+		if(n > 4 && (p[2] << 8 | p[3]) == n && p[4] == 33){
   1.704+			code = p[0];
   1.705+			id = p[1];
   1.706+			p += 4, n -= 4;
   1.707+			conn.write = fdwrite;
   1.708+		} else {
   1.709+			code = 1;
   1.710+			id = 0;
   1.711+			conn.write = peapwrite;
   1.712+		}
   1.713+		eapreq(&conn, code, id, p, n);
   1.714+	}
   1.715 }
   1.716 
   1.717 void
   1.718@@ -544,9 +1121,10 @@ usage(void)
   1.719 void
   1.720 main(int argc, char *argv[])
   1.721 {
   1.722-	uchar mac[Eaddrlen], buf[1024];
   1.723+	uchar mac[Eaddrlen], buf[4096];
   1.724 	static uchar brsne[258];
   1.725-	char addr[128];
   1.726+	static Eapconn conn;
   1.727+	char addr[128], *s;
   1.728 	uchar *rsne;
   1.729 	int rsnelen;
   1.730 	int n, try;
   1.731@@ -608,19 +1186,12 @@ main(int argc, char *argv[])
   1.732 			sysfatal("no essid set");
   1.733 	}
   1.734 
   1.735-	if(prompt){
   1.736-		char *s;
   1.737-
   1.738-		s = smprint("proto=wpapsk essid=%q !password?", essid);
   1.739-		auth_getkey(s);
   1.740-		free(s);
   1.741-	}
   1.742-
   1.743 Connect:
   1.744  	/* bss scan might not be complete yet, so check for 10 seconds.	*/
   1.745-	for(try = 10; (background || try >= 0) && !connected(); try--)
   1.746+	for(try = 10; (background || try >= 0) && !connected(0); try--)
   1.747 		sleep(1000);
   1.748 
   1.749+	ispsk = 1;
   1.750 	if(rsnelen <= 0 || rsne == brsne){
   1.751 		rsne = brsne;
   1.752 		rsnelen = buildrsne(rsne);
   1.753@@ -645,6 +1216,27 @@ Connect:
   1.754 	if(write(cfd, buf, n) != n)
   1.755 		sysfatal("write auth: %r");
   1.756 
   1.757+	if(prompt){
   1.758+		prompt = 0;
   1.759+		if(ispsk){
   1.760+			s = smprint("proto=wpapsk essid=%q !password?", essid);
   1.761+			auth_getkey(s);
   1.762+			free(s);
   1.763+		} else {
   1.764+			UserPasswd *up;
   1.765+
   1.766+			s = smprint("proto=pass service=wpa essid=%q user? !password?", essid);
   1.767+			auth_getkey(s);
   1.768+			free(s);
   1.769+
   1.770+			if((up = auth_getuserpasswd(nil, "proto=pass service=wpa essid=%q", essid)) != nil){
   1.771+				factotumctl("key proto=mschapv2 role=client service=wpa essid=%q user=%q !password=%q\n",
   1.772+					essid, up->user, up->passwd);
   1.773+				freeup(up);
   1.774+			}
   1.775+		}
   1.776+	}
   1.777+
   1.778 	if(!background){
   1.779 		background = 1;
   1.780 		if(!debug){
   1.781@@ -659,11 +1251,23 @@ Connect:
   1.782 			}
   1.783 		}
   1.784 	}
   1.785+
   1.786+	/* wait for getting associated before sending start message */
   1.787+	for(try = 10; (background || try >= 0) && !connected(1); try--)
   1.788+		sleep(500);
   1.789+
   1.790+	conn.fd = fd;
   1.791+	conn.write = eapwrite;
   1.792+	conn.type = 1;	/* Start */
   1.793+	conn.version = 1;
   1.794+	memmove(conn.smac, mac, Eaddrlen);
   1.795+	if(getbssid(conn.amac) == 0)
   1.796+		eapwrite(&conn, nil, 0);
   1.797 	
   1.798 	lastrepc = 0ULL;
   1.799 	for(;;){
   1.800-		uchar smac[Eaddrlen], amac[Eaddrlen], snonce[Noncelen], anonce[Noncelen], *p, *e, *m;
   1.801-		int proto, eapver, flags, vers, datalen;
   1.802+		uchar snonce[Noncelen], anonce[Noncelen], *p, *e, *m;
   1.803+		int proto, flags, vers, datalen;
   1.804 		uvlong repc, rsc, tsc;
   1.805 		Keydescr *kd;
   1.806 
   1.807@@ -671,8 +1275,9 @@ Connect:
   1.808 			sysfatal("read: %r");
   1.809 
   1.810 		if(n == 0){
   1.811-			if(debug != 0)
   1.812+			if(debug)
   1.813 				fprint(2, "got deassociation\n");
   1.814+			eapreset(&conn);
   1.815 			goto Connect;
   1.816 		}
   1.817 
   1.818@@ -680,33 +1285,57 @@ Connect:
   1.819 		e = buf+n;
   1.820 		if(n < 2*Eaddrlen + 2)
   1.821 			continue;
   1.822-		memmove(smac, p, Eaddrlen); p += Eaddrlen;
   1.823-		memmove(amac, p, Eaddrlen); p += Eaddrlen;
   1.824+
   1.825+		memmove(conn.smac, p, Eaddrlen); p += Eaddrlen;
   1.826+		memmove(conn.amac, p, Eaddrlen); p += Eaddrlen;
   1.827 		proto = p[0]<<8 | p[1]; p += 2;
   1.828-		if(proto != 0x888e || memcmp(smac, mac, Eaddrlen) != 0)
   1.829+
   1.830+		if(proto != 0x888e || memcmp(conn.smac, mac, Eaddrlen) != 0)
   1.831 			continue;
   1.832 
   1.833 		m = p;
   1.834 		n = e - p;
   1.835 		if(n < 4)
   1.836 			continue;
   1.837-		eapver = p[0];
   1.838-		if((eapver != 0x01 && eapver != 0x02) || p[1] != 0x03)
   1.839+
   1.840+		conn.version = p[0];
   1.841+		if(conn.version != 0x01 && conn.version != 0x02)
   1.842+			continue;
   1.843+		conn.type = p[1];
   1.844+		n = p[2]<<8 | p[3];
   1.845+		p += 4;
   1.846+		if(p+n > e)
   1.847+			continue;
   1.848+		e = p + n;
   1.849+
   1.850+		if(debug)
   1.851+			fprint(2, "\nrecv(v%d,t%d) %E <- %E: ", conn.version, conn.type, conn.smac, conn.amac);
   1.852+
   1.853+		if(conn.type == 0x00 && !ispsk){
   1.854+			uchar code, id;
   1.855+
   1.856+			if(n < 4)
   1.857+				continue;
   1.858+			code = p[0];
   1.859+			id = p[1];
   1.860+			n = p[3] | p[2]<<8;
   1.861+			if(n < 4 || p + n > e)
   1.862+				continue;
   1.863+			p += 4, n -= 4;
   1.864+			eapreq(&conn, code, id, p, n);
   1.865+			continue;
   1.866+		}
   1.867+
   1.868+		if(conn.type != 0x03)
   1.869 			continue;
   1.870 
   1.871-		if(debug != 0)
   1.872-			fprint(2, "\nrecv(v%d) %E <- %E: ", eapver, smac, amac);
   1.873-
   1.874-		n = p[2]<<8 | p[3];
   1.875-		p += 4;
   1.876-		if(n < Keydescrlen || p + n > e){
   1.877-			if(debug != 0)
   1.878+		if(n < Keydescrlen){
   1.879+			if(debug)
   1.880 				fprint(2, "bad kd size\n");
   1.881 			continue;
   1.882 		}
   1.883-		e = p + n;
   1.884 		kd = (Keydescr*)p;
   1.885-		if(debug != 0)
   1.886+		if(debug)
   1.887 			dumpkeydescr(kd);
   1.888 
   1.889 		if(kd->type[0] != 0xFE && kd->type[0] != 0x02)
   1.890@@ -724,8 +1353,8 @@ Connect:
   1.891 
   1.892 			memmove(anonce, kd->nonce, sizeof(anonce));
   1.893 			genrandom(snonce, sizeof(snonce));
   1.894-			if(getptk(smac, amac, snonce, anonce, ptk) != 0){
   1.895-				if(debug != 0)
   1.896+			if(getptk(conn.smac, conn.amac, snonce, anonce, ptk) != 0){
   1.897+				if(debug)
   1.898 					fprint(2, "getptk: %r\n");
   1.899 				continue;
   1.900 			}
   1.901@@ -734,13 +1363,13 @@ Connect:
   1.902 			memset(kd->rsc, 0, sizeof(kd->rsc));
   1.903 			memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
   1.904 			memmove(kd->nonce, snonce, sizeof(kd->nonce));
   1.905-			reply(eapver, smac, amac, (flags & ~(Fack|Fins)) | Fmic, kd, rsne, rsnelen);
   1.906+			replykey(&conn, (flags & ~(Fack|Fins)) | Fmic, kd, rsne, rsnelen);
   1.907 		} else {
   1.908 			uchar gtk[GTKlen];
   1.909 			int gtklen, gtkkid;
   1.910 
   1.911 			if(checkmic(kd, m, e - m) != 0){
   1.912-				if(debug != 0)
   1.913+				if(debug)
   1.914 					fprint(2, "bad mic\n");
   1.915 				continue;
   1.916 			}
   1.917@@ -754,7 +1383,7 @@ Connect:
   1.918 				(uvlong)kd->repc[1]<<48 |
   1.919 				(uvlong)kd->repc[0]<<56;
   1.920 			if(repc <= lastrepc){
   1.921-				if(debug != 0)
   1.922+				if(debug)
   1.923 					fprint(2, "bad repc: %llux <= %llux\n", repc, lastrepc);
   1.924 				continue;
   1.925 			}
   1.926@@ -773,11 +1402,11 @@ Connect:
   1.927 				else
   1.928 					datalen = aesunwrap(ptk+16, 16, kd->data, datalen);
   1.929 				if(datalen <= 0){
   1.930-					if(debug != 0)
   1.931+					if(debug)
   1.932 						fprint(2, "bad keywrap\n");
   1.933 					continue;
   1.934 				}
   1.935-				if(debug != 0)
   1.936+				if(debug)
   1.937 					fprint(2, "unwraped keydata[%.4x]=%.*H\n", datalen, datalen, kd->data);
   1.938 			}
   1.939 
   1.940@@ -792,7 +1421,7 @@ Connect:
   1.941 				for(; p+2 <= e; p = x){
   1.942 					if((x = p+2+p[1]) > e)
   1.943 						break;
   1.944-					if(debug != 0)
   1.945+					if(debug)
   1.946 						fprint(2, "ie=%.2x data[%.2x]=%.*H\n", p[0], p[1], p[1], p+2);
   1.947 					if(p[0] == 0x30){ /* RSN */
   1.948 					}
   1.949@@ -822,7 +1451,7 @@ Connect:
   1.950 					rsc = 0LL;
   1.951 				}
   1.952 				/* install pairwise receive key */
   1.953-				if(fprint(cfd, "rxkey %.*H %s:%.*H@%llux", Eaddrlen, amac,
   1.954+				if(fprint(cfd, "rxkey %.*H %s:%.*H@%llux", Eaddrlen, conn.amac,
   1.955 					peercipher->name, peercipher->keylen, ptk+32, tsc) < 0)
   1.956 					sysfatal("write rxkey: %r");
   1.957 
   1.958@@ -830,11 +1459,11 @@ Connect:
   1.959 				memset(kd->rsc, 0, sizeof(kd->rsc));
   1.960 				memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
   1.961 				memset(kd->nonce, 0, sizeof(kd->nonce));
   1.962-				reply(eapver, smac, amac, flags & ~(Fack|Fenc|Fins), kd, nil, 0);
   1.963+				replykey(&conn, flags & ~(Fack|Fenc|Fins), kd, nil, 0);
   1.964 				sleep(100);
   1.965 
   1.966 				/* install pairwise transmit key */ 
   1.967-				if(fprint(cfd, "txkey %.*H %s:%.*H@%llux", Eaddrlen, amac,
   1.968+				if(fprint(cfd, "txkey %.*H %s:%.*H@%llux", Eaddrlen, conn.amac,
   1.969 					peercipher->name, peercipher->keylen, ptk+32, tsc) < 0)
   1.970 					sysfatal("write txkey: %r");
   1.971 			} else
   1.972@@ -853,14 +1482,14 @@ Connect:
   1.973 				memset(kd->rsc, 0, sizeof(kd->rsc));
   1.974 				memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
   1.975 				memset(kd->nonce, 0, sizeof(kd->nonce));
   1.976-				reply(eapver, smac, amac, flags & ~(Fenc|Fack), kd, nil, 0);
   1.977+				replykey(&conn, flags & ~(Fenc|Fack), kd, nil, 0);
   1.978 			} else
   1.979 				continue;
   1.980 
   1.981 			if(gtklen >= groupcipher->keylen && gtkkid != -1){
   1.982 				/* install group key */
   1.983 				if(fprint(cfd, "rxkey%d %.*H %s:%.*H@%llux",
   1.984-					gtkkid, Eaddrlen, amac, 
   1.985+					gtkkid, Eaddrlen, conn.amac, 
   1.986 					groupcipher->name, groupcipher->keylen, gtk, rsc) < 0)
   1.987 					sysfatal("write rxkey%d: %r", gtkkid);
   1.988 			}