| /* 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" |
| #include "secutil.h" |
| |
| /* This implementation is derived from the one in nss/lib/certdb/policyxtn.c . |
| ** The chief difference is the addition of the OPTIONAL flag to many |
| ** parts. The idea is to be able to parse and print as much of the |
| ** policy extension as possible, even if some parts are invalid. |
| ** |
| ** If this approach still is unable to decode policy extensions that |
| ** contain invalid parts, then the next approach will be to parse |
| ** the PolicyInfos as a SEQUENCE of ANYs, and then parse each of them |
| ** as PolicyInfos, with the PolicyQualifiers being ANYs, and finally |
| ** parse each of the PolicyQualifiers. |
| */ |
| |
| static const SEC_ASN1Template secu_PolicyQualifierTemplate[] = { |
| { SEC_ASN1_SEQUENCE, |
| 0, NULL, sizeof(CERTPolicyQualifier) }, |
| { SEC_ASN1_OBJECT_ID, |
| offsetof(CERTPolicyQualifier, qualifierID) }, |
| { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, |
| offsetof(CERTPolicyQualifier, qualifierValue) }, |
| { 0 } |
| }; |
| |
| static const SEC_ASN1Template secu_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), |
| secu_PolicyQualifierTemplate }, |
| { 0 } |
| }; |
| |
| static const SEC_ASN1Template secu_CertificatePoliciesTemplate[] = { |
| { SEC_ASN1_SEQUENCE_OF, |
| offsetof(CERTCertificatePolicies, policyInfos), |
| secu_PolicyInfoTemplate, sizeof(CERTCertificatePolicies) } |
| }; |
| |
| static CERTCertificatePolicies * |
| secu_DecodeCertificatePoliciesExtension(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 certifiate policies structure */ |
| policies = PORT_ArenaZNew(arena, 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, |
| secu_CertificatePoliciesTemplate, |
| &newExtnValue); |
| |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| |
| /* initialize the oid tags */ |
| policyInfos = policies->policyInfos; |
| while (policyInfos != NULL && *policyInfos != NULL) { |
| policyInfo = *policyInfos; |
| policyInfo->oid = SECOID_FindOIDTag(&policyInfo->policyID); |
| policyQualifiers = policyInfo->policyQualifiers; |
| while (policyQualifiers && *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); |
| } |
| |
| static char * |
| itemToString(SECItem *item) |
| { |
| char *string; |
| |
| string = PORT_ZAlloc(item->len + 1); |
| if (string == NULL) |
| return NULL; |
| PORT_Memcpy(string, item->data, item->len); |
| string[item->len] = 0; |
| return string; |
| } |
| |
| static SECStatus |
| secu_PrintUserNoticeQualifier(FILE *out, SECItem *qualifierValue, |
| char *msg, int level) |
| { |
| CERTUserNotice *userNotice = NULL; |
| if (qualifierValue) |
| userNotice = CERT_DecodeUserNotice(qualifierValue); |
| if (userNotice) { |
| if (userNotice->noticeReference.organization.len != 0) { |
| char *string = |
| itemToString(&userNotice->noticeReference.organization); |
| SECItem **itemList = userNotice->noticeReference.noticeNumbers; |
| |
| while (itemList && *itemList) { |
| SECU_PrintInteger(out, *itemList, string, level + 1); |
| itemList++; |
| } |
| PORT_Free(string); |
| } |
| if (userNotice->displayText.len != 0) { |
| SECU_PrintString(out, &userNotice->displayText, |
| "Display Text", level + 1); |
| } |
| CERT_DestroyUserNotice(userNotice); |
| return SECSuccess; |
| } |
| return SECFailure; /* caller will print this value */ |
| } |
| |
| static SECStatus |
| secu_PrintPolicyQualifier(FILE *out, CERTPolicyQualifier *policyQualifier, |
| char *msg, int level) |
| { |
| SECStatus rv; |
| SECItem *qualifierValue = &policyQualifier->qualifierValue; |
| |
| SECU_PrintObjectID(out, &policyQualifier->qualifierID, |
| "Policy Qualifier Name", level); |
| if (!qualifierValue->data) { |
| SECU_Indent(out, level); |
| fprintf(out, "Error: missing qualifier\n"); |
| } else |
| switch (policyQualifier->oid) { |
| case SEC_OID_PKIX_USER_NOTICE_QUALIFIER: |
| rv = secu_PrintUserNoticeQualifier(out, qualifierValue, msg, level); |
| if (SECSuccess == rv) |
| break; |
| /* fall through on error */ |
| case SEC_OID_PKIX_CPS_POINTER_QUALIFIER: |
| default: |
| SECU_PrintAny(out, qualifierValue, "Policy Qualifier Data", level); |
| break; |
| } |
| return SECSuccess; |
| } |
| |
| static SECStatus |
| secu_PrintPolicyInfo(FILE *out, CERTPolicyInfo *policyInfo, char *msg, int level) |
| { |
| CERTPolicyQualifier **policyQualifiers; |
| |
| policyQualifiers = policyInfo->policyQualifiers; |
| SECU_PrintObjectID(out, &policyInfo->policyID, "Policy Name", level); |
| |
| while (policyQualifiers && *policyQualifiers != NULL) { |
| secu_PrintPolicyQualifier(out, *policyQualifiers, "", level + 1); |
| policyQualifiers++; |
| } |
| return SECSuccess; |
| } |
| |
| void |
| SECU_PrintPolicy(FILE *out, SECItem *value, char *msg, int level) |
| { |
| CERTCertificatePolicies *policies = NULL; |
| CERTPolicyInfo **policyInfos; |
| |
| if (msg) { |
| SECU_Indent(out, level); |
| fprintf(out, "%s: \n", msg); |
| level++; |
| } |
| policies = secu_DecodeCertificatePoliciesExtension(value); |
| if (policies == NULL) { |
| SECU_PrintAny(out, value, "Invalid Policy Data", level); |
| return; |
| } |
| |
| policyInfos = policies->policyInfos; |
| while (policyInfos && *policyInfos != NULL) { |
| secu_PrintPolicyInfo(out, *policyInfos, "", level); |
| policyInfos++; |
| } |
| |
| CERT_DestroyCertificatePoliciesExtension(policies); |
| } |
| |
| void |
| SECU_PrintPrivKeyUsagePeriodExtension(FILE *out, SECItem *value, |
| char *msg, int level) |
| { |
| CERTPrivKeyUsagePeriod *prd; |
| PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| |
| if (!arena) { |
| goto loser; |
| } |
| prd = CERT_DecodePrivKeyUsagePeriodExtension(arena, value); |
| if (!prd) { |
| goto loser; |
| } |
| if (prd->notBefore.data) { |
| SECU_PrintGeneralizedTime(out, &prd->notBefore, "Not Before", level); |
| } |
| if (prd->notAfter.data) { |
| SECU_PrintGeneralizedTime(out, &prd->notAfter, "Not After ", level); |
| } |
| if (!prd->notBefore.data && !prd->notAfter.data) { |
| SECU_Indent(out, level); |
| fprintf(out, "Error: notBefore or notAfter MUST be present.\n"); |
| loser: |
| SECU_PrintAny(out, value, msg, level); |
| } |
| if (arena) { |
| PORT_FreeArena(arena, PR_FALSE); |
| } |
| } |