Exemple #1
0
int32 sslActivateWriteCipher(ssl_t *ssl)
{
	ssl->encrypt = ssl->cipher->encrypt;
	ssl->generateMac = ssl->cipher->generateMac;
	ssl->enMacSize = ssl->cipher->macSize;
	ssl->enBlockSize = ssl->cipher->blockSize;
	ssl->enIvSize = ssl->cipher->ivSize;
/*
	Reset the outgoing sequence number for the new suite
*/
	memset(ssl->sec.seq, 0x0, sizeof(ssl->sec.seq));
	if (ssl->cipher->id != SSL_NULL_WITH_NULL_NULL) {
		ssl->flags |= SSL_FLAGS_WRITE_SECURE;
/*
		Copy the newly activated write keys into the live buffers
*/
		memcpy(ssl->sec.writeMAC, ssl->sec.wMACptr, ssl->cipher->macSize);
		memcpy(ssl->sec.writeKey, ssl->sec.wKeyptr, ssl->cipher->keySize);
		memcpy(ssl->sec.writeIV, ssl->sec.wIVptr, ssl->cipher->ivSize);
/*
		set up encrypt contexts
 */
		if (ssl->cipher->init(&(ssl->sec), INIT_ENCRYPT_CIPHER) < 0) {
			matrixStrDebugMsg("Unable to init write cipher suite\n", NULL);
			return -1;
		}
	}
	return 0;
}
Exemple #2
0
/*
	Read in the certificate and private keys from the given files
	If privPass is non-NULL, it will be used to decode an encrypted private
	key file.

	The certificate is stored internally as a pointer to DER encoded X.509
	The private key is stored in a crypto provider specific structure
*/
#ifdef USE_FILE_SYSTEM
int32 matrixRsaReadKeys(sslKeys_t **keys, const char *certFile,
						const char *privFile, const char *privPass,
						const char *trustedCAFiles)
{
	return matrixRsaReadKeysEx(PEERSEC_BASE_POOL, keys, certFile, privFile,
		privPass, trustedCAFiles);
}
#else /* USE_FILE_SYSTEM */
int32 matrixRsaReadKeys(sslKeys_t **keys, char *certFile, char *privFile,
					 char *privPass, char *trustedCAFile)
{
	matrixStrDebugMsg("Error: Calling matrixRsaReadKeys against a library " \
					  "built without USE_FILE_SYSTEM defined\n", NULL);
	return -1;
}
Exemple #3
0
/*
    Binary to struct helper for RSA public keys.
*/
int32 matrixRsaParsePubKey(psPool_t *pool, unsigned char *keyBuf,
                              int32 keyBufLen, sslRsaKey_t **key)
{
    unsigned char   *p, *end;
    int32           len;

    p = keyBuf;
    end = p + keyBufLen;
/*
    Supporting both the PKCS#1 RSAPublicKey format and the
    X.509 SubjectPublicKeyInfo format.  If encoding doesn't start with
    the SEQUENCE identifier for the SubjectPublicKeyInfo format, jump down
    to the RSAPublicKey subset parser and try that
*/
    if (getSequence(&p, (int32)(end - p), &len) == 0) {
        if (getAlgorithmIdentifier(&p, (int32)(end - p), &len, 1) < 0) {
            return -1;
        }
    }
/*
    Now have the DER stream to extract from in asnp
 */
    *key = psMalloc(pool, sizeof(sslRsaKey_t));
    if (*key == NULL) {
        return -8; /* SSL_MEM_ERROR */
    }
    memset(*key, 0x0, sizeof(sslRsaKey_t));
    if (getPubKey(pool, &p, (int32)(end - p), *key) < 0) {
        matrixRsaFreeKey(*key);
        *key = NULL;
        matrixStrDebugMsg("Unable to ASN parse public key\n", NULL);
        return -1;
    }
    return 0;
}
Exemple #4
0
/*
	Cipher suites are chosen before they are activated with the 
	ChangeCipherSuite message.  Additionally, the read and write cipher suites
	are activated at different times in the handshake process.  The following
	APIs activate the selected cipher suite callback functions.
*/
int32 sslActivateReadCipher(ssl_t *ssl)
{
	ssl->decrypt = ssl->cipher->decrypt;
	ssl->verifyMac = ssl->cipher->verifyMac;
	ssl->deMacSize = ssl->cipher->macSize;
	ssl->deBlockSize = ssl->cipher->blockSize;
	ssl->deIvSize = ssl->cipher->ivSize;
/*
	Reset the expected incoming sequence number for the new suite
*/
	memset(ssl->sec.remSeq, 0x0, sizeof(ssl->sec.remSeq));

	if (ssl->cipher->id != SSL_NULL_WITH_NULL_NULL) {
		ssl->flags |= SSL_FLAGS_READ_SECURE;
/*
		Copy the newly activated read keys into the live buffers
*/
		memcpy(ssl->sec.readMAC, ssl->sec.rMACptr, ssl->cipher->macSize);
		memcpy(ssl->sec.readKey, ssl->sec.rKeyptr, ssl->cipher->keySize);
		memcpy(ssl->sec.readIV, ssl->sec.rIVptr, ssl->cipher->ivSize);
/*
		set up decrypt contexts
 */
		if (ssl->cipher->init(&(ssl->sec), INIT_DECRYPT_CIPHER) < 0) {
			matrixStrDebugMsg("Unable to initialize read cipher suite\n", NULL);
			return -1;
		}
	}
	return 0;
}
Exemple #5
0
/*
	ServerHelloDone message is a blank handshake message
*/
static int32 writeServerHelloDone(ssl_t *ssl, sslBuf_t *out)
{
	unsigned char	*c, *end, *encryptStart;
	char			padLen;
	int32				messageSize, rc;

	c = out->end;
	end = out->buf + out->size;
	messageSize =
		ssl->recordHeadLen +
		ssl->hshakeHeadLen;

	if ((rc = writeRecordHeader(ssl, SSL_RECORD_TYPE_HANDSHAKE,
			SSL_HS_SERVER_HELLO_DONE, &messageSize, &padLen,
			&encryptStart, &end, &c)) < 0) {
		return rc;
	}

	if ((rc = encryptRecord(ssl, SSL_RECORD_TYPE_HANDSHAKE, messageSize,
			padLen, encryptStart, out, &c)) < 0) {
		return rc;
	}

	if (c - out->end != messageSize) {
		matrixStrDebugMsg("Error generating hello done for write\n", NULL);
		return SSL_ERROR;
	}
	out->end = c;
	return SSL_SUCCESS;
}
Exemple #6
0
/*
	Open and close the PKI module.  These routines are called once in the 
	lifetime of the application and initialize and clean up the library 
	respectively.
*/
int32 matrixPkiOpen(void)
{
	if (sslOpenOsdep() < 0) {
		matrixStrDebugMsg("Osdep open failure\n", NULL);
		return -1;
	}
	return 0;
}
Exemple #7
0
/*
	Implementation specific OID parser for suppported RSA algorithms
*/
int32 getAlgorithmIdentifier(unsigned char **pp, int32 len, int32 *oi,
								  int32 isPubKey)
{
	unsigned char	*p = *pp, *end;
	int32			arcLen, llen;

	end = p + len;
	if (len < 1 || getSequence(&p, len, &llen) < 0) {
		return -1;
	}
	if (end - p < 1) {
		return -1;
	}
	if (*(p++) != ASN_OID || asnParseLength(&p, (int32)(end - p), &arcLen) < 0 ||
			llen < arcLen) {
		return -1;
	}
/*
	List of expected (currently supported) OIDs
	algorithm				  summed	length		hex
	sha1						 88		05		2b0e03021a
	md2							646		08		2a864886f70d0202
	md5							649		08		2a864886f70d0205
	rsaEncryption				645		09		2a864886f70d010101
	md2WithRSAEncryption:		646		09		2a864886f70d010102
	md5WithRSAEncryption		648		09		2a864886f70d010104
	sha-1WithRSAEncryption		649		09		2a864886f70d010105

	Yes, the summing isn't ideal (as can be seen with the duplicate 649),
	but the specific implementation makes this ok.
*/
	if (end - p < 2) {
		return -1;
	}
	if (isPubKey && (*p != 0x2a) && (*(p + 1) != 0x86)) {
/*
		Expecting DSA here if not RSA, but OID doesn't always match
*/
		matrixStrDebugMsg("Unsupported algorithm identifier\n", NULL);
		return -1;
	}
	*oi = 0;
	while (arcLen-- > 0) {
		*oi += (int32)*p++;
	}
/*
	Each of these cases might have a trailing NULL parameter.  Skip it
*/
	if (*p != ASN_NULL) {
		*pp = p;
		return 0;
	}
	if (end - p < 2) {
		return -1;
	}
	*pp = p + 2;
	return 0;
}
Exemple #8
0
/*
	Get an integer
*/
int32 getInteger(unsigned char **pp, int32 len, int32 *val)
{
	unsigned char	*p = *pp, *end;
	uint32			ui;
	int32			vlen;

	end = p + len;
	if (len < 1 || *(p++) != ASN_INTEGER ||
			asnParseLength(&p, len - 1, &vlen) < 0) {
		matrixStrDebugMsg("ASN getInteger failed\n", NULL);
		return -1;
	}
/*
	This check prevents us from having a big positive integer where the 
	high bit is set because it will be encoded as 5 bytes (with leading 
	blank byte).  If that is required, a getUnsigned routine should be used
*/
	if (vlen > sizeof(int32) || end - p < vlen) {
		matrixStrDebugMsg("ASN getInteger failed\n", NULL);
		return -1;
	}
	ui = 0;
/*
	If high bit is set, it's a negative integer, so perform the two's compliment
	Otherwise do a standard big endian read (most likely case for RSA)
*/
	if (*p & 0x80) {
		while (vlen-- > 0) {
			ui = (ui << 8) | (*p ^ 0xFF);
			p++;
		}
		vlen = (int32)ui;
		vlen++;
		vlen = -vlen;
		*val = vlen;
	} else {
		while (vlen-- > 0) {
			ui = (ui << 8) | *p;
			p++;
		}
		*val = (int32)ui;
	}
	*pp = p;
	return 0;
}
Exemple #9
0
/*
 *	Generates all key material.
 */
