// 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 #include #include #include #include <9p.h> #include #include #include #include #include #include "dat.h" #include "fns.h" typedef enum PortControl { Auto, ForceUnauthorized, ForceAuthorized, } PortControl; char * sPortControl[] = { "Auto", "ForceUnauthorized", "ForceAuthorized", }; typedef enum AuthState { Unauthorized, Authorized, } AuthState; char *sAuthState[] = { "Unauthorized", "Authorized", }; // 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, "AccessPoint: %E\n", s->apmac); 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)); seprint(b+strlen(b), b+n, "PaeState: %s\n", paenames[s->paeState]); seprint(b+strlen(b), b+n, "eapRestart: %d\n", s->eapRestart); seprint(b+strlen(b), b+n, "logoffSent: %d\n", s->logoffSent); seprint(b+strlen(b), b+n, "userLogoff: %d\n", s->userLogoff); seprint(b+strlen(b), b+n, "sPortMode: %s\n", sPortControl[s->sPortMode]); seprint(b+strlen(b), b+n, "BackState: %s\n", bnames[s->backState]); seprint(b+strlen(b), b+n, "eapNoResp: %d\n", s->eapNoResp); seprint(b+strlen(b), b+n, "eapReq: %d\n", s->eapReq); seprint(b+strlen(b), b+n, "eapResp: %d\n", s->eapResp); seprint(b+strlen(b), b+n, "eapNoResp: %d\n", s->eapNoResp); seprint(b+strlen(b), b+n, "eapFail: %d\n", s->eapFail); seprint(b+strlen(b), b+n, "eapSuccess: %d\n", s->eapSuccess); seprint(b+strlen(b), b+n, "initialize: %d\n", s->initialize); seprint(b+strlen(b), b+n, "portControl: %s\n", sPortControl[s->portControl]); seprint(b+strlen(b), b+n, "portEnabled: %d\n", s->portEnabled); seprint(b+strlen(b), b+n, "portStatus: %s\n", sAuthState[s->portStatus]); seprint(b+strlen(b), b+n, "suppPortStatus: %s\n", sAuthState[s->suppPortStatus]); seprint(b+strlen(b), b+n, "portValid: %d\n", s->portValid); seprint(b+strlen(b), b+n, "suppSuccess: %d\n", s->suppSuccess); seprint(b+strlen(b), b+n, "suppFail: %d\n", s->suppFail); seprint(b+strlen(b), b+n, "suppTimeout: %d\n", s->suppTimeout); 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. procrfork(tickproc, t, SMALLSTACK, RFNAMEG|RFNOTEG); procrfork(backproc, s, STACK, RFNAMEG|RFNOTEG); recv(s->backdone, nil); procrfork(etherproc, s, SMALLSTACK, RFNAMEG|RFNOTEG); s->portEnabled = 1; procrfork(paeproc, s, STACK, RFNAMEG|RFNOTEG); threadpostmountsrv(&fs, logname, mtpt, MAFTER); threadexits(0); }