diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2015-01-03 13:58:15 +0100 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2015-01-03 13:58:15 +0100 |
commit | 4aca87515a5083ae0e31ce3177189fd43b6d05ac (patch) | |
tree | 7b1d9a31393ca090757dc6f0d3859b4fcd93f271 /release/src/router/matrixssl/src/pki/x509.c | |
parent | 008d0be72b2f160382c6e880765e96b64a050c65 (diff) | |
download | tomato-4aca87515a5083ae0e31ce3177189fd43b6d05ac.tar.gz tomato-4aca87515a5083ae0e31ce3177189fd43b6d05ac.tar.bz2 |
patch to Vanilla Tomato 1.28
Diffstat (limited to 'release/src/router/matrixssl/src/pki/x509.c')
-rw-r--r-- | release/src/router/matrixssl/src/pki/x509.c | 1699 |
1 files changed, 1699 insertions, 0 deletions
diff --git a/release/src/router/matrixssl/src/pki/x509.c b/release/src/router/matrixssl/src/pki/x509.c new file mode 100644 index 00000000..fdd3e678 --- /dev/null +++ b/release/src/router/matrixssl/src/pki/x509.c @@ -0,0 +1,1699 @@ +/* + * x509.c + * Release $Name: MATRIXSSL_1_8_8_OPEN $ + * + * DER/BER coding + */ +/* + * Copyright (c) PeerSec Networks, 2002-2009. All Rights Reserved. + * The latest version of this code is available at http://www.matrixssl.org + * + * This software is open source; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This General Public License does NOT permit incorporating this software + * into proprietary programs. If you are unable to comply with the GPL, a + * commercial license for this software may be purchased from PeerSec Networks + * at http://www.peersec.com + * + * This program is distributed in WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * http://www.gnu.org/copyleft/gpl.html + */ +/******************************************************************************/ + +#include "pkiInternal.h" + +/* + X509 is wrapped in USE_RSA until more key types are added +*/ +#ifdef USE_RSA +#ifdef USE_X509 + +#define IMPLICIT_ISSUER_ID 1 +#define IMPLICIT_SUBJECT_ID 2 +#define EXPLICIT_EXTENSION 3 + +#define RSA_SIG 1 +#define DSA_SIG 2 + +#define OID_SHA1 88 +#define OID_MD2 646 +#define OID_MD5 649 + +/* + Certificate extension hash mappings +*/ +#define EXT_BASIC_CONSTRAINTS 1 +#define EXT_KEY_USAGE 2 +#define EXT_SUBJ_KEY_ID 3 +#define EXT_AUTH_KEY_ID 4 +#define EXT_ALT_SUBJECT_NAME 5 + +static const struct { + unsigned char hash[16]; + int32 id; +} extTable[] = { + { { 0xa5, 0xc4, 0x5e, 0x9a, 0xa3, 0xbb, 0x71, 0x2f, 0x07, + 0xf7, 0x4c, 0xd0, 0xcd, 0x95, 0x65, 0xda }, EXT_BASIC_CONSTRAINTS }, + { { 0xf5, 0xab, 0x88, 0x49, 0xc4, 0xfd, 0xa2, 0x64, 0x6d, + 0x06, 0xa2, 0x3e, 0x83, 0x9b, 0xef, 0xbb }, EXT_KEY_USAGE }, + { { 0x91, 0x54, 0x28, 0xcc, 0x81, 0x59, 0x8c, 0x71, 0x8c, + 0x53, 0xa8, 0x4d, 0xeb, 0xd3, 0xc2, 0x18 }, EXT_SUBJ_KEY_ID }, + { { 0x48, 0x2d, 0xff, 0x49, 0xf7, 0xab, 0x93, 0xe8, 0x1f, + 0x57, 0xb5, 0xaf, 0x7f, 0xaa, 0x31, 0xbb }, EXT_AUTH_KEY_ID }, + { { 0x5c, 0x70, 0xcb, 0xf5, 0xa4, 0x07, 0x5a, 0xcc, 0xd1, + 0x55, 0xd2, 0x44, 0xdd, 0x62, 0x2c, 0x0c }, EXT_ALT_SUBJECT_NAME }, + { { 0 }, -1 } /* Must be last for proper termination */ +}; + + +static int32 getExplicitExtensions(psPool_t *pool, unsigned char **pp, + int32 len, int32 expVal, + v3extensions_t *extensions); +static int32 matrixX509ValidateCertInternal(psPool_t *pool, + sslCert_t *subjectCert, sslCert_t *issuerCert, int32 chain); +#ifdef USE_FILE_SYSTEM +static int32 parseList(psPool_t *pool, const char *list, const char *sep, + char **item); +#endif /* USE_FILE_SYSTEM */ + +/******************************************************************************/ +/* + 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 matrixX509ReadKeys(sslKeys_t **keys, const char *certFile, + const char *privFile, const char *privPass, + const char *trustedCAFiles) +{ + return matrixX509ReadKeysEx(PEERSEC_BASE_POOL, keys, certFile, privFile, + privPass, trustedCAFiles); +} +#else /* USE_FILE_SYSTEM */ +int32 matrixX509ReadKeys(sslKeys_t **keys, char *certFile, char *privFile, + char *privPass, char *trustedCAFile) +{ + matrixStrDebugMsg("Error: Calling matrixX509ReadKeys against a library " \ + "built without USE_FILE_SYSTEM defined\n", NULL); + return -1; +} +#endif /* USE_FILE_SYSTEM */ + +/******************************************************************************/ +/* + In memory version of matrixX509ReadKeys. The buffers are the ASN.1 raw + stream (ie. not base64 PEM encoded) + + API CHANGE: 1.7 changed this protoype and buffer formats (ASN.1 now) but + this function was never properly documented. Users who may have found + this function on their own and are using it will need to convert to this + new version. +*/ +int32 matrixX509ReadKeysMem(sslKeys_t **keys, unsigned char *certBuf, + int32 certLen, unsigned char *privBuf, int32 privLen, + unsigned char *trustedCABuf, int32 trustedCALen) +{ + return matrixRsaParseKeysMem(PEERSEC_BASE_POOL, keys, certBuf, certLen, + privBuf, privLen, trustedCABuf, trustedCALen); +} + +/******************************************************************************/ +/* + Free private key and cert and zero memory allocated by matrixSslReadKeys. +*/ +void matrixRsaFreeKeys(sslKeys_t *keys) +{ + sslLocalCert_t *current, *next; + int32 i = 0; + + if (keys) { + current = &keys->cert; + while (current) { + if (current->certBin) { + memset(current->certBin, 0x0, current->certLen); + psFree(current->certBin); + } + if (current->privKey) { + matrixRsaFreeKey(current->privKey); + } + next = current->next; + if (i++ > 0) { + psFree(current); + } + current = next; + } +#ifdef USE_CLIENT_SIDE_SSL + if (keys->caCerts) { + matrixX509FreeCert(keys->caCerts); + } +#endif /* USE_CLIENT_SIDE_SSL */ + psFree(keys); + } +} + +#ifdef USE_FILE_SYSTEM +/******************************************************************************/ +/* + 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 matrixX509ReadKeysEx(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 + sslCert_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 = matrixX509ReadPrivKey(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; +} + +/******************************************************************************/ +/* + * 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, *endTmp; + 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 = endTmp = certBuf; + + while (certBufLen > 0) { + if (((start = strstr(certBuf, "-----BEGIN")) != NULL) && + ((start = strstr(certBuf, "CERTIFICATE-----")) != NULL) && + ((end = strstr(start, "-----END")) != NULL) && + ((endTmp = strstr(end,"CERTIFICATE-----")) != NULL)) { + start += strlen("CERTIFICATE-----"); + (*chain)[i] = (int32)(end - start); + end = endTmp + strlen("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; +} + +/******************************************************************************/ +/* + 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; +} + +/******************************************************************************/ +/* + 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; +} + +/******************************************************************************/ +/* + * Strtok substitute + */ +static int32 parseList(psPool_t *pool, const char *list, const char *sep, + char **item) +{ + int32 start, listLen; + char *tmp; + + start = listLen = (int32)strlen(list) + 1; + if (start == 1) { + *item = NULL; + return 0; + } + tmp = *item = psMalloc(pool, listLen); + if (tmp == NULL) { + return -8; /* SSL_MEM_ERROR */ + } + memset(*item, 0, listLen); + while (listLen > 0) { + if (*list == sep[0]) { + list++; + listLen--; + break; + } + if (*list == 0) { + break; + } + *tmp++ = *list++; + listLen--; + } + return start - listLen; +} +#endif /* USE_FILE_SYSTEM */ + + +/******************************************************************************/ +/* + 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 + sslCert_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, ¤tCA) < 0) { + matrixX509FreeCert(currentCA); + 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; +} + +/******************************************************************************/ +/* + In-memory version of matrixX509ReadPubKey. + This function was written strictly for clarity in the PeerSec crypto API + subset. It extracts only the public key from a certificate file for use + in the lower level encrypt/decrypt RSA routines. +*/ +int32 matrixX509ParsePubKey(psPool_t *pool, unsigned char *certBuf, + int32 certLen, sslRsaKey_t **key) +{ + sslRsaKey_t *lkey; + sslCert_t *certStruct; + int32 err; + + if (matrixX509ParseCert(pool, certBuf, certLen, &certStruct) < 0) { + matrixX509FreeCert(certStruct); + return -1; + } + lkey = *key = psMalloc(pool, sizeof(sslRsaKey_t)); + memset(lkey, 0x0, sizeof(sslRsaKey_t)); + + if ((err = _mp_init_multi(pool, &lkey->e, &lkey->N, NULL, + NULL, NULL, NULL, NULL, NULL)) != MP_OKAY) { + matrixX509FreeCert(certStruct); + psFree(lkey); + return err; + } + mp_copy(&certStruct->publicKey.e, &lkey->e); + mp_copy(&certStruct->publicKey.N, &lkey->N); + + mp_shrink(&lkey->e); + mp_shrink(&lkey->N); + + lkey->size = certStruct->publicKey.size; + + matrixX509FreeCert(certStruct); + + return 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, + sslCert_t **outcert) +{ + sslCert_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(sslCert_t)); + if (cert == NULL) { + return -8; /* SSL_MEM_ERROR */ + } + memset(cert, '\0', sizeof(sslCert_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 support@peersec.com\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(sslCert_t)); + cert = cert->next; + memset(cert, '\0', sizeof(sslCert_t)); + } else { + parsing = 0; + } + } + + return (int32)(p - pp); +} + +/******************************************************************************/ +/* + User must call after all calls to matrixX509ParseCert + (we violate the coding standard a bit here for clarity) +*/ +void matrixX509FreeCert(sslCert_t *cert) +{ + sslCert_t *curr, *next; + sslSubjectAltName_t *active, *inc; + + curr = cert; + while (curr) { + psFreeDNStruct(&curr->issuer); + psFreeDNStruct(&curr->subject); + if (curr->serialNumber) psFree(curr->serialNumber); + if (curr->notBefore) psFree(curr->notBefore); + if (curr->notAfter) psFree(curr->notAfter); + if (curr->publicKey.N.dp) mp_clear(&(curr->publicKey.N)); + if (curr->publicKey.e.dp) mp_clear(&(curr->publicKey.e)); + if (curr->signature) psFree(curr->signature); + if (curr->uniqueUserId) psFree(curr->uniqueUserId); + if (curr->uniqueSubjectId) psFree(curr->uniqueSubjectId); + + if (curr->extensions.san) { + active = curr->extensions.san; + while (active != NULL) { + inc = active->next; + psFree(active->data); + psFree(active); + active = inc; + } + } + + +#ifdef USE_FULL_CERT_PARSE + if (curr->extensions.keyUsage) psFree(curr->extensions.keyUsage); + if (curr->extensions.sk.id) psFree(curr->extensions.sk.id); + if (curr->extensions.ak.keyId) psFree(curr->extensions.ak.keyId); + if (curr->extensions.ak.serialNum) + psFree(curr->extensions.ak.serialNum); + if (curr->extensions.ak.attribs.commonName) + psFree(curr->extensions.ak.attribs.commonName); + if (curr->extensions.ak.attribs.country) + psFree(curr->extensions.ak.attribs.country); + if (curr->extensions.ak.attribs.state) + psFree(curr->extensions.ak.attribs.state); + if (curr->extensions.ak.attribs.locality) + psFree(curr->extensions.ak.attribs.locality); + if (curr->extensions.ak.attribs.organization) + psFree(curr->extensions.ak.attribs.organization); + if (curr->extensions.ak.attribs.orgUnit) + psFree(curr->extensions.ak.attribs.orgUnit); +#endif /* SSL_FULL_CERT_PARSE */ + next = curr->next; + psFree(curr); + curr = next; + } +} + +/******************************************************************************/ +/* + Do the signature validation for a subject certificate against a + known CA certificate +*/ +int32 psAsnConfirmSignature(unsigned char *sigHash, unsigned char *sigOut, + int32 sigLen) +{ + unsigned char *end, *p = sigOut; + unsigned char hash[SSL_SHA1_HASH_SIZE]; + int32 len, oi; + + end = p + sigLen; +/* + DigestInfo ::= SEQUENCE { + digestAlgorithm DigestAlgorithmIdentifier, + digest Digest } + + DigestAlgorithmIdentifier ::= AlgorithmIdentifier + + Digest ::= OCTET STRING +*/ + if (getSequence(&p, (int32)(end - p), &len) < 0) { + return -1; + } + +/* + Could be MD5 or SHA1 + */ + if (getAlgorithmIdentifier(&p, (int32)(end - p), &oi, 0) < 0) { + return -1; + } + if ((*p++ != ASN_OCTET_STRING) || + asnParseLength(&p, (int32)(end - p), &len) < 0 || (end - p) < len) { + return -1; + } + memcpy(hash, p, len); + if (oi == OID_MD5 || oi == OID_MD2) { + if (len != SSL_MD5_HASH_SIZE) { + return -1; + } + } else if (oi == OID_SHA1) { + if (len != SSL_SHA1_HASH_SIZE) { + return -1; + } + } else { + return -1; + } +/* + hash should match sigHash +*/ + if (memcmp(hash, sigHash, len) != 0) { + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + Extension lookup +*/ +static int32 lookupExt(unsigned char md5hash[SSL_MD5_HASH_SIZE]) +{ + int32 i, j; + const unsigned char *tmp; + + for (i = 0; ;i++) { + if (extTable[i].id == -1) { + return -1; + } + tmp = extTable[i].hash; + for (j = 0; j < SSL_MD5_HASH_SIZE; j++) { + if (md5hash[j] != tmp[j]) { + break; + } + if (j == SSL_MD5_HASH_SIZE - 1) { + return extTable[i].id; + } + } + } +} + +/******************************************************************************/ +/* + 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, critical, fullExtLen; + unsigned char oid[SSL_MD5_HASH_SIZE]; + sslMd5Context_t md5ctx; + sslSubjectAltName_t *activeName, *prevName; + + 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; + } +/* + Have seen some certs that don't include a cA bool. +*/ + if (*p == ASN_BOOLEAN) { + p++; + if (*p++ != 1) { + return -1; + } + extensions->bc.ca = *p++; + } else { + extensions->bc.ca = 0; + } +/* + 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 (extensions->san == NULL) { + activeName = extensions->san = psMalloc(pool, + sizeof(sslSubjectAltName_t)); + } else { +/* + Find the end +*/ + prevName = extensions->san; + activeName = prevName->next; + while (activeName != NULL) { + prevName = activeName; + activeName = prevName->next; + } + prevName->next = psMalloc(pool, + sizeof(sslSubjectAltName_t)); + activeName = prevName->next; + } + activeName->next = NULL; + activeName->data = NULL; + memset(activeName->name, '\0', 16); + + activeName->id = *p & 0xF; + switch (activeName->id) { + case 0: + memcpy(activeName->name, "other", 5); + break; + case 1: + memcpy(activeName->name, "email", 5); + break; + case 2: + memcpy(activeName->name, "DNS", 3); + break; + case 3: + memcpy(activeName->name, "x400Address", 11); + break; + case 4: + memcpy(activeName->name, "directoryName", 13); + break; + case 5: + memcpy(activeName->name, "ediPartyName", 12); + break; + case 6: + memcpy(activeName->name, "URI", 3); + break; + case 7: + memcpy(activeName->name, "iPAddress", 9); + break; + case 8: + memcpy(activeName->name, "registeredID", 12); + break; + default: + memcpy(activeName->name, "unknown", 7); + break; + } + + p++; + activeName->dataLen = *p++; + if (extEnd - p < activeName->dataLen) { + return -1; + } + activeName->data = psMalloc(pool, activeName->dataLen + 1); + if (activeName->data == NULL) { + return -8; /* SSL_MEM_ERROR */ + } + memset(activeName->data, 0x0, activeName->dataLen + 1); + memcpy(activeName->data, p, activeName->dataLen); + + p = p + activeName->dataLen; + /* the magic 2 is the type and length */ + len -= activeName->dataLen + 2; + } + 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; + } +/* + We'd expect a length of 3 with the first byte being '07' to + account for the trailing ignore bits in the second byte. + But it doesn't appear all certificates adhere to the ASN.1 + encoding standard very closely. Just set it all aside for + user to interpret as necessary. +*/ + extensions->keyUsage = psMalloc(pool, len); + memcpy(extensions->keyUsage, p, len); + extensions->keyUsageLen = len; + 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; +} + +/******************************************************************************/ +/* + Walk through the certificate chain and validate it. Return the final + member of the chain as the subjectCert that can then be validated against + the CAs. The subjectCert points into the chain param (no need to free) +*/ +int32 matrixX509ValidateCertChain(psPool_t *pool, sslCert_t *chain, + sslCert_t **subjectCert, int32 *valid) +{ + sslCert_t *ic; + + *subjectCert = chain; + *valid = 1; + while ((*subjectCert)->next != NULL) { + ic = (*subjectCert)->next; + if (matrixX509ValidateCertInternal(pool, *subjectCert, ic, 1) < 0) { + *valid = -1; + return -1; + } +/* + If any portion is invalid, it's all invalid +*/ + if ((*subjectCert)->valid != 1) { + *valid = -1; + } + *subjectCert = (*subjectCert)->next; + } + return 0; +} + +/******************************************************************************/ +/* + A signature validation for certificates. -1 return is an error. The success + of the validation is returned in the 'valid' param of the subjectCert. + 1 if the issuerCert signed the subject cert. -1 if not +*/ +int32 matrixX509ValidateCert(psPool_t *pool, sslCert_t *subjectCert, + sslCert_t *issuerCert, int32 *valid) +{ + if (matrixX509ValidateCertInternal(pool, subjectCert, issuerCert, 0) < 0) { + *valid = -1; + return -1; + } + *valid = subjectCert->valid; + return 0; +} + +static int32 matrixX509ValidateCertInternal(psPool_t *pool, + sslCert_t *subjectCert, sslCert_t *issuerCert, int32 chain) +{ + sslCert_t *ic; + unsigned char sigOut[10 + SSL_SHA1_HASH_SIZE + 5]; /* See below */ + int32 sigLen, sigType, rc; + + 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 constraint 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) { + sigType = RSA_SIG; + 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 */ + sigType = RSA_SIG; + } else { + matrixStrDebugMsg("Unsupported signature algorithm\n", NULL); + return -1; + } + + if (sigType == RSA_SIG) { + sslAssert(sigLen <= sizeof(sigOut)); + + /* note: on error & no CA, flag as invalid, but don't exit as error here (<1.8.7? behavior) -- zzz */ + if (matrixRsaDecryptPub(pool, &(ic->publicKey), + subjectCert->signature, subjectCert->signatureLen, sigOut, + sigLen) < 0) { + matrixStrDebugMsg("Unable to RSA decrypt signature\n", NULL); + if (issuerCert) return -1; + rc = -1; + } else { + rc = psAsnConfirmSignature(subjectCert->sigHash, sigOut, sigLen); + } + } +/* + If this is a chain test, fail on any gaps in the chain +*/ + if (rc < 0) { + if (chain) { + return -1; + } + ic = ic->next; + continue; + } +/* + Fall through to here only if passed signature check. +*/ + subjectCert->valid = 1; + break; + } + return 0; +} + +/******************************************************************************/ +/* + Calls a user defined callback to allow for manual validation of the + certificate. +*/ +int32 matrixX509UserValidator(psPool_t *pool, sslCert_t *subjectCert, + int32 (*certValidator)(sslCertInfo_t *t, void *arg), void *arg) +{ + sslCertInfo_t *cert, *current, *next; + int32 rc; + + if (certValidator == NULL) { + return 0; + } +/* + Pass the entire certificate chain to the user callback. +*/ + current = cert = psMalloc(pool, sizeof(sslCertInfo_t)); + if (current == NULL) { + return -8; /* SSL_MEM_ERROR */ + } + memset(cert, 0x0, sizeof(sslCertInfo_t)); + while (subjectCert) { + + current->issuer.commonName = subjectCert->issuer.commonName; + current->issuer.country = subjectCert->issuer.country; + current->issuer.locality = subjectCert->issuer.locality; + current->issuer.organization = subjectCert->issuer.organization; + current->issuer.orgUnit = subjectCert->issuer.orgUnit; + current->issuer.state = subjectCert->issuer.state; + + current->subject.commonName = subjectCert->subject.commonName; + current->subject.country = subjectCert->subject.country; + current->subject.locality = subjectCert->subject.locality; + current->subject.organization = subjectCert->subject.organization; + current->subject.orgUnit = subjectCert->subject.orgUnit; + current->subject.state = subjectCert->subject.state; + + current->serialNumber = subjectCert->serialNumber; + current->serialNumberLen = subjectCert->serialNumberLen; + current->verified = subjectCert->valid; + current->notBefore = subjectCert->notBefore; + current->notAfter = subjectCert->notAfter; + + current->subjectAltName = subjectCert->extensions.san; + + if (subjectCert->certAlgorithm == OID_RSA_MD5 || + subjectCert->certAlgorithm == OID_RSA_MD2) { + current->sigHashLen = SSL_MD5_HASH_SIZE; + } else if (subjectCert->certAlgorithm == OID_RSA_SHA1) { + current->sigHashLen = SSL_SHA1_HASH_SIZE; + } + current->sigHash = (char*)subjectCert->sigHash; + if (subjectCert->next) { + next = psMalloc(pool, sizeof(sslCertInfo_t)); + if (next == NULL) { + while (cert) { + next = cert->next; + psFree(cert); + cert = next; + } + return -8; /* SSL_MEM_ERROR */ + } + memset(next, 0x0, sizeof(sslCertInfo_t)); + current->next = next; + current = next; + } + subjectCert = subjectCert->next; + } +/* + The user callback +*/ + rc = certValidator(cert, arg); +/* + Free the chain +*/ + while (cert) { + next = cert->next; + psFree(cert); + cert = next; + } + return rc; +} +#endif /* USE_X509 */ +#endif /* USE_RSA */ + + +/******************************************************************************/ + + |