summaryrefslogtreecommitdiff
path: root/release/src/router/matrixssl/src/pki/x509.c
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2015-01-03 13:58:15 +0100
committerAndreas Baumann <mail@andreasbaumann.cc>2015-01-03 13:58:15 +0100
commit4aca87515a5083ae0e31ce3177189fd43b6d05ac (patch)
tree7b1d9a31393ca090757dc6f0d3859b4fcd93f271 /release/src/router/matrixssl/src/pki/x509.c
parent008d0be72b2f160382c6e880765e96b64a050c65 (diff)
downloadtomato-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.c1699
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, &currentCA) < 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 */
+
+
+/******************************************************************************/
+
+