3proxy/src/tlspr.c

293 lines
7.7 KiB
C

/*
3APA3A simpliest proxy server
(c) 2002-2021 by Vladimir Dubrovin <3proxy@3proxy.org>
please read License Agreement
*/
#include "proxy.h"
#ifndef PORTMAP
#define PORTMAP
#endif
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
unsigned size16(unsigned char *buf){
unsigned res;
res = (((unsigned)buf[0]) << 8) + +buf[1];
return res;
}
int readtls(struct clientparam *param, int direction, unsigned char *buf, int bufsize){
int res = 0;
int len;
if(bufsize < 3) return -1;
res = sockgetlinebuf(param, direction, buf, 3, EOF, conf.timeouts[STRING_S]);
if(res !=3 || buf[0] != 22 || buf[1] != 3) return -2;
len = size16(buf+3);
if((len+3) > bufsize) return -3;
res = sockgetlinebuf(param, direction, buf+3, len, EOF, conf.timeouts[STRING_S]);
if(res != len) return -4;
return len+3;
}
#define BSIZE (4096)
#define SNILEN (256)
#define PROTOLEN (32)
int parsehello(int type, unsigned char *hello, int len, char *sni, int *lv, char * proto){
int hlen;
unsigned offset;
int slen;
int cslen;
int elen;
int snllen, snlen, alpnlen;
int snifound=0;
if(len < 64) return -1;
if(hello[5] != type) return -2;
if(hello[6] != 0) return -3;
hlen = size16(hello+7);
if((hlen+9) != len) return -4;
offset = 9;
if(hello[offset] != 3) return -5;
*lv = hello[offset+1];
offset += 34;
slen = hello[offset];
if((offset + slen + 3) > len) return -6;
offset += (slen+1);
if(type == 1){
cslen = size16(hello+offset);
if((offset + cslen + 3) > len) return -7;
offset += (cslen+2);
cslen = hello[offset];
if((offset + cslen + 3) > len) return -8;
offset += (cslen+1);
}
else if(type == 2){
offset += 3;
}
elen = size16(hello+offset);
offset += 2;
if(elen+offset != len) return -9;
while(elen > 1){
int xlen;
xlen = size16(hello+offset+2);
if(xlen+4 > elen) return -10;
if(type == 1 && hello[offset] == 0 && hello[offset+1] == 0){
snllen=size16(hello+offset+4);
if(snllen>3){
if(snllen+2 != xlen) return -12;
if(hello[offset+6] != 0) return -13;
snlen=size16(hello+offset+7);
if(snlen + 3 > snllen) return -14;
if(snlen+1 > SNILEN) return -15;
memcpy(sni, hello + offset + 9, snlen);
sni[snlen] = 0;
snifound = snlen;
}
}
else if(hello[offset] == 0 && hello[offset+1] == 43){
if(xlen>2){
*lv = hello[offset+6];
}
else if(xlen==2){
*lv = hello[offset+5];
}
}
else if(hello[offset] == 0 && hello[offset+1] == 16){
alpnlen=hello[offset+6];
if(alpnlen+7>elen) return -16;
if(alpnlen+1>PROTOLEN) return -17;
memcpy(proto, hello+offset+7, alpnlen);
proto[alpnlen] = 0;
}
offset += (xlen+4);
elen -= (xlen+4);
}
return snifound;
}
int tlstobufcli(struct clientparam *param, int offset){
int len, newlen;
if(!param->clibuf){
if(!(param->clibuf = myalloc(SRVBUFSIZE))) return -1;
param->clibufsize = SRVBUFSIZE;
param->clioffset = param->cliinbuf = 0;
}
if(param->srvinbuf != param->srvoffset){
len = socksend(param, param->clisock, param->srvbuf+param->srvoffset,param->srvinbuf-param->srvoffset, conf.timeouts[STRING_S]);
if(len != param->srvinbuf-param->srvoffset){
return -2;
}
param->srvinbuf = param->srvoffset = 0;
}
len = sockfillbuffcli(param, 5, conf.timeouts[STRING_S]);
if(len < 5) return -2;
if(param->clibuf[1] != 3) {
return -3;
}
else {
len = 5 + size16(param->clibuf+3);
if(len > param->clibufsize) return -4;
for(newlen=param->cliinbuf; newlen < len; newlen=param->cliinbuf){
sockfillbuffcli(param, len, conf.timeouts[STRING_S]);
if(param->cliinbuf <= newlen) return -5;
}
}
return len;
}
int tlstobufsrv(struct clientparam *param, int offset){
int len, newlen;
if(param->cliinbuf != param->clioffset){
len = socksend(param, param->remsock, param->clibuf+param->clioffset,param->cliinbuf-param->clioffset, conf.timeouts[STRING_S]);
if(len != param->cliinbuf-param->clioffset){
return -1;
}
param->cliinbuf = param->clioffset = 0;
}
if(!param->srvbuf){
if(!(param->srvbuf = myalloc(SRVBUFSIZE))) return -1;
param->srvbufsize = SRVBUFSIZE;
param->srvoffset = param->srvinbuf = 0;
}
len = sockfillbuffsrv(param, offset+5, conf.timeouts[STRING_S]);
if(len < offset+5) return -3;
if(param->srvbuf[offset+1] != 3) {
return -4;
}
else {
len = offset + 5 + size16(param->srvbuf+offset+3);
if(len > param->srvbufsize) return -5;
for(newlen=param->srvinbuf; newlen < len; newlen=param->srvinbuf){
sockfillbuffsrv(param, len, conf.timeouts[STRING_S]);
if(param->srvinbuf <= newlen) return -6;
}
}
return len-offset;
}
void * tlsprchild(struct clientparam* param) {
int res;
char sni[SNILEN];
char req[SNILEN+PROTOLEN+16];
int lv=-1;
char proto[PROTOLEN]="-";
res = tlstobufcli(param, 0);
if(res <= 0 || param->clibuf[0] != 22){
if(param->srv->requirecert)RETURN(300-res);
}
else {
lv = param->clibuf[2];
res = parsehello(1, param->clibuf, res, sni, &lv, proto);
if(res > 0){
if(param->hostname){
myfree(param->hostname);
param->hostname = NULL;
}
else if (parsehostname(sni, param, param->srv->targetport? param->srv->targetport:443)) RETURN (100);
if (!param->hostname)param->hostname = (unsigned char *)mystrdup(sni);
}
else if (res < 0 && param->srv->requirecert) RETURN(310-res);
}
param->operation = CONNECT;
param->redirectfunc = NULL;
res = (*param->srv->authfunc)(param);
if(res) {RETURN(res);}
if (param->npredatfilters){
int action;
action = handlepredatflt(param);
if(action == HANDLED){
RETURN(0);
}
if(action != PASS) RETURN(19);
}
if(param->redirectfunc && param->redirectfunc != tlsprchild){
return (*param->redirectfunc)(param);
}
if(param->srv->requirecert > 1){
res = tlstobufsrv(param, 0);
if(res <= 0 || param->srvbuf[0] != 22) RETURN(340-res);
lv = param->srvbuf[2];
res = parsehello(2, param->srvbuf, res, sni, &lv, proto);
if (res < 0) RETURN(350-res);
}
if(param->srv->requirecert > 2){
int srvcert=0, clicert=0, reqcert=0, len, rlen, done;
if(lv > 3) RETURN(370);
for(done=0;!done;) {
len = param->srvinbuf;
if(socksend(param, param->clisock, param->srvbuf,len, conf.timeouts[STRING_S]) != len) RETURN(371);
param->srvinbuf = 0;
res = tlstobufsrv(param, 0);
if(res <= 0) RETURN(380-res);
if(param->srvbuf[0]!= 22) break;
switch(param->srvbuf[5]){
case 11:
/* process server certificates here */
if(param->srvbuf[6]||param->srvbuf[7]||param->srvbuf[8]>64) srvcert = 1;
break;
case 13:
reqcert = 1;
break;
case 14:
done = 1;
break;
default:
break;
}
}
if(!srvcert) RETURN(373);
if(param->srv->requirecert > 3){
if(!reqcert) RETURN(374);
for(done=0;!done;) {
res = tlstobufcli(param, 0);
if(res <= 0) RETURN(390-res);
len = res;
if(param->clibuf[0]!= 22) break;
switch(param->clibuf[5]){
case 11:
/* process client certificates here */
if(param->clibuf[6]||param->clibuf[7]||param->clibuf[8]>64)clicert = 1;
break;
case 14:
done = 1;
break;
default:
break;
}
if(done) break;
if(socksend(param, param->remsock, param->clibuf,len, conf.timeouts[STRING_S]) != len) RETURN(375);
param->cliinbuf = 0;
}
if(!clicert) RETURN(375);
}
}
RETURN (mapsocket(param, conf.timeouts[CONNECTION_L]));
CLEANRET:
sprintf(req, "%sv%d.%d %s %s", lv<0?"NONE":lv?"TLS":"SSL", lv<0?0:lv?1:3, lv<0?0:lv?lv-1:0, param->hostname?(char *)param->hostname:"-", proto);
dolog(param, (unsigned char *)req);
freeparam(param);
return (NULL);
}
#ifdef WITHMAIN
struct proxydef childdef = {
tlsprchild,
1443,
0,
S_TLSPR,
""
};
#include "proxymain.c"
#endif