Milán Major

netscape-revival

1 branch
Code

security/lib/ssl/sslgathr.c

#include "xp.h"
#include "cert.h"
#include "ssl.h"
#include "sslimpl.h"
#include "sslproto.h"

extern int XP_ERRNO_EAGAIN;
extern int XP_ERRNO_EIO;
extern int XP_ERRNO_EWOULDBLOCK;
extern int SEC_ERROR_IO;
extern int SSL_ERROR_NO_CYPHER_OVERLAP;
extern int SSL_ERROR_BAD_MAC_READ;

extern int ssl_HandleClientHelloMessage(SSLSocket *ss);
extern int ssl_HandleServerHelloMessage(SSLSocket *ss);

#ifdef XP_WIN
SOCKET __ssl_last_read_fd;
#endif

/*
** Gather a single record of data from the receiving stream. This code
** first gathers the header (2 or 3 bytes long depending on the value of
** the most significant bit in the first byte) then gathers up the data
** for the record into the readBuf. This code handles non-blocking I/O
** and is to be called multiple times until sec->recordLen != 0.
*/
int ssl_GatherData(SSLSocket *ss, SSLGather *gs, int flags)
{
    SSLSecurityInfo *sec = ss->sec;
    unsigned char *bp, *lbp;
    unsigned char mac[SSL_MAX_MAC_BYTES], seq[4];
    uint32 sequenceNumber;
    int nb, err, rv;

    if (gs->state == GS_INIT) {
	/* Initialize gathering engine */
	gs->state = GS_HEADER;
	gs->remainder = 2;
	gs->count = 2;
	gs->offset = 0;
	gs->recordLen = 0;
	gs->recordPadding = 0;
	gs->hdr[2] = 0;

	gs->writeOffset = 0;
	gs->readOffset = 0;
    }
    if (gs->encrypted) {
	PORT_Assert(sec != 0);
    }

#ifdef XP_WIN
    __ssl_last_read_fd = ss->fd;
#endif
    lbp = gs->buf.buf;
    for (;;) {
	SSL_TRC(30, ("%d: SSL[%d]: gather state %d (need %d more)",
		     SSL_GETPID(), ss->fd, gs->state, gs->remainder));
	bp = ((gs->state != GS_HEADER) ? lbp : gs->hdr) + gs->offset;
	nb = XP_SOCK_RECV(ss->fd, (void *)bp, gs->remainder, flags);
	if (nb > 0) {
	    PRINT_BUF(60, (ss, "raw gather data:", bp, nb));
	}
	if (nb == 0) {
	    /* EOF */
	    SSL_TRC(30, ("%d: SSL[%d]: EOF", SSL_GETPID(), ss->fd));
	    rv = 0;
	    break;
	}
	if (nb < 0) {
	    err = XP_SOCK_ERRNO;
	    PORT_SetError(err);
	    if ((err == XP_ERRNO_EWOULDBLOCK) || (err == XP_ERRNO_EAGAIN)) {
		/* Try again later */
		SSL_TRC(30, ("%d: SSL[%d]: gather blocked",
			     SSL_GETPID(), ss->fd));
		rv = -2;
		break;
	    }
	    SSL_DBG(("%d: SSL[%d]: recv error %d", SSL_GETPID(), ss->fd, err));
	    rv = -1;
	    break;
	}

	/*
	** Try to make some data available to the reader immediately.
	*/
	/*
	 * XXX Should not give data to application until the MAC has
	 * has been verified.
	 * PK 7 Dec 95
	 */
	gs->offset += nb;
	gs->remainder -= nb;
	if (gs->encrypted) {
	    int available;
	    unsigned nout;
	    unsigned char *ibp;

	    switch (gs->state) {
	      case GS_PAD:
	      case GS_MAC:
		/*
		** Only process padding or MAC data when it completely
		** comes in
		*/
		if (gs->remainder) break;
		/* FALL THROUGH */

	      case GS_DATA:
		available = ((gs->offset - gs->writeOffset)
			     >> sec->blockShift) << sec->blockShift;
		if (available == 0) break;

		/* Decrypt the portion of data that we just recieved */
		ibp = lbp + gs->writeOffset;
		rv = (*sec->dec)(sec->readcx, ibp, &nout, available,
				 ibp, available);
		if (rv) {
		    return rv;
		}

		/* Advance write counts */
		gs->writeOffset += available;
		if (gs->state == GS_MAC) {
		    /* Don't give MAC to reader! */
		    gs->readOffset += available;
		} else {
		    (*sec->hash->update)(sec->hashcx, ibp, available);
		}
		if (gs->state == GS_PAD) {
		    /* Don't give padding to reader! */
		    gs->writeOffset -= gs->recordPadding;
		}
		break;
	    }
	}
	
	if (gs->remainder != 0) {
	    continue;
	}

	/* Probably finished this piece */
	switch (gs->state) {
	  case GS_HEADER: 
	    /* See if we need more header */
	    if (ss->enableSSL3) {
		if (gs->hdr[0] == content_handshake) {
		    if ((ss->nextHandshake == ssl_HandleClientHelloMessage) ||
			(ss->nextHandshake == ssl_HandleServerHelloMessage)) {
			rv = ssl_HandleV3Hello(ss);
			if (rv == -1) {
			    return -1;
			}
		    }
		    return -2;
		} else if (gs->hdr[0] == content_alert) {
		    if (ss->nextHandshake == ssl_HandleServerHelloMessage) {
			/* XXX This is a hack.  We're assuming that any failure
			 * XXX on the client hello is a failure to match
			 * XXX ciphers.
			 */
			PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
			return -1;
		    }
		}
	    }
	    if (gs->hdr[0] & 0x80) {
		/* All done getting header. Now get all of the data. */
		if (gs->hdr[2]) {
		    gs->count = ((gs->hdr[0] & 0x3f) << 8) | gs->hdr[1];
		} else {
		    gs->count = ((gs->hdr[0] & 0x7f) << 8) | gs->hdr[1];
		}
		gs->recordPadding = gs->hdr[2];
		if (gs->encrypted) {
		    if (gs->count & (sec->blockSize - 1)) {
			/* This is an error. Sender is misbehaving */
			SSL_DBG(("%d: SSL[%d]: sender, count=%d blockSize=%d",
				 SSL_GETPID(), ss->fd, gs->count,
				 sec->blockSize));
			PORT_SetError(XP_ERRNO_EIO);
			return -1;
		    }
		    gs->remainder = sec->hash->length;
		    gs->state = GS_MAC;
		    gs->recordLen = gs->count - gs->recordPadding
			- sec->hash->length;
		} else {
		    gs->remainder = gs->count;
		    gs->state = GS_DATA;
		    gs->recordLen = gs->count;
		}
		gs->offset = 0;
		if (gs->count > gs->buf.space) {
		    err = ssl_GrowBuf(&gs->buf, gs->count);
		    if (err) {
			return err;
		    }
		    lbp = gs->buf.buf;
		}
	    } else {
		/* Need one more byte of header */
		gs->remainder = 1;
		gs->hdr[0] |= 0x80;
		SSL_TRC(30, ("%d: SSL[%d]: gather record, another header byte",
			     SSL_GETPID(), ss->fd));
	    }
	    break;

	  case GS_MAC:
	    /* All done with MAC portion of record */
	    gs->state = GS_DATA;
	    gs->remainder = gs->count - sec->hash->length - gs->recordPadding;

	    /*
	    ** Prepare MAC by resetting it and feeding it the shared
	    ** secret
	    */
	    (*sec->hash->begin)(sec->hashcx);
	    (*sec->hash->update)(sec->hashcx, sec->rcvSecret.data,
				sec->rcvSecret.len);
	    break;

	  case GS_DATA:
	    /* All done with data portion of record */
	    if (gs->recordPadding) {
		gs->state = GS_PAD;
		gs->remainder = gs->recordPadding;
		break;
	    }
	    /* FALL THROUGH */

	  case GS_PAD:
	    /* All done with data/padding portion of record */
	    gs->state = GS_INIT;
	    gs->offset = 0;
	    gs->recordOffset = 0;
	    if (gs->encrypted) {
		unsigned ml = sec->hash->length;

		gs->recordOffset = ml;
		gs->recordPadding = 0;

		/*
		** Finish up MAC check by adding in the sequence
		** number. Padding was already added in above.
		*/
		sequenceNumber = sec->rcvSequence;
		seq[0] = (unsigned char) (sequenceNumber >> 24);
		seq[1] = (unsigned char) (sequenceNumber >> 16);
		seq[2] = (unsigned char) (sequenceNumber >> 8);
		seq[3] = (unsigned char) (sequenceNumber);
		(*sec->hash->update)(sec->hashcx, seq, 4);
		(*sec->hash->end)(sec->hashcx, mac, &ml, ml);
		if (PORT_Memcmp(mac, lbp, ml) != 0) {
		    /* MAC's didn't match... */
		    SSL_DBG(("%d: SSL[%d]: mac check failed, seq=%d",
			     SSL_GETPID(), ss->fd, sec->rcvSequence));
		    PRINT_BUF(0, (ss, "computed mac:", mac, ml));
		    PRINT_BUF(0, (ss, "received mac:", lbp, ml));
		    PORT_SetError(SSL_ERROR_BAD_MAC_READ);
		    return -1;
		}
	    }
	    if (sec) {
		/* sec might be NULL if gather is being used for socks... */
		sec->rcvSequence++;
	    }
	    PRINT_BUF(50, (ss, "recv clear record:", lbp +
			   (gs->encrypted ? sec->hash->length : 0),
			   gs->recordLen));
	    return 1;
	}
    }
    return rv;
}

