~kris/9p

9hist

37d56a94d78f271c7d1a52333b97934172b450e2 — David du Colombier 27 years ago 420a570
Plan 9 from Bell Labs 1999-09-07
1 files changed, 406 insertions(+), 46 deletions(-)

M port/devsdp.c
M port/devsdp.c => port/devsdp.c +406 -46
@@ 8,11 8,14 @@

#include	<libcrypt.h>

/*
 * sdp - secure datagram protocol
 */

typedef struct Sdp Sdp;
typedef struct Conv Conv;
typedef struct Out Out;
typedef struct In In;
typedef struct ConPkt ConPkt;
typedef struct OneWay OneWay;
typedef struct ConnectPkt ConnectPkt;

enum
{


@@ 31,56 34,40 @@ enum

	MaxQ,

	Maxconv=	256,		// power of 2
	Nfs=		4,			// number of file systems
	Maxconv= 256,		// power of 2
	Nfs= 4,				// number of file systems
	Maxretries=	4,
	KeyLength= 32,
};

#define TYPE(x) 	((x).path & 0xff)
#define CONV(x) 	(((x).path >> 8)&(Maxconv-1))
#define QID(x, y) 	(((x)<<8) | (y))

struct Out
{
	ulong	seqwrap;	// number of wraps of the sequence number
	ulong	seq;

	Block	*controlpkt;		// control channel
	ulong	*controlseq;

	void	*cipherstate;	// state cipher
	int		ivlen;			// in bytes
	int		(*encrypt)(Out*, uchar *buf, int len);

	void	*authstate;		// auth state
	int		authlen;		// auth data length in bytes
	int		(*auth)(Out*, uchar *buf, int len);

	void	*compstate;
	int		(*comp)(Out*, uchar *dst, uchar *src, int n);
};

struct In
struct OneWay
{
	ulong	seqwrap;	// number of wraps of the sequence number
	ulong	seq;
	ulong	window;

	Block	*controlpkt;
	Rendez	controlready;
	Block	*controlpkt;		// control channel
	ulong	controlseq;

	void	*cipherstate;	// state cipher
	int		ivlen;			// in bytes
	int		(*decrypt)(In*, uchar *buf, int len);
	int		cipherivlen;	// initial vector length
	int		cipherblklen;	// block length
	int		(*cipher)(OneWay*, uchar *buf, int len);

	void	*authstate;		// auth state
	int		authlen;		// auth data length in bytes
	int		(*auth)(In*, uchar *buf, int len);
	int		(*auth)(OneWay*, uchar *buf, int len);

	void	*uncompstate;
	int		(*uncomp)(In*, uchar *dst, uchar *src, int n);
	void	*compstate;
	int		(*comp)(OneWay*, uchar *dst, uchar *src, int n);
};

