/* 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); }