int ssl_GatherRecord(SSLSocket *ss, int flags)
{
    return ssl_GatherData(ss, ss->gather, flags);
}

int ssl_StartGatherBytes(SSLSocket *ss, SSLGather *gs, int count)
{
    int rv;

    gs->state = GS_DATA;
    gs->remainder = count;
    gs->count = count;
    gs->offset = 0;
    if (count > gs->buf.space) {
	rv = ssl_GrowBuf(&gs->buf, count);
	if (rv) {
	    return rv;
	}
    }
    return ssl_GatherData(ss, gs, 0);
}

SSLGather *ssl_NewGather(void)
{
    SSLGather *gs;

    gs = (SSLGather*) PORT_ZAlloc(sizeof(SSLGather));
    if (gs) {
	gs->state = GS_INIT;
    }
    return gs;
}

void ssl_DestroyGather(SSLGather *gs)
{
    if (gs->inbuf.buf != NULL) {
	PORT_Free(gs->inbuf.buf);
    }
    if (gs) {
	PORT_FreeBlock(gs->buf.buf);
	PORT_Free(gs);
    }
}

SECStatus
ssl_HandleV3Hello(SSLSocket *ss)
{
    SSLGather *gs = ss->gather;
    SSLSessionID *sid;
    extern ssl3_SendApplicationData();

    gs->remainder = 3;
    gs->count = 0;
    ss->nextHandshake = 0;
    ss->securityHandshake = 0;
    ss->version = SSL_LIBRARY_VERSION_3_0;
    ss->sec->send = ssl3_SendApplicationData;
    if(ss->sec && (sid = ss->sec->ci->sid)) {
	sid->version = SSL_LIBRARY_VERSION_3_0;
    }
    return SECSuccess;
}