|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <mp.h> | 
|  | #include <libsec.h> | 
|  | #include "SConn.h" | 
|  |  | 
|  | extern int verbose; | 
|  |  | 
|  | typedef struct ConnState { | 
|  | uchar secret[SHA1dlen]; | 
|  | ulong seqno; | 
|  | RC4state rc4; | 
|  | } ConnState; | 
|  |  | 
|  | #undef SS | 
|  | typedef struct SS { | 
|  | int fd;		/* file descriptor for read/write of encrypted data */ | 
|  | int alg;	/* if nonzero, "alg sha rc4_128" */ | 
|  | ConnState in, out; | 
|  | } SS; | 
|  |  | 
|  | static int | 
|  | SC_secret(SConn *conn, uchar *sigma, int direction) | 
|  | { | 
|  | SS *ss = (SS*)(conn->chan); | 
|  | int nsigma = conn->secretlen; | 
|  |  | 
|  | if(direction != 0){ | 
|  | hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->out.secret, nil); | 
|  | hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->in.secret, nil); | 
|  | }else{ | 
|  | hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->out.secret, nil); | 
|  | hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->in.secret, nil); | 
|  | } | 
|  | setupRC4state(&ss->in.rc4, ss->in.secret, 16); /* restrict to 128 bits */ | 
|  | setupRC4state(&ss->out.rc4, ss->out.secret, 16); | 
|  | ss->alg = 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | hash(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen]) | 
|  | { | 
|  | DigestState sha; | 
|  | uchar seq[4]; | 
|  |  | 
|  | seq[0] = seqno>>24; | 
|  | seq[1] = seqno>>16; | 
|  | seq[2] = seqno>>8; | 
|  | seq[3] = seqno; | 
|  | memset(&sha, 0, sizeof sha); | 
|  | sha1(secret, SHA1dlen, nil, &sha); | 
|  | sha1(data, len, nil, &sha); | 
|  | sha1(seq, 4, d, &sha); | 
|  | } | 
|  |  | 
|  | static int | 
|  | verify(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen]) | 
|  | { | 
|  | DigestState sha; | 
|  | uchar seq[4]; | 
|  | uchar digest[SHA1dlen]; | 
|  |  | 
|  | seq[0] = seqno>>24; | 
|  | seq[1] = seqno>>16; | 
|  | seq[2] = seqno>>8; | 
|  | seq[3] = seqno; | 
|  | memset(&sha, 0, sizeof sha); | 
|  | sha1(secret, SHA1dlen, nil, &sha); | 
|  | sha1(data, len, nil, &sha); | 
|  | sha1(seq, 4, digest, &sha); | 
|  | return memcmp(d, digest, SHA1dlen); | 
|  | } | 
|  |  | 
|  | static int | 
|  | SC_read(SConn *conn, uchar *buf, int n) | 
|  | { | 
|  | SS *ss = (SS*)(conn->chan); | 
|  | uchar count[2], digest[SHA1dlen]; | 
|  | int len, nr; | 
|  |  | 
|  | if(read(ss->fd, count, 2) != 2 || (count[0]&0x80) == 0){ | 
|  | snprint((char*)buf,n,"!SC_read invalid count"); | 
|  | return -1; | 
|  | } | 
|  | len = (count[0]&0x7f)<<8 | count[1];	/* SSL-style count; no pad */ | 
|  | if(ss->alg){ | 
|  | len -= SHA1dlen; | 
|  | if(len <= 0 || readn(ss->fd, digest, SHA1dlen) != SHA1dlen){ | 
|  | snprint((char*)buf,n,"!SC_read missing sha1"); | 
|  | return -1; | 
|  | } | 
|  | if(len > n || readn(ss->fd, buf, len) != len){ | 
|  | snprint((char*)buf,n,"!SC_read missing data"); | 
|  | return -1; | 
|  | } | 
|  | rc4(&ss->in.rc4, digest, SHA1dlen); | 
|  | rc4(&ss->in.rc4, buf, len); | 
|  | if(verify(ss->in.secret, buf, len, ss->in.seqno, digest) != 0){ | 
|  | snprint((char*)buf,n,"!SC_read integrity check failed"); | 
|  | return -1; | 
|  | } | 
|  | }else{ | 
|  | if(len <= 0 || len > n){ | 
|  | snprint((char*)buf,n,"!SC_read implausible record length"); | 
|  | return -1; | 
|  | } | 
|  | if( (nr = readn(ss->fd, buf, len)) != len){ | 
|  | snprint((char*)buf,n,"!SC_read expected %d bytes, but got %d", len, nr); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | ss->in.seqno++; | 
|  | return len; | 
|  | } | 
|  |  | 
|  | static int | 
|  | SC_write(SConn *conn, uchar *buf, int n) | 
|  | { | 
|  | SS *ss = (SS*)(conn->chan); | 
|  | uchar count[2], digest[SHA1dlen], enc[Maxmsg+1]; | 
|  | int len; | 
|  |  | 
|  | if(n <= 0 || n > Maxmsg+1){ | 
|  | werrstr("!SC_write invalid n %d", n); | 
|  | return -1; | 
|  | } | 
|  | len = n; | 
|  | if(ss->alg) | 
|  | len += SHA1dlen; | 
|  | count[0] = 0x80 | len>>8; | 
|  | count[1] = len; | 
|  | if(write(ss->fd, count, 2) != 2){ | 
|  | werrstr("!SC_write invalid count"); | 
|  | return -1; | 
|  | } | 
|  | if(ss->alg){ | 
|  | hash(ss->out.secret, buf, n, ss->out.seqno, digest); | 
|  | rc4(&ss->out.rc4, digest, SHA1dlen); | 
|  | memcpy(enc, buf, n); | 
|  | rc4(&ss->out.rc4, enc, n); | 
|  | if(write(ss->fd, digest, SHA1dlen) != SHA1dlen || | 
|  | write(ss->fd, enc, n) != n){ | 
|  | werrstr("!SC_write error on send"); | 
|  | return -1; | 
|  | } | 
|  | }else{ | 
|  | if(write(ss->fd, buf, n) != n){ | 
|  | werrstr("!SC_write error on send"); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | ss->out.seqno++; | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static void | 
|  | SC_free(SConn *conn) | 
|  | { | 
|  | SS *ss = (SS*)(conn->chan); | 
|  |  | 
|  | close(ss->fd); | 
|  | free(ss); | 
|  | free(conn); | 
|  | } | 
|  |  | 
|  | SConn* | 
|  | newSConn(int fd) | 
|  | { | 
|  | SS *ss; | 
|  | SConn *conn; | 
|  |  | 
|  | if(fd < 0) | 
|  | return nil; | 
|  | ss = (SS*)emalloc(sizeof(*ss)); | 
|  | conn = (SConn*)emalloc(sizeof(*conn)); | 
|  | ss->fd  = fd; | 
|  | ss->alg = 0; | 
|  | conn->chan = (void*)ss; | 
|  | conn->secretlen = SHA1dlen; | 
|  | conn->free = SC_free; | 
|  | conn->secret = SC_secret; | 
|  | conn->read = SC_read; | 
|  | conn->write = SC_write; | 
|  | return conn; | 
|  | } | 
|  |  | 
|  | void | 
|  | writerr(SConn *conn, char *s) | 
|  | { | 
|  | char buf[Maxmsg]; | 
|  |  | 
|  | snprint(buf, Maxmsg, "!%s", s); | 
|  | conn->write(conn, (uchar*)buf, strlen(buf)); | 
|  | } | 
|  |  | 
|  | int | 
|  | readstr(SConn *conn, char *s) | 
|  | { | 
|  | int n; | 
|  |  | 
|  | n = conn->read(conn, (uchar*)s, Maxmsg); | 
|  | if(n >= 0){ | 
|  | s[n] = 0; | 
|  | if(s[0] == '!'){ | 
|  | memmove(s, s+1, n); | 
|  | n = -1; | 
|  | } | 
|  | }else{ | 
|  | strcpy(s, "read error"); | 
|  | } | 
|  | return n; | 
|  | } | 
|  |  |