/* 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 #include #include #if defined(WIN32) #include "fcntl.h" #include "io.h" #endif #include "secutil.h" #if defined(XP_UNIX) #include #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: PORT_SetError(0); 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; }