11a4050f5SDavid du Colombier /*
21a4050f5SDavid du Colombier * devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0
31a4050f5SDavid du Colombier */
41a4050f5SDavid du Colombier #include "u.h"
51a4050f5SDavid du Colombier #include "lib.h"
61a4050f5SDavid du Colombier #include "dat.h"
71a4050f5SDavid du Colombier #include "fns.h"
81a4050f5SDavid du Colombier #include "error.h"
91a4050f5SDavid du Colombier
101a4050f5SDavid du Colombier #include "libsec.h"
111a4050f5SDavid du Colombier
121a4050f5SDavid du Colombier typedef struct OneWay OneWay;
131a4050f5SDavid du Colombier typedef struct Secret Secret;
141a4050f5SDavid du Colombier typedef struct TlsRec TlsRec;
151a4050f5SDavid du Colombier typedef struct TlsErrs TlsErrs;
161a4050f5SDavid du Colombier
171a4050f5SDavid du Colombier enum {
181a4050f5SDavid du Colombier Statlen= 1024, /* max. length of status or stats message */
191a4050f5SDavid du Colombier /* buffer limits */
201a4050f5SDavid du Colombier MaxRecLen = 1<<14, /* max payload length of a record layer message */
211a4050f5SDavid du Colombier MaxCipherRecLen = MaxRecLen + 2048,
221a4050f5SDavid du Colombier RecHdrLen = 5,
231a4050f5SDavid du Colombier MaxMacLen = SHA1dlen,
241a4050f5SDavid du Colombier
251a4050f5SDavid du Colombier /* protocol versions we can accept */
261a4050f5SDavid du Colombier TLSVersion = 0x0301,
271a4050f5SDavid du Colombier SSL3Version = 0x0300,
281a4050f5SDavid du Colombier ProtocolVersion = 0x0301, /* maximum version we speak */
291a4050f5SDavid du Colombier MinProtoVersion = 0x0300, /* limits on version we accept */
301a4050f5SDavid du Colombier MaxProtoVersion = 0x03ff,
311a4050f5SDavid du Colombier
321a4050f5SDavid du Colombier /* connection states */
331a4050f5SDavid du Colombier SHandshake = 1 << 0, /* doing handshake */
341a4050f5SDavid du Colombier SOpen = 1 << 1, /* application data can be sent */
351a4050f5SDavid du Colombier SRClose = 1 << 2, /* remote side has closed down */
361a4050f5SDavid du Colombier SLClose = 1 << 3, /* sent a close notify alert */
371a4050f5SDavid du Colombier SAlert = 1 << 5, /* sending or sent a fatal alert */
381a4050f5SDavid du Colombier SError = 1 << 6, /* some sort of error has occured */
391a4050f5SDavid du Colombier SClosed = 1 << 7, /* it is all over */
401a4050f5SDavid du Colombier
411a4050f5SDavid du Colombier /* record types */
421a4050f5SDavid du Colombier RChangeCipherSpec = 20,
431a4050f5SDavid du Colombier RAlert,
441a4050f5SDavid du Colombier RHandshake,
451a4050f5SDavid du Colombier RApplication,
461a4050f5SDavid du Colombier
471a4050f5SDavid du Colombier SSL2ClientHello = 1,
481a4050f5SDavid du Colombier HSSL2ClientHello = 9, /* local convention; see tlshand.c */
491a4050f5SDavid du Colombier
501a4050f5SDavid du Colombier /* alerts */
511a4050f5SDavid du Colombier ECloseNotify = 0,
521a4050f5SDavid du Colombier EUnexpectedMessage = 10,
531a4050f5SDavid du Colombier EBadRecordMac = 20,
541a4050f5SDavid du Colombier EDecryptionFailed = 21,
551a4050f5SDavid du Colombier ERecordOverflow = 22,
561a4050f5SDavid du Colombier EDecompressionFailure = 30,
571a4050f5SDavid du Colombier EHandshakeFailure = 40,
581a4050f5SDavid du Colombier ENoCertificate = 41,
591a4050f5SDavid du Colombier EBadCertificate = 42,
601a4050f5SDavid du Colombier EUnsupportedCertificate = 43,
611a4050f5SDavid du Colombier ECertificateRevoked = 44,
621a4050f5SDavid du Colombier ECertificateExpired = 45,
631a4050f5SDavid du Colombier ECertificateUnknown = 46,
641a4050f5SDavid du Colombier EIllegalParameter = 47,
651a4050f5SDavid du Colombier EUnknownCa = 48,
661a4050f5SDavid du Colombier EAccessDenied = 49,
671a4050f5SDavid du Colombier EDecodeError = 50,
681a4050f5SDavid du Colombier EDecryptError = 51,
691a4050f5SDavid du Colombier EExportRestriction = 60,
701a4050f5SDavid du Colombier EProtocolVersion = 70,
711a4050f5SDavid du Colombier EInsufficientSecurity = 71,
721a4050f5SDavid du Colombier EInternalError = 80,
731a4050f5SDavid du Colombier EUserCanceled = 90,
741a4050f5SDavid du Colombier ENoRenegotiation = 100,
751a4050f5SDavid du Colombier
761a4050f5SDavid du Colombier EMAX = 256
771a4050f5SDavid du Colombier };
781a4050f5SDavid du Colombier
791a4050f5SDavid du Colombier struct Secret
801a4050f5SDavid du Colombier {
811a4050f5SDavid du Colombier char *encalg; /* name of encryption alg */
821a4050f5SDavid du Colombier char *hashalg; /* name of hash alg */
831a4050f5SDavid du Colombier int (*enc)(Secret*, uchar*, int);
841a4050f5SDavid du Colombier int (*dec)(Secret*, uchar*, int);
851a4050f5SDavid du Colombier int (*unpad)(uchar*, int, int);
861a4050f5SDavid du Colombier DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
871a4050f5SDavid du Colombier int block; /* encryption block len, 0 if none */
881a4050f5SDavid du Colombier int maclen;
891a4050f5SDavid du Colombier void *enckey;
901a4050f5SDavid du Colombier uchar mackey[MaxMacLen];
911a4050f5SDavid du Colombier };
921a4050f5SDavid du Colombier
931a4050f5SDavid du Colombier struct OneWay
941a4050f5SDavid du Colombier {
951a4050f5SDavid du Colombier QLock io; /* locks io access */
961a4050f5SDavid du Colombier QLock seclock; /* locks secret paramaters */
971a4050f5SDavid du Colombier ulong seq;
981a4050f5SDavid du Colombier Secret *sec; /* cipher in use */
991a4050f5SDavid du Colombier Secret *new; /* cipher waiting for enable */
1001a4050f5SDavid du Colombier };
1011a4050f5SDavid du Colombier
1021a4050f5SDavid du Colombier struct TlsRec
1031a4050f5SDavid du Colombier {
1041a4050f5SDavid du Colombier Chan *c; /* io channel */
1051a4050f5SDavid du Colombier int ref; /* serialized by tdlock for atomic destroy */
1061a4050f5SDavid du Colombier int version; /* version of the protocol we are speaking */
1071a4050f5SDavid du Colombier char verset; /* version has been set */
1081a4050f5SDavid du Colombier char opened; /* opened command every issued? */
1091a4050f5SDavid du Colombier char err[ERRMAX]; /* error message to return to handshake requests */
1101a4050f5SDavid du Colombier vlong handin; /* bytes communicated by the record layer */
1111a4050f5SDavid du Colombier vlong handout;
1121a4050f5SDavid du Colombier vlong datain;
1131a4050f5SDavid du Colombier vlong dataout;
1141a4050f5SDavid du Colombier
1151a4050f5SDavid du Colombier Lock statelk;
1161a4050f5SDavid du Colombier int state;
1171a4050f5SDavid du Colombier int debug;
1181a4050f5SDavid du Colombier
1191a4050f5SDavid du Colombier /* record layer mac functions for different protocol versions */
1201a4050f5SDavid du Colombier void (*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);
1211a4050f5SDavid du Colombier
1221a4050f5SDavid du Colombier /* input side -- protected by in.io */
1231a4050f5SDavid du Colombier OneWay in;
1241a4050f5SDavid du Colombier Block *processed; /* next bunch of application data */
1251a4050f5SDavid du Colombier Block *unprocessed; /* data read from c but not parsed into records */
1261a4050f5SDavid du Colombier
1271a4050f5SDavid du Colombier /* handshake queue */
1281a4050f5SDavid du Colombier Lock hqlock; /* protects hqref, alloc & free of handq, hprocessed */
1291a4050f5SDavid du Colombier int hqref;
1301a4050f5SDavid du Colombier Queue *handq; /* queue of handshake messages */
1311a4050f5SDavid du Colombier Block *hprocessed; /* remainder of last block read from handq */
1321a4050f5SDavid du Colombier QLock hqread; /* protects reads for hprocessed, handq */
1331a4050f5SDavid du Colombier
1341a4050f5SDavid du Colombier /* output side */
1351a4050f5SDavid du Colombier OneWay out;
1361a4050f5SDavid du Colombier
1371a4050f5SDavid du Colombier /* protections */
1381a4050f5SDavid du Colombier char *user;
1391a4050f5SDavid du Colombier int perm;
1401a4050f5SDavid du Colombier };
1411a4050f5SDavid du Colombier
1421a4050f5SDavid du Colombier struct TlsErrs{
1431a4050f5SDavid du Colombier int err;
1441a4050f5SDavid du Colombier int sslerr;
1451a4050f5SDavid du Colombier int tlserr;
1461a4050f5SDavid du Colombier int fatal;
1471a4050f5SDavid du Colombier char *msg;
1481a4050f5SDavid du Colombier };
1491a4050f5SDavid du Colombier
1501a4050f5SDavid du Colombier static TlsErrs tlserrs[] = {
1511a4050f5SDavid du Colombier {ECloseNotify, ECloseNotify, ECloseNotify, 0, "close notify"},
1521a4050f5SDavid du Colombier {EUnexpectedMessage, EUnexpectedMessage, EUnexpectedMessage, 1, "unexpected message"},
1531a4050f5SDavid du Colombier {EBadRecordMac, EBadRecordMac, EBadRecordMac, 1, "bad record mac"},
1541a4050f5SDavid du Colombier {EDecryptionFailed, EIllegalParameter, EDecryptionFailed, 1, "decryption failed"},
1551a4050f5SDavid du Colombier {ERecordOverflow, EIllegalParameter, ERecordOverflow, 1, "record too long"},
1561a4050f5SDavid du Colombier {EDecompressionFailure, EDecompressionFailure, EDecompressionFailure, 1, "decompression failed"},
1571a4050f5SDavid du Colombier {EHandshakeFailure, EHandshakeFailure, EHandshakeFailure, 1, "could not negotiate acceptable security parameters"},
1581a4050f5SDavid du Colombier {ENoCertificate, ENoCertificate, ECertificateUnknown, 1, "no appropriate certificate available"},
1591a4050f5SDavid du Colombier {EBadCertificate, EBadCertificate, EBadCertificate, 1, "corrupted or invalid certificate"},
1601a4050f5SDavid du Colombier {EUnsupportedCertificate, EUnsupportedCertificate, EUnsupportedCertificate, 1, "unsupported certificate type"},
1611a4050f5SDavid du Colombier {ECertificateRevoked, ECertificateRevoked, ECertificateRevoked, 1, "revoked certificate"},
1621a4050f5SDavid du Colombier {ECertificateExpired, ECertificateExpired, ECertificateExpired, 1, "expired certificate"},
1631a4050f5SDavid du Colombier {ECertificateUnknown, ECertificateUnknown, ECertificateUnknown, 1, "unacceptable certificate"},
1641a4050f5SDavid du Colombier {EIllegalParameter, EIllegalParameter, EIllegalParameter, 1, "illegal parameter"},
1651a4050f5SDavid du Colombier {EUnknownCa, EHandshakeFailure, EUnknownCa, 1, "unknown certificate authority"},
1661a4050f5SDavid du Colombier {EAccessDenied, EHandshakeFailure, EAccessDenied, 1, "access denied"},
1671a4050f5SDavid du Colombier {EDecodeError, EIllegalParameter, EDecodeError, 1, "error decoding message"},
1681a4050f5SDavid du Colombier {EDecryptError, EIllegalParameter, EDecryptError, 1, "error decrypting message"},
1691a4050f5SDavid du Colombier {EExportRestriction, EHandshakeFailure, EExportRestriction, 1, "export restriction violated"},
1701a4050f5SDavid du Colombier {EProtocolVersion, EIllegalParameter, EProtocolVersion, 1, "protocol version not supported"},
1711a4050f5SDavid du Colombier {EInsufficientSecurity, EHandshakeFailure, EInsufficientSecurity, 1, "stronger security routines required"},
1721a4050f5SDavid du Colombier {EInternalError, EHandshakeFailure, EInternalError, 1, "internal error"},
1731a4050f5SDavid du Colombier {EUserCanceled, ECloseNotify, EUserCanceled, 0, "handshake canceled by user"},
1741a4050f5SDavid du Colombier {ENoRenegotiation, EUnexpectedMessage, ENoRenegotiation, 0, "no renegotiation"},
1751a4050f5SDavid du Colombier };
1761a4050f5SDavid du Colombier
1771a4050f5SDavid du Colombier enum
1781a4050f5SDavid du Colombier {
1791a4050f5SDavid du Colombier /* max. open tls connections */
1801a4050f5SDavid du Colombier MaxTlsDevs = 1024
1811a4050f5SDavid du Colombier };
1821a4050f5SDavid du Colombier
1831a4050f5SDavid du Colombier static Lock tdlock;
1841a4050f5SDavid du Colombier static int tdhiwat;
1851a4050f5SDavid du Colombier static int maxtlsdevs = 128;
1861a4050f5SDavid du Colombier static TlsRec **tlsdevs;
1871a4050f5SDavid du Colombier static char **trnames;
1881a4050f5SDavid du Colombier static char *encalgs;
1891a4050f5SDavid du Colombier static char *hashalgs;
1901a4050f5SDavid du Colombier
1911a4050f5SDavid du Colombier enum{
1921a4050f5SDavid du Colombier Qtopdir = 1, /* top level directory */
1931a4050f5SDavid du Colombier Qprotodir,
1941a4050f5SDavid du Colombier Qclonus,
1951a4050f5SDavid du Colombier Qencalgs,
1961a4050f5SDavid du Colombier Qhashalgs,
1971a4050f5SDavid du Colombier Qconvdir, /* directory for a conversation */
1981a4050f5SDavid du Colombier Qdata,
1991a4050f5SDavid du Colombier Qctl,
2001a4050f5SDavid du Colombier Qhand,
2011a4050f5SDavid du Colombier Qstatus,
2021a4050f5SDavid du Colombier Qstats,
2031a4050f5SDavid du Colombier };
2041a4050f5SDavid du Colombier
2051a4050f5SDavid du Colombier #define TYPE(x) ((x).path & 0xf)
2061a4050f5SDavid du Colombier #define CONV(x) (((x).path >> 5)&(MaxTlsDevs-1))
2071a4050f5SDavid du Colombier #define QID(c, y) (((c)<<5) | (y))
2081a4050f5SDavid du Colombier
2091a4050f5SDavid du Colombier static void checkstate(TlsRec *, int, int);
2101a4050f5SDavid du Colombier static void ensure(TlsRec*, Block**, int);
2111a4050f5SDavid du Colombier static void consume(Block**, uchar*, int);
2121a4050f5SDavid du Colombier static Chan* buftochan(char*);
2131a4050f5SDavid du Colombier static void tlshangup(TlsRec*);
2141a4050f5SDavid du Colombier static void tlsError(TlsRec*, char *);
2151a4050f5SDavid du Colombier static void alertHand(TlsRec*, char *);
2161a4050f5SDavid du Colombier static TlsRec *newtls(Chan *c);
2171a4050f5SDavid du Colombier static TlsRec *mktlsrec(void);
2181a4050f5SDavid du Colombier static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2191a4050f5SDavid du Colombier static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2201a4050f5SDavid du Colombier static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2211a4050f5SDavid du Colombier static void sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
2221a4050f5SDavid du Colombier static void tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
2231a4050f5SDavid du Colombier static void put64(uchar *p, vlong x);
2241a4050f5SDavid du Colombier static void put32(uchar *p, u32int);
2251a4050f5SDavid du Colombier static void put24(uchar *p, int);
2261a4050f5SDavid du Colombier static void put16(uchar *p, int);
227*ec59a3ddSDavid du Colombier /* static u32int get32(uchar *p); */
2281a4050f5SDavid du Colombier static int get16(uchar *p);
2291a4050f5SDavid du Colombier static void tlsSetState(TlsRec *tr, int new, int old);
2301a4050f5SDavid du Colombier static void rcvAlert(TlsRec *tr, int err);
2311a4050f5SDavid du Colombier static void sendAlert(TlsRec *tr, int err);
2321a4050f5SDavid du Colombier static void rcvError(TlsRec *tr, int err, char *msg, ...);
2331a4050f5SDavid du Colombier static int rc4enc(Secret *sec, uchar *buf, int n);
2341a4050f5SDavid du Colombier static int des3enc(Secret *sec, uchar *buf, int n);
2351a4050f5SDavid du Colombier static int des3dec(Secret *sec, uchar *buf, int n);
2361a4050f5SDavid du Colombier static int noenc(Secret *sec, uchar *buf, int n);
2371a4050f5SDavid du Colombier static int sslunpad(uchar *buf, int n, int block);
2381a4050f5SDavid du Colombier static int tlsunpad(uchar *buf, int n, int block);
2391a4050f5SDavid du Colombier static void freeSec(Secret *sec);
2401a4050f5SDavid du Colombier static char *tlsstate(int s);
2411a4050f5SDavid du Colombier static void pdump(int, void*, char*);
2421a4050f5SDavid du Colombier
2431a4050f5SDavid du Colombier static char *tlsnames[] = {
244d04cc87cSDavid du Colombier /* unused */ 0,
245d04cc87cSDavid du Colombier /* topdir */ 0,
246d04cc87cSDavid du Colombier /* protodir */ 0,
247*ec59a3ddSDavid du Colombier "clone",
248*ec59a3ddSDavid du Colombier "encalgs",
249*ec59a3ddSDavid du Colombier "hashalgs",
250d04cc87cSDavid du Colombier /* convdir */ 0,
251*ec59a3ddSDavid du Colombier "data",
252*ec59a3ddSDavid du Colombier "ctl",
253*ec59a3ddSDavid du Colombier "hand",
254*ec59a3ddSDavid du Colombier "status",
255*ec59a3ddSDavid du Colombier "stats",
2561a4050f5SDavid du Colombier };
2571a4050f5SDavid du Colombier
2581a4050f5SDavid du Colombier static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats };
2591a4050f5SDavid du Colombier
2601a4050f5SDavid du Colombier static int
tlsgen(Chan * c,char * unused1,Dirtab * unused2,int unused3,int s,Dir * dp)2611a4050f5SDavid du Colombier tlsgen(Chan *c, char*unused1, Dirtab *unused2, int unused3, int s, Dir *dp)
2621a4050f5SDavid du Colombier {
2631a4050f5SDavid du Colombier Qid q;
2641a4050f5SDavid du Colombier TlsRec *tr;
2651a4050f5SDavid du Colombier char *name, *nm;
2661a4050f5SDavid du Colombier int perm, t;
2671a4050f5SDavid du Colombier
2681a4050f5SDavid du Colombier q.vers = 0;
2691a4050f5SDavid du Colombier q.type = QTFILE;
2701a4050f5SDavid du Colombier
2711a4050f5SDavid du Colombier t = TYPE(c->qid);
2721a4050f5SDavid du Colombier switch(t) {
2731a4050f5SDavid du Colombier case Qtopdir:
2741a4050f5SDavid du Colombier if(s == DEVDOTDOT){
2751a4050f5SDavid du Colombier q.path = QID(0, Qtopdir);
2761a4050f5SDavid du Colombier q.type = QTDIR;
2771a4050f5SDavid du Colombier devdir(c, q, "#a", 0, eve, 0555, dp);
2781a4050f5SDavid du Colombier return 1;
2791a4050f5SDavid du Colombier }
2801a4050f5SDavid du Colombier if(s > 0)
2811a4050f5SDavid du Colombier return -1;
2821a4050f5SDavid du Colombier q.path = QID(0, Qprotodir);
2831a4050f5SDavid du Colombier q.type = QTDIR;
2841a4050f5SDavid du Colombier devdir(c, q, "tls", 0, eve, 0555, dp);
2851a4050f5SDavid du Colombier return 1;
2861a4050f5SDavid du Colombier case Qprotodir:
2871a4050f5SDavid du Colombier if(s == DEVDOTDOT){
2881a4050f5SDavid du Colombier q.path = QID(0, Qtopdir);
2891a4050f5SDavid du Colombier q.type = QTDIR;
2901a4050f5SDavid du Colombier devdir(c, q, ".", 0, eve, 0555, dp);
2911a4050f5SDavid du Colombier return 1;
2921a4050f5SDavid du Colombier }
2931a4050f5SDavid du Colombier if(s < 3){
2941a4050f5SDavid du Colombier switch(s) {
2951a4050f5SDavid du Colombier default:
2961a4050f5SDavid du Colombier return -1;
2971a4050f5SDavid du Colombier case 0:
2981a4050f5SDavid du Colombier q.path = QID(0, Qclonus);
2991a4050f5SDavid du Colombier break;
3001a4050f5SDavid du Colombier case 1:
3011a4050f5SDavid du Colombier q.path = QID(0, Qencalgs);
3021a4050f5SDavid du Colombier break;
3031a4050f5SDavid du Colombier case 2:
3041a4050f5SDavid du Colombier q.path = QID(0, Qhashalgs);
3051a4050f5SDavid du Colombier break;
3061a4050f5SDavid du Colombier }
3071a4050f5SDavid du Colombier perm = 0444;
3081a4050f5SDavid du Colombier if(TYPE(q) == Qclonus)
3091a4050f5SDavid du Colombier perm = 0555;
3101a4050f5SDavid du Colombier devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp);
3111a4050f5SDavid du Colombier return 1;
3121a4050f5SDavid du Colombier }
3131a4050f5SDavid du Colombier s -= 3;
3141a4050f5SDavid du Colombier if(s >= tdhiwat)
3151a4050f5SDavid du Colombier return -1;
3161a4050f5SDavid du Colombier q.path = QID(s, Qconvdir);
3171a4050f5SDavid du Colombier q.type = QTDIR;
3181a4050f5SDavid du Colombier lock(&tdlock);
3191a4050f5SDavid du Colombier tr = tlsdevs[s];
3201a4050f5SDavid du Colombier if(tr != nil)
3211a4050f5SDavid du Colombier nm = tr->user;
3221a4050f5SDavid du Colombier else
3231a4050f5SDavid du Colombier nm = eve;
3241a4050f5SDavid du Colombier if((name = trnames[s]) == nil){
3251a4050f5SDavid du Colombier name = trnames[s] = smalloc(16);
3261a4050f5SDavid du Colombier sprint(name, "%d", s);
3271a4050f5SDavid du Colombier }
3281a4050f5SDavid du Colombier devdir(c, q, name, 0, nm, 0555, dp);
3291a4050f5SDavid du Colombier unlock(&tdlock);
3301a4050f5SDavid du Colombier return 1;
3311a4050f5SDavid du Colombier case Qconvdir:
3321a4050f5SDavid du Colombier if(s == DEVDOTDOT){
3331a4050f5SDavid du Colombier q.path = QID(0, Qprotodir);
3341a4050f5SDavid du Colombier q.type = QTDIR;
3351a4050f5SDavid du Colombier devdir(c, q, "tls", 0, eve, 0555, dp);
3361a4050f5SDavid du Colombier return 1;
3371a4050f5SDavid du Colombier }
3381a4050f5SDavid du Colombier if(s < 0 || s >= nelem(convdir))
3391a4050f5SDavid du Colombier return -1;
3401a4050f5SDavid du Colombier lock(&tdlock);
3411a4050f5SDavid du Colombier tr = tlsdevs[CONV(c->qid)];
3421a4050f5SDavid du Colombier if(tr != nil){
3431a4050f5SDavid du Colombier nm = tr->user;
3441a4050f5SDavid du Colombier perm = tr->perm;
3451a4050f5SDavid du Colombier }else{
3461a4050f5SDavid du Colombier perm = 0;
3471a4050f5SDavid du Colombier nm = eve;
3481a4050f5SDavid du Colombier }
3491a4050f5SDavid du Colombier t = convdir[s];
3501a4050f5SDavid du Colombier if(t == Qstatus || t == Qstats)
3511a4050f5SDavid du Colombier perm &= 0444;
3521a4050f5SDavid du Colombier q.path = QID(CONV(c->qid), t);
3531a4050f5SDavid du Colombier devdir(c, q, tlsnames[t], 0, nm, perm, dp);
3541a4050f5SDavid du Colombier unlock(&tdlock);
3551a4050f5SDavid du Colombier return 1;
3561a4050f5SDavid du Colombier case Qclonus:
3571a4050f5SDavid du Colombier case Qencalgs:
3581a4050f5SDavid du Colombier case Qhashalgs:
3591a4050f5SDavid du Colombier perm = 0444;
3601a4050f5SDavid du Colombier if(t == Qclonus)
3611a4050f5SDavid du Colombier perm = 0555;
3621a4050f5SDavid du Colombier devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp);
3631a4050f5SDavid du Colombier return 1;
3641a4050f5SDavid du Colombier default:
3651a4050f5SDavid du Colombier lock(&tdlock);
3661a4050f5SDavid du Colombier tr = tlsdevs[CONV(c->qid)];
3671a4050f5SDavid du Colombier if(tr != nil){
3681a4050f5SDavid du Colombier nm = tr->user;
3691a4050f5SDavid du Colombier perm = tr->perm;
3701a4050f5SDavid du Colombier }else{
3711a4050f5SDavid du Colombier perm = 0;
3721a4050f5SDavid du Colombier nm = eve;
3731a4050f5SDavid du Colombier }
3741a4050f5SDavid du Colombier if(t == Qstatus || t == Qstats)
3751a4050f5SDavid du Colombier perm &= 0444;
3761a4050f5SDavid du Colombier devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp);
3771a4050f5SDavid du Colombier unlock(&tdlock);
3781a4050f5SDavid du Colombier return 1;
3791a4050f5SDavid du Colombier }
3801a4050f5SDavid du Colombier return -1;
3811a4050f5SDavid du Colombier }
3821a4050f5SDavid du Colombier
3831a4050f5SDavid du Colombier static Chan*
tlsattach(char * spec)3841a4050f5SDavid du Colombier tlsattach(char *spec)
3851a4050f5SDavid du Colombier {
3861a4050f5SDavid du Colombier Chan *c;
3871a4050f5SDavid du Colombier
3881a4050f5SDavid du Colombier c = devattach('a', spec);
3891a4050f5SDavid du Colombier c->qid.path = QID(0, Qtopdir);
3901a4050f5SDavid du Colombier c->qid.type = QTDIR;
3911a4050f5SDavid du Colombier c->qid.vers = 0;
3921a4050f5SDavid du Colombier return c;
3931a4050f5SDavid du Colombier }
3941a4050f5SDavid du Colombier
3951a4050f5SDavid du Colombier static Walkqid*
tlswalk(Chan * c,Chan * nc,char ** name,int nname)3961a4050f5SDavid du Colombier tlswalk(Chan *c, Chan *nc, char **name, int nname)
3971a4050f5SDavid du Colombier {
3981a4050f5SDavid du Colombier return devwalk(c, nc, name, nname, nil, 0, tlsgen);
3991a4050f5SDavid du Colombier }
4001a4050f5SDavid du Colombier
4011a4050f5SDavid du Colombier static int
tlsstat(Chan * c,uchar * db,int n)4021a4050f5SDavid du Colombier tlsstat(Chan *c, uchar *db, int n)
4031a4050f5SDavid du Colombier {
4041a4050f5SDavid du Colombier return devstat(c, db, n, nil, 0, tlsgen);
4051a4050f5SDavid du Colombier }
4061a4050f5SDavid du Colombier
4071a4050f5SDavid du Colombier static Chan*
tlsopen(Chan * c,int omode)4081a4050f5SDavid du Colombier tlsopen(Chan *c, int omode)
4091a4050f5SDavid du Colombier {
4101a4050f5SDavid du Colombier TlsRec *tr, **pp;
4111a4050f5SDavid du Colombier int t, perm;
4121a4050f5SDavid du Colombier
4131a4050f5SDavid du Colombier perm = 0;
4141a4050f5SDavid du Colombier omode &= 3;
4151a4050f5SDavid du Colombier switch(omode) {
4161a4050f5SDavid du Colombier case OREAD:
4171a4050f5SDavid du Colombier perm = 4;
4181a4050f5SDavid du Colombier break;
4191a4050f5SDavid du Colombier case OWRITE:
4201a4050f5SDavid du Colombier perm = 2;
4211a4050f5SDavid du Colombier break;
4221a4050f5SDavid du Colombier case ORDWR:
4231a4050f5SDavid du Colombier perm = 6;
4241a4050f5SDavid du Colombier break;
4251a4050f5SDavid du Colombier }
4261a4050f5SDavid du Colombier
4271a4050f5SDavid du Colombier t = TYPE(c->qid);
4281a4050f5SDavid du Colombier switch(t) {
4291a4050f5SDavid du Colombier default:
4301a4050f5SDavid du Colombier panic("tlsopen");
4311a4050f5SDavid du Colombier case Qtopdir:
4321a4050f5SDavid du Colombier case Qprotodir:
4331a4050f5SDavid du Colombier case Qconvdir:
4341a4050f5SDavid du Colombier if(omode != OREAD)
4351a4050f5SDavid du Colombier error(Eperm);
4361a4050f5SDavid du Colombier break;
4371a4050f5SDavid du Colombier case Qclonus:
4381a4050f5SDavid du Colombier tr = newtls(c);
4391a4050f5SDavid du Colombier if(tr == nil)
4401a4050f5SDavid du Colombier error(Enodev);
4411a4050f5SDavid du Colombier break;
4421a4050f5SDavid du Colombier case Qctl:
4431a4050f5SDavid du Colombier case Qdata:
4441a4050f5SDavid du Colombier case Qhand:
4451a4050f5SDavid du Colombier case Qstatus:
4461a4050f5SDavid du Colombier case Qstats:
4471a4050f5SDavid du Colombier if((t == Qstatus || t == Qstats) && omode != OREAD)
4481a4050f5SDavid du Colombier error(Eperm);
4491a4050f5SDavid du Colombier if(waserror()) {
4501a4050f5SDavid du Colombier unlock(&tdlock);
4511a4050f5SDavid du Colombier nexterror();
4521a4050f5SDavid du Colombier }
4531a4050f5SDavid du Colombier lock(&tdlock);
4541a4050f5SDavid du Colombier pp = &tlsdevs[CONV(c->qid)];
4551a4050f5SDavid du Colombier tr = *pp;
4561a4050f5SDavid du Colombier if(tr == nil)
4571a4050f5SDavid du Colombier error("must open connection using clone");
4581a4050f5SDavid du Colombier if((perm & (tr->perm>>6)) != perm
4591a4050f5SDavid du Colombier && (strcmp(up->user, tr->user) != 0
4601a4050f5SDavid du Colombier || (perm & tr->perm) != perm))
4611a4050f5SDavid du Colombier error(Eperm);
4621a4050f5SDavid du Colombier if(t == Qhand){
4631a4050f5SDavid du Colombier if(waserror()){
4641a4050f5SDavid du Colombier unlock(&tr->hqlock);
4651a4050f5SDavid du Colombier nexterror();
4661a4050f5SDavid du Colombier }
4671a4050f5SDavid du Colombier lock(&tr->hqlock);
4681a4050f5SDavid du Colombier if(tr->handq != nil)
4691a4050f5SDavid du Colombier error(Einuse);
470*ec59a3ddSDavid du Colombier tr->handq = qopen(2 * MaxCipherRecLen, 0, 0, nil);
4711a4050f5SDavid du Colombier if(tr->handq == nil)
4721a4050f5SDavid du Colombier error("cannot allocate handshake queue");
4731a4050f5SDavid du Colombier tr->hqref = 1;
4741a4050f5SDavid du Colombier unlock(&tr->hqlock);
4751a4050f5SDavid du Colombier poperror();
4761a4050f5SDavid du Colombier }
4771a4050f5SDavid du Colombier tr->ref++;
4781a4050f5SDavid du Colombier unlock(&tdlock);
4791a4050f5SDavid du Colombier poperror();
4801a4050f5SDavid du Colombier break;
4811a4050f5SDavid du Colombier case Qencalgs:
4821a4050f5SDavid du Colombier case Qhashalgs:
4831a4050f5SDavid du Colombier if(omode != OREAD)
4841a4050f5SDavid du Colombier error(Eperm);
4851a4050f5SDavid du Colombier break;
4861a4050f5SDavid du Colombier }
4871a4050f5SDavid du Colombier c->mode = openmode(omode);
4881a4050f5SDavid du Colombier c->flag |= COPEN;
4891a4050f5SDavid du Colombier c->offset = 0;
4901a4050f5SDavid du Colombier c->iounit = qiomaxatomic;
4911a4050f5SDavid du Colombier return c;
4921a4050f5SDavid du Colombier }
4931a4050f5SDavid du Colombier
4941a4050f5SDavid du Colombier static int
tlswstat(Chan * c,uchar * dp,int n)4951a4050f5SDavid du Colombier tlswstat(Chan *c, uchar *dp, int n)
4961a4050f5SDavid du Colombier {
4971a4050f5SDavid du Colombier Dir *d;
4981a4050f5SDavid du Colombier TlsRec *tr;
4991a4050f5SDavid du Colombier int rv;
5001a4050f5SDavid du Colombier
5011a4050f5SDavid du Colombier d = nil;
5021a4050f5SDavid du Colombier if(waserror()){
5031a4050f5SDavid du Colombier free(d);
5041a4050f5SDavid du Colombier unlock(&tdlock);
5051a4050f5SDavid du Colombier nexterror();
5061a4050f5SDavid du Colombier }
5071a4050f5SDavid du Colombier
5081a4050f5SDavid du Colombier lock(&tdlock);
5091a4050f5SDavid du Colombier tr = tlsdevs[CONV(c->qid)];
5101a4050f5SDavid du Colombier if(tr == nil)
5111a4050f5SDavid du Colombier error(Ebadusefd);
5121a4050f5SDavid du Colombier if(strcmp(tr->user, up->user) != 0)
5131a4050f5SDavid du Colombier error(Eperm);
5141a4050f5SDavid du Colombier
5151a4050f5SDavid du Colombier d = smalloc(n + sizeof *d);
5161a4050f5SDavid du Colombier rv = convM2D(dp, n, &d[0], (char*) &d[1]);
5171a4050f5SDavid du Colombier if(rv == 0)
5181a4050f5SDavid du Colombier error(Eshortstat);
5191a4050f5SDavid du Colombier if(!emptystr(d->uid))
5201a4050f5SDavid du Colombier kstrdup(&tr->user, d->uid);
5211a4050f5SDavid du Colombier if(d->mode != ~0UL)
5221a4050f5SDavid du Colombier tr->perm = d->mode;
5231a4050f5SDavid du Colombier
5241a4050f5SDavid du Colombier free(d);
5251a4050f5SDavid du Colombier poperror();
5261a4050f5SDavid du Colombier unlock(&tdlock);
5271a4050f5SDavid du Colombier
5281a4050f5SDavid du Colombier return rv;
5291a4050f5SDavid du Colombier }
5301a4050f5SDavid du Colombier
5311a4050f5SDavid du Colombier static void
dechandq(TlsRec * tr)5321a4050f5SDavid du Colombier dechandq(TlsRec *tr)
5331a4050f5SDavid du Colombier {
5341a4050f5SDavid du Colombier lock(&tr->hqlock);
5351a4050f5SDavid du Colombier if(--tr->hqref == 0){
5361a4050f5SDavid du Colombier if(tr->handq != nil){
5371a4050f5SDavid du Colombier qfree(tr->handq);
5381a4050f5SDavid du Colombier tr->handq = nil;
5391a4050f5SDavid du Colombier }
5401a4050f5SDavid du Colombier if(tr->hprocessed != nil){
5411a4050f5SDavid du Colombier freeb(tr->hprocessed);
5421a4050f5SDavid du Colombier tr->hprocessed = nil;
5431a4050f5SDavid du Colombier }
5441a4050f5SDavid du Colombier }
5451a4050f5SDavid du Colombier unlock(&tr->hqlock);
5461a4050f5SDavid du Colombier }
5471a4050f5SDavid du Colombier
5481a4050f5SDavid du Colombier static void
tlsclose(Chan * c)5491a4050f5SDavid du Colombier tlsclose(Chan *c)
5501a4050f5SDavid du Colombier {
5511a4050f5SDavid du Colombier TlsRec *tr;
5521a4050f5SDavid du Colombier int t;
5531a4050f5SDavid du Colombier
5541a4050f5SDavid du Colombier t = TYPE(c->qid);
5551a4050f5SDavid du Colombier switch(t) {
5561a4050f5SDavid du Colombier case Qctl:
5571a4050f5SDavid du Colombier case Qdata:
5581a4050f5SDavid du Colombier case Qhand:
5591a4050f5SDavid du Colombier case Qstatus:
5601a4050f5SDavid du Colombier case Qstats:
5611a4050f5SDavid du Colombier if((c->flag & COPEN) == 0)
5621a4050f5SDavid du Colombier break;
5631a4050f5SDavid du Colombier
5641a4050f5SDavid du Colombier tr = tlsdevs[CONV(c->qid)];
5651a4050f5SDavid du Colombier if(tr == nil)
5661a4050f5SDavid du Colombier break;
5671a4050f5SDavid du Colombier
5681a4050f5SDavid du Colombier if(t == Qhand)
5691a4050f5SDavid du Colombier dechandq(tr);
5701a4050f5SDavid du Colombier
5711a4050f5SDavid du Colombier lock(&tdlock);
5721a4050f5SDavid du Colombier if(--tr->ref > 0) {
5731a4050f5SDavid du Colombier unlock(&tdlock);
5741a4050f5SDavid du Colombier return;
5751a4050f5SDavid du Colombier }
5761a4050f5SDavid du Colombier tlsdevs[CONV(c->qid)] = nil;
5771a4050f5SDavid du Colombier unlock(&tdlock);
5781a4050f5SDavid du Colombier
5791a4050f5SDavid du Colombier if(tr->c != nil && !waserror()){
5801a4050f5SDavid du Colombier checkstate(tr, 0, SOpen|SHandshake|SRClose);
5811a4050f5SDavid du Colombier sendAlert(tr, ECloseNotify);
5821a4050f5SDavid du Colombier poperror();
5831a4050f5SDavid du Colombier }
5841a4050f5SDavid du Colombier tlshangup(tr);
5851a4050f5SDavid du Colombier if(tr->c != nil)
5861a4050f5SDavid du Colombier cclose(tr->c);
5871a4050f5SDavid du Colombier freeSec(tr->in.sec);
5881a4050f5SDavid du Colombier freeSec(tr->in.new);
5891a4050f5SDavid du Colombier freeSec(tr->out.sec);
5901a4050f5SDavid du Colombier freeSec(tr->out.new);
5911a4050f5SDavid du Colombier free(tr->user);
5921a4050f5SDavid du Colombier free(tr);
5931a4050f5SDavid du Colombier break;
5941a4050f5SDavid du Colombier }
5951a4050f5SDavid du Colombier }
5961a4050f5SDavid du Colombier
5971a4050f5SDavid du Colombier /*
5981a4050f5SDavid du Colombier * make sure we have at least 'n' bytes in list 'l'
5991a4050f5SDavid du Colombier */
6001a4050f5SDavid du Colombier static void
ensure(TlsRec * s,Block ** l,int n)6011a4050f5SDavid du Colombier ensure(TlsRec *s, Block **l, int n)
6021a4050f5SDavid du Colombier {
6031a4050f5SDavid du Colombier int sofar, i;
6041a4050f5SDavid du Colombier Block *b, *bl;
6051a4050f5SDavid du Colombier
6061a4050f5SDavid du Colombier sofar = 0;
6071a4050f5SDavid du Colombier for(b = *l; b; b = b->next){
6081a4050f5SDavid du Colombier sofar += BLEN(b);
6091a4050f5SDavid du Colombier if(sofar >= n)
6101a4050f5SDavid du Colombier return;
6111a4050f5SDavid du Colombier l = &b->next;
6121a4050f5SDavid du Colombier }
6131a4050f5SDavid du Colombier
6141a4050f5SDavid du Colombier while(sofar < n){
6151a4050f5SDavid du Colombier bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0);
6161a4050f5SDavid du Colombier if(bl == 0)
6171a4050f5SDavid du Colombier error(Ehungup);
6181a4050f5SDavid du Colombier *l = bl;
6191a4050f5SDavid du Colombier i = 0;
6201a4050f5SDavid du Colombier for(b = bl; b; b = b->next){
6211a4050f5SDavid du Colombier i += BLEN(b);
6221a4050f5SDavid du Colombier l = &b->next;
6231a4050f5SDavid du Colombier }
6241a4050f5SDavid du Colombier if(i == 0)
6251a4050f5SDavid du Colombier error(Ehungup);
6261a4050f5SDavid du Colombier sofar += i;
6271a4050f5SDavid du Colombier }
6281a4050f5SDavid du Colombier if(s->debug) pprint("ensure read %d\n", sofar);
6291a4050f5SDavid du Colombier }
6301a4050f5SDavid du Colombier
6311a4050f5SDavid du Colombier /*
6321a4050f5SDavid du Colombier * copy 'n' bytes from 'l' into 'p' and free
6331a4050f5SDavid du Colombier * the bytes in 'l'
6341a4050f5SDavid du Colombier */
6351a4050f5SDavid du Colombier static void
consume(Block ** l,uchar * p,int n)6361a4050f5SDavid du Colombier consume(Block **l, uchar *p, int n)
6371a4050f5SDavid du Colombier {
6381a4050f5SDavid du Colombier Block *b;
6391a4050f5SDavid du Colombier int i;
6401a4050f5SDavid du Colombier
6411a4050f5SDavid du Colombier for(; *l && n > 0; n -= i){
6421a4050f5SDavid du Colombier b = *l;
6431a4050f5SDavid du Colombier i = BLEN(b);
6441a4050f5SDavid du Colombier if(i > n)
6451a4050f5SDavid du Colombier i = n;
6461a4050f5SDavid du Colombier memmove(p, b->rp, i);
6471a4050f5SDavid du Colombier b->rp += i;
6481a4050f5SDavid du Colombier p += i;
6491a4050f5SDavid du Colombier if(BLEN(b) < 0)
6501a4050f5SDavid du Colombier panic("consume");
6511a4050f5SDavid du Colombier if(BLEN(b))
6521a4050f5SDavid du Colombier break;
6531a4050f5SDavid du Colombier *l = b->next;
6541a4050f5SDavid du Colombier freeb(b);
6551a4050f5SDavid du Colombier }
6561a4050f5SDavid du Colombier }
6571a4050f5SDavid du Colombier
6581a4050f5SDavid du Colombier /*
6591a4050f5SDavid du Colombier * give back n bytes
6601a4050f5SDavid du Colombier */
6611a4050f5SDavid du Colombier static void
regurgitate(TlsRec * s,uchar * p,int n)6621a4050f5SDavid du Colombier regurgitate(TlsRec *s, uchar *p, int n)
6631a4050f5SDavid du Colombier {
6641a4050f5SDavid du Colombier Block *b;
6651a4050f5SDavid du Colombier
6661a4050f5SDavid du Colombier if(n <= 0)
6671a4050f5SDavid du Colombier return;
6681a4050f5SDavid du Colombier b = s->unprocessed;
6691a4050f5SDavid du Colombier if(s->unprocessed == nil || b->rp - b->base < n) {
6701a4050f5SDavid du Colombier b = allocb(n);
6711a4050f5SDavid du Colombier memmove(b->wp, p, n);
6721a4050f5SDavid du Colombier b->wp += n;
6731a4050f5SDavid du Colombier b->next = s->unprocessed;
6741a4050f5SDavid du Colombier s->unprocessed = b;
6751a4050f5SDavid du Colombier } else {
6761a4050f5SDavid du Colombier b->rp -= n;
6771a4050f5SDavid du Colombier memmove(b->rp, p, n);
6781a4050f5SDavid du Colombier }
6791a4050f5SDavid du Colombier }
6801a4050f5SDavid du Colombier
6811a4050f5SDavid du Colombier /*
6821a4050f5SDavid du Colombier * remove at most n bytes from the queue
6831a4050f5SDavid du Colombier */
6841a4050f5SDavid du Colombier static Block*
qgrab(Block ** l,int n)6851a4050f5SDavid du Colombier qgrab(Block **l, int n)
6861a4050f5SDavid du Colombier {
6871a4050f5SDavid du Colombier Block *bb, *b;
6881a4050f5SDavid du Colombier int i;
6891a4050f5SDavid du Colombier
6901a4050f5SDavid du Colombier b = *l;
6911a4050f5SDavid du Colombier if(BLEN(b) == n){
6921a4050f5SDavid du Colombier *l = b->next;
6931a4050f5SDavid du Colombier b->next = nil;
6941a4050f5SDavid du Colombier return b;
6951a4050f5SDavid du Colombier }
6961a4050f5SDavid du Colombier
6971a4050f5SDavid du Colombier i = 0;
6981a4050f5SDavid du Colombier for(bb = b; bb != nil && i < n; bb = bb->next)
6991a4050f5SDavid du Colombier i += BLEN(bb);
7001a4050f5SDavid du Colombier if(i > n)
7011a4050f5SDavid du Colombier i = n;
7021a4050f5SDavid du Colombier
7031a4050f5SDavid du Colombier bb = allocb(i);
7041a4050f5SDavid du Colombier consume(l, bb->wp, i);
7051a4050f5SDavid du Colombier bb->wp += i;
7061a4050f5SDavid du Colombier return bb;
7071a4050f5SDavid du Colombier }
7081a4050f5SDavid du Colombier
7091a4050f5SDavid du Colombier static void
tlsclosed(TlsRec * tr,int new)7101a4050f5SDavid du Colombier tlsclosed(TlsRec *tr, int new)
7111a4050f5SDavid du Colombier {
7121a4050f5SDavid du Colombier lock(&tr->statelk);
7131a4050f5SDavid du Colombier if(tr->state == SOpen || tr->state == SHandshake)
7141a4050f5SDavid du Colombier tr->state = new;
7151a4050f5SDavid du Colombier else if((new | tr->state) == (SRClose|SLClose))
7161a4050f5SDavid du Colombier tr->state = SClosed;
7171a4050f5SDavid du Colombier unlock(&tr->statelk);
7181a4050f5SDavid du Colombier alertHand(tr, "close notify");
7191a4050f5SDavid du Colombier }
7201a4050f5SDavid du Colombier
7211a4050f5SDavid du Colombier /*
7221a4050f5SDavid du Colombier * read and process one tls record layer message
7231a4050f5SDavid du Colombier * must be called with tr->in.io held
7241a4050f5SDavid du Colombier * We can't let Eintrs lose data, since doing so will get
7251a4050f5SDavid du Colombier * us out of sync with the sender and break the reliablity
7261a4050f5SDavid du Colombier * of the channel. Eintr only happens during the reads in
7271a4050f5SDavid du Colombier * consume. Therefore we put back any bytes consumed before
7281a4050f5SDavid du Colombier * the last call to ensure.
7291a4050f5SDavid du Colombier */
7301a4050f5SDavid du Colombier static void
tlsrecread(TlsRec * tr)7311a4050f5SDavid du Colombier tlsrecread(TlsRec *tr)
7321a4050f5SDavid du Colombier {
7331a4050f5SDavid du Colombier OneWay *volatile in;
7341a4050f5SDavid du Colombier Block *volatile b;
7351a4050f5SDavid du Colombier uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen];
7361a4050f5SDavid du Colombier int volatile nconsumed;
7371a4050f5SDavid du Colombier int len, type, ver, unpad_len;
7381a4050f5SDavid du Colombier
7391a4050f5SDavid du Colombier nconsumed = 0;
7401a4050f5SDavid du Colombier if(waserror()){
7411a4050f5SDavid du Colombier if(strcmp(up->errstr, Eintr) == 0 && !waserror()){
7421a4050f5SDavid du Colombier regurgitate(tr, header, nconsumed);
7431a4050f5SDavid du Colombier poperror();
7441a4050f5SDavid du Colombier }else
7451a4050f5SDavid du Colombier tlsError(tr, "channel error");
7461a4050f5SDavid du Colombier nexterror();
7471a4050f5SDavid du Colombier }
7481a4050f5SDavid du Colombier ensure(tr, &tr->unprocessed, RecHdrLen);
7491a4050f5SDavid du Colombier consume(&tr->unprocessed, header, RecHdrLen);
7501a4050f5SDavid du Colombier if(tr->debug)pprint("consumed %d header\n", RecHdrLen);
7511a4050f5SDavid du Colombier nconsumed = RecHdrLen;
7521a4050f5SDavid du Colombier
7531a4050f5SDavid du Colombier if((tr->handin == 0) && (header[0] & 0x80)){
7541a4050f5SDavid du Colombier /* Cope with an SSL3 ClientHello expressed in SSL2 record format.
7551a4050f5SDavid du Colombier This is sent by some clients that we must interoperate
7561a4050f5SDavid du Colombier with, such as Java's JSSE and Microsoft's Internet Explorer. */
7571a4050f5SDavid du Colombier len = (get16(header) & ~0x8000) - 3;
7581a4050f5SDavid du Colombier type = header[2];
7591a4050f5SDavid du Colombier ver = get16(header + 3);
7601a4050f5SDavid du Colombier if(type != SSL2ClientHello || len < 22)
7611a4050f5SDavid du Colombier rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message");
7621a4050f5SDavid du Colombier }else{ /* normal SSL3 record format */
7631a4050f5SDavid du Colombier type = header[0];
7641a4050f5SDavid du Colombier ver = get16(header+1);
7651a4050f5SDavid du Colombier len = get16(header+3);
7661a4050f5SDavid du Colombier }
7671a4050f5SDavid du Colombier if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion))
7681a4050f5SDavid du Colombier rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'",
7691a4050f5SDavid du Colombier tr->version, tr->verset?"/set":"", len, type, ver, (char*)header);
7701a4050f5SDavid du Colombier if(len > MaxCipherRecLen || len < 0)
7711a4050f5SDavid du Colombier rcvError(tr, ERecordOverflow, "record message too long %d", len);
7721a4050f5SDavid du Colombier ensure(tr, &tr->unprocessed, len);
7731a4050f5SDavid du Colombier nconsumed = 0;
7741a4050f5SDavid du Colombier poperror();
7751a4050f5SDavid du Colombier
7761a4050f5SDavid du Colombier /*
7771a4050f5SDavid du Colombier * If an Eintr happens after this, we'll get out of sync.
7781a4050f5SDavid du Colombier * Make sure nothing we call can sleep.
7791a4050f5SDavid du Colombier * Errors are ok, as they kill the connection.
7801a4050f5SDavid du Colombier * Luckily, allocb won't sleep, it'll just error out.
7811a4050f5SDavid du Colombier */
7821a4050f5SDavid du Colombier b = nil;
7831a4050f5SDavid du Colombier if(waserror()){
7841a4050f5SDavid du Colombier if(b != nil)
7851a4050f5SDavid du Colombier freeb(b);
7861a4050f5SDavid du Colombier tlsError(tr, "channel error");
7871a4050f5SDavid du Colombier nexterror();
7881a4050f5SDavid du Colombier }
7891a4050f5SDavid du Colombier b = qgrab(&tr->unprocessed, len);
7901a4050f5SDavid du Colombier if(tr->debug) pprint("consumed unprocessed %d\n", len);
7911a4050f5SDavid du Colombier
7921a4050f5SDavid du Colombier in = &tr->in;
7931a4050f5SDavid du Colombier if(waserror()){
7941a4050f5SDavid du Colombier qunlock(&in->seclock);
7951a4050f5SDavid du Colombier nexterror();
7961a4050f5SDavid du Colombier }
7971a4050f5SDavid du Colombier qlock(&in->seclock);
7981a4050f5SDavid du Colombier p = b->rp;
7991a4050f5SDavid du Colombier if(in->sec != nil) {
8001a4050f5SDavid du Colombier /* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here
8011a4050f5SDavid du Colombier should look alike, including timing of the response. */
8021a4050f5SDavid du Colombier unpad_len = (*in->sec->dec)(in->sec, p, len);
8031a4050f5SDavid du Colombier if(unpad_len >= in->sec->maclen)
8041a4050f5SDavid du Colombier len = unpad_len - in->sec->maclen;
8051a4050f5SDavid du Colombier if(tr->debug) pprint("decrypted %d\n", unpad_len);
8061a4050f5SDavid du Colombier if(tr->debug) pdump(unpad_len, p, "decrypted:");
8071a4050f5SDavid du Colombier
8081a4050f5SDavid du Colombier /* update length */
8091a4050f5SDavid du Colombier put16(header+3, len);
8101a4050f5SDavid du Colombier put64(seq, in->seq);
8111a4050f5SDavid du Colombier in->seq++;
8121a4050f5SDavid du Colombier (*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
8131a4050f5SDavid du Colombier if(unpad_len < in->sec->maclen)
8141a4050f5SDavid du Colombier rcvError(tr, EBadRecordMac, "short record mac");
8151a4050f5SDavid du Colombier if(memcmp(hmac, p+len, in->sec->maclen) != 0)
8161a4050f5SDavid du Colombier rcvError(tr, EBadRecordMac, "record mac mismatch");
8171a4050f5SDavid du Colombier b->wp = b->rp + len;
8181a4050f5SDavid du Colombier }
8191a4050f5SDavid du Colombier qunlock(&in->seclock);
8201a4050f5SDavid du Colombier poperror();
8211a4050f5SDavid du Colombier if(len < 0)
8221a4050f5SDavid du Colombier rcvError(tr, EDecodeError, "runt record message");
8231a4050f5SDavid du Colombier
8241a4050f5SDavid du Colombier switch(type) {
8251a4050f5SDavid du Colombier default:
8261a4050f5SDavid du Colombier rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type);
8271a4050f5SDavid du Colombier break;
8281a4050f5SDavid du Colombier case RChangeCipherSpec:
8291a4050f5SDavid du Colombier if(len != 1 || p[0] != 1)
8301a4050f5SDavid du Colombier rcvError(tr, EDecodeError, "invalid change cipher spec");
8311a4050f5SDavid du Colombier qlock(&in->seclock);
8321a4050f5SDavid du Colombier if(in->new == nil){
8331a4050f5SDavid du Colombier qunlock(&in->seclock);
8341a4050f5SDavid du Colombier rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec");
8351a4050f5SDavid du Colombier }
8361a4050f5SDavid du Colombier freeSec(in->sec);
8371a4050f5SDavid du Colombier in->sec = in->new;
8381a4050f5SDavid du Colombier in->new = nil;
8391a4050f5SDavid du Colombier in->seq = 0;
8401a4050f5SDavid du Colombier qunlock(&in->seclock);
8411a4050f5SDavid du Colombier break;
8421a4050f5SDavid du Colombier case RAlert:
8431a4050f5SDavid du Colombier if(len != 2)
8441a4050f5SDavid du Colombier rcvError(tr, EDecodeError, "invalid alert");
8451a4050f5SDavid du Colombier if(p[0] == 2)
8461a4050f5SDavid du Colombier rcvAlert(tr, p[1]);
8471a4050f5SDavid du Colombier if(p[0] != 1)
8481a4050f5SDavid du Colombier rcvError(tr, EIllegalParameter, "invalid alert fatal code");
8491a4050f5SDavid du Colombier
8501a4050f5SDavid du Colombier /*
8511a4050f5SDavid du Colombier * propate non-fatal alerts to handshaker
8521a4050f5SDavid du Colombier */
8531a4050f5SDavid du Colombier if(p[1] == ECloseNotify) {
8541a4050f5SDavid du Colombier tlsclosed(tr, SRClose);
8551a4050f5SDavid du Colombier if(tr->opened)
8561a4050f5SDavid du Colombier error("tls hungup");
8571a4050f5SDavid du Colombier error("close notify");
8581a4050f5SDavid du Colombier }
8591a4050f5SDavid du Colombier if(p[1] == ENoRenegotiation)
8601a4050f5SDavid du Colombier alertHand(tr, "no renegotiation");
8611a4050f5SDavid du Colombier else if(p[1] == EUserCanceled)
8621a4050f5SDavid du Colombier alertHand(tr, "handshake canceled by user");
8631a4050f5SDavid du Colombier else
8641a4050f5SDavid du Colombier rcvError(tr, EIllegalParameter, "invalid alert code");
8651a4050f5SDavid du Colombier break;
8661a4050f5SDavid du Colombier case RHandshake:
8671a4050f5SDavid du Colombier /*
8681a4050f5SDavid du Colombier * don't worry about dropping the block
8691a4050f5SDavid du Colombier * qbwrite always queues even if flow controlled and interrupted.
8701a4050f5SDavid du Colombier *
8711a4050f5SDavid du Colombier * if there isn't any handshaker, ignore the request,
8721a4050f5SDavid du Colombier * but notify the other side we are doing so.
8731a4050f5SDavid du Colombier */
8741a4050f5SDavid du Colombier lock(&tr->hqlock);
8751a4050f5SDavid du Colombier if(tr->handq != nil){
8761a4050f5SDavid du Colombier tr->hqref++;
8771a4050f5SDavid du Colombier unlock(&tr->hqlock);
8781a4050f5SDavid du Colombier if(waserror()){
8791a4050f5SDavid du Colombier dechandq(tr);
8801a4050f5SDavid du Colombier nexterror();
8811a4050f5SDavid du Colombier }
8821a4050f5SDavid du Colombier b = padblock(b, 1);
8831a4050f5SDavid du Colombier *b->rp = RHandshake;
8841a4050f5SDavid du Colombier qbwrite(tr->handq, b);
8851a4050f5SDavid du Colombier b = nil;
8861a4050f5SDavid du Colombier poperror();
8871a4050f5SDavid du Colombier dechandq(tr);
8881a4050f5SDavid du Colombier }else{
8891a4050f5SDavid du Colombier unlock(&tr->hqlock);
8901a4050f5SDavid du Colombier if(tr->verset && tr->version != SSL3Version && !waserror()){
8911a4050f5SDavid du Colombier sendAlert(tr, ENoRenegotiation);
8921a4050f5SDavid du Colombier poperror();
8931a4050f5SDavid du Colombier }
8941a4050f5SDavid du Colombier }
8951a4050f5SDavid du Colombier break;
8961a4050f5SDavid du Colombier case SSL2ClientHello:
8971a4050f5SDavid du Colombier lock(&tr->hqlock);
8981a4050f5SDavid du Colombier if(tr->handq != nil){
8991a4050f5SDavid du Colombier tr->hqref++;
9001a4050f5SDavid du Colombier unlock(&tr->hqlock);
9011a4050f5SDavid du Colombier if(waserror()){
9021a4050f5SDavid du Colombier dechandq(tr);
9031a4050f5SDavid du Colombier nexterror();
9041a4050f5SDavid du Colombier }
9051a4050f5SDavid du Colombier /* Pass the SSL2 format data, so that the handshake code can compute
9061a4050f5SDavid du Colombier the correct checksums. HSSL2ClientHello = HandshakeType 9 is
9071a4050f5SDavid du Colombier unused in RFC2246. */
9081a4050f5SDavid du Colombier b = padblock(b, 8);
9091a4050f5SDavid du Colombier b->rp[0] = RHandshake;
9101a4050f5SDavid du Colombier b->rp[1] = HSSL2ClientHello;
9111a4050f5SDavid du Colombier put24(&b->rp[2], len+3);
9121a4050f5SDavid du Colombier b->rp[5] = SSL2ClientHello;
9131a4050f5SDavid du Colombier put16(&b->rp[6], ver);
9141a4050f5SDavid du Colombier qbwrite(tr->handq, b);
9151a4050f5SDavid du Colombier b = nil;
9161a4050f5SDavid du Colombier poperror();
9171a4050f5SDavid du Colombier dechandq(tr);
9181a4050f5SDavid du Colombier }else{
9191a4050f5SDavid du Colombier unlock(&tr->hqlock);
9201a4050f5SDavid du Colombier if(tr->verset && tr->version != SSL3Version && !waserror()){
9211a4050f5SDavid du Colombier sendAlert(tr, ENoRenegotiation);
9221a4050f5SDavid du Colombier poperror();
9231a4050f5SDavid du Colombier }
9241a4050f5SDavid du Colombier }
9251a4050f5SDavid du Colombier break;
9261a4050f5SDavid du Colombier case RApplication:
9271a4050f5SDavid du Colombier if(!tr->opened)
9281a4050f5SDavid du Colombier rcvError(tr, EUnexpectedMessage, "application message received before handshake completed");
9291a4050f5SDavid du Colombier if(BLEN(b) > 0){
9301a4050f5SDavid du Colombier tr->processed = b;
9311a4050f5SDavid du Colombier b = nil;
9321a4050f5SDavid du Colombier }
9331a4050f5SDavid du Colombier break;
9341a4050f5SDavid du Colombier }
9351a4050f5SDavid du Colombier if(b != nil)
9361a4050f5SDavid du Colombier freeb(b);
9371a4050f5SDavid du Colombier poperror();
9381a4050f5SDavid du Colombier }
9391a4050f5SDavid du Colombier
9401a4050f5SDavid du Colombier /*
9411a4050f5SDavid du Colombier * got a fatal alert message
9421a4050f5SDavid du Colombier */
9431a4050f5SDavid du Colombier static void
rcvAlert(TlsRec * tr,int err)9441a4050f5SDavid du Colombier rcvAlert(TlsRec *tr, int err)
9451a4050f5SDavid du Colombier {
9461a4050f5SDavid du Colombier char *s;
9471a4050f5SDavid du Colombier int i;
9481a4050f5SDavid du Colombier
9491a4050f5SDavid du Colombier s = "unknown error";
9501a4050f5SDavid du Colombier for(i=0; i < nelem(tlserrs); i++){
9511a4050f5SDavid du Colombier if(tlserrs[i].err == err){
9521a4050f5SDavid du Colombier s = tlserrs[i].msg;
9531a4050f5SDavid du Colombier break;
9541a4050f5SDavid du Colombier }
9551a4050f5SDavid du Colombier }
9561a4050f5SDavid du Colombier if(tr->debug) pprint("rcvAlert: %s\n", s);
9571a4050f5SDavid du Colombier
9581a4050f5SDavid du Colombier tlsError(tr, s);
9591a4050f5SDavid du Colombier if(!tr->opened)
9601a4050f5SDavid du Colombier error(s);
9611a4050f5SDavid du Colombier error("tls error");
9621a4050f5SDavid du Colombier }
9631a4050f5SDavid du Colombier
9641a4050f5SDavid du Colombier /*
9651a4050f5SDavid du Colombier * found an error while decoding the input stream
9661a4050f5SDavid du Colombier */
9671a4050f5SDavid du Colombier static void
rcvError(TlsRec * tr,int err,char * fmt,...)9681a4050f5SDavid du Colombier rcvError(TlsRec *tr, int err, char *fmt, ...)
9691a4050f5SDavid du Colombier {
9701a4050f5SDavid du Colombier char msg[ERRMAX];
9711a4050f5SDavid du Colombier va_list arg;
9721a4050f5SDavid du Colombier
9731a4050f5SDavid du Colombier va_start(arg, fmt);
9741a4050f5SDavid du Colombier vseprint(msg, msg+sizeof(msg), fmt, arg);
9751a4050f5SDavid du Colombier va_end(arg);
9761a4050f5SDavid du Colombier if(tr->debug) pprint("rcvError: %s\n", msg);
9771a4050f5SDavid du Colombier
9781a4050f5SDavid du Colombier sendAlert(tr, err);
9791a4050f5SDavid du Colombier
9801a4050f5SDavid du Colombier if(!tr->opened)
9811a4050f5SDavid du Colombier error(msg);
9821a4050f5SDavid du Colombier error("tls error");
9831a4050f5SDavid du Colombier }
9841a4050f5SDavid du Colombier
9851a4050f5SDavid du Colombier /*
9861a4050f5SDavid du Colombier * make sure the next hand operation returns with a 'msg' error
9871a4050f5SDavid du Colombier */
9881a4050f5SDavid du Colombier static void
alertHand(TlsRec * tr,char * msg)9891a4050f5SDavid du Colombier alertHand(TlsRec *tr, char *msg)
9901a4050f5SDavid du Colombier {
9911a4050f5SDavid du Colombier Block *b;
9921a4050f5SDavid du Colombier int n;
9931a4050f5SDavid du Colombier
9941a4050f5SDavid du Colombier lock(&tr->hqlock);
9951a4050f5SDavid du Colombier if(tr->handq == nil){
9961a4050f5SDavid du Colombier unlock(&tr->hqlock);
9971a4050f5SDavid du Colombier return;
9981a4050f5SDavid du Colombier }
9991a4050f5SDavid du Colombier tr->hqref++;
10001a4050f5SDavid du Colombier unlock(&tr->hqlock);
10011a4050f5SDavid du Colombier
10021a4050f5SDavid du Colombier n = strlen(msg);
10031a4050f5SDavid du Colombier if(waserror()){
10041a4050f5SDavid du Colombier dechandq(tr);
10051a4050f5SDavid du Colombier nexterror();
10061a4050f5SDavid du Colombier }
10071a4050f5SDavid du Colombier b = allocb(n + 2);
10081a4050f5SDavid du Colombier *b->wp++ = RAlert;
10091a4050f5SDavid du Colombier memmove(b->wp, msg, n + 1);
10101a4050f5SDavid du Colombier b->wp += n + 1;
10111a4050f5SDavid du Colombier
10121a4050f5SDavid du Colombier qbwrite(tr->handq, b);
10131a4050f5SDavid du Colombier
10141a4050f5SDavid du Colombier poperror();
10151a4050f5SDavid du Colombier dechandq(tr);
10161a4050f5SDavid du Colombier }
10171a4050f5SDavid du Colombier
10181a4050f5SDavid du Colombier static void
checkstate(TlsRec * tr,int ishand,int ok)10191a4050f5SDavid du Colombier checkstate(TlsRec *tr, int ishand, int ok)
10201a4050f5SDavid du Colombier {
10211a4050f5SDavid du Colombier int state;
10221a4050f5SDavid du Colombier
10231a4050f5SDavid du Colombier lock(&tr->statelk);
10241a4050f5SDavid du Colombier state = tr->state;
10251a4050f5SDavid du Colombier unlock(&tr->statelk);
10261a4050f5SDavid du Colombier if(state & ok)
10271a4050f5SDavid du Colombier return;
10281a4050f5SDavid du Colombier switch(state){
10291a4050f5SDavid du Colombier case SHandshake:
10301a4050f5SDavid du Colombier case SOpen:
10311a4050f5SDavid du Colombier break;
10321a4050f5SDavid du Colombier case SError:
10331a4050f5SDavid du Colombier case SAlert:
10341a4050f5SDavid du Colombier if(ishand)
10351a4050f5SDavid du Colombier error(tr->err);
10361a4050f5SDavid du Colombier error("tls error");
10371a4050f5SDavid du Colombier case SRClose:
10381a4050f5SDavid du Colombier case SLClose:
10391a4050f5SDavid du Colombier case SClosed:
10401a4050f5SDavid du Colombier error("tls hungup");
10411a4050f5SDavid du Colombier }
10421a4050f5SDavid du Colombier error("tls improperly configured");
10431a4050f5SDavid du Colombier }
10441a4050f5SDavid du Colombier
10451a4050f5SDavid du Colombier static Block*
tlsbread(Chan * c,long n,ulong offset)10461a4050f5SDavid du Colombier tlsbread(Chan *c, long n, ulong offset)
10471a4050f5SDavid du Colombier {
10481a4050f5SDavid du Colombier int ty;
10491a4050f5SDavid du Colombier Block *b;
10501a4050f5SDavid du Colombier TlsRec *volatile tr;
10511a4050f5SDavid du Colombier
10521a4050f5SDavid du Colombier ty = TYPE(c->qid);
10531a4050f5SDavid du Colombier switch(ty) {
10541a4050f5SDavid du Colombier default:
10551a4050f5SDavid du Colombier return devbread(c, n, offset);
10561a4050f5SDavid du Colombier case Qhand:
10571a4050f5SDavid du Colombier case Qdata:
10581a4050f5SDavid du Colombier break;
10591a4050f5SDavid du Colombier }
10601a4050f5SDavid du Colombier
10611a4050f5SDavid du Colombier tr = tlsdevs[CONV(c->qid)];
10621a4050f5SDavid du Colombier if(tr == nil)
10631a4050f5SDavid du Colombier panic("tlsbread");
10641a4050f5SDavid du Colombier
10651a4050f5SDavid du Colombier if(waserror()){
10661a4050f5SDavid du Colombier qunlock(&tr->in.io);
10671a4050f5SDavid du Colombier nexterror();
10681a4050f5SDavid du Colombier }
10691a4050f5SDavid du Colombier qlock(&tr->in.io);
10701a4050f5SDavid du Colombier if(ty == Qdata){
10711a4050f5SDavid du Colombier checkstate(tr, 0, SOpen);
10721a4050f5SDavid du Colombier while(tr->processed == nil)
10731a4050f5SDavid du Colombier tlsrecread(tr);
10741a4050f5SDavid du Colombier
10751a4050f5SDavid du Colombier /* return at most what was asked for */
10761a4050f5SDavid du Colombier b = qgrab(&tr->processed, n);
10771a4050f5SDavid du Colombier if(tr->debug) pprint("consumed processed %d\n", BLEN(b));
10781a4050f5SDavid du Colombier if(tr->debug) pdump(BLEN(b), b->rp, "consumed:");
10791a4050f5SDavid du Colombier qunlock(&tr->in.io);
10801a4050f5SDavid du Colombier poperror();
10811a4050f5SDavid du Colombier tr->datain += BLEN(b);
10821a4050f5SDavid du Colombier }else{
10831a4050f5SDavid du Colombier checkstate(tr, 1, SOpen|SHandshake|SLClose);
10841a4050f5SDavid du Colombier
10851a4050f5SDavid du Colombier /*
10861a4050f5SDavid du Colombier * it's ok to look at state without the lock
10871a4050f5SDavid du Colombier * since it only protects reading records,
10881a4050f5SDavid du Colombier * and we have that tr->in.io held.
10891a4050f5SDavid du Colombier */
10901a4050f5SDavid du Colombier while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq))
10911a4050f5SDavid du Colombier tlsrecread(tr);
10921a4050f5SDavid du Colombier
10931a4050f5SDavid du Colombier qunlock(&tr->in.io);
10941a4050f5SDavid du Colombier poperror();
10951a4050f5SDavid du Colombier
10961a4050f5SDavid du Colombier if(waserror()){
10971a4050f5SDavid du Colombier qunlock(&tr->hqread);
10981a4050f5SDavid du Colombier nexterror();
10991a4050f5SDavid du Colombier }
11001a4050f5SDavid du Colombier qlock(&tr->hqread);
11011a4050f5SDavid du Colombier if(tr->hprocessed == nil){
11021a4050f5SDavid du Colombier b = qbread(tr->handq, MaxRecLen + 1);
11031a4050f5SDavid du Colombier if(*b->rp++ == RAlert){
11041a4050f5SDavid du Colombier kstrcpy(up->errstr, (char*)b->rp, ERRMAX);
11051a4050f5SDavid du Colombier freeb(b);
11061a4050f5SDavid du Colombier nexterror();
11071a4050f5SDavid du Colombier }
11081a4050f5SDavid du Colombier tr->hprocessed = b;
11091a4050f5SDavid du Colombier }
11101a4050f5SDavid du Colombier b = qgrab(&tr->hprocessed, n);
11111a4050f5SDavid du Colombier poperror();
11121a4050f5SDavid du Colombier qunlock(&tr->hqread);
11131a4050f5SDavid du Colombier tr->handin += BLEN(b);
11141a4050f5SDavid du Colombier }
11151a4050f5SDavid du Colombier
11161a4050f5SDavid du Colombier return b;
11171a4050f5SDavid du Colombier }
11181a4050f5SDavid du Colombier
11191a4050f5SDavid du Colombier static long
tlsread(Chan * c,void * a,long n,vlong off)11201a4050f5SDavid du Colombier tlsread(Chan *c, void *a, long n, vlong off)
11211a4050f5SDavid du Colombier {
11221a4050f5SDavid du Colombier Block *volatile b;
11231a4050f5SDavid du Colombier Block *nb;
11241a4050f5SDavid du Colombier uchar *va;
11251a4050f5SDavid du Colombier int i, ty;
11261a4050f5SDavid du Colombier char *buf, *s, *e;
11271a4050f5SDavid du Colombier ulong offset = off;
11281a4050f5SDavid du Colombier TlsRec * tr;
11291a4050f5SDavid du Colombier
11301a4050f5SDavid du Colombier if(c->qid.type & QTDIR)
11311a4050f5SDavid du Colombier return devdirread(c, a, n, 0, 0, tlsgen);
11321a4050f5SDavid du Colombier
11331a4050f5SDavid du Colombier tr = tlsdevs[CONV(c->qid)];
11341a4050f5SDavid du Colombier ty = TYPE(c->qid);
11351a4050f5SDavid du Colombier switch(ty) {
11361a4050f5SDavid du Colombier default:
11371a4050f5SDavid du Colombier error(Ebadusefd);
11381a4050f5SDavid du Colombier case Qstatus:
11391a4050f5SDavid du Colombier buf = smalloc(Statlen);
11401a4050f5SDavid du Colombier qlock(&tr->in.seclock);
11411a4050f5SDavid du Colombier qlock(&tr->out.seclock);
11421a4050f5SDavid du Colombier s = buf;
11431a4050f5SDavid du Colombier e = buf + Statlen;
11441a4050f5SDavid du Colombier s = seprint(s, e, "State: %s\n", tlsstate(tr->state));
11451a4050f5SDavid du Colombier s = seprint(s, e, "Version: 0x%x\n", tr->version);
11461a4050f5SDavid du Colombier if(tr->in.sec != nil)
11471a4050f5SDavid du Colombier s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg);
11481a4050f5SDavid du Colombier if(tr->in.new != nil)
11491a4050f5SDavid du Colombier s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg);
11501a4050f5SDavid du Colombier if(tr->out.sec != nil)
11511a4050f5SDavid du Colombier s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg);
11521a4050f5SDavid du Colombier if(tr->out.new != nil)
11531a4050f5SDavid du Colombier seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg);
11541a4050f5SDavid du Colombier qunlock(&tr->in.seclock);
11551a4050f5SDavid du Colombier qunlock(&tr->out.seclock);
11561a4050f5SDavid du Colombier n = readstr(offset, a, n, buf);
11571a4050f5SDavid du Colombier free(buf);
11581a4050f5SDavid du Colombier return n;
11591a4050f5SDavid du Colombier case Qstats:
11601a4050f5SDavid du Colombier buf = smalloc(Statlen);
11611a4050f5SDavid du Colombier s = buf;
11621a4050f5SDavid du Colombier e = buf + Statlen;
11631a4050f5SDavid du Colombier s = seprint(s, e, "DataIn: %lld\n", tr->datain);
11641a4050f5SDavid du Colombier s = seprint(s, e, "DataOut: %lld\n", tr->dataout);
11651a4050f5SDavid du Colombier s = seprint(s, e, "HandIn: %lld\n", tr->handin);
11661a4050f5SDavid du Colombier seprint(s, e, "HandOut: %lld\n", tr->handout);
11671a4050f5SDavid du Colombier n = readstr(offset, a, n, buf);
11681a4050f5SDavid du Colombier free(buf);
11691a4050f5SDavid du Colombier return n;
11701a4050f5SDavid du Colombier case Qctl:
11711a4050f5SDavid du Colombier buf = smalloc(Statlen);
11721a4050f5SDavid du Colombier snprint(buf, Statlen, "%llud", CONV(c->qid));
11731a4050f5SDavid du Colombier n = readstr(offset, a, n, buf);
11741a4050f5SDavid du Colombier free(buf);
11751a4050f5SDavid du Colombier return n;
11761a4050f5SDavid du Colombier case Qdata:
11771a4050f5SDavid du Colombier case Qhand:
11781a4050f5SDavid du Colombier b = tlsbread(c, n, offset);
11791a4050f5SDavid du Colombier break;
11801a4050f5SDavid du Colombier case Qencalgs:
11811a4050f5SDavid du Colombier return readstr(offset, a, n, encalgs);
11821a4050f5SDavid du Colombier case Qhashalgs:
11831a4050f5SDavid du Colombier return readstr(offset, a, n, hashalgs);
11841a4050f5SDavid du Colombier }
11851a4050f5SDavid du Colombier
11861a4050f5SDavid du Colombier if(waserror()){
11871a4050f5SDavid du Colombier freeblist(b);
11881a4050f5SDavid du Colombier nexterror();
11891a4050f5SDavid du Colombier }
11901a4050f5SDavid du Colombier
11911a4050f5SDavid du Colombier n = 0;
11921a4050f5SDavid du Colombier va = a;
11931a4050f5SDavid du Colombier for(nb = b; nb; nb = nb->next){
11941a4050f5SDavid du Colombier i = BLEN(nb);
11951a4050f5SDavid du Colombier memmove(va+n, nb->rp, i);
11961a4050f5SDavid du Colombier n += i;
11971a4050f5SDavid du Colombier }
11981a4050f5SDavid du Colombier
11991a4050f5SDavid du Colombier freeblist(b);
12001a4050f5SDavid du Colombier poperror();
12011a4050f5SDavid du Colombier
12021a4050f5SDavid du Colombier return n;
12031a4050f5SDavid du Colombier }
12041a4050f5SDavid du Colombier
12051a4050f5SDavid du Colombier /*
12061a4050f5SDavid du Colombier * write a block in tls records
12071a4050f5SDavid du Colombier */
12081a4050f5SDavid du Colombier static void
tlsrecwrite(TlsRec * tr,int type,Block * b)12091a4050f5SDavid du Colombier tlsrecwrite(TlsRec *tr, int type, Block *b)
12101a4050f5SDavid du Colombier {
12111a4050f5SDavid du Colombier Block *volatile bb;
12121a4050f5SDavid du Colombier Block *nb;
12131a4050f5SDavid du Colombier uchar *p, seq[8];
12141a4050f5SDavid du Colombier OneWay *volatile out;
12151a4050f5SDavid du Colombier int n, maclen, pad, ok;
12161a4050f5SDavid du Colombier
12171a4050f5SDavid du Colombier out = &tr->out;
12181a4050f5SDavid du Colombier bb = b;
12191a4050f5SDavid du Colombier if(waserror()){
12201a4050f5SDavid du Colombier qunlock(&out->io);
12211a4050f5SDavid du Colombier if(bb != nil)
12221a4050f5SDavid du Colombier freeb(bb);
12231a4050f5SDavid du Colombier nexterror();
12241a4050f5SDavid du Colombier }
12251a4050f5SDavid du Colombier qlock(&out->io);
12261a4050f5SDavid du Colombier if(tr->debug)pprint("send %d\n", BLEN(b));
12271a4050f5SDavid du Colombier if(tr->debug)pdump(BLEN(b), b->rp, "sent:");
12281a4050f5SDavid du Colombier
12291a4050f5SDavid du Colombier
12301a4050f5SDavid du Colombier ok = SHandshake|SOpen|SRClose;
12311a4050f5SDavid du Colombier if(type == RAlert)
12321a4050f5SDavid du Colombier ok |= SAlert;
12331a4050f5SDavid du Colombier while(bb != nil){
12341a4050f5SDavid du Colombier checkstate(tr, type != RApplication, ok);
12351a4050f5SDavid du Colombier
12361a4050f5SDavid du Colombier /*
12371a4050f5SDavid du Colombier * get at most one maximal record's input,
12381a4050f5SDavid du Colombier * with padding on the front for header and
12391a4050f5SDavid du Colombier * back for mac and maximal block padding.
12401a4050f5SDavid du Colombier */
12411a4050f5SDavid du Colombier if(waserror()){
12421a4050f5SDavid du Colombier qunlock(&out->seclock);
12431a4050f5SDavid du Colombier nexterror();
12441a4050f5SDavid du Colombier }
12451a4050f5SDavid du Colombier qlock(&out->seclock);
12461a4050f5SDavid du Colombier maclen = 0;
12471a4050f5SDavid du Colombier pad = 0;
12481a4050f5SDavid du Colombier if(out->sec != nil){
12491a4050f5SDavid du Colombier maclen = out->sec->maclen;
12501a4050f5SDavid du Colombier pad = maclen + out->sec->block;
12511a4050f5SDavid du Colombier }
12521a4050f5SDavid du Colombier n = BLEN(bb);
12531a4050f5SDavid du Colombier if(n > MaxRecLen){
12541a4050f5SDavid du Colombier n = MaxRecLen;
12551a4050f5SDavid du Colombier nb = allocb(n + pad + RecHdrLen);
12561a4050f5SDavid du Colombier memmove(nb->wp + RecHdrLen, bb->rp, n);
12571a4050f5SDavid du Colombier bb->rp += n;
12581a4050f5SDavid du Colombier }else{
12591a4050f5SDavid du Colombier /*
12601a4050f5SDavid du Colombier * carefully reuse bb so it will get freed if we're out of memory
12611a4050f5SDavid du Colombier */
12621a4050f5SDavid du Colombier bb = padblock(bb, RecHdrLen);
12631a4050f5SDavid du Colombier if(pad)
12641a4050f5SDavid du Colombier nb = padblock(bb, -pad);
12651a4050f5SDavid du Colombier else
12661a4050f5SDavid du Colombier nb = bb;
12671a4050f5SDavid du Colombier bb = nil;
12681a4050f5SDavid du Colombier }
12691a4050f5SDavid du Colombier
12701a4050f5SDavid du Colombier p = nb->rp;
12711a4050f5SDavid du Colombier p[0] = type;
12721a4050f5SDavid du Colombier put16(p+1, tr->version);
12731a4050f5SDavid du Colombier put16(p+3, n);
12741a4050f5SDavid du Colombier
12751a4050f5SDavid du Colombier if(out->sec != nil){
12761a4050f5SDavid du Colombier put64(seq, out->seq);
12771a4050f5SDavid du Colombier out->seq++;
12781a4050f5SDavid du Colombier (*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, p + RecHdrLen + n);
12791a4050f5SDavid du Colombier n += maclen;
12801a4050f5SDavid du Colombier
12811a4050f5SDavid du Colombier /* encrypt */
12821a4050f5SDavid du Colombier n = (*out->sec->enc)(out->sec, p + RecHdrLen, n);
12831a4050f5SDavid du Colombier nb->wp = p + RecHdrLen + n;
12841a4050f5SDavid du Colombier
12851a4050f5SDavid du Colombier /* update length */
12861a4050f5SDavid du Colombier put16(p+3, n);
12871a4050f5SDavid du Colombier }
12881a4050f5SDavid du Colombier if(type == RChangeCipherSpec){
12891a4050f5SDavid du Colombier if(out->new == nil)
12901a4050f5SDavid du Colombier error("change cipher without a new cipher");
12911a4050f5SDavid du Colombier freeSec(out->sec);
12921a4050f5SDavid du Colombier out->sec = out->new;
12931a4050f5SDavid du Colombier out->new = nil;
12941a4050f5SDavid du Colombier out->seq = 0;
12951a4050f5SDavid du Colombier }
12961a4050f5SDavid du Colombier qunlock(&out->seclock);
12971a4050f5SDavid du Colombier poperror();
12981a4050f5SDavid du Colombier
12991a4050f5SDavid du Colombier /*
13001a4050f5SDavid du Colombier * if bwrite error's, we assume the block is queued.
13011a4050f5SDavid du Colombier * if not, we're out of sync with the receiver and will not recover.
13021a4050f5SDavid du Colombier */
13031a4050f5SDavid du Colombier if(waserror()){
13041a4050f5SDavid du Colombier if(strcmp(up->errstr, "interrupted") != 0)
13051a4050f5SDavid du Colombier tlsError(tr, "channel error");
13061a4050f5SDavid du Colombier nexterror();
13071a4050f5SDavid du Colombier }
13081a4050f5SDavid du Colombier devtab[tr->c->type]->bwrite(tr->c, nb, 0);
13091a4050f5SDavid du Colombier poperror();
13101a4050f5SDavid du Colombier }
13111a4050f5SDavid du Colombier qunlock(&out->io);
13121a4050f5SDavid du Colombier poperror();
13131a4050f5SDavid du Colombier }
13141a4050f5SDavid du Colombier
13151a4050f5SDavid du Colombier static long
tlsbwrite(Chan * c,Block * b,ulong offset)13161a4050f5SDavid du Colombier tlsbwrite(Chan *c, Block *b, ulong offset)
13171a4050f5SDavid du Colombier {
13181a4050f5SDavid du Colombier int ty;
13191a4050f5SDavid du Colombier ulong n;
13201a4050f5SDavid du Colombier TlsRec *tr;
13211a4050f5SDavid du Colombier
13221a4050f5SDavid du Colombier n = BLEN(b);
13231a4050f5SDavid du Colombier
13241a4050f5SDavid du Colombier tr = tlsdevs[CONV(c->qid)];
13251a4050f5SDavid du Colombier if(tr == nil)
13261a4050f5SDavid du Colombier panic("tlsbread");
13271a4050f5SDavid du Colombier
13281a4050f5SDavid du Colombier ty = TYPE(c->qid);
13291a4050f5SDavid du Colombier switch(ty) {
13301a4050f5SDavid du Colombier default:
13311a4050f5SDavid du Colombier return devbwrite(c, b, offset);
13321a4050f5SDavid du Colombier case Qhand:
13331a4050f5SDavid du Colombier tlsrecwrite(tr, RHandshake, b);
13341a4050f5SDavid du Colombier tr->handout += n;
13351a4050f5SDavid du Colombier break;
13361a4050f5SDavid du Colombier case Qdata:
13371a4050f5SDavid du Colombier checkstate(tr, 0, SOpen);
13381a4050f5SDavid du Colombier tlsrecwrite(tr, RApplication, b);
13391a4050f5SDavid du Colombier tr->dataout += n;
13401a4050f5SDavid du Colombier break;
13411a4050f5SDavid du Colombier }
13421a4050f5SDavid du Colombier
13431a4050f5SDavid du Colombier return n;
13441a4050f5SDavid du Colombier }
13451a4050f5SDavid du Colombier
13461a4050f5SDavid du Colombier typedef struct Hashalg Hashalg;
13471a4050f5SDavid du Colombier struct Hashalg
13481a4050f5SDavid du Colombier {
13491a4050f5SDavid du Colombier char *name;
13501a4050f5SDavid du Colombier int maclen;
13511a4050f5SDavid du Colombier void (*initkey)(Hashalg *, int, Secret *, uchar*);
13521a4050f5SDavid du Colombier };
13531a4050f5SDavid du Colombier
13541a4050f5SDavid du Colombier static void
initmd5key(Hashalg * ha,int version,Secret * s,uchar * p)13551a4050f5SDavid du Colombier initmd5key(Hashalg *ha, int version, Secret *s, uchar *p)
13561a4050f5SDavid du Colombier {
13571a4050f5SDavid du Colombier s->maclen = ha->maclen;
13581a4050f5SDavid du Colombier if(version == SSL3Version)
13591a4050f5SDavid du Colombier s->mac = sslmac_md5;
13601a4050f5SDavid du Colombier else
13611a4050f5SDavid du Colombier s->mac = hmac_md5;
13621a4050f5SDavid du Colombier memmove(s->mackey, p, ha->maclen);
13631a4050f5SDavid du Colombier }
13641a4050f5SDavid du Colombier
13651a4050f5SDavid du Colombier static void
initclearmac(Hashalg * unused1,int unused2,Secret * s,uchar * unused3)13661a4050f5SDavid du Colombier initclearmac(Hashalg *unused1, int unused2, Secret *s, uchar *unused3)
13671a4050f5SDavid du Colombier {
13681a4050f5SDavid du Colombier s->maclen = 0;
13691a4050f5SDavid du Colombier s->mac = nomac;
13701a4050f5SDavid du Colombier }
13711a4050f5SDavid du Colombier
13721a4050f5SDavid du Colombier static void
initsha1key(Hashalg * ha,int version,Secret * s,uchar * p)13731a4050f5SDavid du Colombier initsha1key(Hashalg *ha, int version, Secret *s, uchar *p)
13741a4050f5SDavid du Colombier {
13751a4050f5SDavid du Colombier s->maclen = ha->maclen;
13761a4050f5SDavid du Colombier if(version == SSL3Version)
13771a4050f5SDavid du Colombier s->mac = sslmac_sha1;
13781a4050f5SDavid du Colombier else
13791a4050f5SDavid du Colombier s->mac = hmac_sha1;
13801a4050f5SDavid du Colombier memmove(s->mackey, p, ha->maclen);
13811a4050f5SDavid du Colombier }
13821a4050f5SDavid du Colombier
13831a4050f5SDavid du Colombier static Hashalg hashtab[] =
13841a4050f5SDavid du Colombier {
13851a4050f5SDavid du Colombier { "clear", 0, initclearmac, },
13861a4050f5SDavid du Colombier { "md5", MD5dlen, initmd5key, },
13871a4050f5SDavid du Colombier { "sha1", SHA1dlen, initsha1key, },
13881a4050f5SDavid du Colombier { 0 }
13891a4050f5SDavid du Colombier };
13901a4050f5SDavid du Colombier
13911a4050f5SDavid du Colombier static Hashalg*
parsehashalg(char * p)13921a4050f5SDavid du Colombier parsehashalg(char *p)
13931a4050f5SDavid du Colombier {
13941a4050f5SDavid du Colombier Hashalg *ha;
13951a4050f5SDavid du Colombier
13961a4050f5SDavid du Colombier for(ha = hashtab; ha->name; ha++)
13971a4050f5SDavid du Colombier if(strcmp(p, ha->name) == 0)
13981a4050f5SDavid du Colombier return ha;
13991a4050f5SDavid du Colombier error("unsupported hash algorithm");
14001a4050f5SDavid du Colombier return nil;
14011a4050f5SDavid du Colombier }
14021a4050f5SDavid du Colombier
14031a4050f5SDavid du Colombier typedef struct Encalg Encalg;
14041a4050f5SDavid du Colombier struct Encalg
14051a4050f5SDavid du Colombier {
14061a4050f5SDavid du Colombier char *name;
14071a4050f5SDavid du Colombier int keylen;
14081a4050f5SDavid du Colombier int ivlen;
14091a4050f5SDavid du Colombier void (*initkey)(Encalg *ea, Secret *, uchar*, uchar*);
14101a4050f5SDavid du Colombier };
14111a4050f5SDavid du Colombier
14121a4050f5SDavid du Colombier static void
initRC4key(Encalg * ea,Secret * s,uchar * p,uchar * unused1)14131a4050f5SDavid du Colombier initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *unused1)
14141a4050f5SDavid du Colombier {
14151a4050f5SDavid du Colombier s->enckey = smalloc(sizeof(RC4state));
14161a4050f5SDavid du Colombier s->enc = rc4enc;
14171a4050f5SDavid du Colombier s->dec = rc4enc;
14181a4050f5SDavid du Colombier s->block = 0;
14191a4050f5SDavid du Colombier setupRC4state(s->enckey, p, ea->keylen);
14201a4050f5SDavid du Colombier }
14211a4050f5SDavid du Colombier
14221a4050f5SDavid du Colombier static void
initDES3key(Encalg * unused1,Secret * s,uchar * p,uchar * iv)14231a4050f5SDavid du Colombier initDES3key(Encalg *unused1, Secret *s, uchar *p, uchar *iv)
14241a4050f5SDavid du Colombier {
14251a4050f5SDavid du Colombier s->enckey = smalloc(sizeof(DES3state));
14261a4050f5SDavid du Colombier s->enc = des3enc;
14271a4050f5SDavid du Colombier s->dec = des3dec;
14281a4050f5SDavid du Colombier s->block = 8;
14291a4050f5SDavid du Colombier setupDES3state(s->enckey, (uchar(*)[8])p, iv);
14301a4050f5SDavid du Colombier }
14311a4050f5SDavid du Colombier
14321a4050f5SDavid du Colombier static void
initclearenc(Encalg * unused1,Secret * s,uchar * unused2,uchar * unused3)14331a4050f5SDavid du Colombier initclearenc(Encalg *unused1, Secret *s, uchar *unused2, uchar *unused3)
14341a4050f5SDavid du Colombier {
14351a4050f5SDavid du Colombier s->enc = noenc;
14361a4050f5SDavid du Colombier s->dec = noenc;
14371a4050f5SDavid du Colombier s->block = 0;
14381a4050f5SDavid du Colombier }
14391a4050f5SDavid du Colombier
14401a4050f5SDavid du Colombier static Encalg encrypttab[] =
14411a4050f5SDavid du Colombier {
14421a4050f5SDavid du Colombier { "clear", 0, 0, initclearenc },
14431a4050f5SDavid du Colombier { "rc4_128", 128/8, 0, initRC4key },
14441a4050f5SDavid du Colombier { "3des_ede_cbc", 3 * 8, 8, initDES3key },
14451a4050f5SDavid du Colombier { 0 }
14461a4050f5SDavid du Colombier };
14471a4050f5SDavid du Colombier
14481a4050f5SDavid du Colombier static Encalg*
parseencalg(char * p)14491a4050f5SDavid du Colombier parseencalg(char *p)
14501a4050f5SDavid du Colombier {
14511a4050f5SDavid du Colombier Encalg *ea;
14521a4050f5SDavid du Colombier
14531a4050f5SDavid du Colombier for(ea = encrypttab; ea->name; ea++)
14541a4050f5SDavid du Colombier if(strcmp(p, ea->name) == 0)
14551a4050f5SDavid du Colombier return ea;
14561a4050f5SDavid du Colombier error("unsupported encryption algorithm");
14571a4050f5SDavid du Colombier return nil;
14581a4050f5SDavid du Colombier }
14591a4050f5SDavid du Colombier
14601a4050f5SDavid du Colombier static long
tlswrite(Chan * c,void * a,long n,vlong off)14611a4050f5SDavid du Colombier tlswrite(Chan *c, void *a, long n, vlong off)
14621a4050f5SDavid du Colombier {
14631a4050f5SDavid du Colombier Encalg *ea;
14641a4050f5SDavid du Colombier Hashalg *ha;
14651a4050f5SDavid du Colombier TlsRec *volatile tr;
14661a4050f5SDavid du Colombier Secret *volatile tos, *volatile toc;
14671a4050f5SDavid du Colombier Block *volatile b;
14681a4050f5SDavid du Colombier Cmdbuf *volatile cb;
14691a4050f5SDavid du Colombier int m, ty;
14701a4050f5SDavid du Colombier char *p, *e;
14711a4050f5SDavid du Colombier uchar *volatile x;
14721a4050f5SDavid du Colombier ulong offset = off;
14731a4050f5SDavid du Colombier
14741a4050f5SDavid du Colombier tr = tlsdevs[CONV(c->qid)];
14751a4050f5SDavid du Colombier if(tr == nil)
14761a4050f5SDavid du Colombier panic("tlswrite");
14771a4050f5SDavid du Colombier
14781a4050f5SDavid du Colombier ty = TYPE(c->qid);
14791a4050f5SDavid du Colombier switch(ty){
14801a4050f5SDavid du Colombier case Qdata:
14811a4050f5SDavid du Colombier case Qhand:
14821a4050f5SDavid du Colombier p = a;
14831a4050f5SDavid du Colombier e = p + n;
14841a4050f5SDavid du Colombier do{
14851a4050f5SDavid du Colombier m = e - p;
14861a4050f5SDavid du Colombier if(m > MaxRecLen)
14871a4050f5SDavid du Colombier m = MaxRecLen;
14881a4050f5SDavid du Colombier
14891a4050f5SDavid du Colombier b = allocb(m);
14901a4050f5SDavid du Colombier if(waserror()){
14911a4050f5SDavid du Colombier freeb(b);
14921a4050f5SDavid du Colombier nexterror();
14931a4050f5SDavid du Colombier }
14941a4050f5SDavid du Colombier memmove(b->wp, p, m);
14951a4050f5SDavid du Colombier poperror();
14961a4050f5SDavid du Colombier b->wp += m;
14971a4050f5SDavid du Colombier
14981a4050f5SDavid du Colombier tlsbwrite(c, b, offset);
14991a4050f5SDavid du Colombier
15001a4050f5SDavid du Colombier p += m;
15011a4050f5SDavid du Colombier }while(p < e);
15021a4050f5SDavid du Colombier return n;
15031a4050f5SDavid du Colombier case Qctl:
15041a4050f5SDavid du Colombier break;
15051a4050f5SDavid du Colombier default:
15061a4050f5SDavid du Colombier error(Ebadusefd);
15071a4050f5SDavid du Colombier return -1;
15081a4050f5SDavid du Colombier }
15091a4050f5SDavid du Colombier
15101a4050f5SDavid du Colombier cb = parsecmd(a, n);
15111a4050f5SDavid du Colombier if(waserror()){
15121a4050f5SDavid du Colombier free(cb);
15131a4050f5SDavid du Colombier nexterror();
15141a4050f5SDavid du Colombier }
15151a4050f5SDavid du Colombier if(cb->nf < 1)
15161a4050f5SDavid du Colombier error("short control request");
15171a4050f5SDavid du Colombier
15181a4050f5SDavid du Colombier /* mutex with operations using what we're about to change */
15191a4050f5SDavid du Colombier if(waserror()){
15201a4050f5SDavid du Colombier qunlock(&tr->in.seclock);
15211a4050f5SDavid du Colombier qunlock(&tr->out.seclock);
15221a4050f5SDavid du Colombier nexterror();
15231a4050f5SDavid du Colombier }
15241a4050f5SDavid du Colombier qlock(&tr->in.seclock);
15251a4050f5SDavid du Colombier qlock(&tr->out.seclock);
15261a4050f5SDavid du Colombier
15271a4050f5SDavid du Colombier if(strcmp(cb->f[0], "fd") == 0){
15281a4050f5SDavid du Colombier if(cb->nf != 3)
15291a4050f5SDavid du Colombier error("usage: fd open-fd version");
15301a4050f5SDavid du Colombier if(tr->c != nil)
15311a4050f5SDavid du Colombier error(Einuse);
15321a4050f5SDavid du Colombier m = strtol(cb->f[2], nil, 0);
15331a4050f5SDavid du Colombier if(m < MinProtoVersion || m > MaxProtoVersion)
15341a4050f5SDavid du Colombier error("unsupported version");
15351a4050f5SDavid du Colombier tr->c = buftochan(cb->f[1]);
15361a4050f5SDavid du Colombier tr->version = m;
15371a4050f5SDavid du Colombier tlsSetState(tr, SHandshake, SClosed);
15381a4050f5SDavid du Colombier }else if(strcmp(cb->f[0], "version") == 0){
15391a4050f5SDavid du Colombier if(cb->nf != 2)
15401a4050f5SDavid du Colombier error("usage: version vers");
15411a4050f5SDavid du Colombier if(tr->c == nil)
15421a4050f5SDavid du Colombier error("must set fd before version");
15431a4050f5SDavid du Colombier if(tr->verset)
15441a4050f5SDavid du Colombier error("version already set");
15451a4050f5SDavid du Colombier m = strtol(cb->f[1], nil, 0);
15461a4050f5SDavid du Colombier if(m == SSL3Version)
15471a4050f5SDavid du Colombier tr->packMac = sslPackMac;
15481a4050f5SDavid du Colombier else if(m == TLSVersion)
15491a4050f5SDavid du Colombier tr->packMac = tlsPackMac;
15501a4050f5SDavid du Colombier else
15511a4050f5SDavid du Colombier error("unsupported version");
15521a4050f5SDavid du Colombier tr->verset = 1;
15531a4050f5SDavid du Colombier tr->version = m;
15541a4050f5SDavid du Colombier }else if(strcmp(cb->f[0], "secret") == 0){
15551a4050f5SDavid du Colombier if(cb->nf != 5)
15561a4050f5SDavid du Colombier error("usage: secret hashalg encalg isclient secretdata");
15571a4050f5SDavid du Colombier if(tr->c == nil || !tr->verset)
15581a4050f5SDavid du Colombier error("must set fd and version before secrets");
15591a4050f5SDavid du Colombier
15601a4050f5SDavid du Colombier if(tr->in.new != nil){
15611a4050f5SDavid du Colombier freeSec(tr->in.new);
15621a4050f5SDavid du Colombier tr->in.new = nil;
15631a4050f5SDavid du Colombier }
15641a4050f5SDavid du Colombier if(tr->out.new != nil){
15651a4050f5SDavid du Colombier freeSec(tr->out.new);
15661a4050f5SDavid du Colombier tr->out.new = nil;
15671a4050f5SDavid du Colombier }
15681a4050f5SDavid du Colombier
15691a4050f5SDavid du Colombier ha = parsehashalg(cb->f[1]);
15701a4050f5SDavid du Colombier ea = parseencalg(cb->f[2]);
15711a4050f5SDavid du Colombier
15721a4050f5SDavid du Colombier p = cb->f[4];
15731a4050f5SDavid du Colombier m = (strlen(p)*3)/2;
15741a4050f5SDavid du Colombier x = smalloc(m);
15751a4050f5SDavid du Colombier tos = nil;
15761a4050f5SDavid du Colombier toc = nil;
15771a4050f5SDavid du Colombier if(waserror()){
15781a4050f5SDavid du Colombier freeSec(tos);
15791a4050f5SDavid du Colombier freeSec(toc);
15801a4050f5SDavid du Colombier free(x);
15811a4050f5SDavid du Colombier nexterror();
15821a4050f5SDavid du Colombier }
15831a4050f5SDavid du Colombier m = dec64(x, m, p, strlen(p));
15841a4050f5SDavid du Colombier if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen)
15851a4050f5SDavid du Colombier error("not enough secret data provided");
15861a4050f5SDavid du Colombier
15871a4050f5SDavid du Colombier tos = smalloc(sizeof(Secret));
15881a4050f5SDavid du Colombier toc = smalloc(sizeof(Secret));
15891a4050f5SDavid du Colombier if(!ha->initkey || !ea->initkey)
15901a4050f5SDavid du Colombier error("misimplemented secret algorithm");
15911a4050f5SDavid du Colombier (*ha->initkey)(ha, tr->version, tos, &x[0]);
15921a4050f5SDavid du Colombier (*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]);
15931a4050f5SDavid du Colombier (*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
15941a4050f5SDavid du Colombier (*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
15951a4050f5SDavid du Colombier
15961a4050f5SDavid du Colombier if(!tos->mac || !tos->enc || !tos->dec
15971a4050f5SDavid du Colombier || !toc->mac || !toc->enc || !toc->dec)
15981a4050f5SDavid du Colombier error("missing algorithm implementations");
15991a4050f5SDavid du Colombier if(strtol(cb->f[3], nil, 0) == 0){
16001a4050f5SDavid du Colombier tr->in.new = tos;
16011a4050f5SDavid du Colombier tr->out.new = toc;
16021a4050f5SDavid du Colombier }else{
16031a4050f5SDavid du Colombier tr->in.new = toc;
16041a4050f5SDavid du Colombier tr->out.new = tos;
16051a4050f5SDavid du Colombier }
16061a4050f5SDavid du Colombier if(tr->version == SSL3Version){
16071a4050f5SDavid du Colombier toc->unpad = sslunpad;
16081a4050f5SDavid du Colombier tos->unpad = sslunpad;
16091a4050f5SDavid du Colombier }else{
16101a4050f5SDavid du Colombier toc->unpad = tlsunpad;
16111a4050f5SDavid du Colombier tos->unpad = tlsunpad;
16121a4050f5SDavid du Colombier }
16131a4050f5SDavid du Colombier toc->encalg = ea->name;
16141a4050f5SDavid du Colombier toc->hashalg = ha->name;
16151a4050f5SDavid du Colombier tos->encalg = ea->name;
16161a4050f5SDavid du Colombier tos->hashalg = ha->name;
16171a4050f5SDavid du Colombier
16181a4050f5SDavid du Colombier free(x);
16191a4050f5SDavid du Colombier poperror();
16201a4050f5SDavid du Colombier }else if(strcmp(cb->f[0], "changecipher") == 0){
16211a4050f5SDavid du Colombier if(cb->nf != 1)
16221a4050f5SDavid du Colombier error("usage: changecipher");
16231a4050f5SDavid du Colombier if(tr->out.new == nil)
16241a4050f5SDavid du Colombier error("cannot change cipher spec without setting secret");
16251a4050f5SDavid du Colombier
16261a4050f5SDavid du Colombier qunlock(&tr->in.seclock);
16271a4050f5SDavid du Colombier qunlock(&tr->out.seclock);
16281a4050f5SDavid du Colombier poperror();
16291a4050f5SDavid du Colombier free(cb);
16301a4050f5SDavid du Colombier poperror();
16311a4050f5SDavid du Colombier
16321a4050f5SDavid du Colombier /*
16331a4050f5SDavid du Colombier * the real work is done as the message is written
16341a4050f5SDavid du Colombier * so the stream is encrypted in sync.
16351a4050f5SDavid du Colombier */
16361a4050f5SDavid du Colombier b = allocb(1);
16371a4050f5SDavid du Colombier *b->wp++ = 1;
16381a4050f5SDavid du Colombier tlsrecwrite(tr, RChangeCipherSpec, b);
16391a4050f5SDavid du Colombier return n;
16401a4050f5SDavid du Colombier }else if(strcmp(cb->f[0], "opened") == 0){
16411a4050f5SDavid du Colombier if(cb->nf != 1)
16421a4050f5SDavid du Colombier error("usage: opened");
16431a4050f5SDavid du Colombier if(tr->in.sec == nil || tr->out.sec == nil)
16441a4050f5SDavid du Colombier error("cipher must be configured before enabling data messages");
16451a4050f5SDavid du Colombier lock(&tr->statelk);
16461a4050f5SDavid du Colombier if(tr->state != SHandshake && tr->state != SOpen){
16471a4050f5SDavid du Colombier unlock(&tr->statelk);
16481a4050f5SDavid du Colombier error("cannot enable data messages");
16491a4050f5SDavid du Colombier }
16501a4050f5SDavid du Colombier tr->state = SOpen;
16511a4050f5SDavid du Colombier unlock(&tr->statelk);
16521a4050f5SDavid du Colombier tr->opened = 1;
16531a4050f5SDavid du Colombier }else if(strcmp(cb->f[0], "alert") == 0){
16541a4050f5SDavid du Colombier if(cb->nf != 2)
16551a4050f5SDavid du Colombier error("usage: alert n");
16561a4050f5SDavid du Colombier if(tr->c == nil)
16571a4050f5SDavid du Colombier error("must set fd before sending alerts");
16581a4050f5SDavid du Colombier m = strtol(cb->f[1], nil, 0);
16591a4050f5SDavid du Colombier
16601a4050f5SDavid du Colombier qunlock(&tr->in.seclock);
16611a4050f5SDavid du Colombier qunlock(&tr->out.seclock);
16621a4050f5SDavid du Colombier poperror();
16631a4050f5SDavid du Colombier free(cb);
16641a4050f5SDavid du Colombier poperror();
16651a4050f5SDavid du Colombier
16661a4050f5SDavid du Colombier sendAlert(tr, m);
16671a4050f5SDavid du Colombier
16681a4050f5SDavid du Colombier if(m == ECloseNotify)
16691a4050f5SDavid du Colombier tlsclosed(tr, SLClose);
16701a4050f5SDavid du Colombier
16711a4050f5SDavid du Colombier return n;
16721a4050f5SDavid du Colombier } else if(strcmp(cb->f[0], "debug") == 0){
16731a4050f5SDavid du Colombier if(cb->nf == 2){
16741a4050f5SDavid du Colombier if(strcmp(cb->f[1], "on") == 0)
16751a4050f5SDavid du Colombier tr->debug = 1;
16761a4050f5SDavid du Colombier else
16771a4050f5SDavid du Colombier tr->debug = 0;
16781a4050f5SDavid du Colombier } else
16791a4050f5SDavid du Colombier tr->debug = 1;
16801a4050f5SDavid du Colombier } else
16811a4050f5SDavid du Colombier error(Ebadarg);
16821a4050f5SDavid du Colombier
16831a4050f5SDavid du Colombier qunlock(&tr->in.seclock);
16841a4050f5SDavid du Colombier qunlock(&tr->out.seclock);
16851a4050f5SDavid du Colombier poperror();
16861a4050f5SDavid du Colombier free(cb);
16871a4050f5SDavid du Colombier poperror();
16881a4050f5SDavid du Colombier
16891a4050f5SDavid du Colombier return n;
16901a4050f5SDavid du Colombier }
16911a4050f5SDavid du Colombier
16921a4050f5SDavid du Colombier static void
tlsinit(void)16931a4050f5SDavid du Colombier tlsinit(void)
16941a4050f5SDavid du Colombier {
16951a4050f5SDavid du Colombier struct Encalg *e;
16961a4050f5SDavid du Colombier struct Hashalg *h;
16971a4050f5SDavid du Colombier int n;
16981a4050f5SDavid du Colombier char *cp;
16991a4050f5SDavid du Colombier static int already;
17001a4050f5SDavid du Colombier
17011a4050f5SDavid du Colombier if(!already){
17021a4050f5SDavid du Colombier fmtinstall('H', encodefmt);
17031a4050f5SDavid du Colombier already = 1;
17041a4050f5SDavid du Colombier }
17051a4050f5SDavid du Colombier
17061a4050f5SDavid du Colombier tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs);
17071a4050f5SDavid du Colombier trnames = smalloc((sizeof *trnames) * maxtlsdevs);
17081a4050f5SDavid du Colombier
17091a4050f5SDavid du Colombier n = 1;
17101a4050f5SDavid du Colombier for(e = encrypttab; e->name != nil; e++)
17111a4050f5SDavid du Colombier n += strlen(e->name) + 1;
17121a4050f5SDavid du Colombier cp = encalgs = smalloc(n);
17131a4050f5SDavid du Colombier for(e = encrypttab;;){
17141a4050f5SDavid du Colombier strcpy(cp, e->name);
17151a4050f5SDavid du Colombier cp += strlen(e->name);
17161a4050f5SDavid du Colombier e++;
17171a4050f5SDavid du Colombier if(e->name == nil)
17181a4050f5SDavid du Colombier break;
17191a4050f5SDavid du Colombier *cp++ = ' ';
17201a4050f5SDavid du Colombier }
17211a4050f5SDavid du Colombier *cp = 0;
17221a4050f5SDavid du Colombier
17231a4050f5SDavid du Colombier n = 1;
17241a4050f5SDavid du Colombier for(h = hashtab; h->name != nil; h++)
17251a4050f5SDavid du Colombier n += strlen(h->name) + 1;
17261a4050f5SDavid du Colombier cp = hashalgs = smalloc(n);
17271a4050f5SDavid du Colombier for(h = hashtab;;){
17281a4050f5SDavid du Colombier strcpy(cp, h->name);
17291a4050f5SDavid du Colombier cp += strlen(h->name);
17301a4050f5SDavid du Colombier h++;
17311a4050f5SDavid du Colombier if(h->name == nil)
17321a4050f5SDavid du Colombier break;
17331a4050f5SDavid du Colombier *cp++ = ' ';
17341a4050f5SDavid du Colombier }
17351a4050f5SDavid du Colombier *cp = 0;
17361a4050f5SDavid du Colombier }
17371a4050f5SDavid du Colombier
17381a4050f5SDavid du Colombier Dev tlsdevtab = {
17391a4050f5SDavid du Colombier 'a',
17401a4050f5SDavid du Colombier "tls",
17411a4050f5SDavid du Colombier
17421a4050f5SDavid du Colombier devreset,
17431a4050f5SDavid du Colombier tlsinit,
17441a4050f5SDavid du Colombier devshutdown,
17451a4050f5SDavid du Colombier tlsattach,
17461a4050f5SDavid du Colombier tlswalk,
17471a4050f5SDavid du Colombier tlsstat,
17481a4050f5SDavid du Colombier tlsopen,
17491a4050f5SDavid du Colombier devcreate,
17501a4050f5SDavid du Colombier tlsclose,
17511a4050f5SDavid du Colombier tlsread,
17521a4050f5SDavid du Colombier tlsbread,
17531a4050f5SDavid du Colombier tlswrite,
17541a4050f5SDavid du Colombier tlsbwrite,
17551a4050f5SDavid du Colombier devremove,
17561a4050f5SDavid du Colombier tlswstat,
17571a4050f5SDavid du Colombier };
17581a4050f5SDavid du Colombier
17591a4050f5SDavid du Colombier /* get channel associated with an fd */
17601a4050f5SDavid du Colombier static Chan*
buftochan(char * p)17611a4050f5SDavid du Colombier buftochan(char *p)
17621a4050f5SDavid du Colombier {
17631a4050f5SDavid du Colombier Chan *c;
17641a4050f5SDavid du Colombier int fd;
17651a4050f5SDavid du Colombier
17661a4050f5SDavid du Colombier if(p == 0)
17671a4050f5SDavid du Colombier error(Ebadarg);
17681a4050f5SDavid du Colombier fd = strtoul(p, 0, 0);
17691a4050f5SDavid du Colombier if(fd < 0)
17701a4050f5SDavid du Colombier error(Ebadarg);
17711a4050f5SDavid du Colombier c = fdtochan(fd, -1, 0, 1); /* error check and inc ref */
17721a4050f5SDavid du Colombier return c;
17731a4050f5SDavid du Colombier }
17741a4050f5SDavid du Colombier
17751a4050f5SDavid du Colombier static void
sendAlert(TlsRec * tr,int err)17761a4050f5SDavid du Colombier sendAlert(TlsRec *tr, int err)
17771a4050f5SDavid du Colombier {
17781a4050f5SDavid du Colombier Block *b;
17791a4050f5SDavid du Colombier int i, fatal;
17801a4050f5SDavid du Colombier char *msg;
17811a4050f5SDavid du Colombier
17821a4050f5SDavid du Colombier if(tr->debug)pprint("sendAlert %d\n", err);
17831a4050f5SDavid du Colombier fatal = 1;
17841a4050f5SDavid du Colombier msg = "tls unknown alert";
17851a4050f5SDavid du Colombier for(i=0; i < nelem(tlserrs); i++) {
17861a4050f5SDavid du Colombier if(tlserrs[i].err == err) {
17871a4050f5SDavid du Colombier msg = tlserrs[i].msg;
17881a4050f5SDavid du Colombier if(tr->version == SSL3Version)
17891a4050f5SDavid du Colombier err = tlserrs[i].sslerr;
17901a4050f5SDavid du Colombier else
17911a4050f5SDavid du Colombier err = tlserrs[i].tlserr;
17921a4050f5SDavid du Colombier fatal = tlserrs[i].fatal;
17931a4050f5SDavid du Colombier break;
17941a4050f5SDavid du Colombier }
17951a4050f5SDavid du Colombier }
17961a4050f5SDavid du Colombier
17971a4050f5SDavid du Colombier if(!waserror()){
17981a4050f5SDavid du Colombier b = allocb(2);
17991a4050f5SDavid du Colombier *b->wp++ = fatal + 1;
18001a4050f5SDavid du Colombier *b->wp++ = err;
18011a4050f5SDavid du Colombier if(fatal)
18021a4050f5SDavid du Colombier tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose);
18031a4050f5SDavid du Colombier tlsrecwrite(tr, RAlert, b);
18041a4050f5SDavid du Colombier poperror();
18051a4050f5SDavid du Colombier }
18061a4050f5SDavid du Colombier if(fatal)
18071a4050f5SDavid du Colombier tlsError(tr, msg);
18081a4050f5SDavid du Colombier }
18091a4050f5SDavid du Colombier
18101a4050f5SDavid du Colombier static void
tlsError(TlsRec * tr,char * msg)18111a4050f5SDavid du Colombier tlsError(TlsRec *tr, char *msg)
18121a4050f5SDavid du Colombier {
18131a4050f5SDavid du Colombier int s;
18141a4050f5SDavid du Colombier
18151a4050f5SDavid du Colombier if(tr->debug)pprint("tleError %s\n", msg);
18161a4050f5SDavid du Colombier lock(&tr->statelk);
18171a4050f5SDavid du Colombier s = tr->state;
18181a4050f5SDavid du Colombier tr->state = SError;
18191a4050f5SDavid du Colombier if(s != SError){
18201a4050f5SDavid du Colombier strncpy(tr->err, msg, ERRMAX - 1);
18211a4050f5SDavid du Colombier tr->err[ERRMAX - 1] = '\0';
18221a4050f5SDavid du Colombier }
18231a4050f5SDavid du Colombier unlock(&tr->statelk);
18241a4050f5SDavid du Colombier if(s != SError)
18251a4050f5SDavid du Colombier alertHand(tr, msg);
18261a4050f5SDavid du Colombier }
18271a4050f5SDavid du Colombier
18281a4050f5SDavid du Colombier static void
tlsSetState(TlsRec * tr,int new,int old)18291a4050f5SDavid du Colombier tlsSetState(TlsRec *tr, int new, int old)
18301a4050f5SDavid du Colombier {
18311a4050f5SDavid du Colombier lock(&tr->statelk);
18321a4050f5SDavid du Colombier if(tr->state & old)
18331a4050f5SDavid du Colombier tr->state = new;
18341a4050f5SDavid du Colombier unlock(&tr->statelk);
18351a4050f5SDavid du Colombier }
18361a4050f5SDavid du Colombier
18371a4050f5SDavid du Colombier /* hand up a digest connection */
18381a4050f5SDavid du Colombier static void
tlshangup(TlsRec * tr)18391a4050f5SDavid du Colombier tlshangup(TlsRec *tr)
18401a4050f5SDavid du Colombier {
18411a4050f5SDavid du Colombier Block *b;
18421a4050f5SDavid du Colombier
18431a4050f5SDavid du Colombier qlock(&tr->in.io);
18441a4050f5SDavid du Colombier for(b = tr->processed; b; b = tr->processed){
18451a4050f5SDavid du Colombier tr->processed = b->next;
18461a4050f5SDavid du Colombier freeb(b);
18471a4050f5SDavid du Colombier }
18481a4050f5SDavid du Colombier if(tr->unprocessed != nil){
18491a4050f5SDavid du Colombier freeb(tr->unprocessed);
18501a4050f5SDavid du Colombier tr->unprocessed = nil;
18511a4050f5SDavid du Colombier }
18521a4050f5SDavid du Colombier qunlock(&tr->in.io);
18531a4050f5SDavid du Colombier
18541a4050f5SDavid du Colombier tlsSetState(tr, SClosed, ~0);
18551a4050f5SDavid du Colombier }
18561a4050f5SDavid du Colombier
18571a4050f5SDavid du Colombier static TlsRec*
newtls(Chan * ch)18581a4050f5SDavid du Colombier newtls(Chan *ch)
18591a4050f5SDavid du Colombier {
18601a4050f5SDavid du Colombier TlsRec **pp, **ep, **np;
18611a4050f5SDavid du Colombier char **nmp;
18621a4050f5SDavid du Colombier int t, newmax;
18631a4050f5SDavid du Colombier
18641a4050f5SDavid du Colombier if(waserror()) {
18651a4050f5SDavid du Colombier unlock(&tdlock);
18661a4050f5SDavid du Colombier nexterror();
18671a4050f5SDavid du Colombier }
18681a4050f5SDavid du Colombier lock(&tdlock);
18691a4050f5SDavid du Colombier ep = &tlsdevs[maxtlsdevs];
18701a4050f5SDavid du Colombier for(pp = tlsdevs; pp < ep; pp++)
18711a4050f5SDavid du Colombier if(*pp == nil)
18721a4050f5SDavid du Colombier break;
18731a4050f5SDavid du Colombier if(pp >= ep) {
18741a4050f5SDavid du Colombier if(maxtlsdevs >= MaxTlsDevs) {
18751a4050f5SDavid du Colombier unlock(&tdlock);
18761a4050f5SDavid du Colombier poperror();
18771a4050f5SDavid du Colombier return nil;
18781a4050f5SDavid du Colombier }
18791a4050f5SDavid du Colombier newmax = 2 * maxtlsdevs;
18801a4050f5SDavid du Colombier if(newmax > MaxTlsDevs)
18811a4050f5SDavid du Colombier newmax = MaxTlsDevs;
18821a4050f5SDavid du Colombier np = smalloc(sizeof(TlsRec*) * newmax);
18831a4050f5SDavid du Colombier memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs);
18841a4050f5SDavid du Colombier tlsdevs = np;
18851a4050f5SDavid du Colombier pp = &tlsdevs[maxtlsdevs];
18861a4050f5SDavid du Colombier memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs));
18871a4050f5SDavid du Colombier
18881a4050f5SDavid du Colombier nmp = smalloc(sizeof *nmp * newmax);
18891a4050f5SDavid du Colombier memmove(nmp, trnames, sizeof *nmp * maxtlsdevs);
18901a4050f5SDavid du Colombier trnames = nmp;
18911a4050f5SDavid du Colombier
18921a4050f5SDavid du Colombier maxtlsdevs = newmax;
18931a4050f5SDavid du Colombier }
18941a4050f5SDavid du Colombier *pp = mktlsrec();
18951a4050f5SDavid du Colombier if(pp - tlsdevs >= tdhiwat)
18961a4050f5SDavid du Colombier tdhiwat++;
18971a4050f5SDavid du Colombier t = TYPE(ch->qid);
18981a4050f5SDavid du Colombier if(t == Qclonus)
18991a4050f5SDavid du Colombier t = Qctl;
19001a4050f5SDavid du Colombier ch->qid.path = QID(pp - tlsdevs, t);
19011a4050f5SDavid du Colombier ch->qid.vers = 0;
19021a4050f5SDavid du Colombier unlock(&tdlock);
19031a4050f5SDavid du Colombier poperror();
19041a4050f5SDavid du Colombier return *pp;
19051a4050f5SDavid du Colombier }
19061a4050f5SDavid du Colombier
19071a4050f5SDavid du Colombier static TlsRec *
mktlsrec(void)19081a4050f5SDavid du Colombier mktlsrec(void)
19091a4050f5SDavid du Colombier {
19101a4050f5SDavid du Colombier TlsRec *tr;
19111a4050f5SDavid du Colombier
19121a4050f5SDavid du Colombier tr = mallocz(sizeof(*tr), 1);
19131a4050f5SDavid du Colombier if(tr == nil)
19141a4050f5SDavid du Colombier error(Enomem);
19151a4050f5SDavid du Colombier tr->state = SClosed;
19161a4050f5SDavid du Colombier tr->ref = 1;
19171a4050f5SDavid du Colombier kstrdup(&tr->user, up->user);
19181a4050f5SDavid du Colombier tr->perm = 0660;
19191a4050f5SDavid du Colombier return tr;
19201a4050f5SDavid du Colombier }
19211a4050f5SDavid du Colombier
19221a4050f5SDavid du Colombier static char*
tlsstate(int s)19231a4050f5SDavid du Colombier tlsstate(int s)
19241a4050f5SDavid du Colombier {
19251a4050f5SDavid du Colombier switch(s){
19261a4050f5SDavid du Colombier case SHandshake:
19271a4050f5SDavid du Colombier return "Handshaking";
19281a4050f5SDavid du Colombier case SOpen:
19291a4050f5SDavid du Colombier return "Established";
19301a4050f5SDavid du Colombier case SRClose:
19311a4050f5SDavid du Colombier return "RemoteClosed";
19321a4050f5SDavid du Colombier case SLClose:
19331a4050f5SDavid du Colombier return "LocalClosed";
19341a4050f5SDavid du Colombier case SAlert:
19351a4050f5SDavid du Colombier return "Alerting";
19361a4050f5SDavid du Colombier case SError:
19371a4050f5SDavid du Colombier return "Errored";
19381a4050f5SDavid du Colombier case SClosed:
19391a4050f5SDavid du Colombier return "Closed";
19401a4050f5SDavid du Colombier }
19411a4050f5SDavid du Colombier return "Unknown";
19421a4050f5SDavid du Colombier }
19431a4050f5SDavid du Colombier
19441a4050f5SDavid du Colombier static void
freeSec(Secret * s)19451a4050f5SDavid du Colombier freeSec(Secret *s)
19461a4050f5SDavid du Colombier {
19471a4050f5SDavid du Colombier if(s != nil){
19481a4050f5SDavid du Colombier free(s->enckey);
19491a4050f5SDavid du Colombier free(s);
19501a4050f5SDavid du Colombier }
19511a4050f5SDavid du Colombier }
19521a4050f5SDavid du Colombier
19531a4050f5SDavid du Colombier static int
noenc(Secret * unused1,uchar * unused2,int n)19541a4050f5SDavid du Colombier noenc(Secret *unused1, uchar *unused2, int n)
19551a4050f5SDavid du Colombier {
19561a4050f5SDavid du Colombier return n;
19571a4050f5SDavid du Colombier }
19581a4050f5SDavid du Colombier
19591a4050f5SDavid du Colombier static int
rc4enc(Secret * sec,uchar * buf,int n)19601a4050f5SDavid du Colombier rc4enc(Secret *sec, uchar *buf, int n)
19611a4050f5SDavid du Colombier {
19621a4050f5SDavid du Colombier rc4(sec->enckey, buf, n);
19631a4050f5SDavid du Colombier return n;
19641a4050f5SDavid du Colombier }
19651a4050f5SDavid du Colombier
19661a4050f5SDavid du Colombier static int
tlsunpad(uchar * buf,int n,int block)19671a4050f5SDavid du Colombier tlsunpad(uchar *buf, int n, int block)
19681a4050f5SDavid du Colombier {
19691a4050f5SDavid du Colombier int pad, nn;
19701a4050f5SDavid du Colombier
19711a4050f5SDavid du Colombier pad = buf[n - 1];
19721a4050f5SDavid du Colombier nn = n - 1 - pad;
19731a4050f5SDavid du Colombier if(nn <= 0 || n % block)
19741a4050f5SDavid du Colombier return -1;
19751a4050f5SDavid du Colombier while(--n > nn)
19761a4050f5SDavid du Colombier if(pad != buf[n - 1])
19771a4050f5SDavid du Colombier return -1;
19781a4050f5SDavid du Colombier return nn;
19791a4050f5SDavid du Colombier }
19801a4050f5SDavid du Colombier
19811a4050f5SDavid du Colombier static int
sslunpad(uchar * buf,int n,int block)19821a4050f5SDavid du Colombier sslunpad(uchar *buf, int n, int block)
19831a4050f5SDavid du Colombier {
19841a4050f5SDavid du Colombier int pad, nn;
19851a4050f5SDavid du Colombier
19861a4050f5SDavid du Colombier pad = buf[n - 1];
19871a4050f5SDavid du Colombier nn = n - 1 - pad;
19881a4050f5SDavid du Colombier if(nn <= 0 || n % block)
19891a4050f5SDavid du Colombier return -1;
19901a4050f5SDavid du Colombier return nn;
19911a4050f5SDavid du Colombier }
19921a4050f5SDavid du Colombier
19931a4050f5SDavid du Colombier static int
blockpad(uchar * buf,int n,int block)19941a4050f5SDavid du Colombier blockpad(uchar *buf, int n, int block)
19951a4050f5SDavid du Colombier {
19961a4050f5SDavid du Colombier int pad, nn;
19971a4050f5SDavid du Colombier
19981a4050f5SDavid du Colombier nn = n + block;
19991a4050f5SDavid du Colombier nn -= nn % block;
20001a4050f5SDavid du Colombier pad = nn - (n + 1);
20011a4050f5SDavid du Colombier while(n < nn)
20021a4050f5SDavid du Colombier buf[n++] = pad;
20031a4050f5SDavid du Colombier return nn;
20041a4050f5SDavid du Colombier }
20051a4050f5SDavid du Colombier
20061a4050f5SDavid du Colombier static int
des3enc(Secret * sec,uchar * buf,int n)20071a4050f5SDavid du Colombier des3enc(Secret *sec, uchar *buf, int n)
20081a4050f5SDavid du Colombier {
20091a4050f5SDavid du Colombier n = blockpad(buf, n, 8);
20101a4050f5SDavid du Colombier des3CBCencrypt(buf, n, sec->enckey);
20111a4050f5SDavid du Colombier return n;
20121a4050f5SDavid du Colombier }
20131a4050f5SDavid du Colombier
20141a4050f5SDavid du Colombier static int
des3dec(Secret * sec,uchar * buf,int n)20151a4050f5SDavid du Colombier des3dec(Secret *sec, uchar *buf, int n)
20161a4050f5SDavid du Colombier {
20171a4050f5SDavid du Colombier des3CBCdecrypt(buf, n, sec->enckey);
20181a4050f5SDavid du Colombier return (*sec->unpad)(buf, n, 8);
20191a4050f5SDavid du Colombier }
20201a4050f5SDavid du Colombier static DigestState*
nomac(uchar * unused1,ulong unused2,uchar * unused3,ulong unused4,uchar * unused5,DigestState * unused6)20211a4050f5SDavid du Colombier nomac(uchar *unused1, ulong unused2, uchar *unused3, ulong unused4,
20221a4050f5SDavid du Colombier uchar *unused5, DigestState *unused6)
20231a4050f5SDavid du Colombier {
20241a4050f5SDavid du Colombier return nil;
20251a4050f5SDavid du Colombier }
20261a4050f5SDavid du Colombier
20271a4050f5SDavid du Colombier /*
20281a4050f5SDavid du Colombier * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac.
20291a4050f5SDavid du Colombier */
20301a4050f5SDavid du Colombier static DigestState*
sslmac_x(uchar * p,ulong len,uchar * key,ulong klen,uchar * digest,DigestState * s,DigestState * (* x)(uchar *,ulong,uchar *,DigestState *),int xlen,int padlen)20311a4050f5SDavid du Colombier sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s,
20321a4050f5SDavid du Colombier DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen)
20331a4050f5SDavid du Colombier {
20341a4050f5SDavid du Colombier int i;
20351a4050f5SDavid du Colombier uchar pad[48], innerdigest[20];
20361a4050f5SDavid du Colombier
20371a4050f5SDavid du Colombier if(xlen > sizeof(innerdigest)
20381a4050f5SDavid du Colombier || padlen > sizeof(pad))
20391a4050f5SDavid du Colombier return nil;
20401a4050f5SDavid du Colombier
20411a4050f5SDavid du Colombier if(klen>64)
20421a4050f5SDavid du Colombier return nil;
20431a4050f5SDavid du Colombier
20441a4050f5SDavid du Colombier /* first time through */
20451a4050f5SDavid du Colombier if(s == nil){
20461a4050f5SDavid du Colombier for(i=0; i<padlen; i++)
20471a4050f5SDavid du Colombier pad[i] = 0x36;
20481a4050f5SDavid du Colombier s = (*x)(key, klen, nil, nil);
20491a4050f5SDavid du Colombier s = (*x)(pad, padlen, nil, s);
20501a4050f5SDavid du Colombier if(s == nil)
20511a4050f5SDavid du Colombier return nil;
20521a4050f5SDavid du Colombier }
20531a4050f5SDavid du Colombier
20541a4050f5SDavid du Colombier s = (*x)(p, len, nil, s);
20551a4050f5SDavid du Colombier if(digest == nil)
20561a4050f5SDavid du Colombier return s;
20571a4050f5SDavid du Colombier
20581a4050f5SDavid du Colombier /* last time through */
20591a4050f5SDavid du Colombier for(i=0; i<padlen; i++)
20601a4050f5SDavid du Colombier pad[i] = 0x5c;
20611a4050f5SDavid du Colombier (*x)(nil, 0, innerdigest, s);
20621a4050f5SDavid du Colombier s = (*x)(key, klen, nil, nil);
20631a4050f5SDavid du Colombier s = (*x)(pad, padlen, nil, s);
20641a4050f5SDavid du Colombier (*x)(innerdigest, xlen, digest, s);
20651a4050f5SDavid du Colombier return nil;
20661a4050f5SDavid du Colombier }
20671a4050f5SDavid du Colombier
20681a4050f5SDavid du Colombier static DigestState*
sslmac_sha1(uchar * p,ulong len,uchar * key,ulong klen,uchar * digest,DigestState * s)20691a4050f5SDavid du Colombier sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
20701a4050f5SDavid du Colombier {
20711a4050f5SDavid du Colombier return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40);
20721a4050f5SDavid du Colombier }
20731a4050f5SDavid du Colombier
20741a4050f5SDavid du Colombier static DigestState*
sslmac_md5(uchar * p,ulong len,uchar * key,ulong klen,uchar * digest,DigestState * s)20751a4050f5SDavid du Colombier sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
20761a4050f5SDavid du Colombier {
20771a4050f5SDavid du Colombier return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48);
20781a4050f5SDavid du Colombier }
20791a4050f5SDavid du Colombier
20801a4050f5SDavid du Colombier static void
sslPackMac(Secret * sec,uchar * mackey,uchar * seq,uchar * header,uchar * body,int len,uchar * mac)20811a4050f5SDavid du Colombier sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
20821a4050f5SDavid du Colombier {
20831a4050f5SDavid du Colombier DigestState *s;
20841a4050f5SDavid du Colombier uchar buf[11];
20851a4050f5SDavid du Colombier
20861a4050f5SDavid du Colombier memmove(buf, seq, 8);
20871a4050f5SDavid du Colombier buf[8] = header[0];
20881a4050f5SDavid du Colombier buf[9] = header[3];
20891a4050f5SDavid du Colombier buf[10] = header[4];
20901a4050f5SDavid du Colombier
20911a4050f5SDavid du Colombier s = (*sec->mac)(buf, 11, mackey, sec->maclen, 0, 0);
20921a4050f5SDavid du Colombier (*sec->mac)(body, len, mackey, sec->maclen, mac, s);
20931a4050f5SDavid du Colombier }
20941a4050f5SDavid du Colombier
20951a4050f5SDavid du Colombier static void
tlsPackMac(Secret * sec,uchar * mackey,uchar * seq,uchar * header,uchar * body,int len,uchar * mac)20961a4050f5SDavid du Colombier tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
20971a4050f5SDavid du Colombier {
20981a4050f5SDavid du Colombier DigestState *s;
20991a4050f5SDavid du Colombier uchar buf[13];
21001a4050f5SDavid du Colombier
21011a4050f5SDavid du Colombier memmove(buf, seq, 8);
21021a4050f5SDavid du Colombier memmove(&buf[8], header, 5);
21031a4050f5SDavid du Colombier
21041a4050f5SDavid du Colombier s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0);
21051a4050f5SDavid du Colombier (*sec->mac)(body, len, mackey, sec->maclen, mac, s);
21061a4050f5SDavid du Colombier }
21071a4050f5SDavid du Colombier
21081a4050f5SDavid du Colombier static void
put32(uchar * p,u32int x)21091a4050f5SDavid du Colombier put32(uchar *p, u32int x)
21101a4050f5SDavid du Colombier {
21111a4050f5SDavid du Colombier p[0] = x>>24;
21121a4050f5SDavid du Colombier p[1] = x>>16;
21131a4050f5SDavid du Colombier p[2] = x>>8;
21141a4050f5SDavid du Colombier p[3] = x;
21151a4050f5SDavid du Colombier }
21161a4050f5SDavid du Colombier
21171a4050f5SDavid du Colombier static void
put64(uchar * p,vlong x)21181a4050f5SDavid du Colombier put64(uchar *p, vlong x)
21191a4050f5SDavid du Colombier {
21201a4050f5SDavid du Colombier put32(p, (u32int)(x >> 32));
21211a4050f5SDavid du Colombier put32(p+4, (u32int)x);
21221a4050f5SDavid du Colombier }
21231a4050f5SDavid du Colombier
21241a4050f5SDavid du Colombier static void
put24(uchar * p,int x)21251a4050f5SDavid du Colombier put24(uchar *p, int x)
21261a4050f5SDavid du Colombier {
21271a4050f5SDavid du Colombier p[0] = x>>16;
21281a4050f5SDavid du Colombier p[1] = x>>8;
21291a4050f5SDavid du Colombier p[2] = x;
21301a4050f5SDavid du Colombier }
21311a4050f5SDavid du Colombier
21321a4050f5SDavid du Colombier static void
put16(uchar * p,int x)21331a4050f5SDavid du Colombier put16(uchar *p, int x)
21341a4050f5SDavid du Colombier {
21351a4050f5SDavid du Colombier p[0] = x>>8;
21361a4050f5SDavid du Colombier p[1] = x;
21371a4050f5SDavid du Colombier }
21381a4050f5SDavid du Colombier
2139*ec59a3ddSDavid du Colombier /*
21401a4050f5SDavid du Colombier static u32int
21411a4050f5SDavid du Colombier get32(uchar *p)
21421a4050f5SDavid du Colombier {
21431a4050f5SDavid du Colombier return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
21441a4050f5SDavid du Colombier }
2145*ec59a3ddSDavid du Colombier */
21461a4050f5SDavid du Colombier
21471a4050f5SDavid du Colombier static int
get16(uchar * p)21481a4050f5SDavid du Colombier get16(uchar *p)
21491a4050f5SDavid du Colombier {
21501a4050f5SDavid du Colombier return (p[0]<<8)|p[1];
21511a4050f5SDavid du Colombier }
21521a4050f5SDavid du Colombier
21531a4050f5SDavid du Colombier static char *charmap = "0123456789abcdef";
21541a4050f5SDavid du Colombier
21551a4050f5SDavid du Colombier static void
pdump(int len,void * a,char * tag)21561a4050f5SDavid du Colombier pdump(int len, void *a, char *tag)
21571a4050f5SDavid du Colombier {
21581a4050f5SDavid du Colombier uchar *p;
21591a4050f5SDavid du Colombier int i;
21601a4050f5SDavid du Colombier char buf[65+32];
21611a4050f5SDavid du Colombier char *q;
21621a4050f5SDavid du Colombier
21631a4050f5SDavid du Colombier p = a;
21641a4050f5SDavid du Colombier strcpy(buf, tag);
21651a4050f5SDavid du Colombier while(len > 0){
21661a4050f5SDavid du Colombier q = buf + strlen(tag);
21671a4050f5SDavid du Colombier for(i = 0; len > 0 && i < 32; i++){
21681a4050f5SDavid du Colombier if(*p >= ' ' && *p < 0x7f){
21691a4050f5SDavid du Colombier *q++ = ' ';
21701a4050f5SDavid du Colombier *q++ = *p;
21711a4050f5SDavid du Colombier } else {
21721a4050f5SDavid du Colombier *q++ = charmap[*p>>4];
21731a4050f5SDavid du Colombier *q++ = charmap[*p & 0xf];
21741a4050f5SDavid du Colombier }
21751a4050f5SDavid du Colombier len--;
21761a4050f5SDavid du Colombier p++;
21771a4050f5SDavid du Colombier }
21781a4050f5SDavid du Colombier *q = 0;
21791a4050f5SDavid du Colombier
21801a4050f5SDavid du Colombier if(len > 0)
21811a4050f5SDavid du Colombier pprint("%s...\n", buf);
21821a4050f5SDavid du Colombier else
21831a4050f5SDavid du Colombier pprint("%s\n", buf);
21841a4050f5SDavid du Colombier }
21851a4050f5SDavid du Colombier }
2186