// 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 <thread.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 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];
Channel *etherchan;
Channel *statuschan;
Channel *timerchan;
Channel *portchan;
Channel *backstart;
Channel *backdone;
Packet *pktr[Npkt], *pktt;
int pkgidx;
int lastEapId;
} PAEstate;
// other
static char *mydefId=""; // hard coded defaults?
static char *mydefPasswd=""; // hard coded defaults?
static UserPasswd*upwd;
static uchar defmac[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03};
static char errbuf[Blen];
// ========== Port timers 'state machine' (8.2.3)
// see timer.c
// ========== receive eapol frames
static void
etherproc(void *arg)
{
PAEstate *s;
Packet *rx;
s = arg;
for(;;) {
syslog(0, logname, "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->n != 0)
// sysfatal("assertion failed: rx->n != 0 (rx->n == %d)\n", rx->n);
rx->n = read(s->etherfd, rx->b, Pktlen);
syslog(0, logname, "etherproc: into %d read %d", s->pkgidx, rx->n);
if(rx->n <= 0)
break;
rx->beyond = rx->b + rx->n;
if (rx->b + rx->n < rx->ether->data) {
syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", rx->n);
continue;
}
if (nhgets(rx->ether->t) != ETEAPOL) {
syslog(0, logname, "etherproc: skipping non-ETEAPOL %x", nhgets(rx->ether->t));
continue;
}
if (rx->b + rx->n < rx->eapol->data) {
syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", rx->n);
continue;
}
if (rx->b + rx->n < rx->eapol->data + nhgets(rx->eapol->ln)) {
syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", rx->n);
continue;
}
switch(rx->eapol->tp) {
case EapolTpEap:
if (rx->b + rx->n < rx->eap->data) {
syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", rx->n);
continue;
}
if (rx->b + rx->n < rx->eapol->data + nhgets(rx->eap->ln)) {
syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", rx->n);
continue;
}
if (s->lastEapId == rx->eap->id)
syslog(0, logname, "etherproc lastEapId==eap->id==%d", rx->eap->id);
s->lastEapId = rx->eap->id;
switch(rx->eap->code) {
case EapRequest:
syslog(0, logname, "- - - - Eap Request id=%d - - - - ", rx->eap->id);
syslog(0, logname, "etherproc: about to send %d ", s->pkgidx);
send(s->etherchan, &rx);
syslog(0, logname, "\tetherproc: done send %d ", s->pkgidx);
s->pkgidx = (s->pkgidx+1)%Npkt;
break;
case EapResponse:
syslog(0, logname, "- - - - Eap Response id=%d - - - - ", rx->eap->id);
break;
case EapSuccess:
syslog(0, logname, "- - - - success id=%d - - - -", rx->eap->id);
s->eapSuccess = 1;
send(s->statuschan, nil);
break;
case EapFailure:
syslog(0, logname, "- - - - fail id=%d - - - -", rx->eap->id);
s->eapFail = 1;
send(s->statuschan, nil);
break;
default:
syslog(0, logname, "- - - - unknown eap id=%d type=%d - - - - ", rx->eap->id, rx->eap->code);
break;
}
break;
case EapolTpStart:
syslog(0, logname, "etherproc: start (ignored)");
break;
case EapolTpLogoff:
syslog(0, logname, "etherproc: logoff (ignored)");
break;
case EapolTpKey:
if (s->keyRun || s->eapSuccess) {
syslog(0, logname, "- - - - key - - - -");
handleKey(s->ethercfd, rx->eapol, rx->n - (rx->ether->data - rx->b));
} else
syslog(0, logname, "etherproc: ignoring key (not authed yet)");
break;
case EapolTpAsf:
syslog(0, logname, "etherproc: asf (ignored)");
break;
default:
syslog(0, logname, "etherproc: unknown type%d", rx->eapol->tp);
break;
}
}
print("etherproc: oops read %d...\n", rx->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();
}
// (get info to) build response to most recent EAP request
static void
clear_eap(Eap*t)
{
memset(t, 0, sizeof(Eap));
}
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(uchar *s, int l)
{
char buf[2048];
// should do better: rfc3748 says:
// s contains UTF-8 encoded ISO 10646 [RFC2279].
memset(buf, 0, sizeof(s));
memcpy(buf, s, l);
syslog(0, logname, "notification: %s", buf);
}
static void
getSuppRsp(PAEstate *s)
{
// handle rxEtherEap
// build txEtherEap
Packet *rx, *tx;
uchar *p, *beyond;
char *ident, prompt[Pktlen], options[Pktlen];
int len;
int tlssucces, tlsfailed;
syslog(0, logname, "getSuppRsp %p", s->rxEtherEap);
if (s->eapResp || s->eapNoResp)
print("oops... getSuppRsp called while result previous of prev call pending\n");
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?
memset(prompt, 0, sizeof(prompt));
memset(options, 0, sizeof(options));
beyond = rx->eapol->data + nhgets(rx->eap->ln);
p = &rx->eap->data[1];
while (*p != '\0' && p < beyond)
p++;
if (*p == '\0' && p < beyond) {
memcpy(prompt, &rx->eap->data[1], p - &rx->eap->data[1]);
p++;
if (p < beyond)
memcpy(options, p, beyond - p);
}
// 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
syslog(0, logname, "received EAP Identity request, prompt=\"%s\" options=\"%s\"", prompt, options);
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(&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)
syslog(0, logname, "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:
if (debug) print("getSuppRsp unexpected eap type %d\n", 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->n = (tx->eapol->data - tx->b) + nhgets(tx->eap->ln);
}
if (!(s->eapResp || s->eapNoResp || s->eapSuccess || s->eapFail))
print("internal error - no eap result set\n");
// prepare for reuse
memset(rx->b, 0, Pktlen);
rx->n = 0;
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;
l = (s->txEtherEap->n > ETHERMINTU) ? s->txEtherEap->n : ETHERMINTU;
if (debug) print("txSuppRsp writing to ether l=%d L=%d\n", l, s->txEtherEap->n);
n = write(s->etherfd, s->txEtherEap->b, l);
if (n != l)
print("txSuppRsp: written %d of %d:%r", n, l);
syslog(0, logname, "txSuppRsp: written %d", n);
}
static void
btrans(PAEstate *s, int new)
{
syslog(0, logname, "back trans: %s -> %s", (s->backState>=0)?bnames[s->backState]:"-", bnames[new]);
s->backState = new;
}
static int
back(PAEstate *s)
{
Packet *rx;
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 {
syslog(0, logname, "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 {
fprint(2, "back Idle: should not happen\n");
syslog(0, logname, "back Idle: should not happen");
threadexitsall("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 {
fprint(2, "back Request: should not happen\n");
syslog(0, logname, "back Request: should not happen");
threadexitsall("back Request: should not happen");
}
break;
case Response:
txSuppRsp(s);
s->eapResp = 0;
btrans(s, Receive);
break;
case Receive:
startTimer(s->authWhile, s->authPeriod);
s->eapolEap = 0;
switch(alt(a)) {
case 0: /* eap received */
syslog(0, logname, "back Receive eap received");
s->rxEtherEap = rx;
s->eapolEap = 1;
btrans(s, Request);
break;
case 1: /* timer expiration event */
syslog(0, logname, "back Receive timer expired");
if (s->authWhile->counter == 0)
btrans(s, Timeout);
else {
fprint(2, "back Receive 1: should not happen\n");
syslog(0, logname, "back Receive 1: should not happen");
threadexitsall("back Receive 1: should not happen");
}
break;
case 2: /* eapSuccess or eapFail */
syslog(0, logname, "back Receive eapSuccess or eapFail");
if (s->eapFail)
btrans(s, Fail);
else if (s->eapSuccess)
btrans(s, Success);
else {
fprint(2, "back Receive 2: should not happen\n");
syslog(0, logname, "back Receive 2: should not happen");
threadexitsall("back Receive 2: should not happen");
}
break;
default:
fprint(2, "back Receive: can't happen\n");
syslog(0, logname, "back Receive: can't happen");
threadexitsall("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)
{
USED(s);
syslog(0, logname, "------ restarting ------");
}
// 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) {
snprint(errbuf, sizeof(errbuf), "could not read access point ether address from %s", s->etherdir);
syslog(0, logname, "%s", errbuf);
fprint(2, "%s\n", errbuf);
threadexitsall(errbuf);
}
syslog(0, logname, "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->n = tx->eapol->data - tx->b;
txSuppRsp(s);
}
// build EAPOL-Logoff frame and transmit to Authenticator
static void
txLogoff(PAEstate *s)
{
USED(s);
}
static void
ptrans(PAEstate *s, int new)
{
syslog(0, logname, "pae trans: %s -> %s", (s->paeState>=0)?paenames[s->paeState]:"-", paenames[new]);
s->paeState = new;
}
static int
pae(PAEstate *s)
{
int val, res;
Packet *rx;
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:
startTimer(s->startWhen, s->startPeriod);
s->startCount ++;
s->eapolEap = 0;
txStart(s);
switch(res = alt(a_c)) {
case 0: /* eap received */
syslog(0, logname, "pae Connecting eap received");
s->rxEtherEap = rx;
s->eapolEap = 1;
ptrans(s, Restart);
break;
case 1: /* timer expiration event */
syslog(0, logname, "pae Connecting timer expired");
if (s->startWhen->counter != 0) {
fprint(2, "pae Connecting 1: should not happen\n");
syslog(0, logname, "pae Connecting 1: should not happen");
threadexitsall("pae Connecting 1: should not happen");
} else if (s->startCount < s->maxStart)
ptrans(s, Connecting);
else if (s->startCount >= s->maxStart && s->portValid)
ptrans(s, Authenticated);
else if (s->startCount >= s->maxStart)
ptrans(s, Held);
else {
fprint(2, "pae Connecting 0: should not happen\n");
syslog(0, logname, "pae Connecting 0: should not happen");
threadexitsall("pae Connecting 0: should not happen");
}
break;
case 2: /* eapSuccess or eapFail */
syslog(0, logname, "pae Connecting eapSuccess or eapFail");
if (s->eapSuccess || s->eapFail)
ptrans(s, Authenticating);
else {
fprint(2, "pae Connecting 3: should not happen\n");
syslog(0, logname, "pae Connecting 3: should not happen");
threadexitsall("pae Connecting 3: should not happen");
}
break;
default:
fprint(2, "pae Connecting can't happen:%d\n", res);
syslog(0, logname, "pae Connecting can't happen:%d", res);
threadexitsall("can't happen");
}
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 {
fprint(2, "pae Authenticating: should not happen\n");
syslog(0, logname, "pae Authenticating: should not happen");
threadexitsall("pae Authenticating: should not happen");
}
break;
case Held:
startTimer(s->heldWhile, s->heldPeriod);
s->suppPortStatus = Unauthorized;
switch(res = alt(a_h)) {
case 0: /* eap received */
syslog(0, logname, "pae Held eap received");
s->rxEtherEap = rx;
s->eapolEap = 1;
ptrans(s, Restart);
break;
case 1: /* timer expiration event */
syslog(0, logname, "pae Held timer expired");
if (s->heldWhile->counter != 0) {
fprint(2, "pae Held: should not happen\n");
syslog(0, logname, "pae Held: should not happen");
threadexitsall("pae Held: should not happen");
} else
ptrans(s, Connecting);
break;
default:
fprint(2, "pae Held can't happen:%d\n", res);
syslog(0, logname, "pae Held can't happen:%d", res);
threadexitsall("pae Held can't happen");
}
resetTimer(s->heldWhile);
break;
case Authenticated:
s->suppPortStatus = Authorized;
switch(res = alt(a_a)) {
case 0: /* eap received */
syslog(0, logname, "pae Authenticated eap received");
if (s->portValid) {
s->rxEtherEap = rx;
s->eapolEap = 1;
ptrans(s, Restart);
}
break;
case 1: /* port validity changed */
syslog(0, logname, "pae Authenticated port validity changed");
s->portValid = val;
if (!s->portValid)
ptrans(s, Disconnected);
break;
default:
fprint(2, "pae Authenticated can't happen:%d\n", res);
syslog(0, logname, "pae Authenticated can't happen:%d", res);
threadexitsall("pae Authenticated can't happen");
}
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
update(PAEstate *s)
{
int ps;
ps = -2;
while(ps != s->paeState)
ps = pae(s);
}
// ========== main thing
static void*
newPacket(void)
{
Packet *p;
p = malloc(sizeof(Packet));
if (p == nil)
sysfatal("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->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 /sys/lib/tls/xxx] [-x /sys/lib/tls/xxx.exclude]\n");
exits("usage");
}
void
threadmain(int argc, char *argv[])
{
char *thumbFile, *thumbFilex;
PAEstate theState, *s;
Timers TheTimers, *t;
char buf[Blen];
s = &theState;
t= &TheTimers;
fmtinstall('E', eipfmt);
thumbFile = nil;
thumbFilex = nil;
ARGBEGIN{
case 'd':
debug++;
break;
case 'D':
debugTLS++;
break;
case 't':
thumbFile = EARGF(usage());
break;
case 'x':
thumbFilex = EARGF(usage());
break;
}ARGEND;
logname = "8021x";
syslog(0, logname, "====== starting =======");
if(thumbFilex && !thumbFile) {
snprint(errbuf, sizeof(errbuf), "specifying -x without -t is useless");
syslog(0, logname, "%s", errbuf);
fprint(2, "%s\n", errbuf);
threadexitsall(errbuf);
}
initTimers(t);
init(s, t);
initTTLS(thumbFile, thumbFilex, t);
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) {
snprint(errbuf, sizeof(errbuf), "could not dial %s: %r", buf);
syslog(0, logname, "%s", errbuf);
fprint(2, "%s\n", errbuf);
threadexitsall(errbuf);
}
if (myetheraddr(s->ourmac, s->etherdir) < 0) {
snprint(errbuf, sizeof(errbuf), "could not read own ether addres from %s", s->etherdir);
syslog(0, logname, "%s", errbuf);
fprint(2, "%s\n", errbuf);
threadexitsall(errbuf);
}
upwd = auth_getuserpasswd(auth_getkey, "proto=pass service=8021x-pap");
if (upwd) {
myId = upwd->user;
myPasswd = upwd->passwd;
} else {
sysfatal("cannot get user/passwd");
}
proccreate(clockproc, t, STACK);
proccreate(backproc, s, STACK);
recv(s->backdone, nil);
proccreate(etherproc, s, STACK);
s->portEnabled = 1;
for(;;) {
update(s);
}
}
|