зеркало из https://github.com/mozilla/pjs.git
1631 строка
49 KiB
C
1631 строка
49 KiB
C
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* 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 the Initial Developer are Copyright (C) 1994-2000
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
/*
|
||
|
** crlgen.c
|
||
|
**
|
||
|
** utility for managing certificates revocation lists generation
|
||
|
**
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <math.h>
|
||
|
|
||
|
#include "nspr.h"
|
||
|
#include "plgetopt.h"
|
||
|
#include "nss.h"
|
||
|
#include "secutil.h"
|
||
|
#include "cert.h"
|
||
|
#include "certi.h"
|
||
|
#include "certdb.h"
|
||
|
#include "pk11func.h"
|
||
|
#include "crlgen.h"
|
||
|
|
||
|
|
||
|
/* these reroutines were taken from secitem.c, which is supposed to
|
||
|
* replace this file some day */
|
||
|
/*
|
||
|
* This is the hash function. We simply XOR the encoded form with
|
||
|
* itself in sizeof(PLHashNumber)-byte chunks. Improving this
|
||
|
* routine is left as an excercise for the more mathematically
|
||
|
* inclined student.
|
||
|
*/
|
||
|
PLHashNumber PR_CALLBACK
|
||
|
SECITEM_Hash ( const void *key)
|
||
|
{
|
||
|
const SECItem *item = (const SECItem *)key;
|
||
|
PLHashNumber rv = 0;
|
||
|
|
||
|
PRUint8 *data = (PRUint8 *)item->data;
|
||
|
PRUint32 i;
|
||
|
PRUint8 *rvc = (PRUint8 *)&rv;
|
||
|
|
||
|
for( i = 0; i < item->len; i++ ) {
|
||
|
rvc[ i % sizeof(rv) ] ^= *data;
|
||
|
data++;
|
||
|
}
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This is the key-compare function. It simply does a lexical
|
||
|
* comparison on the item data. This does not result in
|
||
|
* quite the same ordering as the "sequence of numbers" order,
|
||
|
* but heck it's only used internally by the hash table anyway.
|
||
|
*/
|
||
|
PRIntn PR_CALLBACK
|
||
|
SECITEM_HashCompare ( const void *k1, const void *k2)
|
||
|
{
|
||
|
const SECItem *i1 = (const SECItem *)k1;
|
||
|
const SECItem *i2 = (const SECItem *)k2;
|
||
|
|
||
|
return SECITEM_ItemsAreEqual(i1,i2);
|
||
|
}
|
||
|
|
||
|
/* Destroys extHandle and data. data was create on heap.
|
||
|
* extHandle creaded by CERT_StartCRLEntryExtensions. entry
|
||
|
* was allocated on arena.*/
|
||
|
static void
|
||
|
destroyEntryData(CRLGENEntryData *data)
|
||
|
{
|
||
|
if (!data)
|
||
|
return;
|
||
|
PORT_Assert(data->entry);
|
||
|
if (data->extHandle)
|
||
|
CERT_FinishExtensions(data->extHandle);
|
||
|
PORT_Free(data);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Prints error messages along with line number */
|
||
|
void
|
||
|
crlgen_PrintError(int line, char *msg, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
|
||
|
va_start(args, msg);
|
||
|
|
||
|
fprintf(stderr, "crlgen: (line: %d) ", line);
|
||
|
vfprintf(stderr, msg, args);
|
||
|
|
||
|
va_end(args);
|
||
|
}
|
||
|
/* Finds CRLGENEntryData in hashtable according PRUint64 value
|
||
|
* - certId : cert serial number*/
|
||
|
static CRLGENEntryData*
|
||
|
crlgen_FindEntry(CRLGENGeneratorData *crlGenData, SECItem *certId)
|
||
|
{
|
||
|
if (!crlGenData->entryDataHashTable || !certId)
|
||
|
return NULL;
|
||
|
return (CRLGENEntryData*)
|
||
|
PL_HashTableLookup(crlGenData->entryDataHashTable,
|
||
|
certId);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Removes CRLGENEntryData from hashtable according to certId
|
||
|
* - certId : cert serial number*/
|
||
|
static SECStatus
|
||
|
crlgen_RmEntry(CRLGENGeneratorData *crlGenData, SECItem *certId)
|
||
|
{
|
||
|
CRLGENEntryData *data = NULL;
|
||
|
|
||
|
if (!crlGenData->entryDataHashTable)
|
||
|
return SECSuccess;
|
||
|
data = crlgen_FindEntry(crlGenData, certId);
|
||
|
if (!data)
|
||
|
return SECSuccess;
|
||
|
if (PL_HashTableRemove(crlGenData->entryDataHashTable, certId))
|
||
|
return SECSuccess;
|
||
|
destroyEntryData(data);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Stores CRLGENEntryData in hashtable according to certId
|
||
|
* - certId : cert serial number*/
|
||
|
static CRLGENEntryData*
|
||
|
crlgen_PlaceAnEntry(CRLGENGeneratorData *crlGenData,
|
||
|
CERTCrlEntry *entry, SECItem *certId)
|
||
|
{
|
||
|
CRLGENEntryData *newData = NULL;
|
||
|
|
||
|
PORT_Assert(crlGenData && crlGenData->entryDataHashTable &&
|
||
|
entry);
|
||
|
if (!crlGenData || !crlGenData->entryDataHashTable || !entry) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
newData = PORT_ZNew(CRLGENEntryData);
|
||
|
if (!newData) {
|
||
|
return NULL;
|
||
|
}
|
||
|
newData->entry = entry;
|
||
|
newData->certId = certId;
|
||
|
if (!PL_HashTableAdd(crlGenData->entryDataHashTable,
|
||
|
newData->certId, newData)) {
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"Can not add entryData structure\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
return newData;
|
||
|
}
|
||
|
|
||
|
/* Use this structure to keep pointer when commiting entries extensions */
|
||
|
struct commitData {
|
||
|
int pos;
|
||
|
CERTCrlEntry **entries;
|
||
|
};
|
||
|
|
||
|
/* HT PL_HashTableEnumerateEntries callback. Sorts hashtable entries of the
|
||
|
* table he. Returns value through arg parameter*/
|
||
|
static PRIntn PR_CALLBACK
|
||
|
crlgen_CommitEntryData(PLHashEntry *he, PRIntn i, void *arg)
|
||
|
{
|
||
|
CRLGENEntryData *data = NULL;
|
||
|
|
||
|
PORT_Assert(he);
|
||
|
if (!he) {
|
||
|
return HT_ENUMERATE_NEXT;
|
||
|
}
|
||
|
data = (CRLGENEntryData*)he->value;
|
||
|
|
||
|
PORT_Assert(data);
|
||
|
PORT_Assert(arg);
|
||
|
|
||
|
if (data) {
|
||
|
struct commitData *dt = (struct commitData*)arg;
|
||
|
dt->entries[dt->pos++] = data->entry;
|
||
|
destroyEntryData(data);
|
||
|
}
|
||
|
return HT_ENUMERATE_NEXT;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Copy char * datainto allocated in arena SECItem */
|
||
|
static SECStatus
|
||
|
crlgen_SetString(PRArenaPool *arena, const char *dataIn, SECItem *value)
|
||
|
{
|
||
|
SECItem item;
|
||
|
|
||
|
PORT_Assert(arena && dataIn);
|
||
|
if (!arena || !dataIn) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
item.data = (void*)dataIn;
|
||
|
item.len = PORT_Strlen(dataIn);
|
||
|
|
||
|
return SECITEM_CopyItem(arena, value, &item);
|
||
|
}
|
||
|
|
||
|
/* Creates CERTGeneralName from parsed data for the Authority Key Extension */
|
||
|
static CERTGeneralName *
|
||
|
crlgen_GetGeneralName (PRArenaPool *arena, CRLGENGeneratorData *crlGenData,
|
||
|
const char *data)
|
||
|
{
|
||
|
CERTGeneralName *namesList = NULL;
|
||
|
CERTGeneralName *current;
|
||
|
CERTGeneralName *tail = NULL;
|
||
|
SECStatus rv = SECSuccess;
|
||
|
const char *nextChunk = NULL;
|
||
|
const char *currData = NULL;
|
||
|
int intValue;
|
||
|
char buffer[512];
|
||
|
void *mark;
|
||
|
|
||
|
if (!data)
|
||
|
return NULL;
|
||
|
PORT_Assert (arena);
|
||
|
if (!arena) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
mark = PORT_ArenaMark (arena);
|
||
|
|
||
|
nextChunk = data;
|
||
|
currData = data;
|
||
|
do {
|
||
|
int nameLen = 0;
|
||
|
char name[128];
|
||
|
const char *sepPrt = NULL;
|
||
|
nextChunk = PORT_Strchr(currData, '|');
|
||
|
if (!nextChunk)
|
||
|
nextChunk = data + strlen(data);
|
||
|
sepPrt = PORT_Strchr(currData, ':');
|
||
|
if (sepPrt == NULL || sepPrt >= nextChunk) {
|
||
|
*buffer = '\0';
|
||
|
sepPrt = nextChunk;
|
||
|
} else {
|
||
|
PORT_Memcpy(buffer, sepPrt + 1,
|
||
|
(nextChunk - sepPrt - 1));
|
||
|
buffer[nextChunk - sepPrt - 1] = '\0';
|
||
|
}
|
||
|
nameLen = PR_MIN(sepPrt - currData, sizeof(name) - 1 );
|
||
|
PORT_Memcpy(name, currData, nameLen);
|
||
|
name[nameLen] = '\0';
|
||
|
currData = nextChunk + 1;
|
||
|
|
||
|
if (!PORT_Strcmp(name, "otherName"))
|
||
|
intValue = certOtherName;
|
||
|
else if (!PORT_Strcmp(name, "rfc822Name"))
|
||
|
intValue = certRFC822Name;
|
||
|
else if (!PORT_Strcmp(name, "dnsName"))
|
||
|
intValue = certDNSName;
|
||
|
else if (!PORT_Strcmp(name, "x400Address"))
|
||
|
intValue = certX400Address;
|
||
|
else if (!PORT_Strcmp(name, "directoryName"))
|
||
|
intValue = certDirectoryName;
|
||
|
else if (!PORT_Strcmp(name, "ediPartyName"))
|
||
|
intValue = certEDIPartyName;
|
||
|
else if (!PORT_Strcmp(name, "URI"))
|
||
|
intValue = certURI;
|
||
|
else if (!PORT_Strcmp(name, "ipAddress"))
|
||
|
intValue = certIPAddress;
|
||
|
else if (!PORT_Strcmp(name, "registerID"))
|
||
|
intValue = certRegisterID;
|
||
|
else intValue = -1;
|
||
|
|
||
|
if (intValue >= certOtherName && intValue <= certRegisterID) {
|
||
|
if (namesList == NULL) {
|
||
|
namesList = current = tail = PORT_ArenaZNew(arena,
|
||
|
CERTGeneralName);
|
||
|
} else {
|
||
|
current = PORT_ArenaZNew(arena, CERTGeneralName);
|
||
|
}
|
||
|
if (current == NULL) {
|
||
|
rv = SECFailure;
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
break;
|
||
|
}
|
||
|
current->type = intValue;
|
||
|
switch (current->type) {
|
||
|
case certURI:
|
||
|
case certDNSName:
|
||
|
case certRFC822Name:
|
||
|
current->name.other.data = PORT_ArenaAlloc (arena, strlen (buffer));
|
||
|
if (current->name.other.data == NULL) {
|
||
|
rv = SECFailure;
|
||
|
break;
|
||
|
}
|
||
|
PORT_Memcpy(current->name.other.data, buffer,
|
||
|
current->name.other.len = strlen(buffer));
|
||
|
break;
|
||
|
|
||
|
case certEDIPartyName:
|
||
|
case certIPAddress:
|
||
|
case certOtherName:
|
||
|
case certRegisterID:
|
||
|
case certX400Address: {
|
||
|
|
||
|
current->name.other.data = PORT_ArenaAlloc (arena, strlen (buffer) + 2);
|
||
|
if (current->name.other.data == NULL) {
|
||
|
rv = SECFailure;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
PORT_Memcpy (current->name.other.data + 2, buffer, strlen (buffer));
|
||
|
/* This may not be accurate for all cases.For now, use this tag type */
|
||
|
current->name.other.data[0] = (char)(((current->type - 1) & 0x1f)| 0x80);
|
||
|
current->name.other.data[1] = (char)strlen (buffer);
|
||
|
current->name.other.len = strlen (buffer) + 2;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case certDirectoryName: {
|
||
|
CERTName *directoryName = NULL;
|
||
|
|
||
|
directoryName = CERT_AsciiToName (buffer);
|
||
|
if (!directoryName) {
|
||
|
rv = SECFailure;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
rv = CERT_CopyName (arena, ¤t->name.directoryName, directoryName);
|
||
|
CERT_DestroyName (directoryName);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (rv != SECSuccess)
|
||
|
break;
|
||
|
current->l.next = &(namesList->l);
|
||
|
current->l.prev = &(tail->l);
|
||
|
tail->l.next = &(current->l);
|
||
|
tail = current;
|
||
|
|
||
|
} while(nextChunk != data + strlen(data));
|
||
|
|
||
|
if (rv != SECSuccess) {
|
||
|
PORT_ArenaRelease (arena, mark);
|
||
|
namesList = NULL;
|
||
|
}
|
||
|
return (namesList);
|
||
|
}
|
||
|
|
||
|
/* Creates CERTGeneralName from parsed data for the Authority Key Extension */
|
||
|
static CERTGeneralName *
|
||
|
crlgen_DistinguishedName (PRArenaPool *arena, CRLGENGeneratorData *crlGenData,
|
||
|
const char *data)
|
||
|
{
|
||
|
CERTName *directoryName = NULL;
|
||
|
CERTGeneralName *current;
|
||
|
SECStatus rv = SECFailure;
|
||
|
void *mark;
|
||
|
|
||
|
if (!data)
|
||
|
return NULL;
|
||
|
PORT_Assert (arena);
|
||
|
if (!arena) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
mark = PORT_ArenaMark (arena);
|
||
|
|
||
|
current = PORT_ArenaZNew(arena, CERTGeneralName);
|
||
|
if (current == NULL) {
|
||
|
goto loser;
|
||
|
}
|
||
|
current->type = certDirectoryName;
|
||
|
current->l.next = ¤t->l;
|
||
|
current->l.prev = ¤t->l;
|
||
|
|
||
|
directoryName = CERT_AsciiToName ((char*)data);
|
||
|
if (!directoryName) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
rv = CERT_CopyName (arena, ¤t->name.directoryName, directoryName);
|
||
|
CERT_DestroyName (directoryName);
|
||
|
|
||
|
loser:
|
||
|
if (rv != SECSuccess) {
|
||
|
PORT_SetError (rv);
|
||
|
PORT_ArenaRelease (arena, mark);
|
||
|
current = NULL;
|
||
|
}
|
||
|
return (current);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Adding Authority Key ID extension to extension handle. */
|
||
|
static SECStatus
|
||
|
crlgen_AddAuthKeyID (CRLGENGeneratorData *crlGenData,
|
||
|
const char **dataArr)
|
||
|
{
|
||
|
void *extHandle = NULL;
|
||
|
CERTAuthKeyID *authKeyID = NULL;
|
||
|
PRArenaPool *arena = NULL;
|
||
|
SECStatus rv = SECSuccess;
|
||
|
|
||
|
PORT_Assert(dataArr && crlGenData);
|
||
|
if (!crlGenData || !dataArr) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
extHandle = crlGenData->crlExtHandle;
|
||
|
|
||
|
if (!dataArr[0] || !dataArr[1] || !dataArr[2]) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"insufficient number of parameters.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||
|
if (!arena) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
authKeyID = PORT_ArenaZNew(arena, CERTAuthKeyID);
|
||
|
if (authKeyID == NULL) {
|
||
|
rv = SECFailure;
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
if (dataArr[3] == NULL) {
|
||
|
rv = crlgen_SetString (arena, dataArr[2], &authKeyID->keyID);
|
||
|
if (rv != SECSuccess)
|
||
|
goto loser;
|
||
|
} else {
|
||
|
rv = crlgen_SetString (arena, dataArr[3],
|
||
|
&authKeyID->authCertSerialNumber);
|
||
|
if (rv != SECSuccess)
|
||
|
goto loser;
|
||
|
|
||
|
authKeyID->authCertIssuer =
|
||
|
crlgen_DistinguishedName (arena, crlGenData, dataArr[2]);
|
||
|
if (authKeyID->authCertIssuer == NULL && SECFailure == PORT_GetError ()){
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum, "syntax error.\n");
|
||
|
rv = SECFailure;
|
||
|
goto loser;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rv =
|
||
|
SECU_EncodeAndAddExtensionValue(arena, extHandle, authKeyID,
|
||
|
(*dataArr[1] == '1') ? PR_TRUE : PR_FALSE,
|
||
|
SEC_OID_X509_AUTH_KEY_ID,
|
||
|
(EXTEN_EXT_VALUE_ENCODER) CERT_EncodeAuthKeyID);
|
||
|
loser:
|
||
|
if (arena)
|
||
|
PORT_FreeArena (arena, PR_FALSE);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/* Creates and add Subject Alternative Names extension */
|
||
|
static SECStatus
|
||
|
crlgen_AddIssuerAltNames(CRLGENGeneratorData *crlGenData,
|
||
|
const char **dataArr)
|
||
|
{
|
||
|
CERTGeneralName *nameList = NULL;
|
||
|
PRArenaPool *arena = NULL;
|
||
|
void *extHandle = NULL;
|
||
|
SECStatus rv = SECSuccess;
|
||
|
|
||
|
|
||
|
PORT_Assert(dataArr && crlGenData);
|
||
|
if (!crlGenData || !dataArr) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
if (!dataArr || !dataArr[0] || !dataArr[1] || !dataArr[2]) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"insufficient number of arguments.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
PORT_Assert(dataArr && crlGenData);
|
||
|
if (!crlGenData || !dataArr) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
extHandle = crlGenData->crlExtHandle;
|
||
|
|
||
|
if (!dataArr[0] || !dataArr[1] || !dataArr[2]) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"insufficient number of parameters.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||
|
if (!arena) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
nameList = crlgen_GetGeneralName(arena, crlGenData, dataArr[2]);
|
||
|
if (nameList == NULL) {
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum, "syntax error.\n");
|
||
|
rv = SECFailure;
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
rv =
|
||
|
SECU_EncodeAndAddExtensionValue(arena, extHandle, nameList,
|
||
|
(*dataArr[1] == '1') ? PR_TRUE : PR_FALSE,
|
||
|
SEC_OID_X509_ISSUER_ALT_NAME,
|
||
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodeAltNameExtension);
|
||
|
loser:
|
||
|
if (arena)
|
||
|
PORT_FreeArena (arena, PR_FALSE);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/* Creates and adds CRLNumber extension to extension handle.
|
||
|
* Since, this is CRL extension, extension handle is the one
|
||
|
* related to CRL extensions */
|
||
|
static SECStatus
|
||
|
crlgen_AddCrlNumber(CRLGENGeneratorData *crlGenData, const char **dataArr)
|
||
|
{
|
||
|
PRArenaPool *arena = NULL;
|
||
|
SECItem encodedItem;
|
||
|
void *extHandle = crlGenData->crlExtHandle;
|
||
|
void *dummy;
|
||
|
SECStatus rv = SECFailure;
|
||
|
int code = 0;
|
||
|
|
||
|
PORT_Assert(dataArr && crlGenData);
|
||
|
if (!crlGenData || !dataArr) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
if (!dataArr[0] || !dataArr[1] || !dataArr[2]) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"insufficient number of arguments.\n");
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||
|
if (arena == NULL) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
code = atoi(dataArr[2]);
|
||
|
if (code == 0 && *dataArr[2] != '0') {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
dummy = SEC_ASN1EncodeInteger(arena, &encodedItem, code);
|
||
|
if (!dummy) {
|
||
|
rv = SECFailure;
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
rv = CERT_AddExtension (extHandle, SEC_OID_X509_CRL_NUMBER, &encodedItem,
|
||
|
(*dataArr[1] == '1') ? PR_TRUE : PR_FALSE,
|
||
|
PR_TRUE);
|
||
|
|
||
|
loser:
|
||
|
if (arena)
|
||
|
PORT_FreeArena(arena, PR_FALSE);
|
||
|
return rv;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Creates Cert Revocation Reason code extension. Encodes it and
|
||
|
* returns as SECItem structure */
|
||
|
static SECItem*
|
||
|
crlgen_CreateReasonCode(PRArenaPool *arena, const char **dataArr,
|
||
|
int *extCode)
|
||
|
{
|
||
|
SECItem *encodedItem;
|
||
|
void *dummy;
|
||
|
void *mark;
|
||
|
int code = 0;
|
||
|
|
||
|
PORT_Assert(arena && dataArr);
|
||
|
if (!arena || !dataArr) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
mark = PORT_ArenaMark(arena);
|
||
|
|
||
|
encodedItem = PORT_ArenaZNew (arena, SECItem);
|
||
|
if (encodedItem == NULL) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
if (dataArr[2] == NULL) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
code = atoi(dataArr[2]);
|
||
|
/* aACompromise(10) is the last possible of the values
|
||
|
* for the Reason Core Extension */
|
||
|
if ((code == 0 && *dataArr[2] != '0') || code > 10) {
|
||
|
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
dummy = SEC_ASN1EncodeInteger(arena, encodedItem, code);
|
||
|
if (!dummy) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
*extCode = SEC_OID_X509_REASON_CODE;
|
||
|
return encodedItem;
|
||
|
|
||
|
loser:
|
||
|
PORT_ArenaRelease (arena, mark);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Creates Cert Invalidity Date extension. Encodes it and
|
||
|
* returns as SECItem structure */
|
||
|
static SECItem*
|
||
|
crlgen_CreateInvalidityDate(PRArenaPool *arena, const char **dataArr,
|
||
|
int *extCode)
|
||
|
{
|
||
|
SECItem *encodedItem;
|
||
|
int length = 0;
|
||
|
void *mark;
|
||
|
|
||
|
PORT_Assert(arena && dataArr);
|
||
|
if (!arena || !dataArr) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
mark = PORT_ArenaMark(arena);
|
||
|
|
||
|
encodedItem = PORT_ArenaZNew(arena, SECItem);
|
||
|
if (encodedItem == NULL) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
length = PORT_Strlen(dataArr[2]);
|
||
|
|
||
|
encodedItem->type = siGeneralizedTime;
|
||
|
encodedItem->data = PORT_ArenaAlloc(arena, length);
|
||
|
if (!encodedItem->data) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
PORT_Memcpy(encodedItem->data, dataArr[2], (encodedItem->len = length) *
|
||
|
sizeof(char));
|
||
|
|
||
|
*extCode = SEC_OID_X509_INVALID_DATE;
|
||
|
return encodedItem;
|
||
|
|
||
|
loser:
|
||
|
PORT_ArenaRelease(arena, mark);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Creates(by calling extCreator function) and adds extension to a set
|
||
|
* of already added certs. Uses values of rangeFrom and rangeTo from
|
||
|
* CRLGENCrlGenCtl structure for identifying the inclusive set of certs */
|
||
|
static SECStatus
|
||
|
crlgen_AddEntryExtension(CRLGENGeneratorData *crlGenData,
|
||
|
const char **dataArr, char *extName,
|
||
|
SECItem* (*extCreator)(PRArenaPool *arena,
|
||
|
const char **dataArr,
|
||
|
int *extCode))
|
||
|
{
|
||
|
PRUint64 i = 0;
|
||
|
SECStatus rv = SECFailure;
|
||
|
int extCode = 0;
|
||
|
PRUint64 lastRange ;
|
||
|
SECItem *ext = NULL;
|
||
|
PRArenaPool *arena = NULL;
|
||
|
|
||
|
|
||
|
PORT_Assert(crlGenData && dataArr);
|
||
|
if (!crlGenData || !dataArr) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
if (!dataArr[0] || !dataArr[1]) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"insufficient number of arguments.\n");
|
||
|
}
|
||
|
|
||
|
lastRange = crlGenData->rangeTo - crlGenData->rangeFrom + 1;
|
||
|
|
||
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||
|
if (arena == NULL) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
ext = extCreator(arena, dataArr, &extCode);
|
||
|
if (ext == NULL) {
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"got error while creating extension: %s\n",
|
||
|
extName);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
for (i = 0;i < lastRange;i++) {
|
||
|
CRLGENEntryData * extData = NULL;
|
||
|
void *extHandle = NULL;
|
||
|
SECItem * certIdItem =
|
||
|
SEC_ASN1EncodeInteger(arena, NULL,
|
||
|
crlGenData->rangeFrom + i);
|
||
|
if (!certIdItem) {
|
||
|
rv = SECFailure;
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
extData = crlgen_FindEntry(crlGenData, certIdItem);
|
||
|
if (!extData) {
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"can not add extension: crl entry "
|
||
|
"(serial number: %d) is not in the list yet.\n",
|
||
|
crlGenData->rangeFrom + i);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
extHandle = extData->extHandle;
|
||
|
if (extHandle == NULL) {
|
||
|
extHandle = extData->extHandle =
|
||
|
CERT_StartCRLEntryExtensions(&crlGenData->signCrl->crl,
|
||
|
(CERTCrlEntry*)extData->entry);
|
||
|
}
|
||
|
rv = CERT_AddExtension (extHandle, extCode, ext,
|
||
|
(*dataArr[1] == '1') ? PR_TRUE : PR_FALSE,
|
||
|
PR_TRUE);
|
||
|
if (rv == SECFailure) {
|
||
|
goto loser;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
loser:
|
||
|
if (arena)
|
||
|
PORT_FreeArena(arena, PR_FALSE);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Commits all added entries and their's extensions into CRL. */
|
||
|
SECStatus
|
||
|
CRLGEN_CommitExtensionsAndEntries(CRLGENGeneratorData *crlGenData)
|
||
|
{
|
||
|
int size = 0;
|
||
|
CERTCrl *crl;
|
||
|
PRArenaPool *arena;
|
||
|
SECStatus rv = SECSuccess;
|
||
|
void *mark;
|
||
|
|
||
|
PORT_Assert(crlGenData && crlGenData->signCrl && crlGenData->signCrl->arena);
|
||
|
if (!crlGenData || !crlGenData->signCrl || !crlGenData->signCrl->arena) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
arena = crlGenData->signCrl->arena;
|
||
|
crl = &crlGenData->signCrl->crl;
|
||
|
|
||
|
mark = PORT_ArenaMark(arena);
|
||
|
|
||
|
if (crlGenData->crlExtHandle)
|
||
|
CERT_FinishExtensions(crlGenData->crlExtHandle);
|
||
|
|
||
|
size = crlGenData->entryDataHashTable->nentries;
|
||
|
crl->entries = NULL;
|
||
|
if (size) {
|
||
|
crl->entries = PORT_ArenaZNewArray(arena, CERTCrlEntry*, size + 1);
|
||
|
if (!crl->entries) {
|
||
|
rv = SECFailure;
|
||
|
} else {
|
||
|
struct commitData dt;
|
||
|
dt.entries = crl->entries;
|
||
|
dt.pos = 0;
|
||
|
PL_HashTableEnumerateEntries(crlGenData->entryDataHashTable,
|
||
|
&crlgen_CommitEntryData, &dt);
|
||
|
/* Last should be NULL */
|
||
|
crl->entries[size] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (rv != SECSuccess)
|
||
|
PORT_ArenaRelease(arena, mark);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/* Initializes extHandle with data from extensions array */
|
||
|
static SECStatus
|
||
|
crlgen_InitExtensionHandle(void *extHandle,
|
||
|
CERTCertExtension **extensions)
|
||
|
{
|
||
|
CERTCertExtension *extension = NULL;
|
||
|
|
||
|
if (!extensions)
|
||
|
return SECSuccess;
|
||
|
|
||
|
PORT_Assert(extHandle != NULL);
|
||
|
if (!extHandle) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
extension = *extensions;
|
||
|
while (extension) {
|
||
|
SECOidTag oidTag = SECOID_FindOIDTag (&extension->id);
|
||
|
/* shell we skip unknown extensions? */
|
||
|
CERT_AddExtension (extHandle, oidTag, &extension->value,
|
||
|
(extension->critical.len != 0) ? PR_TRUE : PR_FALSE,
|
||
|
PR_FALSE);
|
||
|
extension = *(++extensions);
|
||
|
}
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Used for initialization of extension handles for crl and certs
|
||
|
* extensions from existing CRL data then modifying existing CRL.*/
|
||
|
SECStatus
|
||
|
CRLGEN_ExtHandleInit(CRLGENGeneratorData *crlGenData)
|
||
|
{
|
||
|
CERTCrl *crl = NULL;
|
||
|
PRUint64 maxSN = 0;
|
||
|
|
||
|
PORT_Assert(crlGenData && crlGenData->signCrl &&
|
||
|
crlGenData->entryDataHashTable);
|
||
|
if (!crlGenData || !crlGenData->signCrl ||
|
||
|
!crlGenData->entryDataHashTable) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
crl = &crlGenData->signCrl->crl;
|
||
|
crlGenData->crlExtHandle = CERT_StartCRLExtensions(crl);
|
||
|
crlgen_InitExtensionHandle(crlGenData->crlExtHandle,
|
||
|
crl->extensions);
|
||
|
crl->extensions = NULL;
|
||
|
|
||
|
if (crl->entries) {
|
||
|
CERTCrlEntry **entry = crl->entries;
|
||
|
while (*entry) {
|
||
|
PRUint64 sn = DER_GetInteger(&(*entry)->serialNumber);
|
||
|
CRLGENEntryData *extData =
|
||
|
crlgen_PlaceAnEntry(crlGenData, *entry, &(*entry)->serialNumber);
|
||
|
if ((*entry)->extensions) {
|
||
|
extData->extHandle =
|
||
|
CERT_StartCRLEntryExtensions(&crlGenData->signCrl->crl,
|
||
|
(CERTCrlEntry*)extData->entry);
|
||
|
if (crlgen_InitExtensionHandle(extData->extHandle,
|
||
|
(*entry)->extensions) == SECFailure)
|
||
|
return SECFailure;
|
||
|
}
|
||
|
(*entry)->extensions = NULL;
|
||
|
entry++;
|
||
|
maxSN = PR_MAX(maxSN, sn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
crlGenData->rangeFrom = crlGenData->rangeTo = maxSN + 1;
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* Parser trigger functions start here
|
||
|
*/
|
||
|
|
||
|
/* Sets new internal range value for add/rm certs.*/
|
||
|
static SECStatus
|
||
|
crlgen_SetNewRangeField(CRLGENGeneratorData *crlGenData, char *value)
|
||
|
{
|
||
|
long rangeFrom = 0, rangeTo = 0;
|
||
|
char *dashPos = NULL;
|
||
|
|
||
|
PORT_Assert(crlGenData);
|
||
|
if (!crlGenData) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
if (value == NULL) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"insufficient number of arguments.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
if ((dashPos = strchr(value, '-')) != NULL) {
|
||
|
char *rangeToS, *rangeFromS = value;
|
||
|
*dashPos = '\0';
|
||
|
rangeFrom = atoi(rangeFromS);
|
||
|
*dashPos = '-';
|
||
|
|
||
|
rangeToS = (char*)(dashPos + 1);
|
||
|
rangeTo = atol(rangeToS);
|
||
|
} else {
|
||
|
rangeFrom = atol(value);
|
||
|
rangeTo = rangeFrom;
|
||
|
}
|
||
|
|
||
|
if (rangeFrom < 1 || rangeTo<rangeFrom) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"bad cert id range: %s.\n", value);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
crlGenData->rangeFrom = rangeFrom;
|
||
|
crlGenData->rangeTo = rangeTo;
|
||
|
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Changes issuer subject field in CRL. By default this data is taken from
|
||
|
* issuer cert subject field.Not yet implemented */
|
||
|
static SECStatus
|
||
|
crlgen_SetIssuerField(CRLGENGeneratorData *crlGenData, char *value)
|
||
|
{
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"Can not change CRL issuer field.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
/* Encode and sets CRL thisUpdate and nextUpdate time fields*/
|
||
|
static SECStatus
|
||
|
crlgen_SetTimeField(CRLGENGeneratorData *crlGenData, char *value,
|
||
|
PRBool setThisUpdate)
|
||
|
{
|
||
|
CERTSignedCrl *signCrl;
|
||
|
PRArenaPool *arena;
|
||
|
CERTCrl *crl;
|
||
|
int length = 0;
|
||
|
SECItem *timeDest = NULL;
|
||
|
|
||
|
PORT_Assert(crlGenData && crlGenData->signCrl &&
|
||
|
crlGenData->signCrl->arena);
|
||
|
if (!crlGenData || !crlGenData->signCrl || !crlGenData->signCrl->arena) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
signCrl = crlGenData->signCrl;
|
||
|
arena = signCrl->arena;
|
||
|
crl = &signCrl->crl;
|
||
|
|
||
|
if (value == NULL) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"insufficient number of arguments.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
length = PORT_Strlen(value);
|
||
|
|
||
|
if (setThisUpdate == PR_TRUE) {
|
||
|
timeDest = &crl->lastUpdate;
|
||
|
} else {
|
||
|
timeDest = &crl->nextUpdate;
|
||
|
}
|
||
|
|
||
|
timeDest->type = siGeneralizedTime;
|
||
|
timeDest->data = PORT_ArenaAlloc(arena, length);
|
||
|
if (!timeDest->data) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
PORT_Memcpy(timeDest->data, value, length);
|
||
|
timeDest->len = length;
|
||
|
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Adds new extension into CRL or added cert handles */
|
||
|
static SECStatus
|
||
|
crlgen_AddExtension(CRLGENGeneratorData *crlGenData, const char **extData)
|
||
|
{
|
||
|
PORT_Assert(crlGenData && crlGenData->crlExtHandle);
|
||
|
if (!crlGenData || !crlGenData->crlExtHandle) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
if (extData == NULL || *extData == NULL) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"insufficient number of arguments.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
if (!PORT_Strcmp(*extData, "authKeyId"))
|
||
|
return crlgen_AddAuthKeyID(crlGenData, extData);
|
||
|
else if (!PORT_Strcmp(*extData, "issuerAltNames"))
|
||
|
return crlgen_AddIssuerAltNames(crlGenData, extData);
|
||
|
else if (!PORT_Strcmp(*extData, "crlNumber"))
|
||
|
return crlgen_AddCrlNumber(crlGenData, extData);
|
||
|
else if (!PORT_Strcmp(*extData, "reasonCode"))
|
||
|
return crlgen_AddEntryExtension(crlGenData, extData, "reasonCode",
|
||
|
crlgen_CreateReasonCode);
|
||
|
else if (!PORT_Strcmp(*extData, "invalidityDate"))
|
||
|
return crlgen_AddEntryExtension(crlGenData, extData, "invalidityDate",
|
||
|
crlgen_CreateInvalidityDate);
|
||
|
else {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"insufficient number of arguments.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Created CRLGENEntryData for cert with serial number certId and
|
||
|
* adds it to entryDataHashTable. certId can be a single cert serial
|
||
|
* number or an inclusive rage of certs */
|
||
|
static SECStatus
|
||
|
crlgen_AddCert(CRLGENGeneratorData *crlGenData,
|
||
|
char *certId, char *revocationDate)
|
||
|
{
|
||
|
CERTSignedCrl *signCrl;
|
||
|
SECItem *certIdItem;
|
||
|
PRArenaPool *arena;
|
||
|
PRUint64 rangeFrom = 0, rangeTo = 0, i = 0;
|
||
|
int timeValLength = -1;
|
||
|
SECStatus rv = SECFailure;
|
||
|
void *mark;
|
||
|
|
||
|
|
||
|
PORT_Assert(crlGenData && crlGenData->signCrl &&
|
||
|
crlGenData->signCrl->arena);
|
||
|
if (!crlGenData || !crlGenData->signCrl || !crlGenData->signCrl->arena) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
signCrl = crlGenData->signCrl;
|
||
|
arena = signCrl->arena;
|
||
|
|
||
|
if (!certId || !revocationDate) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"insufficient number of arguments.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
timeValLength = strlen(revocationDate);
|
||
|
|
||
|
if (crlgen_SetNewRangeField(crlGenData, certId) == SECFailure &&
|
||
|
certId) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
rangeFrom = crlGenData->rangeFrom;
|
||
|
rangeTo = crlGenData->rangeTo;
|
||
|
|
||
|
for (i = 0;i < rangeTo - rangeFrom + 1;i++) {
|
||
|
CERTCrlEntry *entry;
|
||
|
mark = PORT_ArenaMark(arena);
|
||
|
entry = PORT_ArenaZNew(arena, CERTCrlEntry);
|
||
|
if (entry == NULL) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
certIdItem = SEC_ASN1EncodeInteger(arena, &entry->serialNumber,
|
||
|
rangeFrom + i);
|
||
|
if (!certIdItem) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
if (crlgen_FindEntry(crlGenData, certIdItem)) {
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"entry already exists. Use \"range\" "
|
||
|
"and \"rmcert\" before adding a new one with the "
|
||
|
"same serial number %ld\n", rangeFrom + i);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
entry->serialNumber.type = siBuffer;
|
||
|
|
||
|
entry->revocationDate.type = siGeneralizedTime;
|
||
|
|
||
|
entry->revocationDate.data =
|
||
|
PORT_ArenaAlloc(arena, timeValLength);
|
||
|
if (entry->revocationDate.data == NULL) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
PORT_Memcpy(entry->revocationDate.data, revocationDate,
|
||
|
timeValLength * sizeof(char));
|
||
|
entry->revocationDate.len = timeValLength;
|
||
|
|
||
|
|
||
|
entry->extensions = NULL;
|
||
|
if (!crlgen_PlaceAnEntry(crlGenData, entry, certIdItem)) {
|
||
|
goto loser;
|
||
|
}
|
||
|
mark = NULL;
|
||
|
}
|
||
|
|
||
|
rv = SECSuccess;
|
||
|
loser:
|
||
|
if (mark) {
|
||
|
PORT_ArenaRelease(arena, mark);
|
||
|
}
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Removes certs from entryDataHashTable which have certId serial number.
|
||
|
* certId can have value of a range of certs */
|
||
|
static SECStatus
|
||
|
crlgen_RmCert(CRLGENGeneratorData *crlGenData, char *certId)
|
||
|
{
|
||
|
PRUint64 i = 0;
|
||
|
PRArenaPool *arena;
|
||
|
|
||
|
PORT_Assert(crlGenData && certId);
|
||
|
if (!crlGenData || !certId) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
arena = crlGenData->signCrl->arena;
|
||
|
|
||
|
if (crlgen_SetNewRangeField(crlGenData, certId) == SECFailure &&
|
||
|
certId) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
for (i = 0;i < crlGenData->rangeTo - crlGenData->rangeFrom + 1;i++) {
|
||
|
SECItem* certIdItem = SEC_ASN1EncodeInteger(NULL, NULL,
|
||
|
crlGenData->rangeFrom + i);
|
||
|
if (certIdItem) {
|
||
|
CRLGENEntryData *extData =
|
||
|
crlgen_FindEntry(crlGenData, certIdItem);
|
||
|
if (!extData) {
|
||
|
printf("Cert with id %s is not in the list\n", certId);
|
||
|
} else {
|
||
|
crlgen_RmEntry(crlGenData, certIdItem);
|
||
|
}
|
||
|
SECITEM_FreeItem(certIdItem, PR_TRUE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/*************************************************************************
|
||
|
* Lex Parser Helper functions are used to store parsed information
|
||
|
* in context related structures. Context(or state) is identified base on
|
||
|
* a type of a instruction parser currently is going through. New context
|
||
|
* is identified by first token in a line. It can be addcert context,
|
||
|
* addext context, etc. */
|
||
|
|
||
|
/* Updates CRL field depending on current context */
|
||
|
static SECStatus
|
||
|
crlgen_updateCrlFn_field(CRLGENGeneratorData *crlGenData, void *str)
|
||
|
{
|
||
|
CRLGENCrlField *fieldStr = (CRLGENCrlField*)str;
|
||
|
|
||
|
PORT_Assert(crlGenData);
|
||
|
if (!crlGenData) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
switch(crlGenData->contextId) {
|
||
|
case CRLGEN_ISSUER_CONTEXT:
|
||
|
crlgen_SetIssuerField(crlGenData, fieldStr->value);
|
||
|
break;
|
||
|
case CRLGEN_UPDATE_CONTEXT:
|
||
|
return crlgen_SetTimeField(crlGenData, fieldStr->value, PR_TRUE);
|
||
|
break;
|
||
|
case CRLGEN_NEXT_UPDATE_CONTEXT:
|
||
|
return crlgen_SetTimeField(crlGenData, fieldStr->value, PR_FALSE);
|
||
|
break;
|
||
|
case CRLGEN_CHANGE_RANGE_CONTEXT:
|
||
|
return crlgen_SetNewRangeField(crlGenData, fieldStr->value);
|
||
|
break;
|
||
|
default:
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"syntax error (unknow token type: %d)\n",
|
||
|
crlGenData->contextId);
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Sets parsed data for CRL field update into temporary structure */
|
||
|
static SECStatus
|
||
|
crlgen_setNextDataFn_field(CRLGENGeneratorData *crlGenData, void *str,
|
||
|
void *data, unsigned short dtype)
|
||
|
{
|
||
|
CRLGENCrlField *fieldStr = (CRLGENCrlField*)str;
|
||
|
|
||
|
PORT_Assert(crlGenData);
|
||
|
if (!crlGenData) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
switch (crlGenData->contextId) {
|
||
|
case CRLGEN_CHANGE_RANGE_CONTEXT:
|
||
|
if (dtype != CRLGEN_TYPE_DIGIT || dtype != CRLGEN_TYPE_DIGIT_RANGE) {
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"range value should have "
|
||
|
"numeric or numeric range values.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
break;
|
||
|
case CRLGEN_NEXT_UPDATE_CONTEXT:
|
||
|
case CRLGEN_UPDATE_CONTEXT:
|
||
|
if (dtype != CRLGEN_TYPE_ZDATE){
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"bad formated date. Should be "
|
||
|
"YYYYMMDDHHMMSSZ.\n");
|
||
|
return SECFailure;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"syntax error (unknow token type: %d).\n",
|
||
|
crlGenData->contextId, data);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
fieldStr->value = PORT_Strdup(data);
|
||
|
if (!fieldStr->value) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Triggers cert entries update depending on current context */
|
||
|
static SECStatus
|
||
|
crlgen_updateCrlFn_cert(CRLGENGeneratorData *crlGenData, void *str)
|
||
|
{
|
||
|
CRLGENCertEntry *certStr = (CRLGENCertEntry*)str;
|
||
|
|
||
|
PORT_Assert(crlGenData);
|
||
|
if (!crlGenData) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
switch(crlGenData->contextId) {
|
||
|
case CRLGEN_ADD_CERT_CONTEXT:
|
||
|
return crlgen_AddCert(crlGenData, certStr->certId,
|
||
|
certStr->revocationTime);
|
||
|
case CRLGEN_RM_CERT_CONTEXT:
|
||
|
return crlgen_RmCert(crlGenData, certStr->certId);
|
||
|
default:
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"syntax error (unknow token type: %d).\n",
|
||
|
crlGenData->contextId);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Sets parsed data for CRL entries update into temporary structure */
|
||
|
static SECStatus
|
||
|
crlgen_setNextDataFn_cert(CRLGENGeneratorData *crlGenData, void *str,
|
||
|
void *data, unsigned short dtype)
|
||
|
{
|
||
|
CRLGENCertEntry *certStr = (CRLGENCertEntry*)str;
|
||
|
|
||
|
PORT_Assert(crlGenData);
|
||
|
if (!crlGenData) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
switch(dtype) {
|
||
|
case CRLGEN_TYPE_DIGIT:
|
||
|
case CRLGEN_TYPE_DIGIT_RANGE:
|
||
|
certStr->certId = PORT_Strdup(data);
|
||
|
if (!certStr->certId) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
break;
|
||
|
case CRLGEN_TYPE_DATE:
|
||
|
case CRLGEN_TYPE_ZDATE:
|
||
|
certStr->revocationTime = PORT_Strdup(data);
|
||
|
if (!certStr->revocationTime) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"syntax error (unknow token type: %d).\n",
|
||
|
crlGenData->contextId);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Triggers cert entries/crl extension update */
|
||
|
static SECStatus
|
||
|
crlgen_updateCrlFn_extension(CRLGENGeneratorData *crlGenData, void *str)
|
||
|
{
|
||
|
CRLGENExtensionEntry *extStr = (CRLGENExtensionEntry*)str;
|
||
|
|
||
|
return crlgen_AddExtension(crlGenData, (const char**)extStr->extData);
|
||
|
}
|
||
|
|
||
|
/* Defines maximum number of fields extension may have */
|
||
|
#define MAX_EXT_DATA_LENGTH 10
|
||
|
|
||
|
/* Sets parsed extension data for CRL entries/CRL extensions update
|
||
|
* into temporary structure */
|
||
|
static SECStatus
|
||
|
crlgen_setNextDataFn_extension(CRLGENGeneratorData *crlGenData, void *str,
|
||
|
void *data, unsigned short dtype)
|
||
|
{
|
||
|
CRLGENExtensionEntry *extStr = (CRLGENExtensionEntry*)str;
|
||
|
|
||
|
PORT_Assert(crlGenData);
|
||
|
if (!crlGenData) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
if (extStr->extData == NULL) {
|
||
|
extStr->extData = PORT_ZNewArray(char *, MAX_EXT_DATA_LENGTH);
|
||
|
if (!extStr->extData) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
}
|
||
|
if (extStr->nextUpdatedData >= MAX_EXT_DATA_LENGTH) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"number of fields in extension "
|
||
|
"exceeded maximum allowed data length: %d.\n",
|
||
|
MAX_EXT_DATA_LENGTH);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
extStr->extData[extStr->nextUpdatedData] = PORT_Strdup(data);
|
||
|
if (!extStr->extData[extStr->nextUpdatedData]) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
extStr->nextUpdatedData += 1;
|
||
|
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************************
|
||
|
* Top level functions are triggered directly by parser.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* crl generation script parser recreates a temporary data staructure
|
||
|
* for each line it is going through. This function cleans temp structure.
|
||
|
*/
|
||
|
void
|
||
|
crlgen_destroyTempData(CRLGENGeneratorData *crlGenData)
|
||
|
{
|
||
|
if (crlGenData->contextId != CRLGEN_UNKNOWN_CONTEXT) {
|
||
|
switch(crlGenData->contextId) {
|
||
|
case CRLGEN_ISSUER_CONTEXT:
|
||
|
case CRLGEN_UPDATE_CONTEXT:
|
||
|
case CRLGEN_NEXT_UPDATE_CONTEXT:
|
||
|
case CRLGEN_CHANGE_RANGE_CONTEXT:
|
||
|
if (crlGenData->crlField->value)
|
||
|
PORT_Free(crlGenData->crlField->value);
|
||
|
PORT_Free(crlGenData->crlField);
|
||
|
break;
|
||
|
case CRLGEN_ADD_CERT_CONTEXT:
|
||
|
case CRLGEN_RM_CERT_CONTEXT:
|
||
|
if (crlGenData->certEntry->certId)
|
||
|
PORT_Free(crlGenData->certEntry->certId);
|
||
|
if (crlGenData->certEntry->revocationTime)
|
||
|
PORT_Free(crlGenData->certEntry->revocationTime);
|
||
|
PORT_Free(crlGenData->certEntry);
|
||
|
break;
|
||
|
case CRLGEN_ADD_EXTENSION_CONTEXT:
|
||
|
if (crlGenData->extensionEntry->extData) {
|
||
|
int i = 0;
|
||
|
for (;i < crlGenData->extensionEntry->nextUpdatedData;i++)
|
||
|
PORT_Free(*(crlGenData->extensionEntry->extData + i));
|
||
|
PORT_Free(crlGenData->extensionEntry->extData);
|
||
|
}
|
||
|
PORT_Free(crlGenData->extensionEntry);
|
||
|
break;
|
||
|
}
|
||
|
crlGenData->contextId = CRLGEN_UNKNOWN_CONTEXT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
crlgen_updateCrl(CRLGENGeneratorData *crlGenData)
|
||
|
{
|
||
|
SECStatus rv = SECSuccess;
|
||
|
|
||
|
PORT_Assert(crlGenData);
|
||
|
if (!crlGenData) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
switch(crlGenData->contextId) {
|
||
|
case CRLGEN_ISSUER_CONTEXT:
|
||
|
case CRLGEN_UPDATE_CONTEXT:
|
||
|
case CRLGEN_NEXT_UPDATE_CONTEXT:
|
||
|
case CRLGEN_CHANGE_RANGE_CONTEXT:
|
||
|
rv = crlGenData->crlField->updateCrlFn(crlGenData, crlGenData->crlField);
|
||
|
break;
|
||
|
case CRLGEN_RM_CERT_CONTEXT:
|
||
|
case CRLGEN_ADD_CERT_CONTEXT:
|
||
|
rv = crlGenData->certEntry->updateCrlFn(crlGenData, crlGenData->certEntry);
|
||
|
break;
|
||
|
case CRLGEN_ADD_EXTENSION_CONTEXT:
|
||
|
rv = crlGenData->extensionEntry->
|
||
|
updateCrlFn(crlGenData, crlGenData->extensionEntry);
|
||
|
break;
|
||
|
case CRLGEN_UNKNOWN_CONTEXT:
|
||
|
break;
|
||
|
default:
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"unknown lang context type code: %d.\n",
|
||
|
crlGenData->contextId);
|
||
|
PORT_Assert(0);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
/* Clrean structures after crl update */
|
||
|
crlgen_destroyTempData(crlGenData);
|
||
|
|
||
|
crlGenData->parsedLineNum += 1;
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
crlgen_setNextData(CRLGENGeneratorData *crlGenData, void *data,
|
||
|
unsigned short dtype)
|
||
|
{
|
||
|
SECStatus rv = SECSuccess;
|
||
|
|
||
|
PORT_Assert(crlGenData);
|
||
|
if (!crlGenData) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
switch(crlGenData->contextId) {
|
||
|
case CRLGEN_ISSUER_CONTEXT:
|
||
|
case CRLGEN_UPDATE_CONTEXT:
|
||
|
case CRLGEN_NEXT_UPDATE_CONTEXT:
|
||
|
case CRLGEN_CHANGE_RANGE_CONTEXT:
|
||
|
rv = crlGenData->crlField->setNextDataFn(crlGenData, crlGenData->crlField,
|
||
|
data, dtype);
|
||
|
break;
|
||
|
case CRLGEN_ADD_CERT_CONTEXT:
|
||
|
case CRLGEN_RM_CERT_CONTEXT:
|
||
|
rv = crlGenData->certEntry->setNextDataFn(crlGenData, crlGenData->certEntry,
|
||
|
data, dtype);
|
||
|
break;
|
||
|
case CRLGEN_ADD_EXTENSION_CONTEXT:
|
||
|
rv =
|
||
|
crlGenData->extensionEntry->
|
||
|
setNextDataFn(crlGenData, crlGenData->extensionEntry, data, dtype);
|
||
|
break;
|
||
|
case CRLGEN_UNKNOWN_CONTEXT:
|
||
|
break;
|
||
|
default:
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"unknown context type: %d.\n",
|
||
|
crlGenData->contextId);
|
||
|
PORT_Assert(0);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
crlgen_createNewLangStruct(CRLGENGeneratorData *crlGenData,
|
||
|
unsigned structType)
|
||
|
{
|
||
|
PORT_Assert(crlGenData &&
|
||
|
crlGenData->contextId == CRLGEN_UNKNOWN_CONTEXT);
|
||
|
if (!crlGenData ||
|
||
|
crlGenData->contextId != CRLGEN_UNKNOWN_CONTEXT) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
switch(structType) {
|
||
|
case CRLGEN_ISSUER_CONTEXT:
|
||
|
case CRLGEN_UPDATE_CONTEXT:
|
||
|
case CRLGEN_NEXT_UPDATE_CONTEXT:
|
||
|
case CRLGEN_CHANGE_RANGE_CONTEXT:
|
||
|
crlGenData->crlField = PORT_New(CRLGENCrlField);
|
||
|
if (!crlGenData->crlField) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
crlGenData->contextId = structType;
|
||
|
crlGenData->crlField->value = NULL;
|
||
|
crlGenData->crlField->updateCrlFn = &crlgen_updateCrlFn_field;
|
||
|
crlGenData->crlField->setNextDataFn = &crlgen_setNextDataFn_field;
|
||
|
break;
|
||
|
case CRLGEN_RM_CERT_CONTEXT:
|
||
|
case CRLGEN_ADD_CERT_CONTEXT:
|
||
|
crlGenData->certEntry = PORT_New(CRLGENCertEntry);
|
||
|
if (!crlGenData->certEntry) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
crlGenData->contextId = structType;
|
||
|
crlGenData->certEntry->certId = 0;
|
||
|
crlGenData->certEntry->revocationTime = NULL;
|
||
|
crlGenData->certEntry->updateCrlFn = &crlgen_updateCrlFn_cert;
|
||
|
crlGenData->certEntry->setNextDataFn = &crlgen_setNextDataFn_cert;
|
||
|
break;
|
||
|
case CRLGEN_ADD_EXTENSION_CONTEXT:
|
||
|
crlGenData->extensionEntry = PORT_New(CRLGENExtensionEntry);
|
||
|
if (!crlGenData->extensionEntry) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
crlGenData->contextId = structType;
|
||
|
crlGenData->extensionEntry->extData = NULL;
|
||
|
crlGenData->extensionEntry->nextUpdatedData = 0;
|
||
|
crlGenData->extensionEntry->updateCrlFn =
|
||
|
&crlgen_updateCrlFn_extension;
|
||
|
crlGenData->extensionEntry->setNextDataFn =
|
||
|
&crlgen_setNextDataFn_extension;
|
||
|
break;
|
||
|
case CRLGEN_UNKNOWN_CONTEXT:
|
||
|
break;
|
||
|
default:
|
||
|
crlgen_PrintError(crlGenData->parsedLineNum,
|
||
|
"unknown context type: %d.\n", structType);
|
||
|
PORT_Assert(0);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Parser initialization function */
|
||
|
CRLGENGeneratorData*
|
||
|
CRLGEN_InitCrlGeneration(CERTSignedCrl *signCrl, PRFileDesc *src)
|
||
|
{
|
||
|
CRLGENGeneratorData *crlGenData = NULL;
|
||
|
|
||
|
PORT_Assert(signCrl && src);
|
||
|
if (!signCrl || !src) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
crlGenData = PORT_ZNew(CRLGENGeneratorData);
|
||
|
if (!crlGenData) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
crlGenData->entryDataHashTable =
|
||
|
PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
|
||
|
PL_CompareValues, NULL, NULL);
|
||
|
if (!crlGenData->entryDataHashTable) {
|
||
|
PORT_Free(crlGenData);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
crlGenData->src = src;
|
||
|
crlGenData->parsedLineNum = 1;
|
||
|
crlGenData->contextId = CRLGEN_UNKNOWN_CONTEXT;
|
||
|
crlGenData->signCrl = signCrl;
|
||
|
crlGenData->rangeFrom = 0;
|
||
|
crlGenData->rangeTo = 0;
|
||
|
crlGenData->crlExtHandle = NULL;
|
||
|
|
||
|
PORT_SetError(0);
|
||
|
|
||
|
return crlGenData;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CRLGEN_FinalizeCrlGeneration(CRLGENGeneratorData *crlGenData)
|
||
|
{
|
||
|
if (!crlGenData)
|
||
|
return;
|
||
|
if (crlGenData->src)
|
||
|
PR_Close(crlGenData->src);
|
||
|
PL_HashTableDestroy(crlGenData->entryDataHashTable);
|
||
|
PORT_Free(crlGenData);
|
||
|
}
|
||
|
|