/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ /* * Support for various policy related extensions * * $Id: polcyxtn.c,v 1.1 2000-03-31 19:42:44 relyea%netscape.com Exp $ */ #include "seccomon.h" #include "secport.h" #include "secder.h" #include "cert.h" #include "secoid.h" #include "secasn1.h" #include "secerr.h" #include "nspr.h" const SEC_ASN1Template CERT_NoticeReferenceTemplate[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNoticeReference) }, /* NOTE: this should be a choice */ { SEC_ASN1_IA5_STRING, offsetof(CERTNoticeReference, organization) }, { SEC_ASN1_SEQUENCE_OF, offsetof(CERTNoticeReference, noticeNumbers), SEC_IntegerTemplate }, { 0 } }; /* this template can not be encoded because of the option inline */ const SEC_ASN1Template CERT_UserNoticeTemplate[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTUserNotice) }, { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED, offsetof(CERTUserNotice, derNoticeReference) }, { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, offsetof(CERTUserNotice, displayText) }, { 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, offsetof(CERTPolicyInfo, policyQualifiers), CERT_PolicyQualifierTemplate }, { 0 } }; const SEC_ASN1Template CERT_CertificatePoliciesTemplate[] = { { SEC_ASN1_SEQUENCE_OF, offsetof(CERTCertificatePolicies, policyInfos), CERT_PolicyInfoTemplate, sizeof(CERTCertificatePolicies) } }; 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(SECItem *extnValue) { PRArenaPool *arena = NULL; SECStatus rv; CERTCertificatePolicies *policies; CERTPolicyInfo **policyInfos, *policyInfo; CERTPolicyQualifier **policyQualifiers, *policyQualifier; /* make a new arena */ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if ( !arena ) { goto loser; } /* allocate the certifiate policies structure */ policies = (CERTCertificatePolicies *) PORT_ArenaZAlloc(arena, sizeof(CERTCertificatePolicies)); if ( policies == NULL ) { goto loser; } policies->arena = arena; /* decode the policy info */ rv = SEC_ASN1DecodeItem(arena, policies, CERT_CertificatePoliciesTemplate, extnValue); 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 ) { 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; } CERTUserNotice * CERT_DecodeUserNotice(SECItem *noticeItem) { PRArenaPool *arena = NULL; SECStatus rv; CERTUserNotice *userNotice; /* 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; /* decode the user notice */ rv = SEC_ASN1DecodeItem(arena, userNotice, CERT_UserNoticeTemplate, noticeItem); if ( rv != SECSuccess ) { goto loser; } if (userNotice->derNoticeReference.data != NULL) { /* sigh, the asn1 parser stripped the sequence encoding, re add it * before we decode. */ SECItem tmpbuf; int newBytes; newBytes = SEC_ASN1LengthLength(userNotice->derNoticeReference.len)+1; tmpbuf.len = newBytes + userNotice->derNoticeReference.len; tmpbuf.data = PORT_ZAlloc(tmpbuf.len); if (tmpbuf.data == NULL) { goto loser; } tmpbuf.data[0] = SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED; SEC_ASN1EncodeLength(&tmpbuf.data[1],userNotice->derNoticeReference.len); PORT_Memcpy(&tmpbuf.data[newBytes],userNotice->derNoticeReference.data, userNotice->derNoticeReference.len); /* OK, no decode it */ rv = SEC_ASN1DecodeItem(arena, &userNotice->noticeReference, CERT_NoticeReferenceTemplate, &tmpbuf); PORT_Free(tmpbuf.data); tmpbuf.data = NULL; if ( rv != SECSuccess ) { 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 ) { 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, offsetof(CERTOidSequence, oids), SEC_ObjectIDTemplate } }; CERTOidSequence * CERT_DecodeOidSequence(SECItem *seqItem) { PRArenaPool *arena = NULL; SECStatus rv; CERTOidSequence *oidSeq; /* 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; /* decode the user notice */ rv = SEC_ASN1DecodeItem(arena, oidSeq, CERT_OidSeqTemplate, seqItem); if ( rv != SECSuccess ) { goto loser; } return(oidSeq); loser: 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); }