/*
* remove at most n bytes from the queue, if discard is set
* dump the remainder
*/
static Block*
qtake(Block **l, int n, int discard)
{
Block *nb, *b, *first;
int i;
/*
* We can't let Eintr's lose data since the program
* doing the read may be able to handle it. The only
* places Eintr is possible is during the read's in consume.
* Therefore, we make sure we can always put back the bytes
* consumed before the last ensure.
*/
static Block*
sslbread(Chan *c, long n, ulong)
{
Dstate * volatile s;
Block *b;
uchar consumed[3], *p;
int toconsume;
int len, pad;
if(s->processed == 0){
/*
* Read in the whole message. Until we've got it all,
* it stays on s->unprocessed, so that if we get Eintr,
* we'll pick up where we left off.
*/
ensure(s, &s->unprocessed, 3);
s->unprocessed = pullupblock(s->unprocessed, 2);
p = s->unprocessed->rp;
if(p[0] & 0x80){
len = ((p[0] & 0x7f)<<8) | p[1];
ensure(s, &s->unprocessed, len);
pad = 0;
toconsume = 2;
} else {
s->unprocessed = pullupblock(s->unprocessed, 3);
len = ((p[0] & 0x3f)<<8) | p[1];
pad = p[2];
if(pad > len){
print("pad %d buf len %d\n", pad, len);
error("bad pad in ssl message");
}
toconsume = 3;
}
ensure(s, &s->unprocessed, toconsume+len);
if(waserror()){
qunlock(&s->in.ctlq);
if(b != nil)
freeb(b);
nexterror();
}
qlock(&s->in.ctlq);
switch(s->state){
case Sencrypting:
if(b == nil)
error("ssl message too short (encrypting)");
b = decryptb(s, b);
break;
case Sdigesting:
b = pullupblock(b, s->diglen);
if(b == nil)
error("ssl message too short (digesting)");
checkdigestb(s, b);
pullblock(&b, s->diglen);
len -= s->diglen;
break;
case Sdigenc:
b = decryptb(s, b);
b = pullupblock(b, s->diglen);
if(b == nil)
error("ssl message too short (dig+enc)");
checkdigestb(s, b);
pullblock(&b, s->diglen);
len -= s->diglen;
break;
}
/* remove pad */
if(pad)
s->processed = qtake(&b, len - pad, 1);
else
s->processed = b;
b = nil;
s->in.mid++;
qunlock(&s->in.ctlq);
poperror();
}
/* return at most what was asked for */
b = qtake(&s->processed, n, 0);
qunlock(&s->in.q);
poperror();
return b;
}
static long
sslread(Chan *c, void *a, long n, vlong off)
{
Block * volatile b;
Block *nb;
uchar *va;
int i;
char buf[128];
ulong offset = off;
int ft;
if(c->qid.type & QTDIR)
return devdirread(c, a, n, 0, 0, sslgen);
ft = TYPE(c->qid);
switch(ft) {
default:
error(Ebadusefd);
case Qctl:
ft = CONV(c->qid);
sprint(buf, "%d", ft);
return readstr(offset, a, n, buf);
case Qdata:
b = sslbread(c, n, offset);
break;
case Qencalgs:
return readstr(offset, a, n, encalgs);
break;
case Qhashalgs:
return readstr(offset, a, n, hashalgs);
break;
}
if(waserror()){
freeblist(b);
nexterror();
}
n = 0;
va = a;
for(nb = b; nb; nb = nb->next){
i = BLEN(nb);
memmove(va+n, nb->rp, i);
n += i;
}
freeblist(b);
poperror();
return n;
}
static long
sslbwrite(Chan *c, Block *b, ulong)
{
Dstate * volatile s;
long rv;
s = dstate[CONV(c->qid)];
if(s == nil)
panic("sslbwrite");
/*
* use SSL record format, add in count, digest and/or encrypt.
* the write is interruptable. if it is interrupted, we'll
* get out of sync with the far side. not much we can do about
* it since we don't know if any bytes have been written.
*/
static long
sslput(Dstate *s, Block * volatile b)
{
Block *nb;
int h, n, m, pad, rv;
uchar *p;
int offset;
rv = 0;
while(b != nil){
m = n = BLEN(b);
h = s->diglen + 2;
/* trim to maximum block size */
pad = 0;
if(m > s->max){
m = s->max;
} else if(s->blocklen != 1){
pad = (m + s->diglen)%s->blocklen;
if(pad){
if(m > s->maxpad){
pad = 0;
m = s->maxpad;
} else {
pad = s->blocklen - pad;
h++;
}
}
}
rv += m;
if(m != n){
nb = allocb(m + h + pad);
memmove(nb->wp + h, b->rp, m);
nb->wp += m + h;
b->rp += m;
} else {
/* add header space */
nb = padblock(b, h);
b = 0;
}
m += s->diglen;
/* SSL style count */
if(pad){
nb = padblock(nb, -pad);
prng(nb->wp, pad);
nb->wp += pad;
m += pad;
p = nb->rp;
p[0] = (m>>8);
p[1] = m;
p[2] = pad;
offset = 3;
} else {
p = nb->rp;
p[0] = (m>>8) | 0x80;
p[1] = m;
offset = 2;
}
switch(s->state){
case Sencrypting:
nb = encryptb(s, nb, offset);
break;
case Sdigesting:
nb = digestb(s, nb, offset);
break;
case Sdigenc:
nb = digestb(s, nb, offset);
nb = encryptb(s, nb, offset);
break;
}
s->out.mid++;
m = BLEN(nb);
devtab[s->c->type]->bwrite(s->c, nb, s->c->offset);
s->c->offset += m;
}
/*
* 40 bit RC4 is the same as n-bit RC4. However,
* we ignore all but the first 40 bits of the key.
*/
static void
initRC4key_40(OneWay *w)
{
if(w->slen > 5)
w->slen = 5;
initRC4key(w);
}
/*
* 128 bit RC4 is the same as n-bit RC4. However,
* we ignore all but the first 128 bits of the key.
*/
static void
initRC4key_128(OneWay *w)
{
if(w->slen > 16)
w->slen = 16;
initRC4key(w);
}
static long
sslwrite(Chan *c, void *a, long n, vlong)
{
Dstate * volatile s;
Block * volatile b;
int m, t, i;
char *p, *e;
uchar *x;
Cmdbuf *cb;
Cmdtab *ct;
s = dstate[CONV(c->qid)];
if(s == 0)
panic("sslwrite");
t = TYPE(c->qid);
if(t == Qdata){
if(s->state == Sincomplete)
error(Ebadusefd);
/* lock should a write gets split over multiple records */
if(waserror()){
qunlock(&s->out.q);
nexterror();
}
qlock(&s->out.q);
p = a;
e = p + n;
do {
m = e - p;
if(m > s->max)
m = s->max;
b = allocb(m);
if(waserror()){
freeb(b);
nexterror();
}
memmove(b->wp, p, m);
poperror();
b->wp += m;
sslput(s, b);
p += m;
} while(p < e);
poperror();
qunlock(&s->out.q);
return n;
}
/* mutex with operations using what we're about to change */
if(waserror()){
qunlock(&s->in.ctlq);
qunlock(&s->out.q);
nexterror();
}
qlock(&s->in.ctlq);
qlock(&s->out.q);
switch(t){
default:
panic("sslwrite");
case Qsecretin:
setsecret(&s->in, a, n);
goto out;
case Qsecretout:
setsecret(&s->out, a, n);
goto out;
case Qctl:
break;
}
l = &bin;
for(b = bin; b; b = b->next){
/* make sure we have a multiple of s->blocklen */
if(s->blocklen > 1){
i = BLEN(b);
if(i % s->blocklen){
*l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen));
if(b == 0)
error("ssl encrypted message too short");
}
}
l = &b->next;
/* decrypt */
switch(s->encryptalg){
case DESECB:
ds = s->in.state;
ep = b->rp + BLEN(b);
for(p = b->rp; p < ep; p += 8)
block_cipher(ds->expanded, p, 1);
break;
case DESCBC:
ds = s->in.state;
ep = b->rp + BLEN(b);
for(p = b->rp; p < ep;){
memmove(tmp, p, 8);
block_cipher(ds->expanded, p, 1);
tp = tmp;
ip = ds->ivec;
for(eip = ip+8; ip < eip; ){
*p++ ^= *ip;
*ip++ = *tp++;
}
}
break;
case RC4:
rc4(s->in.state, b->rp, BLEN(b));
break;
}
}
return bin;
}
static Block*
digestb(Dstate *s, Block *b, int offset)
{
uchar *p;
DigestState ss;
uchar msgid[4];
ulong n, h;
OneWay *w;
w = &s->out;
memset(&ss, 0, sizeof(ss));
h = s->diglen + offset;
n = BLEN(b) - h;