// 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 #include #include #include #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; ipktr[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); } }