diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
commit | 9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /security/nss/cmd/certutil | |
parent | Initial commit. (diff) | |
download | thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/cmd/certutil')
-rw-r--r-- | security/nss/cmd/certutil/Makefile | 48 | ||||
-rw-r--r-- | security/nss/cmd/certutil/certext.c | 2266 | ||||
-rw-r--r-- | security/nss/cmd/certutil/certutil.c | 4038 | ||||
-rw-r--r-- | security/nss/cmd/certutil/certutil.gyp | 32 | ||||
-rw-r--r-- | security/nss/cmd/certutil/certutil.h | 57 | ||||
-rw-r--r-- | security/nss/cmd/certutil/keystuff.c | 595 | ||||
-rw-r--r-- | security/nss/cmd/certutil/manifest.mn | 25 |
7 files changed, 7061 insertions, 0 deletions
diff --git a/security/nss/cmd/certutil/Makefile b/security/nss/cmd/certutil/Makefile new file mode 100644 index 0000000000..74ae200208 --- /dev/null +++ b/security/nss/cmd/certutil/Makefile @@ -0,0 +1,48 @@ +#! gmake +# +# 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/. + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include ../platlibs.mk + + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + +include ../platrules.mk + diff --git a/security/nss/cmd/certutil/certext.c b/security/nss/cmd/certutil/certext.c new file mode 100644 index 0000000000..501608c850 --- /dev/null +++ b/security/nss/cmd/certutil/certext.c @@ -0,0 +1,2266 @@ +/* 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", + "x509Any", + "ipsecIKE", + "ipsecIKEEnd", + "ipsecIKEIntermediate", + "ipsecEnd", + "ipsecTunnel", + "ipsecUser", + 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) { + /* + * none of the 'new' extended key usage options work with the prompted menu. This is so + * old scripts can continue to work. + */ + 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; + /* + * These new usages can only be added explicitly by the userSuppliedValues. This allows old + * scripts which used '>7' as an exit value to continue to work. + */ + case 8: + if (!userSuppliedValue) + goto endloop; + rv = AddOidToSequence(os, SEC_OID_X509_ANY_EXT_KEY_USAGE); + break; + case 9: + if (!userSuppliedValue) + goto endloop; + rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_IPSEC_IKE); + break; + case 10: + if (!userSuppliedValue) + goto endloop; + rv = AddOidToSequence(os, SEC_OID_IPSEC_IKE_END); + break; + case 11: + if (!userSuppliedValue) + goto endloop; + rv = AddOidToSequence(os, SEC_OID_IPSEC_IKE_INTERMEDIATE); + break; + case 12: + if (!userSuppliedValue) + goto endloop; + rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_IPSEC_END); + break; + case 13: + if (!userSuppliedValue) + goto endloop; + rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_IPSEC_TUNNEL); + break; + case 14: + if (!userSuppliedValue) + goto endloop; + rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_IPSEC_USER); + 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); + } + + 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; +} diff --git a/security/nss/cmd/certutil/certutil.c b/security/nss/cmd/certutil/certutil.c new file mode 100644 index 0000000000..07b1ea51bd --- /dev/null +++ b/security/nss/cmd/certutil/certutil.c @@ -0,0 +1,4038 @@ +/* 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/. */ + +/* +** certutil.c +** +** utility for managing certificates and the cert database +** +*/ +#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 "nspr.h" +#include "prtypes.h" +#include "prtime.h" +#include "prlong.h" + +#include "pk11func.h" +#include "secasn1.h" +#include "cert.h" +#include "cryptohi.h" +#include "secoid.h" +#include "certdb.h" +#include "nss.h" +#include "certutil.h" +#include "basicutil.h" +#include "ssl.h" + +#define MIN_KEY_BITS 512 +/* MAX_KEY_BITS should agree with RSA_MAX_MODULUS_BITS in freebl */ +#define MAX_KEY_BITS 8192 +#define DEFAULT_KEY_BITS 2048 + +#define GEN_BREAK(e) \ + rv = e; \ + break; + +char *progName; + +static SECStatus +ChangeCertTrust(CERTCertDBHandle *handle, CERTCertificate *cert, + CERTCertTrust *trust, PK11SlotInfo *slot, void *pwdata) +{ + SECStatus rv; + + rv = CERT_ChangeCertTrust(handle, cert, trust); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { + rv = PK11_Authenticate(slot, PR_TRUE, pwdata); + if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { + PK11SlotInfo *internalslot; + internalslot = PK11_GetInternalKeySlot(); + rv = PK11_Authenticate(internalslot, PR_TRUE, pwdata); + if (rv != SECSuccess) { + SECU_PrintError(progName, + "could not authenticate to token %s.", + PK11_GetTokenName(internalslot)); + PK11_FreeSlot(internalslot); + return SECFailure; + } + PK11_FreeSlot(internalslot); + } + rv = CERT_ChangeCertTrust(handle, cert, trust); + } + } + return rv; +} + +static CERTCertificateRequest * +GetCertRequest(const SECItem *reqDER, void *pwarg) +{ + CERTCertificateRequest *certReq = NULL; + CERTSignedData signedData; + PLArenaPool *arena = NULL; + SECStatus rv; + + do { + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + GEN_BREAK(SECFailure); + } + + certReq = (CERTCertificateRequest *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificateRequest)); + if (!certReq) { + GEN_BREAK(SECFailure); + } + certReq->arena = arena; + + /* Since cert request is a signed data, must decode to get the inner + data + */ + PORT_Memset(&signedData, 0, sizeof(signedData)); + rv = SEC_ASN1DecodeItem(arena, &signedData, + SEC_ASN1_GET(CERT_SignedDataTemplate), reqDER); + if (rv) { + break; + } + rv = SEC_ASN1DecodeItem(arena, certReq, + SEC_ASN1_GET(CERT_CertificateRequestTemplate), &signedData.data); + if (rv) { + break; + } + rv = CERT_VerifySignedDataWithPublicKeyInfo(&signedData, + &certReq->subjectPublicKeyInfo, pwarg); + } while (0); + + if (rv) { + SECU_PrintError(progName, "bad certificate request\n"); + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + certReq = NULL; + } + + return certReq; +} + +static SECStatus +AddCert(PK11SlotInfo *slot, CERTCertDBHandle *handle, char *name, char *trusts, + const SECItem *certDER, PRBool emailcert, void *pwdata) +{ + CERTCertTrust *trust = NULL; + CERTCertificate *cert = NULL; + SECStatus rv; + + do { + /* Read in an ASCII cert and return a CERTCertificate */ + cert = CERT_DecodeCertFromPackage((char *)certDER->data, certDER->len); + if (!cert) { + SECU_PrintError(progName, "could not decode certificate"); + GEN_BREAK(SECFailure); + } + + /* Create a cert trust */ + trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust)); + if (!trust) { + SECU_PrintError(progName, "unable to allocate cert trust"); + GEN_BREAK(SECFailure); + } + + rv = CERT_DecodeTrustString(trust, trusts); + if (rv) { + SECU_PrintError(progName, "unable to decode trust string"); + GEN_BREAK(SECFailure); + } + + rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, name, PR_FALSE); + if (rv != SECSuccess) { + /* sigh, PK11_Import Cert and CERT_ChangeCertTrust should have + * been coded to take a password arg. */ + if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { + rv = PK11_Authenticate(slot, PR_TRUE, pwdata); + if (rv != SECSuccess) { + SECU_PrintError(progName, + "could not authenticate to token %s.", + PK11_GetTokenName(slot)); + GEN_BREAK(SECFailure); + } + rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, + name, PR_FALSE); + } + if (rv != SECSuccess) { + SECU_PrintError(progName, + "could not add certificate to token or database"); + GEN_BREAK(SECFailure); + } + } + rv = ChangeCertTrust(handle, cert, trust, slot, pwdata); + if (rv != SECSuccess) { + SECU_PrintError(progName, + "could not change trust on certificate"); + GEN_BREAK(SECFailure); + } + + if (emailcert) { + CERT_SaveSMimeProfile(cert, NULL, pwdata); + } + + } while (0); + + CERT_DestroyCertificate(cert); + PORT_Free(trust); + + return rv; +} + +static SECStatus +CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, + SECOidTag hashAlgTag, CERTName *subject, const char *phone, int ascii, + const char *emailAddrs, const char *dnsNames, + certutilExtnList extnList, const char *extGeneric, + PRBool pssCertificate, /*out*/ SECItem *result) +{ + CERTSubjectPublicKeyInfo *spki; + CERTCertificateRequest *cr; + SECItem *encoding; + SECOidTag signAlgTag; + SECStatus rv; + PLArenaPool *arena; + void *extHandle; + SECItem signedReq = { siBuffer, NULL, 0 }; + SECAlgorithmID signAlg; + SECItem *params = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + SECU_PrintError(progName, "out of memory"); + return SECFailure; + } + + /* Create info about public key */ + spki = SECKEY_CreateSubjectPublicKeyInfo(pubk); + if (!spki) { + PORT_FreeArena(arena, PR_FALSE); + SECU_PrintError(progName, "unable to create subject public key"); + return SECFailure; + } + + /* Change cert type to RSA-PSS, if desired. */ + if (pssCertificate) { + params = SEC_CreateSignatureAlgorithmParameters(arena, + NULL, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, + hashAlgTag, + NULL, + privk); + if (!params) { + PORT_FreeArena(arena, PR_FALSE); + SECKEY_DestroySubjectPublicKeyInfo(spki); + SECU_PrintError(progName, "unable to create RSA-PSS parameters"); + return SECFailure; + } + + spki->algorithm.parameters.data = NULL; + rv = SECOID_SetAlgorithmID(arena, &spki->algorithm, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, + hashAlgTag == SEC_OID_UNKNOWN ? NULL : params); + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + SECKEY_DestroySubjectPublicKeyInfo(spki); + SECU_PrintError(progName, "unable to set algorithm ID"); + return SECFailure; + } + } + + /* Generate certificate request */ + cr = CERT_CreateCertificateRequest(subject, spki, NULL); + SECKEY_DestroySubjectPublicKeyInfo(spki); + if (!cr) { + PORT_FreeArena(arena, PR_FALSE); + SECU_PrintError(progName, "unable to make certificate request"); + return SECFailure; + } + + extHandle = CERT_StartCertificateRequestAttributes(cr); + if (extHandle == NULL) { + PORT_FreeArena(arena, PR_FALSE); + CERT_DestroyCertificateRequest(cr); + return SECFailure; + } + if (AddExtensions(extHandle, emailAddrs, dnsNames, extnList, extGeneric) != + SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + CERT_FinishExtensions(extHandle); + CERT_DestroyCertificateRequest(cr); + return SECFailure; + } + CERT_FinishExtensions(extHandle); + CERT_FinishCertificateRequestAttributes(cr); + + /* Der encode the request */ + encoding = SEC_ASN1EncodeItem(arena, NULL, cr, + SEC_ASN1_GET(CERT_CertificateRequestTemplate)); + CERT_DestroyCertificateRequest(cr); + if (encoding == NULL) { + PORT_FreeArena(arena, PR_FALSE); + SECU_PrintError(progName, "der encoding of request failed"); + return SECFailure; + } + + PORT_Memset(&signAlg, 0, sizeof(signAlg)); + if (pssCertificate) { + rv = SECOID_SetAlgorithmID(arena, &signAlg, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, params); + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + SECU_PrintError(progName, "unable to set algorithm ID"); + return SECFailure; + } + } else { + signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); + if (signAlgTag == SEC_OID_UNKNOWN) { + PORT_FreeArena(arena, PR_FALSE); + SECU_PrintError(progName, "unknown Key or Hash type"); + return SECFailure; + } + rv = SECOID_SetAlgorithmID(arena, &signAlg, signAlgTag, 0); + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + SECU_PrintError(progName, "unable to set algorithm ID"); + return SECFailure; + } + } + + /* Sign the request */ + rv = SEC_DerSignDataWithAlgorithmID(arena, &signedReq, + encoding->data, encoding->len, + privk, &signAlg); + if (rv) { + PORT_FreeArena(arena, PR_FALSE); + SECU_PrintError(progName, "signing of data failed"); + return SECFailure; + } + + /* Encode request in specified format */ + if (ascii) { + char *obuf; + char *header, *name, *email, *org, *state, *country; + + obuf = BTOA_ConvertItemToAscii(&signedReq); + if (!obuf) { + goto oom; + } + + name = CERT_GetCommonName(subject); + if (!name) { + name = PORT_Strdup("(not specified)"); + } + + if (!phone) + phone = "(not specified)"; + + email = CERT_GetCertEmailAddress(subject); + if (!email) + email = PORT_Strdup("(not specified)"); + + org = CERT_GetOrgName(subject); + if (!org) + org = PORT_Strdup("(not specified)"); + + state = CERT_GetStateName(subject); + if (!state) + state = PORT_Strdup("(not specified)"); + + country = CERT_GetCountryName(subject); + if (!country) + country = PORT_Strdup("(not specified)"); + + header = PR_smprintf( + "\nCertificate request generated by Netscape certutil\n" + "Phone: %s\n\n" + "Common Name: %s\n" + "Email: %s\n" + "Organization: %s\n" + "State: %s\n" + "Country: %s\n\n" + "%s\n", + phone, name, email, org, state, country, NS_CERTREQ_HEADER); + + PORT_Free(name); + PORT_Free(email); + PORT_Free(org); + PORT_Free(state); + PORT_Free(country); + + if (header) { + char *trailer = PR_smprintf("\n%s\n", NS_CERTREQ_TRAILER); + if (trailer) { + PRUint32 headerLen = PL_strlen(header); + PRUint32 obufLen = PL_strlen(obuf); + PRUint32 trailerLen = PL_strlen(trailer); + SECITEM_AllocItem(NULL, result, + headerLen + obufLen + trailerLen); + if (result->data) { + PORT_Memcpy(result->data, header, headerLen); + PORT_Memcpy(result->data + headerLen, obuf, obufLen); + PORT_Memcpy(result->data + headerLen + obufLen, + trailer, trailerLen); + } + PR_smprintf_free(trailer); + } + PR_smprintf_free(header); + } + PORT_Free(obuf); + } else { + (void)SECITEM_CopyItem(NULL, result, &signedReq); + } + + if (!result->data) { + oom: + SECU_PrintError(progName, "out of memory"); + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + } + + PORT_FreeArena(arena, PR_FALSE); + return rv; +} + +static SECStatus +ChangeTrustAttributes(CERTCertDBHandle *handle, PK11SlotInfo *slot, + char *name, char *trusts, void *pwdata) +{ + SECStatus rv; + CERTCertificate *cert; + CERTCertTrust *trust; + + cert = CERT_FindCertByNicknameOrEmailAddrCX(handle, name, pwdata); + if (!cert) { + SECU_PrintError(progName, "could not find certificate named \"%s\"", + name); + return SECFailure; + } + + trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust)); + if (!trust) { + SECU_PrintError(progName, "unable to allocate cert trust"); + return SECFailure; + } + + /* This function only decodes these characters: pPwcTCu, */ + rv = CERT_DecodeTrustString(trust, trusts); + if (rv) { + SECU_PrintError(progName, "unable to decode trust string"); + return SECFailure; + } + + /* CERT_ChangeCertTrust API does not have a way to pass in + * a context, so NSS can't prompt for the password if it needs to. + * check to see if the failure was token not logged in and + * log in if need be. */ + rv = ChangeCertTrust(handle, cert, trust, slot, pwdata); + if (rv != SECSuccess) { + SECU_PrintError(progName, "unable to modify trust attributes"); + return SECFailure; + } + CERT_DestroyCertificate(cert); + PORT_Free(trust); + + return SECSuccess; +} + +static SECStatus +DumpChain(CERTCertDBHandle *handle, char *name, PRBool ascii, + PRBool simpleSelfSigned) +{ + CERTCertificate *the_cert; + CERTCertificateList *chain; + int i, j; + the_cert = SECU_FindCertByNicknameOrFilename(handle, name, + ascii, NULL); + if (!the_cert) { + SECU_PrintError(progName, "Could not find: %s\n", name); + return SECFailure; + } + if (simpleSelfSigned && + SECEqual == SECITEM_CompareItem(&the_cert->derIssuer, + &the_cert->derSubject)) { + printf("\"%s\" [%s]\n\n", the_cert->nickname, the_cert->subjectName); + CERT_DestroyCertificate(the_cert); + return SECSuccess; + } + + chain = CERT_CertChainFromCert(the_cert, 0, PR_TRUE); + CERT_DestroyCertificate(the_cert); + if (!chain) { + SECU_PrintError(progName, "Could not obtain chain for: %s\n", name); + return SECFailure; + } + for (i = chain->len - 1; i >= 0; i--) { + CERTCertificate *c; + c = CERT_FindCertByDERCert(handle, &chain->certs[i]); + for (j = i; j < chain->len - 1; j++) { + printf(" "); + } + if (c) { + printf("\"%s\" [%s]\n\n", c->nickname, c->subjectName); + CERT_DestroyCertificate(c); + } else { + printf("(null)\n\n"); + } + } + CERT_DestroyCertificateList(chain); + return SECSuccess; +} + +static SECStatus +outputCertOrExtension(CERTCertificate *the_cert, PRBool raw, PRBool ascii, + SECItem *extensionOID, PRFileDesc *outfile) +{ + SECItem data; + PRInt32 numBytes; + SECStatus rv = SECFailure; + if (extensionOID) { + int i; + PRBool found = PR_FALSE; + for (i = 0; the_cert->extensions[i] != NULL; i++) { + CERTCertExtension *extension = the_cert->extensions[i]; + if (SECITEM_CompareItem(&extension->id, extensionOID) == SECEqual) { + found = PR_TRUE; + numBytes = PR_Write(outfile, extension->value.data, + extension->value.len); + rv = SECSuccess; + if (numBytes != (PRInt32)extension->value.len) { + SECU_PrintSystemError(progName, "error writing extension"); + rv = SECFailure; + } + break; + } + } + if (!found) { + SECU_PrintSystemError(progName, "extension not found"); + rv = SECFailure; + } + } else { + data.data = the_cert->derCert.data; + data.len = the_cert->derCert.len; + if (ascii) { + PR_fprintf(outfile, "%s\n%s\n%s\n", NS_CERT_HEADER, + BTOA_DataToAscii(data.data, data.len), NS_CERT_TRAILER); + rv = SECSuccess; + } else if (raw) { + numBytes = PR_Write(outfile, data.data, data.len); + rv = SECSuccess; + if (numBytes != (PRInt32)data.len) { + SECU_PrintSystemError(progName, "error writing raw cert"); + rv = SECFailure; + } + } else { + rv = SEC_PrintCertificateAndTrust(the_cert, "Certificate", NULL); + if (rv != SECSuccess) { + SECU_PrintError(progName, "problem printing certificate"); + } + } + } + return rv; +} + +static SECStatus +listCerts(CERTCertDBHandle *handle, char *name, char *email, + PK11SlotInfo *slot, PRBool raw, PRBool ascii, + SECItem *extensionOID, + PRFileDesc *outfile, void *pwarg) +{ + SECStatus rv = SECFailure; + CERTCertList *certs; + CERTCertListNode *node; + + /* List certs on a non-internal slot. */ + if (!PK11_IsFriendly(slot) && PK11_NeedLogin(slot)) { + SECStatus newrv = PK11_Authenticate(slot, PR_TRUE, pwarg); + if (newrv != SECSuccess) { + SECU_PrintError(progName, "could not authenticate to token %s.", + PK11_GetTokenName(slot)); + return SECFailure; + } + } + if (name) { + CERTCertificate *the_cert = + SECU_FindCertByNicknameOrFilename(handle, name, ascii, NULL); + if (!the_cert) { + SECU_PrintError(progName, "Could not find cert: %s\n", name); + return SECFailure; + } + /* Here, we have one cert with the desired nickname or email + * address. Now, we will attempt to get a list of ALL certs + * with the same subject name as the cert we have. That list + * should contain, at a minimum, the one cert we have already found. + * If the list of certs is empty (NULL), the libraries have failed. + */ + certs = CERT_CreateSubjectCertList(NULL, handle, &the_cert->derSubject, + PR_Now(), PR_FALSE); + CERT_DestroyCertificate(the_cert); + if (!certs) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + SECU_PrintError(progName, "problem printing certificates"); + return SECFailure; + } + for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node, certs); + node = CERT_LIST_NEXT(node)) { + rv = outputCertOrExtension(node->cert, raw, ascii, extensionOID, + outfile); + if (rv != SECSuccess) { + break; + } + } + } else if (email) { + certs = PK11_FindCertsFromEmailAddress(email, NULL); + if (!certs) { + SECU_PrintError(progName, + "Could not find certificates for email address: %s\n", + email); + return SECFailure; + } + for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node, certs); + node = CERT_LIST_NEXT(node)) { + rv = outputCertOrExtension(node->cert, raw, ascii, extensionOID, + outfile); + if (rv != SECSuccess) { + break; + } + } + } else { + certs = PK11_ListCertsInSlot(slot); + if (certs) { + for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node, certs); + node = CERT_LIST_NEXT(node)) { + SECU_PrintCertNickname(node, stdout); + } + rv = SECSuccess; + } + } + if (certs) { + CERT_DestroyCertList(certs); + } + if (rv) { + SECU_PrintError(progName, "problem printing certificate nicknames"); + return SECFailure; + } + + return SECSuccess; /* not rv ?? */ +} + +static SECStatus +ListCerts(CERTCertDBHandle *handle, char *nickname, char *email, + PK11SlotInfo *slot, PRBool raw, PRBool ascii, + SECItem *extensionOID, + PRFileDesc *outfile, secuPWData *pwdata) +{ + SECStatus rv; + + if (slot && PK11_NeedUserInit(slot)) { + printf("\nDatabase needs user init\n"); + } + + if (!ascii && !raw && !nickname && !email) { + PR_fprintf(outfile, "\n%-60s %-5s\n%-60s %-5s\n\n", + "Certificate Nickname", "Trust Attributes", "", + "SSL,S/MIME,JAR/XPI"); + } + if (slot == NULL) { + CERTCertList *list; + CERTCertListNode *node; + + list = PK11_ListCerts(PK11CertListAll, pwdata); + for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list); + node = CERT_LIST_NEXT(node)) { + SECU_PrintCertNickname(node, stdout); + } + CERT_DestroyCertList(list); + return SECSuccess; + } + rv = listCerts(handle, nickname, email, slot, raw, ascii, + extensionOID, outfile, pwdata); + return rv; +} + +static SECStatus +DeleteCert(CERTCertDBHandle *handle, char *name, void *pwdata) +{ + SECStatus rv; + CERTCertificate *cert; + + cert = CERT_FindCertByNicknameOrEmailAddrCX(handle, name, pwdata); + if (!cert) { + SECU_PrintError(progName, "could not find certificate named \"%s\"", + name); + return SECFailure; + } + + rv = SEC_DeletePermCertificate(cert); + CERT_DestroyCertificate(cert); + if (rv) { + SECU_PrintError(progName, "unable to delete certificate"); + } + return rv; +} + +static SECStatus +RenameCert(CERTCertDBHandle *handle, char *name, char *newName, void *pwdata) +{ + SECStatus rv; + CERTCertificate *cert; + + cert = CERT_FindCertByNicknameOrEmailAddrCX(handle, name, pwdata); + if (!cert) { + SECU_PrintError(progName, "could not find certificate named \"%s\"", + name); + return SECFailure; + } + + rv = __PK11_SetCertificateNickname(cert, newName); + CERT_DestroyCertificate(cert); + if (rv) { + SECU_PrintError(progName, "unable to rename certificate"); + } + return rv; +} + +static SECStatus +ValidateCert(CERTCertDBHandle *handle, char *name, char *date, + char *certUsage, PRBool checkSig, PRBool logit, + PRBool ascii, secuPWData *pwdata) +{ + SECStatus rv; + CERTCertificate *cert = NULL; + PRTime timeBoundary; + SECCertificateUsage usage; + CERTVerifyLog reallog; + CERTVerifyLog *log = NULL; + + if (!certUsage) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return (SECFailure); + } + + switch (*certUsage) { + case 'O': + usage = certificateUsageStatusResponder; + break; + case 'L': + usage = certificateUsageSSLCA; + break; + case 'A': + usage = certificateUsageAnyCA; + break; + case 'Y': + usage = certificateUsageVerifyCA; + break; + case 'C': + usage = certificateUsageSSLClient; + break; + case 'V': + usage = certificateUsageSSLServer; + break; + case 'I': + usage = certificateUsageIPsec; + break; + case 'S': + usage = certificateUsageEmailSigner; + break; + case 'R': + usage = certificateUsageEmailRecipient; + break; + case 'J': + usage = certificateUsageObjectSigner; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return (SECFailure); + } + do { + cert = SECU_FindCertByNicknameOrFilename(handle, name, ascii, + NULL); + if (!cert) { + SECU_PrintError(progName, "could not find certificate named \"%s\"", + name); + GEN_BREAK(SECFailure) + } + + if (date != NULL) { + rv = DER_AsciiToTime(&timeBoundary, date); + if (rv) { + SECU_PrintError(progName, "invalid input date"); + GEN_BREAK(SECFailure) + } + } else { + timeBoundary = PR_Now(); + } + + if (logit) { + log = &reallog; + + log->count = 0; + log->head = NULL; + log->tail = NULL; + log->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (log->arena == NULL) { + SECU_PrintError(progName, "out of memory"); + GEN_BREAK(SECFailure) + } + } + + rv = CERT_VerifyCertificate(handle, cert, checkSig, usage, + timeBoundary, pwdata, log, &usage); + if (log) { + if (log->head == NULL) { + fprintf(stdout, "%s: certificate is valid\n", progName); + GEN_BREAK(SECSuccess) + } else { + char *nick; + CERTVerifyLogNode *node; + + node = log->head; + while (node) { + if (node->cert->nickname != NULL) { + nick = node->cert->nickname; + } else { + nick = node->cert->subjectName; + } + fprintf(stderr, "%s : %s\n", nick, + SECU_Strerror(node->error)); + CERT_DestroyCertificate(node->cert); + node = node->next; + } + } + } else { + if (rv != SECSuccess) { + PRErrorCode perr = PORT_GetError(); + fprintf(stdout, "%s: certificate is invalid: %s\n", + progName, SECU_Strerror(perr)); + GEN_BREAK(SECFailure) + } + fprintf(stdout, "%s: certificate is valid\n", progName); + GEN_BREAK(SECSuccess) + } + } while (0); + + if (cert) { + CERT_DestroyCertificate(cert); + } + + return (rv); +} + +static PRBool +ItemIsPrintableASCII(const SECItem *item) +{ + unsigned char *src = item->data; + unsigned int len = item->len; + while (len-- > 0) { + unsigned char uc = *src++; + if (uc < 0x20 || uc > 0x7e) + return PR_FALSE; + } + return PR_TRUE; +} + +/* Caller ensures that dst is at least item->len*2+1 bytes long */ +static void +SECItemToHex(const SECItem *item, char *dst) +{ + if (dst && item && item->data) { + unsigned char *src = item->data; + unsigned int len = item->len; + for (; len > 0; --len, dst += 2) { + snprintf(dst, 3, "%02x", *src++); + } + *dst = '\0'; + } +} + +static const char *const keyTypeName[] = { + "null", "rsa", "dsa", "fortezza", "dh", "kea", "ec", "rsaPss", "rsaOaep" +}; + +#define MAX_CKA_ID_BIN_LEN 20 +#define MAX_CKA_ID_STR_LEN 40 + +/* output human readable key ID in buffer, which should have at least + * MAX_CKA_ID_STR_LEN + 3 octets (quotations and a null terminator) */ +static void +formatPrivateKeyID(SECKEYPrivateKey *privkey, char *buffer) +{ + SECItem *ckaID; + + ckaID = PK11_GetLowLevelKeyIDForPrivateKey(privkey); + if (!ckaID) { + strcpy(buffer, "(no CKA_ID)"); + } else if (ItemIsPrintableASCII(ckaID)) { + int len = PR_MIN(MAX_CKA_ID_STR_LEN, ckaID->len); + buffer[0] = '"'; + memcpy(buffer + 1, ckaID->data, len); + buffer[1 + len] = '"'; + buffer[2 + len] = '\0'; + } else { + /* print ckaid in hex */ + SECItem idItem = *ckaID; + if (idItem.len > MAX_CKA_ID_BIN_LEN) + idItem.len = MAX_CKA_ID_BIN_LEN; + SECItemToHex(&idItem, buffer); + } + SECITEM_ZfreeItem(ckaID, PR_TRUE); +} + +/* print key number, key ID (in hex or ASCII), key label (nickname) */ +static SECStatus +PrintKey(PRFileDesc *out, const char *nickName, int count, + SECKEYPrivateKey *key, void *pwarg) +{ + char ckaIDbuf[MAX_CKA_ID_STR_LEN + 4]; + CERTCertificate *cert; + KeyType keyType; + + formatPrivateKeyID(key, ckaIDbuf); + cert = PK11_GetCertFromPrivateKey(key); + if (cert) { + keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo); + CERT_DestroyCertificate(cert); + } else { + keyType = key->keyType; + } + PR_fprintf(out, "<%2d> %-8.8s %-42.42s %s\n", count, + keyTypeName[keyType], ckaIDbuf, nickName); + + return SECSuccess; +} + +/* returns SECSuccess if ANY keys are found, SECFailure otherwise. */ +static SECStatus +ListKeysInSlot(PK11SlotInfo *slot, const char *nickName, KeyType keyType, + void *pwarg) +{ + SECKEYPrivateKeyList *list; + SECKEYPrivateKeyListNode *node; + int count = 0; + + if (PK11_NeedLogin(slot)) { + SECStatus rv = PK11_Authenticate(slot, PR_TRUE, pwarg); + if (rv != SECSuccess) { + SECU_PrintError(progName, "could not authenticate to token %s.", + PK11_GetTokenName(slot)); + return SECFailure; + } + } + + if (nickName && nickName[0]) + list = PK11_ListPrivKeysInSlot(slot, (char *)nickName, pwarg); + else + list = PK11_ListPrivateKeysInSlot(slot); + if (list == NULL) { + SECU_PrintError(progName, "problem listing keys"); + return SECFailure; + } + for (node = PRIVKEY_LIST_HEAD(list); + !PRIVKEY_LIST_END(node, list); + node = PRIVKEY_LIST_NEXT(node)) { + char *keyName; + static const char orphan[] = { "(orphan)" }; + + if (keyType != nullKey && keyType != node->key->keyType) + continue; + keyName = PK11_GetPrivateKeyNickname(node->key); + if (!keyName || !keyName[0]) { + /* Try extra hard to find nicknames for keys that lack them. */ + CERTCertificate *cert; + PORT_Free((void *)keyName); + keyName = NULL; + cert = PK11_GetCertFromPrivateKey(node->key); + if (cert) { + if (cert->nickname && cert->nickname[0]) { + keyName = PORT_Strdup(cert->nickname); + } else if (cert->emailAddr && cert->emailAddr[0]) { + keyName = PORT_Strdup(cert->emailAddr); + } + CERT_DestroyCertificate(cert); + } + } + if (nickName) { + if (!keyName || PL_strcmp(keyName, nickName)) { + /* PKCS#11 module returned unwanted keys */ + PORT_Free((void *)keyName); + continue; + } + } + if (!keyName) + keyName = (char *)orphan; + + PrintKey(PR_STDOUT, keyName, count, node->key, pwarg); + + if (keyName != (char *)orphan) + PORT_Free((void *)keyName); + count++; + } + SECKEY_DestroyPrivateKeyList(list); + + if (count == 0) { + PR_fprintf(PR_STDOUT, "%s: no keys found\n", progName); + return SECFailure; + } + return SECSuccess; +} + +/* returns SECSuccess if ANY keys are found, SECFailure otherwise. */ +static SECStatus +ListKeys(PK11SlotInfo *slot, const char *nickName, int index, + KeyType keyType, PRBool dopriv, secuPWData *pwdata) +{ + SECStatus rv = SECFailure; + static const char fmt[] = + "%s: Checking token \"%.33s\" in slot \"%.65s\"\n"; + + if (slot == NULL) { + PK11SlotList *list; + PK11SlotListElement *le; + + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, pwdata); + if (list) { + for (le = list->head; le; le = le->next) { + PR_fprintf(PR_STDOUT, fmt, progName, + PK11_GetTokenName(le->slot), + PK11_GetSlotName(le->slot)); + rv &= ListKeysInSlot(le->slot, nickName, keyType, pwdata); + } + PK11_FreeSlotList(list); + } + } else { + PR_fprintf(PR_STDOUT, fmt, progName, PK11_GetTokenName(slot), + PK11_GetSlotName(slot)); + rv = ListKeysInSlot(slot, nickName, keyType, pwdata); + } + return rv; +} + +static SECStatus +DeleteCertAndKey(char *nickname, secuPWData *pwdata) +{ + SECStatus rv; + CERTCertificate *cert; + PK11SlotInfo *slot; + + slot = PK11_GetInternalKeySlot(); + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, pwdata); + if (rv != SECSuccess) { + SECU_PrintError(progName, "could not authenticate to token %s.", + PK11_GetTokenName(slot)); + return SECFailure; + } + } + cert = PK11_FindCertFromNickname(nickname, pwdata); + if (!cert) { + PK11_FreeSlot(slot); + return SECFailure; + } + rv = PK11_DeleteTokenCertAndKey(cert, pwdata); + if (rv != SECSuccess) { + SECU_PrintError("problem deleting private key \"%s\"\n", nickname); + } + CERT_DestroyCertificate(cert); + PK11_FreeSlot(slot); + return rv; +} + +static SECKEYPrivateKey * +findPrivateKeyByID(PK11SlotInfo *slot, const char *ckaID, secuPWData *pwarg) +{ + PORTCheapArenaPool arena; + SECItem ckaIDItem = { 0 }; + SECKEYPrivateKey *privkey = NULL; + SECStatus rv; + + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, pwarg); + if (rv != SECSuccess) { + SECU_PrintError(progName, "could not authenticate to token %s.", + PK11_GetTokenName(slot)); + return NULL; + } + } + + if (0 == PL_strncasecmp("0x", ckaID, 2)) { + ckaID += 2; /* skip leading "0x" */ + } + PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE); + if (SECU_HexString2SECItem(&arena.arena, &ckaIDItem, ckaID)) { + privkey = PK11_FindKeyByKeyID(slot, &ckaIDItem, pwarg); + } + PORT_DestroyCheapArena(&arena); + return privkey; +} + +static SECStatus +DeleteKey(SECKEYPrivateKey *privkey, secuPWData *pwarg) +{ + SECStatus rv; + PK11SlotInfo *slot; + + slot = PK11_GetSlotFromPrivateKey(privkey); + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, pwarg); + if (rv != SECSuccess) { + SECU_PrintError(progName, "could not authenticate to token %s.", + PK11_GetTokenName(slot)); + return SECFailure; + } + } + + rv = PK11_DeleteTokenPrivateKey(privkey, PR_TRUE); + if (rv != SECSuccess) { + char ckaIDbuf[MAX_CKA_ID_STR_LEN + 4]; + formatPrivateKeyID(privkey, ckaIDbuf); + SECU_PrintError("problem deleting private key \"%s\"\n", ckaIDbuf); + } + + PK11_FreeSlot(slot); + return rv; +} + +/* + * L i s t M o d u l e s + * + * Print a list of the PKCS11 modules that are + * available. This is useful for smartcard people to + * make sure they have the drivers loaded. + * + */ +static SECStatus +ListModules(void) +{ + PK11SlotList *list; + PK11SlotListElement *le; + + /* get them all! */ + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, NULL); + if (list == NULL) + return SECFailure; + + /* look at each slot*/ + for (le = list->head; le; le = le->next) { + char *token_uri = PK11_GetTokenURI(le->slot); + printf("\n"); + printf(" slot: %s\n", PK11_GetSlotName(le->slot)); + printf(" token: %s\n", PK11_GetTokenName(le->slot)); + printf(" uri: %s\n", token_uri); + PORT_Free(token_uri); + } + PK11_FreeSlotList(list); + + return SECSuccess; +} + +static void +PrintBuildFlags() +{ +#ifdef NSS_FIPS_DISABLED + PR_fprintf(PR_STDOUT, "NSS_FIPS_DISABLED\n"); +#endif +#ifdef NSS_NO_INIT_SUPPORT + PR_fprintf(PR_STDOUT, "NSS_NO_INIT_SUPPORT\n"); +#endif + exit(0); +} + +static void +PrintSyntax() +{ +#define FPS fprintf(stderr, + FPS "Type %s -H for more detailed descriptions\n", progName); + FPS "Usage: %s -N [-d certdir] [-P dbprefix] [-f pwfile] [--empty-password]\n", progName); + FPS "Usage: %s -T [-d certdir] [-P dbprefix] [-h token-name]\n" + "\t\t [-f pwfile] [-0 SSO-password]\n", progName); + FPS "\t%s -A -n cert-name -t trustargs [-d certdir] [-P dbprefix] [-a] [-i input]\n", + progName); + FPS "\t%s -B -i batch-file\n", progName); + FPS "\t%s -C [-c issuer-name | -x] -i cert-request-file -o cert-file\n" + "\t\t [-m serial-number] [-w warp-months] [-v months-valid]\n" + "\t\t [-f pwfile] [-d certdir] [-P dbprefix] [-Z hashAlg]\n" + "\t\t [-1 | --keyUsage [keyUsageKeyword,..]] [-2] [-3] [-4]\n" + "\t\t [-5 | --nsCertType [nsCertTypeKeyword,...]]\n" + "\t\t [-6 | --extKeyUsage [extKeyUsageKeyword,...]] [-7 emailAddrs]\n" + "\t\t [-8 dns-names] [-a]\n", + progName); + FPS "\t%s -D -n cert-name [-d certdir] [-P dbprefix]\n", progName); + FPS "\t%s --rename -n cert-name --new-n new-cert-name\n" + "\t\t [-d certdir] [-P dbprefix]\n", progName); + FPS "\t%s -E -n cert-name -t trustargs [-d certdir] [-P dbprefix] [-a] [-i input]\n", + progName); + FPS "\t%s -F -n cert-name [-d certdir] [-P dbprefix]\n", + progName); + FPS "\t%s -F -k key-id [-d certdir] [-P dbprefix]\n", + progName); + FPS "\t%s -G -n key-name [-h token-name] [-k rsa] [-g key-size] [-y exp]\n" + "\t\t [-f pwfile] [-z noisefile] [-d certdir] [-P dbprefix]\n", progName); + FPS "\t%s -G [-h token-name] -k dsa [-q pqgfile -g key-size] [-f pwfile]\n" + "\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName); + FPS "\t%s -G [-h token-name] -k ec -q curve [-f pwfile]\n" + "\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName); + FPS "\t%s -K [-n key-name] [-h token-name] [-k dsa|ec|rsa|all]\n", + progName); + FPS "\t\t [-f pwfile] [-X] [-d certdir] [-P dbprefix]\n"); + FPS "\t%s --upgrade-merge --source-dir upgradeDir --upgrade-id uniqueID\n", + progName); + FPS "\t\t [--upgrade-token-name tokenName] [-d targetDBDir]\n"); + FPS "\t\t [-P targetDBPrefix] [--source-prefix upgradeDBPrefix]\n"); + FPS "\t\t [-f targetPWfile] [-@ upgradePWFile]\n"); + FPS "\t%s --merge --source-dir sourceDBDir [-d targetDBdir]\n", + progName); + FPS "\t\t [-P targetDBPrefix] [--source-prefix sourceDBPrefix]\n"); + FPS "\t\t [-f targetPWfile] [-@ sourcePWFile]\n"); + FPS "\t%s -L [-n cert-name] [-h token-name] [--email email-address]\n", + progName); + FPS "\t\t [-X] [-r] [-a] [--dump-ext-val OID] [-d certdir] [-P dbprefix]\n"); + FPS "\t%s --build-flags\n", progName); + FPS "\t%s -M -n cert-name -t trustargs [-d certdir] [-P dbprefix]\n", + progName); + FPS "\t%s -O -n cert-name [-X] [-d certdir] [-a] [-P dbprefix]\n" + "\t\t [--simple-self-signed]\n", + progName); + FPS "\t%s -R -s subj -o cert-request-file [-d certdir] [-P dbprefix] [-p phone] [-a]\n" + "\t\t [-7 emailAddrs] [-k key-type-or-id] [-h token-name] [-f pwfile]\n" + "\t\t [-g key-size] [-Z hashAlg]\n", + progName); + FPS "\t%s -V -n cert-name -u usage [-b time] [-e] [-a]\n" + "\t\t[-X] [-d certdir] [-P dbprefix]\n", + progName); + FPS "Usage: %s -W [-d certdir] [-f pwfile] [-@newpwfile]\n", + progName); + FPS "\t%s -S -n cert-name -s subj [-c issuer-name | -x] -t trustargs\n" + "\t\t [-k key-type-or-id] [-q key-params] [-h token-name] [-g key-size]\n" + "\t\t [-m serial-number] [-w warp-months] [-v months-valid]\n" + "\t\t [-f pwfile] [-d certdir] [-P dbprefix] [-Z hashAlg]\n" + "\t\t [-p phone] [-1] [-2] [-3] [-4] [-5] [-6] [-7 emailAddrs]\n" + "\t\t [-8 DNS-names]\n" + "\t\t [--extAIA] [--extSIA] [--extCP] [--extPM] [--extPC] [--extIA]\n" + "\t\t [--extSKID] [--extNC] [--extSAN type:name[,type:name]...]\n" + "\t\t [--extGeneric OID:critical-flag:filename[,OID:critical-flag:filename]...]\n", progName); + FPS "\t%s -U [-X] [-d certdir] [-P dbprefix]\n", progName); + exit(1); +} + +enum usage_level { + usage_all = 0, + usage_selected = 1 +}; + +static void luCommonDetailsAE(); + +static void +luA(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "A")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Add a certificate to the database (create if needed)\n", + "-A"); + if (ul == usage_selected && !is_my_command) + return; + if (ul == usage_all) { + FPS "%-20s\n", " All options under -E apply"); + } else { + luCommonDetailsAE(); + } +} + +static void +luB(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "B")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Run a series of certutil commands from a batch file\n", "-B"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Specify the batch file\n", " -i batch-file"); +} + +static void +luE(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "E")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Add an Email certificate to the database (create if needed)\n", + "-E"); + if (ul == usage_selected && !is_my_command) + return; + luCommonDetailsAE(); +} + +static void +luCommonDetailsAE() +{ + FPS "%-20s Specify the nickname of the certificate to add\n", + " -n cert-name"); + FPS "%-20s Set the certificate trust attributes:\n", + " -t trustargs"); + FPS "%-25s trustargs is of the form x,y,z where x is for SSL, y is for S/MIME,\n", ""); + FPS "%-25s and z is for code signing. Use ,, for no explicit trust.\n", ""); + FPS "%-25s p \t prohibited (explicitly distrusted)\n", ""); + FPS "%-25s P \t trusted peer\n", ""); + FPS "%-25s c \t valid CA\n", ""); + FPS "%-25s T \t trusted CA to issue client certs (implies c)\n", ""); + FPS "%-25s C \t trusted CA to issue server certs (implies c)\n", ""); + FPS "%-25s u \t user cert\n", ""); + FPS "%-25s w \t send warning\n", ""); + FPS "%-25s g \t make step-up cert\n", ""); + FPS "%-20s Specify the password file\n", + " -f pwfile"); + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s The input certificate is encoded in ASCII (RFC1113)\n", + " -a"); + FPS "%-20s Specify the certificate file (default is stdin)\n", + " -i input"); + FPS "\n"); +} + +static void +luC(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "C")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Create a new binary certificate from a BINARY cert request\n", + "-C"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s The nickname of the issuer cert\n", + " -c issuer-name"); + FPS "%-20s The BINARY certificate request file\n", + " -i cert-request "); + FPS "%-20s Output binary cert to this file (default is stdout)\n", + " -o output-cert"); + FPS "%-20s Self sign\n", + " -x"); + FPS "%-20s Sign the certificate with RSA-PSS (the issuer key must be rsa)\n", + " --pss-sign"); + FPS "%-20s Cert serial number\n", + " -m serial-number"); + FPS "%-20s Time Warp\n", + " -w warp-months"); + FPS "%-20s Months valid (default is 3)\n", + " -v months-valid"); + FPS "%-20s Specify the password file\n", + " -f pwfile"); + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s \n" + "%-20s Specify the hash algorithm to use. Possible keywords:\n" + "%-20s \"MD2\", \"MD4\", \"MD5\", \"SHA1\", \"SHA224\",\n" + "%-20s \"SHA256\", \"SHA384\", \"SHA512\"\n", + " -Z hashAlg", "", "", ""); + FPS "%-20s \n" + "%-20s Create key usage extension. Possible keywords:\n" + "%-20s \"digitalSignature\", \"nonRepudiation\", \"keyEncipherment\",\n" + "%-20s \"dataEncipherment\", \"keyAgreement\", \"certSigning\",\n" + "%-20s \"crlSigning\", \"critical\"\n", + " -1 | --keyUsage keyword,keyword,...", "", "", "", ""); + FPS "%-20s Create basic constraint extension\n", + " -2 "); + FPS "%-20s Create authority key ID extension\n", + " -3 "); + FPS "%-20s Create crl distribution point extension\n", + " -4 "); + FPS "%-20s \n" + "%-20s Create netscape cert type extension. Possible keywords:\n" + "%-20s \"sslClient\", \"sslServer\", \"smime\", \"objectSigning\",\n" + "%-20s \"sslCA\", \"smimeCA\", \"objectSigningCA\", \"critical\".\n", + " -5 | --nsCertType keyword,keyword,... ", "", "", ""); + FPS "%-20s \n" + "%-20s Create extended key usage extension. Possible keywords:\n" + "%-20s \"serverAuth\", \"clientAuth\",\"codeSigning\",\n" + "%-20s \"emailProtection\", \"timeStamp\",\"ocspResponder\",\n" + "%-20s \"stepUp\", \"msTrustListSign\", \"x509Any\",\n" + "%-20s \"ipsecIKE\", \"ipsecIKEEnd\", \"ipsecIKEIntermediate\",\n" + "%-20s \"ipsecEnd\", \"ipsecTunnel\", \"ipsecUser\",\n" + "%-20s \"critical\"\n", + " -6 | --extKeyUsage keyword,keyword,...", "", "", "", "", "", "", ""); + FPS "%-20s Create an email subject alt name extension\n", + " -7 emailAddrs"); + FPS "%-20s Create an dns subject alt name extension\n", + " -8 dnsNames"); + FPS "%-20s The input certificate request is encoded in ASCII (RFC1113)\n", + " -a"); + FPS "\n"); +} + +static void +luG(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "G")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Generate a new key pair\n", + "-G"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Name of token in which to generate key (default is internal)\n", + " -h token-name"); + FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n", + " -k key-type"); + FPS "%-20s Key size in bits, (min %d, max %d, default %d) (not for ec)\n", + " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS); + FPS "%-20s Set the public exponent value (3, 17, 65537) (rsa only)\n", + " -y exp"); + FPS "%-20s Specify the password file\n", + " -f password-file"); + FPS "%-20s Specify the noise file to be used\n", + " -z noisefile"); + FPS "%-20s read PQG value from pqgfile (dsa only)\n", + " -q pqgfile"); + FPS "%-20s Elliptic curve name (ec only)\n", + " -q curve-name"); + FPS "%-20s One of nistp256, nistp384, nistp521, curve25519.\n", ""); + FPS "%-20s If a custom token is present, the following curves are also supported:\n", ""); + FPS "%-20s sect163k1, nistk163, sect163r1, sect163r2,\n", ""); + FPS "%-20s nistb163, sect193r1, sect193r2, sect233k1, nistk233,\n", ""); + FPS "%-20s sect233r1, nistb233, sect239k1, sect283k1, nistk283,\n", ""); + FPS "%-20s sect283r1, nistb283, sect409k1, nistk409, sect409r1,\n", ""); + FPS "%-20s nistb409, sect571k1, nistk571, sect571r1, nistb571,\n", ""); + FPS "%-20s secp160k1, secp160r1, secp160r2, secp192k1, secp192r1,\n", ""); + FPS "%-20s nistp192, secp224k1, secp224r1, nistp224, secp256k1,\n", ""); + FPS "%-20s secp256r1, secp384r1, secp521r1,\n", ""); + FPS "%-20s prime192v1, prime192v2, prime192v3, \n", ""); + FPS "%-20s prime239v1, prime239v2, prime239v3, c2pnb163v1, \n", ""); + FPS "%-20s c2pnb163v2, c2pnb163v3, c2pnb176v1, c2tnb191v1, \n", ""); + FPS "%-20s c2tnb191v2, c2tnb191v3, \n", ""); + FPS "%-20s c2pnb208w1, c2tnb239v1, c2tnb239v2, c2tnb239v3, \n", ""); + FPS "%-20s c2pnb272w1, c2pnb304w1, \n", ""); + FPS "%-20s c2tnb359w1, c2pnb368w1, c2tnb431r1, secp112r1, \n", ""); + FPS "%-20s secp112r2, secp128r1, secp128r2, sect113r1, sect113r2\n", ""); + FPS "%-20s sect131r1, sect131r2\n", ""); + FPS "%-20s Key database directory (default is ~/.netscape)\n", + " -d keydir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s\n" + "%-20s PKCS #11 key Attributes.\n", + " --keyAttrFlags attrflags", ""); + FPS "%-20s Comma separated list of key attribute attribute flags,\n", ""); + FPS "%-20s selected from the following list of choices:\n", ""); + FPS "%-20s {token | session} {public | private} {sensitive | insensitive}\n", ""); + FPS "%-20s {modifiable | unmodifiable} {extractable | unextractable}\n", ""); + FPS "%-20s\n", + " --keyOpFlagsOn opflags"); + FPS "%-20s\n" + "%-20s PKCS #11 key Operation Flags.\n", + " --keyOpFlagsOff opflags", ""); + FPS "%-20s Comma separated list of one or more of the following:\n", ""); + FPS "%-20s encrypt, decrypt, sign, sign_recover, verify,\n", ""); + FPS "%-20s verify_recover, wrap, unwrap, derive\n", ""); + FPS "\n"); +} + +static void +luD(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "D")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Delete a certificate from the database\n", + "-D"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s The nickname of the cert to delete\n", + " -n cert-name"); + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "\n"); +} + +static void +luF(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "F")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Delete a key and associated certificate from the database\n", + "-F"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s The nickname of the key to delete\n", + " -n cert-name"); + FPS "%-20s The key id of the key to delete, obtained using -K\n", + " -k key-id"); + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "\n"); +} + +static void +luU(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "U")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s List all modules\n", /*, or print out a single named module\n",*/ + "-U"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Module database directory (default is '~/.netscape')\n", + " -d moddir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s force the database to open R/W\n", + " -X"); + FPS "\n"); +} + +static void +luK(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "K")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s List all private keys\n", + "-K"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Name of token to search (\"all\" for all tokens)\n", + " -h token-name "); + + FPS "%-20s Key type (\"all\" (default), \"dsa\"," + " \"ec\"," + " \"rsa\")\n", + " -k key-type"); + FPS "%-20s The nickname of the key or associated certificate\n", + " -n name"); + FPS "%-20s Specify the password file\n", + " -f password-file"); + FPS "%-20s Key database directory (default is ~/.netscape)\n", + " -d keydir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s force the database to open R/W\n", + " -X"); + FPS "\n"); +} + +static void +luL(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "L")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s List all certs, or print out a single named cert (or a subset)\n", + "-L"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Name of token to search (\"all\" for all tokens)\n", + " -h token-name "); + FPS "%-20s Pretty print named cert (list all if unspecified)\n", + " -n cert-name"); + FPS "%-20s \n" + "%-20s Pretty print cert with email address (list all if unspecified)\n", + " --email email-address", ""); + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s force the database to open R/W\n", + " -X"); + FPS "%-20s For single cert, print binary DER encoding\n", + " -r"); + FPS "%-20s For single cert, print ASCII encoding (RFC1113)\n", + " -a"); + FPS "%-20s \n" + "%-20s For single cert, print binary DER encoding of extension OID\n", + " --dump-ext-val OID", ""); + FPS "\n"); +} + +static void +luM(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "M")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Modify trust attributes of certificate\n", + "-M"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s The nickname of the cert to modify\n", + " -n cert-name"); + FPS "%-20s Set the certificate trust attributes (see -A above)\n", + " -t trustargs"); + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "\n"); +} + +static void +luN(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "N")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Create a new certificate database\n", + "-N"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s Specify the password file\n", + " -f password-file"); + FPS "%-20s use empty password when creating a new database\n", + " --empty-password"); + FPS "\n"); +} + +static void +luT(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "T")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Reset the Key database or token\n", + "-T"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s Token to reset (default is internal)\n", + " -h token-name"); + FPS "%-20s Set token's Site Security Officer password\n", + " -0 SSO-password"); + FPS "\n"); +} + +static void +luO(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "O")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Print the chain of a certificate\n", + "-O"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s The nickname of the cert to modify\n", + " -n cert-name"); + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Input the certificate in ASCII (RFC1113); default is binary\n", + " -a"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s force the database to open R/W\n", + " -X"); + FPS "%-20s don't search for a chain if issuer name equals subject name\n", + " --simple-self-signed"); + FPS "\n"); +} + +static void +luR(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "R")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Generate a certificate request (stdout)\n", + "-R"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Specify the subject name (using RFC1485)\n", + " -s subject"); + FPS "%-20s Output the cert request to this file\n", + " -o output-req"); + FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n", + " -k key-type-or-id"); + FPS "%-20s or nickname of the cert key to use, or key id obtained using -K\n", + ""); + FPS "%-20s Name of token in which to generate key (default is internal)\n", + " -h token-name"); + FPS "%-20s Key size in bits, RSA keys only (min %d, max %d, default %d)\n", + " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS); + FPS "%-20s Create a certificate request restricted to RSA-PSS (rsa only)\n", + " --pss"); + FPS "%-20s Name of file containing PQG parameters (dsa only)\n", + " -q pqgfile"); + FPS "%-20s Elliptic curve name (ec only)\n", + " -q curve-name"); + FPS "%-20s See the \"-G\" option for a full list of supported names.\n", + ""); + FPS "%-20s Specify the password file\n", + " -f pwfile"); + FPS "%-20s Key database directory (default is ~/.netscape)\n", + " -d keydir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s Specify the contact phone number (\"123-456-7890\")\n", + " -p phone"); + FPS "%-20s \n" + "%-20s Specify the hash algorithm to use. Possible keywords:\n" + "%-20s \"MD2\", \"MD4\", \"MD5\", \"SHA1\", \"SHA224\",\n" + "%-20s \"SHA256\", \"SHA384\", \"SHA512\"\n", + " -Z hashAlg", "", "", ""); + FPS "%-20s Output the cert request in ASCII (RFC1113); default is binary\n", + " -a"); + FPS "%-20s \n", + " See -S for available extension options"); + FPS "%-20s \n", + " See -G for available key flag options"); + FPS "\n"); +} + +static void +luV(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "V")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Validate a certificate\n", + "-V"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s The nickname of the cert to Validate\n", + " -n cert-name"); + FPS "%-20s validity time (\"YYMMDDHHMMSS[+HHMM|-HHMM|Z]\")\n", + " -b time"); + FPS "%-20s Check certificate signature \n", + " -e "); + FPS "%-20s Specify certificate usage:\n", " -u certusage"); + FPS "%-25s C \t SSL Client\n", ""); + FPS "%-25s V \t SSL Server\n", ""); + FPS "%-25s I \t IPsec\n", ""); + FPS "%-25s L \t SSL CA\n", ""); + FPS "%-25s A \t Any CA\n", ""); + FPS "%-25s Y \t Verify CA\n", ""); + FPS "%-25s S \t Email signer\n", ""); + FPS "%-25s R \t Email Recipient\n", ""); + FPS "%-25s O \t OCSP status responder\n", ""); + FPS "%-25s J \t Object signer\n", ""); + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Input the certificate in ASCII (RFC1113); default is binary\n", + " -a"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s force the database to open R/W\n", + " -X"); + FPS "\n"); +} + +static void +luW(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "W")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Change the key database password\n", + "-W"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s cert and key database directory\n", + " -d certdir"); + FPS "%-20s Specify a file with the current password\n", + " -f pwfile"); + FPS "%-20s Specify a file with the new password in two lines\n", + " -@ newpwfile"); + FPS "\n"); +} + +static void +luRename(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "rename")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Change the database nickname of a certificate\n", + "--rename"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s The old nickname of the cert to rename\n", + " -n cert-name"); + FPS "%-20s The new nickname of the cert to rename\n", + " --new-n new-name"); + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "\n"); +} + +static void +luUpgradeMerge(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "upgrade-merge")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Upgrade an old database and merge it into a new one\n", + "--upgrade-merge"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Cert database directory to merge into (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix of the target database\n", + " -P dbprefix"); + FPS "%-20s Specify the password file for the target database\n", + " -f pwfile"); + FPS "%-20s \n%-20s Cert database directory to upgrade from\n", + " --source-dir certdir", ""); + FPS "%-20s \n%-20s Cert & Key database prefix of the upgrade database\n", + " --source-prefix dbprefix", ""); + FPS "%-20s \n%-20s Unique identifier for the upgrade database\n", + " --upgrade-id uniqueID", ""); + FPS "%-20s \n%-20s Name of the token while it is in upgrade state\n", + " --upgrade-token-name name", ""); + FPS "%-20s Specify the password file for the upgrade database\n", + " -@ pwfile"); + FPS "\n"); +} + +static void +luMerge(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "merge")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Merge source database into the target database\n", + "--merge"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Cert database directory of target (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix of the target database\n", + " -P dbprefix"); + FPS "%-20s Specify the password file for the target database\n", + " -f pwfile"); + FPS "%-20s \n%-20s Cert database directory of the source database\n", + " --source-dir certdir", ""); + FPS "%-20s \n%-20s Cert & Key database prefix of the source database\n", + " --source-prefix dbprefix", ""); + FPS "%-20s Specify the password file for the source database\n", + " -@ pwfile"); + FPS "\n"); +} + +static void +luS(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "S")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Make a certificate and add to database\n", + "-S"); + if (ul == usage_selected && !is_my_command) + return; + FPS "%-20s Specify the nickname of the cert\n", + " -n key-name"); + FPS "%-20s Specify the subject name (using RFC1485)\n", + " -s subject"); + FPS "%-20s The nickname of the issuer cert\n", + " -c issuer-name"); + FPS "%-20s Set the certificate trust attributes (see -A above)\n", + " -t trustargs"); + FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n", + " -k key-type-or-id"); + FPS "%-20s Name of token in which to generate key (default is internal)\n", + " -h token-name"); + FPS "%-20s Key size in bits, RSA keys only (min %d, max %d, default %d)\n", + " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS); + FPS "%-20s Create a certificate restricted to RSA-PSS (rsa only)\n", + " --pss"); + FPS "%-20s Name of file containing PQG parameters (dsa only)\n", + " -q pqgfile"); + FPS "%-20s Elliptic curve name (ec only)\n", + " -q curve-name"); + FPS "%-20s See the \"-G\" option for a full list of supported names.\n", + ""); + FPS "%-20s Self sign\n", + " -x"); + FPS "%-20s Sign the certificate with RSA-PSS (the issuer key must be rsa)\n", + " --pss-sign"); + FPS "%-20s Cert serial number\n", + " -m serial-number"); + FPS "%-20s Time Warp\n", + " -w warp-months"); + FPS "%-20s Months valid (default is 3)\n", + " -v months-valid"); + FPS "%-20s Specify the password file\n", + " -f pwfile"); + FPS "%-20s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-20s Cert & Key database prefix\n", + " -P dbprefix"); + FPS "%-20s Specify the contact phone number (\"123-456-7890\")\n", + " -p phone"); + FPS "%-20s \n" + "%-20s Specify the hash algorithm to use. Possible keywords:\n" + "%-20s \"MD2\", \"MD4\", \"MD5\", \"SHA1\", \"SHA224\",\n" + "%-20s \"SHA256\", \"SHA384\", \"SHA512\"\n", + " -Z hashAlg", "", "", ""); + FPS "%-20s Create key usage extension\n", + " -1 "); + FPS "%-20s Create basic constraint extension\n", + " -2 "); + FPS "%-20s Create authority key ID extension\n", + " -3 "); + FPS "%-20s Create crl distribution point extension\n", + " -4 "); + FPS "%-20s Create netscape cert type extension\n", + " -5 "); + FPS "%-20s Create extended key usage extension\n", + " -6 "); + FPS "%-20s Create an email subject alt name extension\n", + " -7 emailAddrs "); + FPS "%-20s Create a DNS subject alt name extension\n", + " -8 DNS-names"); + FPS "%-20s Create an Authority Information Access extension\n", + " --extAIA "); + FPS "%-20s Create a Subject Information Access extension\n", + " --extSIA "); + FPS "%-20s Create a Certificate Policies extension\n", + " --extCP "); + FPS "%-20s Create a Policy Mappings extension\n", + " --extPM "); + FPS "%-20s Create a Policy Constraints extension\n", + " --extPC "); + FPS "%-20s Create an Inhibit Any Policy extension\n", + " --extIA "); + FPS "%-20s Create a subject key ID extension\n", + " --extSKID "); + FPS "%-20s \n", + " See -G for available key flag options"); + FPS "%-20s Create a name constraints extension\n", + " --extNC "); + FPS "%-20s \n" + "%-20s Create a Subject Alt Name extension with one or multiple names\n", + " --extSAN type:name[,type:name]...", ""); + FPS "%-20s - type: directory, dn, dns, edi, ediparty, email, ip, ipaddr,\n", ""); + FPS "%-20s other, registerid, rfc822, uri, x400, x400addr\n", ""); + FPS "%-20s \n" + "%-20s Add one or multiple extensions that certutil cannot encode yet,\n" + "%-20s by loading their encodings from external files.\n", + " --extGeneric OID:critical-flag:filename[,OID:critical-flag:filename]...", "", ""); + FPS "%-20s - OID (example): 1.2.3.4\n", ""); + FPS "%-20s - critical-flag: critical or not-critical\n", ""); + FPS "%-20s - filename: full path to a file containing an encoded extension\n", ""); + FPS "\n"); +} + +static void +luBuildFlags(enum usage_level ul, const char *command) +{ + int is_my_command = (command && 0 == strcmp(command, "build-flags")); + if (ul == usage_all || !command || is_my_command) + FPS "%-15s Print enabled build flags relevant for NSS test execution\n", + "--build-flags"); + if (ul == usage_selected && !is_my_command) + return; + FPS "\n"); +} + +static void +LongUsage(enum usage_level ul, const char *command) +{ + luA(ul, command); + luB(ul, command); + luE(ul, command); + luC(ul, command); + luG(ul, command); + luD(ul, command); + luRename(ul, command); + luF(ul, command); + luU(ul, command); + luK(ul, command); + luL(ul, command); + luBuildFlags(ul, command); + luM(ul, command); + luN(ul, command); + luT(ul, command); + luO(ul, command); + luR(ul, command); + luV(ul, command); + luW(ul, command); + luUpgradeMerge(ul, command); + luMerge(ul, command); + luS(ul, command); +#undef FPS +} + +static void +Usage() +{ + PR_fprintf(PR_STDERR, + "%s - Utility to manipulate NSS certificate databases\n\n" + "Usage: %s <command> -d <database-directory> <options>\n\n" + "Valid commands:\n", + progName, progName); + LongUsage(usage_selected, NULL); + PR_fprintf(PR_STDERR, "\n" + "%s -H <command> : Print available options for the given command\n" + "%s -H : Print complete help output of all commands and options\n" + "%s --syntax : Print a short summary of all commands and options\n", + progName, progName, progName); + exit(1); +} + +static CERTCertificate * +MakeV1Cert(CERTCertDBHandle *handle, + CERTCertificateRequest *req, + char *issuerNickName, + PRBool selfsign, + unsigned int serialNumber, + int warpmonths, + int validityMonths) +{ + CERTCertificate *issuerCert = NULL; + CERTValidity *validity; + CERTCertificate *cert = NULL; + PRExplodedTime printableTime; + PRTime now, after; + + if (!selfsign) { + issuerCert = CERT_FindCertByNicknameOrEmailAddr(handle, issuerNickName); + if (!issuerCert) { + SECU_PrintError(progName, "could not find certificate named \"%s\"", + issuerNickName); + return NULL; + } + } + + now = PR_Now(); + PR_ExplodeTime(now, PR_GMTParameters, &printableTime); + if (warpmonths) { + printableTime.tm_month += warpmonths; + now = PR_ImplodeTime(&printableTime); + PR_ExplodeTime(now, PR_GMTParameters, &printableTime); + } + printableTime.tm_month += validityMonths; + after = PR_ImplodeTime(&printableTime); + + /* note that the time is now in micro-second unit */ + validity = CERT_CreateValidity(now, after); + if (validity) { + cert = CERT_CreateCertificate(serialNumber, + (selfsign ? &req->subject + : &issuerCert->subject), + validity, req); + + CERT_DestroyValidity(validity); + } + if (issuerCert) { + CERT_DestroyCertificate(issuerCert); + } + + return (cert); +} + +static SECStatus +SetSignatureAlgorithm(PLArenaPool *arena, + SECAlgorithmID *signAlg, + SECAlgorithmID *spkiAlg, + SECOidTag hashAlgTag, + SECKEYPrivateKey *privKey, + PRBool pssSign) +{ + SECStatus rv; + + if (pssSign || + SECOID_GetAlgorithmTag(spkiAlg) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + SECItem *srcParams; + SECItem *params; + + if (SECOID_GetAlgorithmTag(spkiAlg) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + srcParams = &spkiAlg->parameters; + } else { + /* If the issuer's public key is RSA, the parameter field + * of the SPKI should be NULL, which can't be used as a + * basis of RSA-PSS parameters. */ + srcParams = NULL; + } + params = SEC_CreateSignatureAlgorithmParameters(arena, + NULL, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, + hashAlgTag, + srcParams, + privKey); + if (!params) { + SECU_PrintError(progName, "Could not create RSA-PSS parameters"); + return SECFailure; + } + rv = SECOID_SetAlgorithmID(arena, signAlg, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, + params); + if (rv != SECSuccess) { + SECU_PrintError(progName, "Could not set signature algorithm id."); + return rv; + } + } else { + KeyType keyType = SECKEY_GetPrivateKeyType(privKey); + SECOidTag algID; + + algID = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); + if (algID == SEC_OID_UNKNOWN) { + SECU_PrintError(progName, "Unknown key or hash type for issuer."); + return SECFailure; + } + rv = SECOID_SetAlgorithmID(arena, signAlg, algID, 0); + if (rv != SECSuccess) { + SECU_PrintError(progName, "Could not set signature algorithm id."); + return rv; + } + } + return SECSuccess; +} + +static SECStatus +SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, + SECOidTag hashAlgTag, + SECKEYPrivateKey *privKey, char *issuerNickName, + int certVersion, PRBool pssSign, void *pwarg) +{ + SECItem der; + SECKEYPrivateKey *caPrivateKey = NULL; + SECStatus rv; + PLArenaPool *arena; + CERTCertificate *issuer; + void *dummy; + + arena = cert->arena; + + if (selfsign) { + issuer = cert; + } else { + issuer = PK11_FindCertFromNickname(issuerNickName, pwarg); + if ((CERTCertificate *)NULL == issuer) { + SECU_PrintError(progName, "unable to find issuer with nickname %s", + issuerNickName); + rv = SECFailure; + goto done; + } + privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg); + if (caPrivateKey == NULL) { + SECU_PrintError(progName, "unable to retrieve key %s", issuerNickName); + rv = SECFailure; + CERT_DestroyCertificate(issuer); + goto done; + } + } + + if (pssSign && + (SECKEY_GetPrivateKeyType(privKey) != rsaKey && + SECKEY_GetPrivateKeyType(privKey) != rsaPssKey)) { + SECU_PrintError(progName, "unable to create RSA-PSS signature with key %s", + issuerNickName); + rv = SECFailure; + if (!selfsign) { + CERT_DestroyCertificate(issuer); + } + goto done; + } + + rv = SetSignatureAlgorithm(arena, + &cert->signature, + &issuer->subjectPublicKeyInfo.algorithm, + hashAlgTag, + privKey, + pssSign); + if (!selfsign) { + CERT_DestroyCertificate(issuer); + } + if (rv != SECSuccess) { + goto done; + } + + switch (certVersion) { + case (SEC_CERTIFICATE_VERSION_1): + /* The initial version for x509 certificates is version one + * and this default value must be an implicit DER encoding. */ + cert->version.data = NULL; + cert->version.len = 0; + break; + case (SEC_CERTIFICATE_VERSION_2): + case (SEC_CERTIFICATE_VERSION_3): + case 3: /* unspecified format (would be version 4 certificate). */ + *(cert->version.data) = certVersion; + cert->version.len = 1; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + rv = SECFailure; + goto done; + } + + der.len = 0; + der.data = NULL; + dummy = SEC_ASN1EncodeItem(arena, &der, cert, + SEC_ASN1_GET(CERT_CertificateTemplate)); + if (!dummy) { + fprintf(stderr, "Could not encode certificate.\n"); + rv = SECFailure; + goto done; + } + + rv = SEC_DerSignDataWithAlgorithmID(arena, &cert->derCert, der.data, der.len, + privKey, &cert->signature); + if (rv != SECSuccess) { + fprintf(stderr, "Could not sign encoded certificate data.\n"); + /* result allocated out of the arena, it will be freed + * when the arena is freed */ + goto done; + } +done: + if (caPrivateKey) { + SECKEY_DestroyPrivateKey(caPrivateKey); + } + return rv; +} + +static SECStatus +CreateCert( + CERTCertDBHandle *handle, + PK11SlotInfo *slot, + char *issuerNickName, + const SECItem *certReqDER, + SECKEYPrivateKey **selfsignprivkey, + void *pwarg, + SECOidTag hashAlgTag, + unsigned int serialNumber, + int warpmonths, + int validityMonths, + const char *emailAddrs, + const char *dnsNames, + PRBool ascii, + PRBool selfsign, + certutilExtnList extnList, + const char *extGeneric, + int certVersion, + PRBool pssSign, + SECItem *certDER) +{ + void *extHandle = NULL; + CERTCertificate *subjectCert = NULL; + CERTCertificateRequest *certReq = NULL; + SECStatus rv = SECSuccess; + CERTCertExtension **CRexts; + + do { + /* Create a certrequest object from the input cert request der */ + certReq = GetCertRequest(certReqDER, pwarg); + if (certReq == NULL) { + GEN_BREAK(SECFailure) + } + + subjectCert = MakeV1Cert(handle, certReq, issuerNickName, selfsign, + serialNumber, warpmonths, validityMonths); + if (subjectCert == NULL) { + GEN_BREAK(SECFailure) + } + + extHandle = CERT_StartCertExtensions(subjectCert); + if (extHandle == NULL) { + GEN_BREAK(SECFailure) + } + + rv = AddExtensions(extHandle, emailAddrs, dnsNames, extnList, extGeneric); + if (rv != SECSuccess) { + GEN_BREAK(SECFailure) + } + + if (certReq->attributes != NULL && + certReq->attributes[0] != NULL && + certReq->attributes[0]->attrType.data != NULL && + certReq->attributes[0]->attrType.len > 0 && + SECOID_FindOIDTag(&certReq->attributes[0]->attrType) == + SEC_OID_PKCS9_EXTENSION_REQUEST) { + rv = CERT_GetCertificateRequestExtensions(certReq, &CRexts); + if (rv != SECSuccess) + break; + rv = CERT_MergeExtensions(extHandle, CRexts); + if (rv != SECSuccess) + break; + } + + CERT_FinishExtensions(extHandle); + extHandle = NULL; + + /* self-signing a cert request, find the private key */ + if (selfsign && *selfsignprivkey == NULL) { + *selfsignprivkey = PK11_FindKeyByDERCert(slot, subjectCert, pwarg); + if (!*selfsignprivkey) { + fprintf(stderr, "Failed to locate private key.\n"); + rv = SECFailure; + break; + } + } + + rv = SignCert(handle, subjectCert, selfsign, hashAlgTag, + *selfsignprivkey, issuerNickName, + certVersion, pssSign, pwarg); + if (rv != SECSuccess) + break; + + rv = SECFailure; + if (ascii) { + char *asciiDER = BTOA_DataToAscii(subjectCert->derCert.data, + subjectCert->derCert.len); + if (asciiDER) { + char *wrapped = PR_smprintf("%s\n%s\n%s\n", + NS_CERT_HEADER, + asciiDER, + NS_CERT_TRAILER); + if (wrapped) { + PRUint32 wrappedLen = PL_strlen(wrapped); + if (SECITEM_AllocItem(NULL, certDER, wrappedLen)) { + PORT_Memcpy(certDER->data, wrapped, wrappedLen); + rv = SECSuccess; + } + PR_smprintf_free(wrapped); + } + PORT_Free(asciiDER); + } + } else { + rv = SECITEM_CopyItem(NULL, certDER, &subjectCert->derCert); + } + } while (0); + if (extHandle) { + CERT_FinishExtensions(extHandle); + } + CERT_DestroyCertificateRequest(certReq); + CERT_DestroyCertificate(subjectCert); + if (rv != SECSuccess) { + PRErrorCode perr = PR_GetError(); + fprintf(stderr, "%s: unable to create cert (%s)\n", progName, + SECU_Strerror(perr)); + } + return (rv); +} + +/* + * map a class to a user presentable string + */ +static const char *objClassArray[] = { + "Data", + "Certificate", + "Public Key", + "Private Key", + "Secret Key", + "Hardware Feature", + "Domain Parameters", + "Mechanism" +}; + +static const char *objNSSClassArray[] = { + "CKO_NSS", + "Crl", + "SMIME Record", + "Trust", + "Builtin Root List" +}; + +const char * +getObjectClass(CK_ULONG classType) +{ + static char buf[sizeof(CK_ULONG) * 2 + 3]; + + if (classType <= CKO_MECHANISM) { + return objClassArray[classType]; + } + if (classType >= CKO_NSS && classType <= CKO_NSS_BUILTIN_ROOT_LIST) { + return objNSSClassArray[classType - CKO_NSS]; + } + snprintf(buf, sizeof(buf), "0x%lx", classType); + return buf; +} + +typedef struct { + char *name; + int nameSize; + CK_ULONG value; +} flagArray; + +#define NAME_SIZE(x) #x, sizeof(#x) - 1 + +flagArray opFlagsArray[] = { + { NAME_SIZE(encrypt), CKF_ENCRYPT }, + { NAME_SIZE(decrypt), CKF_DECRYPT }, + { NAME_SIZE(sign), CKF_SIGN }, + { NAME_SIZE(sign_recover), CKF_SIGN_RECOVER }, + { NAME_SIZE(verify), CKF_VERIFY }, + { NAME_SIZE(verify_recover), CKF_VERIFY_RECOVER }, + { NAME_SIZE(wrap), CKF_WRAP }, + { NAME_SIZE(unwrap), CKF_UNWRAP }, + { NAME_SIZE(derive), CKF_DERIVE } +}; + +int opFlagsCount = PR_ARRAY_SIZE(opFlagsArray); + +flagArray attrFlagsArray[] = { + { NAME_SIZE(token), PK11_ATTR_TOKEN }, + { NAME_SIZE(session), PK11_ATTR_SESSION }, + { NAME_SIZE(private), PK11_ATTR_PRIVATE }, + { NAME_SIZE(public), PK11_ATTR_PUBLIC }, + { NAME_SIZE(modifiable), PK11_ATTR_MODIFIABLE }, + { NAME_SIZE(unmodifiable), PK11_ATTR_UNMODIFIABLE }, + { NAME_SIZE(sensitive), PK11_ATTR_SENSITIVE }, + { NAME_SIZE(insensitive), PK11_ATTR_INSENSITIVE }, + { NAME_SIZE(extractable), PK11_ATTR_EXTRACTABLE }, + { NAME_SIZE(unextractable), PK11_ATTR_UNEXTRACTABLE } +}; + +int attrFlagsCount = PR_ARRAY_SIZE(attrFlagsArray); + +#define MAX_STRING 30 +CK_ULONG +GetFlags(char *flagsString, flagArray *flags, int count) +{ + CK_ULONG flagsValue = strtol(flagsString, NULL, 0); + int i; + + if ((flagsValue != 0) || (*flagsString == 0)) { + return flagsValue; + } + while (*flagsString) { + for (i = 0; i < count; i++) { + if (strncmp(flagsString, flags[i].name, flags[i].nameSize) == + 0) { + flagsValue |= flags[i].value; + flagsString += flags[i].nameSize; + if (*flagsString != 0) { + flagsString++; + } + break; + } + } + if (i == count) { + char name[MAX_STRING]; + char *tok; + + strncpy(name, flagsString, MAX_STRING); + name[MAX_STRING - 1] = 0; + tok = strchr(name, ','); + if (tok) { + *tok = 0; + } + fprintf(stderr, "Unknown flag (%s)\n", name); + tok = strchr(flagsString, ','); + if (tok == NULL) { + break; + } + flagsString = tok + 1; + } + } + return flagsValue; +} + +CK_FLAGS +GetOpFlags(char *flags) +{ + return GetFlags(flags, opFlagsArray, opFlagsCount); +} + +PK11AttrFlags +GetAttrFlags(char *flags) +{ + return GetFlags(flags, attrFlagsArray, attrFlagsCount); +} + +char * +mkNickname(unsigned char *data, int len) +{ + char *nick = PORT_Alloc(len + 1); + if (!nick) { + return nick; + } + PORT_Memcpy(nick, data, len); + nick[len] = 0; + return nick; +} + +/* + * dump a PK11_MergeTokens error log to the console + */ +void +DumpMergeLog(const char *progname, PK11MergeLog *log) +{ + PK11MergeLogNode *node; + + for (node = log->head; node; node = node->next) { + SECItem attrItem; + char *nickname = NULL; + const char *objectClass = NULL; + SECStatus rv; + + attrItem.data = NULL; + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, node->object, + CKA_LABEL, &attrItem); + if (rv == SECSuccess) { + nickname = mkNickname(attrItem.data, attrItem.len); + PORT_Free(attrItem.data); + } + attrItem.data = NULL; + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, node->object, + CKA_CLASS, &attrItem); + if (rv == SECSuccess) { + if (attrItem.len == sizeof(CK_ULONG)) { + objectClass = getObjectClass(*(CK_ULONG *)attrItem.data); + } + PORT_Free(attrItem.data); + } + + fprintf(stderr, "%s: Could not merge object %s (type %s): %s\n", + progName, + nickname ? nickname : "unnamed", + objectClass ? objectClass : "unknown", + SECU_Strerror(node->error)); + + if (nickname) { + PORT_Free(nickname); + } + } +} + +/* Certutil commands */ +enum { + cmd_AddCert = 0, + cmd_CreateNewCert, + cmd_DeleteCert, + cmd_AddEmailCert, + cmd_DeleteKey, + cmd_GenKeyPair, + cmd_PrintHelp, + cmd_PrintSyntax, + cmd_ListKeys, + cmd_ListCerts, + cmd_ModifyCertTrust, + cmd_NewDBs, + cmd_DumpChain, + cmd_CertReq, + cmd_CreateAndAddCert, + cmd_TokenReset, + cmd_ListModules, + cmd_CheckCertValidity, + cmd_ChangePassword, + cmd_Version, + cmd_Batch, + cmd_Merge, + cmd_UpgradeMerge, /* test only */ + cmd_Rename, + cmd_BuildFlags, + max_cmd +}; + +/* Certutil options */ +enum certutilOpts { + opt_SSOPass = 0, + opt_AddKeyUsageExt, + opt_AddBasicConstraintExt, + opt_AddAuthorityKeyIDExt, + opt_AddCRLDistPtsExt, + opt_AddNSCertTypeExt, + opt_AddExtKeyUsageExt, + opt_ExtendedEmailAddrs, + opt_ExtendedDNSNames, + opt_ASCIIForIO, + opt_ValidityTime, + opt_IssuerName, + opt_CertDir, + opt_VerifySig, + opt_PasswordFile, + opt_KeySize, + opt_TokenName, + opt_InputFile, + opt_Emailaddress, + opt_KeyIndex, + opt_KeyType, + opt_DetailedInfo, + opt_SerialNumber, + opt_Nickname, + opt_OutputFile, + opt_PhoneNumber, + opt_DBPrefix, + opt_PQGFile, + opt_BinaryDER, + opt_Subject, + opt_Trust, + opt_Usage, + opt_Validity, + opt_OffsetMonths, + opt_SelfSign, + opt_RW, + opt_Exponent, + opt_NoiseFile, + opt_Hash, + opt_NewPasswordFile, + opt_AddAuthInfoAccExt, + opt_AddSubjInfoAccExt, + opt_AddCertPoliciesExt, + opt_AddPolicyMapExt, + opt_AddPolicyConstrExt, + opt_AddInhibAnyExt, + opt_AddNameConstraintsExt, + opt_AddSubjectKeyIDExt, + opt_AddCmdKeyUsageExt, + opt_AddCmdNSCertTypeExt, + opt_AddCmdExtKeyUsageExt, + opt_SourceDir, + opt_SourcePrefix, + opt_UpgradeID, + opt_UpgradeTokenName, + opt_KeyOpFlagsOn, + opt_KeyOpFlagsOff, + opt_KeyAttrFlags, + opt_EmptyPassword, + opt_CertVersion, + opt_AddSubjectAltNameExt, + opt_DumpExtensionValue, + opt_GenericExtensions, + opt_NewNickname, + opt_Pss, + opt_PssSign, + opt_SimpleSelfSigned, + opt_Help +}; + +static const secuCommandFlag commands_init[] = { + { /* cmd_AddCert */ 'A', PR_FALSE, 0, PR_FALSE }, + { /* cmd_CreateNewCert */ 'C', PR_FALSE, 0, PR_FALSE }, + { /* cmd_DeleteCert */ 'D', PR_FALSE, 0, PR_FALSE }, + { /* cmd_AddEmailCert */ 'E', PR_FALSE, 0, PR_FALSE }, + { /* cmd_DeleteKey */ 'F', PR_FALSE, 0, PR_FALSE }, + { /* cmd_GenKeyPair */ 'G', PR_FALSE, 0, PR_FALSE }, + { /* cmd_PrintHelp */ 'H', PR_FALSE, 0, PR_FALSE, "help" }, + { /* cmd_PrintSyntax */ 0, PR_FALSE, 0, PR_FALSE, + "syntax" }, + { /* cmd_ListKeys */ 'K', PR_FALSE, 0, PR_FALSE }, + { /* cmd_ListCerts */ 'L', PR_FALSE, 0, PR_FALSE }, + { /* cmd_ModifyCertTrust */ 'M', PR_FALSE, 0, PR_FALSE }, + { /* cmd_NewDBs */ 'N', PR_FALSE, 0, PR_FALSE }, + { /* cmd_DumpChain */ 'O', PR_FALSE, 0, PR_FALSE }, + { /* cmd_CertReq */ 'R', PR_FALSE, 0, PR_FALSE }, + { /* cmd_CreateAndAddCert */ 'S', PR_FALSE, 0, PR_FALSE }, + { /* cmd_TokenReset */ 'T', PR_FALSE, 0, PR_FALSE }, + { /* cmd_ListModules */ 'U', PR_FALSE, 0, PR_FALSE }, + { /* cmd_CheckCertValidity */ 'V', PR_FALSE, 0, PR_FALSE }, + { /* cmd_ChangePassword */ 'W', PR_FALSE, 0, PR_FALSE }, + { /* cmd_Version */ 'Y', PR_FALSE, 0, PR_FALSE }, + { /* cmd_Batch */ 'B', PR_FALSE, 0, PR_FALSE }, + { /* cmd_Merge */ 0, PR_FALSE, 0, PR_FALSE, "merge" }, + { /* cmd_UpgradeMerge */ 0, PR_FALSE, 0, PR_FALSE, + "upgrade-merge" }, + { /* cmd_Rename */ 0, PR_FALSE, 0, PR_FALSE, + "rename" }, + { /* cmd_BuildFlags */ 0, PR_FALSE, 0, PR_FALSE, + "build-flags" } +}; +#define NUM_COMMANDS ((sizeof commands_init) / (sizeof commands_init[0])) + +static const secuCommandFlag options_init[] = { + { /* opt_SSOPass */ '0', PR_TRUE, 0, PR_FALSE }, + { /* opt_AddKeyUsageExt */ '1', PR_FALSE, 0, PR_FALSE }, + { /* opt_AddBasicConstraintExt*/ '2', PR_FALSE, 0, PR_FALSE }, + { /* opt_AddAuthorityKeyIDExt*/ '3', PR_FALSE, 0, PR_FALSE }, + { /* opt_AddCRLDistPtsExt */ '4', PR_FALSE, 0, PR_FALSE }, + { /* opt_AddNSCertTypeExt */ '5', PR_FALSE, 0, PR_FALSE }, + { /* opt_AddExtKeyUsageExt */ '6', PR_FALSE, 0, PR_FALSE }, + { /* opt_ExtendedEmailAddrs */ '7', PR_TRUE, 0, PR_FALSE }, + { /* opt_ExtendedDNSNames */ '8', PR_TRUE, 0, PR_FALSE }, + { /* opt_ASCIIForIO */ 'a', PR_FALSE, 0, PR_FALSE }, + { /* opt_ValidityTime */ 'b', PR_TRUE, 0, PR_FALSE }, + { /* opt_IssuerName */ 'c', PR_TRUE, 0, PR_FALSE }, + { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE }, + { /* opt_VerifySig */ 'e', PR_FALSE, 0, PR_FALSE }, + { /* opt_PasswordFile */ 'f', PR_TRUE, 0, PR_FALSE }, + { /* opt_KeySize */ 'g', PR_TRUE, 0, PR_FALSE }, + { /* opt_TokenName */ 'h', PR_TRUE, 0, PR_FALSE }, + { /* opt_InputFile */ 'i', PR_TRUE, 0, PR_FALSE }, + { /* opt_Emailaddress */ 0, PR_TRUE, 0, PR_FALSE, "email" }, + { /* opt_KeyIndex */ 'j', PR_TRUE, 0, PR_FALSE }, + { /* opt_KeyType */ 'k', PR_TRUE, 0, PR_FALSE }, + { /* opt_DetailedInfo */ 'l', PR_FALSE, 0, PR_FALSE }, + { /* opt_SerialNumber */ 'm', PR_TRUE, 0, PR_FALSE }, + { /* opt_Nickname */ 'n', PR_TRUE, 0, PR_FALSE }, + { /* opt_OutputFile */ 'o', PR_TRUE, 0, PR_FALSE }, + { /* opt_PhoneNumber */ 'p', PR_TRUE, 0, PR_FALSE }, + { /* opt_DBPrefix */ 'P', PR_TRUE, 0, PR_FALSE }, + { /* opt_PQGFile */ 'q', PR_TRUE, 0, PR_FALSE }, + { /* opt_BinaryDER */ 'r', PR_FALSE, 0, PR_FALSE }, + { /* opt_Subject */ 's', PR_TRUE, 0, PR_FALSE }, + { /* opt_Trust */ 't', PR_TRUE, 0, PR_FALSE }, + { /* opt_Usage */ 'u', PR_TRUE, 0, PR_FALSE }, + { /* opt_Validity */ 'v', PR_TRUE, 0, PR_FALSE }, + { /* opt_OffsetMonths */ 'w', PR_TRUE, 0, PR_FALSE }, + { /* opt_SelfSign */ 'x', PR_FALSE, 0, PR_FALSE }, + { /* opt_RW */ 'X', PR_FALSE, 0, PR_FALSE }, + { /* opt_Exponent */ 'y', PR_TRUE, 0, PR_FALSE }, + { /* opt_NoiseFile */ 'z', PR_TRUE, 0, PR_FALSE }, + { /* opt_Hash */ 'Z', PR_TRUE, 0, PR_FALSE }, + { /* opt_NewPasswordFile */ '@', PR_TRUE, 0, PR_FALSE }, + { /* opt_AddAuthInfoAccExt */ 0, PR_FALSE, 0, PR_FALSE, "extAIA" }, + { /* opt_AddSubjInfoAccExt */ 0, PR_FALSE, 0, PR_FALSE, "extSIA" }, + { /* opt_AddCertPoliciesExt */ 0, PR_FALSE, 0, PR_FALSE, "extCP" }, + { /* opt_AddPolicyMapExt */ 0, PR_FALSE, 0, PR_FALSE, "extPM" }, + { /* opt_AddPolicyConstrExt */ 0, PR_FALSE, 0, PR_FALSE, "extPC" }, + { /* opt_AddInhibAnyExt */ 0, PR_FALSE, 0, PR_FALSE, "extIA" }, + { /* opt_AddNameConstraintsExt*/ 0, PR_FALSE, 0, PR_FALSE, "extNC" }, + { /* opt_AddSubjectKeyIDExt */ 0, PR_FALSE, 0, PR_FALSE, + "extSKID" }, + { /* opt_AddCmdKeyUsageExt */ 0, PR_TRUE, 0, PR_FALSE, + "keyUsage" }, + { /* opt_AddCmdNSCertTypeExt */ 0, PR_TRUE, 0, PR_FALSE, + "nsCertType" }, + { /* opt_AddCmdExtKeyUsageExt*/ 0, PR_TRUE, 0, PR_FALSE, + "extKeyUsage" }, + + { /* opt_SourceDir */ 0, PR_TRUE, 0, PR_FALSE, + "source-dir" }, + { /* opt_SourcePrefix */ 0, PR_TRUE, 0, PR_FALSE, + "source-prefix" }, + { /* opt_UpgradeID */ 0, PR_TRUE, 0, PR_FALSE, + "upgrade-id" }, + { /* opt_UpgradeTokenName */ 0, PR_TRUE, 0, PR_FALSE, + "upgrade-token-name" }, + { /* opt_KeyOpFlagsOn */ 0, PR_TRUE, 0, PR_FALSE, + "keyOpFlagsOn" }, + { /* opt_KeyOpFlagsOff */ 0, PR_TRUE, 0, PR_FALSE, + "keyOpFlagsOff" }, + { /* opt_KeyAttrFlags */ 0, PR_TRUE, 0, PR_FALSE, + "keyAttrFlags" }, + { /* opt_EmptyPassword */ 0, PR_FALSE, 0, PR_FALSE, + "empty-password" }, + { /* opt_CertVersion */ 0, PR_TRUE, 0, PR_FALSE, + "certVersion" }, + { /* opt_AddSubjectAltExt */ 0, PR_TRUE, 0, PR_FALSE, "extSAN" }, + { /* opt_DumpExtensionValue */ 0, PR_TRUE, 0, PR_FALSE, + "dump-ext-val" }, + { /* opt_GenericExtensions */ 0, PR_TRUE, 0, PR_FALSE, + "extGeneric" }, + { /* opt_NewNickname */ 0, PR_TRUE, 0, PR_FALSE, + "new-n" }, + { /* opt_Pss */ 0, PR_FALSE, 0, PR_FALSE, + "pss" }, + { /* opt_PssSign */ 0, PR_FALSE, 0, PR_FALSE, + "pss-sign" }, + { /* opt_SimpleSelfSigned */ 0, PR_FALSE, 0, PR_FALSE, + "simple-self-signed" }, +}; +#define NUM_OPTIONS ((sizeof options_init) / (sizeof options_init[0])) + +static secuCommandFlag certutil_commands[NUM_COMMANDS]; +static secuCommandFlag certutil_options[NUM_OPTIONS]; + +static const secuCommand certutil = { + NUM_COMMANDS, + NUM_OPTIONS, + certutil_commands, + certutil_options +}; + +static certutilExtnList certutil_extns; + +static int +certutil_main(int argc, char **argv, PRBool initialize) +{ + CERTCertDBHandle *certHandle; + PK11SlotInfo *slot = NULL; + CERTName *subject = 0; + PRFileDesc *inFile = PR_STDIN; + PRFileDesc *outFile = PR_STDOUT; + SECItem certReqDER = { siBuffer, NULL, 0 }; + SECItem certDER = { siBuffer, NULL, 0 }; + const char *slotname = "internal"; + const char *certPrefix = ""; + char *sourceDir = ""; + const char *srcCertPrefix = ""; + char *upgradeID = ""; + char *upgradeTokenName = ""; + KeyType keytype = rsaKey; + char *name = NULL; + char *newName = NULL; + char *email = NULL; + char *keysource = NULL; + SECOidTag hashAlgTag = SEC_OID_UNKNOWN; + int keysize = DEFAULT_KEY_BITS; + int publicExponent = 0x010001; + int certVersion = SEC_CERTIFICATE_VERSION_3; + unsigned int serialNumber = 0; + int warpmonths = 0; + int validityMonths = 3; + int commandsEntered = 0; + char commandToRun = '\0'; + secuPWData pwdata = { PW_NONE, 0 }; + secuPWData pwdata2 = { PW_NONE, 0 }; + PRBool readOnly = PR_FALSE; + PRBool initialized = PR_FALSE; + CK_FLAGS keyOpFlagsOn = 0; + CK_FLAGS keyOpFlagsOff = 0; + PK11AttrFlags keyAttrFlags = + PK11_ATTR_TOKEN | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE; + + SECKEYPrivateKey *privkey = NULL; + SECKEYPublicKey *pubkey = NULL; + + int i; + SECStatus rv; + + progName = PORT_Strrchr(argv[0], '/'); + progName = progName ? progName + 1 : argv[0]; + memcpy(certutil_commands, commands_init, sizeof commands_init); + memcpy(certutil_options, options_init, sizeof options_init); + + rv = SECU_ParseCommandLine(argc, argv, progName, &certutil); + + if (rv != SECSuccess) + Usage(); + + if (certutil.commands[cmd_PrintSyntax].activated) { + PrintSyntax(); + } + + if (certutil.commands[cmd_PrintHelp].activated) { + char buf[2]; + const char *command = NULL; + for (i = 0; i < max_cmd; i++) { + if (i == cmd_PrintHelp) + continue; + if (certutil.commands[i].activated) { + if (certutil.commands[i].flag) { + buf[0] = certutil.commands[i].flag; + buf[1] = 0; + command = buf; + } else { + command = certutil.commands[i].longform; + } + break; + } + } + LongUsage((command ? usage_selected : usage_all), command); + exit(1); + } + + if (certutil.commands[cmd_BuildFlags].activated) { + PrintBuildFlags(); + } + + if (certutil.options[opt_PasswordFile].arg) { + pwdata.source = PW_FROMFILE; + pwdata.data = certutil.options[opt_PasswordFile].arg; + } + if (certutil.options[opt_NewPasswordFile].arg) { + pwdata2.source = PW_FROMFILE; + pwdata2.data = certutil.options[opt_NewPasswordFile].arg; + } + + if (certutil.options[opt_CertDir].activated) + SECU_ConfigDirectory(certutil.options[opt_CertDir].arg); + + if (certutil.options[opt_SourceDir].activated) + sourceDir = certutil.options[opt_SourceDir].arg; + + if (certutil.options[opt_UpgradeID].activated) + upgradeID = certutil.options[opt_UpgradeID].arg; + + if (certutil.options[opt_UpgradeTokenName].activated) + upgradeTokenName = certutil.options[opt_UpgradeTokenName].arg; + + if (certutil.options[opt_KeySize].activated) { + keysize = PORT_Atoi(certutil.options[opt_KeySize].arg); + if ((keysize < MIN_KEY_BITS) || (keysize > MAX_KEY_BITS)) { + PR_fprintf(PR_STDERR, + "%s -g: Keysize must be between %d and %d.\n", + progName, MIN_KEY_BITS, MAX_KEY_BITS); + return 255; + } + if (keytype == ecKey) { + PR_fprintf(PR_STDERR, "%s -g: Not for ec keys.\n", progName); + return 255; + } + } + + /* -h specify token name */ + if (certutil.options[opt_TokenName].activated) { + if (PL_strcmp(certutil.options[opt_TokenName].arg, "all") == 0) + slotname = NULL; + else + slotname = certutil.options[opt_TokenName].arg; + } + + /* -Z hash type */ + if (certutil.options[opt_Hash].activated) { + char *arg = certutil.options[opt_Hash].arg; + hashAlgTag = SECU_StringToSignatureAlgTag(arg); + if (hashAlgTag == SEC_OID_UNKNOWN) { + PR_fprintf(PR_STDERR, "%s -Z: %s is not a recognized type.\n", + progName, arg); + return 255; + } + } + + /* -k key type */ + if (certutil.options[opt_KeyType].activated) { + char *arg = certutil.options[opt_KeyType].arg; + if (PL_strcmp(arg, "rsa") == 0) { + keytype = rsaKey; + } else if (PL_strcmp(arg, "dsa") == 0) { + keytype = dsaKey; + } else if (PL_strcmp(arg, "ec") == 0) { + keytype = ecKey; + } else if (PL_strcmp(arg, "all") == 0) { + keytype = nullKey; + } else { + /* use an existing private/public key pair */ + keysource = arg; + } + } else if (certutil.commands[cmd_ListKeys].activated) { + keytype = nullKey; + } + + if (certutil.options[opt_KeyOpFlagsOn].activated) { + keyOpFlagsOn = GetOpFlags(certutil.options[opt_KeyOpFlagsOn].arg); + } + if (certutil.options[opt_KeyOpFlagsOff].activated) { + keyOpFlagsOff = GetOpFlags(certutil.options[opt_KeyOpFlagsOff].arg); + keyOpFlagsOn &= ~keyOpFlagsOff; /* make off override on */ + } + if (certutil.options[opt_KeyAttrFlags].activated) { + keyAttrFlags = GetAttrFlags(certutil.options[opt_KeyAttrFlags].arg); + } + + /* -m serial number */ + if (certutil.options[opt_SerialNumber].activated) { + int sn = PORT_Atoi(certutil.options[opt_SerialNumber].arg); + if (sn < 0) { + PR_fprintf(PR_STDERR, "%s -m: %s is not a valid serial number.\n", + progName, certutil.options[opt_SerialNumber].arg); + return 255; + } + serialNumber = sn; + } + + /* -P certdb name prefix */ + if (certutil.options[opt_DBPrefix].activated) { + if (certutil.options[opt_DBPrefix].arg) { + certPrefix = certutil.options[opt_DBPrefix].arg; + } else { + Usage(); + } + } + + /* --source-prefix certdb name prefix */ + if (certutil.options[opt_SourcePrefix].activated) { + if (certutil.options[opt_SourcePrefix].arg) { + srcCertPrefix = certutil.options[opt_SourcePrefix].arg; + } else { + Usage(); + } + } + + /* -q PQG file or curve name */ + if (certutil.options[opt_PQGFile].activated) { + if ((keytype != dsaKey) && (keytype != ecKey)) { + PR_fprintf(PR_STDERR, "%s -q: specifies a PQG file for DSA keys" + " (-k dsa) or a named curve for EC keys (-k ec)\n)", + progName); + return 255; + } + } + + /* -s subject name */ + if (certutil.options[opt_Subject].activated) { + subject = CERT_AsciiToName(certutil.options[opt_Subject].arg); + if (!subject) { + PR_fprintf(PR_STDERR, "%s -s: improperly formatted name: \"%s\"\n", + progName, certutil.options[opt_Subject].arg); + return 255; + } + } + + /* -v validity period */ + if (certutil.options[opt_Validity].activated) { + validityMonths = PORT_Atoi(certutil.options[opt_Validity].arg); + if (validityMonths < 0) { + PR_fprintf(PR_STDERR, "%s -v: incorrect validity period: \"%s\"\n", + progName, certutil.options[opt_Validity].arg); + return 255; + } + } + + /* -w warp months */ + if (certutil.options[opt_OffsetMonths].activated) + warpmonths = PORT_Atoi(certutil.options[opt_OffsetMonths].arg); + + /* -y public exponent (for RSA) */ + if (certutil.options[opt_Exponent].activated) { + publicExponent = PORT_Atoi(certutil.options[opt_Exponent].arg); + if ((publicExponent != 3) && + (publicExponent != 17) && + (publicExponent != 65537)) { + PR_fprintf(PR_STDERR, "%s -y: incorrect public exponent %d.", + progName, publicExponent); + PR_fprintf(PR_STDERR, "Must be 3, 17, or 65537.\n"); + return 255; + } + } + + /* --certVersion */ + if (certutil.options[opt_CertVersion].activated) { + certVersion = PORT_Atoi(certutil.options[opt_CertVersion].arg); + if (certVersion < 1 || certVersion > 4) { + PR_fprintf(PR_STDERR, "%s -certVersion: incorrect certificate version %d.", + progName, certVersion); + PR_fprintf(PR_STDERR, "Must be 1, 2, 3 or 4.\n"); + return 255; + } + certVersion = certVersion - 1; + } + + /* Check number of commands entered. */ + commandsEntered = 0; + for (i = 0; i < certutil.numCommands; i++) { + if (certutil.commands[i].activated) { + commandToRun = certutil.commands[i].flag; + commandsEntered++; + } + if (commandsEntered > 1) + break; + } + if (commandsEntered > 1) { + PR_fprintf(PR_STDERR, "%s: only one command at a time!\n", progName); + PR_fprintf(PR_STDERR, "You entered: "); + for (i = 0; i < certutil.numCommands; i++) { + if (certutil.commands[i].activated) + PR_fprintf(PR_STDERR, " -%c", certutil.commands[i].flag); + } + PR_fprintf(PR_STDERR, "\n"); + return 255; + } + if (commandsEntered == 0) { + Usage(); + } + + if (certutil.commands[cmd_ListCerts].activated || + certutil.commands[cmd_PrintHelp].activated || + certutil.commands[cmd_ListKeys].activated || + certutil.commands[cmd_ListModules].activated || + certutil.commands[cmd_CheckCertValidity].activated || + certutil.commands[cmd_Version].activated) { + readOnly = !certutil.options[opt_RW].activated; + } + + /* -A, -D, -M, -S, -V, and all require -n */ + if ((certutil.commands[cmd_AddCert].activated || + certutil.commands[cmd_DeleteCert].activated || + certutil.commands[cmd_DumpChain].activated || + certutil.commands[cmd_ModifyCertTrust].activated || + certutil.commands[cmd_CreateAndAddCert].activated || + certutil.commands[cmd_CheckCertValidity].activated) && + !certutil.options[opt_Nickname].activated) { + PR_fprintf(PR_STDERR, + "%s -%c: nickname is required for this command (-n).\n", + progName, commandToRun); + return 255; + } + + /* -A, -E, -M, -S require trust */ + if ((certutil.commands[cmd_AddCert].activated || + certutil.commands[cmd_AddEmailCert].activated || + certutil.commands[cmd_ModifyCertTrust].activated || + certutil.commands[cmd_CreateAndAddCert].activated) && + !certutil.options[opt_Trust].activated) { + PR_fprintf(PR_STDERR, + "%s -%c: trust is required for this command (-t).\n", + progName, commandToRun); + return 255; + } + + /* if -L is given raw, ascii or dump mode, it must be for only one cert. */ + if (certutil.commands[cmd_ListCerts].activated && + (certutil.options[opt_ASCIIForIO].activated || + certutil.options[opt_DumpExtensionValue].activated || + certutil.options[opt_BinaryDER].activated) && + !certutil.options[opt_Nickname].activated) { + PR_fprintf(PR_STDERR, + "%s: nickname is required to dump cert in raw or ascii mode.\n", + progName); + return 255; + } + + /* -L can only be in (raw || ascii). */ + if (certutil.commands[cmd_ListCerts].activated && + certutil.options[opt_ASCIIForIO].activated && + certutil.options[opt_BinaryDER].activated) { + PR_fprintf(PR_STDERR, + "%s: cannot specify both -r and -a when dumping cert.\n", + progName); + return 255; + } + + /* If making a cert request, need a subject. */ + if ((certutil.commands[cmd_CertReq].activated || + certutil.commands[cmd_CreateAndAddCert].activated) && + !(certutil.options[opt_Subject].activated || keysource)) { + PR_fprintf(PR_STDERR, + "%s -%c: subject is required to create a cert request.\n", + progName, commandToRun); + return 255; + } + + /* If making a cert, need a serial number. */ + if ((certutil.commands[cmd_CreateNewCert].activated || + certutil.commands[cmd_CreateAndAddCert].activated) && + !certutil.options[opt_SerialNumber].activated) { + /* Make a default serial number from the current time. */ + PRTime now = PR_Now(); + LL_USHR(now, now, 19); + LL_L2UI(serialNumber, now); + } + + /* Validation needs the usage to validate for. */ + if (certutil.commands[cmd_CheckCertValidity].activated && + !certutil.options[opt_Usage].activated) { + PR_fprintf(PR_STDERR, + "%s -V: specify a usage to validate the cert for (-u).\n", + progName); + return 255; + } + + /* Rename needs an old and a new nickname */ + if (certutil.commands[cmd_Rename].activated && + !(certutil.options[opt_Nickname].activated && + certutil.options[opt_NewNickname].activated)) { + + PR_fprintf(PR_STDERR, + "%s --rename: specify an old nickname (-n) and\n" + " a new nickname (--new-n).\n", + progName); + return 255; + } + + /* Delete needs a nickname or a key ID */ + if (certutil.commands[cmd_DeleteKey].activated && + !(certutil.options[opt_Nickname].activated || keysource)) { + PR_fprintf(PR_STDERR, + "%s -%c: specify a nickname (-n) or\n" + " a key ID (-k).\n", + progName, commandToRun); + return 255; + } + + /* Upgrade/Merge needs a source database and a upgrade id. */ + if (certutil.commands[cmd_UpgradeMerge].activated && + !(certutil.options[opt_SourceDir].activated && + certutil.options[opt_UpgradeID].activated)) { + + PR_fprintf(PR_STDERR, + "%s --upgrade-merge: specify an upgrade database directory " + "(--source-dir) and\n" + " an upgrade ID (--upgrade-id).\n", + progName); + return 255; + } + + /* Merge needs a source database */ + if (certutil.commands[cmd_Merge].activated && + !certutil.options[opt_SourceDir].activated) { + + PR_fprintf(PR_STDERR, + "%s --merge: specify an source database directory " + "(--source-dir)\n", + progName); + return 255; + } + + /* To make a cert, need either a issuer or to self-sign it. */ + if (certutil.commands[cmd_CreateAndAddCert].activated && + !(certutil.options[opt_IssuerName].activated || + certutil.options[opt_SelfSign].activated)) { + PR_fprintf(PR_STDERR, + "%s -S: must specify issuer (-c) or self-sign (-x).\n", + progName); + return 255; + } + + /* Using slotname == NULL for listing keys and certs on all slots, + * but only that. */ + if (!(certutil.commands[cmd_ListKeys].activated || + certutil.commands[cmd_DumpChain].activated || + certutil.commands[cmd_ListCerts].activated) && + slotname == NULL) { + PR_fprintf(PR_STDERR, + "%s -%c: cannot use \"-h all\" for this command.\n", + progName, commandToRun); + return 255; + } + + /* Using keytype == nullKey for list all key types, but only that. */ + if (!certutil.commands[cmd_ListKeys].activated && keytype == nullKey) { + PR_fprintf(PR_STDERR, + "%s -%c: cannot use \"-k all\" for this command.\n", + progName, commandToRun); + return 255; + } + + /* Open the input file. */ + if (certutil.options[opt_InputFile].activated) { + inFile = PR_Open(certutil.options[opt_InputFile].arg, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, + "%s: unable to open \"%s\" for reading (%ld, %ld).\n", + progName, certutil.options[opt_InputFile].arg, + PR_GetError(), PR_GetOSError()); + return 255; + } + } + + /* Open the output file. */ + if (certutil.options[opt_OutputFile].activated) { + outFile = PR_Open(certutil.options[opt_OutputFile].arg, + PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); + if (!outFile) { + PR_fprintf(PR_STDERR, + "%s: unable to open \"%s\" for writing (%ld, %ld).\n", + progName, certutil.options[opt_OutputFile].arg, + PR_GetError(), PR_GetOSError()); + return 255; + } + } + + name = SECU_GetOptionArg(&certutil, opt_Nickname); + newName = SECU_GetOptionArg(&certutil, opt_NewNickname); + email = SECU_GetOptionArg(&certutil, opt_Emailaddress); + + PK11_SetPasswordFunc(SECU_GetModulePassword); + + if (PR_TRUE == initialize) { + /* Initialize NSPR and NSS. */ + PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + if (!certutil.commands[cmd_UpgradeMerge].activated) { + rv = NSS_Initialize(SECU_ConfigDirectory(NULL), + certPrefix, certPrefix, + "secmod.db", readOnly ? NSS_INIT_READONLY : 0); + } else { + rv = NSS_InitWithMerge(SECU_ConfigDirectory(NULL), + certPrefix, certPrefix, "secmod.db", + sourceDir, srcCertPrefix, srcCertPrefix, + upgradeID, upgradeTokenName, + readOnly ? NSS_INIT_READONLY : 0); + } + if (rv != SECSuccess) { + SECU_PrintPRandOSError(progName); + rv = SECFailure; + goto shutdown; + } + initialized = PR_TRUE; + SECU_RegisterDynamicOids(); + /* Ensure the SSL error code table has been registered. Bug 1460284. */ + SSL_OptionSetDefault(-1, 0); + } + certHandle = CERT_GetDefaultCertDB(); + + if (certutil.commands[cmd_Version].activated) { + printf("Certificate database content version: command not implemented.\n"); + } + + if (PL_strcmp(slotname, "internal") == 0) + slot = PK11_GetInternalKeySlot(); + else if (slotname != NULL) + slot = PK11_FindSlotByName(slotname); + + if (!slot && (certutil.commands[cmd_NewDBs].activated || + certutil.commands[cmd_ModifyCertTrust].activated || + certutil.commands[cmd_ChangePassword].activated || + certutil.commands[cmd_TokenReset].activated || + certutil.commands[cmd_CreateAndAddCert].activated || + certutil.commands[cmd_AddCert].activated || + certutil.commands[cmd_Merge].activated || + certutil.commands[cmd_UpgradeMerge].activated || + certutil.commands[cmd_AddEmailCert].activated)) { + + SECU_PrintError(progName, "could not find the slot %s", slotname); + rv = SECFailure; + goto shutdown; + } + + /* If creating new database, initialize the password. */ + if (certutil.commands[cmd_NewDBs].activated) { + if (certutil.options[opt_EmptyPassword].activated && (PK11_NeedUserInit(slot))) { + rv = PK11_InitPin(slot, (char *)NULL, ""); + } else { + rv = SECU_ChangePW2(slot, 0, 0, certutil.options[opt_PasswordFile].arg, + certutil.options[opt_NewPasswordFile].arg); + } + if (rv != SECSuccess) { + SECU_PrintError(progName, "Could not set password for the slot"); + goto shutdown; + } + } + + /* if we are going to modify the cert database, + * make sure it's initialized */ + if (certutil.commands[cmd_ModifyCertTrust].activated || + certutil.commands[cmd_CreateAndAddCert].activated || + certutil.commands[cmd_AddCert].activated || + certutil.commands[cmd_AddEmailCert].activated) { + if (PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) { + char *password = NULL; + /* fetch the password from the command line or the file + * if no password is supplied, initialize the password to NULL */ + if (pwdata.source == PW_FROMFILE) { + password = SECU_FilePasswd(slot, PR_FALSE, pwdata.data); + } else if (pwdata.source == PW_PLAINTEXT) { + password = PL_strdup(pwdata.data); + } + rv = PK11_InitPin(slot, (char *)NULL, password ? password : ""); + if (password) { + PORT_Memset(password, 0, PL_strlen(password)); + PORT_Free(password); + } + if (rv != SECSuccess) { + SECU_PrintError(progName, "Could not set password for the slot"); + goto shutdown; + } + } + } + + /* walk through the upgrade merge if necessary. + * This option is more to test what some applications will want to do + * to do an automatic upgrade. The --merge command is more useful for + * the general case where 2 database need to be merged together. + */ + if (certutil.commands[cmd_UpgradeMerge].activated) { + if (*upgradeTokenName == 0) { + upgradeTokenName = upgradeID; + } + if (!PK11_IsInternal(slot)) { + fprintf(stderr, "Only internal DB's can be upgraded\n"); + rv = SECSuccess; + goto shutdown; + } + if (!PK11_IsRemovable(slot)) { + printf("database already upgraded.\n"); + rv = SECSuccess; + goto shutdown; + } + if (!PK11_NeedLogin(slot)) { + printf("upgrade complete!\n"); + rv = SECSuccess; + goto shutdown; + } + /* authenticate to the old DB if necessary */ + if (PORT_Strcmp(PK11_GetTokenName(slot), upgradeTokenName) == 0) { + /* if we need a password, supply it. This will be the password + * for the old database */ + rv = PK11_Authenticate(slot, PR_FALSE, &pwdata2); + if (rv != SECSuccess) { + SECU_PrintError(progName, "Could not get password for %s", + upgradeTokenName); + goto shutdown; + } + /* + * if we succeeded above, but still aren't logged in, that means + * we just supplied the password for the old database. We may + * need the password for the new database. NSS will automatically + * change the token names at this point + */ + if (PK11_IsLoggedIn(slot, &pwdata)) { + printf("upgrade complete!\n"); + rv = SECSuccess; + goto shutdown; + } + } + + /* call PK11_IsPresent to update our cached token information */ + if (!PK11_IsPresent(slot)) { + /* this shouldn't happen. We call isPresent to force a token + * info update */ + fprintf(stderr, "upgrade/merge internal error\n"); + rv = SECFailure; + goto shutdown; + } + + /* the token is now set to the state of the source database, + * if we need a password for it, PK11_Authenticate will + * automatically prompt us */ + rv = PK11_Authenticate(slot, PR_FALSE, &pwdata); + if (rv == SECSuccess) { + printf("upgrade complete!\n"); + } else { + SECU_PrintError(progName, "Could not get password for %s", + PK11_GetTokenName(slot)); + } + goto shutdown; + } + + /* + * merge 2 databases. + */ + if (certutil.commands[cmd_Merge].activated) { + PK11SlotInfo *sourceSlot = NULL; + PK11MergeLog *log; + char *modspec = PR_smprintf( + "configDir='%s' certPrefix='%s' tokenDescription='%s'", + sourceDir, srcCertPrefix, + *upgradeTokenName ? upgradeTokenName : "Source Database"); + + if (!modspec) { + rv = SECFailure; + goto shutdown; + } + + sourceSlot = SECMOD_OpenUserDB(modspec); + PR_smprintf_free(modspec); + if (!sourceSlot) { + SECU_PrintError(progName, "couldn't open source database"); + rv = SECFailure; + goto shutdown; + } + + rv = PK11_Authenticate(slot, PR_FALSE, &pwdata); + if (rv != SECSuccess) { + SECU_PrintError(progName, "Couldn't get password for %s", + PK11_GetTokenName(slot)); + goto merge_fail; + } + + rv = PK11_Authenticate(sourceSlot, PR_FALSE, &pwdata2); + if (rv != SECSuccess) { + SECU_PrintError(progName, "Couldn't get password for %s", + PK11_GetTokenName(sourceSlot)); + goto merge_fail; + } + + log = PK11_CreateMergeLog(); + if (!log) { + rv = SECFailure; + SECU_PrintError(progName, "couldn't create error log"); + goto merge_fail; + } + + rv = PK11_MergeTokens(slot, sourceSlot, log, &pwdata, &pwdata2); + if (rv != SECSuccess) { + DumpMergeLog(progName, log); + } + PK11_DestroyMergeLog(log); + + merge_fail: + SECMOD_CloseUserDB(sourceSlot); + PK11_FreeSlot(sourceSlot); + goto shutdown; + } + + /* The following 8 options are mutually exclusive with all others. */ + + /* List certs (-L) */ + if (certutil.commands[cmd_ListCerts].activated) { + if (certutil.options[opt_DumpExtensionValue].activated) { + const char *oid_str; + SECItem oid_item; + SECStatus srv; + oid_item.data = NULL; + oid_item.len = 0; + oid_str = certutil.options[opt_DumpExtensionValue].arg; + srv = GetOidFromString(NULL, &oid_item, oid_str, strlen(oid_str)); + if (srv != SECSuccess) { + SECU_PrintError(progName, "malformed extension OID %s", + oid_str); + goto shutdown; + } + rv = ListCerts(certHandle, name, email, slot, + PR_TRUE /*binary*/, PR_FALSE /*ascii*/, + &oid_item, + outFile, &pwdata); + SECITEM_FreeItem(&oid_item, PR_FALSE); + } else { + rv = ListCerts(certHandle, name, email, slot, + certutil.options[opt_BinaryDER].activated, + certutil.options[opt_ASCIIForIO].activated, + NULL, outFile, &pwdata); + } + goto shutdown; + } + if (certutil.commands[cmd_DumpChain].activated) { + rv = DumpChain(certHandle, name, + certutil.options[opt_ASCIIForIO].activated, + certutil.options[opt_SimpleSelfSigned].activated); + goto shutdown; + } + /* XXX needs work */ + /* List keys (-K) */ + if (certutil.commands[cmd_ListKeys].activated) { + rv = ListKeys(slot, name, 0 /*keyindex*/, keytype, PR_FALSE /*dopriv*/, + &pwdata); + goto shutdown; + } + /* List modules (-U) */ + if (certutil.commands[cmd_ListModules].activated) { + rv = ListModules(); + goto shutdown; + } + /* Delete cert (-D) */ + if (certutil.commands[cmd_DeleteCert].activated) { + rv = DeleteCert(certHandle, name, &pwdata); + goto shutdown; + } + /* Rename cert (--rename) */ + if (certutil.commands[cmd_Rename].activated) { + rv = RenameCert(certHandle, name, newName, &pwdata); + goto shutdown; + } + /* Delete key (-F) */ + if (certutil.commands[cmd_DeleteKey].activated) { + if (certutil.options[opt_Nickname].activated) { + rv = DeleteCertAndKey(name, &pwdata); + } else { + privkey = findPrivateKeyByID(slot, keysource, &pwdata); + if (!privkey) { + SECU_PrintError(progName, "%s is not a key-id", keysource); + rv = SECFailure; + } else { + rv = DeleteKey(privkey, &pwdata); + /* already destroyed by PK11_DeleteTokenPrivateKey */ + privkey = NULL; + } + } + goto shutdown; + } + /* Modify trust attribute for cert (-M) */ + if (certutil.commands[cmd_ModifyCertTrust].activated) { + rv = ChangeTrustAttributes(certHandle, slot, name, + certutil.options[opt_Trust].arg, &pwdata); + goto shutdown; + } + /* Change key db password (-W) (future - change pw to slot?) */ + if (certutil.commands[cmd_ChangePassword].activated) { + rv = SECU_ChangePW2(slot, 0, 0, certutil.options[opt_PasswordFile].arg, + certutil.options[opt_NewPasswordFile].arg); + if (rv != SECSuccess) { + SECU_PrintError(progName, "Could not set password for the slot"); + goto shutdown; + } + } + /* Reset the a token */ + if (certutil.commands[cmd_TokenReset].activated) { + char *sso_pass = ""; + + if (certutil.options[opt_SSOPass].activated) { + sso_pass = certutil.options[opt_SSOPass].arg; + } + rv = PK11_ResetToken(slot, sso_pass); + + goto shutdown; + } + /* Check cert validity against current time (-V) */ + if (certutil.commands[cmd_CheckCertValidity].activated) { + /* XXX temporary hack for fips - must log in to get priv key */ + if (certutil.options[opt_VerifySig].activated) { + if (slot && PK11_NeedLogin(slot)) { + SECStatus newrv = PK11_Authenticate(slot, PR_TRUE, &pwdata); + if (newrv != SECSuccess) { + SECU_PrintError(progName, "could not authenticate to token %s.", + PK11_GetTokenName(slot)); + goto shutdown; + } + } + } + rv = ValidateCert(certHandle, name, + certutil.options[opt_ValidityTime].arg, + certutil.options[opt_Usage].arg, + certutil.options[opt_VerifySig].activated, + certutil.options[opt_DetailedInfo].activated, + certutil.options[opt_ASCIIForIO].activated, + &pwdata); + if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INVALID_ARGS) + SECU_PrintError(progName, "validation failed"); + goto shutdown; + } + + /* + * Key generation + */ + + /* These commands may require keygen. */ + if (certutil.commands[cmd_CertReq].activated || + certutil.commands[cmd_CreateAndAddCert].activated || + certutil.commands[cmd_GenKeyPair].activated) { + if (keysource) { + CERTCertificate *keycert; + keycert = CERT_FindCertByNicknameOrEmailAddr(certHandle, keysource); + if (!keycert) { + keycert = PK11_FindCertFromNickname(keysource, NULL); + } + + if (keycert) { + privkey = PK11_FindKeyByDERCert(slot, keycert, &pwdata); + } else { + /* Interpret keysource as CKA_ID */ + privkey = findPrivateKeyByID(slot, keysource, &pwdata); + } + + if (!privkey) { + SECU_PrintError( + progName, + "%s is neither a key-type nor a nickname nor a key-id", keysource); + return SECFailure; + } + + pubkey = SECKEY_ConvertToPublicKey(privkey); + if (!pubkey) { + SECU_PrintError(progName, + "Could not get keys from cert %s", keysource); + if (keycert) { + CERT_DestroyCertificate(keycert); + } + rv = SECFailure; + goto shutdown; + } + keytype = privkey->keyType; + + /* On CertReq for renewal if no subject has been + * specified obtain it from the certificate. + */ + if (certutil.commands[cmd_CertReq].activated && !subject) { + if (keycert) { + subject = CERT_AsciiToName(keycert->subjectName); + if (!subject) { + SECU_PrintError( + progName, + "Could not get subject from certificate %s", + keysource); + CERT_DestroyCertificate(keycert); + rv = SECFailure; + goto shutdown; + } + } else { + SECU_PrintError(progName, "Subject name not provided"); + rv = SECFailure; + goto shutdown; + } + } + if (keycert) { + CERT_DestroyCertificate(keycert); + } + } else { + privkey = + CERTUTIL_GeneratePrivateKey(keytype, slot, keysize, + publicExponent, + certutil.options[opt_NoiseFile].arg, + &pubkey, + certutil.options[opt_PQGFile].arg, + keyAttrFlags, + keyOpFlagsOn, + keyOpFlagsOff, + &pwdata); + if (privkey == NULL) { + SECU_PrintError(progName, "unable to generate key(s)\n"); + rv = SECFailure; + goto shutdown; + } + } + privkey->wincx = &pwdata; + PORT_Assert(pubkey != NULL); + + /* If all that was needed was keygen, exit. */ + if (certutil.commands[cmd_GenKeyPair].activated) { + rv = SECSuccess; + goto shutdown; + } + } + + if (certutil.options[opt_Pss].activated) { + if (!certutil.commands[cmd_CertReq].activated && + !certutil.commands[cmd_CreateAndAddCert].activated) { + PR_fprintf(PR_STDERR, + "%s -%c: --pss only works with -R or -S.\n", + progName, commandToRun); + return 255; + } + if (keytype != rsaKey) { + PR_fprintf(PR_STDERR, + "%s -%c: --pss only works with RSA keys.\n", + progName, commandToRun); + return 255; + } + } + + /* --pss-sign is to sign a certificate with RSA-PSS, even if the + * issuer's key is an RSA key. If the key is an RSA-PSS key, the + * generated signature is always RSA-PSS. */ + if (certutil.options[opt_PssSign].activated) { + if (!certutil.commands[cmd_CreateNewCert].activated && + !certutil.commands[cmd_CreateAndAddCert].activated) { + PR_fprintf(PR_STDERR, + "%s -%c: --pss-sign only works with -C or -S.\n", + progName, commandToRun); + return 255; + } + if (keytype != rsaKey) { + PR_fprintf(PR_STDERR, + "%s -%c: --pss-sign only works with RSA keys.\n", + progName, commandToRun); + return 255; + } + } + + if (certutil.options[opt_SimpleSelfSigned].activated && + !certutil.commands[cmd_DumpChain].activated) { + PR_fprintf(PR_STDERR, + "%s -%c: --simple-self-signed only works with -O.\n", + progName, commandToRun); + return 255; + } + + /* If we need a list of extensions convert the flags into list format */ + if (certutil.commands[cmd_CertReq].activated || + certutil.commands[cmd_CreateAndAddCert].activated || + certutil.commands[cmd_CreateNewCert].activated) { + certutil_extns[ext_keyUsage].activated = + certutil.options[opt_AddCmdKeyUsageExt].activated; + if (!certutil_extns[ext_keyUsage].activated) { + certutil_extns[ext_keyUsage].activated = + certutil.options[opt_AddKeyUsageExt].activated; + } else { + certutil_extns[ext_keyUsage].arg = + certutil.options[opt_AddCmdKeyUsageExt].arg; + } + certutil_extns[ext_basicConstraint].activated = + certutil.options[opt_AddBasicConstraintExt].activated; + certutil_extns[ext_nameConstraints].activated = + certutil.options[opt_AddNameConstraintsExt].activated; + certutil_extns[ext_authorityKeyID].activated = + certutil.options[opt_AddAuthorityKeyIDExt].activated; + certutil_extns[ext_subjectKeyID].activated = + certutil.options[opt_AddSubjectKeyIDExt].activated; + certutil_extns[ext_CRLDistPts].activated = + certutil.options[opt_AddCRLDistPtsExt].activated; + certutil_extns[ext_NSCertType].activated = + certutil.options[opt_AddCmdNSCertTypeExt].activated; + if (!certutil_extns[ext_NSCertType].activated) { + certutil_extns[ext_NSCertType].activated = + certutil.options[opt_AddNSCertTypeExt].activated; + } else { + certutil_extns[ext_NSCertType].arg = + certutil.options[opt_AddCmdNSCertTypeExt].arg; + } + + certutil_extns[ext_extKeyUsage].activated = + certutil.options[opt_AddCmdExtKeyUsageExt].activated; + if (!certutil_extns[ext_extKeyUsage].activated) { + certutil_extns[ext_extKeyUsage].activated = + certutil.options[opt_AddExtKeyUsageExt].activated; + } else { + certutil_extns[ext_extKeyUsage].arg = + certutil.options[opt_AddCmdExtKeyUsageExt].arg; + } + certutil_extns[ext_subjectAltName].activated = + certutil.options[opt_AddSubjectAltNameExt].activated; + if (certutil_extns[ext_subjectAltName].activated) { + certutil_extns[ext_subjectAltName].arg = + certutil.options[opt_AddSubjectAltNameExt].arg; + } + + certutil_extns[ext_authInfoAcc].activated = + certutil.options[opt_AddAuthInfoAccExt].activated; + certutil_extns[ext_subjInfoAcc].activated = + certutil.options[opt_AddSubjInfoAccExt].activated; + certutil_extns[ext_certPolicies].activated = + certutil.options[opt_AddCertPoliciesExt].activated; + certutil_extns[ext_policyMappings].activated = + certutil.options[opt_AddPolicyMapExt].activated; + certutil_extns[ext_policyConstr].activated = + certutil.options[opt_AddPolicyConstrExt].activated; + certutil_extns[ext_inhibitAnyPolicy].activated = + certutil.options[opt_AddInhibAnyExt].activated; + } + + /* -A -C or -E Read inFile */ + if (certutil.commands[cmd_CreateNewCert].activated || + certutil.commands[cmd_AddCert].activated || + certutil.commands[cmd_AddEmailCert].activated) { + PRBool isCreate = certutil.commands[cmd_CreateNewCert].activated; + rv = SECU_ReadDERFromFile(isCreate ? &certReqDER : &certDER, inFile, + certutil.options[opt_ASCIIForIO].activated, + PR_TRUE); + if (rv) + goto shutdown; + } + + /* + * Certificate request + */ + + /* Make a cert request (-R). */ + if (certutil.commands[cmd_CertReq].activated) { + rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject, + certutil.options[opt_PhoneNumber].arg, + certutil.options[opt_ASCIIForIO].activated, + certutil.options[opt_ExtendedEmailAddrs].arg, + certutil.options[opt_ExtendedDNSNames].arg, + certutil_extns, + (certutil.options[opt_GenericExtensions].activated ? certutil.options[opt_GenericExtensions].arg + : NULL), + certutil.options[opt_Pss].activated, + &certReqDER); + if (rv) + goto shutdown; + privkey->wincx = &pwdata; + } + + /* + * Certificate creation + */ + + /* If making and adding a cert, create a cert request file first without + * any extensions, then load it with the command line extensions + * and output the cert to another file. + */ + if (certutil.commands[cmd_CreateAndAddCert].activated) { + static certutilExtnList nullextnlist = { { PR_FALSE, NULL } }; + rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject, + certutil.options[opt_PhoneNumber].arg, + PR_FALSE, /* do not BASE64-encode regardless of -a option */ + NULL, + NULL, + nullextnlist, + (certutil.options[opt_GenericExtensions].activated ? certutil.options[opt_GenericExtensions].arg + : NULL), + certutil.options[opt_Pss].activated, + &certReqDER); + if (rv) + goto shutdown; + privkey->wincx = &pwdata; + } + + /* Create a certificate (-C or -S). */ + if (certutil.commands[cmd_CreateAndAddCert].activated || + certutil.commands[cmd_CreateNewCert].activated) { + rv = CreateCert(certHandle, slot, + certutil.options[opt_IssuerName].arg, + &certReqDER, &privkey, &pwdata, hashAlgTag, + serialNumber, warpmonths, validityMonths, + certutil.options[opt_ExtendedEmailAddrs].arg, + certutil.options[opt_ExtendedDNSNames].arg, + certutil.options[opt_ASCIIForIO].activated && + certutil.commands[cmd_CreateNewCert].activated, + certutil.options[opt_SelfSign].activated, + certutil_extns, + (certutil.options[opt_GenericExtensions].activated ? certutil.options[opt_GenericExtensions].arg + : NULL), + certVersion, + certutil.options[opt_PssSign].activated, + &certDER); + if (rv) + goto shutdown; + } + + /* + * Adding a cert to the database (or slot) + */ + + /* -A -E or -S Add the cert to the DB */ + if (certutil.commands[cmd_CreateAndAddCert].activated || + certutil.commands[cmd_AddCert].activated || + certutil.commands[cmd_AddEmailCert].activated) { + if (strstr(certutil.options[opt_Trust].arg, "u")) { + fprintf(stderr, "Notice: Trust flag u is set automatically if the " + "private key is present.\n"); + } + rv = AddCert(slot, certHandle, name, + certutil.options[opt_Trust].arg, + &certDER, + certutil.commands[cmd_AddEmailCert].activated, &pwdata); + if (rv) + goto shutdown; + } + + if (certutil.commands[cmd_CertReq].activated || + certutil.commands[cmd_CreateNewCert].activated) { + SECItem *item = certutil.commands[cmd_CertReq].activated ? &certReqDER + : &certDER; + PRInt32 written = PR_Write(outFile, item->data, item->len); + if (written < 0 || (PRUint32)written != item->len) { + rv = SECFailure; + } + } + +shutdown: + if (slot) { + PK11_FreeSlot(slot); + } + if (privkey) { + SECKEY_DestroyPrivateKey(privkey); + } + if (pubkey) { + SECKEY_DestroyPublicKey(pubkey); + } + if (subject) { + CERT_DestroyName(subject); + } + if (name) { + PL_strfree(name); + } + if (newName) { + PL_strfree(newName); + } + if (inFile && inFile != PR_STDIN) { + PR_Close(inFile); + } + if (outFile && outFile != PR_STDOUT) { + PR_Close(outFile); + } + SECITEM_FreeItem(&certReqDER, PR_FALSE); + SECITEM_FreeItem(&certDER, PR_FALSE); + if (pwdata.data && pwdata.source == PW_PLAINTEXT) { + /* Allocated by a PL_strdup call in SECU_GetModulePassword. */ + PL_strfree(pwdata.data); + } + if (email) { + PL_strfree(email); + } + + /* Open the batch command file. + * + * - If -B <command line> option is specified, the contents in the + * command file will be interpreted as subsequent certutil + * commands to be executed in the current certutil process + * context after the current certutil command has been executed. + * - Each line in the command file consists of the command + * line arguments for certutil. + * - The -d <configdir> option will be ignored if specified in the + * command file. + * - Quoting with double quote characters ("...") is supported + * to allow white space in a command line argument. The + * double quote character cannot be escaped and quoting cannot + * be nested in this version. + * - each line in the batch file is limited to 512 characters + */ + + if ((SECSuccess == rv) && certutil.commands[cmd_Batch].activated) { + FILE *batchFile = NULL; + char *nextcommand = NULL; + PRInt32 cmd_len = 0, buf_size = 0; + static const int increment = 512; + + if (!certutil.options[opt_InputFile].activated || + !certutil.options[opt_InputFile].arg) { + PR_fprintf(PR_STDERR, + "%s: no batch input file specified.\n", + progName); + return 255; + } + batchFile = fopen(certutil.options[opt_InputFile].arg, "r"); + if (!batchFile) { + PR_fprintf(PR_STDERR, + "%s: unable to open \"%s\" for reading (%ld, %ld).\n", + progName, certutil.options[opt_InputFile].arg, + PR_GetError(), PR_GetOSError()); + return 255; + } + /* read and execute command-lines in a loop */ + while (SECSuccess == rv) { + PRBool invalid = PR_FALSE; + int newargc = 2; + char *space = NULL; + char *nextarg = NULL; + char **newargv = NULL; + char *crlf; + + if (cmd_len + increment > buf_size) { + char *new_buf; + buf_size += increment; + new_buf = PORT_Realloc(nextcommand, buf_size); + if (!new_buf) { + PR_fprintf(PR_STDERR, "%s: PORT_Realloc(%ld) failed\n", + progName, buf_size); + break; + } + nextcommand = new_buf; + nextcommand[cmd_len] = '\0'; + } + if (!fgets(nextcommand + cmd_len, buf_size - cmd_len, batchFile)) { + break; + } + crlf = PORT_Strrchr(nextcommand, '\n'); + if (crlf) { + *crlf = '\0'; + } + cmd_len = strlen(nextcommand); + if (cmd_len && nextcommand[cmd_len - 1] == '\\') { + nextcommand[--cmd_len] = '\0'; + continue; + } + + /* we now need to split the command into argc / argv format */ + + newargv = PORT_Alloc(sizeof(char *) * (newargc + 1)); + newargv[0] = progName; + newargv[1] = nextcommand; + nextarg = nextcommand; + while ((space = PORT_Strpbrk(nextarg, " \f\n\r\t\v"))) { + while (isspace(*space)) { + *space = '\0'; + space++; + } + if (*space == '\0') { + break; + } else if (*space != '\"') { + nextarg = space; + } else { + char *closingquote = strchr(space + 1, '\"'); + if (closingquote) { + *closingquote = '\0'; + space++; + nextarg = closingquote + 1; + } else { + invalid = PR_TRUE; + nextarg = space; + } + } + newargc++; + newargv = PORT_Realloc(newargv, sizeof(char *) * (newargc + 1)); + newargv[newargc - 1] = space; + } + newargv[newargc] = NULL; + + /* invoke next command */ + if (PR_TRUE == invalid) { + PR_fprintf(PR_STDERR, "Missing closing quote in batch command :\n%s\nNot executed.\n", + nextcommand); + rv = SECFailure; + } else { + if (0 != certutil_main(newargc, newargv, PR_FALSE)) + rv = SECFailure; + } + PORT_Free(newargv); + cmd_len = 0; + nextcommand[0] = '\0'; + } + PORT_Free(nextcommand); + fclose(batchFile); + } + + if ((initialized == PR_TRUE) && NSS_Shutdown() != SECSuccess) { + exit(1); + } + if (rv == SECSuccess) { + return 0; + } else { + return 255; + } +} + +int +main(int argc, char **argv) +{ + int rv = certutil_main(argc, argv, PR_TRUE); + PL_ArenaFinish(); + PR_Cleanup(); + return rv; +} diff --git a/security/nss/cmd/certutil/certutil.gyp b/security/nss/cmd/certutil/certutil.gyp new file mode 100644 index 0000000000..d8f1b5d4c9 --- /dev/null +++ b/security/nss/cmd/certutil/certutil.gyp @@ -0,0 +1,32 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi', + '../../cmd/platlibs.gypi' + ], + 'targets': [ + { + 'target_name': 'certutil', + 'type': 'executable', + 'sources': [ + 'certext.c', + 'certutil.c', + 'keystuff.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:dbm_exports', + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'target_defaults': { + 'defines': [ + 'NSPR20' + ] + }, + 'variables': { + 'module': 'nss' + } +}
\ No newline at end of file diff --git a/security/nss/cmd/certutil/certutil.h b/security/nss/cmd/certutil/certutil.h new file mode 100644 index 0000000000..565587264d --- /dev/null +++ b/security/nss/cmd/certutil/certutil.h @@ -0,0 +1,57 @@ +/* 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/. */ + +#ifndef _CERTUTIL_H +#define _CERTUTIL_H + +extern SECKEYPrivateKey * +CERTUTIL_GeneratePrivateKey(KeyType keytype, + PK11SlotInfo *slot, + int rsasize, + int publicExponent, + char *noise, + SECKEYPublicKey **pubkeyp, + char *pqgFile, + PK11AttrFlags attrFlags, + CK_FLAGS opFlagsOn, + CK_FLAGS opFlagsOff, + secuPWData *pwdata); + +extern char *progName; + +enum certutilExtns { + ext_keyUsage = 0, + ext_basicConstraint, + ext_authorityKeyID, + ext_CRLDistPts, + ext_NSCertType, + ext_extKeyUsage, + ext_authInfoAcc, + ext_subjInfoAcc, + ext_certPolicies, + ext_policyMappings, + ext_policyConstr, + ext_inhibitAnyPolicy, + ext_subjectKeyID, + ext_nameConstraints, + ext_subjectAltName, + ext_End +}; + +typedef struct ExtensionEntryStr { + PRBool activated; + const char *arg; +} ExtensionEntry; + +typedef ExtensionEntry certutilExtnList[ext_End]; + +extern SECStatus +AddExtensions(void *extHandle, const char *emailAddrs, const char *dnsNames, + certutilExtnList extList, const char *extGeneric); + +extern SECStatus +GetOidFromString(PLArenaPool *arena, SECItem *to, + const char *from, size_t fromLen); + +#endif /* _CERTUTIL_H */ diff --git a/security/nss/cmd/certutil/keystuff.c b/security/nss/cmd/certutil/keystuff.c new file mode 100644 index 0000000000..9b96bce911 --- /dev/null +++ b/security/nss/cmd/certutil/keystuff.c @@ -0,0 +1,595 @@ +/* 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/. */ + +#include <stdio.h> +#include <string.h> +#include "secutil.h" + +#if defined(XP_UNIX) +#include <unistd.h> +#include <sys/time.h> +#include <termios.h> +#endif + +#if defined(XP_WIN) || defined(XP_PC) +#include <time.h> +#include <conio.h> +#endif + +#include "nspr.h" +#include "prtypes.h" +#include "prtime.h" +#include "prlong.h" + +#include "pk11func.h" + +#define NUM_KEYSTROKES 120 +#define RAND_BUF_SIZE 60 + +#define ERROR_BREAK \ + rv = SECFailure; \ + break; + +const SEC_ASN1Template SECKEY_PQGParamsTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPQGParams) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams, prime) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams, subPrime) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams, base) }, + { 0 } +}; + +/* returns 0 for success, -1 for failure (EOF encountered) */ +static int +UpdateRNG(void) +{ + char randbuf[RAND_BUF_SIZE]; +#ifdef XP_UNIX + int fd; +#endif + int c; + int rv = 0; + size_t count; +#ifdef XP_UNIX + cc_t orig_cc_min; + cc_t orig_cc_time; + tcflag_t orig_lflag; + struct termios tio; +#endif + char meter[] = { + "\r| |" + }; + +#define FPS fprintf(stderr, + FPS "\n"); + FPS "A random seed must be generated that will be used in the\n"); + FPS "creation of your key. One of the easiest ways to create a\n"); + FPS "random seed is to use the timing of keystrokes on a keyboard.\n"); + FPS "\n"); + FPS "To begin, type keys on the keyboard until this progress meter\n"); + FPS "is full. DO NOT USE THE AUTOREPEAT FUNCTION ON YOUR KEYBOARD!\n"); + FPS "\n"); + FPS "\n"); + FPS "Continue typing until the progress meter is full:\n\n"); + FPS "%s", meter); + FPS "\r|"); + +#if defined(XP_UNIX) + /* turn off echo on stdin & return on 1 char instead of NL */ + fd = fileno(stdin); + + tcgetattr(fd, &tio); + orig_lflag = tio.c_lflag; + orig_cc_min = tio.c_cc[VMIN]; + orig_cc_time = tio.c_cc[VTIME]; + tio.c_lflag &= ~ECHO; + tio.c_lflag &= ~ICANON; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + tcsetattr(fd, TCSAFLUSH, &tio); +#endif + + /* Get random noise from keyboard strokes */ + count = 0; + while (count < sizeof randbuf) { +#if defined(XP_UNIX) + c = getc(stdin); +#else + c = getch(); +#endif + if (c == EOF) { + rv = -1; + break; + } + randbuf[count] = c; + if (count == 0 || c != randbuf[count - 1]) { + count++; + FPS "*"); + } + } + PK11_RandomUpdate(randbuf, sizeof randbuf); + memset(randbuf, 0, sizeof randbuf); + + FPS "\n\n"); + FPS "Finished. Press enter to continue: "); + while ((c = getc(stdin)) != '\n' && c != EOF) + ; + if (c == EOF) + rv = -1; + FPS "\n"); + +#undef FPS + +#if defined(XP_UNIX) + /* set back termio the way it was */ + tio.c_lflag = orig_lflag; + tio.c_cc[VMIN] = orig_cc_min; + tio.c_cc[VTIME] = orig_cc_time; + tcsetattr(fd, TCSAFLUSH, &tio); +#endif + return rv; +} + +static const unsigned char P[] = { 0, + 0xc6, 0x2a, 0x47, 0x73, 0xea, 0x78, 0xfa, 0x65, + 0x47, 0x69, 0x39, 0x10, 0x08, 0x55, 0x6a, 0xdd, + 0xbf, 0x77, 0xe1, 0x9a, 0x69, 0x73, 0xba, 0x66, + 0x37, 0x08, 0x93, 0x9e, 0xdb, 0x5d, 0x01, 0x08, + 0xb8, 0x3a, 0x73, 0xe9, 0x85, 0x5f, 0xa7, 0x2b, + 0x63, 0x7f, 0xd0, 0xc6, 0x4c, 0xdc, 0xfc, 0x8b, + 0xa6, 0x03, 0xc9, 0x9c, 0x80, 0x5e, 0xec, 0xc6, + 0x21, 0x23, 0xf7, 0x8e, 0xa4, 0x7b, 0x77, 0x83, + 0x02, 0x44, 0xf8, 0x05, 0xd7, 0x36, 0x52, 0x13, + 0x57, 0x78, 0x97, 0xf3, 0x7b, 0xcf, 0x1f, 0xc9, + 0x2a, 0xa4, 0x71, 0x9d, 0xa8, 0xd8, 0x5d, 0xc5, + 0x3b, 0x64, 0x3a, 0x72, 0x60, 0x62, 0xb0, 0xb8, + 0xf3, 0xb1, 0xe7, 0xb9, 0x76, 0xdf, 0x74, 0xbe, + 0x87, 0x6a, 0xd2, 0xf1, 0xa9, 0x44, 0x8b, 0x63, + 0x76, 0x4f, 0x5d, 0x21, 0x63, 0xb5, 0x4f, 0x3c, + 0x7b, 0x61, 0xb2, 0xf3, 0xea, 0xc5, 0xd8, 0xef, + 0x30, 0x50, 0x59, 0x33, 0x61, 0xc0, 0xf3, 0x6e, + 0x21, 0xcf, 0x15, 0x35, 0x4a, 0x87, 0x2b, 0xc3, + 0xf6, 0x5a, 0x1f, 0x24, 0x22, 0xc5, 0xeb, 0x47, + 0x34, 0x4a, 0x1b, 0xb5, 0x2e, 0x71, 0x52, 0x8f, + 0x2d, 0x7d, 0xa9, 0x96, 0x8a, 0x7c, 0x61, 0xdb, + 0xc0, 0xdc, 0xf1, 0xca, 0x28, 0x69, 0x1c, 0x97, + 0xad, 0xea, 0x0d, 0x9e, 0x02, 0xe6, 0xe5, 0x7d, + 0xad, 0xe0, 0x42, 0x91, 0x4d, 0xfa, 0xe2, 0x81, + 0x16, 0x2b, 0xc2, 0x96, 0x3b, 0x32, 0x8c, 0x20, + 0x69, 0x8b, 0x5b, 0x17, 0x3c, 0xf9, 0x13, 0x6c, + 0x98, 0x27, 0x1c, 0xca, 0xcf, 0x33, 0xaa, 0x93, + 0x21, 0xaf, 0x17, 0x6e, 0x5e, 0x00, 0x37, 0xd9, + 0x34, 0x8a, 0x47, 0xd2, 0x1c, 0x67, 0x32, 0x60, + 0xb6, 0xc7, 0xb0, 0xfd, 0x32, 0x90, 0x93, 0x32, + 0xaa, 0x11, 0xba, 0x23, 0x19, 0x39, 0x6a, 0x42, + 0x7c, 0x1f, 0xb7, 0x28, 0xdb, 0x64, 0xad, 0xd9 }; +static const unsigned char Q[] = { 0, + 0xe6, 0xa3, 0xc9, 0xc6, 0x51, 0x92, 0x8b, 0xb3, + 0x98, 0x8f, 0x97, 0xb8, 0x31, 0x0d, 0x4a, 0x03, + 0x1e, 0xba, 0x4e, 0xe6, 0xc8, 0x90, 0x98, 0x1d, + 0x3a, 0x95, 0xf4, 0xf1 }; +static const unsigned char G[] = { + 0x70, 0x32, 0x58, 0x5d, 0xb3, 0xbf, 0xc3, 0x62, + 0x63, 0x0b, 0xf8, 0xa5, 0xe1, 0xed, 0xeb, 0x79, + 0xac, 0x18, 0x41, 0x64, 0xb3, 0xda, 0x4c, 0xa7, + 0x92, 0x63, 0xb1, 0x33, 0x7c, 0xcb, 0x43, 0xdc, + 0x1f, 0x38, 0x63, 0x5e, 0x0e, 0x6d, 0x45, 0xd1, + 0xc9, 0x67, 0xf3, 0xcf, 0x3d, 0x2d, 0x16, 0x4e, + 0x92, 0x16, 0x06, 0x59, 0x29, 0x89, 0x6f, 0x54, + 0xff, 0xc5, 0x71, 0xc8, 0x3a, 0x95, 0x84, 0xb6, + 0x7e, 0x7b, 0x1e, 0x8b, 0x47, 0x9d, 0x7a, 0x3a, + 0x36, 0x9b, 0x70, 0x2f, 0xd1, 0xbd, 0xef, 0xe8, + 0x3a, 0x41, 0xd4, 0xf3, 0x1f, 0x81, 0xc7, 0x1f, + 0x96, 0x7c, 0x30, 0xab, 0xf4, 0x7a, 0xac, 0x93, + 0xed, 0x6f, 0x67, 0xb0, 0xc9, 0x5b, 0xf3, 0x83, + 0x9d, 0xa0, 0xd7, 0xb9, 0x01, 0xed, 0x28, 0xae, + 0x1c, 0x6e, 0x2e, 0x48, 0xac, 0x9f, 0x7d, 0xf3, + 0x00, 0x48, 0xee, 0x0e, 0xfb, 0x7e, 0x5e, 0xcb, + 0xf5, 0x39, 0xd8, 0x92, 0x90, 0x61, 0x2d, 0x1e, + 0x3c, 0xd3, 0x55, 0x0d, 0x34, 0xd1, 0x81, 0xc4, + 0x89, 0xea, 0x94, 0x2b, 0x56, 0x33, 0x73, 0x58, + 0x48, 0xbf, 0x23, 0x72, 0x19, 0x5f, 0x19, 0xac, + 0xff, 0x09, 0xc8, 0xcd, 0xab, 0x71, 0xef, 0x9e, + 0x20, 0xfd, 0xe3, 0xb8, 0x27, 0x9e, 0x65, 0xb1, + 0x85, 0xcd, 0x88, 0xfe, 0xd4, 0xd7, 0x64, 0x4d, + 0xe1, 0xe8, 0xa6, 0xe5, 0x96, 0xc8, 0x5d, 0x9c, + 0xc6, 0x70, 0x6b, 0xba, 0x77, 0x4e, 0x90, 0x4a, + 0xb0, 0x96, 0xc5, 0xa0, 0x9e, 0x2c, 0x01, 0x03, + 0xbe, 0xbd, 0x71, 0xba, 0x0a, 0x6f, 0x9f, 0xe5, + 0xdb, 0x04, 0x08, 0xf2, 0x9e, 0x0f, 0x1b, 0xac, + 0xcd, 0xbb, 0x65, 0x12, 0xcf, 0x77, 0xc9, 0x7d, + 0xbe, 0x94, 0x4b, 0x9c, 0x5b, 0xde, 0x0d, 0xfa, + 0x57, 0xdd, 0x77, 0x32, 0xf0, 0x5b, 0x34, 0xfd, + 0x19, 0x95, 0x33, 0x60, 0x87, 0xe2, 0xa2, 0xf4 +}; + +/* P, Q, G have been generated using the NSS makepqg utility: + * makepqg -l 2048 -g 224 -r + * (see also: bug 1170322) + * + * h: 1 (0x1) + * SEED: + * d2:0b:c5:63:1b:af:dc:36:b7:7c:b9:3e:36:01:a0:8f: + * 0e:be:d0:38:e4:78:d5:3c:7c:9e:a9:9a:d2:0b:c5:63: + * 1b:af:dc:36:b7:7c:b9:3e:36:01:a0:8f:0e:be:d0:38: + * e4:78:d5:3c:7c:9e:c7:70:d2:0b:c5:63:1b:af:dc:36: + * b7:7c:b9:3e:36:01:a0:8f:0e:be:d0:38:e4:78:d5:3c: + * 7c:9e:aa:3e + * g: 672 + * counter: 0 + */ + +static const SECKEYPQGParams default_pqg_params = { + NULL, + { 0, (unsigned char *)P, sizeof(P) }, + { 0, (unsigned char *)Q, sizeof(Q) }, + { 0, (unsigned char *)G, sizeof(G) } +}; + +static SECKEYPQGParams * +decode_pqg_params(const char *str) +{ + char *buf; + unsigned int len; + PLArenaPool *arena; + SECKEYPQGParams *params; + SECStatus status; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + return NULL; + + params = PORT_ArenaZAlloc(arena, sizeof(SECKEYPQGParams)); + if (params == NULL) + goto loser; + params->arena = arena; + + buf = (char *)ATOB_AsciiToData(str, &len); + if ((buf == NULL) || (len == 0)) + goto loser; + + status = SEC_ASN1Decode(arena, params, SECKEY_PQGParamsTemplate, buf, len); + if (status != SECSuccess) + goto loser; + + return params; + +loser: + if (arena != NULL) + PORT_FreeArena(arena, PR_FALSE); + return NULL; +} + +void +CERTUTIL_DestroyParamsPQG(SECKEYPQGParams *params) +{ + if (params->arena) { + PORT_FreeArena(params->arena, PR_FALSE); + } +} + +static int +pqg_prime_bits(const SECKEYPQGParams *params) +{ + int primeBits = 0; + + if (params != NULL) { + int i; + for (i = 0; params->prime.data[i] == 0; i++) { + /* empty */; + } + primeBits = (params->prime.len - i) * 8; + } + + return primeBits; +} + +static char * +getPQGString(const char *filename) +{ + unsigned char *buf = NULL; + PRFileDesc *src; + PRInt32 numBytes; + PRStatus prStatus; + PRFileInfo info; + + src = PR_Open(filename, PR_RDONLY, 0); + if (!src) { + fprintf(stderr, "Failed to open PQG file %s\n", filename); + return NULL; + } + + prStatus = PR_GetOpenFileInfo(src, &info); + + if (prStatus == PR_SUCCESS) { + buf = (unsigned char *)PORT_Alloc(info.size + 1); + } + if (!buf) { + PR_Close(src); + fprintf(stderr, "Failed to read PQG file %s\n", filename); + return NULL; + } + + numBytes = PR_Read(src, buf, info.size); + PR_Close(src); + if (numBytes != info.size) { + PORT_Free(buf); + fprintf(stderr, "Failed to read PQG file %s\n", filename); + PORT_SetError(SEC_ERROR_IO); + return NULL; + } + + if (buf[numBytes - 1] == '\n') + numBytes--; + if (buf[numBytes - 1] == '\r') + numBytes--; + buf[numBytes] = 0; + + return (char *)buf; +} + +static SECKEYPQGParams * +getpqgfromfile(int keyBits, const char *pqgFile) +{ + char *end, *str, *pqgString; + SECKEYPQGParams *params = NULL; + + str = pqgString = getPQGString(pqgFile); + if (!str) + return NULL; + + do { + end = PORT_Strchr(str, ','); + if (end) + *end = '\0'; + params = decode_pqg_params(str); + if (params) { + int primeBits = pqg_prime_bits(params); + if (keyBits == primeBits) + break; + CERTUTIL_DestroyParamsPQG(params); + params = NULL; + } + if (end) + str = end + 1; + } while (end); + + PORT_Free(pqgString); + return params; +} + +static SECStatus +CERTUTIL_FileForRNG(const char *noise) +{ + char buf[2048]; + PRFileDesc *fd; + PRInt32 count; + + fd = PR_Open(noise, PR_RDONLY, 0); + if (!fd) { + fprintf(stderr, "failed to open noise file."); + return SECFailure; + } + + do { + count = PR_Read(fd, buf, sizeof(buf)); + if (count > 0) { + PK11_RandomUpdate(buf, count); + } + } while (count > 0); + + PR_Close(fd); + return SECSuccess; +} + +typedef struct curveNameTagPairStr { + char *curveName; + SECOidTag curveOidTag; +} CurveNameTagPair; + +static CurveNameTagPair nameTagPair[] = { + { "sect163k1", SEC_OID_SECG_EC_SECT163K1 }, + { "nistk163", SEC_OID_SECG_EC_SECT163K1 }, + { "sect163r1", SEC_OID_SECG_EC_SECT163R1 }, + { "sect163r2", SEC_OID_SECG_EC_SECT163R2 }, + { "nistb163", SEC_OID_SECG_EC_SECT163R2 }, + { "sect193r1", SEC_OID_SECG_EC_SECT193R1 }, + { "sect193r2", SEC_OID_SECG_EC_SECT193R2 }, + { "sect233k1", SEC_OID_SECG_EC_SECT233K1 }, + { "nistk233", SEC_OID_SECG_EC_SECT233K1 }, + { "sect233r1", SEC_OID_SECG_EC_SECT233R1 }, + { "nistb233", SEC_OID_SECG_EC_SECT233R1 }, + { "sect239k1", SEC_OID_SECG_EC_SECT239K1 }, + { "sect283k1", SEC_OID_SECG_EC_SECT283K1 }, + { "nistk283", SEC_OID_SECG_EC_SECT283K1 }, + { "sect283r1", SEC_OID_SECG_EC_SECT283R1 }, + { "nistb283", SEC_OID_SECG_EC_SECT283R1 }, + { "sect409k1", SEC_OID_SECG_EC_SECT409K1 }, + { "nistk409", SEC_OID_SECG_EC_SECT409K1 }, + { "sect409r1", SEC_OID_SECG_EC_SECT409R1 }, + { "nistb409", SEC_OID_SECG_EC_SECT409R1 }, + { "sect571k1", SEC_OID_SECG_EC_SECT571K1 }, + { "nistk571", SEC_OID_SECG_EC_SECT571K1 }, + { "sect571r1", SEC_OID_SECG_EC_SECT571R1 }, + { "nistb571", SEC_OID_SECG_EC_SECT571R1 }, + { "secp160k1", SEC_OID_SECG_EC_SECP160K1 }, + { "secp160r1", SEC_OID_SECG_EC_SECP160R1 }, + { "secp160r2", SEC_OID_SECG_EC_SECP160R2 }, + { "secp192k1", SEC_OID_SECG_EC_SECP192K1 }, + { "secp192r1", SEC_OID_SECG_EC_SECP192R1 }, + { "nistp192", SEC_OID_SECG_EC_SECP192R1 }, + { "secp224k1", SEC_OID_SECG_EC_SECP224K1 }, + { "secp224r1", SEC_OID_SECG_EC_SECP224R1 }, + { "nistp224", SEC_OID_SECG_EC_SECP224R1 }, + { "secp256k1", SEC_OID_SECG_EC_SECP256K1 }, + { "secp256r1", SEC_OID_SECG_EC_SECP256R1 }, + { "nistp256", SEC_OID_SECG_EC_SECP256R1 }, + { "secp384r1", SEC_OID_SECG_EC_SECP384R1 }, + { "nistp384", SEC_OID_SECG_EC_SECP384R1 }, + { "secp521r1", SEC_OID_SECG_EC_SECP521R1 }, + { "nistp521", SEC_OID_SECG_EC_SECP521R1 }, + + { "prime192v1", SEC_OID_ANSIX962_EC_PRIME192V1 }, + { "prime192v2", SEC_OID_ANSIX962_EC_PRIME192V2 }, + { "prime192v3", SEC_OID_ANSIX962_EC_PRIME192V3 }, + { "prime239v1", SEC_OID_ANSIX962_EC_PRIME239V1 }, + { "prime239v2", SEC_OID_ANSIX962_EC_PRIME239V2 }, + { "prime239v3", SEC_OID_ANSIX962_EC_PRIME239V3 }, + + { "c2pnb163v1", SEC_OID_ANSIX962_EC_C2PNB163V1 }, + { "c2pnb163v2", SEC_OID_ANSIX962_EC_C2PNB163V2 }, + { "c2pnb163v3", SEC_OID_ANSIX962_EC_C2PNB163V3 }, + { "c2pnb176v1", SEC_OID_ANSIX962_EC_C2PNB176V1 }, + { "c2tnb191v1", SEC_OID_ANSIX962_EC_C2TNB191V1 }, + { "c2tnb191v2", SEC_OID_ANSIX962_EC_C2TNB191V2 }, + { "c2tnb191v3", SEC_OID_ANSIX962_EC_C2TNB191V3 }, + { "c2onb191v4", SEC_OID_ANSIX962_EC_C2ONB191V4 }, + { "c2onb191v5", SEC_OID_ANSIX962_EC_C2ONB191V5 }, + { "c2pnb208w1", SEC_OID_ANSIX962_EC_C2PNB208W1 }, + { "c2tnb239v1", SEC_OID_ANSIX962_EC_C2TNB239V1 }, + { "c2tnb239v2", SEC_OID_ANSIX962_EC_C2TNB239V2 }, + { "c2tnb239v3", SEC_OID_ANSIX962_EC_C2TNB239V3 }, + { "c2onb239v4", SEC_OID_ANSIX962_EC_C2ONB239V4 }, + { "c2onb239v5", SEC_OID_ANSIX962_EC_C2ONB239V5 }, + { "c2pnb272w1", SEC_OID_ANSIX962_EC_C2PNB272W1 }, + { "c2pnb304w1", SEC_OID_ANSIX962_EC_C2PNB304W1 }, + { "c2tnb359v1", SEC_OID_ANSIX962_EC_C2TNB359V1 }, + { "c2pnb368w1", SEC_OID_ANSIX962_EC_C2PNB368W1 }, + { "c2tnb431r1", SEC_OID_ANSIX962_EC_C2TNB431R1 }, + + { "secp112r1", SEC_OID_SECG_EC_SECP112R1 }, + { "secp112r2", SEC_OID_SECG_EC_SECP112R2 }, + { "secp128r1", SEC_OID_SECG_EC_SECP128R1 }, + { "secp128r2", SEC_OID_SECG_EC_SECP128R2 }, + + { "sect113r1", SEC_OID_SECG_EC_SECT113R1 }, + { "sect113r2", SEC_OID_SECG_EC_SECT113R2 }, + { "sect131r1", SEC_OID_SECG_EC_SECT131R1 }, + { "sect131r2", SEC_OID_SECG_EC_SECT131R2 }, + { "curve25519", SEC_OID_CURVE25519 }, +}; + +static SECKEYECParams * +getECParams(const char *curve) +{ + SECKEYECParams *ecparams; + SECOidData *oidData = NULL; + SECOidTag curveOidTag = SEC_OID_UNKNOWN; /* default */ + int i, numCurves; + + if (curve != NULL) { + numCurves = sizeof(nameTagPair) / sizeof(CurveNameTagPair); + for (i = 0; ((i < numCurves) && (curveOidTag == SEC_OID_UNKNOWN)); + i++) { + if (PL_strcmp(curve, nameTagPair[i].curveName) == 0) + curveOidTag = nameTagPair[i].curveOidTag; + } + } + + /* Return NULL if curve name is not recognized */ + if ((curveOidTag == SEC_OID_UNKNOWN) || + (oidData = SECOID_FindOIDByTag(curveOidTag)) == NULL) { + fprintf(stderr, "Unrecognized elliptic curve %s\n", curve); + return NULL; + } + + ecparams = SECITEM_AllocItem(NULL, NULL, (2 + oidData->oid.len)); + + /* + * ecparams->data needs to contain the ASN encoding of an object ID (OID) + * representing the named curve. The actual OID is in + * oidData->oid.data so we simply prepend 0x06 and OID length + */ + ecparams->data[0] = SEC_ASN1_OBJECT_ID; + ecparams->data[1] = oidData->oid.len; + memcpy(ecparams->data + 2, oidData->oid.data, oidData->oid.len); + + return ecparams; +} + +SECKEYPrivateKey * +CERTUTIL_GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size, + int publicExponent, const char *noise, + SECKEYPublicKey **pubkeyp, const char *pqgFile, + PK11AttrFlags attrFlags, CK_FLAGS opFlagsOn, + CK_FLAGS opFlagsOff, secuPWData *pwdata) +{ + CK_MECHANISM_TYPE mechanism; + PK11RSAGenParams rsaparams; + SECKEYPQGParams *dsaparams = NULL; + void *params; + SECKEYPrivateKey *privKey = NULL; + + if (slot == NULL) + return NULL; + + if (PK11_Authenticate(slot, PR_TRUE, pwdata) != SECSuccess) + return NULL; + + /* + * Do some random-number initialization. + */ + + if (noise) { + SECStatus rv = CERTUTIL_FileForRNG(noise); + if (rv != SECSuccess) { + PORT_SetError(PR_END_OF_FILE_ERROR); /* XXX */ + return NULL; + } + } else { + int rv = UpdateRNG(); + if (rv) { + PORT_SetError(PR_END_OF_FILE_ERROR); + return NULL; + } + } + + switch (keytype) { + case rsaKey: + rsaparams.keySizeInBits = size; + rsaparams.pe = publicExponent; + mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + params = &rsaparams; + break; + case dsaKey: + mechanism = CKM_DSA_KEY_PAIR_GEN; + if (pqgFile) { + dsaparams = getpqgfromfile(size, pqgFile); + if (dsaparams == NULL) + return NULL; + params = dsaparams; + } else { + /* cast away const, and don't set dsaparams */ + params = (void *)&default_pqg_params; + } + break; + case ecKey: + mechanism = CKM_EC_KEY_PAIR_GEN; + /* For EC keys, PQGFile determines EC parameters */ + if ((params = (void *)getECParams(pqgFile)) == NULL) + return NULL; + break; + default: + return NULL; + } + + fprintf(stderr, "\n\n"); + fprintf(stderr, "Generating key. This may take a few moments...\n\n"); + + privKey = PK11_GenerateKeyPairWithOpFlags(slot, mechanism, params, pubkeyp, + attrFlags, opFlagsOn, opFlagsOn | opFlagsOff, + pwdata /*wincx*/); + /* free up the params */ + switch (keytype) { + case dsaKey: + if (dsaparams) + CERTUTIL_DestroyParamsPQG(dsaparams); + break; + case ecKey: + SECITEM_FreeItem((SECItem *)params, PR_TRUE); + break; + default: /* nothing to free */ + break; + } + return privKey; +} diff --git a/security/nss/cmd/certutil/manifest.mn b/security/nss/cmd/certutil/manifest.mn new file mode 100644 index 0000000000..c0bce4c7e6 --- /dev/null +++ b/security/nss/cmd/certutil/manifest.mn @@ -0,0 +1,25 @@ +# +# 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/. + +CORE_DEPTH = ../.. + +DEFINES += -DNSPR20 + +# MODULE public and private header directories are implicitly REQUIRED. +MODULE = nss + +CSRCS = \ + certext.c \ + certutil.c \ + keystuff.c \ + $(NULL) + +# The MODULE is always implicitly required. +# Listing it here in REQUIRES makes it appear twice in the cc command line. +REQUIRES = dbm seccmd + +PROGRAM = certutil + +#USE_STATIC_LIBS = 1 |