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