зеркало из https://github.com/mozilla/gecko-dev.git
2213 строки
70 KiB
C
2213 строки
70 KiB
C
/* 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/. */
|
|
|
|
/*
|
|
** certext.c
|
|
**
|
|
** part of certutil for managing certificates extensions
|
|
**
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#if defined(WIN32)
|
|
#include "fcntl.h"
|
|
#include "io.h"
|
|
#endif
|
|
|
|
#include "secutil.h"
|
|
|
|
#if defined(XP_UNIX)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "cert.h"
|
|
#include "xconst.h"
|
|
#include "prprf.h"
|
|
#include "certutil.h"
|
|
#include "genname.h"
|
|
#include "prnetdb.h"
|
|
|
|
#define GEN_BREAK(e) \
|
|
rv = e; \
|
|
break;
|
|
|
|
static char *
|
|
Gets_s(char *buff, size_t size)
|
|
{
|
|
char *str;
|
|
|
|
if (buff == NULL || size < 1) {
|
|
PORT_Assert(0);
|
|
return NULL;
|
|
}
|
|
if ((str = fgets(buff, size, stdin)) != NULL) {
|
|
int len = PORT_Strlen(str);
|
|
/*
|
|
* fgets() automatically converts native text file
|
|
* line endings to '\n'. As defensive programming
|
|
* (just in case fgets has a bug or we put stdin in
|
|
* binary mode by mistake), we handle three native
|
|
* text file line endings here:
|
|
* '\n' Unix (including Linux and Mac OS X)
|
|
* '\r''\n' DOS/Windows & OS/2
|
|
* '\r' Mac OS Classic
|
|
* len can not be less then 1, since in case with
|
|
* empty string it has at least '\n' in the buffer
|
|
*/
|
|
if (buff[len - 1] == '\n' || buff[len - 1] == '\r') {
|
|
buff[len - 1] = '\0';
|
|
if (len > 1 && buff[len - 2] == '\r')
|
|
buff[len - 2] = '\0';
|
|
}
|
|
} else {
|
|
buff[0] = '\0';
|
|
}
|
|
return str;
|
|
}
|
|
|
|
static SECStatus
|
|
PrintChoicesAndGetAnswer(char *str, char *rBuff, int rSize)
|
|
{
|
|
fputs(str, stdout);
|
|
fputs(" > ", stdout);
|
|
fflush(stdout);
|
|
if (Gets_s(rBuff, rSize) == NULL) {
|
|
PORT_SetError(SEC_ERROR_INPUT_LEN);
|
|
return SECFailure;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
static CERTGeneralName *
|
|
GetGeneralName(PLArenaPool *arena, CERTGeneralName *useExistingName, PRBool onlyOne)
|
|
{
|
|
CERTGeneralName *namesList = NULL;
|
|
CERTGeneralName *current;
|
|
CERTGeneralName *tail = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
int intValue;
|
|
char buffer[512];
|
|
void *mark;
|
|
|
|
PORT_Assert(arena);
|
|
mark = PORT_ArenaMark(arena);
|
|
do {
|
|
if (PrintChoicesAndGetAnswer(
|
|
"\nSelect one of the following general name type: \n"
|
|
"\t2 - rfc822Name\n"
|
|
"\t3 - dnsName\n"
|
|
"\t5 - directoryName\n"
|
|
"\t7 - uniformResourceidentifier\n"
|
|
"\t8 - ipAddress\n"
|
|
"\t9 - registerID\n"
|
|
"\tAny other number to finish\n"
|
|
"\t\tChoice:",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
intValue = PORT_Atoi(buffer);
|
|
/*
|
|
* Should use ZAlloc instead of Alloc to avoid problem with garbage
|
|
* initialized pointers in CERT_CopyName
|
|
*/
|
|
switch (intValue) {
|
|
case certRFC822Name:
|
|
case certDNSName:
|
|
case certDirectoryName:
|
|
case certURI:
|
|
case certIPAddress:
|
|
case certRegisterID:
|
|
break;
|
|
default:
|
|
intValue = 0; /* force a break for anything else */
|
|
}
|
|
|
|
if (intValue == 0)
|
|
break;
|
|
|
|
if (namesList == NULL) {
|
|
if (useExistingName) {
|
|
namesList = current = tail = useExistingName;
|
|
} else {
|
|
namesList = current = tail =
|
|
PORT_ArenaZNew(arena, CERTGeneralName);
|
|
}
|
|
} else {
|
|
current = PORT_ArenaZNew(arena, CERTGeneralName);
|
|
}
|
|
if (current == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
current->type = intValue;
|
|
puts("\nEnter data:");
|
|
fflush(stdout);
|
|
if (Gets_s(buffer, sizeof(buffer)) == NULL) {
|
|
PORT_SetError(SEC_ERROR_INPUT_LEN);
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
switch (current->type) {
|
|
case certURI:
|
|
case certDNSName:
|
|
case certRFC822Name:
|
|
current->name.other.data =
|
|
PORT_ArenaAlloc(arena, strlen(buffer));
|
|
if (current->name.other.data == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
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) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
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) {
|
|
fprintf(stderr, "certutil: improperly formatted name: "
|
|
"\"%s\"\n",
|
|
buffer);
|
|
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 (!onlyOne);
|
|
|
|
if (rv != SECSuccess) {
|
|
PORT_ArenaRelease(arena, mark);
|
|
namesList = NULL;
|
|
}
|
|
return (namesList);
|
|
}
|
|
|
|
static CERTGeneralName *
|
|
CreateGeneralName(PLArenaPool *arena)
|
|
{
|
|
return GetGeneralName(arena, NULL, PR_FALSE);
|
|
}
|
|
|
|
static SECStatus
|
|
GetString(PLArenaPool *arena, char *prompt, SECItem *value)
|
|
{
|
|
char buffer[251];
|
|
char *buffPrt;
|
|
|
|
buffer[0] = '\0';
|
|
value->data = NULL;
|
|
value->len = 0;
|
|
|
|
puts(prompt);
|
|
buffPrt = Gets_s(buffer, sizeof(buffer));
|
|
/* returned NULL here treated the same way as empty string */
|
|
if (buffPrt && strlen(buffer) > 0) {
|
|
value->data = PORT_ArenaAlloc(arena, strlen(buffer));
|
|
if (value->data == NULL) {
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
return (SECFailure);
|
|
}
|
|
PORT_Memcpy(value->data, buffer, value->len = strlen(buffer));
|
|
}
|
|
return (SECSuccess);
|
|
}
|
|
|
|
static PRBool
|
|
GetYesNo(char *prompt)
|
|
{
|
|
char buf[3];
|
|
char *buffPrt;
|
|
|
|
buf[0] = 'n';
|
|
puts(prompt);
|
|
buffPrt = Gets_s(buf, sizeof(buf));
|
|
return (buffPrt && (buf[0] == 'y' || buf[0] == 'Y')) ? PR_TRUE : PR_FALSE;
|
|
}
|
|
|
|
/* Parses comma separated values out of the string pointed by nextPos.
|
|
* Parsed value is compared to an array of possible values(valueArray).
|
|
* If match is found, a value index is returned, otherwise returns SECFailue.
|
|
* nextPos is set to the token after found comma separator or to NULL.
|
|
* NULL in nextPos should be used as indication of the last parsed token.
|
|
* A special value "critical" can be parsed out from the supplied sting.*/
|
|
|
|
static SECStatus
|
|
parseNextCmdInput(const char *const *valueArray, int *value, char **nextPos,
|
|
PRBool *critical)
|
|
{
|
|
char *thisPos = *nextPos;
|
|
int keyLen = 0;
|
|
int arrIndex = 0;
|
|
|
|
if (!valueArray || !value || !nextPos || !critical) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
while (1) {
|
|
if ((*nextPos = strchr(thisPos, ',')) == NULL) {
|
|
keyLen = strlen(thisPos);
|
|
} else {
|
|
keyLen = *nextPos - thisPos;
|
|
*nextPos += 1;
|
|
}
|
|
/* if critical keyword is found, go for another loop,
|
|
* but check, if it is the last keyword of
|
|
* the string.*/
|
|
if (!strncmp("critical", thisPos, keyLen)) {
|
|
*critical = PR_TRUE;
|
|
if (*nextPos == NULL) {
|
|
return SECSuccess;
|
|
}
|
|
thisPos = *nextPos;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
for (arrIndex = 0; valueArray[arrIndex]; arrIndex++) {
|
|
if (!strncmp(valueArray[arrIndex], thisPos, keyLen)) {
|
|
*value = arrIndex;
|
|
return SECSuccess;
|
|
}
|
|
}
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
static const char *const
|
|
keyUsageKeyWordArray[] = { "digitalSignature",
|
|
"nonRepudiation",
|
|
"keyEncipherment",
|
|
"dataEncipherment",
|
|
"keyAgreement",
|
|
"certSigning",
|
|
"crlSigning",
|
|
NULL };
|
|
|
|
static SECStatus
|
|
AddKeyUsage(void *extHandle, const char *userSuppliedValue)
|
|
{
|
|
SECItem bitStringValue;
|
|
unsigned char keyUsage = 0x0;
|
|
char buffer[5];
|
|
int value;
|
|
char *nextPos = (char *)userSuppliedValue;
|
|
PRBool isCriticalExt = PR_FALSE;
|
|
|
|
if (!userSuppliedValue) {
|
|
while (1) {
|
|
if (PrintChoicesAndGetAnswer(
|
|
"\t\t0 - Digital Signature\n"
|
|
"\t\t1 - Non-repudiation\n"
|
|
"\t\t2 - Key encipherment\n"
|
|
"\t\t3 - Data encipherment\n"
|
|
"\t\t4 - Key agreement\n"
|
|
"\t\t5 - Cert signing key\n"
|
|
"\t\t6 - CRL signing key\n"
|
|
"\t\tOther to finish\n",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
return SECFailure;
|
|
}
|
|
value = PORT_Atoi(buffer);
|
|
if (value < 0 || value > 6)
|
|
break;
|
|
if (value == 0) {
|
|
/* Checking that zero value of variable 'value'
|
|
* corresponds to '0' input made by user */
|
|
char *chPtr = strchr(buffer, '0');
|
|
if (chPtr == NULL) {
|
|
continue;
|
|
}
|
|
}
|
|
keyUsage |= (0x80 >> value);
|
|
}
|
|
isCriticalExt = GetYesNo("Is this a critical extension [y/N]?");
|
|
} else {
|
|
while (1) {
|
|
if (parseNextCmdInput(keyUsageKeyWordArray, &value, &nextPos,
|
|
&isCriticalExt) == SECFailure) {
|
|
return SECFailure;
|
|
}
|
|
keyUsage |= (0x80 >> value);
|
|
if (!nextPos)
|
|
break;
|
|
}
|
|
}
|
|
|
|
bitStringValue.data = &keyUsage;
|
|
bitStringValue.len = 1;
|
|
|
|
return (CERT_EncodeAndAddBitStrExtension(extHandle, SEC_OID_X509_KEY_USAGE, &bitStringValue,
|
|
isCriticalExt));
|
|
}
|
|
|
|
static CERTOidSequence *
|
|
CreateOidSequence(void)
|
|
{
|
|
CERTOidSequence *rv = (CERTOidSequence *)NULL;
|
|
PLArenaPool *arena = (PLArenaPool *)NULL;
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if ((PLArenaPool *)NULL == arena) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = (CERTOidSequence *)PORT_ArenaZNew(arena, CERTOidSequence);
|
|
if ((CERTOidSequence *)NULL == rv) {
|
|
goto loser;
|
|
}
|
|
|
|
rv->oids = (SECItem **)PORT_ArenaZNew(arena, SECItem *);
|
|
if ((SECItem **)NULL == rv->oids) {
|
|
goto loser;
|
|
}
|
|
|
|
rv->arena = arena;
|
|
return rv;
|
|
|
|
loser:
|
|
if ((PLArenaPool *)NULL != arena) {
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
}
|
|
|
|
return (CERTOidSequence *)NULL;
|
|
}
|
|
|
|
static void
|
|
DestroyOidSequence(CERTOidSequence *os)
|
|
{
|
|
if (os->arena) {
|
|
PORT_FreeArena(os->arena, PR_FALSE);
|
|
}
|
|
}
|
|
|
|
static SECStatus
|
|
AddOidToSequence(CERTOidSequence *os, SECOidTag oidTag)
|
|
{
|
|
SECItem **oids;
|
|
PRUint32 count = 0;
|
|
SECOidData *od;
|
|
|
|
od = SECOID_FindOIDByTag(oidTag);
|
|
if ((SECOidData *)NULL == od) {
|
|
return SECFailure;
|
|
}
|
|
|
|
for (oids = os->oids; (SECItem *)NULL != *oids; oids++) {
|
|
if (*oids == &od->oid) {
|
|
/* We already have this oid */
|
|
return SECSuccess;
|
|
}
|
|
count++;
|
|
}
|
|
|
|
/* ArenaZRealloc */
|
|
|
|
{
|
|
PRUint32 i;
|
|
|
|
oids = (SECItem **)PORT_ArenaZNewArray(os->arena, SECItem *, count + 2);
|
|
if ((SECItem **)NULL == oids) {
|
|
return SECFailure;
|
|
}
|
|
|
|
for (i = 0; i < count; i++) {
|
|
oids[i] = os->oids[i];
|
|
}
|
|
|
|
/* ArenaZFree(os->oids); */
|
|
}
|
|
|
|
os->oids = oids;
|
|
os->oids[count] = &od->oid;
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
|
|
|
|
const SEC_ASN1Template CERT_OidSeqTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, offsetof(CERTOidSequence, oids),
|
|
SEC_ASN1_SUB(SEC_ObjectIDTemplate) }
|
|
};
|
|
|
|
static SECItem *
|
|
EncodeOidSequence(CERTOidSequence *os)
|
|
{
|
|
SECItem *rv;
|
|
|
|
rv = (SECItem *)PORT_ArenaZNew(os->arena, SECItem);
|
|
if ((SECItem *)NULL == rv) {
|
|
goto loser;
|
|
}
|
|
|
|
if (!SEC_ASN1EncodeItem(os->arena, rv, os, CERT_OidSeqTemplate)) {
|
|
goto loser;
|
|
}
|
|
|
|
return rv;
|
|
|
|
loser:
|
|
return (SECItem *)NULL;
|
|
}
|
|
|
|
static const char *const
|
|
extKeyUsageKeyWordArray[] = { "serverAuth",
|
|
"clientAuth",
|
|
"codeSigning",
|
|
"emailProtection",
|
|
"timeStamp",
|
|
"ocspResponder",
|
|
"stepUp",
|
|
"msTrustListSigning",
|
|
NULL };
|
|
|
|
static SECStatus
|
|
AddExtKeyUsage(void *extHandle, const char *userSuppliedValue)
|
|
{
|
|
char buffer[5];
|
|
int value;
|
|
CERTOidSequence *os;
|
|
SECStatus rv;
|
|
SECItem *item;
|
|
PRBool isCriticalExt = PR_FALSE;
|
|
char *nextPos = (char *)userSuppliedValue;
|
|
|
|
os = CreateOidSequence();
|
|
if ((CERTOidSequence *)NULL == os) {
|
|
return SECFailure;
|
|
}
|
|
|
|
while (1) {
|
|
if (!userSuppliedValue) {
|
|
if (PrintChoicesAndGetAnswer(
|
|
"\t\t0 - Server Auth\n"
|
|
"\t\t1 - Client Auth\n"
|
|
"\t\t2 - Code Signing\n"
|
|
"\t\t3 - Email Protection\n"
|
|
"\t\t4 - Timestamp\n"
|
|
"\t\t5 - OCSP Responder\n"
|
|
"\t\t6 - Step-up\n"
|
|
"\t\t7 - Microsoft Trust List Signing\n"
|
|
"\t\tOther to finish\n",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
value = PORT_Atoi(buffer);
|
|
|
|
if (value == 0) {
|
|
/* Checking that zero value of variable 'value'
|
|
* corresponds to '0' input made by user */
|
|
char *chPtr = strchr(buffer, '0');
|
|
if (chPtr == NULL) {
|
|
continue;
|
|
}
|
|
}
|
|
} else {
|
|
if (parseNextCmdInput(extKeyUsageKeyWordArray, &value, &nextPos,
|
|
&isCriticalExt) == SECFailure) {
|
|
return SECFailure;
|
|
}
|
|
}
|
|
|
|
switch (value) {
|
|
case 0:
|
|
rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH);
|
|
break;
|
|
case 1:
|
|
rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH);
|
|
break;
|
|
case 2:
|
|
rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_CODE_SIGN);
|
|
break;
|
|
case 3:
|
|
rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT);
|
|
break;
|
|
case 4:
|
|
rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_TIME_STAMP);
|
|
break;
|
|
case 5:
|
|
rv = AddOidToSequence(os, SEC_OID_OCSP_RESPONDER);
|
|
break;
|
|
case 6:
|
|
rv = AddOidToSequence(os, SEC_OID_NS_KEY_USAGE_GOVT_APPROVED);
|
|
break;
|
|
case 7:
|
|
rv = AddOidToSequence(os, SEC_OID_MS_EXT_KEY_USAGE_CTL_SIGNING);
|
|
break;
|
|
default:
|
|
goto endloop;
|
|
}
|
|
|
|
if (userSuppliedValue && !nextPos)
|
|
break;
|
|
if (SECSuccess != rv)
|
|
goto loser;
|
|
}
|
|
|
|
endloop:
|
|
item = EncodeOidSequence(os);
|
|
|
|
if (!userSuppliedValue) {
|
|
isCriticalExt = GetYesNo("Is this a critical extension [y/N]?");
|
|
}
|
|
|
|
rv = CERT_AddExtension(extHandle, SEC_OID_X509_EXT_KEY_USAGE, item,
|
|
isCriticalExt, PR_TRUE);
|
|
/*FALLTHROUGH*/
|
|
loser:
|
|
DestroyOidSequence(os);
|
|
return rv;
|
|
}
|
|
|
|
static const char *const
|
|
nsCertTypeKeyWordArray[] = { "sslClient",
|
|
"sslServer",
|
|
"smime",
|
|
"objectSigning",
|
|
"Not!Used",
|
|
"sslCA",
|
|
"smimeCA",
|
|
"objectSigningCA",
|
|
NULL };
|
|
|
|
static SECStatus
|
|
AddNscpCertType(void *extHandle, const char *userSuppliedValue)
|
|
{
|
|
SECItem bitStringValue;
|
|
unsigned char keyUsage = 0x0;
|
|
char buffer[5];
|
|
int value;
|
|
char *nextPos = (char *)userSuppliedValue;
|
|
PRBool isCriticalExt = PR_FALSE;
|
|
|
|
if (!userSuppliedValue) {
|
|
while (1) {
|
|
if (PrintChoicesAndGetAnswer(
|
|
"\t\t0 - SSL Client\n"
|
|
"\t\t1 - SSL Server\n"
|
|
"\t\t2 - S/MIME\n"
|
|
"\t\t3 - Object Signing\n"
|
|
"\t\t4 - Reserved for future use\n"
|
|
"\t\t5 - SSL CA\n"
|
|
"\t\t6 - S/MIME CA\n"
|
|
"\t\t7 - Object Signing CA\n"
|
|
"\t\tOther to finish\n",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
return SECFailure;
|
|
}
|
|
value = PORT_Atoi(buffer);
|
|
if (value < 0 || value > 7)
|
|
break;
|
|
if (value == 0) {
|
|
/* Checking that zero value of variable 'value'
|
|
* corresponds to '0' input made by user */
|
|
char *chPtr = strchr(buffer, '0');
|
|
if (chPtr == NULL) {
|
|
continue;
|
|
}
|
|
}
|
|
keyUsage |= (0x80 >> value);
|
|
}
|
|
isCriticalExt = GetYesNo("Is this a critical extension [y/N]?");
|
|
} else {
|
|
while (1) {
|
|
if (parseNextCmdInput(nsCertTypeKeyWordArray, &value, &nextPos,
|
|
&isCriticalExt) == SECFailure) {
|
|
return SECFailure;
|
|
}
|
|
keyUsage |= (0x80 >> value);
|
|
if (!nextPos)
|
|
break;
|
|
}
|
|
}
|
|
|
|
bitStringValue.data = &keyUsage;
|
|
bitStringValue.len = 1;
|
|
|
|
return (CERT_EncodeAndAddBitStrExtension(extHandle, SEC_OID_NS_CERT_EXT_CERT_TYPE, &bitStringValue,
|
|
isCriticalExt));
|
|
}
|
|
|
|
SECStatus
|
|
GetOidFromString(PLArenaPool *arena, SECItem *to,
|
|
const char *from, size_t fromLen)
|
|
{
|
|
SECStatus rv;
|
|
SECOidTag tag;
|
|
SECOidData *coid;
|
|
|
|
/* try dotted form first */
|
|
rv = SEC_StringToOID(arena, to, from, fromLen);
|
|
if (rv == SECSuccess) {
|
|
return rv;
|
|
}
|
|
|
|
/* Check to see if it matches a name in our oid table.
|
|
* SECOID_FindOIDByTag returns NULL if tag is out of bounds.
|
|
*/
|
|
tag = SEC_OID_UNKNOWN;
|
|
coid = SECOID_FindOIDByTag(tag);
|
|
for (; coid; coid = SECOID_FindOIDByTag(++tag)) {
|
|
if (PORT_Strncasecmp(from, coid->desc, fromLen) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (coid == NULL) {
|
|
/* none found */
|
|
return SECFailure;
|
|
}
|
|
return SECITEM_CopyItem(arena, to, &coid->oid);
|
|
}
|
|
|
|
static SECStatus
|
|
AddSubjectAltNames(PLArenaPool *arena, CERTGeneralName **existingListp,
|
|
const char *constNames, CERTGeneralNameType type)
|
|
{
|
|
CERTGeneralName *nameList = NULL;
|
|
CERTGeneralName *current = NULL;
|
|
PRCList *prev = NULL;
|
|
char *cp, *nextName = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
PRBool readTypeFromName = (PRBool)(type == 0);
|
|
char *names = NULL;
|
|
|
|
if (constNames)
|
|
names = PORT_Strdup(constNames);
|
|
|
|
if (names == NULL) {
|
|
return SECFailure;
|
|
}
|
|
|
|
/*
|
|
* walk down the comma separated list of names. NOTE: there is
|
|
* no sanity checks to see if the email address look like
|
|
* email addresses.
|
|
*
|
|
* Each name may optionally be prefixed with a type: string.
|
|
* If it isn't, the type from the previous name will be used.
|
|
* If there wasn't a previous name yet, the type given
|
|
* as a parameter to this function will be used.
|
|
* If the type value is zero (undefined), we'll fail.
|
|
*/
|
|
for (cp = names; cp; cp = nextName) {
|
|
int len;
|
|
char *oidString;
|
|
char *nextComma;
|
|
CERTName *name;
|
|
PRStatus status;
|
|
unsigned char *data;
|
|
PRNetAddr addr;
|
|
|
|
nextName = NULL;
|
|
if (*cp == ',') {
|
|
cp++;
|
|
}
|
|
nextComma = PORT_Strchr(cp, ',');
|
|
if (nextComma) {
|
|
*nextComma = 0;
|
|
nextName = nextComma + 1;
|
|
}
|
|
if ((*cp) == 0) {
|
|
continue;
|
|
}
|
|
if (readTypeFromName) {
|
|
char *save = cp;
|
|
/* Because we already replaced nextComma with end-of-string,
|
|
* a found colon belongs to the current name */
|
|
cp = PORT_Strchr(cp, ':');
|
|
if (cp) {
|
|
*cp = 0;
|
|
cp++;
|
|
type = CERT_GetGeneralNameTypeFromString(save);
|
|
if (*cp == 0) {
|
|
continue;
|
|
}
|
|
} else {
|
|
if (type == 0) {
|
|
/* no type known yet */
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
cp = save;
|
|
}
|
|
}
|
|
|
|
current = PORT_ArenaZNew(arena, CERTGeneralName);
|
|
if (!current) {
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
|
|
current->type = type;
|
|
switch (type) {
|
|
/* string types */
|
|
case certRFC822Name:
|
|
case certDNSName:
|
|
case certURI:
|
|
current->name.other.data =
|
|
(unsigned char *)PORT_ArenaStrdup(arena, cp);
|
|
current->name.other.len = PORT_Strlen(cp);
|
|
break;
|
|
/* unformated data types */
|
|
case certX400Address:
|
|
case certEDIPartyName:
|
|
/* turn a string into a data and len */
|
|
rv = SECFailure; /* punt on these for now */
|
|
fprintf(stderr, "EDI Party Name and X.400 Address not supported\n");
|
|
break;
|
|
case certDirectoryName:
|
|
/* certDirectoryName */
|
|
name = CERT_AsciiToName(cp);
|
|
if (name == NULL) {
|
|
rv = SECFailure;
|
|
fprintf(stderr, "Invalid Directory Name (\"%s\")\n", cp);
|
|
break;
|
|
}
|
|
rv = CERT_CopyName(arena, ¤t->name.directoryName, name);
|
|
CERT_DestroyName(name);
|
|
break;
|
|
/* types that require more processing */
|
|
case certIPAddress:
|
|
/* convert the string to an ip address */
|
|
status = PR_StringToNetAddr(cp, &addr);
|
|
if (status != PR_SUCCESS) {
|
|
rv = SECFailure;
|
|
fprintf(stderr, "Invalid IP Address (\"%s\")\n", cp);
|
|
break;
|
|
}
|
|
|
|
if (PR_NetAddrFamily(&addr) == PR_AF_INET) {
|
|
len = sizeof(addr.inet.ip);
|
|
data = (unsigned char *)&addr.inet.ip;
|
|
} else if (PR_NetAddrFamily(&addr) == PR_AF_INET6) {
|
|
len = sizeof(addr.ipv6.ip);
|
|
data = (unsigned char *)&addr.ipv6.ip;
|
|
} else {
|
|
fprintf(stderr, "Invalid IP Family\n");
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
current->name.other.data = PORT_ArenaAlloc(arena, len);
|
|
if (current->name.other.data == NULL) {
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
current->name.other.len = len;
|
|
PORT_Memcpy(current->name.other.data, data, len);
|
|
break;
|
|
case certRegisterID:
|
|
rv = GetOidFromString(arena, ¤t->name.other, cp, strlen(cp));
|
|
break;
|
|
case certOtherName:
|
|
oidString = cp;
|
|
cp = PORT_Strchr(cp, ';');
|
|
if (cp == NULL) {
|
|
rv = SECFailure;
|
|
fprintf(stderr, "missing name in other name\n");
|
|
break;
|
|
}
|
|
*cp++ = 0;
|
|
current->name.OthName.name.data =
|
|
(unsigned char *)PORT_ArenaStrdup(arena, cp);
|
|
if (current->name.OthName.name.data == NULL) {
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
current->name.OthName.name.len = PORT_Strlen(cp);
|
|
rv = GetOidFromString(arena, ¤t->name.OthName.oid,
|
|
oidString, strlen(oidString));
|
|
break;
|
|
default:
|
|
rv = SECFailure;
|
|
fprintf(stderr, "Missing or invalid Subject Alternate Name type\n");
|
|
break;
|
|
}
|
|
if (rv == SECFailure) {
|
|
break;
|
|
}
|
|
|
|
if (prev) {
|
|
current->l.prev = prev;
|
|
prev->next = &(current->l);
|
|
} else {
|
|
nameList = current;
|
|
}
|
|
prev = &(current->l);
|
|
}
|
|
PORT_Free(names);
|
|
/* at this point nameList points to the head of a doubly linked,
|
|
* but not yet circular, list and current points to its tail. */
|
|
if (rv == SECSuccess && nameList) {
|
|
if (*existingListp != NULL) {
|
|
PRCList *existingprev;
|
|
/* add nameList to the end of the existing list */
|
|
existingprev = (*existingListp)->l.prev;
|
|
(*existingListp)->l.prev = &(current->l);
|
|
nameList->l.prev = existingprev;
|
|
existingprev->next = &(nameList->l);
|
|
current->l.next = &((*existingListp)->l);
|
|
} else {
|
|
/* make nameList circular and set it as the new existingList */
|
|
nameList->l.prev = prev;
|
|
current->l.next = &(nameList->l);
|
|
*existingListp = nameList;
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static SECStatus
|
|
AddEmailSubjectAlt(PLArenaPool *arena, CERTGeneralName **existingListp,
|
|
const char *emailAddrs)
|
|
{
|
|
return AddSubjectAltNames(arena, existingListp, emailAddrs,
|
|
certRFC822Name);
|
|
}
|
|
|
|
static SECStatus
|
|
AddDNSSubjectAlt(PLArenaPool *arena, CERTGeneralName **existingListp,
|
|
const char *dnsNames)
|
|
{
|
|
return AddSubjectAltNames(arena, existingListp, dnsNames, certDNSName);
|
|
}
|
|
|
|
static SECStatus
|
|
AddGeneralSubjectAlt(PLArenaPool *arena, CERTGeneralName **existingListp,
|
|
const char *altNames)
|
|
{
|
|
return AddSubjectAltNames(arena, existingListp, altNames, 0);
|
|
}
|
|
|
|
static SECStatus
|
|
AddBasicConstraint(void *extHandle)
|
|
{
|
|
CERTBasicConstraints basicConstraint;
|
|
SECStatus rv;
|
|
char buffer[10];
|
|
PRBool yesNoAns;
|
|
|
|
do {
|
|
basicConstraint.pathLenConstraint = CERT_UNLIMITED_PATH_CONSTRAINT;
|
|
basicConstraint.isCA = GetYesNo("Is this a CA certificate [y/N]?");
|
|
|
|
buffer[0] = '\0';
|
|
if (PrintChoicesAndGetAnswer("Enter the path length constraint, "
|
|
"enter to skip [<0 for unlimited path]:",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
if (PORT_Strlen(buffer) > 0)
|
|
basicConstraint.pathLenConstraint = PORT_Atoi(buffer);
|
|
|
|
yesNoAns = GetYesNo("Is this a critical extension [y/N]?");
|
|
|
|
rv = SECU_EncodeAndAddExtensionValue(NULL, extHandle,
|
|
&basicConstraint, yesNoAns, SEC_OID_X509_BASIC_CONSTRAINTS,
|
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodeBasicConstraintValue);
|
|
} while (0);
|
|
|
|
return (rv);
|
|
}
|
|
|
|
static SECStatus
|
|
AddNameConstraints(void *extHandle)
|
|
{
|
|
PLArenaPool *arena = NULL;
|
|
CERTNameConstraints *constraints = NULL;
|
|
|
|
CERTNameConstraint *current = NULL;
|
|
CERTNameConstraint *last_permited = NULL;
|
|
CERTNameConstraint *last_excluded = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
|
|
char buffer[512];
|
|
int intValue = 0;
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (arena) {
|
|
constraints = PORT_ArenaZNew(arena, CERTNameConstraints);
|
|
}
|
|
|
|
if (!arena || !constraints) {
|
|
SECU_PrintError(progName, "out of memory");
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return SECFailure;
|
|
}
|
|
|
|
constraints->permited = constraints->excluded = NULL;
|
|
|
|
do {
|
|
current = PORT_ArenaZNew(arena, CERTNameConstraint);
|
|
if (!current) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
(void)SEC_ASN1EncodeInteger(arena, ¤t->min, 0);
|
|
|
|
if (!GetGeneralName(arena, ¤t->name, PR_TRUE)) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
if (PrintChoicesAndGetAnswer("Type of Name Constraint?\n"
|
|
"\t1 - permitted\n\t2 - excluded\n\tAny"
|
|
"other number to finish\n\tChoice",
|
|
buffer, sizeof(buffer)) !=
|
|
SECSuccess) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
intValue = PORT_Atoi(buffer);
|
|
switch (intValue) {
|
|
case 1:
|
|
if (constraints->permited == NULL) {
|
|
constraints->permited = last_permited = current;
|
|
}
|
|
last_permited->l.next = &(current->l);
|
|
current->l.prev = &(last_permited->l);
|
|
last_permited = current;
|
|
break;
|
|
case 2:
|
|
if (constraints->excluded == NULL) {
|
|
constraints->excluded = last_excluded = current;
|
|
}
|
|
last_excluded->l.next = &(current->l);
|
|
current->l.prev = &(last_excluded->l);
|
|
last_excluded = current;
|
|
break;
|
|
}
|
|
|
|
PR_snprintf(buffer, sizeof(buffer), "Add another entry to the"
|
|
" Name Constraint Extension [y/N]");
|
|
|
|
if (GetYesNo(buffer) == 0) {
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
|
|
if (rv == SECSuccess) {
|
|
int oidIdent = SEC_OID_X509_NAME_CONSTRAINTS;
|
|
|
|
PRBool yesNoAns = GetYesNo("Is this a critical extension [y/N]?");
|
|
|
|
if (constraints->permited != NULL) {
|
|
last_permited->l.next = &(constraints->permited->l);
|
|
constraints->permited->l.prev = &(last_permited->l);
|
|
}
|
|
if (constraints->excluded != NULL) {
|
|
last_excluded->l.next = &(constraints->excluded->l);
|
|
constraints->excluded->l.prev = &(last_excluded->l);
|
|
}
|
|
|
|
rv = SECU_EncodeAndAddExtensionValue(arena, extHandle, constraints,
|
|
yesNoAns, oidIdent,
|
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodeNameConstraintsExtension);
|
|
}
|
|
if (arena)
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return (rv);
|
|
}
|
|
|
|
static SECStatus
|
|
AddAuthKeyID(void *extHandle)
|
|
{
|
|
CERTAuthKeyID *authKeyID = NULL;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
PRBool yesNoAns;
|
|
|
|
do {
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (!arena) {
|
|
SECU_PrintError(progName, "out of memory");
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
if (GetYesNo("Enter value for the authKeyID extension [y/N]?") == 0)
|
|
break;
|
|
|
|
authKeyID = PORT_ArenaZNew(arena, CERTAuthKeyID);
|
|
if (authKeyID == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
rv = GetString(arena, "Enter value for the key identifier fields,"
|
|
"enter to omit:",
|
|
&authKeyID->keyID);
|
|
if (rv != SECSuccess)
|
|
break;
|
|
|
|
SECU_SECItemHexStringToBinary(&authKeyID->keyID);
|
|
|
|
authKeyID->authCertIssuer = CreateGeneralName(arena);
|
|
if (authKeyID->authCertIssuer == NULL &&
|
|
SECFailure == PORT_GetError())
|
|
break;
|
|
|
|
rv = GetString(arena, "Enter value for the authCertSerial field, "
|
|
"enter to omit:",
|
|
&authKeyID->authCertSerialNumber);
|
|
|
|
yesNoAns = GetYesNo("Is this a critical extension [y/N]?");
|
|
|
|
rv = SECU_EncodeAndAddExtensionValue(arena, extHandle,
|
|
authKeyID, yesNoAns, SEC_OID_X509_AUTH_KEY_ID,
|
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodeAuthKeyID);
|
|
if (rv)
|
|
break;
|
|
|
|
} while (0);
|
|
if (arena)
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return (rv);
|
|
}
|
|
|
|
static SECStatus
|
|
AddSubjKeyID(void *extHandle)
|
|
{
|
|
SECItem keyID;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
PRBool yesNoAns;
|
|
|
|
do {
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (!arena) {
|
|
SECU_PrintError(progName, "out of memory");
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
printf("Adding Subject Key ID extension.\n");
|
|
|
|
rv = GetString(arena, "Enter value for the key identifier fields,"
|
|
"enter to omit:",
|
|
&keyID);
|
|
if (rv != SECSuccess)
|
|
break;
|
|
|
|
SECU_SECItemHexStringToBinary(&keyID);
|
|
|
|
yesNoAns = GetYesNo("Is this a critical extension [y/N]?");
|
|
|
|
rv = SECU_EncodeAndAddExtensionValue(arena, extHandle,
|
|
&keyID, yesNoAns, SEC_OID_X509_SUBJECT_KEY_ID,
|
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodeSubjectKeyID);
|
|
if (rv)
|
|
break;
|
|
|
|
} while (0);
|
|
if (arena)
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return (rv);
|
|
}
|
|
|
|
static SECStatus
|
|
AddCrlDistPoint(void *extHandle)
|
|
{
|
|
PLArenaPool *arena = NULL;
|
|
CERTCrlDistributionPoints *crlDistPoints = NULL;
|
|
CRLDistributionPoint *current;
|
|
SECStatus rv = SECSuccess;
|
|
int count = 0, intValue;
|
|
char buffer[512];
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (!arena)
|
|
return (SECFailure);
|
|
|
|
do {
|
|
current = NULL;
|
|
|
|
current = PORT_ArenaZNew(arena, CRLDistributionPoint);
|
|
if (current == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
/* Get the distributionPointName fields - this field is optional */
|
|
if (PrintChoicesAndGetAnswer(
|
|
"Enter the type of the distribution point name:\n"
|
|
"\t1 - Full Name\n\t2 - Relative Name\n\tAny other "
|
|
"number to finish\n\t\tChoice: ",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
intValue = PORT_Atoi(buffer);
|
|
switch (intValue) {
|
|
case generalName:
|
|
current->distPointType = intValue;
|
|
current->distPoint.fullName = CreateGeneralName(arena);
|
|
rv = PORT_GetError();
|
|
break;
|
|
|
|
case relativeDistinguishedName: {
|
|
CERTName *name;
|
|
|
|
current->distPointType = intValue;
|
|
puts("Enter the relative name: ");
|
|
fflush(stdout);
|
|
if (Gets_s(buffer, sizeof(buffer)) == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
/* For simplicity, use CERT_AsciiToName to converse from a string
|
|
to NAME, but we only interest in the first RDN */
|
|
name = CERT_AsciiToName(buffer);
|
|
if (!name) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
rv = CERT_CopyRDN(arena, ¤t->distPoint.relativeName,
|
|
name->rdns[0]);
|
|
CERT_DestroyName(name);
|
|
break;
|
|
}
|
|
}
|
|
if (rv != SECSuccess)
|
|
break;
|
|
|
|
/* Get the reason flags */
|
|
if (PrintChoicesAndGetAnswer(
|
|
"\nSelect one of the following for the reason flags\n"
|
|
"\t0 - unused\n\t1 - keyCompromise\n"
|
|
"\t2 - caCompromise\n\t3 - affiliationChanged\n"
|
|
"\t4 - superseded\n\t5 - cessationOfOperation\n"
|
|
"\t6 - certificateHold\n"
|
|
"\tAny other number to finish\t\tChoice: ",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
intValue = PORT_Atoi(buffer);
|
|
if (intValue == 0) {
|
|
/* Checking that zero value of variable 'value'
|
|
* corresponds to '0' input made by user */
|
|
char *chPtr = strchr(buffer, '0');
|
|
if (chPtr == NULL) {
|
|
intValue = -1;
|
|
}
|
|
}
|
|
if (intValue >= 0 && intValue < 8) {
|
|
current->reasons.data = PORT_ArenaAlloc(arena, sizeof(char));
|
|
if (current->reasons.data == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
*current->reasons.data = (char)(0x80 >> intValue);
|
|
current->reasons.len = 1;
|
|
}
|
|
puts("Enter value for the CRL Issuer name:\n");
|
|
current->crlIssuer = CreateGeneralName(arena);
|
|
if (current->crlIssuer == NULL && (rv = PORT_GetError()) == SECFailure)
|
|
break;
|
|
|
|
if (crlDistPoints == NULL) {
|
|
crlDistPoints = PORT_ArenaZNew(arena, CERTCrlDistributionPoints);
|
|
if (crlDistPoints == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
}
|
|
|
|
crlDistPoints->distPoints =
|
|
PORT_ArenaGrow(arena, crlDistPoints->distPoints,
|
|
sizeof(*crlDistPoints->distPoints) * count,
|
|
sizeof(*crlDistPoints->distPoints) * (count + 1));
|
|
if (crlDistPoints->distPoints == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
crlDistPoints->distPoints[count] = current;
|
|
++count;
|
|
if (GetYesNo("Enter another value for the CRLDistributionPoint "
|
|
"extension [y/N]?") == 0) {
|
|
/* Add null to the end to mark end of data */
|
|
crlDistPoints->distPoints =
|
|
PORT_ArenaGrow(arena, crlDistPoints->distPoints,
|
|
sizeof(*crlDistPoints->distPoints) * count,
|
|
sizeof(*crlDistPoints->distPoints) * (count + 1));
|
|
crlDistPoints->distPoints[count] = NULL;
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
|
|
if (rv == SECSuccess) {
|
|
PRBool yesNoAns = GetYesNo("Is this a critical extension [y/N]?");
|
|
|
|
rv = SECU_EncodeAndAddExtensionValue(arena, extHandle,
|
|
crlDistPoints, yesNoAns, SEC_OID_X509_CRL_DIST_POINTS,
|
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodeCRLDistributionPoints);
|
|
}
|
|
if (arena)
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return (rv);
|
|
}
|
|
|
|
static SECStatus
|
|
AddPolicyConstraints(void *extHandle)
|
|
{
|
|
CERTCertificatePolicyConstraints *policyConstr;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
SECItem *item, *dummy;
|
|
char buffer[512];
|
|
int value;
|
|
PRBool yesNoAns;
|
|
PRBool skipExt = PR_TRUE;
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (!arena) {
|
|
SECU_PrintError(progName, "out of memory");
|
|
return SECFailure;
|
|
}
|
|
|
|
policyConstr = PORT_ArenaZNew(arena, CERTCertificatePolicyConstraints);
|
|
if (policyConstr == NULL) {
|
|
SECU_PrintError(progName, "out of memory");
|
|
goto loser;
|
|
}
|
|
|
|
if (PrintChoicesAndGetAnswer("for requireExplicitPolicy enter the number "
|
|
"of certs in path\nbefore explicit policy is required\n"
|
|
"(press Enter to omit)",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
goto loser;
|
|
}
|
|
|
|
if (PORT_Strlen(buffer)) {
|
|
value = PORT_Atoi(buffer);
|
|
if (value < 0) {
|
|
goto loser;
|
|
}
|
|
item = &policyConstr->explicitPolicySkipCerts;
|
|
dummy = SEC_ASN1EncodeInteger(arena, item, value);
|
|
if (!dummy) {
|
|
goto loser;
|
|
}
|
|
skipExt = PR_FALSE;
|
|
}
|
|
|
|
if (PrintChoicesAndGetAnswer("for inihibitPolicyMapping enter "
|
|
"the number of certs in path\n"
|
|
"after which policy mapping is not allowed\n"
|
|
"(press Enter to omit)",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
goto loser;
|
|
}
|
|
|
|
if (PORT_Strlen(buffer)) {
|
|
value = PORT_Atoi(buffer);
|
|
if (value < 0) {
|
|
goto loser;
|
|
}
|
|
item = &policyConstr->inhibitMappingSkipCerts;
|
|
dummy = SEC_ASN1EncodeInteger(arena, item, value);
|
|
if (!dummy) {
|
|
goto loser;
|
|
}
|
|
skipExt = PR_FALSE;
|
|
}
|
|
|
|
if (!skipExt) {
|
|
yesNoAns = GetYesNo("Is this a critical extension [y/N]?");
|
|
|
|
rv = SECU_EncodeAndAddExtensionValue(arena, extHandle, policyConstr,
|
|
yesNoAns, SEC_OID_X509_POLICY_CONSTRAINTS,
|
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodePolicyConstraintsExtension);
|
|
} else {
|
|
fprintf(stdout, "Policy Constraint extensions must contain "
|
|
"at least one policy field\n");
|
|
rv = SECFailure;
|
|
}
|
|
|
|
loser:
|
|
if (arena) {
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
static SECStatus
|
|
AddInhibitAnyPolicy(void *extHandle)
|
|
{
|
|
CERTCertificateInhibitAny certInhibitAny;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
SECItem *item, *dummy;
|
|
char buffer[10];
|
|
int value;
|
|
PRBool yesNoAns;
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (!arena) {
|
|
SECU_PrintError(progName, "out of memory");
|
|
return SECFailure;
|
|
}
|
|
|
|
if (PrintChoicesAndGetAnswer("Enter the number of certs in the path "
|
|
"permitted to use anyPolicy.\n"
|
|
"(press Enter for 0)",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
goto loser;
|
|
}
|
|
|
|
item = &certInhibitAny.inhibitAnySkipCerts;
|
|
value = PORT_Atoi(buffer);
|
|
if (value < 0) {
|
|
goto loser;
|
|
}
|
|
dummy = SEC_ASN1EncodeInteger(arena, item, value);
|
|
if (!dummy) {
|
|
goto loser;
|
|
}
|
|
|
|
yesNoAns = GetYesNo("Is this a critical extension [y/N]?");
|
|
|
|
rv = SECU_EncodeAndAddExtensionValue(arena, extHandle, &certInhibitAny,
|
|
yesNoAns, SEC_OID_X509_INHIBIT_ANY_POLICY,
|
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodeInhibitAnyExtension);
|
|
loser:
|
|
if (arena) {
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
static SECStatus
|
|
AddPolicyMappings(void *extHandle)
|
|
{
|
|
CERTPolicyMap **policyMapArr = NULL;
|
|
CERTPolicyMap *current;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
int count = 0;
|
|
char buffer[512];
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (!arena) {
|
|
SECU_PrintError(progName, "out of memory");
|
|
return SECFailure;
|
|
}
|
|
|
|
do {
|
|
if (PrintChoicesAndGetAnswer("Enter an Object Identifier (dotted "
|
|
"decimal format) for Issuer Domain Policy",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
current = PORT_ArenaZNew(arena, CERTPolicyMap);
|
|
if (current == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
rv = SEC_StringToOID(arena, ¤t->issuerDomainPolicy, buffer, 0);
|
|
if (rv == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
if (PrintChoicesAndGetAnswer("Enter an Object Identifier for "
|
|
"Subject Domain Policy",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
rv = SEC_StringToOID(arena, ¤t->subjectDomainPolicy, buffer, 0);
|
|
if (rv == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
if (policyMapArr == NULL) {
|
|
policyMapArr = PORT_ArenaZNew(arena, CERTPolicyMap *);
|
|
if (policyMapArr == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
}
|
|
|
|
policyMapArr = PORT_ArenaGrow(arena, policyMapArr,
|
|
sizeof(current) * count,
|
|
sizeof(current) * (count + 1));
|
|
if (policyMapArr == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
policyMapArr[count] = current;
|
|
++count;
|
|
|
|
if (!GetYesNo("Enter another Policy Mapping [y/N]")) {
|
|
/* Add null to the end to mark end of data */
|
|
policyMapArr = PORT_ArenaGrow(arena, policyMapArr,
|
|
sizeof(current) * count,
|
|
sizeof(current) * (count + 1));
|
|
if (policyMapArr == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
policyMapArr[count] = NULL;
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
|
|
if (rv == SECSuccess) {
|
|
CERTCertificatePolicyMappings mappings;
|
|
PRBool yesNoAns = GetYesNo("Is this a critical extension [y/N]?");
|
|
|
|
mappings.arena = arena;
|
|
mappings.policyMaps = policyMapArr;
|
|
rv = SECU_EncodeAndAddExtensionValue(arena, extHandle, &mappings,
|
|
yesNoAns, SEC_OID_X509_POLICY_MAPPINGS,
|
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodePolicyMappingExtension);
|
|
}
|
|
if (arena)
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return (rv);
|
|
}
|
|
|
|
enum PoliciQualifierEnum {
|
|
cpsPointer = 1,
|
|
userNotice = 2
|
|
};
|
|
|
|
static CERTPolicyQualifier **
|
|
RequestPolicyQualifiers(PLArenaPool *arena, SECItem *policyID)
|
|
{
|
|
CERTPolicyQualifier **policyQualifArr = NULL;
|
|
CERTPolicyQualifier *current;
|
|
SECStatus rv = SECSuccess;
|
|
int count = 0;
|
|
char buffer[512];
|
|
void *mark;
|
|
SECOidData *oid = NULL;
|
|
int intValue = 0;
|
|
int inCount = 0;
|
|
|
|
PORT_Assert(arena);
|
|
mark = PORT_ArenaMark(arena);
|
|
do {
|
|
current = PORT_ArenaZNew(arena, CERTPolicyQualifier);
|
|
if (current == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
/* Get the accessMethod fields */
|
|
SECU_PrintObjectID(stdout, policyID,
|
|
"Choose the type of qualifier for policy", 0);
|
|
|
|
if (PrintChoicesAndGetAnswer(
|
|
"\t1 - CPS Pointer qualifier\n"
|
|
"\t2 - User notice qualifier\n"
|
|
"\tAny other number to finish\n"
|
|
"\t\tChoice: ",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
intValue = PORT_Atoi(buffer);
|
|
switch (intValue) {
|
|
case cpsPointer: {
|
|
SECItem input;
|
|
|
|
oid = SECOID_FindOIDByTag(SEC_OID_PKIX_CPS_POINTER_QUALIFIER);
|
|
if (PrintChoicesAndGetAnswer("Enter CPS pointer URI: ",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
input.len = PORT_Strlen(buffer);
|
|
input.data = (void *)PORT_ArenaStrdup(arena, buffer);
|
|
if (input.data == NULL ||
|
|
SEC_ASN1EncodeItem(arena, ¤t->qualifierValue, &input,
|
|
SEC_ASN1_GET(SEC_IA5StringTemplate)) == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
break;
|
|
}
|
|
case userNotice: {
|
|
SECItem **noticeNumArr;
|
|
CERTUserNotice *notice = PORT_ArenaZNew(arena, CERTUserNotice);
|
|
if (!notice) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
oid = SECOID_FindOIDByTag(SEC_OID_PKIX_USER_NOTICE_QUALIFIER);
|
|
|
|
if (GetYesNo("\t add a User Notice reference? [y/N]")) {
|
|
|
|
if (PrintChoicesAndGetAnswer("Enter user organization string: ",
|
|
buffer, sizeof(buffer)) ==
|
|
SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
notice->noticeReference.organization.type = siAsciiString;
|
|
notice->noticeReference.organization.len =
|
|
PORT_Strlen(buffer);
|
|
notice->noticeReference.organization.data =
|
|
(void *)PORT_ArenaStrdup(arena, buffer);
|
|
|
|
noticeNumArr = PORT_ArenaZNewArray(arena, SECItem *, 2);
|
|
if (!noticeNumArr) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
do {
|
|
SECItem *noticeNum;
|
|
|
|
noticeNum = PORT_ArenaZNew(arena, SECItem);
|
|
|
|
if (PrintChoicesAndGetAnswer(
|
|
"Enter User Notice reference number "
|
|
"(or -1 to quit): ",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
intValue = PORT_Atoi(buffer);
|
|
if (noticeNum == NULL) {
|
|
if (intValue < 0) {
|
|
fprintf(stdout, "a noticeReference must have at "
|
|
"least one reference number\n");
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
} else {
|
|
if (intValue >= 0) {
|
|
noticeNumArr = PORT_ArenaGrow(arena, noticeNumArr,
|
|
sizeof(current) *
|
|
inCount,
|
|
sizeof(current) *
|
|
(inCount + 1));
|
|
if (noticeNumArr == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (!SEC_ASN1EncodeInteger(arena, noticeNum, intValue)) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
noticeNumArr[inCount++] = noticeNum;
|
|
noticeNumArr[inCount] = NULL;
|
|
|
|
} while (1);
|
|
if (rv == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
notice->noticeReference.noticeNumbers = noticeNumArr;
|
|
rv = CERT_EncodeNoticeReference(arena, ¬ice->noticeReference,
|
|
¬ice->derNoticeReference);
|
|
if (rv == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
}
|
|
if (GetYesNo("\t EnterUser Notice explicit text? [y/N]")) {
|
|
/* Getting only 200 bytes - RFC limitation */
|
|
if (PrintChoicesAndGetAnswer(
|
|
"\t", buffer, 200) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
notice->displayText.type = siAsciiString;
|
|
notice->displayText.len = PORT_Strlen(buffer);
|
|
notice->displayText.data =
|
|
(void *)PORT_ArenaStrdup(arena, buffer);
|
|
if (notice->displayText.data == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
}
|
|
|
|
rv = CERT_EncodeUserNotice(arena, notice, ¤t->qualifierValue);
|
|
if (rv == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
if (rv == SECFailure || oid == NULL ||
|
|
SECITEM_CopyItem(arena, ¤t->qualifierID, &oid->oid) ==
|
|
SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
if (!policyQualifArr) {
|
|
policyQualifArr = PORT_ArenaZNew(arena, CERTPolicyQualifier *);
|
|
} else {
|
|
policyQualifArr = PORT_ArenaGrow(arena, policyQualifArr,
|
|
sizeof(current) * count,
|
|
sizeof(current) * (count + 1));
|
|
}
|
|
if (policyQualifArr == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
policyQualifArr[count] = current;
|
|
++count;
|
|
|
|
if (!GetYesNo("Enter another policy qualifier [y/N]")) {
|
|
/* Add null to the end to mark end of data */
|
|
policyQualifArr = PORT_ArenaGrow(arena, policyQualifArr,
|
|
sizeof(current) * count,
|
|
sizeof(current) * (count + 1));
|
|
if (policyQualifArr == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
policyQualifArr[count] = NULL;
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
|
|
if (rv != SECSuccess) {
|
|
PORT_ArenaRelease(arena, mark);
|
|
policyQualifArr = NULL;
|
|
}
|
|
return (policyQualifArr);
|
|
}
|
|
|
|
static SECStatus
|
|
AddCertPolicies(void *extHandle)
|
|
{
|
|
CERTPolicyInfo **certPoliciesArr = NULL;
|
|
CERTPolicyInfo *current;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
int count = 0;
|
|
char buffer[512];
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (!arena) {
|
|
SECU_PrintError(progName, "out of memory");
|
|
return SECFailure;
|
|
}
|
|
|
|
do {
|
|
current = PORT_ArenaZNew(arena, CERTPolicyInfo);
|
|
if (current == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
if (PrintChoicesAndGetAnswer("Enter a CertPolicy Object Identifier "
|
|
"(dotted decimal format)\n"
|
|
"or \"any\" for AnyPolicy:",
|
|
buffer, sizeof(buffer)) == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
if (strncmp(buffer, "any", 3) == 0) {
|
|
/* use string version of X509_CERTIFICATE_POLICIES.anyPolicy */
|
|
strcpy(buffer, "OID.2.5.29.32.0");
|
|
}
|
|
rv = SEC_StringToOID(arena, ¤t->policyID, buffer, 0);
|
|
|
|
if (rv == SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
current->policyQualifiers =
|
|
RequestPolicyQualifiers(arena, ¤t->policyID);
|
|
|
|
if (!certPoliciesArr) {
|
|
certPoliciesArr = PORT_ArenaZNew(arena, CERTPolicyInfo *);
|
|
} else {
|
|
certPoliciesArr = PORT_ArenaGrow(arena, certPoliciesArr,
|
|
sizeof(current) * count,
|
|
sizeof(current) * (count + 1));
|
|
}
|
|
if (certPoliciesArr == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
certPoliciesArr[count] = current;
|
|
++count;
|
|
|
|
if (!GetYesNo("Enter another PolicyInformation field [y/N]?")) {
|
|
/* Add null to the end to mark end of data */
|
|
certPoliciesArr = PORT_ArenaGrow(arena, certPoliciesArr,
|
|
sizeof(current) * count,
|
|
sizeof(current) * (count + 1));
|
|
if (certPoliciesArr == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
certPoliciesArr[count] = NULL;
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
|
|
if (rv == SECSuccess) {
|
|
CERTCertificatePolicies policies;
|
|
PRBool yesNoAns = GetYesNo("Is this a critical extension [y/N]?");
|
|
|
|
policies.arena = arena;
|
|
policies.policyInfos = certPoliciesArr;
|
|
|
|
rv = SECU_EncodeAndAddExtensionValue(arena, extHandle, &policies,
|
|
yesNoAns, SEC_OID_X509_CERTIFICATE_POLICIES,
|
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodeCertPoliciesExtension);
|
|
}
|
|
if (arena)
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return (rv);
|
|
}
|
|
|
|
enum AuthInfoAccessTypesEnum {
|
|
caIssuers = 1,
|
|
ocsp = 2
|
|
};
|
|
|
|
enum SubjInfoAccessTypesEnum {
|
|
caRepository = 1,
|
|
timeStamping = 2
|
|
};
|
|
|
|
/* Encode and add an AIA or SIA extension */
|
|
static SECStatus
|
|
AddInfoAccess(void *extHandle, PRBool addSIAExt, PRBool isCACert)
|
|
{
|
|
CERTAuthInfoAccess **infoAccArr = NULL;
|
|
CERTAuthInfoAccess *current;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
int count = 0;
|
|
char buffer[512];
|
|
SECOidData *oid = NULL;
|
|
int intValue = 0;
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (!arena) {
|
|
SECU_PrintError(progName, "out of memory");
|
|
return SECFailure;
|
|
}
|
|
|
|
do {
|
|
current = NULL;
|
|
current = PORT_ArenaZNew(arena, CERTAuthInfoAccess);
|
|
if (current == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
/* Get the accessMethod fields */
|
|
if (addSIAExt) {
|
|
if (isCACert) {
|
|
puts("Adding \"CA Repository\" access method type for "
|
|
"Subject Information Access extension:\n");
|
|
intValue = caRepository;
|
|
} else {
|
|
puts("Adding \"Time Stamping Services\" access method type for "
|
|
"Subject Information Access extension:\n");
|
|
intValue = timeStamping;
|
|
}
|
|
} else {
|
|
if (PrintChoicesAndGetAnswer("Enter access method type "
|
|
"for Authority Information Access extension:\n"
|
|
"\t1 - CA Issuers\n\t2 - OCSP\n\tAny"
|
|
"other number to finish\n\tChoice",
|
|
buffer, sizeof(buffer)) !=
|
|
SECSuccess) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
intValue = PORT_Atoi(buffer);
|
|
}
|
|
if (addSIAExt) {
|
|
switch (intValue) {
|
|
case caRepository:
|
|
oid = SECOID_FindOIDByTag(SEC_OID_PKIX_CA_REPOSITORY);
|
|
break;
|
|
|
|
case timeStamping:
|
|
oid = SECOID_FindOIDByTag(SEC_OID_PKIX_TIMESTAMPING);
|
|
break;
|
|
}
|
|
} else {
|
|
switch (intValue) {
|
|
case caIssuers:
|
|
oid = SECOID_FindOIDByTag(SEC_OID_PKIX_CA_ISSUERS);
|
|
break;
|
|
|
|
case ocsp:
|
|
oid = SECOID_FindOIDByTag(SEC_OID_PKIX_OCSP);
|
|
break;
|
|
}
|
|
}
|
|
if (oid == NULL ||
|
|
SECITEM_CopyItem(arena, ¤t->method, &oid->oid) ==
|
|
SECFailure) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
current->location = CreateGeneralName(arena);
|
|
if (!current->location) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
if (infoAccArr == NULL) {
|
|
infoAccArr = PORT_ArenaZNew(arena, CERTAuthInfoAccess *);
|
|
} else {
|
|
infoAccArr = PORT_ArenaGrow(arena, infoAccArr,
|
|
sizeof(current) * count,
|
|
sizeof(current) * (count + 1));
|
|
}
|
|
if (infoAccArr == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
|
|
infoAccArr[count] = current;
|
|
++count;
|
|
|
|
PR_snprintf(buffer, sizeof(buffer), "Add another location to the %s"
|
|
" Information Access extension [y/N]",
|
|
(addSIAExt) ? "Subject" : "Authority");
|
|
|
|
if (GetYesNo(buffer) == 0) {
|
|
/* Add null to the end to mark end of data */
|
|
infoAccArr = PORT_ArenaGrow(arena, infoAccArr,
|
|
sizeof(current) * count,
|
|
sizeof(current) * (count + 1));
|
|
if (infoAccArr == NULL) {
|
|
GEN_BREAK(SECFailure);
|
|
}
|
|
infoAccArr[count] = NULL;
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
|
|
if (rv == SECSuccess) {
|
|
int oidIdent = SEC_OID_X509_AUTH_INFO_ACCESS;
|
|
|
|
PRBool yesNoAns = GetYesNo("Is this a critical extension [y/N]?");
|
|
|
|
if (addSIAExt) {
|
|
oidIdent = SEC_OID_X509_SUBJECT_INFO_ACCESS;
|
|
}
|
|
rv = SECU_EncodeAndAddExtensionValue(arena, extHandle, infoAccArr,
|
|
yesNoAns, oidIdent,
|
|
(EXTEN_EXT_VALUE_ENCODER)CERT_EncodeInfoAccessExtension);
|
|
}
|
|
if (arena)
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return (rv);
|
|
}
|
|
|
|
/* Example of valid input:
|
|
* 1.2.3.4:critical:/tmp/abc,5.6.7.8:not-critical:/tmp/xyz
|
|
*/
|
|
static SECStatus
|
|
parseNextGenericExt(const char *nextExtension, const char **oid, int *oidLen,
|
|
const char **crit, int *critLen,
|
|
const char **filename, int *filenameLen,
|
|
const char **next)
|
|
{
|
|
const char *nextColon;
|
|
const char *nextComma;
|
|
const char *iter = nextExtension;
|
|
|
|
if (!iter || !*iter)
|
|
return SECFailure;
|
|
|
|
/* Require colons at earlier positions than nextComma (or end of string ) */
|
|
nextComma = strchr(iter, ',');
|
|
|
|
*oid = iter;
|
|
nextColon = strchr(iter, ':');
|
|
if (!nextColon || (nextComma && nextColon > nextComma))
|
|
return SECFailure;
|
|
*oidLen = (nextColon - *oid);
|
|
|
|
if (!*oidLen)
|
|
return SECFailure;
|
|
|
|
iter = nextColon;
|
|
++iter;
|
|
|
|
*crit = iter;
|
|
nextColon = strchr(iter, ':');
|
|
if (!nextColon || (nextComma && nextColon > nextComma))
|
|
return SECFailure;
|
|
*critLen = (nextColon - *crit);
|
|
|
|
if (!*critLen)
|
|
return SECFailure;
|
|
|
|
iter = nextColon;
|
|
++iter;
|
|
|
|
*filename = iter;
|
|
if (nextComma) {
|
|
*filenameLen = (nextComma - *filename);
|
|
iter = nextComma;
|
|
++iter;
|
|
*next = iter;
|
|
} else {
|
|
*filenameLen = strlen(*filename);
|
|
*next = NULL;
|
|
}
|
|
|
|
if (!*filenameLen)
|
|
return SECFailure;
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
AddExtensions(void *extHandle, const char *emailAddrs, const char *dnsNames,
|
|
certutilExtnList extList, const char *extGeneric)
|
|
{
|
|
SECStatus rv = SECSuccess;
|
|
char *errstring = NULL;
|
|
const char *nextExtension = NULL;
|
|
|
|
do {
|
|
/* Add key usage extension */
|
|
if (extList[ext_keyUsage].activated) {
|
|
rv = AddKeyUsage(extHandle, extList[ext_keyUsage].arg);
|
|
if (rv) {
|
|
errstring = "KeyUsage";
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Add extended key usage extension */
|
|
if (extList[ext_extKeyUsage].activated) {
|
|
rv = AddExtKeyUsage(extHandle, extList[ext_extKeyUsage].arg);
|
|
if (rv) {
|
|
errstring = "ExtendedKeyUsage";
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Add basic constraint extension */
|
|
if (extList[ext_basicConstraint].activated) {
|
|
rv = AddBasicConstraint(extHandle);
|
|
if (rv) {
|
|
errstring = "BasicConstraint";
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Add name constraints extension */
|
|
if (extList[ext_nameConstraints].activated) {
|
|
rv = AddNameConstraints(extHandle);
|
|
if (rv) {
|
|
errstring = "NameConstraints";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (extList[ext_authorityKeyID].activated) {
|
|
rv = AddAuthKeyID(extHandle);
|
|
if (rv) {
|
|
errstring = "AuthorityKeyID";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (extList[ext_subjectKeyID].activated) {
|
|
rv = AddSubjKeyID(extHandle);
|
|
if (rv) {
|
|
errstring = "SubjectKeyID";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (extList[ext_CRLDistPts].activated) {
|
|
rv = AddCrlDistPoint(extHandle);
|
|
if (rv) {
|
|
errstring = "CRLDistPoints";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (extList[ext_NSCertType].activated) {
|
|
rv = AddNscpCertType(extHandle, extList[ext_NSCertType].arg);
|
|
if (rv) {
|
|
errstring = "NSCertType";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (extList[ext_authInfoAcc].activated ||
|
|
extList[ext_subjInfoAcc].activated) {
|
|
rv = AddInfoAccess(extHandle, extList[ext_subjInfoAcc].activated,
|
|
extList[ext_basicConstraint].activated);
|
|
if (rv) {
|
|
errstring = "InformationAccess";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (extList[ext_certPolicies].activated) {
|
|
rv = AddCertPolicies(extHandle);
|
|
if (rv) {
|
|
errstring = "Policies";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (extList[ext_policyMappings].activated) {
|
|
rv = AddPolicyMappings(extHandle);
|
|
if (rv) {
|
|
errstring = "PolicyMappings";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (extList[ext_policyConstr].activated) {
|
|
rv = AddPolicyConstraints(extHandle);
|
|
if (rv) {
|
|
errstring = "PolicyConstraints";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (extList[ext_inhibitAnyPolicy].activated) {
|
|
rv = AddInhibitAnyPolicy(extHandle);
|
|
if (rv) {
|
|
errstring = "InhibitAnyPolicy";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (emailAddrs || dnsNames || extList[ext_subjectAltName].activated) {
|
|
PLArenaPool *arena;
|
|
CERTGeneralName *namelist = NULL;
|
|
SECItem item = { 0, NULL, 0 };
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
|
|
rv = SECSuccess;
|
|
|
|
if (emailAddrs) {
|
|
rv |= AddEmailSubjectAlt(arena, &namelist, emailAddrs);
|
|
}
|
|
|
|
if (dnsNames) {
|
|
rv |= AddDNSSubjectAlt(arena, &namelist, dnsNames);
|
|
}
|
|
|
|
if (extList[ext_subjectAltName].activated) {
|
|
rv |= AddGeneralSubjectAlt(arena, &namelist,
|
|
extList[ext_subjectAltName].arg);
|
|
}
|
|
|
|
if (rv == SECSuccess) {
|
|
rv = CERT_EncodeAltNameExtension(arena, namelist, &item);
|
|
if (rv == SECSuccess) {
|
|
rv = CERT_AddExtension(extHandle,
|
|
SEC_OID_X509_SUBJECT_ALT_NAME,
|
|
&item, PR_FALSE, PR_TRUE);
|
|
}
|
|
}
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
if (rv) {
|
|
errstring = "SubjectAltName";
|
|
break;
|
|
}
|
|
}
|
|
} while (0);
|
|
|
|
if (rv != SECSuccess) {
|
|
SECU_PrintError(progName, "Problem creating %s extension", errstring);
|
|
}
|
|
|
|
nextExtension = extGeneric;
|
|
while (nextExtension && *nextExtension) {
|
|
SECItem oid_item, value;
|
|
PRBool isCritical;
|
|
const char *oid, *crit, *filename, *next;
|
|
int oidLen, critLen, filenameLen;
|
|
PRFileDesc *inFile = NULL;
|
|
char *zeroTerminatedFilename = NULL;
|
|
|
|
rv = parseNextGenericExt(nextExtension, &oid, &oidLen, &crit, &critLen,
|
|
&filename, &filenameLen, &next);
|
|
if (rv != SECSuccess) {
|
|
SECU_PrintError(progName,
|
|
"error parsing generic extension parameter %s",
|
|
nextExtension);
|
|
break;
|
|
}
|
|
oid_item.data = NULL;
|
|
oid_item.len = 0;
|
|
rv = GetOidFromString(NULL, &oid_item, oid, oidLen);
|
|
if (rv != SECSuccess) {
|
|
SECU_PrintError(progName, "malformed extension OID %s", nextExtension);
|
|
break;
|
|
}
|
|
if (!strncmp("critical", crit, critLen)) {
|
|
isCritical = PR_TRUE;
|
|
} else if (!strncmp("not-critical", crit, critLen)) {
|
|
isCritical = PR_FALSE;
|
|
} else {
|
|
rv = SECFailure;
|
|
SECU_PrintError(progName, "expected 'critical' or 'not-critical'");
|
|
break;
|
|
}
|
|
zeroTerminatedFilename = PL_strndup(filename, filenameLen);
|
|
if (!zeroTerminatedFilename) {
|
|
rv = SECFailure;
|
|
SECU_PrintError(progName, "out of memory");
|
|
break;
|
|
}
|
|
rv = SECFailure;
|
|
inFile = PR_Open(zeroTerminatedFilename, PR_RDONLY, 0);
|
|
if (inFile) {
|
|
rv = SECU_ReadDERFromFile(&value, inFile, PR_FALSE, PR_FALSE);
|
|
PR_Close(inFile);
|
|
inFile = NULL;
|
|
}
|
|
if (rv != SECSuccess) {
|
|
SECU_PrintError(progName, "unable to read file %s",
|
|
zeroTerminatedFilename);
|
|
}
|
|
PL_strfree(zeroTerminatedFilename);
|
|
if (rv != SECSuccess) {
|
|
break;
|
|
}
|
|
rv = CERT_AddExtensionByOID(extHandle, &oid_item, &value, isCritical,
|
|
PR_FALSE /*copyData*/);
|
|
if (rv != SECSuccess) {
|
|
SECITEM_FreeItem(&oid_item, PR_FALSE);
|
|
SECITEM_FreeItem(&value, PR_FALSE);
|
|
SECU_PrintError(progName, "failed to add extension %s", nextExtension);
|
|
break;
|
|
}
|
|
nextExtension = next;
|
|
}
|
|
|
|
return rv;
|
|
}
|