// 802.1x thingy
//
// what do we have to do:
//
// be able to send/receive EAPOL frames
// implement Supplicant state machine and sub-machine
// set wep keys when applicable
//
// 802.1x thingy
//
//
// our job:
//
// get access tocard interface
// read/write eapol frames
// be able to set wep keys
//
// supplicant state machine
// key receival state machine
// auth interaction
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <bio.h>
#include <ip.h>
#include <mp.h>
#include <libsec.h>
#include <auth.h>
#include "dat.h"
#include "fns.h"
typedef enum PortControl {
Auto,
ForceUnauthorized,
ForceAuthorized,
} PortControl;
typedef enum AuthState {
Unauthorized,
Authorized,
} AuthState;
// Supplicant PAE state machine (8.2.11) states
enum {
Logoff,
Disconnected,
Connecting,
Authenticating,
Held,
Authenticated,
Restart,
ForceAuth,
ForceUnauth,
};
char *paenames[] = {
[Logoff] "Logoff",
[Disconnected] "Disconnected",
[Connecting] "Connecting",
[Authenticating] "Authenticating",
[Held] "Held",
[Authenticated] "Authenticated",
[Restart] "Restart",
[ForceAuth] "ForceAuth",
[ForceUnauth] "ForceUnauth",
};
// Supplicant Backend state machine (8.2.12) states
enum {
Request,
Response,
Success,
Fail,
Timeout,
Idle,
Initialize,
Receive,
};
char *bnames[] = {
[Request] "Request",
[Response] "Response",
[Success] "Success",
[Fail] "Fail",
[Timeout] "Timeout",
[Idle] "Idle",
[Initialize] "Initialize",
[Receive] "Receive",
};
typedef struct backendstate {
// Supplicant Backend state machine constants (sect 8.2.12.1.2)
int authPeriod;
int backState;
// Supplicant Backend state machine variables (sect 8.2.12.1.1)
int eapNoResp;
int eapReq;
int eapResp;
// Timers (sect 8.2.2.1)
Timer *authWhile;
} backendstate;
typedef struct phasestate {
vlong startTime;
vlong doneTime;
char *type;
int success;
} phasestate;
typedef struct PAEstate {
// Supplicant PAE state machine constants (sect 8.2.11.1.2)
int heldPeriod;
int startPeriod;
int maxStart;
// Supplicant PAE state machine variables (sect 8.2.11.1)
int eapRestart;
int logoffSent;
PortControl sPortMode;
int startCount;
int userLogoff;
int paeState;
// Timers (sect 8.2.2.1)
Timer *heldWhile;
Timer *startWhen;
backendstate;
// Global variables (sect 8.2.2.2)
int eapFail;
int eapolEap;
int eapSuccess;
int initialize;
int keyDone;
int keyRun;
PortControl portControl;
int portEnabled;
AuthState portStatus;
int portValid; // needs work. happens if we cannot see the AP; ifstats shows ap = 4444444 or so
int suppAbort;
int suppFail;
AuthState suppPortStatus;
int suppSuccess;
int suppTimeout;
// other
int eapExpectTtlsStart;
Packet *rxEtherEap;
Packet *txEtherEap;
char *etherdir;
int etherfd, ethercfd;
uchar ourmac[6];
uchar apmac[6];
char *prompt;
char *options;
Channel *etherchan;
Channel *statuschan;
Channel *timerchan;
Channel *portchan;
Channel *backstart;
Channel *backdone;
Packet *pktr[Npkt], *pktt;
int pkgidx;
ReadBuf keysbuf;
ReadBuf notesbuf;
vlong paeTime;
vlong backTime;
vlong startTime;
vlong restartTime;
vlong eapidTime;
vlong keyTime;
vlong noteTime;
vlong verdictTime;
phasestate phase[2];
int lastEapId;
} PAEstate;
// other
static UserPasswd*upwd;
static uchar defmac[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03};
// static char buf[Blen];
static char buf[2048];
// ========== Port timers 'state machine' (8.2.3)
// see timer.c
// ==========
static PAEstate theState;
static Timers TheTimers;
extern Srv fs;
// ========== receive eapol frames
static void
etherproc(void *arg)
{
PAEstate *s;
Packet *rx;
int n;
s = arg;
for(;;) {
loglog("etherproc: waiting for %d into %d", s->etherfd, s->pkgidx);
rx = s->pktr[s->pkgidx];
// don't do this: we do not reset rx-> for packets not sent over etherchan
//if (rx->e != rx->b)
// logfatal("assertion failed: rx->e != rx->b (n == %ld)", rx->e - rx->b);
n = read(s->etherfd, rx->b, Pktlen);
loglog("etherproc: into %d read %d", s->pkgidx, n);
if(n <= 0)
break;
rx->e = rx->b + n;
if (rx->e < rx->ether->data) {
logall("etherproc: skipping short packet (ether len=%d)", n);
continue;
}
if (nhgets(rx->ether->t) != ETEAPOL) {
logall("etherproc: skipping non-ETEAPOL %x", nhgets(rx->ether->t));
continue;
}
if (rx->e < rx->eapol->data) {
logall("etherproc: skipping short packet (ether len=%d)", n);
continue;
}
if (rx->e < rx->eapol->data + nhgets(rx->eapol->ln)) {
logall("etherproc: skipping short packet (ether len=%d)", n);
continue;
}
switch(rx->eapol->tp) {
case EapolTpEap:
if (rx->e < rx->eap->data) {
logall("etherproc: skipping short packet (ether len=%d)", n);
continue;
}
if (rx->e < rx->eapol->data + nhgets(rx->eap->ln)) {
logall("etherproc: skipping short packet (ether len=%d)", n);
continue;
}
if (s->lastEapId == rx->eap->id)
loglog("etherproc lastEapId==eap->id==%d", rx->eap->id);
s->lastEapId = rx->eap->id;
switch(rx->eap->code) {
case EapRequest:
loglog("- - - - Eap Request id=%d - - - - ", rx->eap->id);
loglog("etherproc: about to send %d ", s->pkgidx);
send(s->etherchan, &rx);
loglog("\tetherproc: done send %d ", s->pkgidx);
s->pkgidx = (s->pkgidx+1)%Npkt;
break;
case EapResponse:
loglog("- - - - Eap Response id=%d - - - - ", rx->eap->id);
break;
case EapSuccess:
loglog("- - - - success id=%d - - - -", rx->eap->id);
syslog(0, logname, "etherproc: success id=%d", rx->eap->id);
s->verdictTime = nsec();
s->eapSuccess = 1;
send(s->statuschan, nil);
clearlog(getKeysbuf());
break;
case EapFailure:
loglog("- - - - fail id=%d - - - -", rx->eap->id);
syslog(0, logname, "etherproc: fail id=%d", rx->eap->id);
s->verdictTime = nsec();
s->eapFail = 1;
send(s->statuschan, nil);
clearlog(getKeysbuf());
break;
default:
loglog("- - - - unknown eap id=%d type=%d - - - - ", rx->eap->id, rx->eap->code);
syslog(0, logname, "etherproc: unknown eap id=%d type=%d", rx->eap->id, rx->eap->code);
break;
}
break;
case EapolTpStart:
logall("etherproc: start (ignored)");
break;
case EapolTpLogoff:
logall("etherproc: logoff (ignored)");
break;
case EapolTpKey:
if (s->keyRun || s->eapSuccess) {
loglog("- - - - key - - - -");
syslog(0, logname, "etherproc: key");
s->keyTime = nsec();
handleKey(s->ethercfd, rx->eapol, rx->e - rx->ether->data);
} else
logall("etherproc: ignoring key (not authed yet)");
break;
case EapolTpAsf:
logall("etherproc: asf (ignored)");
break;
default:
logall("etherproc: unknown type%d", rx->eapol->tp);
break;
}
}
logfatal(0, "etherproc: oops read %d...", n);
}
// ========== Key receive 'state machine' (8.2.7)
// XXX do we do this in a separate thread/proc, or in the main one?
// see key.c:/^handleKey
// ========== Supplicant backend state machine
// clean up/initialize
static void
abortSupp(PAEstate *s)
{
s->eapSuccess = 0;
s->eapFail = 0;
s->eapNoResp = 0;
s->eapReq = 0;
s->eapResp = 0;
s->suppAbort = 0;
// abortTTLS();
}
static void
build_eap(Eap*t, int code, int id, int datalen)
{
t->code = code;
t->id = id;
hnputs(t->ln, EAPHDR + datalen);
}
static void
show_notification(PAEstate *s, uchar *m, int l)
{
// should do better: rfc3748 says:
// s contains UTF-8 encoded ISO 10646 [RFC2279].
s->noteTime = nsec();
memset(buf, 0, sizeof(m));
memcpy(buf, m, l);
logall("notification: %s", buf);
appendlog(getNotesbuf(), 0, "%s", buf);
}
static void
getSuppRsp(PAEstate *s)
{
// handle rxEtherEap
// build txEtherEap
Packet *rx, *tx;
uchar *p, *beyond;
char *ident, *prompt;
int len;
int tlssucces, tlsfailed;
loglog("getSuppRsp %p", s->rxEtherEap);
if (s->eapResp || s->eapNoResp)
logall("oops... getSuppRsp called while result previous of prev call pending");
rx = s->rxEtherEap;
tx = s->pktt;
s->txEtherEap = tx;
memset(s->txEtherEap->b, 0, Pktlen);
switch(rx->eap->code) {
case EapRequest:
if (debug) print("getSuppRsp EapRequest: %d \n", rx->eap->data[0]);
switch(rx->eap->data[0]) {
case EapTpIdentity:
// data format: [ prompt ] [ '\0' piggy-backed-options ]
// show prompt? extract options?
beyond = rx->eapol->data + nhgets(rx->eap->ln);
p = &rx->eap->data[1];
prompt = (char*)p;
for (; *p != '\0' && p+1 < beyond; p++)
;
memset(buf, 0, sizeof(buf));
if (*p == '\0' && p+1 < beyond) {
memcpy(buf, p+1, beyond - (p+1));
logall("received EAP Identity request, prompt=\"%s\" options=\"%s\"", prompt, buf);
free(s->prompt);
s->prompt = strdup(prompt);
free(s->options);
s->options = strdup(buf);
// while ((p = strchr(buf, ',')) != nil)
// *p = '\n';
} else {
memcpy(buf, &rx->eap->data[1], beyond - &rx->eap->data[1]);
logall("received EAP Identity request, data=\"%s\"", buf);
free(s->prompt);
s->prompt = strdup(buf);
free(s->options);
s->options = nil;
}
// the following is a HACK.
// but: SNT macosX notes only mention config of
// internal username and password (for TTLS-PAP),
// and allow leaving external identity blank.
// rfc3748 specifically says to _not_ include the
// username in the external identity
if ((ident = strchr(myId, '@')) == nil)
ident = "";
tx->eap->data[0] = EapTpIdentity;
memcpy(&tx->eap->data[1], ident, strlen(ident));
build_eap(tx->eap, EapResponse, rx->eap->id, 1+strlen(ident));
s->eapResp = 1;
s->eapExpectTtlsStart = 1;
break;
case EapTpNotification:
tx->eap->data[0] = EapTpNotification;
build_eap(tx->eap, EapResponse, rx->eap->id, 1);
s->eapResp = 1;
show_notification(s, &rx->eap->data[1] , nhgets(rx->eap->ln)-EAPHDR+1);
break;
case EapTpTtls:
tlssucces = 0;
tlsfailed = 0;
len = processTTLS(rx->eap->data, nhgets(rx->eap->ln)-EAPHDR, s->eapExpectTtlsStart, tx->eap->data, ETHERMAXTU-ETHERHDR-EAPOLHDR-EAPHDR, &tlssucces, &tlsfailed);
if (tlsfailed)
loglog("processTTLS failed");
s->eapExpectTtlsStart = 0;
if (debug) print("processTTLS returns len=%d\n", len);
if (len > 0) {
build_eap(tx->eap, EapResponse, rx->eap->id, len);
s->eapResp = 1;
} else
s->eapNoResp = 1;
break;
case EapTpNak: // only allowed in responses
case EapTpExtp:
case EapTpExus:
default:
// tell we can't deal with this type; tell we can only do ttls
tx->eap->data[0] = EapTpNak;
tx->eap->data[1] = EapTpTtls;
build_eap(tx->eap, EapResponse, rx->eap->id, 1+1);
s->eapResp = 1;
break;
}
break;
default:
logall("getSuppRsp unexpected eap type %d", rx->eap->code);
break;
}
if (s->eapResp) {
memcpy(tx->ether->s, rx->ether->d, 6);
memcpy(tx->ether->d, rx->ether->s, 6);
memcpy(tx->ether->t, rx->ether->t, 2);
tx->eapol->ver = rx->eapol->ver;
tx->eapol->tp = rx->eapol->tp;
memcpy(tx->eapol->ln, tx->eap->ln, 2);
tx->e = tx->eapol->data + nhgets(tx->eap->ln);
}
if (!(s->eapResp || s->eapNoResp || s->eapSuccess || s->eapFail))
logall("internal error - no eap result set\n");
// prepare for reuse
memset(rx->b, 0, Pktlen);
rx->e = 0; // or rx->e = rx->b; ???
s->eapReq = 0;
if (debug) print("getSuppRsp done eapResp=%d eapNoResp=%d\n", s->eapResp, s->eapNoResp);
}
// transmit EAP-Packet EAPOL frame to Authenticator
static void
txSuppRsp(PAEstate *s)
{
int n, l, len;
Packet *p;
p = s->txEtherEap;
len = p->e - p->b;
l = (len > ETHERMINTU) ? len : ETHERMINTU;
if (debug) print("txSuppRsp writing to ether l=%d L=%d\n", l, len);
if (p->eapol->tp == EapolTpEap &&
p->eap->code == EapResponse &&
p->eap->data[0] == EapTpIdentity) {
logall("sending eap external identity");
s->eapidTime = nsec();
}
n = write(s->etherfd, p->b, l);
if (n != l)
logall("txSuppRsp: written %d of %d: %r", n, l);
loglog("txSuppRsp: written %d", n);
}
static void
btrans(PAEstate *s, int new)
{
loglog("back trans: %s -> %s", (s->backState>=0)?bnames[s->backState]:"-", bnames[new]);
s->backState = new;
s->backTime = nsec();
}
static int
back(PAEstate *s)
{
Packet *rx;
int done;
Alt a[] = {
/* c v op */
{s->etherchan, &rx, CHANRCV},
{s->timerchan, nil, CHANRCV},
{s->statuschan, nil, CHANRCV},
{nil, nil, CHANEND},
};
if (s->backState != Initialize && (s->initialize || s->suppAbort))
btrans(s, Initialize);
switch(s->backState) {
case Initialize:
abortSupp(s);
s->suppAbort = 0;
if (!s->initialize && !s->suppAbort) {
} else
logall("back Initialize: should not happen");
btrans(s, Idle);
break;
case Idle:
send(s->backdone, nil);
recv(s->backstart, nil);
if (s->eapolEap)
btrans(s, Request);
else if(s->eapSuccess)
btrans(s, Success);
else if(s->eapFail)
btrans(s, Fail);
else if(s->suppAbort )
btrans(s, Initialize);
else
logfatal(0, "back Idle: should not happen");
break;
case Request:
s->eapReq = 1;
getSuppRsp(s);
if (s->eapResp)
btrans(s, Response);
else if (s->eapNoResp)
btrans(s, Receive);
else if (s->eapFail)
btrans(s, Fail);
else if (s->eapSuccess)
btrans(s, Success);
else if(s->suppAbort )
btrans(s, Initialize);
else
logfatal(0, "back Request: should not happen");
break;
case Response:
txSuppRsp(s);
s->eapResp = 0;
btrans(s, Receive);
break;
case Receive:
s->eapolEap = 0;
startTimer(s->authWhile, s->authPeriod);
done = 0;
while(!done)
switch(alt(a)) {
case 0: /* eap received */
loglog("back Receive eap received");
done = 1;
s->rxEtherEap = rx;
s->eapolEap = 1;
btrans(s, Request);
break;
case 1: /* timer expiration event */
// loglog("back Receive timer tick");
tickTimer(s->authWhile);
if (s->authWhile->counter == 0) {
logall("authWhile timer expired");
done = 1;
btrans(s, Timeout);
}
break;
case 2: /* eapSuccess or eapFail */
loglog("back Receive eapSuccess or eapFail");
done = 1;
if (s->eapFail)
btrans(s, Fail);
else if (s->eapSuccess)
btrans(s, Success);
else
logfatal(0, "back Receive 2: should not happen");
break;
default:
logfatal(0, "back Receive: can't happen");
}
resetTimer(s->authWhile);
s->eapNoResp = 0;
break;
case Success:
// try to avoid race: first set vars, then unset s->eapSuccess
s->suppSuccess = 1;
s->keyRun=1;
s->portValid = 1; // we should actually check this
s->eapSuccess = 0;
btrans(s, Idle);
break;
case Fail:
s->suppFail = 1;
s->eapFail = 0;
btrans(s, Idle);
break;
case Timeout:
s->suppTimeout = 1;
btrans(s, Idle);
break;
}
return s->backState;
}
static void
backproc(void *arg)
{
PAEstate *s;
s = arg;
for(;;) {
back(s);
}
}
// ========== Supplicant PAE state machine
static void
waitUntilUserLoggedOn(PAEstate *s)
{
s->userLogoff = 0;
}
static void
waitUntilPortEnabled(PAEstate *s)
{
s->portEnabled = 1;
}
static void
acknowledgeStart(PAEstate *s)
{
loglog("------ restarting ------");
syslog(0, logname, "restarting");
s->restartTime = nsec();
}
// build EAPOL-Start frame and transmit to Authenticator
static void
txStart(PAEstate *s)
{
Packet *tx;
// get fresh ap mac - we may have roamed
if (apetheraddr(s->apmac, s->etherdir) < 0)
logfatal(0, "could not read access point ether address from %s", s->etherdir);
loglog("sending EAPOL Start frame to %E", s->apmac);
tx = s->pktt;
s->txEtherEap = tx;
memset(s->txEtherEap->b, 0, Pktlen);
tx->eapol->ver = EapolVersion;
tx->eapol->tp = EapolTpStart;
memset(tx->eapol->ln, 0, 2);
memcpy(tx->ether->s, s->ourmac, 6);
memcpy(tx->ether->d, s->apmac, 6);
hnputs(tx->ether->t, ETEAPOL);
tx->e = tx->eapol->data;
txSuppRsp(s);
}
// build EAPOL-Logoff frame and transmit to Authenticator
static void
txLogoff(PAEstate *s)
{
USED(s);
}
static void
ptrans(PAEstate *s, int new)
{
loglog("pae trans: %s -> %s", (s->paeState>=0)?paenames[s->paeState]:"-", paenames[new]);
s->paeState = new;
s->paeTime = nsec();
}
static int
pae(PAEstate *s)
{
int val, res;
Packet *rx;
int done;
Alt a_c[] = {
/* c v op */
{s->etherchan, &rx, CHANRCV},
{s->timerchan, nil, CHANRCV},
{s->statuschan, nil, CHANRCV},
{nil, nil, CHANEND},
};
Alt a_h[] = {
/* c v op */
{s->etherchan, &rx, CHANRCV},
{s->timerchan, nil, CHANRCV},
{nil, nil, CHANEND},
};
Alt a_a[] = {
/* c v op */
{s->etherchan, &rx, CHANRCV},
{s->portchan, &val, CHANRCV},
{nil, nil, CHANEND},
};
// if (debug) print("_");
//print("pae: %s\n", (s->paeState>=0)?paenames[s->paeState]:"-");
if (s->paeState!=Logoff && (s->userLogoff && !s->logoffSent && s->portEnabled && !s->initialize))
ptrans(s, Logoff);
else if (s->paeState!=Disconnected && ((s->portControl==Auto && s->sPortMode!=s->portControl) || s->initialize || !s->portEnabled))
ptrans(s, Disconnected);
else if (s->paeState!=ForceAuth && (s->portControl==ForceAuthorized && s->sPortMode!=ForceAuthorized && s->portEnabled && !s->initialize))
ptrans(s, ForceAuth);
else if (s->paeState!=ForceUnauth && (s->portControl==ForceUnauthorized && s->sPortMode!=ForceUnauthorized && s->portEnabled && !s->initialize))
ptrans(s, ForceUnauth);
switch(s->paeState) {
case Logoff:
txLogoff(s);
s->logoffSent = 1;
s->suppPortStatus = Unauthorized;
waitUntilUserLoggedOn(s); // s->userLogoff = 0
ptrans(s, Disconnected);
break;
case Disconnected:
s->sPortMode = Auto;
s->startCount = 0;
s->logoffSent = 0;
s->suppPortStatus = Unauthorized;
s->suppAbort = 1;
send(s->backstart, nil);
recv(s->backdone, nil);
waitUntilPortEnabled(s); // s->portEnabled = 1
ptrans(s, Connecting);
break;
case Connecting:
s->startCount ++;
s->eapolEap = 0;
clearlog(getNotesbuf()); // when should we do this???
txStart(s);
startTimer(s->startWhen, s->startPeriod);
done = 0;
while(!done)
switch(res = alt(a_c)) {
case 0: /* eap received */
loglog("pae Connecting eap received");
done = 1;
s->rxEtherEap = rx;
s->eapolEap = 1;
ptrans(s, Restart);
break;
case 1: /* timer tick event */
// loglog("pae Connecting startWhen timer tick");
tickTimer(s->startWhen);
if (s->startWhen->counter == 0) {
logall("startWhen timer expired");
done = 1;
if (s->startCount < s->maxStart)
ptrans(s, Connecting);
else if (s->startCount >= s->maxStart && s->portValid) {
logall("startCount >= maxStart (==%d), assume auth-ed", s->maxStart);
ptrans(s, Authenticated);
} else if (s->startCount >= s->maxStart) {
logall("startCount >= maxStart (==%d), but port not valid", s->maxStart);
ptrans(s, Held);
} else
logfatal(0, "pae Connecting 0: should not happen");
}
break;
case 2: /* eapSuccess or eapFail */
loglog("pae Connecting eapSuccess or eapFail");
done = 1;
if (s->eapSuccess || s->eapFail)
ptrans(s, Authenticating);
else
logfatal(0, "pae Connecting 3: should not happen");
break;
default:
logfatal(0, "pae Connecting can't happen:%d", res);
}
resetTimer(s->startWhen);
break;
case Authenticating:
s->startCount = 0;
s->suppSuccess = 0;
s->suppFail = 0;
s->suppTimeout = 0;
s->keyRun = 0;
s->keyDone = 0;
send(s->backstart, nil);
recv(s->backdone, nil);
if (s->suppSuccess && s->portValid)
ptrans(s, Authenticated);
else if(s->suppSuccess)
USED(s); // ???
else if (s->suppFail || (s->keyDone && !s->portValid))
ptrans(s, Held);
else if (s->suppTimeout)
ptrans(s, Connecting);
else
logfatal(0, "pae Authenticating: should not happen");
break;
case Held:
startTimer(s->heldWhile, s->heldPeriod);
s->suppPortStatus = Unauthorized;
done = 0;
while(!done)
switch(res = alt(a_h)) {
case 0: /* eap received */
loglog("pae Held eap received");
done = 1;
s->rxEtherEap = rx;
s->eapolEap = 1;
ptrans(s, Restart);
break;
case 1: /* timer expiration event */
// loglog("pae Held timer tick");
tickTimer(s->heldWhile);
if (s->heldWhile->counter == 0) {
logall("heldWhile timer expired");
done = 1;
ptrans(s, Connecting);
}
break;
default:
logfatal(0, "pae Held can't happen:%d", res);
}
resetTimer(s->heldWhile);
break;
case Authenticated:
s->suppPortStatus = Authorized;
switch(res = alt(a_a)) {
case 0: /* eap received */
loglog("pae Authenticated eap received");
if (s->portValid) {
s->rxEtherEap = rx;
s->eapolEap = 1;
ptrans(s, Restart);
}
break;
case 1: /* port validity changed */
loglog("pae Authenticated port validity changed");
s->portValid = val;
if (!s->portValid)
ptrans(s, Disconnected);
break;
default:
logfatal(0, "pae Authenticated can't happen:%d", res);
}
break;
case Restart:
acknowledgeStart(s);
ptrans(s, Authenticating);
break;
case ForceAuth:
s->suppPortStatus = Authorized;
s->sPortMode = ForceAuthorized;
break;
case ForceUnauth:
s->suppPortStatus = Unauthorized;
s->sPortMode = ForceUnauthorized;
// no check??
txLogoff(s);
s->logoffSent = 1;
break;
}
//print("pae return: %s\n", paenames[s->paeState]);
return s->paeState;
}
// ========== run state machines
static void
paeproc(void *arg)
{
PAEstate *s;
s = arg;
for(;;) {
pae(s);
}
}
// ========== fs support
typedef struct timeInfo {
int id;
vlong time;
} timeInfo;
static int
cmptimenfo(void*a, void*b)
{
timeInfo *ta, *tb;
vlong td;
ta = a ;
tb = b;
if (ta->time == 0 && tb->time > 0)
return 1;
if (ta->time > 0 && tb->time == 0)
return -1;
td = ta->time - tb->time;
if (td > 0)
return 1;
if (td < 0)
return -1;
return ta->id - tb->id;
}
enum {
InitTime = 0,
StartTime,
PaeTime,
BackTime,
eapidTime,
verdictTime,
ph1startTime,
ph1doneTime,
ph2startTime,
ph2doneTime,
KeyTime,
NoteTime,
NTime,
};
static char *timeNames[] = {
[InitTime] "Init",
[StartTime] "Start",
[PaeTime] "Pae",
[BackTime] "Back",
[eapidTime] "eapid",
[verdictTime] "verdict",
[ph1startTime] "ph1start",
[ph1doneTime] "ph1done",
[ph2startTime] "ph2start",
[ph2doneTime] "ph2done",
[KeyTime] "Key",
[NoteTime] "Note",
[NTime] "",
};
static timeInfo t[NTime];
static timeInfo*
getPAETimes(PAEstate *s)
{
t[InitTime].time = s->startTime;
t[InitTime].id = InitTime;
t[StartTime].time = s->restartTime;
t[StartTime].id = StartTime;
t[PaeTime].time = s->paeTime;
t[PaeTime].id = PaeTime;
t[BackTime].time = s->backTime;
t[BackTime].id = BackTime;
t[eapidTime].time = s->eapidTime;
t[eapidTime].id = eapidTime;
t[verdictTime].time = s->verdictTime;
t[verdictTime].id = verdictTime;
t[ph1startTime].time = s->phase[0].startTime;
t[ph1startTime].id = ph1startTime;
t[ph1doneTime].time = s->phase[0].doneTime;
t[ph1doneTime].id = ph1doneTime;
t[ph2startTime].time = s->phase[1].startTime;
t[ph2startTime].id = ph2startTime;
t[ph2doneTime].time = s->phase[1].doneTime;
t[ph2doneTime].id = ph2doneTime;
t[KeyTime].time = s->keyTime;
t[KeyTime].id = KeyTime;
t[NoteTime].time = s->noteTime;
t[NoteTime].id = NoteTime;
t[NTime].time = 0;
t[NTime].id = NTime;
qsort(t, NTime, sizeof(timeInfo), cmptimenfo);
return t;
}
void
getPAEStatus(char *b, int n)
{
PAEstate *s;
timeInfo *t;
int i;
s = &theState;
*b = '\0';
seprint(b+strlen(b), b+n, "Verdict:\t%s\n",
((s->eapSuccess || s->suppSuccess) ? "success" :
((s->eapFail || s->suppFail) ? "fail" : "")));
seprint(b+strlen(b), b+n, "PaeState: %s\n", paenames[s->paeState]);
seprint(b+strlen(b), b+n, "BackState: %s\n", bnames[s->backState]);
seprint(b+strlen(b), b+n, "EapId Prompt: %s\n", getstring(s->prompt));
seprint(b+strlen(b), b+n, "EapId Options: %s\n", getstring(s->options));
seprint(b+strlen(b), b+n, "ph1type: %s\n", getstring(s->phase[0].type));
seprint(b+strlen(b), b+n, "ph2type: %s\n", getstring(s->phase[1].type));
t = getPAETimes(s);
for (i=0; i < NTime; i++)
seprint(b+strlen(b), b+n, "%s:\t%s\n", timeNames[t[i].id], nsctime(t[i].time));
seprint(b+strlen(b), b+n, "startCount:\t%d\n", s->startCount);
seprint(b+strlen(b), b+n, "maxStart:\t%d\n", s->maxStart);
seprint(b+strlen(b), b+n, "heldWhile:\t%d\n", timerVal(s->heldWhile));
seprint(b+strlen(b), b+n, "heldPeriod:\t%d\n", s->heldPeriod);
seprint(b+strlen(b), b+n, "startWhen:\t%d\n", timerVal(s->startWhen));
seprint(b+strlen(b), b+n, "startPeriod:\t%d\n", s->startPeriod);
seprint(b+strlen(b), b+n, "authWhile:\t%d\n", timerVal(s->authWhile));
seprint(b+strlen(b), b+n, "authPeriod:\t%d\n", s->authPeriod);
}
long
getChangetime(int qid)
{
PAEstate *s;
s = &theState;
switch(qid){
case Qstatus:
if (s->paeTime > s->backTime)
return nsec2sec(s->paeTime);
else
return nsec2sec(s->backTime);
case Qkeys:
return nsec2sec(s->keyTime);
case Qnote:
return nsec2sec(s->noteTime);
default:
return 0;
}
}
ReadBuf*
getKeysbuf(void)
{
PAEstate *s;
s = &theState;
return &s->keysbuf;
}
ReadBuf*
getNotesbuf(void)
{
PAEstate *s;
s = &theState;
return &s->notesbuf;
}
// phases are 1, 2, ...
// s->phase indices are 0, 1, ...
void
markPhaseStart(int phase, char *type)
{
PAEstate *s;
if (phase <= 0 || phase > nelem(s->phase))
return;
s = &theState;
free(s->phase[phase-1].type);
s->phase[phase-1].type = strdup(type);
s->phase[phase-1].startTime = nsec();
logall("auth phase %d starts: %s", phase, type);
}
void
markPhaseDone(int phase, char *type)
{
PAEstate *s;
if (phase <= 0 || phase > nelem(s->phase))
return;
s = &theState;
free(s->phase[phase-1].type);
s->phase[phase-1].type = strdup(type);
s->phase[phase-1].doneTime = nsec();
logall("auth phase %d done: %s", phase, type);
}
void
markPhaseResult(int phase, char *type, int success)
{
PAEstate *s;
if (phase <= 0 || phase > nelem(s->phase))
return;
s = &theState;
free(s->phase[phase-1].type);
s->phase[phase-1].type = strdup(type);
s->phase[phase-1].success = success;
s->phase[phase-1].doneTime = nsec();
logall("auth phase %d done: %s: %s", phase, type, success ? "success" : "fail");
}
// ========== main thing
static void*
newPacket(void)
{
Packet *p;
p = malloc(sizeof(Packet));
if (p == nil)
logfatal(1, "could not allocate Packet");
memset(p, 0, sizeof(Packet));
p->ether = (Ether*)p->b;
p->eapol = (Eapol*)p->ether->data;
p->eap = (Eap*)p->eapol->data;
return p;
}
static void
init(PAEstate *s, Timers *t)
{
int i;
memset(s, 0, sizeof(PAEstate));
s->heldPeriod = 60; //seconds
s->startPeriod = 30; //seconds
s->authPeriod = 30; //seconds
s->maxStart = 3;
s->heldWhile = addTimer(t, "heldWhile");
s->startWhen = addTimer(t, "startWhen");
s->authWhile = addTimer(t, "authWhile");
s->paeState = Disconnected;
s->backState = Initialize;
s->startTime = nsec();
s->paeTime = s->startTime;
s->backTime = s->startTime;
// s->noteTime = s->startTime;
// s->keyTime = s->startTime;
// s->restartTime = nsec();
s->lastEapId = -1;
for (i = 0; i < Npkt; i++)
s->pktr[i] = newPacket();
s->pktt = newPacket();
s->portchan = chancreate(sizeof(int), 0);
s->backstart = chancreate(sizeof(int), 0);
s->backdone = chancreate(sizeof(int), 0);
s->etherchan = chancreate(sizeof(Packet*), 0);
s->statuschan = chancreate(sizeof(int), 0);
s->timerchan = t->timerchan;
}
void
usage(void)
{
fprint(2, "usage: 8021x [-d] [-D] [-T] [-m mtpt] [-t /sys/lib/tls/xxx] [-x /sys/lib/tls/xxx.exclude]\n");
syslog(0, logname, "usage");
threadexitsall("usage");
}
void
threadmain(int argc, char *argv[])
{
char *thumbFile, *thumbFilex, *mtpt;
PAEstate *s;
Timers *t;
mtpt = "/net";
s = &theState;
t= &TheTimers;
fmtinstall('E', eipfmt);
thumbFile = nil;
thumbFilex = nil;
ARGBEGIN{
case 'd':
debug++;
break;
case 'D':
chatty9p++;
break;
case 'T':
debugTLS++;
break;
case 'm':
mtpt = ARGF();
break;
case 't':
thumbFile = EARGF(usage());
break;
case 'x':
thumbFilex = EARGF(usage());
break;
}ARGEND;
logname = "8021x";
loglog("====== starting =======");
syslog(0, logname, "starting");
if(mtpt == nil || argc > 0)
usage();
if(thumbFilex && !thumbFile)
logfatal(1, "specifying -x without -t is useless");
initTimers(t);
init(s, t);
initTTLS(thumbFile, thumbFilex, t);
initFs();
if(argc == 0)
s->etherdir = "/net/ether0";
else
s->etherdir = argv[0];
snprint(buf, Blen, "%s!0x888e", s->etherdir);
s->etherfd = dial(buf, 0, 0, &s->ethercfd);
if(s->etherfd < 0)
logfatal(1, "could not dial %s: %r", buf);
if (myetheraddr(s->ourmac, s->etherdir) < 0)
logfatal(1, "could not read own ether addres from %s", s->etherdir);
upwd = auth_getuserpasswd(auth_getkey, "proto=pass service=8021x-pap");
if (upwd) {
myId = upwd->user;
myPasswd = upwd->passwd;
} else
logfatal(1, "cannot get user/passwd");
// we could try to threadcreate backproc and paeproc
// (since they are coroutines),
// such that they are in a single process,
// as we had them before we turned this into an fs,
// but then the current process does not exit
// and things don't work.
// this may just be because of my limited understanding.
proccreate(tickproc, t, SMALLSTACK);
proccreate(backproc, s, STACK);
recv(s->backdone, nil);
proccreate(etherproc, s, SMALLSTACK);
s->portEnabled = 1;
proccreate(paeproc, s, STACK);
threadpostmountsrv(&fs, logname, mtpt, MAFTER);
threadexits(0);
}
|