michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Support for various policy related extensions michael@0: */ michael@0: michael@0: #include "seccomon.h" michael@0: #include "secport.h" michael@0: #include "secder.h" michael@0: #include "cert.h" michael@0: #include "secoid.h" michael@0: #include "secasn1.h" michael@0: #include "secerr.h" michael@0: #include "nspr.h" michael@0: #include "secutil.h" michael@0: michael@0: /* This implementation is derived from the one in nss/lib/certdb/policyxtn.c . michael@0: ** The chief difference is the addition of the OPTIONAL flag to many michael@0: ** parts. The idea is to be able to parse and print as much of the michael@0: ** policy extension as possible, even if some parts are invalid. michael@0: ** michael@0: ** If this approach still is unable to decode policy extensions that michael@0: ** contain invalid parts, then the next approach will be to parse michael@0: ** the PolicyInfos as a SEQUENCE of ANYs, and then parse each of them michael@0: ** as PolicyInfos, with the PolicyQualifiers being ANYs, and finally michael@0: ** parse each of the PolicyQualifiers. michael@0: */ michael@0: michael@0: static const SEC_ASN1Template secu_PolicyQualifierTemplate[] = { michael@0: { SEC_ASN1_SEQUENCE, michael@0: 0, NULL, sizeof(CERTPolicyQualifier) }, michael@0: { SEC_ASN1_OBJECT_ID, michael@0: offsetof(CERTPolicyQualifier, qualifierID) }, michael@0: { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, michael@0: offsetof(CERTPolicyQualifier, qualifierValue) }, michael@0: { 0 } michael@0: }; michael@0: michael@0: static const SEC_ASN1Template secu_PolicyInfoTemplate[] = { michael@0: { SEC_ASN1_SEQUENCE, michael@0: 0, NULL, sizeof(CERTPolicyInfo) }, michael@0: { SEC_ASN1_OBJECT_ID, michael@0: offsetof(CERTPolicyInfo, policyID) }, michael@0: { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_OPTIONAL, michael@0: offsetof(CERTPolicyInfo, policyQualifiers), michael@0: secu_PolicyQualifierTemplate }, michael@0: { 0 } michael@0: }; michael@0: michael@0: static const SEC_ASN1Template secu_CertificatePoliciesTemplate[] = { michael@0: { SEC_ASN1_SEQUENCE_OF, michael@0: offsetof(CERTCertificatePolicies, policyInfos), michael@0: secu_PolicyInfoTemplate, sizeof(CERTCertificatePolicies) } michael@0: }; michael@0: michael@0: michael@0: static CERTCertificatePolicies * michael@0: secu_DecodeCertificatePoliciesExtension(SECItem *extnValue) michael@0: { michael@0: PLArenaPool *arena = NULL; michael@0: SECStatus rv; michael@0: CERTCertificatePolicies *policies; michael@0: CERTPolicyInfo **policyInfos, *policyInfo; michael@0: CERTPolicyQualifier **policyQualifiers, *policyQualifier; michael@0: SECItem newExtnValue; michael@0: michael@0: /* make a new arena */ michael@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: michael@0: if ( !arena ) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* allocate the certifiate policies structure */ michael@0: policies = PORT_ArenaZNew(arena, CERTCertificatePolicies); michael@0: if ( policies == NULL ) { michael@0: goto loser; michael@0: } michael@0: michael@0: policies->arena = arena; michael@0: michael@0: /* copy the DER into the arena, since Quick DER returns data that points michael@0: into the DER input, which may get freed by the caller */ michael@0: rv = SECITEM_CopyItem(arena, &newExtnValue, extnValue); michael@0: if ( rv != SECSuccess ) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* decode the policy info */ michael@0: rv = SEC_QuickDERDecodeItem(arena, policies, michael@0: secu_CertificatePoliciesTemplate, michael@0: &newExtnValue); michael@0: michael@0: if ( rv != SECSuccess ) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* initialize the oid tags */ michael@0: policyInfos = policies->policyInfos; michael@0: while (policyInfos != NULL && *policyInfos != NULL ) { michael@0: policyInfo = *policyInfos; michael@0: policyInfo->oid = SECOID_FindOIDTag(&policyInfo->policyID); michael@0: policyQualifiers = policyInfo->policyQualifiers; michael@0: while ( policyQualifiers && *policyQualifiers != NULL ) { michael@0: policyQualifier = *policyQualifiers; michael@0: policyQualifier->oid = michael@0: SECOID_FindOIDTag(&policyQualifier->qualifierID); michael@0: policyQualifiers++; michael@0: } michael@0: policyInfos++; michael@0: } michael@0: michael@0: return(policies); michael@0: michael@0: loser: michael@0: if ( arena != NULL ) { michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: } michael@0: michael@0: return(NULL); michael@0: } michael@0: michael@0: michael@0: static char * michael@0: itemToString(SECItem *item) michael@0: { michael@0: char *string; michael@0: michael@0: string = PORT_ZAlloc(item->len+1); michael@0: if (string == NULL) return NULL; michael@0: PORT_Memcpy(string,item->data,item->len); michael@0: string[item->len] = 0; michael@0: return string; michael@0: } michael@0: michael@0: static SECStatus michael@0: secu_PrintUserNoticeQualifier(FILE *out, SECItem * qualifierValue, michael@0: char *msg, int level) michael@0: { michael@0: CERTUserNotice *userNotice = NULL; michael@0: if (qualifierValue) michael@0: userNotice = CERT_DecodeUserNotice(qualifierValue); michael@0: if (userNotice) { michael@0: if (userNotice->noticeReference.organization.len != 0) { michael@0: char *string = michael@0: itemToString(&userNotice->noticeReference.organization); michael@0: SECItem **itemList = userNotice->noticeReference.noticeNumbers; michael@0: michael@0: while (itemList && *itemList) { michael@0: SECU_PrintInteger(out,*itemList,string,level+1); michael@0: itemList++; michael@0: } michael@0: PORT_Free(string); michael@0: } michael@0: if (userNotice->displayText.len != 0) { michael@0: SECU_PrintString(out,&userNotice->displayText, michael@0: "Display Text", level+1); michael@0: } michael@0: CERT_DestroyUserNotice(userNotice); michael@0: return SECSuccess; michael@0: } michael@0: return SECFailure; /* caller will print this value */ michael@0: } michael@0: michael@0: static SECStatus michael@0: secu_PrintPolicyQualifier(FILE *out,CERTPolicyQualifier *policyQualifier, michael@0: char *msg,int level) michael@0: { michael@0: SECStatus rv; michael@0: SECItem * qualifierValue = &policyQualifier->qualifierValue; michael@0: michael@0: SECU_PrintObjectID(out, &policyQualifier->qualifierID , michael@0: "Policy Qualifier Name", level); michael@0: if (!qualifierValue->data) { michael@0: SECU_Indent(out, level); michael@0: fprintf(out,"Error: missing qualifier\n"); michael@0: } else michael@0: switch (policyQualifier->oid) { michael@0: case SEC_OID_PKIX_USER_NOTICE_QUALIFIER: michael@0: rv = secu_PrintUserNoticeQualifier(out, qualifierValue, msg, level); michael@0: if (SECSuccess == rv) michael@0: break; michael@0: /* fall through on error */ michael@0: case SEC_OID_PKIX_CPS_POINTER_QUALIFIER: michael@0: default: michael@0: SECU_PrintAny(out, qualifierValue, "Policy Qualifier Data", level); michael@0: break; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: static SECStatus michael@0: secu_PrintPolicyInfo(FILE *out,CERTPolicyInfo *policyInfo,char *msg,int level) michael@0: { michael@0: CERTPolicyQualifier **policyQualifiers; michael@0: michael@0: policyQualifiers = policyInfo->policyQualifiers; michael@0: SECU_PrintObjectID(out, &policyInfo->policyID , "Policy Name", level); michael@0: michael@0: while (policyQualifiers && *policyQualifiers != NULL) { michael@0: secu_PrintPolicyQualifier(out,*policyQualifiers,"",level+1); michael@0: policyQualifiers++; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: void michael@0: SECU_PrintPolicy(FILE *out, SECItem *value, char *msg, int level) michael@0: { michael@0: CERTCertificatePolicies *policies = NULL; michael@0: CERTPolicyInfo **policyInfos; michael@0: michael@0: if (msg) { michael@0: SECU_Indent(out, level); michael@0: fprintf(out,"%s: \n",msg); michael@0: level++; michael@0: } michael@0: policies = secu_DecodeCertificatePoliciesExtension(value); michael@0: if (policies == NULL) { michael@0: SECU_PrintAny(out, value, "Invalid Policy Data", level); michael@0: return; michael@0: } michael@0: michael@0: policyInfos = policies->policyInfos; michael@0: while (policyInfos && *policyInfos != NULL) { michael@0: secu_PrintPolicyInfo(out,*policyInfos,"",level); michael@0: policyInfos++; michael@0: } michael@0: michael@0: CERT_DestroyCertificatePoliciesExtension(policies); michael@0: } michael@0: michael@0: michael@0: void michael@0: SECU_PrintPrivKeyUsagePeriodExtension(FILE *out, SECItem *value, michael@0: char *msg, int level) michael@0: { michael@0: CERTPrivKeyUsagePeriod * prd; michael@0: PLArenaPool * arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: michael@0: if ( !arena ) { michael@0: goto loser; michael@0: } michael@0: prd = CERT_DecodePrivKeyUsagePeriodExtension(arena, value); michael@0: if (!prd) { michael@0: goto loser; michael@0: } michael@0: if (prd->notBefore.data) { michael@0: SECU_PrintGeneralizedTime(out, &prd->notBefore, "Not Before", level); michael@0: } michael@0: if (prd->notAfter.data) { michael@0: SECU_PrintGeneralizedTime(out, &prd->notAfter, "Not After ", level); michael@0: } michael@0: if (!prd->notBefore.data && !prd->notAfter.data) { michael@0: SECU_Indent(out, level); michael@0: fprintf(out, "Error: notBefore or notAfter MUST be present.\n"); michael@0: loser: michael@0: SECU_PrintAny(out, value, msg, level); michael@0: } michael@0: if (arena) { michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: } michael@0: }