зеркало из https://github.com/mozilla/pjs.git
542 строки
13 KiB
C
542 строки
13 KiB
C
/*
|
|
* 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);
|
|
}
|