int32 sslDeriveKeys(ssl_t *ssl)
{
	sslMd5Context_t		md5Ctx;
	sslSha1Context_t	sha1Ctx;
	unsigned char		buf[SSL_MD5_HASH_SIZE + SSL_SHA1_HASH_SIZE];
	unsigned char		*tmp;
	int32				i;

/*
	If this session is resumed, we want to reuse the master secret to 
	regenerate the key block with the new random values.
*/
	if (ssl->flags & SSL_FLAGS_RESUMED) {
		goto skipPremaster;
	}
/*
	master_secret =
		MD5(pre_master_secret + SHA('A' + pre_master_secret +
			ClientHello.random + ServerHello.random)) +
		MD5(pre_master_secret + SHA('BB' + pre_master_secret +
			ClientHello.random + ServerHello.random)) +
		MD5(pre_master_secret + SHA('CCC' + pre_master_secret +
			ClientHello.random + ServerHello.random));
*/
	tmp = ssl->sec.masterSecret;
	for (i = 0; i < 3; i++) {
		matrixSha1Init(&sha1Ctx);
		matrixSha1Update(&sha1Ctx, salt[i], i + 1);
		matrixSha1Update(&sha1Ctx, ssl->sec.premaster, ssl->sec.premasterSize);
		matrixSha1Update(&sha1Ctx, ssl->sec.clientRandom, SSL_HS_RANDOM_SIZE);
		matrixSha1Update(&sha1Ctx, ssl->sec.serverRandom, SSL_HS_RANDOM_SIZE);
		matrixSha1Final(&sha1Ctx, buf);
		
		matrixMd5Init(&md5Ctx);
		matrixMd5Update(&md5Ctx, ssl->sec.premaster, ssl->sec.premasterSize);
		matrixMd5Update(&md5Ctx, buf, SSL_SHA1_HASH_SIZE);
		matrixMd5Final(&md5Ctx, tmp);
		tmp += SSL_MD5_HASH_SIZE;
	}
	memset(buf, 0x0, SSL_MD5_HASH_SIZE + SSL_SHA1_HASH_SIZE);
/*
	premaster is now allocated for DH reasons.  Can free here
*/
	psFree(ssl->sec.premaster);
	ssl->sec.premaster = NULL;
	ssl->sec.premasterSize = 0;

skipPremaster:
	if (createKeyBlock(ssl, ssl->sec.clientRandom, ssl->sec.serverRandom, 
			ssl->sec.masterSecret, SSL_HS_MASTER_SIZE) < 0) {
		matrixStrDebugMsg("Unable to create key block\n", NULL);
		return -1;
	}
	
	return SSL_HS_MASTER_SIZE;
}
Exemple #10
0
/*
	Callback to extract a big int32 (stream of bytes) from the DER stream
*/
int32 getBig(psPool_t *pool, unsigned char **pp, int32 len, mp_int *big)
{
	unsigned char	*p = *pp;
	int32			vlen;

	if (len < 1 || *(p++) != ASN_INTEGER ||
			asnParseLength(&p, len - 1, &vlen) < 0) {
		matrixStrDebugMsg("ASN getBig failed\n", NULL);
		return -1;
	}
	mp_init(pool, big);
	if (mp_read_unsigned_bin(big, p, vlen) != 0) {
		mp_clear(big);
		matrixStrDebugMsg("ASN getBig failed\n", NULL);
		return -1;
	}
	p += vlen;
	*pp = p;
	return 0;
}
Exemple #11
0
/*
	Open and close the SSL module.  These routines are called once in the 
	lifetime of the application and initialize and clean up the library 
	respectively.
*/
int32 matrixSslOpen(void)
{
	if (matrixPkiOpen() < 0) {
		matrixStrDebugMsg("PKI open failure\n", NULL);
		printf("open failed\n");
		return -1;
	}

#ifdef USE_SERVER_SIDE_SSL
	memset(sessionTable, 0x0, 
		sizeof(sslSessionEntry_t) * SSL_SESSION_TABLE_SIZE);
	sslCreateMutex(&sessionTableLock);
#endif /* USE_SERVER_SIDE_SSL */
	return 0;
}
Exemple #12
0
/*
	On success, p will be updated to point to first character of value and
	valLen will contain number of bytes in value
	Return:
		0			Success
		< 0			Error
*/
int32 asnParseLength(unsigned char **p, int32 size, int32 *valLen)
{
	unsigned char	*c, *end;
	int32			len, olen;

	c = *p;
	end = c + size;
	if (end - c < 1) {
		return -1;
	}
/*
	If the length byte has high bit only set, it's an indefinite length
	We don't support this!
*/
	if (*c == 0x80) {
		*valLen = -1;
		matrixStrDebugMsg("Unsupported: ASN indefinite length\n", NULL);
		return -1;
	}
/*
	If the high bit is set, the lower 7 bits represent the number of 
	bytes that follow and represent length
	If the high bit is not set, the lower 7 represent the actual length
*/
	len = *c & 0x7F;
	if (*(c++) & 0x80) {
/*
		Make sure there aren't more than 4 bytes of length specifier,
		and that we have that many bytes left in the buffer
*/
		if (len > sizeof(int32) || len == 0x7f || (end - c) < len) {
			return -1;
		}
		olen = 0;
		while (len-- > 0) {
			olen = (olen << 8) | *c;
			c++;
		}
		if (olen < 0 || olen > INT_MAX) {
			return -1;
		}
		len = olen;
	}
	*p = c;
	*valLen = len;
	return 0;
}
Exemple #13
0
/*
	Allows for semi-colon delimited list of certificates for cert chaining.
	Also allows multiple certificiates in a single file.
	
	HOWERVER, in both cases the first in the list must be the identifying
	cert of the application. Each subsequent cert is the parent of the previous
*/
int32 readCertChain(psPool_t *pool, const char *certFiles,
					sslLocalCert_t *lkeys)
{
	sslLocalCert_t	*currCert;
	unsigned char	*certBin, *tmp;
	sslChainLen_t	chain;
	int32			certLen, i;

	if (certFiles == NULL) {
		return 0;
	}

	if (matrixX509ReadCert(pool, certFiles, &certBin, &certLen, &chain) < 0) {
		matrixStrDebugMsg("Error reading cert file %s\n", (char*)certFiles);
		return -1;
	}
/*
	The first cert is allocated in the keys struct.  All others in
	linked list are allocated here.
*/
	i = 0;
	tmp = certBin;
	while (chain[i] != 0) {
		if (i == 0) {
			currCert = lkeys;
		} else {
			currCert->next = psMalloc(pool, sizeof(sslLocalCert_t));
			if (currCert->next == NULL) {
				psFree(tmp);
				return -8; /* SSL_MEM_ERROR */
			}
			memset(currCert->next, 0x0, sizeof(sslLocalCert_t));
			currCert = currCert->next;
		}
		currCert->certBin = psMalloc(pool, chain[i]);
		memcpy(currCert->certBin, certBin, chain[i]);
		currCert->certLen = chain[i];
		certBin += chain[i]; certLen -= chain[i];
		i++;
	}
	psFree(tmp);
	sslAssert(certLen == 0);
	return 0;
}
/*
	Encrypt the message using the current cipher.  This call is used in
	conjunction with the writeRecordHeader function above to finish writing
	an SSL record.  Updates handshake hash if necessary, generates message
	MAC, writes the padding, and does the encrytion.
*/
static int32 encryptRecord(ssl_t *ssl, int32 type, int32 messageSize, int32 padLen, 
							 unsigned char *encryptStart, sslBuf_t *out,
							 unsigned char **c)
{
	if (type == SSL_RECORD_TYPE_HANDSHAKE) {
		sslUpdateHSHash(ssl, encryptStart, (int32)(*c - encryptStart));
	}
	*c += ssl->generateMac(ssl, type, encryptStart, 
		(int32)(*c - encryptStart), *c);
	
	*c += sslWritePad(*c, padLen);

	if (ssl->encrypt(&ssl->sec.encryptCtx, encryptStart, encryptStart, 
			(int32)(*c - encryptStart)) < 0 || *c - out->end != messageSize) {
		matrixStrDebugMsg("Error encrypting message for write\n", NULL);
		return SSL_ERROR;
	}
	return 0;
}
Exemple #15
0
void matrixSslClose(void)
{
#ifdef USE_SERVER_SIDE_SSL
	int32		i;

	sslLockMutex(&sessionTableLock);
	for (i = 0; i < SSL_SESSION_TABLE_SIZE; i++) {
		if (sessionTable[i].inUse == 1) {
			matrixStrDebugMsg("Warning: closing while session still in use\n",
				NULL);
		}
	}
	memset(sessionTable, 0x0, 
		sizeof(sslSessionEntry_t) * SSL_SESSION_TABLE_SIZE);
	sslUnlockMutex(&sessionTableLock);
	sslDestroyMutex(&sessionTableLock);
#endif /* USE_SERVER_SIDE_SSL */
	matrixPkiClose();
}
Exemple #16
0
/*
	Pad a value to be encrypted by RSA, according to PKCS#1 v1.5
	http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
	When encrypting a value with RSA, the value is first padded to be 
	equal to the public key size using the following method:
		00 <id> <data> 00 <value to be encrypted>
	- id denotes a public or private key operation
	- if id is private, data is however many non-zero bytes it takes to pad the
		value to the key length (randomLen = keyLen - 3 - valueLen).
	- if id is public, data is FF for the same length as described above
	- There must be at least 8 bytes of data.
*/
static int32 sslPadRSA(unsigned char *in, int32 inlen, unsigned char *out,
					   int32 outlen, int32 cryptType)
{
	unsigned char	*c;
	int32			randomLen;
	
	randomLen = outlen - 3 - inlen;
	if (randomLen < 8) {
		matrixIntDebugMsg("RSA encryption data too large: %d\n", inlen);
		return -1;
	}
	c = out;
	*c = 0x00;
	c++;
	*c = (unsigned char)cryptType;
	c++;
	if (cryptType == RSA_PUBLIC) {
		while (randomLen-- > 0) {
			*c++ = 0xFF;
		}
	} else {
		if (sslGetEntropy(c, randomLen) < 0) {
			matrixStrDebugMsg("Error gathering RSA pad entropy\n", NULL);
			return -1;
		}
/*
		SECURITY:  Read through the random data and change all 0x0 to 0x01.
		This is per spec that no random bytes should be 0
*/
		while (randomLen-- > 0) {
			if (*c == 0x0) {
				*c = 0x01;
			}
			c++;
		}
	}
	*c = 0x00;
	c++;
	memcpy(c, in, inlen);
	
	return outlen;
}
Exemple #17
0
/*
	Binary to struct helper for RSA public keys
*/
int32 matrixRsaParsePubKey(psPool_t *pool, unsigned char *keyBuf,
							  int32 keyBufLen, sslRsaKey_t **key)
{
/*
	Now have the DER stream to extract from in asnp
 */
	*key = psMalloc(pool, sizeof(sslRsaKey_t));
	if (*key == NULL) {
		return -8; /* SSL_MEM_ERROR */
	}
	memset(*key, 0x0, sizeof(sslRsaKey_t));

	if (getPubKey(pool, &keyBuf, keyBufLen, *key) < 0) {
		matrixRsaFreeKey(*key);
		*key = NULL;
		matrixStrDebugMsg("Unable to ASN parse public key\n", NULL);
		return -1;
	}
	return 0;
}
Exemple #18
0
/*
	This function was written strictly for clarity in the PeerSec crypto API
	product.  It extracts only the public key from a certificate file for use
	in the lower level encrypt/decrypt RSA routines
*/
int32 matrixX509ReadPubKey(psPool_t *pool, const char *certFile,
						   sslRsaKey_t **key)
{
	unsigned char	*certBuf;
	sslChainLen_t	chain;
	int32			certBufLen;

	certBuf = NULL;
	if (matrixX509ReadCert(pool, certFile, &certBuf, &certBufLen, &chain) < 0) {
		matrixStrDebugMsg("Unable to read certificate file %s\n",
			(char*)certFile);
		if (certBuf) psFree(certBuf);
		return -1;
	}
	if (matrixX509ParsePubKey(pool, certBuf, certBufLen, key) < 0) {
		psFree(certBuf);
		return -1;
	}
	psFree(certBuf);
	return 0;
}
/*
	Server initiated rehandshake public API call.
*/
int32 matrixSslEncodeHelloRequest(ssl_t *ssl, sslBuf_t *out)
{
	unsigned char	*c, *end, *encryptStart;
	char			padLen;
	int32				messageSize, rc;

	if (ssl->flags & SSL_FLAGS_ERROR || ssl->flags & SSL_FLAGS_CLOSED) {
		return SSL_ERROR;
	}
	if (!(ssl->flags & SSL_FLAGS_SERVER) || (ssl->hsState != SSL_HS_DONE)) {
		return SSL_ERROR;
	}

	c = out->end;
	end = out->buf + out->size;
	messageSize =
		ssl->recordHeadLen +
		ssl->hshakeHeadLen;
	if ((rc = writeRecordHeader(ssl, SSL_RECORD_TYPE_HANDSHAKE,
			SSL_HS_HELLO_REQUEST, &messageSize, &padLen,
			&encryptStart, &end, &c)) < 0) {
		return rc;
	}

	if ((rc = encryptRecord(ssl, SSL_RECORD_TYPE_HANDSHAKE, messageSize,
			padLen, encryptStart, out, &c)) < 0) {
		return rc;
	}

	if (c - out->end != messageSize) {
		matrixStrDebugMsg("Error generating hello request for write\n", NULL);
		return SSL_ERROR;
	}
	out->end = c;

	return SSL_SUCCESS;
}
Exemple #20
0
/*
    New SSL protocol context
    This structure is associated with a single SSL connection.  Each socket
    using SSL should be associated with a new SSL context.

    certBuf and privKey ARE NOT duplicated within the server context, in order
    to minimize memory usage with multiple simultaneous requests.  They must
    not be deleted by caller until all server contexts using them are deleted.
*/
int32 matrixSslNewSession(ssl_t **ssl, sslKeys_t *keys, sslSessionId_t *session,
                        int32 flags)
{
    psPool_t    *pool = NULL;
    ssl_t       *lssl;

/*
    First API level chance to make sure a user is not attempting to use
    client or server support that was not built into this library compile
*/
#ifndef USE_SERVER_SIDE_SSL
    if (flags & SSL_FLAGS_SERVER) {
        matrixStrDebugMsg("MatrixSSL lib not compiled with server support\n",
            NULL);
        return -1;
    }
#endif
#ifndef USE_CLIENT_SIDE_SSL
    if (!(flags & SSL_FLAGS_SERVER)) {
        matrixStrDebugMsg("MatrixSSL lib not compiled with client support\n",
            NULL);
        return -1;
    }
#endif
    if (flags & SSL_FLAGS_CLIENT_AUTH) {
        matrixStrDebugMsg("MatrixSSL lib not compiled with client " \
            "authentication support\n", NULL);
        return -1;
    }

    if (flags & SSL_FLAGS_SERVER) {
        if (keys == NULL) {
            matrixStrDebugMsg("NULL keys in  matrixSslNewSession\n", NULL);
            return -1;
        }
        if (session != NULL) {
            matrixStrDebugMsg("Server session must be NULL\n", NULL);
            return -1;
        }
    }

    *ssl = lssl = psMalloc(pool, sizeof(ssl_t));
    if (lssl == NULL) {
        return SSL_MEM_ERROR;
    }
    memset(lssl, 0x0, sizeof(ssl_t));

    lssl->pool = pool;
    lssl->cipher = sslGetCipherSpec(SSL_NULL_WITH_NULL_NULL);
    sslActivateReadCipher(lssl);
    sslActivateWriteCipher(lssl);
    sslActivatePublicCipher(lssl);

    lssl->recordHeadLen = SSL3_HEADER_LEN;
    lssl->hshakeHeadLen = SSL3_HANDSHAKE_HEADER_LEN;

    if (flags & SSL_FLAGS_SERVER) {
        lssl->flags |= SSL_FLAGS_SERVER;
/*
        Client auth can only be requested by server, not set by client
*/
        if (flags & SSL_FLAGS_CLIENT_AUTH) {
            lssl->flags |= SSL_FLAGS_CLIENT_AUTH;
        }
        lssl->hsState = SSL_HS_CLIENT_HELLO;
    } else {
/*
        Client is first to set protocol version information based on
        compile and/or the 'flags' parameter so header information in
        the handshake messages will be correctly set.
*/
        lssl->majVer = SSL3_MAJ_VER;
        lssl->minVer = SSL3_MIN_VER;
        lssl->hsState = SSL_HS_SERVER_HELLO;
        if (session != NULL && session->cipherId != SSL_NULL_WITH_NULL_NULL) {
            lssl->cipher = sslGetCipherSpec(session->cipherId);
            if (lssl->cipher == NULL) {
                matrixStrDebugMsg("Invalid session id to matrixSslNewSession\n",
                    NULL);
            } else {
                memcpy(lssl->sec.masterSecret, session->masterSecret,
                    SSL_HS_MASTER_SIZE);
                lssl->sessionIdLen = SSL_MAX_SESSION_ID_SIZE;
                memcpy(lssl->sessionId, session->id, SSL_MAX_SESSION_ID_SIZE);
            }
        }
    }
    lssl->err = SSL_ALERT_NONE;
    lssl->keys = keys;

    return 0;
}
Exemple #21
0
/*
    Set a custom callback to receive the certificate being presented to the
    session to perform custom authentication if needed
*/
void matrixSslSetCertValidator(ssl_t *ssl,
                int32 (*certValidator)(sslCertInfo_t *t, void *arg), void *arg)
{
    if (certValidator) {
        ssl->sec.validateCert = certValidator;
        ssl->sec.validateCertArg = arg;
    }
}
#else /* Public API, so should always be linkable */
void matrixSslSetCertValidator(ssl_t *ssl,
                int32 (*certValidator)(sslCertInfo_t *t, void *arg), void *arg)
{
    matrixStrDebugMsg("matrixSslSetCertValidator is not available\n", NULL);
    matrixStrDebugMsg("Library not built for cert validation support\n", NULL);
}
Exemple #22
0
/*
	Preferred version for commercial users who make use of memory pools.

	This use of the sslKeys_t param implies this is for use in the MatrixSSL
	product (input to matrixSslNewSession).  However, we didn't want to
	expose this API at the matrixSsl.h level due to the pool parameter. This
	is strictly an API that commerical users will have access to.
*/
int32 matrixRsaParseKeysMem(psPool_t *pool, sslKeys_t **keys,
			unsigned char *certBuf,	int32 certLen, unsigned char *privBuf,
			int32 privLen, unsigned char *trustedCABuf, int32 trustedCALen)
{
	sslKeys_t		*lkeys;
	sslLocalCert_t	*current, *next;
	unsigned char	*binPtr;
	int32			len, lenOh, i;
#ifdef USE_CLIENT_SIDE_SSL
	sslRsaCert_t	*currentCA, *nextCA;
#endif /* USE_CLIENT_SIDE_SSL */

	*keys = lkeys = psMalloc(pool, sizeof(sslKeys_t));
	if (lkeys == NULL) {
		return -8; /* SSL_MEM_ERROR */
	}
	memset(lkeys, 0x0, sizeof(sslKeys_t));
/*
	The buffers are just the ASN.1 streams so the intermediate parse
	that used to be here is gone.  Doing a straight memcpy for this
	and passing that along to X509ParseCert
*/
	i = 0;
	current = &lkeys->cert;
	binPtr = certBuf;
/*
	Need to check for a chain here.  Only way to do this is to read off the
	length id from the DER stream for each.  The chain must be just a stream
	of DER certs with the child-most cert always first.
*/
	while (certLen > 0) {
		if (getSequence(&certBuf, certLen, &len) < 0) {
			matrixStrDebugMsg("Unable to parse length of cert stream\n", NULL);
			matrixRsaFreeKeys(lkeys);
			return -1;
		}
/*
		Account for the overhead of storing the length itself
*/
		lenOh = (int32)(certBuf - binPtr);
		len += lenOh;
		certBuf -= lenOh;
/*
		First cert is already malloced
*/
		if (i > 0) {
			next = psMalloc(pool, sizeof(sslLocalCert_t));
			memset(next, 0x0, sizeof(sslLocalCert_t));
			current->next = next;
			current = next;
		}
		current->certBin = psMalloc(pool, len);
		memcpy(current->certBin, certBuf, len);
		current->certLen = len;
		certLen -= len;
		certBuf += len;
		binPtr = certBuf;
		i++;
	}

/*
	Parse private key
*/
	if (privLen > 0) {
		if (matrixRsaParsePrivKey(pool, privBuf, privLen,
				&lkeys->cert.privKey) < 0) {
			matrixStrDebugMsg("Error reading private key mem\n", NULL);
			matrixRsaFreeKeys(lkeys);
			return -1;
		}
	}


/*
	Trusted CAs
*/
#ifdef USE_CLIENT_SIDE_SSL
	if (trustedCABuf != NULL && trustedCALen > 0) {
		i = 0;
		binPtr = trustedCABuf;
		currentCA = NULL;
/*
		Need to check for list here.  Only way to do this is to read off the
		length id from the DER stream for each.
*/
		while (trustedCALen > 0) {
			if (getSequence(&trustedCABuf, trustedCALen, &len) < 0) {
				matrixStrDebugMsg("Unable to parse length of CA stream\n",
					NULL);
				matrixRsaFreeKeys(lkeys);
				return -1;
			}
/*
			Account for the overhead of storing the length itself
*/
			lenOh = (int32)(trustedCABuf - binPtr);
			len += lenOh;
			trustedCABuf -= lenOh;

			if (matrixX509ParseCert(pool, trustedCABuf, len, &currentCA) < 0) {
				matrixStrDebugMsg("Error parsing CA cert\n", NULL);
				matrixRsaFreeKeys(lkeys);
				return -1;
			}
/*
			First cert should be assigned to lkeys
*/
			if (i == 0) {
				lkeys->caCerts = currentCA;
				nextCA = lkeys->caCerts;
			} else {
				nextCA->next = currentCA;
				nextCA = currentCA;
			}
			currentCA = currentCA->next;
			trustedCALen -= len;
			trustedCABuf += len;
			binPtr = trustedCABuf;
			i++;
		}
	}
#endif /* USE_CLIENT_SIDE_SSL */

	return 0;
}
Exemple #23
0
/*
	Parse an X509 ASN.1 certificate stream
	http://www.faqs.org/rfcs/rfc2459.html section 4.1
*/
int32 matrixX509ParseCert(psPool_t *pool, unsigned char *pp, int32 size, 
						sslRsaCert_t **outcert)
{
	sslRsaCert_t		*cert;
	sslMd5Context_t		md5Ctx;
	sslSha1Context_t	sha1Ctx;
	unsigned char		*p, *end, *certStart, *certEnd;
	int32				certLen, len, parsing;
#ifdef USE_MD2
	sslMd2Context_t		md2Ctx;
#endif /* USE_MD2 */

/*
	Allocate the cert structure right away.  User MUST always call
	matrixX509FreeCert regardless of whether this function succeeds.
	memset is important because the test for NULL is what is used
	to determine what to free
*/
	*outcert = cert = psMalloc(pool, sizeof(sslRsaCert_t));
	if (cert == NULL) {
		return -8; /* SSL_MEM_ERROR */
	}
	memset(cert, '\0', sizeof(sslRsaCert_t));

	p = pp;
	end = p + size;
/*
		Certificate  ::=  SEQUENCE  {
		tbsCertificate		TBSCertificate,
		signatureAlgorithm	AlgorithmIdentifier,
		signatureValue		BIT STRING	}
*/
	parsing = 1;
	while (parsing) {
		if (getSequence(&p, (int32)(end - p), &len) < 0) {
			matrixStrDebugMsg("Initial cert parse error\n", NULL);
			return -1;
		}
		certStart = p;
/*	
		TBSCertificate  ::=  SEQUENCE  {
		version			[0]		EXPLICIT Version DEFAULT v1,
		serialNumber			CertificateSerialNumber,
		signature				AlgorithmIdentifier,
		issuer					Name,
		validity				Validity,
		subject					Name,
		subjectPublicKeyInfo	SubjectPublicKeyInfo,
		issuerUniqueID	[1]		IMPLICIT UniqueIdentifier OPTIONAL,
							-- If present, version shall be v2 or v3
		subjectUniqueID	[2]	IMPLICIT UniqueIdentifier OPTIONAL,
							-- If present, version shall be v2 or v3
		extensions		[3]	EXPLICIT Extensions OPTIONAL
							-- If present, version shall be v3	}
*/
		if (getSequence(&p, (int32)(end - p), &len) < 0) {
			matrixStrDebugMsg("ASN sequence parse error\n", NULL);
			return -1;
		}
		certEnd = p + len;
		certLen = (int32)(certEnd - certStart);

/*
		Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
*/
		if (getExplicitVersion(&p, (int32)(end - p), 0, &cert->version) < 0) {
			matrixStrDebugMsg("ASN version parse error\n", NULL);
			return -1;
		}
		if (cert->version != 2) {
			matrixIntDebugMsg("Warning: non-v3 certificate version: %d\n",
			cert->version);
		}
/*
		CertificateSerialNumber  ::=  INTEGER
*/
		if (getSerialNum(pool, &p, (int32)(end - p), &cert->serialNumber,
				&cert->serialNumberLen) < 0) {
			matrixStrDebugMsg("ASN serial number parse error\n", NULL);
			return -1;
		}
/*
		AlgorithmIdentifier  ::=  SEQUENCE  {
		algorithm				OBJECT IDENTIFIER,
		parameters				ANY DEFINED BY algorithm OPTIONAL }
*/
		if (getAlgorithmIdentifier(&p, (int32)(end - p),
				&cert->certAlgorithm, 0) < 0) {
			return -1;
		}
/*
		Name ::= CHOICE {
		RDNSequence }

		RDNSequence ::= SEQUENCE OF RelativeDistinguishedName

		RelativeDistinguishedName ::= SET OF AttributeTypeAndValue

		AttributeTypeAndValue ::= SEQUENCE {
		type	AttributeType,
		value	AttributeValue }

		AttributeType ::= OBJECT IDENTIFIER

		AttributeValue ::= ANY DEFINED BY AttributeType
*/
		if (getDNAttributes(pool, &p, (int32)(end - p), &cert->issuer) < 0) {
			return -1;
		}
/*
		Validity ::= SEQUENCE {
		notBefore	Time,
		notAfter	Time	}
*/
		if (getValidity(pool, &p, (int32)(end - p), &cert->notBefore,
				&cert->notAfter) < 0) {
			return -1;
		}
/*
		Subject DN
*/
		if (getDNAttributes(pool, &p, (int32)(end - p), &cert->subject) < 0) {
			return -1;
		}
/*
		SubjectPublicKeyInfo  ::=  SEQUENCE  {
		algorithm			AlgorithmIdentifier,
		subjectPublicKey	BIT STRING	}
*/
		if (getSequence(&p, (int32)(end - p), &len) < 0) {
			return -1;
		}
		if (getAlgorithmIdentifier(&p, (int32)(end - p),
				&cert->pubKeyAlgorithm, 1) < 0) {
			return -1;
		}
		if (getPubKey(pool, &p, (int32)(end - p), &cert->publicKey) < 0) {
			return -1;
		}
/*
		As the next three values are optional, we can do a specific test here
*/
		if (*p != (ASN_SEQUENCE | ASN_CONSTRUCTED)) {
			if (getImplicitBitString(pool, &p, (int32)(end - p), IMPLICIT_ISSUER_ID,
					&cert->uniqueUserId, &cert->uniqueUserIdLen) < 0 ||
				getImplicitBitString(pool, &p, (int32)(end - p), IMPLICIT_SUBJECT_ID,
					&cert->uniqueSubjectId, &cert->uniqueSubjectIdLen) < 0 ||
				getExplicitExtensions(pool, &p, (int32)(end - p), EXPLICIT_EXTENSION,
					&cert->extensions) < 0) {
				matrixStrDebugMsg("There was an error parsing a certificate\n", NULL);
				matrixStrDebugMsg("extension.  This is likely caused by an\n", NULL);
				matrixStrDebugMsg("extension format that is not currently\n", NULL);
				matrixStrDebugMsg("recognized.  Please email [email protected]\n", NULL);
				matrixStrDebugMsg("to add support for the extension.\n\n", NULL);
				return -1;
			}
		}
/*
		This is the end of the cert.  Do a check here to be certain
*/
		if (certEnd != p) {
			return -1;
		}
/*
		Certificate signature info
*/
		if (getAlgorithmIdentifier(&p, (int32)(end - p),
				&cert->sigAlgorithm, 0) < 0) {
			return -1;
		}
/*
		Signature algorithm must match that specified in TBS cert
*/
		if (cert->certAlgorithm != cert->sigAlgorithm) {
			matrixStrDebugMsg("Parse error: mismatched signature type\n", NULL);
			return -1; 
		}
/*
		Compute the hash of the cert here for CA validation
*/
		if (cert->certAlgorithm == OID_RSA_MD5) {
			matrixMd5Init(&md5Ctx);
			matrixMd5Update(&md5Ctx, certStart, certLen);
			matrixMd5Final(&md5Ctx, cert->sigHash);
		} else if (cert->certAlgorithm == OID_RSA_SHA1) {
			matrixSha1Init(&sha1Ctx);
			matrixSha1Update(&sha1Ctx, certStart, certLen);
			matrixSha1Final(&sha1Ctx, cert->sigHash);
		}
#ifdef USE_MD2
		else if (cert->certAlgorithm == OID_RSA_MD2) {
			matrixMd2Init(&md2Ctx);
			matrixMd2Update(&md2Ctx, certStart, certLen);
			matrixMd2Final(&md2Ctx, cert->sigHash);
		}
#endif /* USE_MD2 */

		if (getSignature(pool, &p, (int32)(end - p), &cert->signature,
				&cert->signatureLen) < 0) {
			return -1;
		}
/*
		The ability to parse additional chained certs is a PKI product
		feature addition.  Chaining in MatrixSSL is handled internally.
*/
		if (p != end) {
			cert->next = psMalloc(pool, sizeof(sslRsaCert_t));
			cert = cert->next;
			memset(cert, '\0', sizeof(sslRsaCert_t));
		} else {
			parsing = 0;
		}
	}
		
	return (int32)(p - pp);
}
/*
	Write out the ServerHello message
*/
static int32 writeServerHello(ssl_t *ssl, sslBuf_t *out)
{
	unsigned char	*c, *end, *encryptStart;
	char			padLen;
	int32				messageSize, rc;
	time_t			t;

	c = out->end;
	end = out->buf + out->size;
/*
	Calculate the size of the message up front, and verify we have room
	We assume there will be a sessionId in the message, and make adjustments
	below if there is no sessionId.
*/
	messageSize =
		ssl->recordHeadLen +
		ssl->hshakeHeadLen +
		38 + SSL_MAX_SESSION_ID_SIZE;
/*
	First 4 bytes of the serverRandom are the unix time to prevent replay
	attacks, the rest are random
*/
	t = time(0);
	ssl->sec.serverRandom[0] = (unsigned char)((t & 0xFF000000) >> 24);
	ssl->sec.serverRandom[1] = (unsigned char)((t & 0xFF0000) >> 16);
	ssl->sec.serverRandom[2] = (unsigned char)((t & 0xFF00) >> 8);
	ssl->sec.serverRandom[3] = (unsigned char)(t & 0xFF);
	if (sslGetEntropy(ssl->sec.serverRandom + 4, SSL_HS_RANDOM_SIZE - 4) < 0) {
		matrixStrDebugMsg("Error gathering serverRandom entropy\n", NULL);
		return SSL_ERROR;
	}
/*
	We register session here because at this point the serverRandom value is
	populated.  If we are able to register the session, the sessionID and
	sessionIdLen fields will be non-NULL, otherwise the session couldn't
	be registered.
*/
	if (!(ssl->flags & SSL_FLAGS_RESUMED)) {
		matrixRegisterSession(ssl);
	}
	messageSize -= (SSL_MAX_SESSION_ID_SIZE - ssl->sessionIdLen);

	if ((rc = writeRecordHeader(ssl, SSL_RECORD_TYPE_HANDSHAKE,
			SSL_HS_SERVER_HELLO, &messageSize, &padLen, &encryptStart,
			&end, &c)) < 0) {
		return rc;
	}
/*
	First two fields in the ServerHello message are the major and minor
	SSL protocol versions we agree to talk with
*/
	*c = ssl->majVer; c++;
	*c = ssl->minVer; c++;
/*
	The next 32 bytes are the server's random value, to be combined with
	the client random and premaster for key generation later
*/
	memcpy(c, ssl->sec.serverRandom, SSL_HS_RANDOM_SIZE);
	c += SSL_HS_RANDOM_SIZE;
/*
	The next data is a single byte containing the session ID length,
	and up to 32 bytes containing the session id.
	First register the session, which will give us a session id and length
	if not all session slots in the table are used
*/
	*c = ssl->sessionIdLen; c++;
	if (ssl->sessionIdLen > 0) {
        memcpy(c, ssl->sessionId, ssl->sessionIdLen);
		c += ssl->sessionIdLen;
	}
/*
	Two byte cipher suite we've chosen based on the list sent by the client
	and what we support.
	One byte compression method (always zero)
*/
	*c = (ssl->cipher->id & 0xFF00) >> 8; c++;
	*c = ssl->cipher->id & 0xFF; c++;
	*c = 0; c++;

	if ((rc = encryptRecord(ssl, SSL_RECORD_TYPE_HANDSHAKE, messageSize,
			padLen, encryptStart, out, &c)) < 0) {
		return rc;
	}
/*
	If we're resuming a session, we now have the clientRandom, master and 
	serverRandom, so we can derive keys which we'll be using shortly.
*/
	if (ssl->flags & SSL_FLAGS_RESUMED) {
		sslDeriveKeys(ssl);
	}
/*
	Verify that we've calculated the messageSize correctly, really this
	should never fail; it's more of an implementation verification
*/
	if (c - out->end != messageSize) {
		return SSL_ERROR;
	}
	out->end = c;
	return SSL_SUCCESS;
}
Exemple #25
0
/*
	Implementations of this specification MUST be prepared to receive
	the following standard attribute types in issuer names:
	country, organization, organizational-unit, distinguished name qualifier,
	state or province name, and common name 
*/
int32 getDNAttributes(psPool_t *pool, unsigned char **pp, int32 len, 
						   DNattributes_t *attribs)
{
	sslSha1Context_t	hash;
	unsigned char		*p = *pp;
	unsigned char		*dnEnd, *dnStart;
	int32				llen, setlen, arcLen, id, stringType;
	char				*stringOut;

	dnStart = p;
	if (getSequence(&p, len, &llen) < 0) {
		return -1;
	}
	dnEnd = p + llen;

	matrixSha1Init(&hash);
	while (p < dnEnd) {
		if (getSet(&p, (int32)(dnEnd - p), &setlen) < 0) {
			return -1;
		}
		if (getSequence(&p, (int32)(dnEnd - p), &llen) < 0) {
			return -1;
		}
		if (dnEnd <= p || (*(p++) != ASN_OID) ||
				asnParseLength(&p, (int32)(dnEnd - p), &arcLen) < 0 || 
				(dnEnd - p) < arcLen) {
			return -1;
		}
/*
		id-at   OBJECT IDENTIFIER       ::=     {joint-iso-ccitt(2) ds(5) 4}
		id-at-commonName		OBJECT IDENTIFIER		::=		{id-at 3}
		id-at-countryName		OBJECT IDENTIFIER		::=		{id-at 6}
		id-at-localityName		OBJECT IDENTIFIER		::=		{id-at 7}
		id-at-stateOrProvinceName		OBJECT IDENTIFIER	::=	{id-at 8}
		id-at-organizationName			OBJECT IDENTIFIER	::=	{id-at 10}
		id-at-organizationalUnitName	OBJECT IDENTIFIER	::=	{id-at 11}
*/
		*pp = p;
/*
		FUTURE: Currently skipping OIDs not of type {joint-iso-ccitt(2) ds(5) 4}
		However, we could be dealing with an OID we MUST support per RFC.
		domainComponent is one such example.
*/
		if (dnEnd - p < 2) {
			return -1;
		}
		if ((*p++ != 85) || (*p++ != 4) ) {
			p = *pp;
/*
			Move past the OID and string type, get data size, and skip it.
			NOTE: Have had problems parsing older certs in this area.
*/
			if (dnEnd - p < arcLen + 1) {
				return -1;
			}
			p += arcLen + 1;
			if (asnParseLength(&p, (int32)(dnEnd - p), &llen) < 0 || 
					dnEnd - p < llen) {
				return -1;
			}
			p = p + llen;
			continue;
		}
/*
		Next are the id of the attribute type and the ASN string type
*/
		if (arcLen != 3 || dnEnd - p < 2) {
			return -1;
		}
		id = (int32)*p++;
/*
		Done with OID parsing
*/
		stringType = (int32)*p++;

		asnParseLength(&p, (int32)(dnEnd - p), &llen);
		if (dnEnd - p < llen) {
			return -1;
		}
		switch (stringType) {
			case ASN_PRINTABLESTRING:
			case ASN_UTF8STRING:
			case ASN_IA5STRING:
			case ASN_T61STRING:
			case ASN_BMPSTRING:
				stringOut = psMalloc(pool, llen + 1);
				if (stringOut == NULL) {
					return -8; /* SSL_MEM_ERROR */
				}
				memcpy(stringOut, p, llen);
				stringOut[llen] = '\0';
				p = p + llen;
				break;
			default:
				matrixStrDebugMsg("Parsing untested DN attrib type\n", NULL);
				return -1;
		}

		switch (id) {
			case ATTRIB_COUNTRY_NAME:
				if (attribs->country) {
					psFree(attribs->country);
				}
				attribs->country = stringOut;
				break;
			case ATTRIB_STATE_PROVINCE:
				if (attribs->state) {
					psFree(attribs->state);
				}
				attribs->state = stringOut;
				break;
			case ATTRIB_LOCALITY:
				if (attribs->locality) {
					psFree(attribs->locality);
				}
				attribs->locality = stringOut;
				break;
			case ATTRIB_ORGANIZATION:
				if (attribs->organization) {
					psFree(attribs->organization);
				}
				attribs->organization = stringOut;
				break;
			case ATTRIB_ORG_UNIT:
				if (attribs->orgUnit) {
					psFree(attribs->orgUnit);
				}
				attribs->orgUnit = stringOut;
				break;
			case ATTRIB_COMMON_NAME:
				if (attribs->commonName) {
					psFree(attribs->commonName);
				}
				attribs->commonName = stringOut;
				break;
/*
			Not a MUST support
*/
			default:
				psFree(stringOut);
				stringOut = NULL;
				break;
		}
/*
		Hash up the DN.  Nice for validation later
*/
		if (stringOut != NULL) {
			matrixSha1Update(&hash, (unsigned char*)stringOut, llen);
		}
	}
	matrixSha1Final(&hash, (unsigned char*)attribs->hash);
	*pp = p;
	return 0;
}
Exemple #26
0
/*
 *	Public API to return a binary buffer from a cert.  Suitable to send
 *	over the wire.  Caller must free 'out' if this function returns success (0)
 *	Parse .pem files according to http://www.faqs.org/rfcs/rfc1421.html
 */
