// 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)
int 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)
int heldWhile;
int 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;
int rcvdEtherEap;
uchar *txEtherEap;
int txEtherLen;
Channel *etherchan;
char *etherdir;
int etherfd, ethercfd;
uchar ourmac[6];
uchar apmac[6];
Channel *statuschan;
Channel *timerstart;
Channel *timerchan;
Channel *portchan;
Channel *backstart;
Channel *backdone;
Packet *pktr[Npkt], *pktt;
int pkgidx;
} PAEstate;
// other
static char *mydefId=""; // hard coded defaults?
static char *mydefPasswd=""; // hard coded defaults?
static UserPasswd*upwd;
static char ext_identity[] = "";
static char *int_identity;
static uchar defmac[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03};
static char errbuf[Blen];
// ========== Port timers 'state machine' (8.2.3)
static void
tick(PAEstate *s)
{
if (s->authWhile >= 0)
s->authWhile--;
if (s->heldWhile >= 0)
s->heldWhile--;
if (s->startWhen >= 0)
s->startWhen--;
}
static void
clockproc(void *arg)
{
PAEstate *s;
s = arg;
for(;;){
recv(s->timerstart, nil);
for (;;) {
if (s->authWhile == -1 && s->heldWhile == -1 && s->startWhen == -1)
break;
sleep(1000);
if (s->authWhile == -1 && s->heldWhile == -1 && s->startWhen == -1)
break;
tick(s);
if (s->authWhile == 0 || s->heldWhile == 0 || s->startWhen == 0) {
send(s->timerchan, nil);
break;
}
}
}
}
static char*
timerName(PAEstate *s, int *p)
{
if (p == &s->authWhile)
return "authWhile";
if (p == &s->heldWhile)
return "heldWhile";
if (p == &s->startWhen)
return "startWhen";
return "unknown";
}
static void
startTimer(PAEstate *s, int *p, int val)
{
syslog(0, logname, "startTimer %s to %d", timerName(s, p), val);
if (s->authWhile >= 0)
syslog(0, logname, "startTimer oops: %s runs, val=%d", timerName(s, &s->authWhile), s->authWhile);
if (s->heldWhile >= 0)
syslog(0, logname, "startTimer oops: %s runs, val=%d", timerName(s, &s->heldWhile), s->heldWhile);
if (s->startWhen >= 0)
syslog(0, logname, "startTimer oops: %s runs, val=%d", timerName(s, &s->startWhen), s->startWhen);
*p = val;
if (nbsend(s->timerstart, nil) == 0)
syslog(0, logname, "startTimer oops: could not timerstart");
}
static void
resetTimer(PAEstate *s, int *p)
{
syslog(0, logname, "resetTimer %s (val was %d)", timerName(s, p), *p);
*p = -1;
syslog(0, logname, "\tresetTimer %s (val is %d)", timerName(s, p), *p);
}
// ========== receive eapol frames
static void
etherproc(void *arg)
{
PAEstate *s;
Packet *r;
Ether *ether;
Eapol *eapol;
Eap *eap;
s = arg;
for(;;){
// if (debug) print("etherproc: waiting for %d\n", s->etherfd);
syslog(0, logname, "etherproc: waiting for %d into %d", s->etherfd, s->pkgidx);
r = s->pktr[s->pkgidx];
r->n = read(s->etherfd, r->b, Pktlen);
syslog(0, logname, "etherproc: into %d read %d", s->pkgidx, r->n);
// if (debug) print("etherproc: read %d\n", n);
if(r->n <= 0)
break;
ether = (Ether*)r->b;
if (nhgets(ether->t) != ETEAPOL) {
syslog(0, logname, "etherproc: skipping non-ETEAPOL %x", nhgets(ether->t));
continue;
}
if (r->n <= ETHERHDR + EAPOLHDR) {
syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", r->n);
continue;
}
eapol = (Eapol*)ether->data;
if (r->n < ETHERHDR + EAPOLHDR + nhgets(eapol->ln)) {
syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", r->n);
continue;
}
switch(eapol->tp){
case EapolTpEap:
// if (debug) print("etherproc: eap pkt =%p\n", r->b);
if (r->n <= ETHERHDR + EAPOLHDR + EAPHDR) {
syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", r->n);
continue;
}
eap = (Eap*)eapol->data;
if (r->n < ETHERHDR + EAPOLHDR + nhgets(eap->ln)) {
syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", r->n);
continue;
}
switch(eap->code) {
case EapRequest:
syslog(0, logname, "etherproc: about to send %d ", s->pkgidx);
send(s->etherchan, &s->pkgidx);
syslog(0, logname, "\tetherproc: done send %d ", s->pkgidx);
s->pkgidx=(s->pkgidx+1)%Npkt;
break;
case EapResponse:
if (debug) print("etherproc EapResponse\n");
break;
case EapSuccess:
if (debug) print("etherproc EapSuccess\n");
syslog(0, logname, "- - - - success - - - -");
s->eapSuccess = 1;
send(s->statuschan, nil);
break;
case EapFailure:
if (debug) print("etherproc EapFailure\n");
syslog(0, logname, "- - - - fail - - - -");
s->eapFail = 1;
send(s->statuschan, nil);
break;
default:
if (debug) print("etherproc unknown eap type %d\n", 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, eapol, r->n - (ether->data - r->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\n", eapol->tp);
break;
}
}
print("etherproc: oops read %d...\n", r->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 rcvdEtherEap
// build txEtherEap
Ether *er, *et;
Eapol *lr, *lt;
Eap *r, *t;
uchar tp;
uchar *p, *br, *bt, *beyond;
char *ident, prompt[Pktlen], options[Pktlen];
int len;
int tlssucces, tlsfailed;
// if (debug) print("getSuppRsp eapResp=%d eapNoResp=%d\n", eapResp, eapNoResp);
syslog(0, logname, "getSuppRsp %d", s->rcvdEtherEap);
if (s->eapResp || s->eapNoResp)
print("oops... getSuppRsp called while result previous of prev call pending\n");
p = s->pktr[s->rcvdEtherEap]->b;
// if (debug) print("rcvdEtherEap=%d pkt=%p\n", rcvdEtherEap, p);
er = (Ether*)p;
lr = (Eapol*)er->data;
r = (Eap*)lr->data;
br = r->data;
// if (debug) print("getSuppRsp p=%p er=%p lr=%p r=%p br=%p\n", p, er,lr,r,br);
p = s->pktt->b;
s->txEtherEap = p;
memset(s->txEtherEap, 0, Pktlen);
et = (Ether*)p;
lt = (Eapol*)et->data;
t = (Eap*)lt->data;
bt = t->data;
// if (debug) print("getSuppRsp et=%p lt=%p t=%p bt=%p\n", et,lt,t,bt);
if (debug) print("getSuppRsp code=%d id=%d len=%d ", (uchar)r->code, (uchar)r->id, nhgets(r->ln));
switch(r->code){
case EapRequest:
tp = br[0];
if (debug) print("getSuppRsp EapRequest: %d \n", tp);
switch(tp){
case EapTpIdentity:
// data format: [ prompt ] [ '\0' piggy-backed-options ]
// show prompt? extract options?
memset(prompt, 0, sizeof(prompt));
memset(options, 0, sizeof(options));
beyond = lr->data + nhgets(r->ln);
p = br+1;
while (*p != '\0' && p < beyond)
p++;
if (*p == '\0' && p < beyond) {
memcpy(prompt, br, p - (br+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 (strcmp(ext_identity,"") != 0)
ident = ext_identity;
else if ((ident = strchr(int_identity, '@')) == nil)
ident = "";
bt[0] = EapTpIdentity;
memcpy(bt+1, ident, strlen(ident));
build_eap(t, EapResponse, r->id, 1+strlen(ident));
s->eapResp = 1;
s->eapExpectTtlsStart = 1;
break;
case EapTpNotification:
bt[0] = EapTpNotification;
build_eap(t, EapResponse, r->id, 1);
s->eapResp = 1;
show_notification(br+1, nhgets(r->ln)-EAPHDR+1);
break;
case EapTpTtls:
tlssucces = 0;
tlsfailed = 0;
len = processTTLS(br, nhgets(r->ln)-EAPHDR, s->eapExpectTtlsStart, bt, 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(t, EapResponse, r->id, len);
s->eapResp = 1;
} else
s->eapNoResp = 1;
break;
case EapTpNak: // only allowed in responses
case EapTpExtp:
case EapTpExus:
default:
bt[0] = EapTpNak;
bt[1] = EapTpTtls;
build_eap(t, EapResponse, r->id, 1+1);
s->eapResp = 1;
break;
}
break;
default:
if (debug) print("getSuppRsp unexpected eap type %d\n", r->code);
break;
}
if (s->eapResp){
memcpy(et->s, er->d, 6);
memcpy(et->d, er->s, 6);
memcpy(et->t, er->t, 2);
lt->ver = lr->ver;
lt->tp = lr->tp;
memcpy(lt->ln,t->ln,2);
s->txEtherLen = nhgets(t->ln)+EAPOLHDR+ETHERHDR;
}
if (!(s->eapResp || s->eapNoResp || s->eapSuccess || s->eapFail))
print("internal error - no eap result set\n");
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->txEtherLen>ETHERMINTU)?s->txEtherLen:ETHERMINTU;
if (debug) print("txSuppRsp writing to ether l=%d L=%d\n", l, s->txEtherLen);
n = write(s->etherfd, s->txEtherEap, l);
if (n != l)
print("txSuppRsp: writen %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]);
if (debug) print("back trans: %s -> %s\n", (s->backState>=0)?bnames[s->backState]:"-", bnames[new]);
s->backState = new;
}
static int
back(PAEstate *s)
{
int t, idx;
Alt a[] = {
/* c v op */
{s->etherchan, &idx, CHANRCV},
{s->timerchan, &t, CHANRCV},
{s->statuschan, nil, CHANRCV},
{nil, nil, CHANEND},
};
// if (debug) print("^");
//print("back: %s\n", s->backState, (s->backState>=0)?bnames[s->backState]:"-");
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, &s->authWhile, s->authPeriod);
s->eapolEap = 0;
switch(alt(a)){
case 0: /* eap received */
syslog(0, logname, "back Receive eap received");
s->rcvdEtherEap = idx;
s->eapolEap = 1;
btrans(s, Request);
break;
case 1: /* timer expiration event */
syslog(0, logname, "back Receive timer expired");
if (s->authWhile == 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, &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 or so
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;
}
//print("back return: %s\n", bnames[s->backState]);
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 ------");
}
// transmit EAPOL-Start frame to Authenticator
static void
txStart(PAEstate *s)
{
Ether *et;
Eapol *lt;
uchar *p;
// 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);
p = s->pktt->b;
s->txEtherEap = p;
memset(s->txEtherEap, 0, Pktlen);
et = (Ether*)p;
lt = (Eapol*)et->data;
lt->ver=EapolVersion;
lt->tp=EapolTpStart;
memset(lt->ln, 0, 2);
memcpy(et->s, s->ourmac, 6);
memcpy(et->d, s->apmac, 6);
hnputs(et->t, ETEAPOL);
s->txEtherLen = EAPOLHDR+ETHERHDR;
txSuppRsp(s);
}
// transmit EAPOL-Logoff frame 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]);
if (debug) print("pae trans: %s -> %s\n", (s->paeState>=0)?paenames[s->paeState]:"-", paenames[new]);
s->paeState = new;
}
static int
pae(PAEstate *s)
{
int t, idx, val, res;
Alt a_c[] = {
/* c v op */
{s->etherchan, &idx, CHANRCV},
{s->timerchan, &t, CHANRCV},
{s->statuschan, nil, CHANRCV},
{nil, nil, CHANEND},
};
Alt a_h[] = {
/* c v op */
{s->etherchan, &idx, CHANRCV},
{s->timerchan, &t, CHANRCV},
{nil, nil, CHANEND},
};
Alt a_a[] = {
/* c v op */
{s->etherchan, &idx, 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, &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->rcvdEtherEap = idx;
s->eapolEap = 1;
ptrans(s, Restart);
break;
case 1: /* timer expiration event */
syslog(0, logname, "pae Connecting timer expired");
if (s->startWhen != 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, &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, &s->heldWhile, s->heldPeriod);
s->suppPortStatus = Unauthorized;
switch(res = alt(a_h)){
case 0: /* eap received */
syslog(0, logname, "pae Held eap received");
s->rcvdEtherEap = idx;
s->eapolEap = 1;
ptrans(s, Restart);
break;
case 1: /* timer expiration event */
syslog(0, logname, "pae Held timer expired");
if (s->heldWhile != 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, &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->rcvdEtherEap = idx;
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
init(PAEstate *s)
{
int i;
memset(s, 0, sizeof(PAEstate));
s->heldPeriod = 60; //seconds
s->startPeriod = 30; //seconds
s->maxStart = 3;
s->paeState = Disconnected;
s->heldWhile = -1;
s->startWhen = -1;
s->authPeriod = 30; //seconds
s->backState = Initialize;
s->authWhile = -1;
for (i=0; i<Npkt; i++) {
s->pktr[i] = malloc(sizeof(Packet));
if (s->pktr[i] == nil)
sysfatal("could not allocate Packet");
memset(s->pktr[i], 0, sizeof(Packet));
}
s->pktt = malloc(sizeof(Packet));
if (s->pktt == nil)
sysfatal("could not allocate Packet");
memset(s->pktt, 0, sizeof(Packet));
}
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;
char buf[Blen];
s = &theState;
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;
init(s);
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);
}
if(argc == 0)
s->etherdir = "/net/ether0";
else
s->etherdir = argv[0];
logname = "8021x";
syslog(0, logname, "====== starting =======");
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 {
myId = mydefId;
myPasswd = mydefPasswd;
}
int_identity = myId;
s->paeState = Disconnected;
s->backState = Initialize;
// must make all channels before executing first alt
s->portchan = chancreate(sizeof(int), 0);
s->timerchan = chancreate(sizeof(int), 0);
s->timerstart = chancreate(sizeof(int), 0);
s->backstart = chancreate(sizeof(int), 0);
s->backdone = chancreate(sizeof(int), 0);
s->etherchan = chancreate(sizeof(int), 0);
s->statuschan = chancreate(sizeof(int), 0);
proccreate(clockproc, s, STACK);
proccreate(backproc, s, STACK);
recv(s->backdone, nil);
proccreate(etherproc, s, STACK);
s->portEnabled = 1;
initTTLS(thumbFile, thumbFilex);
for(;;) {
update(s);
}
}
|