#include <u.h>
#include <libc.h>
#include <thread.h>
#include <bio.h>
#include <ip.h>
#include <mp.h>
#include <libsec.h>
#include "dat.h"
#include "fns.h"
typedef struct TTLS {
uchar tp;
uchar flags;
uchar tln[4]; //optional, present if L flag set
} TTLS;
enum {
TtlsFlagL = 1<<7, // header contains tln field
TtlsFlagM = 1<<6, // more fragment(s) will follow for current msg
TtlsFlagS = 1<<5, // start of tls session
TtlsVersion = (1<<2)|(1<<1)|(1<<0),
TtlsShortHlen = 2, // without tln field
TtlsLongHlen = TtlsShortHlen+4, // with tln field
// TTLS state
Idle = 0,
Start,
Waiting,
Timeout,
Sending,
RecvAck,
Receiving,
SendAck,
Received,
};
char *snames[] = {
[Idle] "Idle",
[Start] "Start",
[Waiting] "Waiting",
[Timeout] "Timeout",
[Sending] "Sending",
[RecvAck] "RecvAck",
[Receiving] "Receiving",
[SendAck] "SendAck",
[Received] "Received",
};
typedef struct TTLSstate {
TLSconn tlsconn; // our handle to the tls connection
int tlspipe[2]; // double pipe over which we talk with our tls
// the stuff we read from it has to be fragmented, encapsulated and sent
// the fragments we receive have to be reassembled and then written to it
Channel *tlstidc; // used to send threadid from tlsClient
Channel *readc; // contains index in rbuf containing last msg read from tlspipe
Channel *eofc; // confirm eof on tlspipe
Channel *startclientc; // start new clientclient session
Channel *startreadc; // start new readproc session
Channel *timerc;
int tlsfd; // before tlsClient call: -2; after return: -1 (error) or >= 0 (ok)
int clientid; // thread id of clientproc
int readid; // thread id of readproc
int clientStarted; // did tlsClient start?
int clientReturned; // have we already received from clientproc?
Timer *ttlsWhile;
Timer *cleanupWhile;
int ttlsPeriod;
int cleanupPeriod;
int ttslTxLen; // length of frame we prepared for sending
int ttlsDone; // done processing the frame (and, if needed, preparing the response)?
int ttlsState; // ttls state we are in
uint ttlsVersion;
Buf rbuf[Nbuf]; // buffers read from tlspipe, to be sent/fragmented over ether
int ridx; // index of first free rbuf
Buf *ptoe; // current pipe-to-ether buf (one of rbuf)
Buf wbuf; // buffer read/ reassembled from ether, to be written to tlspipe
Buf *etop; // current ether-to-pipe buf (wbuf)
Thumbprint *thumbTable;
int inuse; // is cleanup needed at all?
uchar*theSessionCert;
int theSessionCertlen;
uchar* theSessionID;
int theSessionIDlen;
} TTLSstate;
static TTLSstate theTTLSstate;
static char errbuf[256];
static void
readproc(void *arg)
{
TTLSstate *s;
Buf *rx;
int fd, n;
s = arg;
syslog(0, logname, "readproc starts: %d", threadid());
while(recvul(s->startreadc)) {
syslog(0, logname, "readproc monitoring pipe: %d", s->tlspipe[0]);
fd = s->tlspipe[0];
for(;;) {
if (s->tlspipe[0] < 0) {
syslog(0, logname, "(readproc pipe not active: %d :%d)", fd, s->tlspipe[0]);
// break;
}
rx = &s->rbuf[s->ridx];
rx->p = rx->b;
rx->e = rx->b;
n = read(fd, rx->b, Buflen);
if (n < 0) {
syslog(0, logname, "readproc fail on pipe: %d: %r", fd);
break;
} else if (n == 0) {
syslog(0, logname, "readproc read 0 or eof on pipe: %d", fd);
break;
} else {
rx->e = rx->b +n;
syslog(0, logname, "readproc read from %d: %d", fd, n);
}
if (s->tlspipe[0] < 0) {
syslog(0, logname, "(readproc pipe no longer active: %d: %d)", fd, s->tlspipe[0]);
// break;
}
// syslog(0, logname, "readproc sending...");
send(s->readc, &rx);
s->ridx = (s->ridx+1)%Nbuf;
}
syslog(0, logname, "readproc sending eofc: %d: %d", fd, s->tlspipe[0]);
sendul(s->eofc, 0);
syslog(0, logname, "readproc restarts: %d: %d", fd, s->tlspipe[0]);
}
syslog(0, logname, "readproc exits: %d", threadid());
threadexits(nil);
}
static void
clientproc(void *arg)
{
TTLSstate *s;
int fd;
uchar hash[SHA1dlen];
s = arg;
syslog(0, logname, "clientproc starts: %d", threadid());
s->clientStarted = 1; // beyond this we are forced to send on tlstidc
syslog(0, logname, "clientproc (re)starting: tlspipe[1]=%d", s->tlspipe[1]);
if (s->tlspipe[1] <= 0) {
snprint(errbuf, sizeof(errbuf), "clientproc: no fd for tlsClient:%d", s->tlspipe[1]);
syslog(0, logname, "%s", errbuf);
fprint(2, "%s\n", errbuf);
threadexitsall(errbuf);
}
syslog(0, logname, "calling tlsClient");
fd = tlsClient(s->tlspipe[1], &s->tlsconn);
syslog(0, logname, "return tlsClient...");
if (s->clientid != threadid()) {
syslog(0, logname, "oops tlsClient schizophrenie clientid=%d threadid=%d", s->clientid, threadid());
fprint(2, "oops tlsClient schizophrenie clientid=%d threadid=%d", s->clientid, threadid());
} else {
s->tlsfd = fd;
}
syslog(0, logname, "tlsClient %d result: fd=%d", threadid(), fd);
if (fd < 0) {
syslog(0, logname, "tlsClient %d failed: %r", threadid());
fprint(2, "tlsClient %d failed: %r\n", threadid());
} else {
syslog(0, logname, "tlsClient %d ok fd=%d", threadid(), fd);
if (s->tlsconn.cert==nil || s->tlsconn.certlen<=0) {
syslog(0, logname, "server did not provide TLS certificate");
fprint(2, "server did not provide TLS certificate\n");
} else {
// X509dump(s->tlsconn.cert, s->tlsconn.certlen);
if (s->thumbTable != nil) {
sha1(s->tlsconn.cert, s->tlsconn.certlen, hash, nil);
if(!okThumbprint(hash, s->thumbTable)) {
syslog(0, logname, "server certificate %.*H not recognized", SHA1dlen, hash);
fprint(2, "server certificate %.*H not recognized\n", SHA1dlen, hash);
}
} else {
syslog(0, logname, "no thumbprint to check server certificate");
}
}
}
// clean up before we (implicitly) yield in the sendul
if (s->tlsconn.sessionID != nil)
free(s->tlsconn.sessionID);
s->tlsconn.sessionID = nil;
s->tlsconn.sessionIDlen = 0;
if (s->tlsconn.cert)
free(s->tlsconn.cert);
s->tlsconn.cert = nil;
s->tlsconn.certlen = 0;
sendul(s->tlstidc, threadid());
syslog(0, logname, "clientproc %d ... finished: fd=%d", threadid(), s->tlsfd);
syslog(0, logname, "clientproc exits: %d", threadid());
threadexits(nil);
}
static void
cleanup(TTLSstate* s)
{
int consumeRead, consumeClient, id, ret, timeout, n;
Buf *rx;
char dummy[1];
Alt a[] = {
/* c v op */
{s->readc, &rx, CHANRCV},
{s->eofc, nil, CHANRCV},
{s->tlstidc, &id, CHANRCV},
{s->timerc, nil, CHANRCV},
{nil, nil, CHANEND},
};
syslog(0, logname, "cleanup pre tlsfd=%d clientStarted=%d clientReturned=%d clientid=%d clientpid=%d tlspipe[0]=%d tlspipe[1]=%d", s->tlsfd, s->clientStarted, s->clientReturned, s->clientid, threadpid(s->clientid), s->tlspipe[0], s->tlspipe[1]);
if (!s->inuse)
return;
if (s->tlsfd >= 0) {
syslog(0, logname, "\tcleanup: closing tlsfd: %d", s->tlsfd);
// should make devtls close s->tlspipe[1], causing eof on s->tlspipe[0] in readproc
if (close(s->tlsfd) < 0)
syslog(0, logname, "\tcleanup: failed closing tlsfd: %d:%r", s->tlsfd);
else
syslog(0, logname, "\tcleanup: closed tlsfd: %d", s->tlsfd);
s->tlsfd = -2;
s->tlspipe[1] = -1;
} else {
if (s->tlspipe[1] >= 0) {
syslog(0, logname, "\tcleanup: writing 0 to tlspipe[1]: %d", s->tlspipe[1]);
if ((n = write(s->tlspipe[1], dummy, 0)) < 0)
syslog(0, logname, "\tcleanup: failed writing 0 to tlspipe[1]: %d : %r", s->tlspipe[1]);
else
syslog(0, logname, "\tcleanup: written 0 to tlspipe[1]: %d : %d", s->tlspipe[1], n);
syslog(0, logname, "\tcleanup: closing tlspipe[1]: %d", s->tlspipe[1]);
if (close(s->tlspipe[1]) < 0)
syslog(0, logname, "\tcleanup: failed closing tlspipe[1]: %d: %r", s->tlspipe[1]);
else
syslog(0, logname, "\tcleanup: closed tlspipe[1]: %d", s->tlspipe[1]);
s->tlspipe[1] = -1;
} else {
syslog(0, logname, "\tcleanup: oops should not happen tlspipe[1] < 0: %d", s->tlspipe[1]);
}
if (s->tlspipe[0] >= 0) {
syslog(0, logname, "\tcleanup: closing tlspipe[0]: %d", s->tlspipe[0]);
if (close(s->tlspipe[0]) < 0)
syslog(0, logname, "\tcleanup: failed closing tlspipe[0]: %d:%r", s->tlspipe[0]);
else
syslog(0, logname, "\tcleanup: closed tlspipe[0]: %d", s->tlspipe[0]);
s->tlspipe[0] = -1;
} else {
syslog(0, logname, "\tcleanup: oops should not happen tlspipe[0] < 0: %d", s->tlspipe[0]);
}
}
consumeRead = 1;
if (s->clientStarted && !s->clientReturned)
consumeClient = 1;
else
consumeClient = 0;
syslog(0, logname, "\tcleanup middle consumeRead=%d consumeClient=%d tlsfd=%d clientStarted=%d clientReturned=%d clientid=%d clientpid=%d tlspipe[0]=%d tlspipe[1]=%d", consumeRead, consumeClient, s->tlsfd, s->clientStarted, s->clientReturned, s->clientid, threadpid(s->clientid), s->tlspipe[0], s->tlspipe[1]);
timeout = 0;
startTimer(s->cleanupWhile, s->cleanupPeriod);
while((consumeRead || consumeClient) && !timeout) {
syslog(0, logname, "\tcleanup receiving...");
switch(ret = alt(a)){
case 0:
syslog(0, logname, "\t\toops... cleanup recv from readc: %p", rx);
// is this the close assert . if so, should we write this to ether?
break;
case 1:
syslog(0, logname, "\t\tcleanup: confirmed eof from readproc");
consumeRead = 0;
if (s->tlspipe[0] >= 0) {
syslog(0, logname, "\tcleanup: closing tlspipe[0]: %d", s->tlspipe[0]);
if (close(s->tlspipe[0]) < 0)
syslog(0, logname, "\tcleanup: failed closing tlspipe[0]: %d:%r", s->tlspipe[0]);
else
syslog(0, logname, "\tcleanup: closed tlspipe[0]: %d", s->tlspipe[0]);
s->tlspipe[0] = -1;
}
break;
case 2:
if (s->clientid == id) {
syslog(0, logname, "\t\tcleanup: confirmed return from clientproc %d", id);
consumeClient = 0;
} else
syslog(0, logname, "\t\tcleanup: oops return from older clientproc %d", id);
break;
case 3: /* timer expiration event */
syslog(0, logname, "ttls cleanup timer expired");
if (s->cleanupWhile->counter == 0){
if (consumeClient) {
syslog(0, logname, "ttls cleanup threadkill clientproc %d", s->clientid);
threadkill(s->clientid);
consumeClient = 0;
startTimer(s->cleanupWhile, s->cleanupPeriod);
} else if (consumeRead) {
syslog(0, logname, "ttls cleanup threadkill readproc %d", s->readid);
threadkill(s->readid);
s->readid = proccreate(readproc, s, STACK);
syslog(0, logname, "ttls cleanup started readproc tid=%d pid=%d", s->readid, threadpid(s->readid));
consumeRead = 0;
startTimer(s->cleanupWhile, s->cleanupPeriod);
} else {
syslog(0, logname, "ttls cleanup giving up");
timeout = 1;
}
} else {
fprint(2, "cleanup 3: should not happen\n");
syslog(0, logname, "cleanup 3: should not happen");
threadexitsall("cleanup 3: should not happen");
}
break;
default:
syslog(0, logname, "\t\tcleanup: unexpected %d", ret);
break;
}
}
resetTimer(s->cleanupWhile);
if (s->tlsfd != -2) {
syslog(0, logname, "\tcleanup: reset tlsfd: %d", s->tlsfd);
s->tlsfd = -2;
}
syslog(0, logname, "\tcleanup post consumeRead=%d consumeClient=%d tlsfd=%d clientStarted=%d clientReturned=%d clientid=%d clientpid=%d tlspipe[0]=%d tlspipe[1]=%d", consumeRead, consumeClient, s->tlsfd, s->clientStarted, s->clientReturned, s->clientid, threadpid(s->clientid), s->tlspipe[0], s->tlspipe[1]);
}
static void
setupTls(TTLSstate *s)
{
syslog(0, logname, "setupTls pre tlspipe[0]=%d tlspipe[1]=%d", s->tlspipe[0], s->tlspipe[1]);
if (s->tlspipe[0] >= 0 || s->tlspipe[1] >= 0) {
snprint(errbuf, sizeof(errbuf), "setupTls: pipe already open? %d %d", s->tlspipe[0], s->tlspipe[1]);
fprint(2, "%s\n", errbuf);
syslog(0, logname, "%s", errbuf);
threadexitsall(errbuf);
}
if (s->tlsfd != -2) {
snprint(errbuf, sizeof(errbuf), "setupTls: tlsfd init error? %d (expected -2)", s->tlsfd);
fprint(2, "%s\n", errbuf);
syslog(0, logname, "%s", errbuf);
threadexitsall(errbuf);
}
if (pipe(s->tlspipe) < 0) {
fprint(2, "pipe failed: %r\n");
syslog(0, logname, "pipe failed: %r");
threadexitsall("pipe failed");
}
s->clientStarted = 0;
s->clientReturned = 0;
// call tlsClient and wait for result
syslog(0, logname, "setupTls clientproc...");
s->clientid = proccreate(clientproc, s, STACK);
syslog(0, logname, "started clientproc tid=%d pid=%d", s->clientid, threadpid(s->clientid));
// signal reader to restart
syslog(0, logname, "setupTls startreadc...");
sendul(s->startreadc, 1);
s->inuse = 1;
syslog(0, logname, "setupTls post tlspipe[0]=%d tlspipe[1]=%d", s->tlspipe[0], s->tlspipe[1]);
}
static int
buildFrameStart(TTLSstate *s, uchar*b, int mtu)
{
TTLS *t;
int todo;
todo = s->ptoe->e - s->ptoe->p;
if (mtu <= TtlsLongHlen)
print("buildFrameStart error: mtu much too small: mtu=%d, longhdr=%d\n", mtu, TtlsLongHlen);
if (todo <= mtu-TtlsLongHlen)
print("buildFrameStart error: small enough, no framing needed: sz=%d, space=%d\n", todo, mtu-TtlsLongHlen);
t = (TTLS*)b;
memset(t, 0, TtlsLongHlen);
t->tp = EapTpTtls;
t->flags = TtlsFlagM | TtlsFlagL;
hnputl(t->tln, todo);
memcpy(b+TtlsLongHlen, s->ptoe->p, mtu-TtlsLongHlen);
s->ttslTxLen = mtu;
s->ptoe->p += mtu-TtlsLongHlen;
return mtu;
}
static int
buildFrameMiddle(TTLSstate *s, uchar*b, int mtu)
{
TTLS *t;
int todo;
todo = s->ptoe->e - s->ptoe->p;
if (mtu <= TtlsShortHlen)
print("buildFrameMiddle error: mtu much too small: mtu=%d, longhdr=%d\n", mtu, TtlsShortHlen);
if (todo <= mtu-TtlsShortHlen)
print("buildFrameMiddle error: small enough, no framing needed: sz=%d, space=%d\n", todo, mtu-TtlsShortHlen);
t = (TTLS*)b;
memset(t, 0, TtlsShortHlen);
t->tp = EapTpTtls;
t->flags = TtlsFlagM;
memcpy(b+TtlsShortHlen, s->ptoe->p, mtu-TtlsShortHlen);
s->ttslTxLen = mtu;
s->ptoe->p += mtu-TtlsShortHlen;
return mtu;
}
static int
buildMsg(TTLSstate *s, uchar*b, int mtu)
{
TTLS *t;
int todo;
todo = s->ptoe->e - s->ptoe->p;
if (mtu <= TtlsShortHlen)
print("buildMsg error: mtu much too small: mtu=%d, longhdr=%d\n", mtu, TtlsShortHlen);
if (todo > mtu-TtlsShortHlen)
print("buildMsg error: too big, framing needed: sz=%d, space=%d\n", todo, mtu-TtlsShortHlen);
t = (TTLS*)b;
memset(t, 0, TtlsShortHlen);
t->tp = EapTpTtls;
memcpy(b+TtlsShortHlen, s->ptoe->p, todo);
s->ttslTxLen = TtlsShortHlen + todo;
s->ptoe->p = 0;
return todo;
}
static void
buildAck(TTLSstate *s, uchar*b, int mtu)
{
TTLS *t;
USED(mtu);
t = (TTLS*)b;
memset(t, 0, TtlsShortHlen);
t->tp = EapTpTtls;
s->ttslTxLen = TtlsShortHlen;
}
static void
trans(TTLSstate *s, int new)
{
syslog(0, logname, "ttls trans: %s -> %s", (s->ttlsState>=0) ? snames[s->ttlsState] : "-", snames[new]);
switch(new){
case RecvAck:
s->ttlsDone = 1;
break;
case Receiving:
s->ttlsDone = 1;
break;
case Idle:
s->ttlsDone = 1;
break;
}
s->ttlsState = new;
}
static void
ttls(TTLSstate *s, uchar*rcvp, uint rcvl, uchar*txp, uint mtu, int*ttlsSuccess, int*ttlsFail)
{
Buf *rx;
int id;
Alt a[] = {
/* c v op */
{s->tlstidc, &id, CHANRCV},
{s->readc, &rx, CHANRCV},
{s->timerc, nil, CHANRCV},
{nil, nil, CHANEND},
};
TTLS *t;
uchar *p;
uint l;
int n;
int olen, flen;
int todo, total;
switch(s->ttlsState){
case Idle:
trans(s, Idle);
break;
case Start:
setupTls(s); // new session
trans(s, Waiting);
break;
case Waiting:
while(s->ttlsState == Waiting) {
startTimer(s->ttlsWhile, s->ttlsPeriod);
switch(alt(a)){
case 0: // the tlsClient call returned
syslog(0, logname, "ttls Waiting tlsClient %d returns", id);
if (s->clientid == id) {
syslog(0, logname, "ttls Waiting tlsClient %d return %d", id, s->tlsfd);
s->clientReturned = 1;
if (s->tlsfd < 0) {
*ttlsFail = 1;
// XXX build fail packet???
trans(s, Idle);
} else {
doTTLSphase2(s->tlsfd);
}
} else
syslog(0, logname, "ttls Waiting oops older tlsClient %d returned %d", id, s->tlsfd);
break;
case 1: // something read from tlspipe: encapsulate and send
// we do no treat end-of-file on tlspipe here, but leave that for cleanup.
// is this a wise choice?
syslog(0, logname, "ttls Waiting read from tlspipe");
s->ptoe = rx;
s->ptoe->p = s->ptoe->b;
if (debug) print("ttls readc: rx=%p rx->p=%p rx->e=%p n=%ld\n", rx, rx->p, rx->e, rx->e - rx->b);
trans(s, Sending);
break;
case 2: /* timer expiration event */
syslog(0, logname, "ttls Waiting tlsClient timer expired");
if (s->ttlsWhile->counter == 0)
trans(s, Timeout);
else {
fprint(2, "ttls Waiting 2: should not happen\n");
syslog(0, logname, "ttls Waiting 2: should not happen");
threadexitsall("ttls Waiting 2: should not happen");
}
break;
}
resetTimer(s->ttlsWhile);
}
break;
case Timeout:
trans(s, Receiving); // seems we need more stuff to satisfy tlsClient
break;
case Sending:
todo = s->ptoe->e - s->ptoe->p;
total = s->ptoe->e - s->ptoe->b;
if (s->ptoe->p == s->ptoe->b && todo > mtu-TtlsShortHlen) {
olen = todo;
flen = buildFrameStart(s, txp, mtu);
if (debug) print("ttls first fragment framed %d of %d, total %d, todo %d\n", flen, olen, total, todo);
trans(s, RecvAck);
} else if (todo > mtu-TtlsShortHlen) {
olen = todo;
flen = buildFrameMiddle(s, txp, mtu);
if (debug) print("ttls framed %d of %d, total %d, todo %d\n", flen, olen, total, todo);
trans(s, RecvAck);
} else {
olen = todo;
flen = buildMsg(s, txp, mtu);
if (debug) print("ttls framed %d of %d, total %d, todo %d\n", flen, olen, total, todo);
s->etop = &s->wbuf;
s->etop->p = s->etop->b;
s->etop->e = s->etop->b;
trans(s, Receiving);
}
break;
case RecvAck:
t = (TTLS*)rcvp;
if (t->flags&TtlsFlagS)
print("tls: unexpected TtlsFlagS in %s\n", snames[s->ttlsState]);
if (t->flags&TtlsFlagM)
print("tls: unexpected TtlsFlagM in %s\n", snames[s->ttlsState]);
if (t->flags&TtlsFlagL)
print("tls: unexpected TtlsFlagL in %s\n", snames[s->ttlsState]);
trans(s, Sending);
break;
case Receiving:
t = (TTLS*)rcvp;
total = s->etop->e - s->etop->b;
if (t->flags&TtlsFlagS)
print("tls: unexpected TtlsFlagS in %s\n", snames[s->ttlsState]);
if (t->flags&TtlsFlagL && total != 0)
print("tls: TtlsFlagL when total=%d != 0\n", total);
if (t->flags&TtlsFlagL) {
total = nhgetl(t->tln);
s->etop->e = s->etop->b + total;
if (debug) print("ttls: TtlsFlagL len=%d\n", total);
p = rcvp+TtlsLongHlen;
l = rcvl-TtlsLongHlen;
if (s->etop->p != s->etop->b)
print("ttls %s: ptoe->p != ptoe->b ptoe->p=%p ptoe->b=%p\n", snames[s->ttlsState], s->etop->p, s->etop->b);
} else {
p = rcvp+TtlsShortHlen;
l = rcvl-TtlsShortHlen;
}
memcpy(s->etop->p, p, l);
s->etop->p += l;
if (debug) print("ttls %s: received %d; total=%d\n", snames[s->ttlsState], l, total);
if (t->flags&TtlsFlagM)
trans(s, SendAck);
else {
if (s->etop->b != s->etop->e && s->etop->p != s->etop->e)
print("ttls : reassembled=%ld != total=%d\n", s->etop->p - s->etop->b, total);
if (s->etop->b != s->etop->e && s->etop->p == s->etop->e)
trans(s, Received);
else if (s->etop->b != s->etop->e && s->etop->p != s->etop->e) {
// just ignore the packet
// should we report the wrong-amount-read to other side?
syslog(0, logname, "ttls: skipping short reassembly (total=%d reass=%ld)", total, s->etop->p - s->etop->b);
trans(s, Waiting);
} else
trans(s, Waiting);
}
break;
case SendAck:
buildAck(s, txp, mtu);
trans(s, Receiving);
break;
case Received:
// we only get here if we read the right amount
total = s->etop->e - s->etop->b;
if (debug) print("ttls %s: writing tlspipe[0]: %s\n", snames[s->ttlsState], hexprefix(s->etop->b, total, 5));
n = write(s->tlspipe[0], s->etop->b, total);
if (n<0)
print("ttls %s: error writing tlspipe[0]: %r\n", snames[s->ttlsState]);
syslog(0, logname, "writeproc written %d", n);
if (n != total)
print("ttls %s: writing tlspipe[0]: n=%d != total=%d\n", snames[s->ttlsState], n, total);
trans(s, Waiting);
break;
}
}
void
initTTLS(char *file, char *filex, Timers *t)
{
TTLSstate *s;
syslog(0, logname, "initTTLS");
s = &theTTLSstate;
memset(s, 0, sizeof(TTLSstate));
s->ttlsState = Idle;
s->tlstidc = chancreate(sizeof(int), 0);
s->readc = chancreate(sizeof(Buf*), 0);
s->eofc = chancreate(sizeof(int), 0);
s->startclientc = chancreate(sizeof(int), 0);
s->startreadc = chancreate(sizeof(int), 0);
s->timerc = t->timerchan;
s->tlsfd = -2;
s->tlspipe[0] = -1;
s->tlspipe[1] = -1;
s->ttlsWhile = addTimer(t, "ttlsWhile");
s->cleanupWhile = addTimer(t, "cleanupWhile");
s->ttlsPeriod = 5; //seconds
s->cleanupPeriod = 5; //seconds
s->tlsconn.sessionType = "ttls";
s->tlsconn.sessionConst = "ttls keying material";
s->tlsconn.sessionKey = theSessionKey;
s->tlsconn.sessionKeylen = sizeof(theSessionKey);
if (debugTLS)
s->tlsconn.trace = print;
fmtinstall('H', encodefmt);
if (file) {
s->thumbTable = initThumbprints(file, filex);
if (s->thumbTable == nil) {
snprint(errbuf, sizeof(errbuf), "initThumbprints: %r");
syslog(0, logname, "%s", errbuf);
fprint(2, "%s\n", errbuf);
threadexitsall(errbuf);
}
}
s->readid = proccreate(readproc, s, STACK);
syslog(0, logname, "initTTLS started readproc tid=%d pid=%d", s->readid, threadpid(s->readid));
}
void
abortTTLS(void)
{
TTLSstate *s;
syslog(0, logname, "abortTTLS");
s = &theTTLSstate;
if (s->tlspipe[0] >= 0) {
close(s->tlspipe[0]);
s->tlspipe[0] = -1;
}
}
static void
run(TTLSstate *s, uchar*rcvp, uint rcvl, uchar*txp, uint mtu, int*success, int*failed)
{
s->ttlsDone = 0;
while (!s->ttlsDone)
ttls(s, rcvp, rcvl, txp, mtu, success, failed);
}
int
processTTLS(uchar*rcvp, uint rcvl, int expectStart, uchar*txp, uint mtu, int*success, int*failed)
{
TTLS *hr;
uchar flags, version;
TTLSstate *s;
// if (debug) print("processTTLS br=%p txp=%p mtu=%d bl=%d\n", br, txp, mtu, bl);
s = &theTTLSstate;
hr = (TTLS*)rcvp;
if (hr->tp != EapTpTtls)
return 0; // flag error??
// first thing should be EAP-TTLS start packet
flags = rcvp[1]; // check length
version = flags & TtlsVersion;
if (debug) print("processTTLS flags=%s%s%s ver=%d mtu=%d bl=%d\n",
(flags&TtlsFlagS ? "S":""),
(flags&TtlsFlagM ? "M":""),
(flags&TtlsFlagL ? "L":""),
version, mtu, rcvl);
if (expectStart && !flags&TtlsFlagS) {
fprint(2, "expected EAP-TTLS start packet\n");
syslog(0, logname, "expected EAP-TTLS start packet");
threadexitsall("expected EAP-TTLS start packet");
}
if (flags & TtlsFlagS) {
cleanup(s); // previous session
// ack??
// look for piggy-backed stuff?
s->ttlsVersion = version;
s->ttlsState = Start;
s->ttlsDone = 0;
s->ptoe = nil;
s->etop = nil;
s->wbuf.p = 0;
s->wbuf.e = 0;
// we don't have a client certificate
s->tlsconn.cert = nil;
s->tlsconn.certlen = 0;
// avoid trying session resumption - tlsClient does not support it
s->tlsconn.sessionID = nil;
s->tlsconn.sessionIDlen = 0;
// if (debug) print("processTTLS TtlsFlagS version=%d \n", version);
}
s->ttslTxLen = 0;
run(s, rcvp, rcvl, txp, mtu, success, failed);
return s->ttslTxLen;
}
|