int32 matrixX509ReadCert(psPool_t *pool, const char *fileName,
					unsigned char **out, int32 *outLen, sslChainLen_t *chain)
{
	int32			certBufLen, rc, certChainLen, i;
	unsigned char	*oneCert[MAX_CHAIN_LENGTH];
	unsigned char	*certPtr, *tmp;
	char			*certFile, *start, *end, *certBuf;
	const char		sep[] = ";";

/*
	Init chain array and output params
*/
	for (i=0; i < MAX_CHAIN_LENGTH; i++) {
		oneCert[i] = NULL;
		(*chain)[i] = 0;
	}
	*outLen = certChainLen = i = 0;
	rc = -1;

/*
	For PKI product purposes, this routine now accepts a chain of certs.
*/
	if (fileName != NULL) {
		fileName += parseList(pool, fileName, sep, &certFile);
	} else {
		return 0;
	}

	while (certFile != NULL) {
	
		if (i == MAX_CHAIN_LENGTH) {
			matrixIntDebugMsg("Exceeded maximum cert chain length of %d\n",
				MAX_CHAIN_LENGTH);
			psFree(certFile);
			rc = -1;
			goto err;
		}
		if ((rc = psGetFileBin(pool, certFile, (unsigned char**)&certBuf,
				&certBufLen)) < 0) {
			matrixStrDebugMsg("Couldn't open file %s\n", certFile);
			goto err;
		}
		psFree(certFile);
		certPtr = (unsigned char*)certBuf;
		start = end = certBuf;

		while (certBufLen > 0) {
			if (((start = strstr(certBuf, "-----BEGIN")) != NULL) && 
					((start = strstr(certBuf, "CERTIFICATE-----")) != NULL) &&
					(end = strstr(start, "-----END")) != NULL) {
				start += strlen("CERTIFICATE-----");
				(*chain)[i] = (int32)(end - start);
				end += strlen("-----END CERTIFICATE-----");
				while (*end == '\r' || *end == '\n' || *end == '\t' || *end == ' ') {
					end++;
				}
			} else {
				psFree(certPtr);
				rc = -1;
				goto err;
			}
			oneCert[i] = psMalloc(pool, (*chain)[i]);
			certBufLen -= (int32)(end - certBuf);
			certBuf = end;
			memset(oneCert[i], '\0', (*chain)[i]);

			if (ps_base64_decode((unsigned char*)start, (*chain)[i], oneCert[i],
					&(*chain)[i]) != 0) {
				psFree(certPtr);
				matrixStrDebugMsg("Unable to base64 decode certificate\n", NULL);
				rc = -1;
				goto err;
			}
			certChainLen += (*chain)[i];
			i++;
			if (i == MAX_CHAIN_LENGTH) { 
				matrixIntDebugMsg("Exceeded maximum cert chain length of %d\n", 
					MAX_CHAIN_LENGTH); 
				psFree(certPtr); 
				rc = -1; 
				goto err; 
			} 
		}
		psFree(certPtr);
/*
		Check for more files
*/
		fileName += parseList(pool, fileName, sep, &certFile);
	}

	*outLen = certChainLen;
/*
	Don't bother stringing them together if only one was passed in
*/
	if (i == 1) {
		sslAssert(certChainLen == (*chain)[0]);
		*out = oneCert[0];
		return 0;
	} else {
		*out = tmp = psMalloc(pool, certChainLen);
		for (i=0; i < MAX_CHAIN_LENGTH; i++) {
			if (oneCert[i]) {
				memcpy(tmp, oneCert[i], (*chain)[i]);
				tmp += (*chain)[i];
			}
		}
		rc = 0;
	}

err:
	for (i=0; i < MAX_CHAIN_LENGTH; i++) {
		if (oneCert[i]) psFree(oneCert[i]);
	}
	return rc;
}
Exemple #27
0
/*
	Preferred version for commercial users who make use of memory pools.

	This use of the sslKeys_t param implies this is for use in the MatrixSSL
	product (input to matrixSslNewSession).  However, we didn't want to
	expose this API at the matrixSsl.h level due to the pool parameter. This
	is strictly an API that commerical users will have access to
*/
int32 matrixRsaReadKeysEx(psPool_t *pool, sslKeys_t **keys,
				const char *certFile, const char *privFile,
				const char *privPass, const char *trustedCAFiles)
{
	sslKeys_t		*lkeys;
	unsigned char	*privKeyMem;
	int32			rc, privKeyMemLen;
#ifdef USE_CLIENT_SIDE_SSL
	sslRsaCert_t	*currCert, *prevCert = NULL;
	unsigned char	*caCert, *caStream;
	sslChainLen_t	chain;
	int32			caCertLen, first, i;
#endif /* USE_CLIENT_SIDE_SSL */

	*keys = lkeys = psMalloc(pool, sizeof(sslKeys_t));
	if (lkeys == NULL) {
		return -8; /* SSL_MEM_ERROR */
	}
	memset(lkeys, 0x0, sizeof(sslKeys_t));
/*
	Load certificate files.  Any additional certificate files should chain
	to the root CA held on the other side.
*/
	rc = readCertChain(pool, certFile, &lkeys->cert);
	if (rc < 0 ) {
		matrixRsaFreeKeys(lkeys);
		return rc;
	}
/*
	The first cert in certFile must be associated with the provided
	private key. 
*/
	if (privFile) {
		rc = matrixRsaReadPrivKey(pool, privFile, privPass, &privKeyMem,
			&privKeyMemLen);
		if (rc < 0) {
			matrixStrDebugMsg("Error reading private key file: %s\n",
				(char*)privFile);
			matrixRsaFreeKeys(lkeys);
			return rc;
		}
		rc = matrixRsaParsePrivKey(pool, privKeyMem, privKeyMemLen,
			&lkeys->cert.privKey);
		if (rc < 0) {
			matrixStrDebugMsg("Error parsing private key file: %s\n",
				(char*)privFile);
			psFree(privKeyMem);
			matrixRsaFreeKeys(lkeys);
			return rc;
		}
		psFree(privKeyMem);
	}

#ifdef USE_CLIENT_SIDE_SSL
/*
	Now deal with Certificate Authorities
*/
	if (trustedCAFiles != NULL) {
		if (matrixX509ReadCert(pool, trustedCAFiles, &caCert, &caCertLen,
				&chain) < 0 || caCert == NULL) {
			matrixStrDebugMsg("Error reading CA cert files %s\n",
				(char*)trustedCAFiles);
			matrixRsaFreeKeys(lkeys);
			return -1;
		}

		caStream = caCert;
		i = first = 0;
		while (chain[i] != 0) {
/*
			Don't allow one bad cert to ruin the whole bunch if possible
*/
			if (matrixX509ParseCert(pool, caStream, chain[i], &currCert) < 0) {
				matrixX509FreeCert(currCert);
				matrixStrDebugMsg("Error parsing CA cert %s\n",
					(char*)trustedCAFiles);
				caStream += chain[i]; caCertLen -= chain[i];
				i++;
				continue;
			}
		
			if (first == 0) {
				lkeys->caCerts = currCert;
			} else {
				prevCert->next = currCert;
			}
			first++;
			prevCert = currCert;
			currCert = NULL;
			caStream += chain[i]; caCertLen -= chain[i];
			i++;
		}
		sslAssert(caCertLen == 0);
		psFree(caCert);
	}
/*
	Check to see that if a set of CAs were passed in at least
	one ended up being valid.
*/
	if (trustedCAFiles != NULL && lkeys->caCerts == NULL) {
		matrixStrDebugMsg("No valid CA certs in %s\n",
			(char*)trustedCAFiles);
		matrixRsaFreeKeys(lkeys);
		return -1;
	}
#endif /* USE_CLIENT_SIDE_SSL */
	return 0; 
}
Exemple #28
0
static int32 matrixX509ValidateCertInternal(psPool_t *pool, sslRsaCert_t *subjectCert, 
						   sslRsaCert_t *issuerCert, int32 chain)
{
	sslRsaCert_t	*ic;
	unsigned char	sigOut[10 + SSL_SHA1_HASH_SIZE + 5];	/* See below */
	int32			sigLen;

	subjectCert->valid = -1;
/*
	Supporting a one level chain or a self-signed cert.  If the issuer
	is NULL, the self-signed test is done.
*/
	if (issuerCert == NULL) {
		matrixStrDebugMsg("Warning:  No CA to validate cert with\n", NULL);
		matrixStrDebugMsg("\tPerforming self-signed CA test\n", NULL);
		ic = subjectCert;
	} else {
		ic = issuerCert;
	}
/*
	Path confirmation.	If this is a chain verification, do not allow
	any holes in the path.  Error out if issuer does not have CA permissions
	or if hashes do not match anywhere along the way.
*/
	while (ic) {
		if (subjectCert != ic) {
/*
			Certificate authority contraint32 only available in version 3 certs
*/
			if ((ic->version > 1) && (ic->extensions.bc.ca <= 0)) {
				if (chain) {
					return -1;
				}
				ic = ic->next;
				continue;
			}
/*
			Use sha1 hash of issuer fields computed at parse time to compare
*/
			if (memcmp(subjectCert->issuer.hash, ic->subject.hash,
					SSL_SHA1_HASH_SIZE) != 0) {
				if (chain) {
					return -1;
				}
				ic = ic->next;
				continue;
			}
		}
/*
		Signature confirmation
		The sigLen is the ASN.1 size in bytes for encoding the hash.
		The magic 10 is comprised of the SEQUENCE and ALGORITHM ID overhead.
		The magic 8 and 5 are the OID lengths of the corresponding algorithm.
		NOTE: if sigLen is modified, above sigOut static size must be changed
*/
		if (subjectCert->sigAlgorithm ==  OID_RSA_MD5 ||
				subjectCert->sigAlgorithm == OID_RSA_MD2) {
			sigLen = 10 + SSL_MD5_HASH_SIZE + 8;	/* See above */
		} else if (subjectCert->sigAlgorithm == OID_RSA_SHA1) {
			sigLen = 10 + SSL_SHA1_HASH_SIZE + 5;	/* See above */
		} else {
			matrixStrDebugMsg("Unsupported signature algorithm\n", NULL);
			return -1;
		}
		sslAssert(sigLen <= sizeof(sigOut));
		matrixRsaDecryptPub(pool, &(ic->publicKey), subjectCert->signature,
			subjectCert->signatureLen, sigOut, sigLen);
/*
		If this is a chain test, fail on any gaps in the chain
*/
		if (psAsnConfirmSignature(subjectCert->sigHash, sigOut, sigLen) < 0) {
			if (chain) {
				return -1;
			}
			ic = ic->next;
			continue;
		}
/*
		Fall through to here only if passed signature check.
*/
		subjectCert->valid = 1;
		break;
	}
	return 0;
}
Exemple #29
0
/*
	X509v3 extensions
*/
static int32 getExplicitExtensions(psPool_t *pool, unsigned char **pp, 
								 int32 inlen, int32 expVal,
								 v3extensions_t *extensions)
{
	unsigned char		*p = *pp, *end;
	unsigned char		*extEnd, *extStart;
	int32				len, noid, tmpLen, critical, fullExtLen;
	unsigned char		oid[SSL_MD5_HASH_SIZE];
	sslMd5Context_t		md5ctx;

	end = p + inlen;
	if (inlen < 1) {
		return -1;
	}
/*
	Not treating this as an error because it is optional.
*/
	if (*p != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | expVal)) {
		return 0;
	}
	p++;
	if (asnParseLength(&p, (int32)(end - p), &len) < 0 || (end - p) < len) {
		return -1;
	}
