diff options
Diffstat (limited to 'security/nss/cmd/certutil/certext.c')
-rw-r--r-- | security/nss/cmd/certutil/certext.c | 2218 |
1 files changed, 2218 insertions, 0 deletions
diff --git a/security/nss/cmd/certutil/certext.c b/security/nss/cmd/certutil/certext.c new file mode 100644 index 000000000..b080f06f9 --- /dev/null +++ b/security/nss/cmd/certutil/certext.c @@ -0,0 +1,2218 @@ +/* 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; + int keyLen = 0; + int arrIndex = 0; + + if (!valueArray || !value || !nextPos || !critical) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + thisPos = *nextPos; + 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(PLArenaPool *arena, 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(arena, 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); + } + } + + if (crlDistPoints->distPoints) { + crlDistPoints->distPoints = + PORT_ArenaGrow(arena, crlDistPoints->distPoints, + sizeof(*crlDistPoints->distPoints) * count, + sizeof(*crlDistPoints->distPoints) * (count + 1)); + } else { + crlDistPoints->distPoints = + PORT_ArenaZAlloc(arena, 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) +{ + PLArenaPool *arena; + SECStatus rv = SECSuccess; + char *errstring = NULL; + const char *nextExtension = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + return SECFailure; + } + + 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(arena, 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) { + CERTGeneralName *namelist = NULL; + SECItem item = { 0, NULL, 0 }; + + 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); + } + } + if (rv) { + errstring = "SubjectAltName"; + break; + } + } + } while (0); + + PORT_FreeArena(arena, PR_FALSE); + + 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_TRUE /*copyData*/); + SECITEM_FreeItem(&value, PR_FALSE); + SECITEM_FreeItem(&oid_item, PR_FALSE); + if (rv != SECSuccess) { + SECU_PrintError(progName, "failed to add extension %s", nextExtension); + break; + } + nextExtension = next; + } + + return rv; +} |