// conv states
enum {
	CInit,
	COpening,


@@ 98,7 85,6 @@ struct Conv {
	int state;
	int dataopen;

	Proc *readproc;

	ulong	timeout;
	int		retries;


@@ 107,15 93,19 @@ struct Conv {
	ulong dialid;
	ulong acceptid;

	Proc *readproc;
	QLock readlk;
	Chan *chan;	// packet channel

	char	user[NAMELEN];		/* protections */
	char owner[NAMELEN];		/* protections */
	int	perm;

	uchar	masterkey[KeyLength];

	int drop;

	In	in;
	Out	out;
	OneWay	in;
	OneWay	out;
};

struct Sdp {


@@ 144,7 134,7 @@ enum {
	ConCloseAck,
};

struct ConPkt
struct ConnectPkt
{
	uchar type;		// always zero = connection packet
	uchar op;


@@ 153,6 143,7 @@ struct ConPkt
	uchar acceptid[4];
};


static Dirtab sdpdirtab[]={
	"stats",	{Qstats},	0,	0444,
	"log",		{Qlog},		0,	0666,


@@ 192,8 183,17 @@ static Sdp sdptab[Nfs];
static int sdpgen(Chan *c, Dirtab*, int, int s, Dir *dp);
static Conv *sdpclone(Sdp *sdp);
static void convsetstate(Conv *c, int state);
static void sendconnect(Conv *c, int op, ulong dialid, ulong acceptid);
static void sdpackproc(void *a);
static void onewaycleanup(OneWay *ow);
static int readready(void *a);
static int controlread();
static Block *conviput(Conv *c, Block *b, int control);
static void conviput2(Conv *c, Block *b);
static Block *readcontrol(Conv *c, int n);
static Block *readdata(Conv *c, int n);
static void convoput(Conv *c, int type, Block *b);
static void convoput2(Conv *c, int op, ulong dialid, ulong acceptid);


static void
sdpinit(void)


@@ 315,9 315,11 @@ sdpopen(Chan* ch, int omode)
			nexterror();
		}
		if((perm & (c->perm>>6)) != perm)
		if(strcmp(up->user, c->user) != 0 || (perm & c->perm) != perm)
		if(strcmp(up->user, c->owner) != 0 || (perm & c->perm) != perm)
				error(Eperm);
		c->ref++;
		if(TYPE(ch->qid) == Qdata)
			c->dataopen++;
		qunlock(c);
		poperror();
		break;


@@ 332,12 334,47 @@ static void
sdpclose(Chan* ch)
{
	Sdp *sdp  = sdptab + ch->dev;
	Conv *c;

	switch(TYPE(ch->qid)) {
	case Qlog:
		if(ch->flag & COPEN)
			logclose(sdp);
		break;
	case Qdata:
	case Qctl:
	case Qstatus:
	case Qcontrol:
		if(!(ch->flag & COPEN))
			break;
		c = sdp->conv[CONV(ch->qid)];
		qlock(c);
		if(waserror()) {
			qunlock(c);
			nexterror();
		}
		c->ref--;
		if(TYPE(ch->qid) == Qdata) {
			c->dataopen--;
			if(c->dataopen == 0)
				wakeup(&c->in.controlready);
		}

		if(c->ref == 0) {
			switch(c->state) {
			default:
				convsetstate(c, CClosed);
				break;
			case COpen:
				convsetstate(c, CClosing);
				break;
			case CClosing:
				break;
			}
		}
		qunlock(c);
		poperror();
		break;
	}
}



@@ 348,6 385,7 @@ sdpread(Chan *ch, void *a, long n, vlong off)
	Sdp *sdp = sdptab + ch->dev;
	char *s;
	Conv *c;
	Block *b;

	USED(off);
	switch(TYPE(ch->qid)) {


@@ 384,9 422,37 @@ sdpread(Chan *ch, void *a, long n, vlong off)
	case Qctl:
		sprint(buf, "%lud", CONV(ch->qid));
		return readstr(off, a, n, buf);
	case Qcontrol:
		b = readcontrol(sdp->conv[CONV(ch->qid)], n);
		if(b == nil)
			return 0;
		if(BLEN(b) < n)
			n = BLEN(b);
		memmove(a, b->rp, n);
		freeb(b);
		return n;
	case Qdata:
		b = readdata(sdp->conv[CONV(ch->qid)], n);
		if(b == nil)
			return 0;
		if(BLEN(b) < n)
			n = BLEN(b);
		memmove(a, b->rp, n);
		freeb(b);
		return n;
	}
}

static Block*
sdpbread(Chan* ch, long n, ulong offset)
{
	Sdp *sdp = sdptab + ch->dev;

	if(TYPE(ch->qid) != Qdata)
		return devbread(ch, n, offset);
	return readdata(sdp->conv[CONV(ch->qid)], n);
}

static long
sdpwrite(Chan *ch, void *a, long n, vlong off)
{


@@ 538,7 604,7 @@ sdpclone(Sdp *sdp)
	c->ref++;
	c->state = CInit;

	strncpy(c->user, up->user, sizeof(c->user));
	strncpy(c->owner, up->user, sizeof(c->owner));
	c->perm = 0660;
	qunlock(c);



@@ 573,7 639,7 @@ convtimer(Conv *c, ulong sec)
	case COpening:
print("COpening timeout\n");
		if(convretry(c))
			sendconnect(c, ConOpen, c->dialid, 0);
			convoput2(c, ConOpen, c->dialid, 0);
		break;
	case COpen:
		// check for control packet


@@ 581,7 647,7 @@ print("COpening timeout\n");
	case CClosing:
print("CClosing timeout\n");
		if(convretry(c))
			sendconnect(c, ConClose, c->dialid, c->acceptid);
			convoput2(c, ConClose, c->dialid, c->acceptid);
		break;
	}
	qunlock(c);


@@ 636,6 702,9 @@ Dev sdpdevtab = {
static void
convsetstate(Conv *c, int state)
{

print("convsetstate %d -> %d\n", c->state, state);

	switch(state) {
	default:
		panic("setstate: bad state: %d", state);


@@ 645,7 714,7 @@ convsetstate(Conv *c, int state)
		c->dialid = (rand()<<16) + rand();
		c->timeout = TK2SEC(m->ticks) + 2;
		c->retries = 0;
		sendconnect(c, ConOpen, c->dialid, 0);
		convoput2(c, ConOpen, c->dialid, 0);
		break;
	case COpen:
		switch(c->state) {


@@ 653,7 722,7 @@ convsetstate(Conv *c, int state)
			error("convsetstate: illegal transition");
		case CInit:
			c->acceptid = (rand()<<16) + rand();
			sendconnect(c, ConOpenAck, c->dialid, c->acceptid);
			convoput2(c, ConOpenAck, c->dialid, c->acceptid);
			break;
		case COpening:
			break;


@@ 661,19 730,230 @@ convsetstate(Conv *c, int state)
		// setup initial key and auth method
		break;
	case CClosing:
		convoput2(c, ConClose, c->dialid, c->acceptid);
		c->timeout = TK2SEC(m->ticks) + 2;
		c->retries = 0;
		break;
	case CClosed:
		if(c->readproc)
			postnote(c->readproc, 1, "interrupt", 0);
		if(c->ref)
			break;
		if(c->chan) {	
			cclose(c->chan);
			c->chan = nil;
		}
		strcpy(c->owner, "network");
		c->perm = 0660;
		c->dialid = 0;
		c->acceptid = 0;
		c->timeout = 0;
		c->retries = 0;
		c->drop = 0;
		memset(c->masterkey, 0, sizeof(c->masterkey));
		onewaycleanup(&c->in);
		onewaycleanup(&c->out);
		break;
	}
	c->state = state;
}

static void
sendconnect(Conv *c, int op, ulong dialid, ulong acceptid)
onewaycleanup(OneWay *ow)
{
	if(ow->controlpkt)
		freeb(ow->controlpkt);
	if(ow->authstate)
		free(ow->authstate);
	if(ow->cipherstate)
		free(ow->cipherstate);
	if(ow->compstate)
		free(ow->compstate);
	memset(ow, 0, sizeof(OneWay));
}


static Block *
convreadblock(Conv *c, int n)
{
	Block *b;

	qlock(&c->readlk);
	if(waserror()) {
		c->readproc = nil;
		qunlock(&c->readlk);
		nexterror();
	}
	qlock(c);
	if(c->state == CClosed) {
		qunlock(c);
		poperror();
		qunlock(&c->readlk);
		return 0;
	}
	c->readproc = up;
	qunlock(c);

	b = devtab[c->chan->type]->bread(c->chan, n, 0);
	c->readproc = nil;
	poperror();
	qunlock(&c->readlk);

	return b;
}


// assume we hold lock for c
static Block *
conviput(Conv *c, Block *b, int control)
{
	int type;
	ulong seq, cseq;

	if(BLEN(b) < 4) {
		freeb(b);
		return nil;
	}
	
	type = b->rp[0];
	if(type == TConnect) {
		conviput2(c, b);
		return nil;
	}

	seq = (b->rp[1]<<16) + (b->rp[2]<<8) + b->rp[3];
	b->rp += 4;

	USED(seq);
	// auth
	// decrypt

	// ok the packet is good

	switch(type) {
	case TControl:
		if(BLEN(b) <= 4)
			break;
		cseq = nhgetl(b->rp);
		if(cseq == c->in.controlseq) {
			// duplicate control packet
			// send ack
			b->wp = b->rp + 4;
			convoput(c, TControlAck, b);
			return nil;
		}

		if(cseq != c->in.controlseq+1)
			break;
	
		c->in.controlseq = cseq;
		b->rp += 4;
		if(control)
			return b;
		c->in.controlpkt = b;
		wakeup(&c->in.controlready);
		return nil;
	case TControlAck:
		if(BLEN(b) != 4)
			break;
		cseq = nhgetl(b->rp);
		if(cseq != c->out.controlseq)
			break;
		freeb(b);
		freeb(c->out.controlpkt);
		c->out.controlpkt = 0;
		wakeup(&c->out.controlready);
		return nil;
	case TData:
		if(control)
			break;
		return b;
	}
print("droping packet %d n=%ld\n", type, BLEN(b));
	freeb(b);
	return nil;
}

// assume hold conv lock
static void
conviput2(Conv *c, Block *b)
{
	ConnectPkt *con;
	ulong dialid;
	ulong acceptid;

	if(BLEN(b) != sizeof(ConnectPkt)) {
		freeb(b);
		return;
	}
	con = (ConnectPkt*)b->rp;
	dialid = nhgetl(con->dialid);
	acceptid = nhgetl(con->acceptid);

print("conviput2: %d %uld %uld\n", con->op, dialid, acceptid);
	switch(con->op) {
	case ConOpen:
		if(c->state != COpen || dialid != c->dialid || acceptid != c->acceptid)
			convoput2(c, ConOpenNack, dialid, acceptid);
		else
			convoput2(c, ConOpenAck, dialid, acceptid);
		break;
	case ConOpenAck:
		if(c->state != COpening || dialid != c->dialid)
			break;
		c->acceptid = acceptid;
		convsetstate(c, COpen);
		break;
	case ConOpenNack:
		if(c->state != COpening || dialid != c->dialid)
			break;
		convsetstate(c, CClosed);
		break;
	case ConClose:
		if(dialid != c->dialid || acceptid != c->acceptid)
			break;
		convsetstate(c, CClosed);
		break;
	case ConCloseAck:
		if(c->state != CClosing || dialid != c->dialid || acceptid != c->acceptid)
			break;
		convsetstate(c, CClosed);
		break;
	}
}

// assume hold conv lock
static void
convoput(Conv *c, int type, Block *b)
{
	// try and compress

	/* Make space to fit sdp header */
	b = padblock(b, 4 + c->out.cipherivlen);
	b->rp[0] = type;
	c->out.seq++;
	if(c->out.seq == (1<<24)) {
		c->out.seq = 0;
		c->out.seqwrap++;
	}
	b->rp[1] = c->out.seq>>16;
	b->rp[2] = c->out.seq>>8;
	b->rp[3] = c->out.seq;
	
	// encrypt
	// auth

	// simulated errors
	if(c->drop && c->drop > nrand(c->drop))
		return;
	devtab[c->chan->type]->bwrite(c->chan, b, 0);
}

// assume hold conv lock
static void
convoput2(Conv *c, int op, ulong dialid, ulong acceptid)
{
	ConPkt con;
	ConnectPkt con;

	if(c->chan == nil) {
print("chan = nil\n");


@@ 690,3 970,83 @@ print("chan = nil\n");
		return;
	devtab[c->chan->type]->write(c->chan, &con, sizeof(con), 0);
}

static int
readready(void *a)
{
	Conv *c = a;

	return (c->state == CClosed) || c->in.controlpkt != nil || c->dataopen == 0;
}

static Block *
readcontrol(Conv *c, int n)
{
	Block *b;

	for(;;) {
		qlock(c);
		if(c->state == CClosed || c->state == CInit) {
			qunlock(c);
			return nil;
		}

		if(c->in.controlpkt != nil) {
			b = c->in.controlpkt;
			c->in.controlpkt = nil;
			qunlock(c);
			return b;
		}
		qunlock(c);

		// hack - this is to avoid gating onto the
		// read which will in general result in excessive
		// context switches.
		// The assumed behavior is that the client will read
		// from the control channel until the session is authenticated
		// at which point it will open the data channel and
		// start reading on that.  After the data channel is opened,
		// read on the channel are required for packets to
		// be delivered to the control channel

		if(c->dataopen) {
			sleep(&c->in.controlready, readready, c);
		} else {
			b = convreadblock(c, n);
			if(b == nil)
				return nil;
			qlock(c);
			if(waserror()) {
				qunlock(c);
				return nil;
			}
			b = conviput(c, b, 1);
			poperror();
			qunlock(c);
			if(b != nil)
				return b;
		}
	}
}

static Block *
readdata(Conv *c, int n)
{
	Block *b;

	for(;;) {
		b = convreadblock(c, n);
		if(b == nil)
			return nil;
		qlock(c);
		if(waserror()) {
			qunlock(c);
			return nil;
		}
		b = conviput(c, b, 0);
		poperror();
		qunlock(c);
		if(b != nil)
			return b;
	}
}