/*
	Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension

	Extension  ::=  SEQUENCE {
		extnID		OBJECT IDENTIFIER,
		extnValue	OCTET STRING	}
*/
	if (getSequence(&p, (int32)(end - p), &len) < 0) {
		return -1;
	}
	extEnd = p + len;
	while ((p != extEnd) && *p == (ASN_SEQUENCE | ASN_CONSTRUCTED)) {
		if (getSequence(&p, (int32)(extEnd - p), &fullExtLen) < 0) {
			return -1;
		}
		extStart = p;
/*
		Conforming CAs MUST support key identifiers, basic constraints,
		key usage, and certificate policies extensions
	
		id-ce-authorityKeyIdentifier	OBJECT IDENTIFIER ::=	{ id-ce 35 }
		id-ce-basicConstraints			OBJECT IDENTIFIER ::=	{ id-ce 19 } 133
		id-ce-keyUsage					OBJECT IDENTIFIER ::=	{ id-ce 15 }
		id-ce-certificatePolicies		OBJECT IDENTIFIER ::=	{ id-ce 32 }
		id-ce-subjectAltName			OBJECT IDENTIFIER ::=	{ id-ce 17 }  131
*/
		if (extEnd - p < 1 || *p++ != ASN_OID) {
			return -1;
		}
		
		if (asnParseLength(&p, (int32)(extEnd - p), &len) < 0 || 
				(extEnd - p) < len) {
			return -1;
		}
/*
		Send the OID through a digest to get the unique id
*/
		matrixMd5Init(&md5ctx);
		while (len-- > 0) {
			matrixMd5Update(&md5ctx, p, sizeof(char));
			p++;
		}
		matrixMd5Final(&md5ctx, oid);
		noid = lookupExt(oid);

/*
		Possible boolean value here for 'critical' id.  It's a failure if a
		critical extension is found that is not supported
*/
		critical = 0;
		if (*p == ASN_BOOLEAN) {
			p++;
			if (*p++ != 1) {
				matrixStrDebugMsg("Error parsing cert extension\n", NULL);
				return -1;
			}
			if (*p++ > 0) {
				critical = 1;
			}
		}
		if (extEnd - p < 1 || (*p++ != ASN_OCTET_STRING) ||
				asnParseLength(&p, (int32)(extEnd - p), &len) < 0 || 
				extEnd - p < len) {
			matrixStrDebugMsg("Expecting OCTET STRING in ext parse\n", NULL);
			return -1;
		}

		switch (noid) {
/*
			 BasicConstraints ::= SEQUENCE {
				cA						BOOLEAN DEFAULT FALSE,
				pathLenConstraint		INTEGER (0..MAX) OPTIONAL }
*/
			case EXT_BASIC_CONSTRAINTS:
				if (getSequence(&p, (int32)(extEnd - p), &len) < 0) {
					return -1;
				}
/*
				"This goes against PKIX guidelines but some CAs do it and some
				software requires this to avoid interpreting an end user
				certificate as a CA."
					- OpenSSL certificate configuration doc

				basicConstraints=CA:FALSE
*/
				if (len == 0) {
					break;
				}
				if (extEnd - p < 3) {
					return -1;
				}
				if (*p++ != ASN_BOOLEAN) {
					return -1;
				}
				if (*p++ != 1) {
					return -1;
				}
				extensions->bc.ca = *p++;
/*
				Now need to check if there is a path constraint. Only makes
				sense if cA is true.  If it's missing, there is no limit to
				the cert path
*/
				if (*p == ASN_INTEGER) {
					if (getInteger(&p, (int32)(extEnd - p),
							&(extensions->bc.pathLenConstraint)) < 0) {
						return -1;
					}
				} else {
					extensions->bc.pathLenConstraint = -1;
				}
				break;
			case EXT_ALT_SUBJECT_NAME:
				if (getSequence(&p, (int32)(extEnd - p), &len) < 0) {
					return -1;
				}
/*
				Looking only for DNS, URI, and email here to support
				FQDN for Web clients

				FUTURE:  Support all subject alt name members
				GeneralName ::= CHOICE {
					otherName						[0]		OtherName,
					rfc822Name						[1]		IA5String,
					dNSName							[2]		IA5String,
					x400Address						[3]		ORAddress,
					directoryName					[4]		Name,
					ediPartyName					[5]		EDIPartyName,
					uniformResourceIdentifier		[6]		IA5String,
					iPAddress						[7]		OCTET STRING,
					registeredID					[8]		OBJECT IDENTIFIER }
*/
				while (len > 0) {
					if (*p == (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 2)) {
						p++;
						tmpLen = *p++;
						if (extEnd - p < tmpLen) {
							return -1;
						}
						extensions->san.dns = psMalloc(pool, tmpLen + 1);
						if (extensions->san.dns == NULL) {
							return -8; /* SSL_MEM_ERROR */
						}
						memset(extensions->san.dns, 0x0, tmpLen + 1);
						memcpy(extensions->san.dns, p, tmpLen);
					} else if (*p == (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 6)) {
						p++;
						tmpLen = *p++;
						if (extEnd - p < tmpLen) {
							return -1;
						}
						extensions->san.uri = psMalloc(pool, tmpLen + 1);
						if (extensions->san.uri == NULL) {
							return -8; /* SSL_MEM_ERROR */
						}
						memset(extensions->san.uri, 0x0, tmpLen + 1);
						memcpy(extensions->san.uri, p, tmpLen);
					} else if (*p == (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 1)) {
						p++;
						tmpLen = *p++;
						if (extEnd - p < tmpLen) {
							return -1;
						}
						extensions->san.email = psMalloc(pool, tmpLen + 1);
						if (extensions->san.email == NULL) {
							return -8; /* SSL_MEM_ERROR */
						}
						memset(extensions->san.email, 0x0, tmpLen + 1);
						memcpy(extensions->san.email, p, tmpLen);
					} else {
						matrixStrDebugMsg("Unsupported subjectAltName type.n",
							NULL);
						p++;
						tmpLen = *p++;
						if (extEnd - p < tmpLen) {
							return -1;
						}
					}
					p = p + tmpLen;
					len -= tmpLen + 2; /* the magic 2 is the type and length */
				}
				break;
#ifdef USE_FULL_CERT_PARSE
			case EXT_AUTH_KEY_ID:
/*
				AuthorityKeyIdentifier ::= SEQUENCE {
				keyIdentifier				[0] KeyIdentifier			OPTIONAL,
				authorityCertIssuer			[1] GeneralNames			OPTIONAL,
				authorityCertSerialNumber	[2] CertificateSerialNumber	OPTIONAL }

				KeyIdentifier ::= OCTET STRING
*/
				if (getSequence(&p, (int32)(extEnd - p), &len) < 0) {
					return -1;
				}
/*
				Have seen a cert that has a zero length ext here.  Let it pass.
*/
				if (len == 0) {
					break;
				}
/*
				All memebers are optional
*/
				if (*p == (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 0)) {
					p++;
					if (asnParseLength(&p, (int32)(extEnd - p), 
							&extensions->ak.keyLen) < 0 ||
							extEnd - p < extensions->ak.keyLen) {
						return -1;
					}
					extensions->ak.keyId = psMalloc(pool, extensions->ak.keyLen);
					if (extensions->ak.keyId == NULL) {
						return -8; /* SSL_MEM_ERROR */
					}
					memcpy(extensions->ak.keyId, p, extensions->ak.keyLen);
					p = p + extensions->ak.keyLen;
				}
				if (*p == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1)) {
					p++;
					if (asnParseLength(&p, (int32)(extEnd - p), &len) < 0 ||
							len < 1 || extEnd - p < len) {
						return -1;
					}
					if ((*p ^ ASN_CONTEXT_SPECIFIC ^ ASN_CONSTRUCTED) != 4) {
/*
						FUTURE: support other name types
						We are just dealing with DN formats here
*/
						matrixIntDebugMsg("Error auth key-id name type: %d\n",
							*p ^ ASN_CONTEXT_SPECIFIC ^ ASN_CONSTRUCTED);
						return -1;
					}
					p++;
					if (asnParseLength(&p, (int32)(extEnd - p), &len) < 0 || 
							extEnd - p < len) {
						return -1;
					}
					if (getDNAttributes(pool, &p, (int32)(extEnd - p),
							&(extensions->ak.attribs)) < 0) {
						return -1;
					}
				}
				if ((*p == (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 2)) ||
						(*p == ASN_INTEGER)){
/*
					Treat as a serial number (not a native INTEGER)
*/
					if (getSerialNum(pool, &p, (int32)(extEnd - p),
							&(extensions->ak.serialNum), &len) < 0) {
						return -1;
					}
					extensions->ak.serialNumLen = len;
				}
				break;

			case EXT_KEY_USAGE:
/*
				KeyUsage ::= BIT STRING {
					digitalSignature		(0),
					nonRepudiation			(1),
					keyEncipherment			(2),
					dataEncipherment		(3),
					keyAgreement			(4),
					keyCertSign				(5),

					cRLSign					(6),
					encipherOnly			(7),
					decipherOnly			(8) }
*/
				if (*p++ != ASN_BIT_STRING) {
					return -1;
				}
				if (asnParseLength(&p, (int32)(extEnd - p), &len) < 0 || 
						extEnd - p < len) {
					return -1;
				}
				if (len != 2) {
					return -1;
				}
/*
				Assure all unused bits are 0 and store away
*/
				extensions->keyUsage = (*(p + 1)) & ~((1 << *p) -1);
				p = p + len;
				break;
			case EXT_SUBJ_KEY_ID:
/*
				The value of the subject key identifier MUST be the value
				placed in the key identifier field of the Auth Key Identifier
				extension of certificates issued by the subject of
				this certificate.
*/
				if (*p++ != ASN_OCTET_STRING || asnParseLength(&p,
						(int32)(extEnd - p), &(extensions->sk.len)) < 0 ||
						extEnd - p < extensions->sk.len) {
					return -1;
				}
				extensions->sk.id = psMalloc(pool, extensions->sk.len);
				if (extensions->sk.id == NULL) {
					return -8; /* SSL_MEM_ERROR */
				}
				memcpy(extensions->sk.id, p, extensions->sk.len);
				p = p + extensions->sk.len;
				break;
#endif /* USE_FULL_CERT_PARSE */
/*
			Unsupported or skipping because USE_FULL_CERT_PARSE is undefined
*/
			default:
				if (critical) {
/*
					SPEC DIFFERENCE:  Ignoring an unrecognized critical
					extension.  The specification dictates an error should
					occur, but real-world experience has shown this is not
					a realistic or desirable action.  Also, no other SSL
					implementations have been found to error in this case.
					It is NOT a security risk in an RSA authenticaion sense.
*/
					matrixStrDebugMsg("Unknown critical ext encountered\n",
						NULL);
				}
				p++;
/*
				Skip over based on the length reported from the ASN_SEQUENCE
				surrounding the entire extension.  It is not a guarantee that
				the value of the extension itself will contain it's own length.
*/
				p = p + (fullExtLen - (p - extStart));
				break;
		}
	}
	*pp = p;
	return 0;
}
/*
	Server initiated rehandshake public API call.
*/
int32 matrixSslEncodeHelloRequest(ssl_t *ssl, sslBuf_t *out)
{
	unsigned char	*c, *end, *encryptStart;
	char			padLen;
	int32				messageSize, rc;

	if (ssl->flags & SSL_FLAGS_ERROR || ssl->flags & SSL_FLAGS_CLOSED) {
		return SSL_ERROR;
	}
	if (!(ssl->flags & SSL_FLAGS_SERVER) || (ssl->hsState != SSL_HS_DONE)) {
		return SSL_ERROR;
	}

	c = out->end;
	end = out->buf + out->size;
	messageSize =
		ssl->recordHeadLen +
		ssl->hshakeHeadLen;
	if ((rc = writeRecordHeader(ssl, SSL_RECORD_TYPE_HANDSHAKE,
			SSL_HS_HELLO_REQUEST, &messageSize, &padLen,
			&encryptStart, &end, &c)) < 0) {
		return rc;
	}

	if ((rc = encryptRecord(ssl, SSL_RECORD_TYPE_HANDSHAKE, messageSize,
			padLen, encryptStart, out, &c)) < 0) {
		return rc;
	}

	if (c - out->end != messageSize) {
		matrixStrDebugMsg("Error generating hello request for write\n", NULL);
		return SSL_ERROR;
	}
	out->end = c;

	return SSL_SUCCESS;
}
#else /* USE_SERVER_SIDE_SSL */
int32 matrixSslEncodeHelloRequest(ssl_t *ssl, sslBuf_t *out)
{
		matrixStrDebugMsg("Library not built with USE_SERVER_SIDE_SSL\n", NULL);
		return -1;
}