diff options
Diffstat (limited to 'security/nss/cmd/crlutil/crlgen.c')
-rw-r--r-- | security/nss/cmd/crlutil/crlgen.c | 1542 |
1 files changed, 1542 insertions, 0 deletions
diff --git a/security/nss/cmd/crlutil/crlgen.c b/security/nss/cmd/crlutil/crlgen.c new file mode 100644 index 000000000..1f9dc4b43 --- /dev/null +++ b/security/nss/cmd/crlutil/crlgen.c @@ -0,0 +1,1542 @@ +/* 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/. */ + +/* +** 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" + +/* 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; + SECStatus rv = SECSuccess; + + if (!crlGenData->entryDataHashTable) { + return SECSuccess; + } + + data = crlgen_FindEntry(crlGenData, certId); + if (!data) { + return SECSuccess; + } + + if (!PL_HashTableRemove(crlGenData->entryDataHashTable, certId)) { + rv = SECFailure; + } + + destroyEntryData(data); + return rv; +} + +/* 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(PLArenaPool *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(PLArenaPool *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(PLArenaPool *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; + PLArenaPool *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; + PLArenaPool *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) +{ + PLArenaPool *arena = NULL; + SECItem encodedItem; + 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(crlGenData->crlExtHandle, 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(PLArenaPool *arena, const char **dataArr, + int *extCode) +{ + SECItem *encodedItem; + void *dummy; + void *mark = NULL; + 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: + if (mark) { + PORT_ArenaRelease(arena, mark); + } + return NULL; +} + +/* Creates Cert Invalidity Date extension. Encodes it and + * returns as SECItem structure */ +static SECItem * +crlgen_CreateInvalidityDate(PLArenaPool *arena, const char **dataArr, + int *extCode) +{ + SECItem *encodedItem; + int length = 0; + void *mark = NULL; + + 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: + if (mark) { + 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)(PLArenaPool *arena, + const char **dataArr, + int *extCode)) +{ + PRUint64 i = 0; + SECStatus rv = SECFailure; + int extCode = 0; + PRUint64 lastRange; + SECItem *ext = NULL; + PLArenaPool *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; + PLArenaPool *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; + PLArenaPool *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; + PLArenaPool *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; + + PORT_Assert(crlGenData && certId); + if (!crlGenData || !certId) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + 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); +} |