19a747e4fSDavid du Colombier /*
22a47d117SDavid du Colombier * devtls - record layer for transport layer security 1.0, 1.1, 1.2 and secure sockets layer 3.0
39a747e4fSDavid du Colombier */
49a747e4fSDavid du Colombier #include "u.h"
59a747e4fSDavid du Colombier #include "../port/lib.h"
69a747e4fSDavid du Colombier #include "mem.h"
79a747e4fSDavid du Colombier #include "dat.h"
89a747e4fSDavid du Colombier #include "fns.h"
99a747e4fSDavid du Colombier #include "../port/error.h"
109a747e4fSDavid du Colombier
119a747e4fSDavid du Colombier #include <libsec.h>
129a747e4fSDavid du Colombier
139a747e4fSDavid du Colombier typedef struct OneWay OneWay;
149a747e4fSDavid du Colombier typedef struct Secret Secret;
159a747e4fSDavid du Colombier typedef struct TlsRec TlsRec;
169a747e4fSDavid du Colombier typedef struct TlsErrs TlsErrs;
179a747e4fSDavid du Colombier
189a747e4fSDavid du Colombier enum {
199a747e4fSDavid du Colombier Statlen= 1024, /* max. length of status or stats message */
209a747e4fSDavid du Colombier /* buffer limits */
219a747e4fSDavid du Colombier MaxRecLen = 1<<14, /* max payload length of a record layer message */
229a747e4fSDavid du Colombier MaxCipherRecLen = MaxRecLen + 2048,
239a747e4fSDavid du Colombier RecHdrLen = 5,
242a47d117SDavid du Colombier MaxMacLen = SHA2_256dlen,
259a747e4fSDavid du Colombier
269a747e4fSDavid du Colombier /* protocol versions we can accept */
279a747e4fSDavid du Colombier SSL3Version = 0x0300,
282a47d117SDavid du Colombier TLS10Version = 0x0301,
292a47d117SDavid du Colombier TLS11Version = 0x0302,
302a47d117SDavid du Colombier TLS12Version = 0x0303,
319a747e4fSDavid du Colombier MinProtoVersion = 0x0300, /* limits on version we accept */
329a747e4fSDavid du Colombier MaxProtoVersion = 0x03ff,
339a747e4fSDavid du Colombier
349a747e4fSDavid du Colombier /* connection states */
359a747e4fSDavid du Colombier SHandshake = 1 << 0, /* doing handshake */
369a747e4fSDavid du Colombier SOpen = 1 << 1, /* application data can be sent */
379a747e4fSDavid du Colombier SRClose = 1 << 2, /* remote side has closed down */
389a747e4fSDavid du Colombier SLClose = 1 << 3, /* sent a close notify alert */
399a747e4fSDavid du Colombier SAlert = 1 << 5, /* sending or sent a fatal alert */
409a747e4fSDavid du Colombier SError = 1 << 6, /* some sort of error has occured */
419a747e4fSDavid du Colombier SClosed = 1 << 7, /* it is all over */
429a747e4fSDavid du Colombier
439a747e4fSDavid du Colombier /* record types */
449a747e4fSDavid du Colombier RChangeCipherSpec = 20,
459a747e4fSDavid du Colombier RAlert,
469a747e4fSDavid du Colombier RHandshake,
479a747e4fSDavid du Colombier RApplication,
489a747e4fSDavid du Colombier
499a747e4fSDavid du Colombier SSL2ClientHello = 1,
509a747e4fSDavid du Colombier HSSL2ClientHello = 9, /* local convention; see tlshand.c */
519a747e4fSDavid du Colombier
529a747e4fSDavid du Colombier /* alerts */
539a747e4fSDavid du Colombier ECloseNotify = 0,
549a747e4fSDavid du Colombier EUnexpectedMessage = 10,
559a747e4fSDavid du Colombier EBadRecordMac = 20,
569a747e4fSDavid du Colombier EDecryptionFailed = 21,
579a747e4fSDavid du Colombier ERecordOverflow = 22,
589a747e4fSDavid du Colombier EDecompressionFailure = 30,
599a747e4fSDavid du Colombier EHandshakeFailure = 40,
609a747e4fSDavid du Colombier ENoCertificate = 41,
619a747e4fSDavid du Colombier EBadCertificate = 42,
629a747e4fSDavid du Colombier EUnsupportedCertificate = 43,
639a747e4fSDavid du Colombier ECertificateRevoked = 44,
649a747e4fSDavid du Colombier ECertificateExpired = 45,
659a747e4fSDavid du Colombier ECertificateUnknown = 46,
669a747e4fSDavid du Colombier EIllegalParameter = 47,
679a747e4fSDavid du Colombier EUnknownCa = 48,
689a747e4fSDavid du Colombier EAccessDenied = 49,
699a747e4fSDavid du Colombier EDecodeError = 50,
709a747e4fSDavid du Colombier EDecryptError = 51,
719a747e4fSDavid du Colombier EExportRestriction = 60,
729a747e4fSDavid du Colombier EProtocolVersion = 70,
739a747e4fSDavid du Colombier EInsufficientSecurity = 71,
749a747e4fSDavid du Colombier EInternalError = 80,
759a747e4fSDavid du Colombier EUserCanceled = 90,
769a747e4fSDavid du Colombier ENoRenegotiation = 100,
772a47d117SDavid du Colombier EUnrecognizedName = 112,
789a747e4fSDavid du Colombier
799a747e4fSDavid du Colombier EMAX = 256
809a747e4fSDavid du Colombier };
819a747e4fSDavid du Colombier
829a747e4fSDavid du Colombier struct Secret
839a747e4fSDavid du Colombier {
849a747e4fSDavid du Colombier char *encalg; /* name of encryption alg */
859a747e4fSDavid du Colombier char *hashalg; /* name of hash alg */
869a747e4fSDavid du Colombier int (*enc)(Secret*, uchar*, int);
879a747e4fSDavid du Colombier int (*dec)(Secret*, uchar*, int);
889a747e4fSDavid du Colombier int (*unpad)(uchar*, int, int);
899a747e4fSDavid du Colombier DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
909a747e4fSDavid du Colombier int block; /* encryption block len, 0 if none */
919a747e4fSDavid du Colombier int maclen;
929a747e4fSDavid du Colombier void *enckey;
939a747e4fSDavid du Colombier uchar mackey[MaxMacLen];
949a747e4fSDavid du Colombier };
959a747e4fSDavid du Colombier
969a747e4fSDavid du Colombier struct OneWay
979a747e4fSDavid du Colombier {
989a747e4fSDavid du Colombier QLock io; /* locks io access */
999a747e4fSDavid du Colombier QLock seclock; /* locks secret paramaters */
1009a747e4fSDavid du Colombier ulong seq;
1019a747e4fSDavid du Colombier Secret *sec; /* cipher in use */
1029a747e4fSDavid du Colombier Secret *new; /* cipher waiting for enable */
1039a747e4fSDavid du Colombier };
1049a747e4fSDavid du Colombier
1059a747e4fSDavid du Colombier struct TlsRec
1069a747e4fSDavid du Colombier {
1079a747e4fSDavid du Colombier Chan *c; /* io channel */
1089a747e4fSDavid du Colombier int ref; /* serialized by tdlock for atomic destroy */
1099a747e4fSDavid du Colombier int version; /* version of the protocol we are speaking */
1109a747e4fSDavid du Colombier char verset; /* version has been set */
1119a747e4fSDavid du Colombier char opened; /* opened command every issued? */
1129a747e4fSDavid du Colombier char err[ERRMAX]; /* error message to return to handshake requests */
1139a747e4fSDavid du Colombier vlong handin; /* bytes communicated by the record layer */
1149a747e4fSDavid du Colombier vlong handout;
1159a747e4fSDavid du Colombier vlong datain;
1169a747e4fSDavid du Colombier vlong dataout;
1179a747e4fSDavid du Colombier
1189a747e4fSDavid du Colombier Lock statelk;
1199a747e4fSDavid du Colombier int state;
1203751babcSDavid du Colombier int debug;
1219a747e4fSDavid du Colombier
1229a747e4fSDavid du Colombier /* record layer mac functions for different protocol versions */
1239a747e4fSDavid du Colombier void (*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);
1249a747e4fSDavid du Colombier
1259a747e4fSDavid du Colombier /* input side -- protected by in.io */
1269a747e4fSDavid du Colombier OneWay in;
1279a747e4fSDavid du Colombier Block *processed; /* next bunch of application data */
1289a747e4fSDavid du Colombier Block *unprocessed; /* data read from c but not parsed into records */
1299a747e4fSDavid du Colombier
1309a747e4fSDavid du Colombier /* handshake queue */
1319a747e4fSDavid du Colombier Lock hqlock; /* protects hqref, alloc & free of handq, hprocessed */
1329a747e4fSDavid du Colombier int hqref;
1339a747e4fSDavid du Colombier Queue *handq; /* queue of handshake messages */
1349a747e4fSDavid du Colombier Block *hprocessed; /* remainder of last block read from handq */
1359a747e4fSDavid du Colombier QLock hqread; /* protects reads for hprocessed, handq */
1369a747e4fSDavid du Colombier
1379a747e4fSDavid du Colombier /* output side */
1389a747e4fSDavid du Colombier OneWay out;
1399a747e4fSDavid du Colombier
1409a747e4fSDavid du Colombier /* protections */
1419a747e4fSDavid du Colombier char *user;
1429a747e4fSDavid du Colombier int perm;
1439a747e4fSDavid du Colombier };
1449a747e4fSDavid du Colombier
1459a747e4fSDavid du Colombier struct TlsErrs{
1469a747e4fSDavid du Colombier int err;
1479a747e4fSDavid du Colombier int sslerr;
1489a747e4fSDavid du Colombier int tlserr;
1499a747e4fSDavid du Colombier int fatal;
1509a747e4fSDavid du Colombier char *msg;
1519a747e4fSDavid du Colombier };
1529a747e4fSDavid du Colombier
1539a747e4fSDavid du Colombier static TlsErrs tlserrs[] = {
1549a747e4fSDavid du Colombier {ECloseNotify, ECloseNotify, ECloseNotify, 0, "close notify"},
1559a747e4fSDavid du Colombier {EUnexpectedMessage, EUnexpectedMessage, EUnexpectedMessage, 1, "unexpected message"},
1569a747e4fSDavid du Colombier {EBadRecordMac, EBadRecordMac, EBadRecordMac, 1, "bad record mac"},
1579a747e4fSDavid du Colombier {EDecryptionFailed, EIllegalParameter, EDecryptionFailed, 1, "decryption failed"},
1589a747e4fSDavid du Colombier {ERecordOverflow, EIllegalParameter, ERecordOverflow, 1, "record too long"},
1599a747e4fSDavid du Colombier {EDecompressionFailure, EDecompressionFailure, EDecompressionFailure, 1, "decompression failed"},
160f0ed0fb6SDavid du Colombier {EHandshakeFailure, EHandshakeFailure, EHandshakeFailure, 1, "could not negotiate acceptable security parameters"},
1619a747e4fSDavid du Colombier {ENoCertificate, ENoCertificate, ECertificateUnknown, 1, "no appropriate certificate available"},
1629a747e4fSDavid du Colombier {EBadCertificate, EBadCertificate, EBadCertificate, 1, "corrupted or invalid certificate"},
1639a747e4fSDavid du Colombier {EUnsupportedCertificate, EUnsupportedCertificate, EUnsupportedCertificate, 1, "unsupported certificate type"},
1649a747e4fSDavid du Colombier {ECertificateRevoked, ECertificateRevoked, ECertificateRevoked, 1, "revoked certificate"},
1659a747e4fSDavid du Colombier {ECertificateExpired, ECertificateExpired, ECertificateExpired, 1, "expired certificate"},
1669a747e4fSDavid du Colombier {ECertificateUnknown, ECertificateUnknown, ECertificateUnknown, 1, "unacceptable certificate"},
1679a747e4fSDavid du Colombier {EIllegalParameter, EIllegalParameter, EIllegalParameter, 1, "illegal parameter"},
1689a747e4fSDavid du Colombier {EUnknownCa, EHandshakeFailure, EUnknownCa, 1, "unknown certificate authority"},
1699a747e4fSDavid du Colombier {EAccessDenied, EHandshakeFailure, EAccessDenied, 1, "access denied"},
1709a747e4fSDavid du Colombier {EDecodeError, EIllegalParameter, EDecodeError, 1, "error decoding message"},
1719a747e4fSDavid du Colombier {EDecryptError, EIllegalParameter, EDecryptError, 1, "error decrypting message"},
1729a747e4fSDavid du Colombier {EExportRestriction, EHandshakeFailure, EExportRestriction, 1, "export restriction violated"},
1739a747e4fSDavid du Colombier {EProtocolVersion, EIllegalParameter, EProtocolVersion, 1, "protocol version not supported"},
1749a747e4fSDavid du Colombier {EInsufficientSecurity, EHandshakeFailure, EInsufficientSecurity, 1, "stronger security routines required"},
1759a747e4fSDavid du Colombier {EInternalError, EHandshakeFailure, EInternalError, 1, "internal error"},
1769a747e4fSDavid du Colombier {EUserCanceled, ECloseNotify, EUserCanceled, 0, "handshake canceled by user"},
1779a747e4fSDavid du Colombier {ENoRenegotiation, EUnexpectedMessage, ENoRenegotiation, 0, "no renegotiation"},
1789a747e4fSDavid du Colombier };
1799a747e4fSDavid du Colombier
1809a747e4fSDavid du Colombier enum
1819a747e4fSDavid du Colombier {
1829a747e4fSDavid du Colombier /* max. open tls connections */
183f20f6594SDavid du Colombier MaxTlsDevs = 16*1024
1849a747e4fSDavid du Colombier };
1859a747e4fSDavid du Colombier
1869a747e4fSDavid du Colombier static Lock tdlock;
1879a747e4fSDavid du Colombier static int tdhiwat;
1889a747e4fSDavid du Colombier static int maxtlsdevs = 128;
1899a747e4fSDavid du Colombier static TlsRec **tlsdevs;
1909a747e4fSDavid du Colombier static char **trnames;
1919a747e4fSDavid du Colombier static char *encalgs;
1929a747e4fSDavid du Colombier static char *hashalgs;
1939a747e4fSDavid du Colombier
1949a747e4fSDavid du Colombier enum{
1959a747e4fSDavid du Colombier Qtopdir = 1, /* top level directory */
1969a747e4fSDavid du Colombier Qprotodir,
1979a747e4fSDavid du Colombier Qclonus,
1989a747e4fSDavid du Colombier Qencalgs,
1999a747e4fSDavid du Colombier Qhashalgs,
2009a747e4fSDavid du Colombier Qconvdir, /* directory for a conversation */
2019a747e4fSDavid du Colombier Qdata,
2029a747e4fSDavid du Colombier Qctl,
2039a747e4fSDavid du Colombier Qhand,
2049a747e4fSDavid du Colombier Qstatus,
2059a747e4fSDavid du Colombier Qstats,
2069a747e4fSDavid du Colombier };
2079a747e4fSDavid du Colombier
2089a747e4fSDavid du Colombier #define TYPE(x) ((x).path & 0xf)
2099a747e4fSDavid du Colombier #define CONV(x) (((x).path >> 5)&(MaxTlsDevs-1))
2109a747e4fSDavid du Colombier #define QID(c, y) (((c)<<5) | (y))
2119a747e4fSDavid du Colombier
2129a747e4fSDavid du Colombier static void checkstate(TlsRec *, int, int);
2139a747e4fSDavid du Colombier static void ensure(TlsRec*, Block**, int);
2149a747e4fSDavid du Colombier static void consume(Block**, uchar*, int);
2159a747e4fSDavid du Colombier static Chan* buftochan(char*);
2169a747e4fSDavid du Colombier static void tlshangup(TlsRec*);
2179a747e4fSDavid du Colombier static void tlsError(TlsRec*, char *);
2189a747e4fSDavid du Colombier static void alertHand(TlsRec*, char *);
2199a747e4fSDavid du Colombier static TlsRec *newtls(Chan *c);
2209a747e4fSDavid du Colombier static TlsRec *mktlsrec(void);
2219a747e4fSDavid du Colombier static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2229a747e4fSDavid du Colombier static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2239a747e4fSDavid du Colombier static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2249a747e4fSDavid du Colombier static void sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
2259a747e4fSDavid du Colombier static void tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
2269a747e4fSDavid du Colombier static void put64(uchar *p, vlong x);
2279a747e4fSDavid du Colombier static void put32(uchar *p, u32int);
2289a747e4fSDavid du Colombier static void put24(uchar *p, int);
2299a747e4fSDavid du Colombier static void put16(uchar *p, int);
2309a747e4fSDavid du Colombier static u32int get32(uchar *p);
2319a747e4fSDavid du Colombier static int get16(uchar *p);
2329a747e4fSDavid du Colombier static void tlsSetState(TlsRec *tr, int new, int old);
2339a747e4fSDavid du Colombier static void rcvAlert(TlsRec *tr, int err);
2349a747e4fSDavid du Colombier static void sendAlert(TlsRec *tr, int err);
2359a747e4fSDavid du Colombier static void rcvError(TlsRec *tr, int err, char *msg, ...);
2369a747e4fSDavid du Colombier static int rc4enc(Secret *sec, uchar *buf, int n);
2379a747e4fSDavid du Colombier static int des3enc(Secret *sec, uchar *buf, int n);
2389a747e4fSDavid du Colombier static int des3dec(Secret *sec, uchar *buf, int n);
239ad6ca847SDavid du Colombier static int aesenc(Secret *sec, uchar *buf, int n);
240ad6ca847SDavid du Colombier static int aesdec(Secret *sec, uchar *buf, int n);
2419a747e4fSDavid du Colombier static int noenc(Secret *sec, uchar *buf, int n);
2429a747e4fSDavid du Colombier static int sslunpad(uchar *buf, int n, int block);
2439a747e4fSDavid du Colombier static int tlsunpad(uchar *buf, int n, int block);
2449a747e4fSDavid du Colombier static void freeSec(Secret *sec);
2459a747e4fSDavid du Colombier static char *tlsstate(int s);
2463751babcSDavid du Colombier static void pdump(int, void*, char*);
2479a747e4fSDavid du Colombier
2489a747e4fSDavid du Colombier #pragma varargck argpos rcvError 3
2499a747e4fSDavid du Colombier
2509a747e4fSDavid du Colombier static char *tlsnames[] = {
2519a747e4fSDavid du Colombier [Qclonus] "clone",
2529a747e4fSDavid du Colombier [Qencalgs] "encalgs",
2539a747e4fSDavid du Colombier [Qhashalgs] "hashalgs",
2549a747e4fSDavid du Colombier [Qdata] "data",
2559a747e4fSDavid du Colombier [Qctl] "ctl",
2569a747e4fSDavid du Colombier [Qhand] "hand",
2579a747e4fSDavid du Colombier [Qstatus] "status",
2589a747e4fSDavid du Colombier [Qstats] "stats",
2599a747e4fSDavid du Colombier };
2609a747e4fSDavid du Colombier
2619a747e4fSDavid du Colombier static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats };
2629a747e4fSDavid du Colombier
2639a747e4fSDavid du Colombier static int
tlsgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)2649a747e4fSDavid du Colombier tlsgen(Chan *c, char*, Dirtab *, int, int s, Dir *dp)
2659a747e4fSDavid du Colombier {
2669a747e4fSDavid du Colombier Qid q;
2679a747e4fSDavid du Colombier TlsRec *tr;
2689a747e4fSDavid du Colombier char *name, *nm;
2699a747e4fSDavid du Colombier int perm, t;
2709a747e4fSDavid du Colombier
2719a747e4fSDavid du Colombier q.vers = 0;
2729a747e4fSDavid du Colombier q.type = QTFILE;
2739a747e4fSDavid du Colombier
2749a747e4fSDavid du Colombier t = TYPE(c->qid);
2759a747e4fSDavid du Colombier switch(t) {
2769a747e4fSDavid du Colombier case Qtopdir:
2779a747e4fSDavid du Colombier if(s == DEVDOTDOT){
2789a747e4fSDavid du Colombier q.path = QID(0, Qtopdir);
2799a747e4fSDavid du Colombier q.type = QTDIR;
2809a747e4fSDavid du Colombier devdir(c, q, "#a", 0, eve, 0555, dp);
2819a747e4fSDavid du Colombier return 1;
2829a747e4fSDavid du Colombier }
2839a747e4fSDavid du Colombier if(s > 0)
2849a747e4fSDavid du Colombier return -1;
2859a747e4fSDavid du Colombier q.path = QID(0, Qprotodir);
2869a747e4fSDavid du Colombier q.type = QTDIR;
2879a747e4fSDavid du Colombier devdir(c, q, "tls", 0, eve, 0555, dp);
2889a747e4fSDavid du Colombier return 1;
2899a747e4fSDavid du Colombier case Qprotodir:
2909a747e4fSDavid du Colombier if(s == DEVDOTDOT){
2919a747e4fSDavid du Colombier q.path = QID(0, Qtopdir);
2929a747e4fSDavid du Colombier q.type = QTDIR;
2939a747e4fSDavid du Colombier devdir(c, q, ".", 0, eve, 0555, dp);
2949a747e4fSDavid du Colombier return 1;
2959a747e4fSDavid du Colombier }
2969a747e4fSDavid du Colombier if(s < 3){
2979a747e4fSDavid du Colombier switch(s) {
2989a747e4fSDavid du Colombier default:
2999a747e4fSDavid du Colombier return -1;
3009a747e4fSDavid du Colombier case 0:
3019a747e4fSDavid du Colombier q.path = QID(0, Qclonus);
3029a747e4fSDavid du Colombier break;
3039a747e4fSDavid du Colombier case 1:
3049a747e4fSDavid du Colombier q.path = QID(0, Qencalgs);
3059a747e4fSDavid du Colombier break;
3069a747e4fSDavid du Colombier case 2:
3079a747e4fSDavid du Colombier q.path = QID(0, Qhashalgs);
3089a747e4fSDavid du Colombier break;
3099a747e4fSDavid du Colombier }
3109a747e4fSDavid du Colombier perm = 0444;
3119a747e4fSDavid du Colombier if(TYPE(q) == Qclonus)
3129a747e4fSDavid du Colombier perm = 0555;
3139a747e4fSDavid du Colombier devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp);
3149a747e4fSDavid du Colombier return 1;
3159a747e4fSDavid du Colombier }
3169a747e4fSDavid du Colombier s -= 3;
3179a747e4fSDavid du Colombier if(s >= tdhiwat)
3189a747e4fSDavid du Colombier return -1;
3199a747e4fSDavid du Colombier q.path = QID(s, Qconvdir);
3209a747e4fSDavid du Colombier q.type = QTDIR;
3219a747e4fSDavid du Colombier lock(&tdlock);
3229a747e4fSDavid du Colombier tr = tlsdevs[s];
3239a747e4fSDavid du Colombier if(tr != nil)
3249a747e4fSDavid du Colombier nm = tr->user;
3259a747e4fSDavid du Colombier else
3269a747e4fSDavid du Colombier nm = eve;
3279a747e4fSDavid du Colombier if((name = trnames[s]) == nil){
3289a747e4fSDavid du Colombier name = trnames[s] = smalloc(16);
3294e3613abSDavid du Colombier snprint(name, 16, "%d", s);
3309a747e4fSDavid du Colombier }
3319a747e4fSDavid du Colombier devdir(c, q, name, 0, nm, 0555, dp);
3329a747e4fSDavid du Colombier unlock(&tdlock);
3339a747e4fSDavid du Colombier return 1;
3349a747e4fSDavid du Colombier case Qconvdir:
3359a747e4fSDavid du Colombier if(s == DEVDOTDOT){
3369a747e4fSDavid du Colombier q.path = QID(0, Qprotodir);
3379a747e4fSDavid du Colombier q.type = QTDIR;
3389a747e4fSDavid du Colombier devdir(c, q, "tls", 0, eve, 0555, dp);
3399a747e4fSDavid du Colombier return 1;
3409a747e4fSDavid du Colombier }
3419a747e4fSDavid du Colombier if(s < 0 || s >= nelem(convdir))
3429a747e4fSDavid du Colombier return -1;
3439a747e4fSDavid du Colombier lock(&tdlock);
3449a747e4fSDavid du Colombier tr = tlsdevs[CONV(c->qid)];
3459a747e4fSDavid du Colombier if(tr != nil){
3469a747e4fSDavid du Colombier nm = tr->user;
3479a747e4fSDavid du Colombier perm = tr->perm;
3489a747e4fSDavid du Colombier }else{
3499a747e4fSDavid du Colombier perm = 0;
3509a747e4fSDavid du Colombier nm = eve;
3519a747e4fSDavid du Colombier }
3529a747e4fSDavid du Colombier t = convdir[s];
3539a747e4fSDavid du Colombier if(t == Qstatus || t == Qstats)
3549a747e4fSDavid du Colombier perm &= 0444;
3559a747e4fSDavid du Colombier q.path = QID(CONV(c->qid), t);
3569a747e4fSDavid du Colombier devdir(c, q, tlsnames[t], 0, nm, perm, dp);
3579a747e4fSDavid du Colombier unlock(&tdlock);
3589a747e4fSDavid du Colombier return 1;
3599a747e4fSDavid du Colombier case Qclonus:
3609a747e4fSDavid du Colombier case Qencalgs:
3619a747e4fSDavid du Colombier case Qhashalgs:
3629a747e4fSDavid du Colombier perm = 0444;
3639a747e4fSDavid du Colombier if(t == Qclonus)
3649a747e4fSDavid du Colombier perm = 0555;
3659a747e4fSDavid du Colombier devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp);
3669a747e4fSDavid du Colombier return 1;
3679a747e4fSDavid du Colombier default:
3689a747e4fSDavid du Colombier lock(&tdlock);
3699a747e4fSDavid du Colombier tr = tlsdevs[CONV(c->qid)];
3709a747e4fSDavid du Colombier if(tr != nil){
3719a747e4fSDavid du Colombier nm = tr->user;
3729a747e4fSDavid du Colombier perm = tr->perm;
3739a747e4fSDavid du Colombier }else{
3749a747e4fSDavid du Colombier perm = 0;
3759a747e4fSDavid du Colombier nm = eve;
3769a747e4fSDavid du Colombier }
3779a747e4fSDavid du Colombier if(t == Qstatus || t == Qstats)
3789a747e4fSDavid du Colombier perm &= 0444;
3799a747e4fSDavid du Colombier devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp);
3809a747e4fSDavid du Colombier unlock(&tdlock);
3819a747e4fSDavid du Colombier return 1;
3829a747e4fSDavid du Colombier }
3839a747e4fSDavid du Colombier }
3849a747e4fSDavid du Colombier
3859a747e4fSDavid du Colombier static Chan*
tlsattach(char * spec)3869a747e4fSDavid du Colombier tlsattach(char *spec)
3879a747e4fSDavid du Colombier {
3889a747e4fSDavid du Colombier Chan *c;
3899a747e4fSDavid du Colombier
3909a747e4fSDavid du Colombier c = devattach('a', spec);
3919a747e4fSDavid du Colombier c->qid.path = QID(0, Qtopdir);
3929a747e4fSDavid du Colombier c->qid.type = QTDIR;
3939a747e4fSDavid du Colombier c->qid.vers = 0;
3949a747e4fSDavid du Colombier return c;
3959a747e4fSDavid du Colombier }
3969a747e4fSDavid du Colombier
3979a747e4fSDavid du Colombier static Walkqid*
tlswalk(Chan * c,Chan * nc,char ** name,int nname)3989a747e4fSDavid du Colombier tlswalk(Chan *c, Chan *nc, char **name, int nname)
3999a747e4fSDavid du Colombier {
4009a747e4fSDavid du Colombier return devwalk(c, nc, name, nname, nil, 0, tlsgen);
4019a747e4fSDavid du Colombier }
4029a747e4fSDavid du Colombier
4039a747e4fSDavid du Colombier static int
tlsstat(Chan * c,uchar * db,int n)4049a747e4fSDavid du Colombier tlsstat(Chan *c, uchar *db, int n)
4059a747e4fSDavid du Colombier {
4069a747e4fSDavid du Colombier return devstat(c, db, n, nil, 0, tlsgen);
4079a747e4fSDavid du Colombier }
4089a747e4fSDavid du Colombier
4099a747e4fSDavid du Colombier static Chan*
tlsopen(Chan * c,int omode)4109a747e4fSDavid du Colombier tlsopen(Chan *c, int omode)
4119a747e4fSDavid du Colombier {
4129a747e4fSDavid du Colombier TlsRec *tr, **pp;
4139a747e4fSDavid du Colombier int t, perm;
4149a747e4fSDavid du Colombier
4159a747e4fSDavid du Colombier perm = 0;
4169a747e4fSDavid du Colombier omode &= 3;
4179a747e4fSDavid du Colombier switch(omode) {
4189a747e4fSDavid du Colombier case OREAD:
4199a747e4fSDavid du Colombier perm = 4;
4209a747e4fSDavid du Colombier break;
4219a747e4fSDavid du Colombier case OWRITE:
4229a747e4fSDavid du Colombier perm = 2;
4239a747e4fSDavid du Colombier break;
4249a747e4fSDavid du Colombier case ORDWR:
4259a747e4fSDavid du Colombier perm = 6;
4269a747e4fSDavid du Colombier break;
4279a747e4fSDavid du Colombier }
4289a747e4fSDavid du Colombier
4299a747e4fSDavid du Colombier t = TYPE(c->qid);
4309a747e4fSDavid du Colombier switch(t) {
4319a747e4fSDavid du Colombier default:
4329a747e4fSDavid du Colombier panic("tlsopen");
4339a747e4fSDavid du Colombier case Qtopdir:
4349a747e4fSDavid du Colombier case Qprotodir:
4359a747e4fSDavid du Colombier case Qconvdir:
4369a747e4fSDavid du Colombier if(omode != OREAD)
4379a747e4fSDavid du Colombier error(Eperm);
4389a747e4fSDavid du Colombier break;
4399a747e4fSDavid du Colombier case Qclonus:
4409a747e4fSDavid du Colombier tr = newtls(c);
4419a747e4fSDavid du Colombier if(tr == nil)
4429a747e4fSDavid du Colombier error(Enodev);
4439a747e4fSDavid du Colombier break;
4449a747e4fSDavid du Colombier case Qctl:
4459a747e4fSDavid du Colombier case Qdata:
4469a747e4fSDavid du Colombier case Qhand:
4479a747e4fSDavid du Colombier case Qstatus:
4489a747e4fSDavid du Colombier case Qstats:
4499a747e4fSDavid du Colombier if((t == Qstatus || t == Qstats) && omode != OREAD)
4509a747e4fSDavid du Colombier error(Eperm);
4519a747e4fSDavid du Colombier if(waserror()) {
4529a747e4fSDavid du Colombier unlock(&tdlock);
4539a747e4fSDavid du Colombier nexterror();
4549a747e4fSDavid du Colombier }
4559a747e4fSDavid du Colombier lock(&tdlock);
4569a747e4fSDavid du Colombier pp = &tlsdevs[CONV(c->qid)];
4579a747e4fSDavid du Colombier tr = *pp;
4589a747e4fSDavid du Colombier if(tr == nil)
4599a747e4fSDavid du Colombier error("must open connection using clone");
4609a747e4fSDavid du Colombier if((perm & (tr->perm>>6)) != perm
4619a747e4fSDavid du Colombier && (strcmp(up->user, tr->user) != 0
4629a747e4fSDavid du Colombier || (perm & tr->perm) != perm))
4639a747e4fSDavid du Colombier error(Eperm);
4649a747e4fSDavid du Colombier if(t == Qhand){
4659a747e4fSDavid du Colombier if(waserror()){
4669a747e4fSDavid du Colombier unlock(&tr->hqlock);
4679a747e4fSDavid du Colombier nexterror();
4689a747e4fSDavid du Colombier }
4699a747e4fSDavid du Colombier lock(&tr->hqlock);
4709a747e4fSDavid du Colombier if(tr->handq != nil)
4719a747e4fSDavid du Colombier error(Einuse);
4729a747e4fSDavid du Colombier tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil);
4739a747e4fSDavid du Colombier if(tr->handq == nil)
4748cd4f5a6SDavid du Colombier error("cannot allocate handshake queue");
4759a747e4fSDavid du Colombier tr->hqref = 1;
4769a747e4fSDavid du Colombier unlock(&tr->hqlock);
4779a747e4fSDavid du Colombier poperror();
4789a747e4fSDavid du Colombier }
4799a747e4fSDavid du Colombier tr->ref++;
4809a747e4fSDavid du Colombier unlock(&tdlock);
4819a747e4fSDavid du Colombier poperror();
4829a747e4fSDavid du Colombier break;
4839a747e4fSDavid du Colombier case Qencalgs:
4849a747e4fSDavid du Colombier case Qhashalgs:
4859a747e4fSDavid du Colombier if(omode != OREAD)
4869a747e4fSDavid du Colombier error(Eperm);
4879a747e4fSDavid du Colombier break;
4889a747e4fSDavid du Colombier }
4899a747e4fSDavid du Colombier c->mode = openmode(omode);
4909a747e4fSDavid du Colombier c->flag |= COPEN;
4919a747e4fSDavid du Colombier c->offset = 0;
4929a747e4fSDavid du Colombier c->iounit = qiomaxatomic;
4939a747e4fSDavid du Colombier return c;
4949a747e4fSDavid du Colombier }
4959a747e4fSDavid du Colombier
4969a747e4fSDavid du Colombier static int
tlswstat(Chan * c,uchar * dp,int n)4979a747e4fSDavid du Colombier tlswstat(Chan *c, uchar *dp, int n)
4989a747e4fSDavid du Colombier {
4999a747e4fSDavid du Colombier Dir *d;
5009a747e4fSDavid du Colombier TlsRec *tr;
5019a747e4fSDavid du Colombier int rv;
5029a747e4fSDavid du Colombier
5039a747e4fSDavid du Colombier d = nil;
5049a747e4fSDavid du Colombier if(waserror()){
5059a747e4fSDavid du Colombier free(d);
5069a747e4fSDavid du Colombier unlock(&tdlock);
5079a747e4fSDavid du Colombier nexterror();
5089a747e4fSDavid du Colombier }
5099a747e4fSDavid du Colombier
5109a747e4fSDavid du Colombier lock(&tdlock);
5119a747e4fSDavid du Colombier tr = tlsdevs[CONV(c->qid)];
5129a747e4fSDavid du Colombier if(tr == nil)
5139a747e4fSDavid du Colombier error(Ebadusefd);
5149a747e4fSDavid du Colombier if(strcmp(tr->user, up->user) != 0)
5159a747e4fSDavid du Colombier error(Eperm);
5169a747e4fSDavid du Colombier
5179a747e4fSDavid du Colombier d = smalloc(n + sizeof *d);
5189a747e4fSDavid du Colombier rv = convM2D(dp, n, &d[0], (char*) &d[1]);
5199a747e4fSDavid du Colombier if(rv == 0)
5209a747e4fSDavid du Colombier error(Eshortstat);
5219a747e4fSDavid du Colombier if(!emptystr(d->uid))
5229a747e4fSDavid du Colombier kstrdup(&tr->user, d->uid);
5239a747e4fSDavid du Colombier if(d->mode != ~0UL)
5249a747e4fSDavid du Colombier tr->perm = d->mode;
5259a747e4fSDavid du Colombier
5269a747e4fSDavid du Colombier free(d);
5279a747e4fSDavid du Colombier poperror();
5289a747e4fSDavid du Colombier unlock(&tdlock);
5299a747e4fSDavid du Colombier
5309a747e4fSDavid du Colombier return rv;
5319a747e4fSDavid du Colombier }
5329a747e4fSDavid du Colombier
5339a747e4fSDavid du Colombier static void
dechandq(TlsRec * tr)5349a747e4fSDavid du Colombier dechandq(TlsRec *tr)
5359a747e4fSDavid du Colombier {
5369a747e4fSDavid du Colombier lock(&tr->hqlock);
5379a747e4fSDavid du Colombier if(--tr->hqref == 0){
5389a747e4fSDavid du Colombier if(tr->handq != nil){
5399a747e4fSDavid du Colombier qfree(tr->handq);
5409a747e4fSDavid du Colombier tr->handq = nil;
5419a747e4fSDavid du Colombier }
5429a747e4fSDavid du Colombier if(tr->hprocessed != nil){
5439a747e4fSDavid du Colombier freeb(tr->hprocessed);
5449a747e4fSDavid du Colombier tr->hprocessed = nil;
5459a747e4fSDavid du Colombier }
5469a747e4fSDavid du Colombier }
5479a747e4fSDavid du Colombier unlock(&tr->hqlock);
5489a747e4fSDavid du Colombier }
5499a747e4fSDavid du Colombier
5509a747e4fSDavid du Colombier static void
tlsclose(Chan * c)5519a747e4fSDavid du Colombier tlsclose(Chan *c)
5529a747e4fSDavid du Colombier {
5539a747e4fSDavid du Colombier TlsRec *tr;
5549a747e4fSDavid du Colombier int t;
5559a747e4fSDavid du Colombier
5569a747e4fSDavid du Colombier t = TYPE(c->qid);
5579a747e4fSDavid du Colombier switch(t) {
5589a747e4fSDavid du Colombier case Qctl:
5599a747e4fSDavid du Colombier case Qdata:
5609a747e4fSDavid du Colombier case Qhand:
5619a747e4fSDavid du Colombier case Qstatus:
5629a747e4fSDavid du Colombier case Qstats:
5639a747e4fSDavid du Colombier if((c->flag & COPEN) == 0)
5649a747e4fSDavid du Colombier break;
5659a747e4fSDavid du Colombier
5669a747e4fSDavid du Colombier tr = tlsdevs[CONV(c->qid)];
5679a747e4fSDavid du Colombier if(tr == nil)
5689a747e4fSDavid du Colombier break;
5699a747e4fSDavid du Colombier
5709a747e4fSDavid du Colombier if(t == Qhand)
5719a747e4fSDavid du Colombier dechandq(tr);
5729a747e4fSDavid du Colombier
5739a747e4fSDavid du Colombier lock(&tdlock);
5749a747e4fSDavid du Colombier if(--tr->ref > 0) {
5759a747e4fSDavid du Colombier unlock(&tdlock);
5769a747e4fSDavid du Colombier return;
5779a747e4fSDavid du Colombier }
5789a747e4fSDavid du Colombier tlsdevs[CONV(c->qid)] = nil;
5799a747e4fSDavid du Colombier unlock(&tdlock);
5809a747e4fSDavid du Colombier
5819a747e4fSDavid du Colombier if(tr->c != nil && !waserror()){
5829a747e4fSDavid du Colombier checkstate(tr, 0, SOpen|SHandshake|SRClose);
5839a747e4fSDavid du Colombier sendAlert(tr, ECloseNotify);
5849a747e4fSDavid du Colombier poperror();
5859a747e4fSDavid du Colombier }
5869a747e4fSDavid du Colombier tlshangup(tr);
5879a747e4fSDavid du Colombier if(tr->c != nil)
5889a747e4fSDavid du Colombier cclose(tr->c);
5899a747e4fSDavid du Colombier freeSec(tr->in.sec);
5909a747e4fSDavid du Colombier freeSec(tr->in.new);
5919a747e4fSDavid du Colombier freeSec(tr->out.sec);
5929a747e4fSDavid du Colombier freeSec(tr->out.new);
5939a747e4fSDavid du Colombier free(tr->user);
5949a747e4fSDavid du Colombier free(tr);
5959a747e4fSDavid du Colombier break;
5969a747e4fSDavid du Colombier }
5979a747e4fSDavid du Colombier }
5989a747e4fSDavid du Colombier
5999a747e4fSDavid du Colombier /*
6009a747e4fSDavid du Colombier * make sure we have at least 'n' bytes in list 'l'
6019a747e4fSDavid du Colombier */
6029a747e4fSDavid du Colombier static void
ensure(TlsRec * s,Block ** l,int n)6039a747e4fSDavid du Colombier ensure(TlsRec *s, Block **l, int n)
6049a747e4fSDavid du Colombier {
6059a747e4fSDavid du Colombier int sofar, i;
6069a747e4fSDavid du Colombier Block *b, *bl;
6079a747e4fSDavid du Colombier
6089a747e4fSDavid du Colombier sofar = 0;
6099a747e4fSDavid du Colombier for(b = *l; b; b = b->next){
6109a747e4fSDavid du Colombier sofar += BLEN(b);
6119a747e4fSDavid du Colombier if(sofar >= n)
6129a747e4fSDavid du Colombier return;
6139a747e4fSDavid du Colombier l = &b->next;
6149a747e4fSDavid du Colombier }
6159a747e4fSDavid du Colombier
6169a747e4fSDavid du Colombier while(sofar < n){
6179a747e4fSDavid du Colombier bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0);
6189a747e4fSDavid du Colombier if(bl == 0)
6199a747e4fSDavid du Colombier error(Ehungup);
6209a747e4fSDavid du Colombier *l = bl;
6219a747e4fSDavid du Colombier i = 0;
6229a747e4fSDavid du Colombier for(b = bl; b; b = b->next){
6239a747e4fSDavid du Colombier i += BLEN(b);
6249a747e4fSDavid du Colombier l = &b->next;
6259a747e4fSDavid du Colombier }
6269a747e4fSDavid du Colombier if(i == 0)
6279a747e4fSDavid du Colombier error(Ehungup);
6289a747e4fSDavid du Colombier sofar += i;
6299a747e4fSDavid du Colombier }
6303751babcSDavid du Colombier if(s->debug) pprint("ensure read %d\n", sofar);
6319a747e4fSDavid du Colombier }
6329a747e4fSDavid du Colombier
6339a747e4fSDavid du Colombier /*
6349a747e4fSDavid du Colombier * copy 'n' bytes from 'l' into 'p' and free
6359a747e4fSDavid du Colombier * the bytes in 'l'
6369a747e4fSDavid du Colombier */
6379a747e4fSDavid du Colombier static void
consume(Block ** l,uchar * p,int n)6389a747e4fSDavid du Colombier consume(Block **l, uchar *p, int n)
6399a747e4fSDavid du Colombier {
6409a747e4fSDavid du Colombier Block *b;
6419a747e4fSDavid du Colombier int i;
6429a747e4fSDavid du Colombier
6439a747e4fSDavid du Colombier for(; *l && n > 0; n -= i){
6449a747e4fSDavid du Colombier b = *l;
6459a747e4fSDavid du Colombier i = BLEN(b);
6469a747e4fSDavid du Colombier if(i > n)
6479a747e4fSDavid du Colombier i = n;
6489a747e4fSDavid du Colombier memmove(p, b->rp, i);
6499a747e4fSDavid du Colombier b->rp += i;
6509a747e4fSDavid du Colombier p += i;
6519a747e4fSDavid du Colombier if(BLEN(b) < 0)
6529a747e4fSDavid du Colombier panic("consume");
6539a747e4fSDavid du Colombier if(BLEN(b))
6549a747e4fSDavid du Colombier break;
6559a747e4fSDavid du Colombier *l = b->next;
6569a747e4fSDavid du Colombier freeb(b);
6579a747e4fSDavid du Colombier }
6589a747e4fSDavid du Colombier }
6599a747e4fSDavid du Colombier
6609a747e4fSDavid du Colombier /*
6619a747e4fSDavid du Colombier * give back n bytes
6629a747e4fSDavid du Colombier */
6639a747e4fSDavid du Colombier static void
regurgitate(TlsRec * s,uchar * p,int n)6649a747e4fSDavid du Colombier regurgitate(TlsRec *s, uchar *p, int n)
6659a747e4fSDavid du Colombier {
6669a747e4fSDavid du Colombier Block *b;
6679a747e4fSDavid du Colombier
6689a747e4fSDavid du Colombier if(n <= 0)
6699a747e4fSDavid du Colombier return;
6709a747e4fSDavid du Colombier b = s->unprocessed;
6719a747e4fSDavid du Colombier if(s->unprocessed == nil || b->rp - b->base < n) {
6729a747e4fSDavid du Colombier b = allocb(n);
6739a747e4fSDavid du Colombier memmove(b->wp, p, n);
6749a747e4fSDavid du Colombier b->wp += n;
6759a747e4fSDavid du Colombier b->next = s->unprocessed;
6769a747e4fSDavid du Colombier s->unprocessed = b;
6779a747e4fSDavid du Colombier } else {
6789a747e4fSDavid du Colombier b->rp -= n;
6799a747e4fSDavid du Colombier memmove(b->rp, p, n);
6809a747e4fSDavid du Colombier }
6819a747e4fSDavid du Colombier }
6829a747e4fSDavid du Colombier
6839a747e4fSDavid du Colombier /*
6849a747e4fSDavid du Colombier * remove at most n bytes from the queue
6859a747e4fSDavid du Colombier */
6869a747e4fSDavid du Colombier static Block*
qgrab(Block ** l,int n)6879a747e4fSDavid du Colombier qgrab(Block **l, int n)
6889a747e4fSDavid du Colombier {
6899a747e4fSDavid du Colombier Block *bb, *b;
6909a747e4fSDavid du Colombier int i;
6919a747e4fSDavid du Colombier
6929a747e4fSDavid du Colombier b = *l;
6939a747e4fSDavid du Colombier if(BLEN(b) == n){
6949a747e4fSDavid du Colombier *l = b->next;
6959a747e4fSDavid du Colombier b->next = nil;
6969a747e4fSDavid du Colombier return b;
6979a747e4fSDavid du Colombier }
6989a747e4fSDavid du Colombier
6999a747e4fSDavid du Colombier i = 0;
7009a747e4fSDavid du Colombier for(bb = b; bb != nil && i < n; bb = bb->next)
7019a747e4fSDavid du Colombier i += BLEN(bb);
7029a747e4fSDavid du Colombier if(i > n)
7039a747e4fSDavid du Colombier i = n;
7049a747e4fSDavid du Colombier
7059a747e4fSDavid du Colombier bb = allocb(i);
7069a747e4fSDavid du Colombier consume(l, bb->wp, i);
7079a747e4fSDavid du Colombier bb->wp += i;
7089a747e4fSDavid du Colombier return bb;
7099a747e4fSDavid du Colombier }
7109a747e4fSDavid du Colombier
7119a747e4fSDavid du Colombier static void
tlsclosed(TlsRec * tr,int new)7129a747e4fSDavid du Colombier tlsclosed(TlsRec *tr, int new)
7139a747e4fSDavid du Colombier {
7149a747e4fSDavid du Colombier lock(&tr->statelk);
7159a747e4fSDavid du Colombier if(tr->state == SOpen || tr->state == SHandshake)
7169a747e4fSDavid du Colombier tr->state = new;
7179a747e4fSDavid du Colombier else if((new | tr->state) == (SRClose|SLClose))
7189a747e4fSDavid du Colombier tr->state = SClosed;
7199a747e4fSDavid du Colombier unlock(&tr->statelk);
7209a747e4fSDavid du Colombier alertHand(tr, "close notify");
7219a747e4fSDavid du Colombier }
7229a747e4fSDavid du Colombier
7239a747e4fSDavid du Colombier /*
7249a747e4fSDavid du Colombier * read and process one tls record layer message
7259a747e4fSDavid du Colombier * must be called with tr->in.io held
7269a747e4fSDavid du Colombier * We can't let Eintrs lose data, since doing so will get
7279a747e4fSDavid du Colombier * us out of sync with the sender and break the reliablity
7289a747e4fSDavid du Colombier * of the channel. Eintr only happens during the reads in
7299a747e4fSDavid du Colombier * consume. Therefore we put back any bytes consumed before
7309a747e4fSDavid du Colombier * the last call to ensure.
7319a747e4fSDavid du Colombier */
7329a747e4fSDavid du Colombier static void
tlsrecread(TlsRec * tr)7339a747e4fSDavid du Colombier tlsrecread(TlsRec *tr)
7349a747e4fSDavid du Colombier {
7359a747e4fSDavid du Colombier OneWay *volatile in;
7369a747e4fSDavid du Colombier Block *volatile b;
7379b7bf7dfSDavid du Colombier uchar *p, seq[8], header[RecHdrLen], hmac[MaxMacLen];
7389a747e4fSDavid du Colombier int volatile nconsumed;
739a6a9e072SDavid du Colombier int len, type, ver, unpad_len;
7409a747e4fSDavid du Colombier
7419a747e4fSDavid du Colombier nconsumed = 0;
7429a747e4fSDavid du Colombier if(waserror()){
7439a747e4fSDavid du Colombier if(strcmp(up->errstr, Eintr) == 0 && !waserror()){
7449a747e4fSDavid du Colombier regurgitate(tr, header, nconsumed);
7459a747e4fSDavid du Colombier poperror();
7469a747e4fSDavid du Colombier }else
7479a747e4fSDavid du Colombier tlsError(tr, "channel error");
7489a747e4fSDavid du Colombier nexterror();
7499a747e4fSDavid du Colombier }
7509a747e4fSDavid du Colombier ensure(tr, &tr->unprocessed, RecHdrLen);
7519a747e4fSDavid du Colombier consume(&tr->unprocessed, header, RecHdrLen);
7523751babcSDavid du Colombier if(tr->debug)pprint("consumed %d header\n", RecHdrLen);
7539a747e4fSDavid du Colombier nconsumed = RecHdrLen;
7549a747e4fSDavid du Colombier
7559a747e4fSDavid du Colombier if((tr->handin == 0) && (header[0] & 0x80)){
7569a747e4fSDavid du Colombier /* Cope with an SSL3 ClientHello expressed in SSL2 record format.
7579a747e4fSDavid du Colombier This is sent by some clients that we must interoperate
7589a747e4fSDavid du Colombier with, such as Java's JSSE and Microsoft's Internet Explorer. */
7599a747e4fSDavid du Colombier len = (get16(header) & ~0x8000) - 3;
7609a747e4fSDavid du Colombier type = header[2];
7619a747e4fSDavid du Colombier ver = get16(header + 3);
7629a747e4fSDavid du Colombier if(type != SSL2ClientHello || len < 22)
7639a747e4fSDavid du Colombier rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message");
7649a747e4fSDavid du Colombier }else{ /* normal SSL3 record format */
7659a747e4fSDavid du Colombier type = header[0];
7669a747e4fSDavid du Colombier ver = get16(header+1);
7679a747e4fSDavid du Colombier len = get16(header+3);
7689a747e4fSDavid du Colombier }
7699a747e4fSDavid du Colombier if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion))
7709a747e4fSDavid du Colombier rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'",
7719a747e4fSDavid du Colombier tr->version, tr->verset?"/set":"", len, type, ver, (char*)header);
772*e873706dSDavid du Colombier if(len > MaxCipherRecLen || len <= 0)
773*e873706dSDavid du Colombier rcvError(tr, ERecordOverflow, "bad record message length %d", len);
7749a747e4fSDavid du Colombier ensure(tr, &tr->unprocessed, len);
7759a747e4fSDavid du Colombier nconsumed = 0;
7769a747e4fSDavid du Colombier poperror();
7779a747e4fSDavid du Colombier
7789a747e4fSDavid du Colombier /*
7799a747e4fSDavid du Colombier * If an Eintr happens after this, we'll get out of sync.
7809a747e4fSDavid du Colombier * Make sure nothing we call can sleep.
7819a747e4fSDavid du Colombier * Errors are ok, as they kill the connection.
7829a747e4fSDavid du Colombier * Luckily, allocb won't sleep, it'll just error out.
7839a747e4fSDavid du Colombier */
7849a747e4fSDavid du Colombier b = nil;
7859a747e4fSDavid du Colombier if(waserror()){
7869a747e4fSDavid du Colombier if(b != nil)
7879a747e4fSDavid du Colombier freeb(b);
7889a747e4fSDavid du Colombier tlsError(tr, "channel error");
7899a747e4fSDavid du Colombier nexterror();
7909a747e4fSDavid du Colombier }
7919a747e4fSDavid du Colombier b = qgrab(&tr->unprocessed, len);
7923751babcSDavid du Colombier if(tr->debug) pprint("consumed unprocessed %d\n", len);
7939a747e4fSDavid du Colombier
7949a747e4fSDavid du Colombier in = &tr->in;
7959a747e4fSDavid du Colombier if(waserror()){
7969a747e4fSDavid du Colombier qunlock(&in->seclock);
7979a747e4fSDavid du Colombier nexterror();
7989a747e4fSDavid du Colombier }
7999a747e4fSDavid du Colombier qlock(&in->seclock);
8009a747e4fSDavid du Colombier p = b->rp;
8019a747e4fSDavid du Colombier if(in->sec != nil) {
802a6a9e072SDavid du Colombier /* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here
803a6a9e072SDavid du Colombier should look alike, including timing of the response. */
804a6a9e072SDavid du Colombier unpad_len = (*in->sec->dec)(in->sec, p, len);
8052a47d117SDavid du Colombier
8062a47d117SDavid du Colombier /* excplicit iv */
8072a47d117SDavid du Colombier if(tr->version >= TLS11Version){
8082a47d117SDavid du Colombier len -= in->sec->block;
8092a47d117SDavid du Colombier if(len < 0)
8102a47d117SDavid du Colombier rcvError(tr, EDecodeError, "runt record message");
8112a47d117SDavid du Colombier
8122a47d117SDavid du Colombier unpad_len -= in->sec->block;
8132a47d117SDavid du Colombier p += in->sec->block;
8142a47d117SDavid du Colombier }
8152a47d117SDavid du Colombier
8163751babcSDavid du Colombier if(unpad_len >= in->sec->maclen)
817a6a9e072SDavid du Colombier len = unpad_len - in->sec->maclen;
8183751babcSDavid du Colombier if(tr->debug) pprint("decrypted %d\n", unpad_len);
8193751babcSDavid du Colombier if(tr->debug) pdump(unpad_len, p, "decrypted:");
8209a747e4fSDavid du Colombier
8219a747e4fSDavid du Colombier /* update length */
8229a747e4fSDavid du Colombier put16(header+3, len);
8239a747e4fSDavid du Colombier put64(seq, in->seq);
8249a747e4fSDavid du Colombier in->seq++;
8259a747e4fSDavid du Colombier (*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
8263751babcSDavid du Colombier if(unpad_len < in->sec->maclen)
827dc5a79c1SDavid du Colombier rcvError(tr, EBadRecordMac, "short record mac");
828dc5a79c1SDavid du Colombier if(memcmp(hmac, p+len, in->sec->maclen) != 0)
8299a747e4fSDavid du Colombier rcvError(tr, EBadRecordMac, "record mac mismatch");
8302a47d117SDavid du Colombier b->rp = p;
8312a47d117SDavid du Colombier b->wp = p+len;
8329a747e4fSDavid du Colombier }
8339a747e4fSDavid du Colombier qunlock(&in->seclock);
8349a747e4fSDavid du Colombier poperror();
8353751babcSDavid du Colombier if(len < 0)
8369a747e4fSDavid du Colombier rcvError(tr, EDecodeError, "runt record message");
8379a747e4fSDavid du Colombier
8389a747e4fSDavid du Colombier switch(type) {
8399a747e4fSDavid du Colombier default:
840567483c8SDavid du Colombier rcvError(tr, EIllegalParameter, "invalid record message %#x", type);
8419a747e4fSDavid du Colombier break;
8429a747e4fSDavid du Colombier case RChangeCipherSpec:
8439a747e4fSDavid du Colombier if(len != 1 || p[0] != 1)
8449a747e4fSDavid du Colombier rcvError(tr, EDecodeError, "invalid change cipher spec");
8459a747e4fSDavid du Colombier qlock(&in->seclock);
8469a747e4fSDavid du Colombier if(in->new == nil){
8479a747e4fSDavid du Colombier qunlock(&in->seclock);
8489a747e4fSDavid du Colombier rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec");
8499a747e4fSDavid du Colombier }
8509a747e4fSDavid du Colombier freeSec(in->sec);
8519a747e4fSDavid du Colombier in->sec = in->new;
8529a747e4fSDavid du Colombier in->new = nil;
8539a747e4fSDavid du Colombier in->seq = 0;
8549a747e4fSDavid du Colombier qunlock(&in->seclock);
8559a747e4fSDavid du Colombier break;
8569a747e4fSDavid du Colombier case RAlert:
8579a747e4fSDavid du Colombier if(len != 2)
8589a747e4fSDavid du Colombier rcvError(tr, EDecodeError, "invalid alert");
8599a747e4fSDavid du Colombier if(p[0] == 2)
8609a747e4fSDavid du Colombier rcvAlert(tr, p[1]);
8619a747e4fSDavid du Colombier if(p[0] != 1)
8629a747e4fSDavid du Colombier rcvError(tr, EIllegalParameter, "invalid alert fatal code");
8639a747e4fSDavid du Colombier
8649a747e4fSDavid du Colombier /*
8659a747e4fSDavid du Colombier * propate non-fatal alerts to handshaker
8669a747e4fSDavid du Colombier */
8672a47d117SDavid du Colombier switch(p[1]){
8682a47d117SDavid du Colombier case ECloseNotify:
8699a747e4fSDavid du Colombier tlsclosed(tr, SRClose);
8709a747e4fSDavid du Colombier if(tr->opened)
8719a747e4fSDavid du Colombier error("tls hungup");
8729a747e4fSDavid du Colombier error("close notify");
8732a47d117SDavid du Colombier break;
8742a47d117SDavid du Colombier case ENoRenegotiation:
8759a747e4fSDavid du Colombier alertHand(tr, "no renegotiation");
8762a47d117SDavid du Colombier break;
8772a47d117SDavid du Colombier case EUserCanceled:
8789a747e4fSDavid du Colombier alertHand(tr, "handshake canceled by user");
8792a47d117SDavid du Colombier break;
8802a47d117SDavid du Colombier case EUnrecognizedName:
8812a47d117SDavid du Colombier /* happens in response to SNI, can be ignored. */
8822a47d117SDavid du Colombier break;
8832a47d117SDavid du Colombier default:
8849a747e4fSDavid du Colombier rcvError(tr, EIllegalParameter, "invalid alert code");
8852a47d117SDavid du Colombier }
8869a747e4fSDavid du Colombier break;
8879a747e4fSDavid du Colombier case RHandshake:
8889a747e4fSDavid du Colombier /*
8899a747e4fSDavid du Colombier * don't worry about dropping the block
8909a747e4fSDavid du Colombier * qbwrite always queues even if flow controlled and interrupted.
8919a747e4fSDavid du Colombier *
8929a747e4fSDavid du Colombier * if there isn't any handshaker, ignore the request,
8939a747e4fSDavid du Colombier * but notify the other side we are doing so.
8949a747e4fSDavid du Colombier */
8959a747e4fSDavid du Colombier lock(&tr->hqlock);
8969a747e4fSDavid du Colombier if(tr->handq != nil){
8979a747e4fSDavid du Colombier tr->hqref++;
8989a747e4fSDavid du Colombier unlock(&tr->hqlock);
8999a747e4fSDavid du Colombier if(waserror()){
9009a747e4fSDavid du Colombier dechandq(tr);
9019a747e4fSDavid du Colombier nexterror();
9029a747e4fSDavid du Colombier }
9039a747e4fSDavid du Colombier b = padblock(b, 1);
9049a747e4fSDavid du Colombier *b->rp = RHandshake;
9059a747e4fSDavid du Colombier qbwrite(tr->handq, b);
9069a747e4fSDavid du Colombier b = nil;
9079a747e4fSDavid du Colombier poperror();
9089a747e4fSDavid du Colombier dechandq(tr);
9099a747e4fSDavid du Colombier }else{
9109a747e4fSDavid du Colombier unlock(&tr->hqlock);
9119a747e4fSDavid du Colombier if(tr->verset && tr->version != SSL3Version && !waserror()){
9129a747e4fSDavid du Colombier sendAlert(tr, ENoRenegotiation);
9139a747e4fSDavid du Colombier poperror();
9149a747e4fSDavid du Colombier }
9159a747e4fSDavid du Colombier }
9169a747e4fSDavid du Colombier break;
9179a747e4fSDavid du Colombier case SSL2ClientHello:
9189a747e4fSDavid du Colombier lock(&tr->hqlock);
9199a747e4fSDavid du Colombier if(tr->handq != nil){
9209a747e4fSDavid du Colombier tr->hqref++;
9219a747e4fSDavid du Colombier unlock(&tr->hqlock);
9229a747e4fSDavid du Colombier if(waserror()){
9239a747e4fSDavid du Colombier dechandq(tr);
9249a747e4fSDavid du Colombier nexterror();
9259a747e4fSDavid du Colombier }
9269a747e4fSDavid du Colombier /* Pass the SSL2 format data, so that the handshake code can compute
9279a747e4fSDavid du Colombier the correct checksums. HSSL2ClientHello = HandshakeType 9 is
9289a747e4fSDavid du Colombier unused in RFC2246. */
9299a747e4fSDavid du Colombier b = padblock(b, 8);
9309a747e4fSDavid du Colombier b->rp[0] = RHandshake;
9319a747e4fSDavid du Colombier b->rp[1] = HSSL2ClientHello;
9329a747e4fSDavid du Colombier put24(&b->rp[2], len+3);
9339a747e4fSDavid du Colombier b->rp[5] = SSL2ClientHello;
9349a747e4fSDavid du Colombier put16(&b->rp[6], ver);
9359a747e4fSDavid du Colombier qbwrite(tr->handq, b);
9369a747e4fSDavid du Colombier b = nil;
9379a747e4fSDavid du Colombier poperror();
9389a747e4fSDavid du Colombier dechandq(tr);
9399a747e4fSDavid du Colombier }else{
9409a747e4fSDavid du Colombier unlock(&tr->hqlock);
9419a747e4fSDavid du Colombier if(tr->verset && tr->version != SSL3Version && !waserror()){
9429a747e4fSDavid du Colombier sendAlert(tr, ENoRenegotiation);
9439a747e4fSDavid du Colombier poperror();
9449a747e4fSDavid du Colombier }
9459a747e4fSDavid du Colombier }
9469a747e4fSDavid du Colombier break;
9479a747e4fSDavid du Colombier case RApplication:
9489a747e4fSDavid du Colombier if(!tr->opened)
9499a747e4fSDavid du Colombier rcvError(tr, EUnexpectedMessage, "application message received before handshake completed");
9503751babcSDavid du Colombier if(BLEN(b) > 0){
9519a747e4fSDavid du Colombier tr->processed = b;
9529a747e4fSDavid du Colombier b = nil;
9533751babcSDavid du Colombier }
9549a747e4fSDavid du Colombier break;
9559a747e4fSDavid du Colombier }
9569a747e4fSDavid du Colombier if(b != nil)
9579a747e4fSDavid du Colombier freeb(b);
9589a747e4fSDavid du Colombier poperror();
9599a747e4fSDavid du Colombier }
9609a747e4fSDavid du Colombier
9619a747e4fSDavid du Colombier /*
9629a747e4fSDavid du Colombier * got a fatal alert message
9639a747e4fSDavid du Colombier */
9649a747e4fSDavid du Colombier static void
rcvAlert(TlsRec * tr,int err)9659a747e4fSDavid du Colombier rcvAlert(TlsRec *tr, int err)
9669a747e4fSDavid du Colombier {
9679a747e4fSDavid du Colombier char *s;
9689a747e4fSDavid du Colombier int i;
9699a747e4fSDavid du Colombier
9709a747e4fSDavid du Colombier s = "unknown error";
9719a747e4fSDavid du Colombier for(i=0; i < nelem(tlserrs); i++){
9729a747e4fSDavid du Colombier if(tlserrs[i].err == err){
9739a747e4fSDavid du Colombier s = tlserrs[i].msg;
9749a747e4fSDavid du Colombier break;
9759a747e4fSDavid du Colombier }
9769a747e4fSDavid du Colombier }
9773751babcSDavid du Colombier if(tr->debug) pprint("rcvAlert: %s\n", s);
9789a747e4fSDavid du Colombier
9799a747e4fSDavid du Colombier tlsError(tr, s);
9809a747e4fSDavid du Colombier if(!tr->opened)
9819a747e4fSDavid du Colombier error(s);
9829a747e4fSDavid du Colombier error("tls error");
9839a747e4fSDavid du Colombier }
9849a747e4fSDavid du Colombier
9859a747e4fSDavid du Colombier /*
9869a747e4fSDavid du Colombier * found an error while decoding the input stream
9879a747e4fSDavid du Colombier */
9889a747e4fSDavid du Colombier static void
rcvError(TlsRec * tr,int err,char * fmt,...)9899a747e4fSDavid du Colombier rcvError(TlsRec *tr, int err, char *fmt, ...)
9909a747e4fSDavid du Colombier {
9919a747e4fSDavid du Colombier char msg[ERRMAX];
9929a747e4fSDavid du Colombier va_list arg;
9939a747e4fSDavid du Colombier
9949a747e4fSDavid du Colombier va_start(arg, fmt);
9959a747e4fSDavid du Colombier vseprint(msg, msg+sizeof(msg), fmt, arg);
9969a747e4fSDavid du Colombier va_end(arg);
9973751babcSDavid du Colombier if(tr->debug) pprint("rcvError: %s\n", msg);
9989a747e4fSDavid du Colombier
9999a747e4fSDavid du Colombier sendAlert(tr, err);
10009a747e4fSDavid du Colombier
10019a747e4fSDavid du Colombier if(!tr->opened)
10029a747e4fSDavid du Colombier error(msg);
10039a747e4fSDavid du Colombier error("tls error");
10049a747e4fSDavid du Colombier }
10059a747e4fSDavid du Colombier
10069a747e4fSDavid du Colombier /*
10079a747e4fSDavid du Colombier * make sure the next hand operation returns with a 'msg' error
10089a747e4fSDavid du Colombier */
10099a747e4fSDavid du Colombier static void
alertHand(TlsRec * tr,char * msg)10109a747e4fSDavid du Colombier alertHand(TlsRec *tr, char *msg)
10119a747e4fSDavid du Colombier {
10129a747e4fSDavid du Colombier Block *b;
10139a747e4fSDavid du Colombier int n;
10149a747e4fSDavid du Colombier
10159a747e4fSDavid du Colombier lock(&tr->hqlock);
10169a747e4fSDavid du Colombier if(tr->handq == nil){
10179a747e4fSDavid du Colombier unlock(&tr->hqlock);
10189a747e4fSDavid du Colombier return;
10199a747e4fSDavid du Colombier }
10209a747e4fSDavid du Colombier tr->hqref++;
10219a747e4fSDavid du Colombier unlock(&tr->hqlock);
10229a747e4fSDavid du Colombier
10239a747e4fSDavid du Colombier n = strlen(msg);
10249a747e4fSDavid du Colombier if(waserror()){
10259a747e4fSDavid du Colombier dechandq(tr);
10269a747e4fSDavid du Colombier nexterror();
10279a747e4fSDavid du Colombier }
10289a747e4fSDavid du Colombier b = allocb(n + 2);
10299a747e4fSDavid du Colombier *b->wp++ = RAlert;
10309a747e4fSDavid du Colombier memmove(b->wp, msg, n + 1);
10319a747e4fSDavid du Colombier b->wp += n + 1;
10329a747e4fSDavid du Colombier
10339a747e4fSDavid du Colombier qbwrite(tr->handq, b);
10349a747e4fSDavid du Colombier
10359a747e4fSDavid du Colombier poperror();
10369a747e4fSDavid du Colombier dechandq(tr);
10379a747e4fSDavid du Colombier }
10389a747e4fSDavid du Colombier
10399a747e4fSDavid du Colombier static void
checkstate(TlsRec * tr,int ishand,int ok)10409a747e4fSDavid du Colombier checkstate(TlsRec *tr, int ishand, int ok)
10419a747e4fSDavid du Colombier {
10429a747e4fSDavid du Colombier int state;
10439a747e4fSDavid du Colombier
10449a747e4fSDavid du Colombier lock(&tr->statelk);
10459a747e4fSDavid du Colombier state = tr->state;
10469a747e4fSDavid du Colombier unlock(&tr->statelk);
10479a747e4fSDavid du Colombier if(state & ok)
10489a747e4fSDavid du Colombier return;
10499a747e4fSDavid du Colombier switch(state){
10509a747e4fSDavid du Colombier case SHandshake:
10519a747e4fSDavid du Colombier case SOpen:
10529a747e4fSDavid du Colombier break;
10539a747e4fSDavid du Colombier case SError:
10549a747e4fSDavid du Colombier case SAlert:
10559a747e4fSDavid du Colombier if(ishand)
10569a747e4fSDavid du Colombier error(tr->err);
10579a747e4fSDavid du Colombier error("tls error");
10589a747e4fSDavid du Colombier case SRClose:
10599a747e4fSDavid du Colombier case SLClose:
10609a747e4fSDavid du Colombier case SClosed:
10619a747e4fSDavid du Colombier error("tls hungup");
10629a747e4fSDavid du Colombier }
10639a747e4fSDavid du Colombier error("tls improperly configured");
10649a747e4fSDavid du Colombier }
10659a747e4fSDavid du Colombier
10669a747e4fSDavid du Colombier static Block*
tlsbread(Chan * c,long n,ulong offset)10679a747e4fSDavid du Colombier tlsbread(Chan *c, long n, ulong offset)
10689a747e4fSDavid du Colombier {
10699a747e4fSDavid du Colombier int ty;
10709a747e4fSDavid du Colombier Block *b;
10719a747e4fSDavid du Colombier TlsRec *volatile tr;
10729a747e4fSDavid du Colombier
10739a747e4fSDavid du Colombier ty = TYPE(c->qid);
10749a747e4fSDavid du Colombier switch(ty) {
10759a747e4fSDavid du Colombier default:
10769a747e4fSDavid du Colombier return devbread(c, n, offset);
10779a747e4fSDavid du Colombier case Qhand:
10789a747e4fSDavid du Colombier case Qdata:
10799a747e4fSDavid du Colombier break;
10809a747e4fSDavid du Colombier }
10819a747e4fSDavid du Colombier
10829a747e4fSDavid du Colombier tr = tlsdevs[CONV(c->qid)];
10839a747e4fSDavid du Colombier if(tr == nil)
10849a747e4fSDavid du Colombier panic("tlsbread");
10859a747e4fSDavid du Colombier
10869a747e4fSDavid du Colombier if(waserror()){
10879a747e4fSDavid du Colombier qunlock(&tr->in.io);
10889a747e4fSDavid du Colombier nexterror();
10899a747e4fSDavid du Colombier }
10909a747e4fSDavid du Colombier qlock(&tr->in.io);
10919a747e4fSDavid du Colombier if(ty == Qdata){
10929a747e4fSDavid du Colombier checkstate(tr, 0, SOpen);
10939a747e4fSDavid du Colombier while(tr->processed == nil)
10949a747e4fSDavid du Colombier tlsrecread(tr);
10959a747e4fSDavid du Colombier
10969a747e4fSDavid du Colombier /* return at most what was asked for */
10979a747e4fSDavid du Colombier b = qgrab(&tr->processed, n);
1098e464ed1aSDavid du Colombier if(tr->debug) pprint("consumed processed %ld\n", BLEN(b));
10993751babcSDavid du Colombier if(tr->debug) pdump(BLEN(b), b->rp, "consumed:");
11009a747e4fSDavid du Colombier qunlock(&tr->in.io);
11019a747e4fSDavid du Colombier poperror();
11029a747e4fSDavid du Colombier tr->datain += BLEN(b);
11039a747e4fSDavid du Colombier }else{
11049a747e4fSDavid du Colombier checkstate(tr, 1, SOpen|SHandshake|SLClose);
11059a747e4fSDavid du Colombier
11069a747e4fSDavid du Colombier /*
11079a747e4fSDavid du Colombier * it's ok to look at state without the lock
11089a747e4fSDavid du Colombier * since it only protects reading records,
11099a747e4fSDavid du Colombier * and we have that tr->in.io held.
11109a747e4fSDavid du Colombier */
11119a747e4fSDavid du Colombier while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq))
11129a747e4fSDavid du Colombier tlsrecread(tr);
11139a747e4fSDavid du Colombier
11149a747e4fSDavid du Colombier qunlock(&tr->in.io);
11159a747e4fSDavid du Colombier poperror();
11169a747e4fSDavid du Colombier
11179a747e4fSDavid du Colombier if(waserror()){
11189a747e4fSDavid du Colombier qunlock(&tr->hqread);
11199a747e4fSDavid du Colombier nexterror();
11209a747e4fSDavid du Colombier }
11219a747e4fSDavid du Colombier qlock(&tr->hqread);
11229a747e4fSDavid du Colombier if(tr->hprocessed == nil){
11239a747e4fSDavid du Colombier b = qbread(tr->handq, MaxRecLen + 1);
11249a747e4fSDavid du Colombier if(*b->rp++ == RAlert){
1125dc5a79c1SDavid du Colombier kstrcpy(up->errstr, (char*)b->rp, ERRMAX);
11269a747e4fSDavid du Colombier freeb(b);
11279a747e4fSDavid du Colombier nexterror();
11289a747e4fSDavid du Colombier }
11299a747e4fSDavid du Colombier tr->hprocessed = b;
11309a747e4fSDavid du Colombier }
11319a747e4fSDavid du Colombier b = qgrab(&tr->hprocessed, n);
11329a747e4fSDavid du Colombier poperror();
11339a747e4fSDavid du Colombier qunlock(&tr->hqread);
11349a747e4fSDavid du Colombier tr->handin += BLEN(b);
11359a747e4fSDavid du Colombier }
11369a747e4fSDavid du Colombier
11379a747e4fSDavid du Colombier return b;
11389a747e4fSDavid du Colombier }
11399a747e4fSDavid du Colombier
11409a747e4fSDavid du Colombier static long
tlsread(Chan * c,void * a,long n,vlong off)11419a747e4fSDavid du Colombier tlsread(Chan *c, void *a, long n, vlong off)
11429a747e4fSDavid du Colombier {
11439a747e4fSDavid du Colombier Block *volatile b;
11449a747e4fSDavid du Colombier Block *nb;
11459a747e4fSDavid du Colombier uchar *va;
11469a747e4fSDavid du Colombier int i, ty;
11479a747e4fSDavid du Colombier char *buf, *s, *e;
11489a747e4fSDavid du Colombier ulong offset = off;
11499a747e4fSDavid du Colombier TlsRec * tr;
11509a747e4fSDavid du Colombier
11519a747e4fSDavid du Colombier if(c->qid.type & QTDIR)
11529a747e4fSDavid du Colombier return devdirread(c, a, n, 0, 0, tlsgen);
11539a747e4fSDavid du Colombier
11549a747e4fSDavid du Colombier tr = tlsdevs[CONV(c->qid)];
11559a747e4fSDavid du Colombier ty = TYPE(c->qid);
11569a747e4fSDavid du Colombier switch(ty) {
11579a747e4fSDavid du Colombier default:
11589a747e4fSDavid du Colombier error(Ebadusefd);
11599a747e4fSDavid du Colombier case Qstatus:
11609a747e4fSDavid du Colombier buf = smalloc(Statlen);
11619a747e4fSDavid du Colombier qlock(&tr->in.seclock);
11629a747e4fSDavid du Colombier qlock(&tr->out.seclock);
11639a747e4fSDavid du Colombier s = buf;
11649a747e4fSDavid du Colombier e = buf + Statlen;
11659a747e4fSDavid du Colombier s = seprint(s, e, "State: %s\n", tlsstate(tr->state));
1166567483c8SDavid du Colombier s = seprint(s, e, "Version: %#x\n", tr->version);
11679a747e4fSDavid du Colombier if(tr->in.sec != nil)
11689a747e4fSDavid du Colombier s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg);
11699a747e4fSDavid du Colombier if(tr->in.new != nil)
11709a747e4fSDavid du Colombier s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg);
11719a747e4fSDavid du Colombier if(tr->out.sec != nil)
11729a747e4fSDavid du Colombier s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg);
11739a747e4fSDavid du Colombier if(tr->out.new != nil)
11749a747e4fSDavid du Colombier seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg);
11759a747e4fSDavid du Colombier qunlock(&tr->in.seclock);
11769a747e4fSDavid du Colombier qunlock(&tr->out.seclock);
11779a747e4fSDavid du Colombier n = readstr(offset, a, n, buf);
11789a747e4fSDavid du Colombier free(buf);
11799a747e4fSDavid du Colombier return n;
11809a747e4fSDavid du Colombier case Qstats:
11819a747e4fSDavid du Colombier buf = smalloc(Statlen);
11829a747e4fSDavid du Colombier s = buf;
11839a747e4fSDavid du Colombier e = buf + Statlen;
11849a747e4fSDavid du Colombier s = seprint(s, e, "DataIn: %lld\n", tr->datain);
11859a747e4fSDavid du Colombier s = seprint(s, e, "DataOut: %lld\n", tr->dataout);
11869a747e4fSDavid du Colombier s = seprint(s, e, "HandIn: %lld\n", tr->handin);
11879a747e4fSDavid du Colombier seprint(s, e, "HandOut: %lld\n", tr->handout);
11889a747e4fSDavid du Colombier n = readstr(offset, a, n, buf);
11899a747e4fSDavid du Colombier free(buf);
11909a747e4fSDavid du Colombier return n;
11919a747e4fSDavid du Colombier case Qctl:
11929a747e4fSDavid du Colombier buf = smalloc(Statlen);
11939a747e4fSDavid du Colombier snprint(buf, Statlen, "%llud", CONV(c->qid));
11949a747e4fSDavid du Colombier n = readstr(offset, a, n, buf);
11959a747e4fSDavid du Colombier free(buf);
11969a747e4fSDavid du Colombier return n;
11979a747e4fSDavid du Colombier case Qdata:
11989a747e4fSDavid du Colombier case Qhand:
11999a747e4fSDavid du Colombier b = tlsbread(c, n, offset);
12009a747e4fSDavid du Colombier break;
12019a747e4fSDavid du Colombier case Qencalgs:
12029a747e4fSDavid du Colombier return readstr(offset, a, n, encalgs);
12039a747e4fSDavid du Colombier case Qhashalgs:
12049a747e4fSDavid du Colombier return readstr(offset, a, n, hashalgs);
12059a747e4fSDavid du Colombier }
12069a747e4fSDavid du Colombier
12079a747e4fSDavid du Colombier if(waserror()){
12089a747e4fSDavid du Colombier freeblist(b);
12099a747e4fSDavid du Colombier nexterror();
12109a747e4fSDavid du Colombier }
12119a747e4fSDavid du Colombier
12129a747e4fSDavid du Colombier n = 0;
12139a747e4fSDavid du Colombier va = a;
12149a747e4fSDavid du Colombier for(nb = b; nb; nb = nb->next){
12159a747e4fSDavid du Colombier i = BLEN(nb);
12169a747e4fSDavid du Colombier memmove(va+n, nb->rp, i);
12179a747e4fSDavid du Colombier n += i;
12189a747e4fSDavid du Colombier }
12199a747e4fSDavid du Colombier
12209a747e4fSDavid du Colombier freeblist(b);
12219a747e4fSDavid du Colombier poperror();
12229a747e4fSDavid du Colombier
12239a747e4fSDavid du Colombier return n;
12249a747e4fSDavid du Colombier }
12259a747e4fSDavid du Colombier
12262a47d117SDavid du Colombier static void
randfill(uchar * buf,int len)12272a47d117SDavid du Colombier randfill(uchar *buf, int len)
12282a47d117SDavid du Colombier {
12292a47d117SDavid du Colombier while(len-- > 0)
12302a47d117SDavid du Colombier *buf++ = nrand(256);
12312a47d117SDavid du Colombier }
12322a47d117SDavid du Colombier
12339a747e4fSDavid du Colombier /*
12349a747e4fSDavid du Colombier * write a block in tls records
12359a747e4fSDavid du Colombier */
12369a747e4fSDavid du Colombier static void
tlsrecwrite(TlsRec * tr,int type,Block * b)12379a747e4fSDavid du Colombier tlsrecwrite(TlsRec *tr, int type, Block *b)
12389a747e4fSDavid du Colombier {
12399a747e4fSDavid du Colombier Block *volatile bb;
12409a747e4fSDavid du Colombier Block *nb;
12419a747e4fSDavid du Colombier uchar *p, seq[8];
12429a747e4fSDavid du Colombier OneWay *volatile out;
12432a47d117SDavid du Colombier int n, ivlen, maclen, pad, ok;
12449a747e4fSDavid du Colombier
12459a747e4fSDavid du Colombier out = &tr->out;
12469a747e4fSDavid du Colombier bb = b;
12479a747e4fSDavid du Colombier if(waserror()){
12489a747e4fSDavid du Colombier qunlock(&out->io);
12499a747e4fSDavid du Colombier if(bb != nil)
12509a747e4fSDavid du Colombier freeb(bb);
12519a747e4fSDavid du Colombier nexterror();
12529a747e4fSDavid du Colombier }
12539a747e4fSDavid du Colombier qlock(&out->io);
1254e464ed1aSDavid du Colombier if(tr->debug)pprint("send %ld\n", BLEN(b));
12553751babcSDavid du Colombier if(tr->debug)pdump(BLEN(b), b->rp, "sent:");
12563751babcSDavid du Colombier
12578b74d3adSDavid du Colombier if(type == RApplication)
12588b74d3adSDavid du Colombier checkstate(tr, 0, SOpen);
12599a747e4fSDavid du Colombier
12609a747e4fSDavid du Colombier ok = SHandshake|SOpen|SRClose;
12619a747e4fSDavid du Colombier if(type == RAlert)
12629a747e4fSDavid du Colombier ok |= SAlert;
12639a747e4fSDavid du Colombier while(bb != nil){
12649a747e4fSDavid du Colombier checkstate(tr, type != RApplication, ok);
12659a747e4fSDavid du Colombier
12669a747e4fSDavid du Colombier /*
12679a747e4fSDavid du Colombier * get at most one maximal record's input,
12689a747e4fSDavid du Colombier * with padding on the front for header and
12699a747e4fSDavid du Colombier * back for mac and maximal block padding.
12709a747e4fSDavid du Colombier */
12719a747e4fSDavid du Colombier if(waserror()){
12729a747e4fSDavid du Colombier qunlock(&out->seclock);
12739a747e4fSDavid du Colombier nexterror();
12749a747e4fSDavid du Colombier }
12759a747e4fSDavid du Colombier qlock(&out->seclock);
12769a747e4fSDavid du Colombier maclen = 0;
12779a747e4fSDavid du Colombier pad = 0;
12782a47d117SDavid du Colombier ivlen = 0;
12799a747e4fSDavid du Colombier if(out->sec != nil){
12809a747e4fSDavid du Colombier maclen = out->sec->maclen;
12819a747e4fSDavid du Colombier pad = maclen + out->sec->block;
12822a47d117SDavid du Colombier if(tr->version >= TLS11Version)
12832a47d117SDavid du Colombier ivlen = out->sec->block;
12849a747e4fSDavid du Colombier }
12859a747e4fSDavid du Colombier n = BLEN(bb);
12869a747e4fSDavid du Colombier if(n > MaxRecLen){
12879a747e4fSDavid du Colombier n = MaxRecLen;
12882a47d117SDavid du Colombier nb = allocb(RecHdrLen + ivlen + n + pad);
12892a47d117SDavid du Colombier memmove(nb->wp + RecHdrLen + ivlen, bb->rp, n);
12909a747e4fSDavid du Colombier bb->rp += n;
12919a747e4fSDavid du Colombier }else{
12929a747e4fSDavid du Colombier /*
12939a747e4fSDavid du Colombier * carefully reuse bb so it will get freed if we're out of memory
12949a747e4fSDavid du Colombier */
12952a47d117SDavid du Colombier bb = padblock(bb, RecHdrLen + ivlen);
12969a747e4fSDavid du Colombier if(pad)
12979a747e4fSDavid du Colombier nb = padblock(bb, -pad);
12989a747e4fSDavid du Colombier else
12999a747e4fSDavid du Colombier nb = bb;
13009a747e4fSDavid du Colombier bb = nil;
13019a747e4fSDavid du Colombier }
13029a747e4fSDavid du Colombier
13039a747e4fSDavid du Colombier p = nb->rp;
13049a747e4fSDavid du Colombier p[0] = type;
13059a747e4fSDavid du Colombier put16(p+1, tr->version);
13069a747e4fSDavid du Colombier put16(p+3, n);
13079a747e4fSDavid du Colombier
13089a747e4fSDavid du Colombier if(out->sec != nil){
13099a747e4fSDavid du Colombier put64(seq, out->seq);
13109a747e4fSDavid du Colombier out->seq++;
13112a47d117SDavid du Colombier (*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen + ivlen, n, p + RecHdrLen + ivlen + n);
13129a747e4fSDavid du Colombier n += maclen;
13139a747e4fSDavid du Colombier
13142a47d117SDavid du Colombier /* explicit iv */
13152a47d117SDavid du Colombier if(ivlen > 0){
13162a47d117SDavid du Colombier randfill(p + RecHdrLen, ivlen);
13172a47d117SDavid du Colombier n += ivlen;
13182a47d117SDavid du Colombier }
13192a47d117SDavid du Colombier
13209a747e4fSDavid du Colombier /* encrypt */
13219a747e4fSDavid du Colombier n = (*out->sec->enc)(out->sec, p + RecHdrLen, n);
13229a747e4fSDavid du Colombier nb->wp = p + RecHdrLen + n;
13239a747e4fSDavid du Colombier
13249a747e4fSDavid du Colombier /* update length */
13259a747e4fSDavid du Colombier put16(p+3, n);
13269a747e4fSDavid du Colombier }
13279a747e4fSDavid du Colombier if(type == RChangeCipherSpec){
13289a747e4fSDavid du Colombier if(out->new == nil)
13299a747e4fSDavid du Colombier error("change cipher without a new cipher");
13309a747e4fSDavid du Colombier freeSec(out->sec);
13319a747e4fSDavid du Colombier out->sec = out->new;
13329a747e4fSDavid du Colombier out->new = nil;
13339a747e4fSDavid du Colombier out->seq = 0;
13349a747e4fSDavid du Colombier }
13359a747e4fSDavid du Colombier qunlock(&out->seclock);
13369a747e4fSDavid du Colombier poperror();
13379a747e4fSDavid du Colombier
13389a747e4fSDavid du Colombier /*
13399a747e4fSDavid du Colombier * if bwrite error's, we assume the block is queued.
13409a747e4fSDavid du Colombier * if not, we're out of sync with the receiver and will not recover.
13419a747e4fSDavid du Colombier */
13429a747e4fSDavid du Colombier if(waserror()){
13439a747e4fSDavid du Colombier if(strcmp(up->errstr, "interrupted") != 0)
13449a747e4fSDavid du Colombier tlsError(tr, "channel error");
13459a747e4fSDavid du Colombier nexterror();
13469a747e4fSDavid du Colombier }
13479a747e4fSDavid du Colombier devtab[tr->c->type]->bwrite(tr->c, nb, 0);
13489a747e4fSDavid du Colombier poperror();
13499a747e4fSDavid du Colombier }
13509a747e4fSDavid du Colombier qunlock(&out->io);
13519a747e4fSDavid du Colombier poperror();
13529a747e4fSDavid du Colombier }
13539a747e4fSDavid du Colombier
13549a747e4fSDavid du Colombier static long
tlsbwrite(Chan * c,Block * b,ulong offset)13559a747e4fSDavid du Colombier tlsbwrite(Chan *c, Block *b, ulong offset)
13569a747e4fSDavid du Colombier {
13579a747e4fSDavid du Colombier int ty;
13589a747e4fSDavid du Colombier ulong n;
13599a747e4fSDavid du Colombier TlsRec *tr;
13609a747e4fSDavid du Colombier
13619a747e4fSDavid du Colombier n = BLEN(b);
13629a747e4fSDavid du Colombier
13639a747e4fSDavid du Colombier tr = tlsdevs[CONV(c->qid)];
13649a747e4fSDavid du Colombier if(tr == nil)
13659b7bf7dfSDavid du Colombier panic("tlsbwrite");
13669a747e4fSDavid du Colombier
13679a747e4fSDavid du Colombier ty = TYPE(c->qid);
13689a747e4fSDavid du Colombier switch(ty) {
13699a747e4fSDavid du Colombier default:
13709a747e4fSDavid du Colombier return devbwrite(c, b, offset);
13719a747e4fSDavid du Colombier case Qhand:
13729a747e4fSDavid du Colombier tlsrecwrite(tr, RHandshake, b);
13739a747e4fSDavid du Colombier tr->handout += n;
13749a747e4fSDavid du Colombier break;
13759a747e4fSDavid du Colombier case Qdata:
13769a747e4fSDavid du Colombier tlsrecwrite(tr, RApplication, b);
13779a747e4fSDavid du Colombier tr->dataout += n;
13789a747e4fSDavid du Colombier break;
13799a747e4fSDavid du Colombier }
13809a747e4fSDavid du Colombier
13819a747e4fSDavid du Colombier return n;
13829a747e4fSDavid du Colombier }
13839a747e4fSDavid du Colombier
13849a747e4fSDavid du Colombier typedef struct Hashalg Hashalg;
13859a747e4fSDavid du Colombier struct Hashalg
13869a747e4fSDavid du Colombier {
13879a747e4fSDavid du Colombier char *name;
13889a747e4fSDavid du Colombier int maclen;
13899a747e4fSDavid du Colombier void (*initkey)(Hashalg *, int, Secret *, uchar*);
13909a747e4fSDavid du Colombier };
13919a747e4fSDavid du Colombier
13929a747e4fSDavid du Colombier static void
initmd5key(Hashalg * ha,int version,Secret * s,uchar * p)13939a747e4fSDavid du Colombier initmd5key(Hashalg *ha, int version, Secret *s, uchar *p)
13949a747e4fSDavid du Colombier {
13959a747e4fSDavid du Colombier s->maclen = ha->maclen;
13969a747e4fSDavid du Colombier if(version == SSL3Version)
13979a747e4fSDavid du Colombier s->mac = sslmac_md5;
13989a747e4fSDavid du Colombier else
13999a747e4fSDavid du Colombier s->mac = hmac_md5;
14009a747e4fSDavid du Colombier memmove(s->mackey, p, ha->maclen);
14019a747e4fSDavid du Colombier }
14029a747e4fSDavid du Colombier
14039a747e4fSDavid du Colombier static void
initclearmac(Hashalg *,int,Secret * s,uchar *)14049a747e4fSDavid du Colombier initclearmac(Hashalg *, int, Secret *s, uchar *)
14059a747e4fSDavid du Colombier {
14069a747e4fSDavid du Colombier s->maclen = 0;
14079a747e4fSDavid du Colombier s->mac = nomac;
14089a747e4fSDavid du Colombier }
14099a747e4fSDavid du Colombier
14109a747e4fSDavid du Colombier static void
initsha1key(Hashalg * ha,int version,Secret * s,uchar * p)14119a747e4fSDavid du Colombier initsha1key(Hashalg *ha, int version, Secret *s, uchar *p)
14129a747e4fSDavid du Colombier {
14139a747e4fSDavid du Colombier s->maclen = ha->maclen;
14149a747e4fSDavid du Colombier if(version == SSL3Version)
14159a747e4fSDavid du Colombier s->mac = sslmac_sha1;
14169a747e4fSDavid du Colombier else
14179a747e4fSDavid du Colombier s->mac = hmac_sha1;
14189a747e4fSDavid du Colombier memmove(s->mackey, p, ha->maclen);
14199a747e4fSDavid du Colombier }
14209a747e4fSDavid du Colombier
14212a47d117SDavid du Colombier static void
initsha2_256key(Hashalg * ha,int version,Secret * s,uchar * p)14222a47d117SDavid du Colombier initsha2_256key(Hashalg *ha, int version, Secret *s, uchar *p)
14232a47d117SDavid du Colombier {
14242a47d117SDavid du Colombier if(version == SSL3Version)
14252a47d117SDavid du Colombier error("sha256 cannot be used with SSL");
14262a47d117SDavid du Colombier s->maclen = ha->maclen;
14272a47d117SDavid du Colombier s->mac = hmac_sha2_256;
14282a47d117SDavid du Colombier memmove(s->mackey, p, ha->maclen);
14292a47d117SDavid du Colombier }
14302a47d117SDavid du Colombier
14319a747e4fSDavid du Colombier static Hashalg hashtab[] =
14329a747e4fSDavid du Colombier {
14339a747e4fSDavid du Colombier { "clear", 0, initclearmac, },
14349a747e4fSDavid du Colombier { "md5", MD5dlen, initmd5key, },
14359a747e4fSDavid du Colombier { "sha1", SHA1dlen, initsha1key, },
14362a47d117SDavid du Colombier { "sha256", SHA2_256dlen, initsha2_256key, },
14379a747e4fSDavid du Colombier { 0 }
14389a747e4fSDavid du Colombier };
14399a747e4fSDavid du Colombier
14409a747e4fSDavid du Colombier static Hashalg*
parsehashalg(char * p)14419a747e4fSDavid du Colombier parsehashalg(char *p)
14429a747e4fSDavid du Colombier {
14439a747e4fSDavid du Colombier Hashalg *ha;
14449a747e4fSDavid du Colombier
14459a747e4fSDavid du Colombier for(ha = hashtab; ha->name; ha++)
14469a747e4fSDavid du Colombier if(strcmp(p, ha->name) == 0)
14479a747e4fSDavid du Colombier return ha;
14489a747e4fSDavid du Colombier error("unsupported hash algorithm");
14499a747e4fSDavid du Colombier return nil;
14509a747e4fSDavid du Colombier }
14519a747e4fSDavid du Colombier
14529a747e4fSDavid du Colombier typedef struct Encalg Encalg;
14539a747e4fSDavid du Colombier struct Encalg
14549a747e4fSDavid du Colombier {
14559a747e4fSDavid du Colombier char *name;
14569a747e4fSDavid du Colombier int keylen;
14579a747e4fSDavid du Colombier int ivlen;
14589a747e4fSDavid du Colombier void (*initkey)(Encalg *ea, Secret *, uchar*, uchar*);
14599a747e4fSDavid du Colombier };
14609a747e4fSDavid du Colombier
14619a747e4fSDavid du Colombier static void
initRC4key(Encalg * ea,Secret * s,uchar * p,uchar *)14629a747e4fSDavid du Colombier initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *)
14639a747e4fSDavid du Colombier {
14649a747e4fSDavid du Colombier s->enckey = smalloc(sizeof(RC4state));
14659a747e4fSDavid du Colombier s->enc = rc4enc;
14669a747e4fSDavid du Colombier s->dec = rc4enc;
14679a747e4fSDavid du Colombier s->block = 0;
14689a747e4fSDavid du Colombier setupRC4state(s->enckey, p, ea->keylen);
14699a747e4fSDavid du Colombier }
14709a747e4fSDavid du Colombier
14719a747e4fSDavid du Colombier static void
initDES3key(Encalg *,Secret * s,uchar * p,uchar * iv)14729a747e4fSDavid du Colombier initDES3key(Encalg *, Secret *s, uchar *p, uchar *iv)
14739a747e4fSDavid du Colombier {
14749a747e4fSDavid du Colombier s->enckey = smalloc(sizeof(DES3state));
14759a747e4fSDavid du Colombier s->enc = des3enc;
14769a747e4fSDavid du Colombier s->dec = des3dec;
14779a747e4fSDavid du Colombier s->block = 8;
14789a747e4fSDavid du Colombier setupDES3state(s->enckey, (uchar(*)[8])p, iv);
14799a747e4fSDavid du Colombier }
14809a747e4fSDavid du Colombier
14819a747e4fSDavid du Colombier static void
initAESkey(Encalg * ea,Secret * s,uchar * p,uchar * iv)1482ad6ca847SDavid du Colombier initAESkey(Encalg *ea, Secret *s, uchar *p, uchar *iv)
1483ad6ca847SDavid du Colombier {
1484ad6ca847SDavid du Colombier s->enckey = smalloc(sizeof(AESstate));
1485ad6ca847SDavid du Colombier s->enc = aesenc;
1486ad6ca847SDavid du Colombier s->dec = aesdec;
1487ad6ca847SDavid du Colombier s->block = 16;
1488ad6ca847SDavid du Colombier setupAESstate(s->enckey, p, ea->keylen, iv);
1489ad6ca847SDavid du Colombier }
1490ad6ca847SDavid du Colombier
1491ad6ca847SDavid du Colombier static void
initclearenc(Encalg *,Secret * s,uchar *,uchar *)14929a747e4fSDavid du Colombier initclearenc(Encalg *, Secret *s, uchar *, uchar *)
14939a747e4fSDavid du Colombier {
14949a747e4fSDavid du Colombier s->enc = noenc;
14959a747e4fSDavid du Colombier s->dec = noenc;
14969a747e4fSDavid du Colombier s->block = 0;
14979a747e4fSDavid du Colombier }
14989a747e4fSDavid du Colombier
14999a747e4fSDavid du Colombier static Encalg encrypttab[] =
15009a747e4fSDavid du Colombier {
15019a747e4fSDavid du Colombier { "clear", 0, 0, initclearenc },
15029a747e4fSDavid du Colombier { "rc4_128", 128/8, 0, initRC4key },
15039a747e4fSDavid du Colombier { "3des_ede_cbc", 3 * 8, 8, initDES3key },
1504ad6ca847SDavid du Colombier { "aes_128_cbc", 128/8, 16, initAESkey },
1505ad6ca847SDavid du Colombier { "aes_256_cbc", 256/8, 16, initAESkey },
15069a747e4fSDavid du Colombier { 0 }
15079a747e4fSDavid du Colombier };
15089a747e4fSDavid du Colombier
15099a747e4fSDavid du Colombier static Encalg*
parseencalg(char * p)15109a747e4fSDavid du Colombier parseencalg(char *p)
15119a747e4fSDavid du Colombier {
15129a747e4fSDavid du Colombier Encalg *ea;
15139a747e4fSDavid du Colombier
15149a747e4fSDavid du Colombier for(ea = encrypttab; ea->name; ea++)
15159a747e4fSDavid du Colombier if(strcmp(p, ea->name) == 0)
15169a747e4fSDavid du Colombier return ea;
15179a747e4fSDavid du Colombier error("unsupported encryption algorithm");
15189a747e4fSDavid du Colombier return nil;
15199a747e4fSDavid du Colombier }
15209a747e4fSDavid du Colombier
15219a747e4fSDavid du Colombier static long
tlswrite(Chan * c,void * a,long n,vlong off)15229a747e4fSDavid du Colombier tlswrite(Chan *c, void *a, long n, vlong off)
15239a747e4fSDavid du Colombier {
15249a747e4fSDavid du Colombier Encalg *ea;
15259a747e4fSDavid du Colombier Hashalg *ha;
15269a747e4fSDavid du Colombier TlsRec *volatile tr;
15279a747e4fSDavid du Colombier Secret *volatile tos, *volatile toc;
15289a747e4fSDavid du Colombier Block *volatile b;
15299a747e4fSDavid du Colombier Cmdbuf *volatile cb;
15309a747e4fSDavid du Colombier int m, ty;
15319a747e4fSDavid du Colombier char *p, *e;
15329a747e4fSDavid du Colombier uchar *volatile x;
15339a747e4fSDavid du Colombier ulong offset = off;
15349a747e4fSDavid du Colombier
15359a747e4fSDavid du Colombier tr = tlsdevs[CONV(c->qid)];
15369a747e4fSDavid du Colombier if(tr == nil)
15379a747e4fSDavid du Colombier panic("tlswrite");
15389a747e4fSDavid du Colombier
15399a747e4fSDavid du Colombier ty = TYPE(c->qid);
15409a747e4fSDavid du Colombier switch(ty){
15419a747e4fSDavid du Colombier case Qdata:
15429a747e4fSDavid du Colombier case Qhand:
15439a747e4fSDavid du Colombier p = a;
15449a747e4fSDavid du Colombier e = p + n;
15459a747e4fSDavid du Colombier do{
15469a747e4fSDavid du Colombier m = e - p;
15479a747e4fSDavid du Colombier if(m > MaxRecLen)
15489a747e4fSDavid du Colombier m = MaxRecLen;
15499a747e4fSDavid du Colombier
15509a747e4fSDavid du Colombier b = allocb(m);
15519a747e4fSDavid du Colombier if(waserror()){
15529a747e4fSDavid du Colombier freeb(b);
15539a747e4fSDavid du Colombier nexterror();
15549a747e4fSDavid du Colombier }
15559a747e4fSDavid du Colombier memmove(b->wp, p, m);
15569a747e4fSDavid du Colombier poperror();
15579a747e4fSDavid du Colombier b->wp += m;
15589a747e4fSDavid du Colombier
15599a747e4fSDavid du Colombier tlsbwrite(c, b, offset);
15609a747e4fSDavid du Colombier
15619a747e4fSDavid du Colombier p += m;
15629a747e4fSDavid du Colombier }while(p < e);
15639a747e4fSDavid du Colombier return n;
15649a747e4fSDavid du Colombier case Qctl:
15659a747e4fSDavid du Colombier break;
15669a747e4fSDavid du Colombier default:
15679a747e4fSDavid du Colombier error(Ebadusefd);
15689a747e4fSDavid du Colombier return -1;
15699a747e4fSDavid du Colombier }
15709a747e4fSDavid du Colombier
15719a747e4fSDavid du Colombier cb = parsecmd(a, n);
15729a747e4fSDavid du Colombier if(waserror()){
15739a747e4fSDavid du Colombier free(cb);
15749a747e4fSDavid du Colombier nexterror();
15759a747e4fSDavid du Colombier }
15769a747e4fSDavid du Colombier if(cb->nf < 1)
15779a747e4fSDavid du Colombier error("short control request");
15789a747e4fSDavid du Colombier
15799a747e4fSDavid du Colombier /* mutex with operations using what we're about to change */
15809a747e4fSDavid du Colombier if(waserror()){
15819a747e4fSDavid du Colombier qunlock(&tr->in.seclock);
15829a747e4fSDavid du Colombier qunlock(&tr->out.seclock);
15839a747e4fSDavid du Colombier nexterror();
15849a747e4fSDavid du Colombier }
15859a747e4fSDavid du Colombier qlock(&tr->in.seclock);
15869a747e4fSDavid du Colombier qlock(&tr->out.seclock);
15879a747e4fSDavid du Colombier
15889a747e4fSDavid du Colombier if(strcmp(cb->f[0], "fd") == 0){
15899a747e4fSDavid du Colombier if(cb->nf != 3)
15909a747e4fSDavid du Colombier error("usage: fd open-fd version");
15919a747e4fSDavid du Colombier if(tr->c != nil)
15929a747e4fSDavid du Colombier error(Einuse);
15939a747e4fSDavid du Colombier m = strtol(cb->f[2], nil, 0);
15949a747e4fSDavid du Colombier if(m < MinProtoVersion || m > MaxProtoVersion)
15959a747e4fSDavid du Colombier error("unsupported version");
15969a747e4fSDavid du Colombier tr->c = buftochan(cb->f[1]);
15979a747e4fSDavid du Colombier tr->version = m;
15989a747e4fSDavid du Colombier tlsSetState(tr, SHandshake, SClosed);
15999a747e4fSDavid du Colombier }else if(strcmp(cb->f[0], "version") == 0){
16009a747e4fSDavid du Colombier if(cb->nf != 2)
16019a747e4fSDavid du Colombier error("usage: version vers");
16029a747e4fSDavid du Colombier if(tr->c == nil)
16039a747e4fSDavid du Colombier error("must set fd before version");
16049a747e4fSDavid du Colombier if(tr->verset)
16059a747e4fSDavid du Colombier error("version already set");
16069a747e4fSDavid du Colombier m = strtol(cb->f[1], nil, 0);
16072a47d117SDavid du Colombier if(m < MinProtoVersion || m > MaxProtoVersion)
16082a47d117SDavid du Colombier error("unsupported version");
16099a747e4fSDavid du Colombier if(m == SSL3Version)
16109a747e4fSDavid du Colombier tr->packMac = sslPackMac;
16119a747e4fSDavid du Colombier else
16122a47d117SDavid du Colombier tr->packMac = tlsPackMac;
16139a747e4fSDavid du Colombier tr->verset = 1;
16149a747e4fSDavid du Colombier tr->version = m;
16159a747e4fSDavid du Colombier }else if(strcmp(cb->f[0], "secret") == 0){
16169a747e4fSDavid du Colombier if(cb->nf != 5)
16179a747e4fSDavid du Colombier error("usage: secret hashalg encalg isclient secretdata");
16189a747e4fSDavid du Colombier if(tr->c == nil || !tr->verset)
16199a747e4fSDavid du Colombier error("must set fd and version before secrets");
16209a747e4fSDavid du Colombier
16219a747e4fSDavid du Colombier if(tr->in.new != nil){
16229a747e4fSDavid du Colombier freeSec(tr->in.new);
16239a747e4fSDavid du Colombier tr->in.new = nil;
16249a747e4fSDavid du Colombier }
16259a747e4fSDavid du Colombier if(tr->out.new != nil){
16269a747e4fSDavid du Colombier freeSec(tr->out.new);
16279a747e4fSDavid du Colombier tr->out.new = nil;
16289a747e4fSDavid du Colombier }
16299a747e4fSDavid du Colombier
16309a747e4fSDavid du Colombier ha = parsehashalg(cb->f[1]);
16319a747e4fSDavid du Colombier ea = parseencalg(cb->f[2]);
16329a747e4fSDavid du Colombier
16339a747e4fSDavid du Colombier p = cb->f[4];
16349a747e4fSDavid du Colombier m = (strlen(p)*3)/2;
16359a747e4fSDavid du Colombier x = smalloc(m);
16369a747e4fSDavid du Colombier tos = nil;
16379a747e4fSDavid du Colombier toc = nil;
16389a747e4fSDavid du Colombier if(waserror()){
16399a747e4fSDavid du Colombier freeSec(tos);
16409a747e4fSDavid du Colombier freeSec(toc);
16419a747e4fSDavid du Colombier free(x);
16429a747e4fSDavid du Colombier nexterror();
16439a747e4fSDavid du Colombier }
16449a747e4fSDavid du Colombier m = dec64(x, m, p, strlen(p));
16459a747e4fSDavid du Colombier if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen)
16469a747e4fSDavid du Colombier error("not enough secret data provided");
16479a747e4fSDavid du Colombier
16489a747e4fSDavid du Colombier tos = smalloc(sizeof(Secret));
16499a747e4fSDavid du Colombier toc = smalloc(sizeof(Secret));
16509a747e4fSDavid du Colombier if(!ha->initkey || !ea->initkey)
16519a747e4fSDavid du Colombier error("misimplemented secret algorithm");
16529a747e4fSDavid du Colombier (*ha->initkey)(ha, tr->version, tos, &x[0]);
16539a747e4fSDavid du Colombier (*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]);
16549a747e4fSDavid du Colombier (*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
16559a747e4fSDavid du Colombier (*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
16569a747e4fSDavid du Colombier
16579a747e4fSDavid du Colombier if(!tos->mac || !tos->enc || !tos->dec
16589a747e4fSDavid du Colombier || !toc->mac || !toc->enc || !toc->dec)
16599a747e4fSDavid du Colombier error("missing algorithm implementations");
16609a747e4fSDavid du Colombier if(strtol(cb->f[3], nil, 0) == 0){
16619a747e4fSDavid du Colombier tr->in.new = tos;
16629a747e4fSDavid du Colombier tr->out.new = toc;
16639a747e4fSDavid du Colombier }else{
16649a747e4fSDavid du Colombier tr->in.new = toc;
16659a747e4fSDavid du Colombier tr->out.new = tos;
16669a747e4fSDavid du Colombier }
16679a747e4fSDavid du Colombier if(tr->version == SSL3Version){
16689a747e4fSDavid du Colombier toc->unpad = sslunpad;
16699a747e4fSDavid du Colombier tos->unpad = sslunpad;
16709a747e4fSDavid du Colombier }else{
16719a747e4fSDavid du Colombier toc->unpad = tlsunpad;
16729a747e4fSDavid du Colombier tos->unpad = tlsunpad;
16739a747e4fSDavid du Colombier }
16749a747e4fSDavid du Colombier toc->encalg = ea->name;
16759a747e4fSDavid du Colombier toc->hashalg = ha->name;
16769a747e4fSDavid du Colombier tos->encalg = ea->name;
16779a747e4fSDavid du Colombier tos->hashalg = ha->name;
16789a747e4fSDavid du Colombier
16799a747e4fSDavid du Colombier free(x);
16809a747e4fSDavid du Colombier poperror();
16819a747e4fSDavid du Colombier }else if(strcmp(cb->f[0], "changecipher") == 0){
16829a747e4fSDavid du Colombier if(cb->nf != 1)
16839a747e4fSDavid du Colombier error("usage: changecipher");
16849a747e4fSDavid du Colombier if(tr->out.new == nil)
16858cd4f5a6SDavid du Colombier error("cannot change cipher spec without setting secret");
16869a747e4fSDavid du Colombier
16879a747e4fSDavid du Colombier qunlock(&tr->in.seclock);
16889a747e4fSDavid du Colombier qunlock(&tr->out.seclock);
16899a747e4fSDavid du Colombier poperror();
16909a747e4fSDavid du Colombier free(cb);
16919a747e4fSDavid du Colombier poperror();
16929a747e4fSDavid du Colombier
16939a747e4fSDavid du Colombier /*
16949a747e4fSDavid du Colombier * the real work is done as the message is written
16959a747e4fSDavid du Colombier * so the stream is encrypted in sync.
16969a747e4fSDavid du Colombier */
16979a747e4fSDavid du Colombier b = allocb(1);
16989a747e4fSDavid du Colombier *b->wp++ = 1;
16999a747e4fSDavid du Colombier tlsrecwrite(tr, RChangeCipherSpec, b);
17009a747e4fSDavid du Colombier return n;
17019a747e4fSDavid du Colombier }else if(strcmp(cb->f[0], "opened") == 0){
17029a747e4fSDavid du Colombier if(cb->nf != 1)
17039a747e4fSDavid du Colombier error("usage: opened");
17049a747e4fSDavid du Colombier if(tr->in.sec == nil || tr->out.sec == nil)
17059a747e4fSDavid du Colombier error("cipher must be configured before enabling data messages");
17069a747e4fSDavid du Colombier lock(&tr->statelk);
17079a747e4fSDavid du Colombier if(tr->state != SHandshake && tr->state != SOpen){
17089a747e4fSDavid du Colombier unlock(&tr->statelk);
17098cd4f5a6SDavid du Colombier error("cannot enable data messages");
17109a747e4fSDavid du Colombier }
17119a747e4fSDavid du Colombier tr->state = SOpen;
17129a747e4fSDavid du Colombier unlock(&tr->statelk);
17139a747e4fSDavid du Colombier tr->opened = 1;
17149a747e4fSDavid du Colombier }else if(strcmp(cb->f[0], "alert") == 0){
17159a747e4fSDavid du Colombier if(cb->nf != 2)
17169a747e4fSDavid du Colombier error("usage: alert n");
17179a747e4fSDavid du Colombier if(tr->c == nil)
17189a747e4fSDavid du Colombier error("must set fd before sending alerts");
17199a747e4fSDavid du Colombier m = strtol(cb->f[1], nil, 0);
17209a747e4fSDavid du Colombier
17219a747e4fSDavid du Colombier qunlock(&tr->in.seclock);
17229a747e4fSDavid du Colombier qunlock(&tr->out.seclock);
17239a747e4fSDavid du Colombier poperror();
17249a747e4fSDavid du Colombier free(cb);
17259a747e4fSDavid du Colombier poperror();
17269a747e4fSDavid du Colombier
17279a747e4fSDavid du Colombier sendAlert(tr, m);
17289a747e4fSDavid du Colombier
17299a747e4fSDavid du Colombier if(m == ECloseNotify)
17309a747e4fSDavid du Colombier tlsclosed(tr, SLClose);
17319a747e4fSDavid du Colombier
17329a747e4fSDavid du Colombier return n;
17333751babcSDavid du Colombier } else if(strcmp(cb->f[0], "debug") == 0){
17343751babcSDavid du Colombier if(cb->nf == 2){
17353751babcSDavid du Colombier if(strcmp(cb->f[1], "on") == 0)
17363751babcSDavid du Colombier tr->debug = 1;
17373751babcSDavid du Colombier else
17383751babcSDavid du Colombier tr->debug = 0;
17393751babcSDavid du Colombier } else
17403751babcSDavid du Colombier tr->debug = 1;
17419a747e4fSDavid du Colombier } else
17429a747e4fSDavid du Colombier error(Ebadarg);
17439a747e4fSDavid du Colombier
17449a747e4fSDavid du Colombier qunlock(&tr->in.seclock);
17459a747e4fSDavid du Colombier qunlock(&tr->out.seclock);
17469a747e4fSDavid du Colombier poperror();
17479a747e4fSDavid du Colombier free(cb);
17489a747e4fSDavid du Colombier poperror();
17499a747e4fSDavid du Colombier
17509a747e4fSDavid du Colombier return n;
17519a747e4fSDavid du Colombier }
17529a747e4fSDavid du Colombier
17539a747e4fSDavid du Colombier static void
tlsinit(void)17549a747e4fSDavid du Colombier tlsinit(void)
17559a747e4fSDavid du Colombier {
17569a747e4fSDavid du Colombier struct Encalg *e;
17579a747e4fSDavid du Colombier struct Hashalg *h;
17589a747e4fSDavid du Colombier int n;
17599a747e4fSDavid du Colombier char *cp;
17603751babcSDavid du Colombier static int already;
17613751babcSDavid du Colombier
17623751babcSDavid du Colombier if(!already){
17633751babcSDavid du Colombier fmtinstall('H', encodefmt);
17643751babcSDavid du Colombier already = 1;
17653751babcSDavid du Colombier }
17669a747e4fSDavid du Colombier
17679a747e4fSDavid du Colombier tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs);
17689a747e4fSDavid du Colombier trnames = smalloc((sizeof *trnames) * maxtlsdevs);
17699a747e4fSDavid du Colombier
17709a747e4fSDavid du Colombier n = 1;
17719a747e4fSDavid du Colombier for(e = encrypttab; e->name != nil; e++)
17729a747e4fSDavid du Colombier n += strlen(e->name) + 1;
17739a747e4fSDavid du Colombier cp = encalgs = smalloc(n);
17749a747e4fSDavid du Colombier for(e = encrypttab;;){
17759a747e4fSDavid du Colombier strcpy(cp, e->name);
17769a747e4fSDavid du Colombier cp += strlen(e->name);
17779a747e4fSDavid du Colombier e++;
17789a747e4fSDavid du Colombier if(e->name == nil)
17799a747e4fSDavid du Colombier break;
17809a747e4fSDavid du Colombier *cp++ = ' ';
17819a747e4fSDavid du Colombier }
17829a747e4fSDavid du Colombier *cp = 0;
17839a747e4fSDavid du Colombier
17849a747e4fSDavid du Colombier n = 1;
17859a747e4fSDavid du Colombier for(h = hashtab; h->name != nil; h++)
17869a747e4fSDavid du Colombier n += strlen(h->name) + 1;
17879a747e4fSDavid du Colombier cp = hashalgs = smalloc(n);
17889a747e4fSDavid du Colombier for(h = hashtab;;){
17899a747e4fSDavid du Colombier strcpy(cp, h->name);
17909a747e4fSDavid du Colombier cp += strlen(h->name);
17919a747e4fSDavid du Colombier h++;
17929a747e4fSDavid du Colombier if(h->name == nil)
17939a747e4fSDavid du Colombier break;
17949a747e4fSDavid du Colombier *cp++ = ' ';
17959a747e4fSDavid du Colombier }
17969a747e4fSDavid du Colombier *cp = 0;
17979a747e4fSDavid du Colombier }
17989a747e4fSDavid du Colombier
17999a747e4fSDavid du Colombier Dev tlsdevtab = {
18009a747e4fSDavid du Colombier 'a',
18019a747e4fSDavid du Colombier "tls",
18029a747e4fSDavid du Colombier
18039a747e4fSDavid du Colombier devreset,
18049a747e4fSDavid du Colombier tlsinit,
18059a747e4fSDavid du Colombier devshutdown,
18069a747e4fSDavid du Colombier tlsattach,
18079a747e4fSDavid du Colombier tlswalk,
18089a747e4fSDavid du Colombier tlsstat,
18099a747e4fSDavid du Colombier tlsopen,
18109a747e4fSDavid du Colombier devcreate,
18119a747e4fSDavid du Colombier tlsclose,
18129a747e4fSDavid du Colombier tlsread,
18139a747e4fSDavid du Colombier tlsbread,
18149a747e4fSDavid du Colombier tlswrite,
18159a747e4fSDavid du Colombier tlsbwrite,
18169a747e4fSDavid du Colombier devremove,
18179a747e4fSDavid du Colombier tlswstat,
18189a747e4fSDavid du Colombier };
18199a747e4fSDavid du Colombier
18209a747e4fSDavid du Colombier /* get channel associated with an fd */
18219a747e4fSDavid du Colombier static Chan*
buftochan(char * p)18229a747e4fSDavid du Colombier buftochan(char *p)
18239a747e4fSDavid du Colombier {
18249a747e4fSDavid du Colombier Chan *c;
18259a747e4fSDavid du Colombier int fd;
18269a747e4fSDavid du Colombier
18279a747e4fSDavid du Colombier if(p == 0)
18289a747e4fSDavid du Colombier error(Ebadarg);
18299a747e4fSDavid du Colombier fd = strtoul(p, 0, 0);
18309a747e4fSDavid du Colombier if(fd < 0)
18319a747e4fSDavid du Colombier error(Ebadarg);
18329a747e4fSDavid du Colombier c = fdtochan(fd, -1, 0, 1); /* error check and inc ref */
18339a747e4fSDavid du Colombier return c;
18349a747e4fSDavid du Colombier }
18359a747e4fSDavid du Colombier
18369a747e4fSDavid du Colombier static void
sendAlert(TlsRec * tr,int err)18379a747e4fSDavid du Colombier sendAlert(TlsRec *tr, int err)
18389a747e4fSDavid du Colombier {
18399a747e4fSDavid du Colombier Block *b;
18409a747e4fSDavid du Colombier int i, fatal;
18419a747e4fSDavid du Colombier char *msg;
18429a747e4fSDavid du Colombier
18433751babcSDavid du Colombier if(tr->debug)pprint("sendAlert %d\n", err);
18449a747e4fSDavid du Colombier fatal = 1;
18459a747e4fSDavid du Colombier msg = "tls unknown alert";
18469a747e4fSDavid du Colombier for(i=0; i < nelem(tlserrs); i++) {
18479a747e4fSDavid du Colombier if(tlserrs[i].err == err) {
18489a747e4fSDavid du Colombier msg = tlserrs[i].msg;
18499a747e4fSDavid du Colombier if(tr->version == SSL3Version)
18509a747e4fSDavid du Colombier err = tlserrs[i].sslerr;
18519a747e4fSDavid du Colombier else
18529a747e4fSDavid du Colombier err = tlserrs[i].tlserr;
18539a747e4fSDavid du Colombier fatal = tlserrs[i].fatal;
18549a747e4fSDavid du Colombier break;
18559a747e4fSDavid du Colombier }
18569a747e4fSDavid du Colombier }
18579a747e4fSDavid du Colombier
18589a747e4fSDavid du Colombier if(!waserror()){
18599a747e4fSDavid du Colombier b = allocb(2);
18609a747e4fSDavid du Colombier *b->wp++ = fatal + 1;
18619a747e4fSDavid du Colombier *b->wp++ = err;
18629a747e4fSDavid du Colombier if(fatal)
18639a747e4fSDavid du Colombier tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose);
18649a747e4fSDavid du Colombier tlsrecwrite(tr, RAlert, b);
18659a747e4fSDavid du Colombier poperror();
18669a747e4fSDavid du Colombier }
18679a747e4fSDavid du Colombier if(fatal)
18689a747e4fSDavid du Colombier tlsError(tr, msg);
18699a747e4fSDavid du Colombier }
18709a747e4fSDavid du Colombier
18719a747e4fSDavid du Colombier static void
tlsError(TlsRec * tr,char * msg)18729a747e4fSDavid du Colombier tlsError(TlsRec *tr, char *msg)
18739a747e4fSDavid du Colombier {
18749a747e4fSDavid du Colombier int s;
18759a747e4fSDavid du Colombier
18763751babcSDavid du Colombier if(tr->debug)pprint("tleError %s\n", msg);
18779a747e4fSDavid du Colombier lock(&tr->statelk);
18789a747e4fSDavid du Colombier s = tr->state;
18799a747e4fSDavid du Colombier tr->state = SError;
18809a747e4fSDavid du Colombier if(s != SError){
18819a747e4fSDavid du Colombier strncpy(tr->err, msg, ERRMAX - 1);
18829a747e4fSDavid du Colombier tr->err[ERRMAX - 1] = '\0';
18839a747e4fSDavid du Colombier }
18849a747e4fSDavid du Colombier unlock(&tr->statelk);
18859a747e4fSDavid du Colombier if(s != SError)
18869a747e4fSDavid du Colombier alertHand(tr, msg);
18879a747e4fSDavid du Colombier }
18889a747e4fSDavid du Colombier
18899a747e4fSDavid du Colombier static void
tlsSetState(TlsRec * tr,int new,int old)18909a747e4fSDavid du Colombier tlsSetState(TlsRec *tr, int new, int old)
18919a747e4fSDavid du Colombier {
18929a747e4fSDavid du Colombier lock(&tr->statelk);
18939a747e4fSDavid du Colombier if(tr->state & old)
18949a747e4fSDavid du Colombier tr->state = new;
18959a747e4fSDavid du Colombier unlock(&tr->statelk);
18969a747e4fSDavid du Colombier }
18979a747e4fSDavid du Colombier
18989a747e4fSDavid du Colombier /* hand up a digest connection */
18999a747e4fSDavid du Colombier static void
tlshangup(TlsRec * tr)19009a747e4fSDavid du Colombier tlshangup(TlsRec *tr)
19019a747e4fSDavid du Colombier {
19029a747e4fSDavid du Colombier Block *b;
19039a747e4fSDavid du Colombier
19049a747e4fSDavid du Colombier qlock(&tr->in.io);
19059a747e4fSDavid du Colombier for(b = tr->processed; b; b = tr->processed){
19069a747e4fSDavid du Colombier tr->processed = b->next;
19079a747e4fSDavid du Colombier freeb(b);
19089a747e4fSDavid du Colombier }
19099a747e4fSDavid du Colombier if(tr->unprocessed != nil){
19109a747e4fSDavid du Colombier freeb(tr->unprocessed);
19119a747e4fSDavid du Colombier tr->unprocessed = nil;
19129a747e4fSDavid du Colombier }
19139a747e4fSDavid du Colombier qunlock(&tr->in.io);
19149a747e4fSDavid du Colombier
19159a747e4fSDavid du Colombier tlsSetState(tr, SClosed, ~0);
19169a747e4fSDavid du Colombier }
19179a747e4fSDavid du Colombier
19189a747e4fSDavid du Colombier static TlsRec*
newtls(Chan * ch)19199a747e4fSDavid du Colombier newtls(Chan *ch)
19209a747e4fSDavid du Colombier {
19219a747e4fSDavid du Colombier TlsRec **pp, **ep, **np;
19229a747e4fSDavid du Colombier char **nmp;
19239a747e4fSDavid du Colombier int t, newmax;
19249a747e4fSDavid du Colombier
19259a747e4fSDavid du Colombier if(waserror()) {
19269a747e4fSDavid du Colombier unlock(&tdlock);
19279a747e4fSDavid du Colombier nexterror();
19289a747e4fSDavid du Colombier }
19299a747e4fSDavid du Colombier lock(&tdlock);
19309a747e4fSDavid du Colombier ep = &tlsdevs[maxtlsdevs];
19319a747e4fSDavid du Colombier for(pp = tlsdevs; pp < ep; pp++)
19329a747e4fSDavid du Colombier if(*pp == nil)
19339a747e4fSDavid du Colombier break;
19349a747e4fSDavid du Colombier if(pp >= ep) {
19359a747e4fSDavid du Colombier if(maxtlsdevs >= MaxTlsDevs) {
19369a747e4fSDavid du Colombier unlock(&tdlock);
19379a747e4fSDavid du Colombier poperror();
19389a747e4fSDavid du Colombier return nil;
19399a747e4fSDavid du Colombier }
19409a747e4fSDavid du Colombier newmax = 2 * maxtlsdevs;
19419a747e4fSDavid du Colombier if(newmax > MaxTlsDevs)
19429a747e4fSDavid du Colombier newmax = MaxTlsDevs;
19439a747e4fSDavid du Colombier np = smalloc(sizeof(TlsRec*) * newmax);
19449a747e4fSDavid du Colombier memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs);
19459a747e4fSDavid du Colombier tlsdevs = np;
19469a747e4fSDavid du Colombier pp = &tlsdevs[maxtlsdevs];
19479a747e4fSDavid du Colombier memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs));
19489a747e4fSDavid du Colombier
19499a747e4fSDavid du Colombier nmp = smalloc(sizeof *nmp * newmax);
19509a747e4fSDavid du Colombier memmove(nmp, trnames, sizeof *nmp * maxtlsdevs);
19519a747e4fSDavid du Colombier trnames = nmp;
19529a747e4fSDavid du Colombier
19539a747e4fSDavid du Colombier maxtlsdevs = newmax;
19549a747e4fSDavid du Colombier }
19559a747e4fSDavid du Colombier *pp = mktlsrec();
19569a747e4fSDavid du Colombier if(pp - tlsdevs >= tdhiwat)
19579a747e4fSDavid du Colombier tdhiwat++;
19589a747e4fSDavid du Colombier t = TYPE(ch->qid);
19599a747e4fSDavid du Colombier if(t == Qclonus)
19609a747e4fSDavid du Colombier t = Qctl;
19619a747e4fSDavid du Colombier ch->qid.path = QID(pp - tlsdevs, t);
19629a747e4fSDavid du Colombier ch->qid.vers = 0;
19639a747e4fSDavid du Colombier unlock(&tdlock);
19649a747e4fSDavid du Colombier poperror();
19659a747e4fSDavid du Colombier return *pp;
19669a747e4fSDavid du Colombier }
19679a747e4fSDavid du Colombier
19689a747e4fSDavid du Colombier static TlsRec *
mktlsrec(void)19699a747e4fSDavid du Colombier mktlsrec(void)
19709a747e4fSDavid du Colombier {
19719a747e4fSDavid du Colombier TlsRec *tr;
19729a747e4fSDavid du Colombier
19739a747e4fSDavid du Colombier tr = mallocz(sizeof(*tr), 1);
19749a747e4fSDavid du Colombier if(tr == nil)
19759a747e4fSDavid du Colombier error(Enomem);
19769a747e4fSDavid du Colombier tr->state = SClosed;
19779a747e4fSDavid du Colombier tr->ref = 1;
19789a747e4fSDavid du Colombier kstrdup(&tr->user, up->user);
19799a747e4fSDavid du Colombier tr->perm = 0660;
19809a747e4fSDavid du Colombier return tr;
19819a747e4fSDavid du Colombier }
19829a747e4fSDavid du Colombier
19839a747e4fSDavid du Colombier static char*
tlsstate(int s)19849a747e4fSDavid du Colombier tlsstate(int s)
19859a747e4fSDavid du Colombier {
19869a747e4fSDavid du Colombier switch(s){
19879a747e4fSDavid du Colombier case SHandshake:
19889a747e4fSDavid du Colombier return "Handshaking";
19899a747e4fSDavid du Colombier case SOpen:
19909a747e4fSDavid du Colombier return "Established";
19919a747e4fSDavid du Colombier case SRClose:
19929a747e4fSDavid du Colombier return "RemoteClosed";
19939a747e4fSDavid du Colombier case SLClose:
19949a747e4fSDavid du Colombier return "LocalClosed";
19959a747e4fSDavid du Colombier case SAlert:
19969a747e4fSDavid du Colombier return "Alerting";
19979a747e4fSDavid du Colombier case SError:
19989a747e4fSDavid du Colombier return "Errored";
19999a747e4fSDavid du Colombier case SClosed:
20009a747e4fSDavid du Colombier return "Closed";
20019a747e4fSDavid du Colombier }
20029a747e4fSDavid du Colombier return "Unknown";
20039a747e4fSDavid du Colombier }
20049a747e4fSDavid du Colombier
20059a747e4fSDavid du Colombier static void
freeSec(Secret * s)20069a747e4fSDavid du Colombier freeSec(Secret *s)
20079a747e4fSDavid du Colombier {
20089a747e4fSDavid du Colombier if(s != nil){
20099a747e4fSDavid du Colombier free(s->enckey);
20109a747e4fSDavid du Colombier free(s);
20119a747e4fSDavid du Colombier }
20129a747e4fSDavid du Colombier }
20139a747e4fSDavid du Colombier
20149a747e4fSDavid du Colombier static int
noenc(Secret *,uchar *,int n)20159a747e4fSDavid du Colombier noenc(Secret *, uchar *, int n)
20169a747e4fSDavid du Colombier {
20179a747e4fSDavid du Colombier return n;
20189a747e4fSDavid du Colombier }
20199a747e4fSDavid du Colombier
20209a747e4fSDavid du Colombier static int
rc4enc(Secret * sec,uchar * buf,int n)20219a747e4fSDavid du Colombier rc4enc(Secret *sec, uchar *buf, int n)
20229a747e4fSDavid du Colombier {
20239a747e4fSDavid du Colombier rc4(sec->enckey, buf, n);
20249a747e4fSDavid du Colombier return n;
20259a747e4fSDavid du Colombier }
20269a747e4fSDavid du Colombier
20279a747e4fSDavid du Colombier static int
tlsunpad(uchar * buf,int n,int block)20289a747e4fSDavid du Colombier tlsunpad(uchar *buf, int n, int block)
20299a747e4fSDavid du Colombier {
20309a747e4fSDavid du Colombier int pad, nn;
20319a747e4fSDavid du Colombier
20329a747e4fSDavid du Colombier pad = buf[n - 1];
20339a747e4fSDavid du Colombier nn = n - 1 - pad;
20349a747e4fSDavid du Colombier if(nn <= 0 || n % block)
20359a747e4fSDavid du Colombier return -1;
20369a747e4fSDavid du Colombier while(--n > nn)
20379a747e4fSDavid du Colombier if(pad != buf[n - 1])
20389a747e4fSDavid du Colombier return -1;
20399a747e4fSDavid du Colombier return nn;
20409a747e4fSDavid du Colombier }
20419a747e4fSDavid du Colombier
20429a747e4fSDavid du Colombier static int
sslunpad(uchar * buf,int n,int block)20439a747e4fSDavid du Colombier sslunpad(uchar *buf, int n, int block)
20449a747e4fSDavid du Colombier {
20459a747e4fSDavid du Colombier int pad, nn;
20469a747e4fSDavid du Colombier
20479a747e4fSDavid du Colombier pad = buf[n - 1];
20489a747e4fSDavid du Colombier nn = n - 1 - pad;
20499a747e4fSDavid du Colombier if(nn <= 0 || n % block)
20509a747e4fSDavid du Colombier return -1;
20519a747e4fSDavid du Colombier return nn;
20529a747e4fSDavid du Colombier }
20539a747e4fSDavid du Colombier
20549a747e4fSDavid du Colombier static int
blockpad(uchar * buf,int n,int block)20559a747e4fSDavid du Colombier blockpad(uchar *buf, int n, int block)
20569a747e4fSDavid du Colombier {
20579a747e4fSDavid du Colombier int pad, nn;
20589a747e4fSDavid du Colombier
20599a747e4fSDavid du Colombier nn = n + block;
20609a747e4fSDavid du Colombier nn -= nn % block;
20619a747e4fSDavid du Colombier pad = nn - (n + 1);
20629a747e4fSDavid du Colombier while(n < nn)
20639a747e4fSDavid du Colombier buf[n++] = pad;
20649a747e4fSDavid du Colombier return nn;
20659a747e4fSDavid du Colombier }
20669a747e4fSDavid du Colombier
20679a747e4fSDavid du Colombier static int
des3enc(Secret * sec,uchar * buf,int n)20689a747e4fSDavid du Colombier des3enc(Secret *sec, uchar *buf, int n)
20699a747e4fSDavid du Colombier {
20709a747e4fSDavid du Colombier n = blockpad(buf, n, 8);
20719a747e4fSDavid du Colombier des3CBCencrypt(buf, n, sec->enckey);
20729a747e4fSDavid du Colombier return n;
20739a747e4fSDavid du Colombier }
20749a747e4fSDavid du Colombier
20759a747e4fSDavid du Colombier static int
des3dec(Secret * sec,uchar * buf,int n)20769a747e4fSDavid du Colombier des3dec(Secret *sec, uchar *buf, int n)
20779a747e4fSDavid du Colombier {
20789a747e4fSDavid du Colombier des3CBCdecrypt(buf, n, sec->enckey);
20799a747e4fSDavid du Colombier return (*sec->unpad)(buf, n, 8);
20809a747e4fSDavid du Colombier }
2081ad6ca847SDavid du Colombier
2082ad6ca847SDavid du Colombier static int
aesenc(Secret * sec,uchar * buf,int n)2083ad6ca847SDavid du Colombier aesenc(Secret *sec, uchar *buf, int n)
2084ad6ca847SDavid du Colombier {
2085ad6ca847SDavid du Colombier n = blockpad(buf, n, 16);
2086ad6ca847SDavid du Colombier aesCBCencrypt(buf, n, sec->enckey);
2087ad6ca847SDavid du Colombier return n;
2088ad6ca847SDavid du Colombier }
2089ad6ca847SDavid du Colombier
2090ad6ca847SDavid du Colombier static int
aesdec(Secret * sec,uchar * buf,int n)2091ad6ca847SDavid du Colombier aesdec(Secret *sec, uchar *buf, int n)
2092ad6ca847SDavid du Colombier {
2093ad6ca847SDavid du Colombier aesCBCdecrypt(buf, n, sec->enckey);
2094ad6ca847SDavid du Colombier return (*sec->unpad)(buf, n, 16);
2095ad6ca847SDavid du Colombier }
2096ad6ca847SDavid du Colombier
20979a747e4fSDavid du Colombier static DigestState*
nomac(uchar *,ulong,uchar *,ulong,uchar *,DigestState *)20989a747e4fSDavid du Colombier nomac(uchar *, ulong, uchar *, ulong, uchar *, DigestState *)
20999a747e4fSDavid du Colombier {
21009a747e4fSDavid du Colombier return nil;
21019a747e4fSDavid du Colombier }
21029a747e4fSDavid du Colombier
21039a747e4fSDavid du Colombier /*
21049a747e4fSDavid du Colombier * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac.
21059a747e4fSDavid du Colombier */
21069a747e4fSDavid 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)21079a747e4fSDavid du Colombier sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s,
21089a747e4fSDavid du Colombier DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen)
21099a747e4fSDavid du Colombier {
21109a747e4fSDavid du Colombier int i;
21119a747e4fSDavid du Colombier uchar pad[48], innerdigest[20];
21129a747e4fSDavid du Colombier
21139a747e4fSDavid du Colombier if(xlen > sizeof(innerdigest)
21149a747e4fSDavid du Colombier || padlen > sizeof(pad))
21159a747e4fSDavid du Colombier return nil;
21169a747e4fSDavid du Colombier
21179a747e4fSDavid du Colombier if(klen>64)
21189a747e4fSDavid du Colombier return nil;
21199a747e4fSDavid du Colombier
21209a747e4fSDavid du Colombier /* first time through */
21219a747e4fSDavid du Colombier if(s == nil){
21229a747e4fSDavid du Colombier for(i=0; i<padlen; i++)
21239a747e4fSDavid du Colombier pad[i] = 0x36;
21249a747e4fSDavid du Colombier s = (*x)(key, klen, nil, nil);
21259a747e4fSDavid du Colombier s = (*x)(pad, padlen, nil, s);
21269a747e4fSDavid du Colombier if(s == nil)
21279a747e4fSDavid du Colombier return nil;
21289a747e4fSDavid du Colombier }
21299a747e4fSDavid du Colombier
21309a747e4fSDavid du Colombier s = (*x)(p, len, nil, s);
21319a747e4fSDavid du Colombier if(digest == nil)
21329a747e4fSDavid du Colombier return s;
21339a747e4fSDavid du Colombier
21349a747e4fSDavid du Colombier /* last time through */
21359a747e4fSDavid du Colombier for(i=0; i<padlen; i++)
21369a747e4fSDavid du Colombier pad[i] = 0x5c;
21379a747e4fSDavid du Colombier (*x)(nil, 0, innerdigest, s);
21389a747e4fSDavid du Colombier s = (*x)(key, klen, nil, nil);
21399a747e4fSDavid du Colombier s = (*x)(pad, padlen, nil, s);
21409a747e4fSDavid du Colombier (*x)(innerdigest, xlen, digest, s);
21419a747e4fSDavid du Colombier return nil;
21429a747e4fSDavid du Colombier }
21439a747e4fSDavid du Colombier
21449a747e4fSDavid du Colombier static DigestState*
sslmac_sha1(uchar * p,ulong len,uchar * key,ulong klen,uchar * digest,DigestState * s)21459a747e4fSDavid du Colombier sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
21469a747e4fSDavid du Colombier {
21479a747e4fSDavid du Colombier return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40);
21489a747e4fSDavid du Colombier }
21499a747e4fSDavid du Colombier
21509a747e4fSDavid du Colombier static DigestState*
sslmac_md5(uchar * p,ulong len,uchar * key,ulong klen,uchar * digest,DigestState * s)21519a747e4fSDavid du Colombier sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
21529a747e4fSDavid du Colombier {
21539a747e4fSDavid du Colombier return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48);
21549a747e4fSDavid du Colombier }
21559a747e4fSDavid du Colombier
21569a747e4fSDavid du Colombier static void
sslPackMac(Secret * sec,uchar * mackey,uchar * seq,uchar * header,uchar * body,int len,uchar * mac)21579a747e4fSDavid du Colombier sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
21589a747e4fSDavid du Colombier {
21599a747e4fSDavid du Colombier DigestState *s;
21609a747e4fSDavid du Colombier uchar buf[11];
21619a747e4fSDavid du Colombier
21629a747e4fSDavid du Colombier memmove(buf, seq, 8);
21639a747e4fSDavid du Colombier buf[8] = header[0];
21649a747e4fSDavid du Colombier buf[9] = header[3];
21659a747e4fSDavid du Colombier buf[10] = header[4];
21669a747e4fSDavid du Colombier
21679a747e4fSDavid du Colombier s = (*sec->mac)(buf, 11, mackey, sec->maclen, 0, 0);
21689a747e4fSDavid du Colombier (*sec->mac)(body, len, mackey, sec->maclen, mac, s);
21699a747e4fSDavid du Colombier }
21709a747e4fSDavid du Colombier
21719a747e4fSDavid du Colombier static void
tlsPackMac(Secret * sec,uchar * mackey,uchar * seq,uchar * header,uchar * body,int len,uchar * mac)21729a747e4fSDavid du Colombier tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
21739a747e4fSDavid du Colombier {
21749a747e4fSDavid du Colombier DigestState *s;
21759a747e4fSDavid du Colombier uchar buf[13];
21769a747e4fSDavid du Colombier
21779a747e4fSDavid du Colombier memmove(buf, seq, 8);
21789a747e4fSDavid du Colombier memmove(&buf[8], header, 5);
21799a747e4fSDavid du Colombier
21809a747e4fSDavid du Colombier s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0);
21819a747e4fSDavid du Colombier (*sec->mac)(body, len, mackey, sec->maclen, mac, s);
21829a747e4fSDavid du Colombier }
21839a747e4fSDavid du Colombier
21849a747e4fSDavid du Colombier static void
put32(uchar * p,u32int x)21859a747e4fSDavid du Colombier put32(uchar *p, u32int x)
21869a747e4fSDavid du Colombier {
21879a747e4fSDavid du Colombier p[0] = x>>24;
21889a747e4fSDavid du Colombier p[1] = x>>16;
21899a747e4fSDavid du Colombier p[2] = x>>8;
21909a747e4fSDavid du Colombier p[3] = x;
21919a747e4fSDavid du Colombier }
21929a747e4fSDavid du Colombier
21939a747e4fSDavid du Colombier static void
put64(uchar * p,vlong x)21949a747e4fSDavid du Colombier put64(uchar *p, vlong x)
21959a747e4fSDavid du Colombier {
21969a747e4fSDavid du Colombier put32(p, (u32int)(x >> 32));
21979a747e4fSDavid du Colombier put32(p+4, (u32int)x);
21989a747e4fSDavid du Colombier }
21999a747e4fSDavid du Colombier
22009a747e4fSDavid du Colombier static void
put24(uchar * p,int x)22019a747e4fSDavid du Colombier put24(uchar *p, int x)
22029a747e4fSDavid du Colombier {
22039a747e4fSDavid du Colombier p[0] = x>>16;
22049a747e4fSDavid du Colombier p[1] = x>>8;
22059a747e4fSDavid du Colombier p[2] = x;
22069a747e4fSDavid du Colombier }
22079a747e4fSDavid du Colombier
22089a747e4fSDavid du Colombier static void
put16(uchar * p,int x)22099a747e4fSDavid du Colombier put16(uchar *p, int x)
22109a747e4fSDavid du Colombier {
22119a747e4fSDavid du Colombier p[0] = x>>8;
22129a747e4fSDavid du Colombier p[1] = x;
22139a747e4fSDavid du Colombier }
22149a747e4fSDavid du Colombier
22159a747e4fSDavid du Colombier static u32int
get32(uchar * p)22169a747e4fSDavid du Colombier get32(uchar *p)
22179a747e4fSDavid du Colombier {
22189a747e4fSDavid du Colombier return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
22199a747e4fSDavid du Colombier }
22209a747e4fSDavid du Colombier
22219a747e4fSDavid du Colombier static int
get16(uchar * p)22229a747e4fSDavid du Colombier get16(uchar *p)
22239a747e4fSDavid du Colombier {
22249a747e4fSDavid du Colombier return (p[0]<<8)|p[1];
22259a747e4fSDavid du Colombier }
22263751babcSDavid du Colombier
22273751babcSDavid du Colombier static char *charmap = "0123456789abcdef";
22283751babcSDavid du Colombier
22293751babcSDavid du Colombier static void
pdump(int len,void * a,char * tag)22303751babcSDavid du Colombier pdump(int len, void *a, char *tag)
22313751babcSDavid du Colombier {
22323751babcSDavid du Colombier uchar *p;
22333751babcSDavid du Colombier int i;
22343751babcSDavid du Colombier char buf[65+32];
22353751babcSDavid du Colombier char *q;
22363751babcSDavid du Colombier
22373751babcSDavid du Colombier p = a;
22383751babcSDavid du Colombier strcpy(buf, tag);
22393751babcSDavid du Colombier while(len > 0){
22403751babcSDavid du Colombier q = buf + strlen(tag);
22413751babcSDavid du Colombier for(i = 0; len > 0 && i < 32; i++){
22423751babcSDavid du Colombier if(*p >= ' ' && *p < 0x7f){
22433751babcSDavid du Colombier *q++ = ' ';
22443751babcSDavid du Colombier *q++ = *p;
22453751babcSDavid du Colombier } else {
22463751babcSDavid du Colombier *q++ = charmap[*p>>4];
22473751babcSDavid du Colombier *q++ = charmap[*p & 0xf];
22483751babcSDavid du Colombier }
22493751babcSDavid du Colombier len--;
22503751babcSDavid du Colombier p++;
22513751babcSDavid du Colombier }
22523751babcSDavid du Colombier *q = 0;
22533751babcSDavid du Colombier
22543751babcSDavid du Colombier if(len > 0)
22553751babcSDavid du Colombier pprint("%s...\n", buf);
22563751babcSDavid du Colombier else
22573751babcSDavid du Colombier pprint("%s\n", buf);
22583751babcSDavid du Colombier }
22593751babcSDavid du Colombier }
2260