126 lines
4.0 KiB
C
126 lines
4.0 KiB
C
#include "tinytls.h"
|
|
#include <stdlib.h>
|
|
#include <libowfat/uint16.h>
|
|
#include <libowfat/uint32.h>
|
|
#include <libowfat/buffer.h>
|
|
#include <string.h>
|
|
|
|
inline int puts(const char* s) {
|
|
buffer_putmflush(buffer_1,s,"\n");
|
|
return 0;
|
|
}
|
|
|
|
tls_error_code tls_connect(uintptr_t fd,struct ssl_context* sc) {
|
|
size_t l;
|
|
tls_error_code r,ret=PROTOCOLFAIL;
|
|
switch (sc->state) {
|
|
case NONE:
|
|
puts("TLS_CONNECT");
|
|
// initial connect attempt; send client hello
|
|
sc->message.l=fmt_tls_clienthello(NULL,sc);
|
|
// scratch should be enough to hold the client hello
|
|
// depending on session data length and sc->hostname
|
|
if (sc->message.l<=sizeof(sc->scratch))
|
|
sc->message.s=sc->scratch;
|
|
else {
|
|
if (!(sc->message.s=malloc(sc->message.l)))
|
|
return OOM;
|
|
}
|
|
sc->message.l=fmt_tls_clienthello((char*)sc->message.s,sc);
|
|
sc->ofsinmessage=0; sc->message.s=sc->scratch;
|
|
sc->state=WRITE_CLIENTHELLO;
|
|
// fall through
|
|
|
|
case WRITE_CLIENTHELLO:
|
|
puts("WRITE_CLIENTHELLO");
|
|
r=tls_dowrite(fd,sc);
|
|
if (r!=OK) return r;
|
|
// r=READ_SERVERHELLO; // since we fall through, don't do dead store
|
|
sc->ofsinmessage=0;
|
|
// fall through
|
|
|
|
case READ_SERVERHELLO:
|
|
puts("READ_SERVERHELLO");
|
|
r=tls_doread(fd,sc);
|
|
if (r!=OK) return r;
|
|
if (sc->message.s[0]!=22) { // "handshake"
|
|
nothandshake:
|
|
fmt_tls_alert_pkt(sc->scratch,2,UNEXPECTED_MESSAGE);
|
|
goto alertfail;
|
|
}
|
|
if ((l=uint16_read_big(sc->message.s+3))<54) { // outer length
|
|
decodeerror:
|
|
fmt_tls_alert_pkt(sc->scratch,2,DECODE_ERROR);
|
|
goto alertfail;
|
|
}
|
|
if (sc->message.s[5]!=2) goto nothandshake; // "server hello"
|
|
if ((uint32_read_big(sc->message.s+5)&0xffffff)+4!=l) goto decodeerror; // inner length
|
|
if ((size_t)(unsigned char)(sc->message.s[5+38])+54<l) goto decodeerror;
|
|
{
|
|
const char* x=sc->message.s+sc->message.s[5+38]+5+38+1;
|
|
// make sure they don't pull a fast one on us
|
|
// and "agree" to a cipher/compression method we did not offer
|
|
uint16_t cipher=uint16_read_big(x);
|
|
if (tls_cipherprio(cipher)<0) goto decodeerror;
|
|
if (x[2]!=0) goto decodeerror;
|
|
sc->cipher=cipher;
|
|
sc->compressionmethod=0;
|
|
}
|
|
// r=READ_CERT; // since we fall through, this would be a dead store
|
|
sc->ofsinmessage=0;
|
|
// fall through
|
|
|
|
case READ_CERT:
|
|
puts("READ_CERT");
|
|
r=tls_doread(fd,sc);
|
|
if (r!=OK) return r;
|
|
if (sc->message.s[0]!=22) goto nothandshake; // "handshake"
|
|
if ((l=uint16_read_big(sc->message.s+3))<50) goto decodeerror;
|
|
if (sc->message.s[5]!=11) goto nothandshake; // "certificate"
|
|
if ((uint32_read_big(sc->message.s+5)&0xffffff)+4!=l) goto decodeerror; // inner length
|
|
if ((uint32_read_big(sc->message.s+8)&0xffffff)+7!=l) goto decodeerror; // innerer length
|
|
{
|
|
const char* x=sc->message.s+9+3;
|
|
const char* max=x+l-7;
|
|
size_t i;
|
|
sc->theircert[0].s=malloc(l);
|
|
for (i=0; i<MAXCERT; ++i) {
|
|
if (x>=max) break;
|
|
if (x[0]) goto decodeerror;
|
|
sc->theircert[i].l=uint16_read_big(x+1);
|
|
x+=3;
|
|
if ((uintptr_t)(max-x) < sc->theircert[i].l) goto decodeerror;
|
|
if (i!=0) sc->theircert[i].s=sc->theircert[i-1].s+sc->theircert[i-1].l;
|
|
memcpy((char*)sc->theircert[i].s,x,sc->theircert[i].l);
|
|
x+=sc->theircert[i].l;
|
|
}
|
|
}
|
|
|
|
// r=READ_SERVERHELLODONE; // since we fall through, this would be a dead store
|
|
sc->ofsinmessage=0;
|
|
// fall through
|
|
|
|
case READ_SERVERHELLODONE:
|
|
puts("READ_SERVERHELLODONE");
|
|
r=tls_doread(fd,sc);
|
|
if (r!=OK) return r;
|
|
if (sc->message.s[0]!=22) goto nothandshake; // "handshake"
|
|
if ((l=uint16_read_big(sc->message.s+3))!=4) goto decodeerror;
|
|
if (sc->message.s[5]!=14) goto nothandshake; // "server hello done"
|
|
if ((uint32_read_big(sc->message.s+5)&0xffffff)+4!=l) goto decodeerror; // inner length
|
|
return OK;
|
|
|
|
case WRITE_ALERTFAIL:
|
|
alertfail:
|
|
sc->state=WRITE_ALERTFAIL;
|
|
sc->message.s=sc->scratch;
|
|
sc->message.l=7;
|
|
r=tls_dowrite(fd,sc);
|
|
if (r!=OK) return r;
|
|
|
|
default:
|
|
sc->state=FAIL;
|
|
}
|
|
return ret;
|
|
}
|