Plan 9 from Bell Labs’s /usr/web/sources/contrib/axel/8021x/v02/8021x.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


// 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;

	uchar *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;
	int n;
	Ether *ether;
	Eapol *eapol;
	Eap *eap;
	uchar*p;

	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);
		n = read(s->etherfd, s->pktr[s->pkgidx], Pktlen);
		syslog(0, logname, "etherproc: into %d read %d", s->pkgidx, n);
		// if (debug) print("etherproc: read %d\n", n);
		if(n <= 0)
			break;
		p = s->pktr[s->pkgidx];
		ether = (Ether*)p;
		if (nhgets(ether->t) != ETEAPOL) {
			print("etherproc: skipping non-ETEAPOL %x\n", nhgets(ether->t));
			continue;
		}
		eapol = (Eapol*)ether->data;
		if (debug) print("etherproc: read %d pktr[pkgidx]=%p eapol=%p ehsz=%x eapol type %d ver %d len %d\n", n, s->pktr[s->pkgidx], eapol, ETHERHDR, eapol->tp, eapol->ver, nhgets(eapol->ln));
		switch(eapol->tp){
		case EapolTpEap:
			// if (debug) print("etherproc: eap pkt =%p\n", s->pktr[s->pkgidx]);
			eap = (Eap*)eapol->data;
			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, n - (ether->data - ether->d));
			} 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", 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];
//	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;
	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;
	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)
{
	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;
}

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[])
{
	int i;
	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);
	}

	for(i=0; i<Npkt;i++)
		s->pktr[i] = malloc(Pktlen);
	s->pktt = malloc(Pktlen);

	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);
	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].