ref: fa9f4c5c6852ce467644f610e77504664cb86af4
dir: /emu/port/devssl.c/
/*
* devssl - secure sockets layer
*/
#include "dat.h"
#include "fns.h"
#include "error.h"
#include "mp.h"
#include "libsec.h"
typedef struct OneWay OneWay;
struct OneWay
{
QLock q;
QLock ctlq;
void *state; /* encryption state */
int slen; /* secret data length */
uchar *secret; /* secret */
ulong mid; /* message id */
};
enum
{
/* connection states */
Sincomplete= 0,
Sclear= 1,
Sencrypting= 2,
Sdigesting= 4,
Sdigenc= Sencrypting|Sdigesting,
/* encryption algorithms */
Noencryption= 0,
DESCBC= 1,
DESECB= 2,
RC4= 3,
IDEACBC= 4,
IDEAECB= 5
};
typedef struct Dstate Dstate;
struct Dstate
{
Chan *c; /* io channel */
uchar state; /* state of connection */
int ref; /* serialized by dslock for atomic destroy */
uchar encryptalg; /* encryption algorithm */
ushort blocklen; /* blocking length */
ushort diglen; /* length of digest */
DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */
/* for SSL format */
int max; /* maximum unpadded data per msg */
int maxpad; /* maximum padded data per msg */
/* input side */
OneWay in;
Block *processed;
Block *unprocessed;
/* output side */
OneWay out;
/* protections */
char* user;
int perm;
};
enum
{
Maxdmsg= 1<<16,
Maxdstate= 1<<10,
};
Lock dslock;
int dshiwat;
int maxdstate = 20;
Dstate** dstate;
enum{
Qtopdir = 1, /* top level directory */
Qclonus,
Qconvdir, /* directory for a conversation */
Qdata,
Qctl,
Qsecretin,
Qsecretout,
Qencalgs,
Qhashalgs
};
#define TYPE(x) ((ulong)(x).path & 0xf)
#define CONV(x) (((ulong)(x).path >> 4)&(Maxdstate-1))
#define QID(c, y) (((c)<<4) | (y))
static char* encalgs;
static char* hashalgs;
void producerand(void);
static void alglistinit(void);
static void ensure(Dstate*, Block**, int);
static void consume(Block**, uchar*, int);
static void setsecret(OneWay*, uchar*, int);
static Block* encryptb(Dstate*, Block*, int);
static Block* decryptb(Dstate*, Block*);
static Block* digestb(Dstate*, Block*, int);
static void checkdigestb(Dstate*, Block*);
static Chan* buftochan(char*);
static void sslhangup(Dstate*);
static void dsclone(Chan *c);
static void dsnew(Chan *c, Dstate **);
static int
sslgen(Chan *c, char *dname, Dirtab *d, int nd, int s, Dir *dp)
{
Qid q;
Dstate *ds;
char *p, *nm;
USED(dname);
USED(nd);
USED(d);
q.type = QTFILE;
q.vers = 0;
if(s == DEVDOTDOT){
q.path = QID(0, Qtopdir);
q.type = QTDIR;
devdir(c, q, "#D", 0, eve, 0555, dp);
return 1;
}
switch(TYPE(c->qid)) {
case Qtopdir:
if(s < dshiwat) {
q.path = QID(s, Qconvdir);
q.type = QTDIR;
ds = dstate[s];
if(ds != 0)
nm = ds->user;
else
nm = eve;
snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
devdir(c, q, up->genbuf, 0, nm, DMDIR|0555, dp);
return 1;
}
if(s > dshiwat)
return -1;
/* fall through */
case Qclonus:
q.path = QID(0, Qclonus);
devdir(c, q, "clone", 0, eve, 0666, dp);
return 1;
case Qconvdir:
ds = dstate[CONV(c->qid)];
if(ds != 0)
nm = ds->user;
else
nm = eve;
switch(s) {
default:
return -1;
case 0:
q.path = QID(CONV(c->qid), Qctl);
p = "ctl";
break;
case 1:
q.path = QID(CONV(c->qid), Qdata);
p = "data";
break;
case 2:
q.path = QID(CONV(c->qid), Qsecretin);
p = "secretin";
break;
case 3:
q.path = QID(CONV(c->qid), Qsecretout);
p = "secretout";
break;
case 4:
q.path = QID(CONV(c->qid), Qencalgs);
p = "encalgs";
break;
case 5:
q.path = QID(CONV(c->qid), Qhashalgs);
p = "hashalgs";
break;
}
devdir(c, q, p, 0, nm, 0660, dp);
return 1;
}
return -1;
}
static void
sslinit(void)
{
if((dstate = malloc(sizeof(Dstate*) * maxdstate)) == 0)
panic("sslinit");
alglistinit();
}
static Chan *
sslattach(char *spec)
{
Chan *c;
c = devattach('D', spec);
c->qid.path = QID(0, Qtopdir);
c->qid.vers = 0;
c->qid.type = QTDIR;
return c;
}
static Walkqid*
sslwalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, 0, 0, sslgen);
}
static int
sslstat(Chan *c, uchar *db, int n)
{
return devstat(c, db, n, 0, 0, sslgen);
}
static Chan*
sslopen(Chan *c, int omode)
{
Dstate *s, **pp;
int perm;
perm = 0;
omode &= 3;
switch(omode) {
case OREAD:
perm = 4;
break;
case OWRITE:
perm = 2;
break;
case ORDWR:
perm = 6;
break;
}
switch(TYPE(c->qid)) {
default:
panic("sslopen");
case Qtopdir:
case Qconvdir:
if(omode != OREAD)
error(Eperm);
break;
case Qclonus:
dsclone(c);
break;
case Qctl:
case Qdata:
case Qsecretin:
case Qsecretout:
if(waserror()) {
unlock(&dslock);
nexterror();
}
lock(&dslock);
pp = &dstate[CONV(c->qid)];
s = *pp;
if(s == 0)
dsnew(c, pp);
else {
if((perm & (s->perm>>6)) != perm
&& (strcmp(up->env->user, s->user) != 0
|| (perm & s->perm) != perm))
error(Eperm);
s->ref++;
}
unlock(&dslock);
poperror();
break;
case Qencalgs:
case Qhashalgs:
if(omode != OREAD)
error(Eperm);
break;
}
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
return c;
}
static int
sslwstat(Chan *c, uchar *db, int n)
{
Dir *dir;
Dstate *s;
int m;
s = dstate[CONV(c->qid)];
if(s == 0)
error(Ebadusefd);
if(strcmp(s->user, up->env->user) != 0)
error(Eperm);
dir = smalloc(sizeof(Dir)+n);
m = convM2D(db, n, &dir[0], (char*)&dir[1]);
if(m == 0){
free(dir);
error(Eshortstat);
}
if(!emptystr(dir->uid))
kstrdup(&s->user, dir->uid);
if(dir->mode != ~0UL)
s->perm = dir->mode;
free(dir);
return m;
}
static void
sslclose(Chan *c)
{
Dstate *s;
switch(TYPE(c->qid)) {
case Qctl:
case Qdata:
case Qsecretin:
case Qsecretout:
if((c->flag & COPEN) == 0)
break;
s = dstate[CONV(c->qid)];
if(s == 0)
break;
lock(&dslock);
if(--s->ref > 0) {
unlock(&dslock);
break;
}
dstate[CONV(c->qid)] = 0;
unlock(&dslock);
sslhangup(s);
if(s->c)
cclose(s->c);
free(s->user);
free(s->in.secret);
free(s->out.secret);
free(s->in.state);
free(s->out.state);
free(s);
}
}
/*
* make sure we have at least 'n' bytes in list 'l'
*/
static void
ensure(Dstate *s, Block **l, int n)
{
int sofar, i;
Block *b, *bl;
sofar = 0;
for(b = *l; b; b = b->next){
sofar += BLEN(b);
if(sofar >= n)
return;
l = &b->next;
}
while(sofar < n){
bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0);
if(bl == 0)
error(Ehungup);
*l = bl;
i = 0;
for(b = bl; b; b = b->next){
i += BLEN(b);
l = &b->next;
}
if(i == 0)
error(Ehungup);
sofar += i;
}
}
/*
* copy 'n' bytes from 'l' into 'p' and free
* the bytes in 'l'
*/
static void
consume(Block **l, uchar *p, int n)
{
Block *b;
int i;
for(; *l && n > 0; n -= i){
b = *l;
i = BLEN(b);
if(i > n)
i = n;
memmove(p, b->rp, i);
b->rp += i;
p += i;
if(BLEN(b) < 0)
panic("consume");
if(BLEN(b))
break;
*l = b->next;
freeb(b);
}
}
/*
* 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;
first = *l;
for(b = first; b; b = b->next){
i = BLEN(b);
if(i == n){
if(discard){
freeblist(b->next);
*l = 0;
} else
*l = b->next;
b->next = 0;
return first;
} else if(i > n){
i -= n;
if(discard){
freeblist(b->next);
*l = 0;
} else {
nb = allocb(i);
memmove(nb->wp, b->rp+n, i);
nb->wp += i;
nb->next = b->next;
*l = nb;
}
b->wp -= i;
b->next = 0;
if(BLEN(b) < 0)
panic("qtake");
return first;
} else
n -= i;
if(BLEN(b) < 0)
panic("qtake");
}
*l = 0;
return first;
}
static Block*
sslbread(Chan *c, long n, ulong offset)
{
volatile struct { Dstate *s; } s;
volatile struct { int nc; } nc;
Block *b;
uchar count[3];
int len, pad;
USED(offset);
s.s = dstate[CONV(c->qid)];
if(s.s == 0)
panic("sslbread");
if(s.s->state == Sincomplete)
error(Ebadusefd);
nc.nc = 0;
if(waserror()){
qunlock(&s.s->in.q);
if(strcmp(up->env->errstr, "interrupted") == 0){
if(nc.nc > 0){
b = allocb(nc.nc);
memmove(b->wp, count, nc.nc);
b->wp += nc.nc;
b->next = s.s->unprocessed;
s.s->unprocessed = b;
}
} else
sslhangup(s.s);
nexterror();
}
qlock(&s.s->in.q);
if(s.s->processed == 0){
/* read in the whole message */
ensure(s.s, &s.s->unprocessed, 2);
consume(&s.s->unprocessed, count, 2);
nc.nc += 2;
if(count[0] & 0x80){
len = ((count[0] & 0x7f)<<8) | count[1];
ensure(s.s, &s.s->unprocessed, len);
pad = 0;
} else {
len = ((count[0] & 0x3f)<<8) | count[1];
ensure(s.s, &s.s->unprocessed, len+1);
consume(&s.s->unprocessed, count + nc.nc, 1);
pad = count[nc.nc];
nc.nc++;
if(pad > len){
print("pad %d buf len %d\n", pad, len);
error("bad pad in ssl message");
}
}
nc.nc = 0;
/* put extra on unprocessed queue */
s.s->processed = qtake(&s.s->unprocessed, len, 0);
if(waserror()){
qunlock(&s.s->in.ctlq);
nexterror();
}
qlock(&s.s->in.ctlq);
switch(s.s->state){
case Sencrypting:
s.s->processed = decryptb(s.s, s.s->processed);
break;
case Sdigesting:
s.s->processed = pullupblock(s.s->processed, s.s->diglen);
if(s.s->processed == 0)
error("ssl message too short");
checkdigestb(s.s, s.s->processed);
s.s->processed->rp += s.s->diglen;
break;
case Sdigenc:
s.s->processed = decryptb(s.s, s.s->processed);
s.s->processed = pullupblock(s.s->processed, s.s->diglen);
if(s.s->processed == 0)
error("ssl message too short");
checkdigestb(s.s, s.s->processed);
s.s->processed->rp += s.s->diglen;
len -= s.s->diglen;
break;
}
s.s->in.mid++;
qunlock(&s.s->in.ctlq);
poperror();
/* remove pad */
if(pad)
s.s->processed = qtake(&s.s->processed, len - pad, 1);
}
/* return at most what was asked for */
b = qtake(&s.s->processed, n, 0);
qunlock(&s.s->in.q);
poperror();
return b;
}
static long
sslread(Chan *c, void *a, long n, vlong offset)
{
volatile struct { Block *b; } b;
Block *nb;
uchar *va;
int i;
char buf[128];
if(c->qid.type & QTDIR)
return devdirread(c, a, n, 0, 0, sslgen);
switch(TYPE(c->qid)) {
default:
error(Ebadusefd);
case Qctl:
sprint(buf, "%ld", CONV(c->qid));
return readstr(offset, a, n, buf);
case Qdata:
b.b = sslbread(c, n, offset);
break;
case Qencalgs:
return readstr(offset, a, n, encalgs);
case Qhashalgs:
return readstr(offset, a, n, hashalgs);
}
n = 0;
va = a;
for(nb = b.b; nb; nb = nb->next){
i = BLEN(nb);
memmove(va+n, nb->rp, i);
n += i;
}
freeblist(b.b);
return n;
}
/*
* this algorithm doesn't have to be great since we're just
* trying to obscure the block fill
*/
static void
randfill(uchar *buf, int len)
{
while(len-- > 0)
*buf++ = nrand(256);
}
/*
* use SSL record format, add in count and digest or encrypt
*/
static long
sslbwrite(Chan *c, Block *b, ulong offset)
{
volatile struct { Dstate *s; } s;
volatile struct { Block *b; } bb;
Block *nb;
int h, n, m, pad, rv;
uchar *p;
bb.b = b;
s.s = dstate[CONV(c->qid)];
if(s.s == 0)
panic("sslbwrite");
if(s.s->state == Sincomplete){
freeb(b);
error(Ebadusefd);
}
if(waserror()){
qunlock(&s.s->out.q);
if(bb.b)
freeb(bb.b);
sslhangup(s.s);
nexterror();
}
qlock(&s.s->out.q);
rv = 0;
while(bb.b){
m = n = BLEN(bb.b);
h = s.s->diglen + 2;
/* trim to maximum block size */
pad = 0;
if(m > s.s->max){
m = s.s->max;
} else if(s.s->blocklen != 1){
pad = (m + s.s->diglen)%s.s->blocklen;
if(pad){
if(m > s.s->maxpad){
pad = 0;
m = s.s->maxpad;
} else {
pad = s.s->blocklen - pad;
h++;
}
}
}
rv += m;
if(m != n){
nb = allocb(m + h + pad);
memmove(nb->wp + h, bb.b->rp, m);
nb->wp += m + h;
bb.b->rp += m;
} else {
/* add header space */
nb = padblock(bb.b, h);
bb.b = 0;
}
m += s.s->diglen;
/* SSLv2 style count */
if(pad){
nb = padblock(nb, -pad);
randfill(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.s->state){
case Sencrypting:
nb = encryptb(s.s, nb, offset);
break;
case Sdigesting:
nb = digestb(s.s, nb, offset);
break;
case Sdigenc:
nb = digestb(s.s, nb, offset);
nb = encryptb(s.s, nb, offset);
break;
}
s.s->out.mid++;
m = BLEN(nb);
devtab[s.s->c->type]->bwrite(s.s->c, nb, s.s->c->offset);
s.s->c->offset += m;
}
qunlock(&s.s->out.q);
poperror();
return rv;
}
static void
setsecret(OneWay *w, uchar *secret, int n)
{
free(w->secret);
w->secret = mallocz(n, 0);
if(w->secret == nil)
error(Enomem);
memmove(w->secret, secret, n);
w->slen = n;
}
static void
initIDEAkey(OneWay *w)
{
free(w->state);
w->state = malloc(sizeof(IDEAstate));
if(w->state == nil)
error(Enomem);
if(w->slen >= 24)
setupIDEAstate(w->state, w->secret, w->secret+16);
else if(w->slen >= 16)
setupIDEAstate(w->state, w->secret, 0);
else
error("secret too short");
}
static void
initDESkey(OneWay *w)
{
free(w->state);
w->state = malloc(sizeof(DESstate));
if (!w->state)
error(Enomem);
if(w->slen >= 16)
setupDESstate(w->state, w->secret, w->secret+8);
else if(w->slen >= 8)
setupDESstate(w->state, w->secret, 0);
else
error("secret too short");
}
/*
* 40 bit DES is the same as 56 bit DES. However,
* 16 bits of the key are masked to zero.
*/
static void
initDESkey_40(OneWay *w)
{
uchar key[8];
if(w->slen >= 8) {
memmove(key, w->secret, 8);
key[0] &= 0x0f;
key[2] &= 0x0f;
key[4] &= 0x0f;
key[6] &= 0x0f;
}
free(w->state);
w->state = malloc(sizeof(DESstate));
if (!w->state)
error(Enomem);
if(w->slen >= 16)
setupDESstate(w->state, key, w->secret+8);
else if(w->slen >= 8)
setupDESstate(w->state, key, 0);
else
error("secret too short");
}
static void
initRC4key(OneWay *w)
{
free(w->state);
w->state = malloc(sizeof(RC4state));
if (!w->state)
error(Enomem);
setupRC4state(w->state, w->secret, w->slen);
}
/*
* 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)
{
int slen = w->slen;
if(slen > 5)
slen = 5;
free(w->state);
w->state = malloc(sizeof(RC4state));
if (!w->state)
error(Enomem);
setupRC4state(w->state, w->secret, slen);
}
/*
* 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)
{
int slen = w->slen;
if(slen > 16)
slen = 16;
free(w->state);
w->state = malloc(sizeof(RC4state));
if (!w->state)
error(Enomem);
setupRC4state(w->state, w->secret, slen);
}
typedef struct Hashalg Hashalg;
struct Hashalg
{
char *name;
int diglen;
DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);
};
Hashalg hashtab[] =
{
{ "md4", MD4dlen, md4, },
{ "md5", MD5dlen, md5, },
{ "sha1", SHA1dlen, sha1, },
{ "sha", SHA1dlen, sha1, },
{ 0 }
};
static int
parsehashalg(char *p, Dstate *s)
{
Hashalg *ha;
for(ha = hashtab; ha->name; ha++){
if(strcmp(p, ha->name) == 0){
s->hf = ha->hf;
s->diglen = ha->diglen;
s->state &= ~Sclear;
s->state |= Sdigesting;
return 0;
}
}
return -1;
}
typedef struct Encalg Encalg;
struct Encalg
{
char *name;
int blocklen;
int alg;
void (*keyinit)(OneWay*);
};
Encalg encrypttab[] =
{
{ "descbc", 8, DESCBC, initDESkey, }, /* DEPRECATED -- use des_56_cbc */
{ "desecb", 8, DESECB, initDESkey, }, /* DEPRECATED -- use des_56_ecb */
{ "des_56_cbc", 8, DESCBC, initDESkey, },
{ "des_56_ecb", 8, DESECB, initDESkey, },
{ "des_40_cbc", 8, DESCBC, initDESkey_40, },
{ "des_40_ecb", 8, DESECB, initDESkey_40, },
{ "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */
{ "rc4_256", 1, RC4, initRC4key, },
{ "rc4_128", 1, RC4, initRC4key_128, },
{ "rc4_40", 1, RC4, initRC4key_40, },
{ "ideacbc", 8, IDEACBC, initIDEAkey, },
{ "ideaecb", 8, IDEAECB, initIDEAkey, },
{ 0 }
};
static int
parseencryptalg(char *p, Dstate *s)
{
Encalg *ea;
for(ea = encrypttab; ea->name; ea++){
if(strcmp(p, ea->name) == 0){
s->encryptalg = ea->alg;
s->blocklen = ea->blocklen;
(*ea->keyinit)(&s->in);
(*ea->keyinit)(&s->out);
s->state &= ~Sclear;
s->state |= Sencrypting;
return 0;
}
}
return -1;
}
static void
alglistinit(void)
{
Hashalg *h;
Encalg *e;
int n;
n = 1;
for(e = encrypttab; e->name != nil; e++)
n += strlen(e->name) + 1;
encalgs = malloc(n);
if(encalgs == nil)
panic("sslinit");
n = 0;
for(e = encrypttab; e->name != nil; e++){
strcpy(encalgs+n, e->name);
n += strlen(e->name);
if(e[1].name == nil)
break;
encalgs[n++] = ' ';
}
encalgs[n] = 0;
n = 1;
for(h = hashtab; h->name != nil; h++)
n += strlen(h->name) + 1;
hashalgs = malloc(n);
if(hashalgs == nil)
panic("sslinit");
n = 0;
for(h = hashtab; h->name != nil; h++){
strcpy(hashalgs+n, h->name);
n += strlen(h->name);
if(h[1].name == nil)
break;
hashalgs[n++] = ' ';
}
hashalgs[n] = 0;
}
static long
sslwrite(Chan *c, void *a, long n, vlong offset)
{
volatile struct { Dstate *s; } s;
volatile struct { Block *b; } b;
int m, t;
char *p, *np, *e, buf[32];
uchar *x;
s.s = dstate[CONV(c->qid)];
if(s.s == 0)
panic("sslwrite");
t = TYPE(c->qid);
if(t == Qdata){
if(s.s->state == Sincomplete)
error(Ebadusefd);
p = a;
e = p + n;
do {
m = e - p;
if(m > s.s->max)
m = s.s->max;
b.b = allocb(m);
memmove(b.b->wp, p, m);
b.b->wp += m;
sslbwrite(c, b.b, offset);
p += m;
} while(p < e);
return n;
}
/* mutex with operations using what we're about to change */
if(waserror()){
qunlock(&s.s->in.ctlq);
qunlock(&s.s->out.q);
nexterror();
}
qlock(&s.s->in.ctlq);
qlock(&s.s->out.q);
switch(t){
default:
panic("sslwrite");
case Qsecretin:
setsecret(&s.s->in, a, n);
goto out;
case Qsecretout:
setsecret(&s.s->out, a, n);
goto out;
case Qctl:
break;
}
if(n >= sizeof(buf))
error(Ebadarg);
strncpy(buf, a, n);
buf[n] = 0;
p = strchr(buf, '\n');
if(p)
*p = 0;
p = strchr(buf, ' ');
if(p)
*p++ = 0;
if(strcmp(buf, "fd") == 0){
s.s->c = buftochan(p);
/* default is clear (msg delimiters only) */
s.s->state = Sclear;
s.s->blocklen = 1;
s.s->diglen = 0;
s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
s.s->in.mid = 0;
s.s->out.mid = 0;
} else if(strcmp(buf, "alg") == 0 && p != 0){
s.s->blocklen = 1;
s.s->diglen = 0;
if(s.s->c == 0)
error("must set fd before algorithm");
if(strcmp(p, "clear") == 0){
s.s->state = Sclear;
s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
goto out;
}
if(s.s->in.secret && s.s->out.secret == 0)
setsecret(&s.s->out, s.s->in.secret, s.s->in.slen);
if(s.s->out.secret && s.s->in.secret == 0)
setsecret(&s.s->in, s.s->out.secret, s.s->out.slen);
if(s.s->in.secret == 0 || s.s->out.secret == 0)
error("algorithm but no secret");
s.s->hf = 0;
s.s->encryptalg = Noencryption;
s.s->blocklen = 1;
for(;;){
np = strchr(p, ' ');
if(np)
*np++ = 0;
else{
np = strchr(p, '/');
if(np)
*np++ = 0;
}
if(parsehashalg(p, s.s) < 0)
if(parseencryptalg(p, s.s) < 0)
error(Ebadarg);
if(np == 0)
break;
p = np;
}
if(s.s->hf == 0 && s.s->encryptalg == Noencryption)
error(Ebadarg);
if(s.s->blocklen != 1){
/* make multiple of blocklen */
s.s->max = (1<<15) - s.s->diglen - 1;
s.s->max -= s.s->max % s.s->blocklen;
s.s->maxpad = (1<<14) - s.s->diglen - 1;
s.s->maxpad -= s.s->maxpad % s.s->blocklen;
} else
s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
} else if(strcmp(buf, "secretin") == 0 && p != 0) {
m = (strlen(p)*3)/2;
x = smalloc(m);
if(waserror()){
free(x);
nexterror();
}
t = dec64(x, m, p, strlen(p));
setsecret(&s.s->in, x, t);
poperror();
free(x);
} else if(strcmp(buf, "secretout") == 0 && p != 0) {
m = (strlen(p)*3)/2;
x = smalloc(m);
if(waserror()){
free(x);
nexterror();
}
t = dec64(x, m, p, strlen(p));
setsecret(&s.s->out, x, t);
poperror();
free(x);
} else
error(Ebadarg);
out:
qunlock(&s.s->in.ctlq);
qunlock(&s.s->out.q);
poperror();
return n;
}
static Block*
encryptb(Dstate *s, Block *b, int offset)
{
uchar *p, *ep, *p2, *ip, *eip;
DESstate *ds;
IDEAstate *is;
switch(s->encryptalg){
case DESECB:
ds = s->out.state;
ep = b->rp + BLEN(b);
for(p = b->rp + offset; p < ep; p += 8)
block_cipher(ds->expanded, p, 0);
break;
case DESCBC:
ds = s->out.state;
ep = b->rp + BLEN(b);
for(p = b->rp + offset; p < ep; p += 8){
p2 = p;
ip = ds->ivec;
for(eip = ip+8; ip < eip; )
*p2++ ^= *ip++;
block_cipher(ds->expanded, p, 0);
memmove(ds->ivec, p, 8);
}
break;
case IDEAECB:
is = s->out.state;
ep = b->rp + BLEN(b);
for(p = b->rp + offset; p < ep; p += 8)
idea_cipher(is->edkey, p, 0);
break;
case IDEACBC:
is = s->out.state;
ep = b->rp + BLEN(b);
for(p = b->rp + offset; p < ep; p += 8){
p2 = p;
ip = is->ivec;
for(eip = ip+8; ip < eip; )
*p2++ ^= *ip++;
idea_cipher(is->edkey, p, 0);
memmove(is->ivec, p, 8);
}
break;
case RC4:
rc4(s->out.state, b->rp + offset, BLEN(b) - offset);
break;
}
return b;
}
static Block*
decryptb(Dstate *s, Block *inb)
{
Block *b, **l;
uchar *p, *ep, *tp, *ip, *eip;
DESstate *ds;
IDEAstate *is;
uchar tmp[8];
int i;
l = &inb;
for(b = inb; 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 IDEAECB:
is = s->in.state;
ep = b->rp + BLEN(b);
for(p = b->rp; p < ep; p += 8)
idea_cipher(is->edkey, p, 1);
break;
case IDEACBC:
is = s->in.state;
ep = b->rp + BLEN(b);
for(p = b->rp; p < ep;){
memmove(tmp, p, 8);
idea_cipher(is->edkey, p, 1);
tp = tmp;
ip = is->ivec;
for(eip = ip+8; ip < eip; ){
*p++ ^= *ip;
*ip++ = *tp++;
}
}
break;
case RC4:
rc4(s->in.state, b->rp, BLEN(b));
break;
}
}
return inb;
}
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;
/* hash secret + message */
(*s->hf)(w->secret, w->slen, 0, &ss);
(*s->hf)(b->rp + h, n, 0, &ss);
/* hash message id */
p = msgid;
n = w->mid;
*p++ = n>>24;
*p++ = n>>16;
*p++ = n>>8;
*p = n;
(*s->hf)(msgid, 4, b->rp + offset, &ss);
return b;
}
static void
checkdigestb(Dstate *s, Block *inb)
{
uchar *p;
DigestState ss;
uchar msgid[4];
int n, h;
OneWay *w;
uchar digest[128];
Block *b;
w = &s->in;
memset(&ss, 0, sizeof(ss));
/* hash secret */
(*s->hf)(w->secret, w->slen, 0, &ss);
/* hash message */
h = s->diglen;
for(b = inb; b; b = b->next){
n = BLEN(b) - h;
if(n < 0)
panic("checkdigestb");
(*s->hf)(b->rp + h, n, 0, &ss);
h = 0;
}
/* hash message id */
p = msgid;
n = w->mid;
*p++ = n>>24;
*p++ = n>>16;
*p++ = n>>8;
*p = n;
(*s->hf)(msgid, 4, digest, &ss);
/* requires pullupblock */
if(memcmp(digest, inb->rp, s->diglen) != 0)
error("bad digest");
}
/* get channel associated with an fd */
static Chan*
buftochan(char *p)
{
Chan *c;
int fd;
if(p == 0)
error(Ebadarg);
fd = strtoul(p, 0, 0);
if(fd < 0)
error(Ebadarg);
c = fdtochan(up->env->fgrp, fd, -1, 0, 1); /* error check and inc ref */
return c;
}
/* hang up a digest connection */
static void
sslhangup(Dstate *s)
{
qlock(&s->in.q);
freeblist(s->processed);
s->processed = 0;
freeblist(s->unprocessed);
s->unprocessed = 0;
s->state = Sincomplete;
qunlock(&s->in.q);
}
static void
dsclone(Chan *ch)
{
Dstate **pp, **ep, **np;
int newmax;
lock(&dslock);
if(waserror()) {
unlock(&dslock);
nexterror();
}
ep = &dstate[maxdstate];
for(pp = dstate; pp < ep; pp++) {
if(*pp == 0) {
dsnew(ch, pp);
break;
}
}
if(pp >= ep) {
if(maxdstate >= Maxdstate)
error(Enodev);
newmax = 2 * maxdstate;
if(newmax > Maxdstate)
newmax = Maxdstate;
np = realloc(dstate, sizeof(Dstate*) * newmax);
if(np == 0)
error(Enomem);
dstate = np;
pp = &dstate[maxdstate];
memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate));
maxdstate = newmax;
dsnew(ch, pp);
}
poperror();
unlock(&dslock);
}
static void
dsnew(Chan *ch, Dstate **pp)
{
Dstate *s;
int t;
*pp = s = mallocz(sizeof(*s), 1);
if(s == nil)
error(Enomem);
if(pp - dstate >= dshiwat)
dshiwat++;
s->state = Sincomplete;
s->ref = 1;
kstrdup(&s->user, up->env->user);
s->perm = 0660;
t = TYPE(ch->qid);
if(t == Qclonus)
t = Qctl;
ch->qid.path = QID(pp - dstate, t);
ch->qid.vers = 0;
ch->qid.type = QTFILE;
}
Dev ssldevtab = {
'D',
"ssl",
sslinit,
sslattach,
sslwalk,
sslstat,
sslopen,
devcreate,
sslclose,
sslread,
sslbread,
sslwrite,
sslbwrite,
devremove,
sslwstat
};