// 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; 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; long backTime; // 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; long paeTime; // 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]; // 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(;;) { 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->e != rx->b) // sysfatal("assertion failed: rx->e != rx->b (n == %ld)\n", rx->e - rx->b); n = read(s->etherfd, rx->b, Pktlen); syslog(0, logname, "etherproc: into %d read %d", s->pkgidx, n); if(n <= 0) break; rx->e = rx->b + n; if (rx->e < rx->ether->data) { syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", n); continue; } if (nhgets(rx->ether->t) != ETEAPOL) { syslog(0, logname, "etherproc: skipping non-ETEAPOL %x", nhgets(rx->ether->t)); continue; } if (rx->e < rx->eapol->data) { syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", n); continue; } if (rx->e < rx->eapol->data + nhgets(rx->eapol->ln)) { syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", n); continue; } switch(rx->eapol->tp) { case EapolTpEap: if (rx->e < rx->eap->data) { syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", n); continue; } if (rx->e < rx->eapol->data + nhgets(rx->eap->ln)) { syslog(0, logname, "etherproc: skipping short packet (ether len=%d)", 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->e - rx->ether->data); } 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", 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) { // 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; 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? 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, &rx->eap->data[1], p - &rx->eap->data[1]); syslog(0, logname, "received EAP Identity request, prompt=\"%s\" options=\"%s\"", prompt, buf); } else { memcpy(buf, &rx->eap->data[1], p - &rx->eap->data[1]); syslog(0, logname, "received EAP Identity request, data=\"%s\"", buf); } // 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 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->e = tx->eapol->data + 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->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; len = s->txEtherEap->e - s->txEtherEap->b; l = (len > ETHERMINTU) ? len : ETHERMINTU; if (debug) print("txSuppRsp writing to ether l=%d L=%d\n", l, len); 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; s->backTime = time(0); } 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 { 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: s->eapolEap = 0; startTimer(s->authWhile, s->authPeriod); done = 0; while(!done) switch(alt(a)) { case 0: /* eap received */ syslog(0, logname, "back Receive eap received"); done = 1; s->rxEtherEap = rx; s->eapolEap = 1; btrans(s, Request); break; case 1: /* timer expiration event */ // syslog(0, logname, "back Receive timer tick"); tickTimer(s->authWhile); if (s->authWhile->counter == 0) { syslog(0, logname, "back Receive timer expired"); done = 1; btrans(s, Timeout); } break; case 2: /* eapSuccess or eapFail */ syslog(0, logname, "back Receive eapSuccess or eapFail"); done = 1; 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->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) { syslog(0, logname, "pae trans: %s -> %s", (s->paeState>=0)?paenames[s->paeState]:"-", paenames[new]); s->paeState = new; s->paeTime = time(0); } 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; txStart(s); startTimer(s->startWhen, s->startPeriod); done = 0; while(!done) switch(res = alt(a_c)) { case 0: /* eap received */ syslog(0, logname, "pae Connecting eap received"); done = 1; s->rxEtherEap = rx; s->eapolEap = 1; ptrans(s, Restart); break; case 1: /* timer tick event */ // syslog(0, logname, "pae Connecting startWhen timer tick"); tickTimer(s->startWhen); if (s->startWhen->counter == 0) { syslog(0, logname, "pae Connecting startWhen timer expired"); done = 1; 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"); done = 1; 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; done = 0; while(!done) switch(res = alt(a_h)) { case 0: /* eap received */ syslog(0, logname, "pae Held eap received"); done = 1; s->rxEtherEap = rx; s->eapolEap = 1; ptrans(s, Restart); break; case 1: /* timer expiration event */ // syslog(0, logname, "pae Held timer tick"); tickTimer(s->heldWhile); if (s->heldWhile->counter == 0) { syslog(0, logname, "pae Held timer expired"); done = 1; 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 paeproc(void *arg) { PAEstate *s; s = arg; for(;;) { 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->paeTime = time(0); s->backState = Initialize; s->backTime = time(0); 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 getPAEStatus(char *b, int n) { PAEstate *s; char *ctim; s = &theState; *b = '\0'; seprint(b+strlen(b), b+n, "PaeState: %s\n", paenames[s->paeState]); ctim = ctime(s->paeTime); seprint(b+strlen(b), b+n, "PaeTime: %.*s\n", 15, ctim+4); seprint(b+strlen(b), b+n, "BackState: %s\n", bnames[s->backState]); ctim = ctime(s->backTime); seprint(b+strlen(b), b+n, "BackTime: %.*s\n", 15, ctim+4); } void usage(void) { fprint(2, "usage: 8021x [-d] [-D] [-T] [-m mtpt] [-t /sys/lib/tls/xxx] [-x /sys/lib/tls/xxx.exclude]\n"); 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"; syslog(0, logname, "====== starting ======="); if(mtpt == nil || argc > 0) usage(); 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, SMALLSTACK); proccreate(backproc, s, STACK); // threadcreate(backproc, s, STACK); recv(s->backdone, nil); proccreate(etherproc, s, SMALLSTACK); s->portEnabled = 1; proccreate(paeproc, s, STACK); threadpostmountsrv(&fs, logname, mtpt, MAFTER); exits(0); }