Decodes the Name Constraints certificate extension on the CA cert

and checks the names on the peer cert, rejecting it if invalid
based on the name.
This commit is contained in:
John Safranek 2014-04-28 11:03:24 -07:00
parent 8d8fca67c3
commit 618d282d94
5 changed files with 407 additions and 1 deletions

View File

@ -1272,6 +1272,11 @@ void InitDecodedCert(DecodedCert* cert, byte* source, word32 inSz, void* heap)
cert->subjectCNLen = 0;
cert->subjectCNStored = 0;
cert->altNames = NULL;
#ifndef IGNORE_NAME_CONSTRAINTS
cert->altEmailNames = NULL;
cert->permittedNames = NULL;
cert->excludedNames = NULL;
#endif /* IGNORE_NAME_CONSTRAINTS */
cert->issuer[0] = '\0';
cert->subject[0] = '\0';
cert->source = source; /* don't own */
@ -1341,6 +1346,9 @@ void InitDecodedCert(DecodedCert* cert, byte* source, word32 inSz, void* heap)
cert->extSubjKeyIdSrc = NULL;
cert->extSubjKeyIdSz = 0;
#endif /* OPENSSL_EXTRA */
#if defined(OPENSSL_EXTRA) || !defined(IGNORE_NAME_CONSTRAINTS)
cert->extNameConstraintSet = 0;
#endif /* OPENSSL_EXTRA || !IGNORE_NAME_CONSTRAINTS */
#ifdef HAVE_ECC
cert->pkCurveOID = 0;
#endif /* HAVE_ECC */
@ -1372,6 +1380,22 @@ void FreeAltNames(DNS_entry* altNames, void* heap)
}
}
#ifndef IGNORE_NAME_CONSTRAINTS
void FreeNameSubtrees(Base_entry* names, void* heap)
{
(void)heap;
while (names) {
Base_entry* tmp = names->next;
XFREE(names->name, heap, DYNAMIC_TYPE_ALTNAME);
XFREE(names, heap, DYNAMIC_TYPE_ALTNAME);
names = tmp;
}
}
#endif /* IGNORE_NAME_CONSTRAINTS */
void FreeDecodedCert(DecodedCert* cert)
{
@ -1381,6 +1405,14 @@ void FreeDecodedCert(DecodedCert* cert)
XFREE(cert->publicKey, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
if (cert->altNames)
FreeAltNames(cert->altNames, cert->heap);
#ifndef IGNORE_NAME_CONSTRAINTS
if (cert->altEmailNames)
FreeAltNames(cert->altEmailNames, cert->heap);
if (cert->permittedNames)
FreeNameSubtrees(cert->permittedNames, cert->heap);
if (cert->excludedNames)
FreeNameSubtrees(cert->excludedNames, cert->heap);
#endif /* IGNORE_NAME_CONSTRAINTS */
#ifdef CYASSL_SEP
XFREE(cert->deviceType, cert->heap, 0);
XFREE(cert->hwType, cert->heap, 0);
@ -1863,7 +1895,30 @@ static int GetName(DecodedCert* cert, int nameType)
dName->emailIdx = cert->srcIdx;
dName->emailLen = adv;
#endif /* OPENSSL_EXTRA */
#ifndef IGNORE_NAME_CONSTRAINTS
{
DNS_entry* emailName = NULL;
emailName = (DNS_entry*)XMALLOC(sizeof(DNS_entry),
cert->heap, DYNAMIC_TYPE_ALTNAME);
if (emailName == NULL) {
CYASSL_MSG("\tOut of Memory");
return MEMORY_E;
}
emailName->name = (char*)XMALLOC(adv + 1,
cert->heap, DYNAMIC_TYPE_ALTNAME);
if (emailName->name == NULL) {
CYASSL_MSG("\tOut of Memory");
return MEMORY_E;
}
XMEMCPY(emailName->name,
&cert->source[cert->srcIdx], adv);
emailName->name[adv] = 0;
emailName->next = cert->altEmailNames;
cert->altEmailNames = emailName;
}
#endif /* IGNORE_NAME_CONSTRAINTS */
if (!tooBig) {
XMEMCPY(&full[idx], &cert->source[cert->srcIdx], adv);
idx += adv;
@ -2870,6 +2925,152 @@ static int ConfirmSignature(const byte* buf, word32 bufSz,
}
#ifndef IGNORE_NAME_CONSTRAINTS
static int MatchBaseName(int type, const char* name, int nameSz,
const char* base, int baseSz)
{
if (base == NULL || baseSz <= 0 || name == NULL || nameSz <= 0 ||
name[0] == '.' || nameSz < baseSz ||
(type != ASN_RFC822_TYPE && type != ASN_DNS_TYPE))
return 0;
/* If an email type, handle special cases where the base is only
* a domain, or is an email address itself. */
if (type == ASN_RFC822_TYPE) {
const char* p = NULL;
int count = 0;
if (base[0] != '.') {
p = base;
count = 0;
/* find the '@' in the base */
while (*p != '@' && count < baseSz) {
count++;
p++;
}
/* No '@' in base, reset p to NULL */
if (count >= baseSz)
p = NULL;
}
if (p == NULL) {
/* Base isn't an email address, it is a domain name,
* wind the name forward one character past its '@'. */
p = name;
count = 0;
while (*p != '@' && count < baseSz) {
count++;
p++;
}
if (count < baseSz && *p == '@') {
name = p + 1;
nameSz -= count + 1;
}
}
}
if ((type == ASN_DNS_TYPE || type == ASN_RFC822_TYPE) && base[0] == '.') {
int szAdjust = nameSz - baseSz;
name += szAdjust;
nameSz -= szAdjust;
}
while (nameSz > 0) {
if (XTOLOWER(*name++) != XTOLOWER(*base++))
return 0;
nameSz--;
}
return 1;
}
static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
{
if (signer == NULL || cert == NULL)
return 0;
/* Check against the excluded list */
if (signer->excludedNames) {
Base_entry* base = signer->excludedNames;
while (base != NULL) {
if (base->type == ASN_DNS_TYPE) {
DNS_entry* name = cert->altNames;
while (name != NULL) {
if (MatchBaseName(ASN_DNS_TYPE,
name->name, (int)XSTRLEN(name->name),
base->name, (int)XSTRLEN(base->name)))
return 0;
name = name->next;
}
}
else if (base->type == ASN_RFC822_TYPE) {
DNS_entry* name = cert->altEmailNames;
while (name != NULL) {
if (MatchBaseName(ASN_RFC822_TYPE,
name->name, (int)XSTRLEN(name->name),
base->name, (int)XSTRLEN(base->name)))
return 0;
name = name->next;
}
}
base = base->next;
}
}
/* Check against the permitted list */
if (signer->permittedNames != NULL) {
int needDns = 0;
int matchDns = 0;
int needEmail = 0;
int matchEmail = 0;
Base_entry* base = signer->permittedNames;
while (base != NULL) {
if (base->type == ASN_DNS_TYPE) {
DNS_entry* name = cert->altNames;
if (name != NULL)
needDns = 1;
while (name != NULL) {
matchDns = MatchBaseName(ASN_DNS_TYPE,
name->name, (int)XSTRLEN(name->name),
base->name, (int)XSTRLEN(base->name));
name = name->next;
}
}
else if (base->type == ASN_RFC822_TYPE) {
DNS_entry* name = cert->altEmailNames;
if (name != NULL)
needEmail = 1;
while (name != NULL) {
matchEmail = MatchBaseName(ASN_DNS_TYPE,
name->name, (int)XSTRLEN(name->name),
base->name, (int)XSTRLEN(base->name));
name = name->next;
}
}
base = base->next;
}
if ((needDns && !matchDns) || (needEmail && !matchEmail))
return 0;
}
return 1;
}
#endif /* IGNORE_NAME_CONSTRAINTS */
static int DecodeAltNames(byte* input, int sz, DecodedCert* cert)
{
word32 idx = 0;
@ -2924,6 +3125,43 @@ static int DecodeAltNames(byte* input, int sz, DecodedCert* cert)
length -= strLen;
idx += strLen;
}
#ifndef IGNORE_NAME_CONSTRAINTS
else if (b == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) {
DNS_entry* emailEntry;
int strLen;
word32 lenStartIdx = idx;
if (GetLength(input, &idx, &strLen, sz) < 0) {
CYASSL_MSG("\tfail: str length");
return ASN_PARSE_E;
}
length -= (idx - lenStartIdx);
emailEntry = (DNS_entry*)XMALLOC(sizeof(DNS_entry), cert->heap,
DYNAMIC_TYPE_ALTNAME);
if (emailEntry == NULL) {
CYASSL_MSG("\tOut of Memory");
return ASN_PARSE_E;
}
emailEntry->name = (char*)XMALLOC(strLen + 1, cert->heap,
DYNAMIC_TYPE_ALTNAME);
if (emailEntry->name == NULL) {
CYASSL_MSG("\tOut of Memory");
XFREE(emailEntry, cert->heap, DYNAMIC_TYPE_ALTNAME);
return ASN_PARSE_E;
}
XMEMCPY(emailEntry->name, &input[idx], strLen);
emailEntry->name[strLen] = '\0';
emailEntry->next = cert->altEmailNames;
cert->altEmailNames = emailEntry;
length -= strLen;
idx += strLen;
}
#endif /* IGNORE_NAME_CONSTRAINTS */
#ifdef CYASSL_SEP
else if (b == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE))
{
@ -3329,7 +3567,7 @@ static int DecodeExtKeyUsage(byte* input, int sz, DecodedCert* cert)
CYASSL_ENTER("DecodeExtKeyUsage");
if (GetSequence(input, &idx, &length, sz) < 0) {
CYASSL_MSG("\tfail: should be a SEQUENCE\n");
CYASSL_MSG("\tfail: should be a SEQUENCE");
return ASN_PARSE_E;
}
@ -3366,6 +3604,103 @@ static int DecodeExtKeyUsage(byte* input, int sz, DecodedCert* cert)
}
#ifndef IGNORE_NAME_CONSTRAINTS
static int DecodeSubtree(byte* input, int sz, Base_entry** head, void* heap)
{
word32 idx = 0;
(void)heap;
while (idx < (word32)sz) {
int seqLength, strLength;
word32 nameIdx;
byte b;
if (GetSequence(input, &idx, &seqLength, sz) < 0) {
CYASSL_MSG("\tfail: should be a SEQUENCE");
return ASN_PARSE_E;
}
nameIdx = idx;
b = input[nameIdx++];
if (GetLength(input, &nameIdx, &strLength, sz) <= 0) {
CYASSL_MSG("\tinvalid length");
return ASN_PARSE_E;
}
if (b == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE) ||
b == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) {
Base_entry* entry = (Base_entry*)XMALLOC(sizeof(Base_entry),
heap, DYNAMIC_TYPE_ALTNAME);
if (entry == NULL) {
CYASSL_MSG("allocate error");
return MEMORY_E;
}
entry->name = (char*)XMALLOC(strLength + 1,
heap, DYNAMIC_TYPE_ALTNAME);
if (entry->name == NULL) {
CYASSL_MSG("allocate error");
return MEMORY_E;
}
XMEMCPY(entry->name, &input[nameIdx], strLength);
entry->name[strLength] = '\0';
entry->type = b & 0x0F;
entry->next = *head;
*head = entry;
}
idx += seqLength;
}
return 0;
}
static int DecodeNameConstraints(byte* input, int sz, DecodedCert* cert)
{
word32 idx = 0;
int length = 0;
CYASSL_ENTER("DecodeNameConstraints");
if (GetSequence(input, &idx, &length, sz) < 0) {
CYASSL_MSG("\tfail: should be a SEQUENCE");
return ASN_PARSE_E;
}
while (idx < (word32)sz) {
byte b = input[idx++];
Base_entry** subtree = NULL;
if (GetLength(input, &idx, &length, sz) <= 0) {
CYASSL_MSG("\tinvalid length");
return ASN_PARSE_E;
}
if (b == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 0))
subtree = &cert->permittedNames;
else if (b == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1))
subtree = &cert->excludedNames;
else {
CYASSL_MSG("\tinvalid subtree");
return ASN_PARSE_E;
}
DecodeSubtree(input + idx, length, subtree, cert->heap);
idx += length;
}
return 0;
}
#endif /* IGNORE_NAME_CONSTRAINTS */
#ifdef CYASSL_SEP
static int DecodeCertPolicy(byte* input, int sz, DecodedCert* cert)
{
@ -3552,6 +3887,17 @@ static int DecodeCertExtensions(DecodedCert* cert)
return ASN_PARSE_E;
break;
#ifndef IGNORE_NAME_CONSTRAINTS
case NAME_CONS_OID:
cert->extNameConstraintSet = 1;
#ifdef OPENSSL_EXTRA
cert->extNameConstraintCrit = critical;
#endif
if (DecodeNameConstraints(&input[idx], length, cert) < 0)
return ASN_PARSE_E;
break;
#endif /* IGNORE_NAME_CONSTRAINTS */
case INHIBIT_ANY_OID:
CYASSL_MSG("Inhibit anyPolicy extension not supported yet.");
break;
@ -3714,6 +4060,14 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm)
CYASSL_MSG("Confirm signature failed");
return ASN_SIG_CONFIRM_E;
}
#ifndef IGNORE_NAME_CONSTRAINTS
/* check that this cert's name is permitted by the signer's
* name constraints */
if (!ConfirmNameConstraints(ca, cert)) {
CYASSL_MSG("Confirm name constraint failed");
return ASN_NAME_INVALID_E;
}
#endif /* IGNORE_NAME_CONSTRAINTS */
}
else {
/* no signer */
@ -3743,6 +4097,10 @@ Signer* MakeSigner(void* heap)
signer->publicKey = NULL;
signer->nameLen = 0;
signer->name = NULL;
#ifndef IGNORE_NAME_CONSTRAINTS
signer->permittedNames = NULL;
signer->excludedNames = NULL;
#endif /* IGNORE_NAME_CONSTRAINTS */
signer->next = NULL;
}
(void)heap;
@ -3756,6 +4114,12 @@ void FreeSigner(Signer* signer, void* heap)
{
XFREE(signer->name, heap, DYNAMIC_TYPE_SUBJECT_CN);
XFREE(signer->publicKey, heap, DYNAMIC_TYPE_PUBLIC_KEY);
#ifndef IGNORE_NAME_CONSTRAINTS
if (signer->permittedNames)
FreeNameSubtrees(signer->permittedNames, heap);
if (signer->excludedNames)
FreeNameSubtrees(signer->excludedNames, heap);
#endif
XFREE(signer, heap, DYNAMIC_TYPE_SIGNER);
(void)heap;

View File

@ -351,6 +351,10 @@ void CTaoCryptErrorString(int error, char* buffer)
XSTRNCPY(buffer, "FIPS mode not allowed error", max);
break;
case ASN_NAME_INVALID_E:
XSTRNCPY(buffer, "Name Constraint error", max);
break;
default:
XSTRNCPY(buffer, "unknown error number", max);

View File

@ -64,6 +64,7 @@ enum ASN_Tags {
ASN_SET = 0x11,
ASN_UTC_TIME = 0x17,
ASN_OTHER_TYPE = 0x00,
ASN_RFC822_TYPE = 0x01,
ASN_DNS_TYPE = 0x02,
ASN_GENERALIZED_TIME = 0x18,
CRL_EXTENSIONS = 0xa0,
@ -219,6 +220,7 @@ enum Extensions_Sum {
KEY_USAGE_OID = 129, /* 2.5.29.15 */
INHIBIT_ANY_OID = 168, /* 2.5.29.54 */
EXT_KEY_USAGE_OID = 151, /* 2.5.29.37 */
NAME_CONS_OID = 144 /* 2.5.29.30 */
};
enum CertificatePolicy_Sum {
@ -272,6 +274,15 @@ struct DNS_entry {
};
typedef struct Base_entry Base_entry;
struct Base_entry {
Base_entry* next; /* next on name base list */
char* name; /* actual name base */
byte type; /* Name base type (DNS or RFC822) */
};
struct DecodedName {
char* fullName;
int fullNameLen;
@ -315,6 +326,11 @@ struct DecodedCert {
word32 keyOID; /* sum of key algo object id */
int version; /* cert version, 1 or 3 */
DNS_entry* altNames; /* alt names list of dns entries */
#ifndef IGNORE_NAME_CONSTRAINTS
DNS_entry* altEmailNames; /* alt names list of RFC822 entries */
Base_entry* permittedNames; /* Permitted name bases */
Base_entry* excludedNames; /* Excluded name bases */
#endif /* IGNORE_NAME_CONSTRAINTS */
byte subjectHash[SHA_SIZE]; /* hash of all Names */
byte issuerHash[SHA_SIZE]; /* hash of all Names */
#ifdef HAVE_OCSP
@ -344,6 +360,9 @@ struct DecodedCert {
byte extSubjKeyIdSet; /* Set when the SKID was read from cert */
byte extAuthKeyId[SHA_SIZE]; /* Authority Key ID */
byte extAuthKeyIdSet; /* Set when the AKID was read from cert */
#ifndef IGNORE_NAME_CONSTRAINTS
byte extNameConstraintSet;
#endif /* IGNORE_NAME_CONSTRAINTS */
byte isCA; /* CA basic constraint true */
byte extKeyUsageSet;
word16 extKeyUsage; /* Key usage bitfield */
@ -357,6 +376,9 @@ struct DecodedCert {
byte extSubjAltNameSet;
byte extSubjAltNameCrit;
byte extAuthKeyIdCrit;
#ifndef IGNORE_NAME_CONSTRAINTS
byte extNameConstraintCrit;
#endif /* IGNORE_NAME_CONSTRAINTS */
byte extSubjKeyIdCrit;
byte extKeyUsageCrit;
byte extExtKeyUsageCrit;
@ -430,6 +452,10 @@ struct Signer {
byte* publicKey;
int nameLen;
char* name; /* common name */
#ifndef IGNORE_NAME_CONSTRAINTS
Base_entry* permittedNames;
Base_entry* excludedNames;
#endif /* IGNORE_NAME_CONSTRAINTS */
byte subjectNameHash[SIGNER_DIGEST_SIZE];
/* sha hash of names in certificate */
#ifndef NO_SKID
@ -448,6 +474,9 @@ struct Signer {
#endif
CYASSL_TEST_API void FreeAltNames(DNS_entry*, void*);
#ifndef IGNORE_NAME_CONSTRAINTS
CYASSL_TEST_API void FreeNameSubtrees(Base_entry*, void*);
#endif /* IGNORE_NAME_CONSTRAINTS */
CYASSL_TEST_API void InitDecodedCert(DecodedCert*, byte*, word32, void*);
CYASSL_TEST_API void FreeDecodedCert(DecodedCert*);
CYASSL_TEST_API int ParseCert(DecodedCert*, int type, int verify, void* cm);

View File

@ -123,6 +123,7 @@ enum {
PKCS7_OID_E = -195, /* PKCS#7, mismatched OID error */
PKCS7_RECIP_E = -196, /* PKCS#7, recipient error */
FIPS_NOT_ALLOWED_E = -197, /* FIPS not allowed error */
ASN_NAME_INVALID_E = -198, /* ASN name constraint error */
MIN_CODE_E = -200 /* errors -101 - -199 */
};

View File

@ -1520,6 +1520,10 @@ int AddCA(CYASSL_CERT_MANAGER* cm, buffer der, int type, int verify)
signer->pubKeySize = cert.pubKeySize;
signer->nameLen = cert.subjectCNLen;
signer->name = cert.subjectCN;
#ifndef IGNORE_NAME_CONSTRAINTS
signer->permittedNames = cert.permittedNames;
signer->excludedNames = cert.excludedNames;
#endif
#ifndef NO_SKID
XMEMCPY(signer->subjectKeyIdHash,
cert.extSubjKeyId, SHA_DIGEST_SIZE);
@ -1531,6 +1535,10 @@ int AddCA(CYASSL_CERT_MANAGER* cm, buffer der, int type, int verify)
cert.publicKey = 0; /* don't free here */
cert.subjectCN = 0;
#ifndef IGNORE_NAME_CONSTRAINTS
cert.permittedNames = NULL;
cert.excludedNames = NULL;
#endif
#ifndef NO_SKID
row = HashSigner(signer->subjectKeyIdHash);