diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/certdb/polcyxtn.c | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/security/nss/lib/certdb/polcyxtn.c b/security/nss/lib/certdb/polcyxtn.c new file mode 100644 index 0000000000..aae34e2433 --- /dev/null +++ b/security/nss/lib/certdb/polcyxtn.c @@ -0,0 +1,806 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Support for various policy related extensions + */ + +#include "seccomon.h" +#include "secport.h" +#include "secder.h" +#include "cert.h" +#include "secoid.h" +#include "secasn1.h" +#include "secerr.h" +#include "nspr.h" + +SEC_ASN1_MKSUB(SEC_IntegerTemplate) +SEC_ASN1_MKSUB(SEC_ObjectIDTemplate) + +const SEC_ASN1Template CERT_DisplayTextTypeTemplate[] = { + { SEC_ASN1_CHOICE, offsetof(SECItem, type), 0, sizeof(SECItem) }, + { SEC_ASN1_IA5_STRING, 0, 0, siAsciiString }, + { SEC_ASN1_VISIBLE_STRING, 0, 0, siVisibleString }, + { SEC_ASN1_BMP_STRING, 0, 0, siBMPString }, + { SEC_ASN1_UTF8_STRING, 0, 0, siUTF8String }, + { 0 } +}; + +const SEC_ASN1Template CERT_NoticeReferenceTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNoticeReference) }, + { SEC_ASN1_INLINE, offsetof(CERTNoticeReference, organization), + CERT_DisplayTextTypeTemplate, 0 }, + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, + offsetof(CERTNoticeReference, noticeNumbers), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { 0 } +}; + +const SEC_ASN1Template CERT_UserNoticeTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTUserNotice) }, + { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL, + offsetof(CERTUserNotice, noticeReference), CERT_NoticeReferenceTemplate, + 0 }, + { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL, + offsetof(CERTUserNotice, displayText), CERT_DisplayTextTypeTemplate, 0 }, + { 0 } +}; + +const SEC_ASN1Template CERT_PolicyQualifierTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPolicyQualifier) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTPolicyQualifier, qualifierID) }, + { SEC_ASN1_ANY, offsetof(CERTPolicyQualifier, qualifierValue) }, + { 0 } +}; + +const SEC_ASN1Template CERT_PolicyInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPolicyInfo) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTPolicyInfo, policyID) }, + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_OPTIONAL, + offsetof(CERTPolicyInfo, policyQualifiers), + CERT_PolicyQualifierTemplate }, + { 0 } +}; + +const SEC_ASN1Template CERT_CertificatePoliciesTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, offsetof(CERTCertificatePolicies, policyInfos), + CERT_PolicyInfoTemplate, sizeof(CERTCertificatePolicies) } +}; + +const SEC_ASN1Template CERT_PolicyMapTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPolicyMap) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTPolicyMap, issuerDomainPolicy) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTPolicyMap, subjectDomainPolicy) }, + { 0 } +}; + +const SEC_ASN1Template CERT_PolicyMappingsTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, offsetof(CERTCertificatePolicyMappings, policyMaps), + CERT_PolicyMapTemplate, sizeof(CERTPolicyMap) } +}; + +const SEC_ASN1Template CERT_PolicyConstraintsTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertificatePolicyConstraints) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(CERTCertificatePolicyConstraints, explicitPolicySkipCerts), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, + offsetof(CERTCertificatePolicyConstraints, inhibitMappingSkipCerts), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { 0 } +}; + +const SEC_ASN1Template CERT_InhibitAnyTemplate[] = { + { SEC_ASN1_INTEGER, + offsetof(CERTCertificateInhibitAny, inhibitAnySkipCerts), NULL, + sizeof(CERTCertificateInhibitAny) } +}; + +static void +breakLines(char *string) +{ + char *tmpstr; + char *lastspace = NULL; + int curlen = 0; + int c; + + tmpstr = string; + + while ((c = *tmpstr) != '\0') { + switch (c) { + case ' ': + lastspace = tmpstr; + break; + case '\n': + lastspace = NULL; + curlen = 0; + break; + } + + if ((curlen >= 55) && (lastspace != NULL)) { + *lastspace = '\n'; + curlen = (tmpstr - lastspace); + lastspace = NULL; + } + + curlen++; + tmpstr++; + } + + return; +} + +CERTCertificatePolicies * +CERT_DecodeCertificatePoliciesExtension(const SECItem *extnValue) +{ + PLArenaPool *arena = NULL; + SECStatus rv; + CERTCertificatePolicies *policies; + CERTPolicyInfo **policyInfos, *policyInfo; + CERTPolicyQualifier **policyQualifiers, *policyQualifier; + SECItem newExtnValue; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + goto loser; + } + + /* allocate the certificate policies structure */ + policies = (CERTCertificatePolicies *)PORT_ArenaZAlloc( + arena, sizeof(CERTCertificatePolicies)); + + if (policies == NULL) { + goto loser; + } + + policies->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newExtnValue, extnValue); + if (rv != SECSuccess) { + goto loser; + } + + /* decode the policy info */ + rv = SEC_QuickDERDecodeItem( + arena, policies, CERT_CertificatePoliciesTemplate, &newExtnValue); + + if (rv != SECSuccess) { + goto loser; + } + + /* initialize the oid tags */ + policyInfos = policies->policyInfos; + while (*policyInfos != NULL) { + policyInfo = *policyInfos; + policyInfo->oid = SECOID_FindOIDTag(&policyInfo->policyID); + policyQualifiers = policyInfo->policyQualifiers; + while (policyQualifiers != NULL && *policyQualifiers != NULL) { + policyQualifier = *policyQualifiers; + policyQualifier->oid = + SECOID_FindOIDTag(&policyQualifier->qualifierID); + policyQualifiers++; + } + policyInfos++; + } + + return (policies); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (NULL); +} + +void +CERT_DestroyCertificatePoliciesExtension(CERTCertificatePolicies *policies) +{ + if (policies != NULL) { + PORT_FreeArena(policies->arena, PR_FALSE); + } + return; +} + +CERTCertificatePolicyMappings * +CERT_DecodePolicyMappingsExtension(SECItem *extnValue) +{ + PLArenaPool *arena = NULL; + SECStatus rv; + CERTCertificatePolicyMappings *mappings; + SECItem newExtnValue; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + goto loser; + } + + /* allocate the policy mappings structure */ + mappings = (CERTCertificatePolicyMappings *)PORT_ArenaZAlloc( + arena, sizeof(CERTCertificatePolicyMappings)); + if (mappings == NULL) { + goto loser; + } + mappings->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newExtnValue, extnValue); + if (rv != SECSuccess) { + goto loser; + } + + /* decode the policy mappings */ + rv = SEC_QuickDERDecodeItem(arena, mappings, CERT_PolicyMappingsTemplate, + &newExtnValue); + if (rv != SECSuccess) { + goto loser; + } + + return (mappings); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (NULL); +} + +SECStatus +CERT_DestroyPolicyMappingsExtension(CERTCertificatePolicyMappings *mappings) +{ + if (mappings != NULL) { + PORT_FreeArena(mappings->arena, PR_FALSE); + } + return SECSuccess; +} + +SECStatus +CERT_DecodePolicyConstraintsExtension( + CERTCertificatePolicyConstraints *decodedValue, const SECItem *encodedValue) +{ + CERTCertificatePolicyConstraints decodeContext; + PLArenaPool *arena = NULL; + SECStatus rv = SECSuccess; + + /* initialize so we can tell when an optional component is omitted */ + PORT_Memset(&decodeContext, 0, sizeof(decodeContext)); + + /* make a new arena */ + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if (!arena) { + return SECFailure; + } + + do { + /* decode the policy constraints */ + rv = SEC_QuickDERDecodeItem(arena, &decodeContext, + CERT_PolicyConstraintsTemplate, + encodedValue); + + if (rv != SECSuccess) { + break; + } + + if (decodeContext.explicitPolicySkipCerts.len == 0) { + *(PRInt32 *)decodedValue->explicitPolicySkipCerts.data = -1; + } else { + *(PRInt32 *)decodedValue->explicitPolicySkipCerts.data = + DER_GetInteger(&decodeContext.explicitPolicySkipCerts); + } + + if (decodeContext.inhibitMappingSkipCerts.len == 0) { + *(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data = -1; + } else { + *(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data = + DER_GetInteger(&decodeContext.inhibitMappingSkipCerts); + } + + if ((*(PRInt32 *)decodedValue->explicitPolicySkipCerts.data == + PR_INT32_MIN) || + (*(PRInt32 *)decodedValue->explicitPolicySkipCerts.data == + PR_INT32_MAX) || + (*(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data == + PR_INT32_MIN) || + (*(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data == + PR_INT32_MAX)) { + rv = SECFailure; + } + + } while (0); + + PORT_FreeArena(arena, PR_FALSE); + return (rv); +} + +SECStatus +CERT_DecodeInhibitAnyExtension(CERTCertificateInhibitAny *decodedValue, + SECItem *encodedValue) +{ + CERTCertificateInhibitAny decodeContext; + PLArenaPool *arena = NULL; + SECStatus rv = SECSuccess; + + /* make a new arena */ + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if (!arena) { + return SECFailure; + } + + do { + + /* decode the policy mappings */ + decodeContext.inhibitAnySkipCerts.type = siUnsignedInteger; + rv = SEC_QuickDERDecodeItem(arena, &decodeContext, + CERT_InhibitAnyTemplate, encodedValue); + + if (rv != SECSuccess) { + break; + } + + *(PRInt32 *)decodedValue->inhibitAnySkipCerts.data = + DER_GetInteger(&decodeContext.inhibitAnySkipCerts); + + } while (0); + + PORT_FreeArena(arena, PR_FALSE); + return (rv); +} + +CERTUserNotice * +CERT_DecodeUserNotice(SECItem *noticeItem) +{ + PLArenaPool *arena = NULL; + SECStatus rv; + CERTUserNotice *userNotice; + SECItem newNoticeItem; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + goto loser; + } + + /* allocate the userNotice structure */ + userNotice = + (CERTUserNotice *)PORT_ArenaZAlloc(arena, sizeof(CERTUserNotice)); + + if (userNotice == NULL) { + goto loser; + } + + userNotice->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newNoticeItem, noticeItem); + if (rv != SECSuccess) { + goto loser; + } + + /* decode the user notice */ + rv = SEC_QuickDERDecodeItem(arena, userNotice, CERT_UserNoticeTemplate, + &newNoticeItem); + + if (rv != SECSuccess) { + goto loser; + } + + if (userNotice->derNoticeReference.data != NULL) { + + rv = SEC_QuickDERDecodeItem(arena, &userNotice->noticeReference, + CERT_NoticeReferenceTemplate, + &userNotice->derNoticeReference); + if (rv == SECFailure) { + goto loser; + } + } + + return (userNotice); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (NULL); +} + +void +CERT_DestroyUserNotice(CERTUserNotice *userNotice) +{ + if (userNotice != NULL) { + PORT_FreeArena(userNotice->arena, PR_FALSE); + } + return; +} + +static CERTPolicyStringCallback policyStringCB = NULL; +static void *policyStringCBArg = NULL; + +void +CERT_SetCAPolicyStringCallback(CERTPolicyStringCallback cb, void *cbarg) +{ + policyStringCB = cb; + policyStringCBArg = cbarg; + return; +} + +char * +stringFromUserNotice(SECItem *noticeItem) +{ + SECItem *org; + unsigned int len, headerlen; + char *stringbuf; + CERTUserNotice *userNotice; + char *policystr; + char *retstr = NULL; + SECItem *displayText; + SECItem **noticeNumbers; + unsigned int strnum; + + /* decode the user notice */ + userNotice = CERT_DecodeUserNotice(noticeItem); + if (userNotice == NULL) { + return (NULL); + } + + org = &userNotice->noticeReference.organization; + if ((org->len != 0) && (policyStringCB != NULL)) { + /* has a noticeReference */ + + /* extract the org string */ + len = org->len; + stringbuf = (char *)PORT_Alloc(len + 1); + if (stringbuf != NULL) { + PORT_Memcpy(stringbuf, org->data, len); + stringbuf[len] = '\0'; + + noticeNumbers = userNotice->noticeReference.noticeNumbers; + while (*noticeNumbers != NULL) { + /* XXX - only one byte integers right now*/ + strnum = (*noticeNumbers)->data[0]; + policystr = + (*policyStringCB)(stringbuf, strnum, policyStringCBArg); + if (policystr != NULL) { + if (retstr != NULL) { + retstr = PR_sprintf_append(retstr, "\n%s", policystr); + } else { + retstr = PR_sprintf_append(retstr, "%s", policystr); + } + + PORT_Free(policystr); + } + + noticeNumbers++; + } + + PORT_Free(stringbuf); + } + } + + if (retstr == NULL) { + if (userNotice->displayText.len != 0) { + displayText = &userNotice->displayText; + + if (displayText->len > 2) { + if (displayText->data[0] == SEC_ASN1_VISIBLE_STRING) { + headerlen = 2; + if (displayText->data[1] & 0x80) { + /* multibyte length */ + headerlen += (displayText->data[1] & 0x7f); + } + + len = displayText->len - headerlen; + retstr = (char *)PORT_Alloc(len + 1); + if (retstr != NULL) { + PORT_Memcpy(retstr, &displayText->data[headerlen], len); + retstr[len] = '\0'; + } + } + } + } + } + + CERT_DestroyUserNotice(userNotice); + + return (retstr); +} + +char * +CERT_GetCertCommentString(CERTCertificate *cert) +{ + char *retstring = NULL; + SECStatus rv; + SECItem policyItem; + CERTCertificatePolicies *policies = NULL; + CERTPolicyInfo **policyInfos; + CERTPolicyQualifier **policyQualifiers, *qualifier; + + policyItem.data = NULL; + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_CERTIFICATE_POLICIES, + &policyItem); + if (rv != SECSuccess) { + goto nopolicy; + } + + policies = CERT_DecodeCertificatePoliciesExtension(&policyItem); + if (policies == NULL) { + goto nopolicy; + } + + policyInfos = policies->policyInfos; + /* search through policyInfos looking for the verisign policy */ + while (*policyInfos != NULL) { + if ((*policyInfos)->oid == SEC_OID_VERISIGN_USER_NOTICES) { + policyQualifiers = (*policyInfos)->policyQualifiers; + /* search through the policy qualifiers looking for user notice */ + while (policyQualifiers != NULL && *policyQualifiers != NULL) { + qualifier = *policyQualifiers; + if (qualifier->oid == SEC_OID_PKIX_USER_NOTICE_QUALIFIER) { + retstring = + stringFromUserNotice(&qualifier->qualifierValue); + break; + } + + policyQualifiers++; + } + break; + } + policyInfos++; + } + +nopolicy: + if (policyItem.data != NULL) { + PORT_Free(policyItem.data); + } + + if (policies != NULL) { + CERT_DestroyCertificatePoliciesExtension(policies); + } + + if (retstring == NULL) { + retstring = + CERT_FindNSStringExtension(cert, SEC_OID_NS_CERT_EXT_COMMENT); + } + + if (retstring != NULL) { + breakLines(retstring); + } + + return (retstring); +} + +const SEC_ASN1Template CERT_OidSeqTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, offsetof(CERTOidSequence, oids), + SEC_ASN1_SUB(SEC_ObjectIDTemplate) } +}; + +CERTOidSequence * +CERT_DecodeOidSequence(const SECItem *seqItem) +{ + PLArenaPool *arena = NULL; + SECStatus rv; + CERTOidSequence *oidSeq; + SECItem newSeqItem; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + goto loser; + } + + /* allocate the userNotice structure */ + oidSeq = + (CERTOidSequence *)PORT_ArenaZAlloc(arena, sizeof(CERTOidSequence)); + + if (oidSeq == NULL) { + goto loser; + } + + oidSeq->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newSeqItem, seqItem); + if (rv != SECSuccess) { + goto loser; + } + + /* decode the user notice */ + rv = + SEC_QuickDERDecodeItem(arena, oidSeq, CERT_OidSeqTemplate, &newSeqItem); + + if (rv != SECSuccess) { + goto loser; + } + + return (oidSeq); + +loser: + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + return (NULL); +} + +void +CERT_DestroyOidSequence(CERTOidSequence *oidSeq) +{ + if (oidSeq != NULL) { + PORT_FreeArena(oidSeq->arena, PR_FALSE); + } + return; +} + +PRBool +CERT_GovtApprovedBitSet(CERTCertificate *cert) +{ + SECStatus rv; + SECItem extItem; + CERTOidSequence *oidSeq = NULL; + PRBool ret; + SECItem **oids; + SECItem *oid; + SECOidTag oidTag; + + extItem.data = NULL; + rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE, &extItem); + if (rv != SECSuccess) { + goto loser; + } + + oidSeq = CERT_DecodeOidSequence(&extItem); + if (oidSeq == NULL) { + goto loser; + } + + oids = oidSeq->oids; + while (oids != NULL && *oids != NULL) { + oid = *oids; + + oidTag = SECOID_FindOIDTag(oid); + + if (oidTag == SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) { + goto success; + } + + oids++; + } + +loser: + ret = PR_FALSE; + goto done; +success: + ret = PR_TRUE; +done: + if (oidSeq != NULL) { + CERT_DestroyOidSequence(oidSeq); + } + if (extItem.data != NULL) { + PORT_Free(extItem.data); + } + return (ret); +} + +SECStatus +CERT_EncodePolicyConstraintsExtension(PLArenaPool *arena, + CERTCertificatePolicyConstraints *constr, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(constr != NULL && dest != NULL); + if (constr == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, constr, + CERT_PolicyConstraintsTemplate) == NULL) { + rv = SECFailure; + } + return (rv); +} + +SECStatus +CERT_EncodePolicyMappingExtension(PLArenaPool *arena, + CERTCertificatePolicyMappings *mapping, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(mapping != NULL && dest != NULL); + if (mapping == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, mapping, CERT_PolicyMappingsTemplate) == + NULL) { + rv = SECFailure; + } + return (rv); +} + +SECStatus +CERT_EncodeCertPoliciesExtension(PLArenaPool *arena, CERTPolicyInfo **info, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(info != NULL && dest != NULL); + if (info == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, info, + CERT_CertificatePoliciesTemplate) == NULL) { + rv = SECFailure; + } + return (rv); +} + +SECStatus +CERT_EncodeUserNotice(PLArenaPool *arena, CERTUserNotice *notice, SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(notice != NULL && dest != NULL); + if (notice == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, notice, CERT_UserNoticeTemplate) == + NULL) { + rv = SECFailure; + } + + return (rv); +} + +SECStatus +CERT_EncodeNoticeReference(PLArenaPool *arena, CERTNoticeReference *reference, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(reference != NULL && dest != NULL); + if (reference == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, reference, + CERT_NoticeReferenceTemplate) == NULL) { + rv = SECFailure; + } + + return (rv); +} + +SECStatus +CERT_EncodeInhibitAnyExtension(PLArenaPool *arena, + CERTCertificateInhibitAny *certInhibitAny, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(certInhibitAny != NULL && dest != NULL); + if (certInhibitAny == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, certInhibitAny, + CERT_InhibitAnyTemplate) == NULL) { + rv = SECFailure; + } + return (rv); +} |