/* 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/. */ /* Cert-O-Matic CGI */ #include "nspr.h" #include "prtypes.h" #include "prtime.h" #include "prlong.h" #include "pk11func.h" #include "cert.h" #include "cryptohi.h" #include "secoid.h" #include "secder.h" #include "genname.h" #include "xconst.h" #include "secutil.h" #include "pk11pqg.h" #include "certxutl.h" #include "nss.h" /* #define TEST 1 */ /* #define FILEOUT 1 */ /* #define OFFLINE 1 */ #define START_FIELDS 100 #define PREFIX_LEN 6 #define SERIAL_FILE "../serial" #define DB_DIRECTORY ".." static char *progName; typedef struct PairStr Pair; struct PairStr { char *name; char *data; }; char prefix[PREFIX_LEN]; const SEC_ASN1Template CERTIA5TypeTemplate[] = { { SEC_ASN1_IA5_STRING } }; SECKEYPrivateKey *privkeys[9] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; #ifdef notdef const SEC_ASN1Template CERT_GeneralNameTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_AnyTemplate } }; #endif static void error_out(char *error_string) { printf("Content-type: text/plain\n\n"); printf("%s", error_string); fflush(stderr); fflush(stdout); exit(1); } static void error_allocate(void) { error_out("ERROR: Unable to allocate memory"); } static char * make_copy_string(char *read_pos, int length, char sentinal_value) /* copys string from to a new string it creates and returns a pointer to the new string */ { int remaining = length; char *write_pos; char *new; new = write_pos = (char *)PORT_Alloc(length); if (new == NULL) { error_allocate(); } while (*read_pos != sentinal_value) { if (remaining == 1) { remaining += length; length = length * 2; new = PORT_Realloc(new, length); if (new == NULL) { error_allocate(); } write_pos = new + length - remaining; } *write_pos = *read_pos; ++write_pos; ++read_pos; remaining = remaining - 1; } *write_pos = '\0'; return new; } static SECStatus clean_input(Pair *data) /* converts the non-alphanumeric characters in a form post from hex codes back to characters */ { int length; int hi_digit; int low_digit; char character; char *begin_pos; char *read_pos; char *write_pos; PRBool name = PR_TRUE; begin_pos = data->name; while (begin_pos != NULL) { length = strlen(begin_pos); read_pos = write_pos = begin_pos; while ((read_pos - begin_pos) < length) { if (*read_pos == '+') { *read_pos = ' '; } if (*read_pos == '%') { hi_digit = *(read_pos + 1); low_digit = *(read_pos + 2); read_pos += 3; if (isdigit(hi_digit)) { hi_digit = hi_digit - '0'; } else { hi_digit = toupper(hi_digit); if (isxdigit(hi_digit)) { hi_digit = (hi_digit - 'A') + 10; } else { error_out("ERROR: Form data incorrectly formated"); } } if (isdigit(low_digit)) { low_digit = low_digit - '0'; } else { low_digit = toupper(low_digit); if ((low_digit >= 'A') && (low_digit <= 'F')) { low_digit = (low_digit - 'A') + 10; } else { error_out("ERROR: Form data incorrectly formated"); } } character = (hi_digit << 4) | low_digit; if (character != 10) { *write_pos = character; ++write_pos; } } else { *write_pos = *read_pos; ++write_pos; ++read_pos; } } *write_pos = '\0'; if (name == PR_TRUE) { begin_pos = data->data; name = PR_FALSE; } else { data++; begin_pos = data->name; name = PR_TRUE; } } return SECSuccess; } static char * make_name(char *new_data) /* gets the next field name in the input string and returns a pointer to a string containing a copy of it */ { int length = 20; char *name; name = make_copy_string(new_data, length, '='); return name; } static char * make_data(char *new_data) /* gets the data for the next field in the input string and returns a pointer to a string containing it */ { int length = 100; char *data; char *read_pos; read_pos = new_data; while (*(read_pos - 1) != '=') { ++read_pos; } data = make_copy_string(read_pos, length, '&'); return data; } static Pair make_pair(char *new_data) /* makes a pair name/data pair from the input string */ { Pair temp; temp.name = make_name(new_data); temp.data = make_data(new_data); return temp; } static Pair * make_datastruct(char *data, int len) /* parses the input from the form post into a data structure of field name/data pairs */ { Pair *datastruct; Pair *current; char *curr_pos; int fields = START_FIELDS; int remaining = START_FIELDS; curr_pos = data; datastruct = current = (Pair *)PORT_Alloc(fields * sizeof(Pair)); if (datastruct == NULL) { error_allocate(); } while (curr_pos - data < len) { if (remaining == 1) { remaining += fields; fields = fields * 2; datastruct = (Pair *)PORT_Realloc(datastruct, fields * sizeof(Pair)); if (datastruct == NULL) { error_allocate(); } current = datastruct + (fields - remaining); } *current = make_pair(curr_pos); while (*curr_pos != '&') { ++curr_pos; } ++curr_pos; ++current; remaining = remaining - 1; } current->name = NULL; return datastruct; } static char * return_name(Pair *data_struct, int n) /* returns a pointer to the name of the nth (starting from 0) item in the data structure */ { char *name; if ((data_struct + n)->name != NULL) { name = (data_struct + n)->name; return name; } else { return NULL; } } static char * return_data(Pair *data_struct, int n) /* returns a pointer to the data of the nth (starting from 0) itme in the data structure */ { char *data; data = (data_struct + n)->data; return data; } static char * add_prefix(char *field_name) { extern char prefix[PREFIX_LEN]; int i = 0; char *rv; char *write; rv = write = PORT_Alloc(PORT_Strlen(prefix) + PORT_Strlen(field_name) + 1); for (i = 0; i < PORT_Strlen(prefix); i++) { *write = prefix[i]; write++; } *write = '\0'; rv = PORT_Strcat(rv, field_name); return rv; } static char * find_field(Pair *data, char *field_name, PRBool add_pre) /* returns a pointer to the data of the first pair thats name matches the string it is passed */ { int i = 0; char *retrieved; int found = 0; if (add_pre) { field_name = add_prefix(field_name); } while (return_name(data, i) != NULL) { if (PORT_Strcmp(return_name(data, i), field_name) == 0) { retrieved = return_data(data, i); found = 1; break; } i++; } if (!found) { retrieved = NULL; } return retrieved; } static PRBool find_field_bool(Pair *data, char *fieldname, PRBool add_pre) { char *rv; rv = find_field(data, fieldname, add_pre); if ((rv != NULL) && (PORT_Strcmp(rv, "true")) == 0) { return PR_TRUE; } else { return PR_FALSE; } } static CERTCertificateRequest * makeCertReq(Pair *form_data, int which_priv_key) /* makes and encodes a certrequest */ { PK11SlotInfo *slot; CERTCertificateRequest *certReq = NULL; CERTSubjectPublicKeyInfo *spki; SECKEYPrivateKey *privkey = NULL; SECKEYPublicKey *pubkey = NULL; CERTName *name; char *key; extern SECKEYPrivateKey *privkeys[9]; int keySizeInBits; char *challenge = "foo"; SECStatus rv = SECSuccess; PQGParams *pqgParams = NULL; PQGVerify *pqgVfy = NULL; name = CERT_AsciiToName(find_field(form_data, "subject", PR_TRUE)); if (name == NULL) { error_out("ERROR: Unable to create Subject Name"); } key = find_field(form_data, "key", PR_TRUE); if (key == NULL) { switch (*find_field(form_data, "keysize", PR_TRUE)) { case '0': keySizeInBits = 2048; break; case '1': keySizeInBits = 1024; break; case '2': keySizeInBits = 512; break; default: error_out("ERROR: Unsupported Key length selected"); } if (find_field_bool(form_data, "keyType-dsa", PR_TRUE)) { rv = PK11_PQG_ParamGen(keySizeInBits, &pqgParams, &pqgVfy); if (rv != SECSuccess) { error_out("ERROR: Unable to generate PQG parameters"); } slot = PK11_GetBestSlot(CKM_DSA_KEY_PAIR_GEN, NULL); privkey = PK11_GenerateKeyPair(slot, CKM_DSA_KEY_PAIR_GEN, pqgParams, &pubkey, PR_FALSE, PR_TRUE, NULL); } else { privkey = SECKEY_CreateRSAPrivateKey(keySizeInBits, &pubkey, NULL); } privkeys[which_priv_key] = privkey; spki = SECKEY_CreateSubjectPublicKeyInfo(pubkey); } else { spki = SECKEY_ConvertAndDecodePublicKeyAndChallenge(key, challenge, NULL); if (spki == NULL) { error_out("ERROR: Unable to decode Public Key and Challenge String"); } } certReq = CERT_CreateCertificateRequest(name, spki, NULL); if (certReq == NULL) { error_out("ERROR: Unable to create Certificate Request"); } if (pubkey != NULL) { SECKEY_DestroyPublicKey(pubkey); } if (spki != NULL) { SECKEY_DestroySubjectPublicKeyInfo(spki); } if (pqgParams != NULL) { PK11_PQG_DestroyParams(pqgParams); } if (pqgVfy != NULL) { PK11_PQG_DestroyVerify(pqgVfy); } return certReq; } static CERTCertificate * MakeV1Cert(CERTCertDBHandle *handle, CERTCertificateRequest *req, char *issuerNameStr, PRBool selfsign, int serialNumber, int warpmonths, Pair *data) { CERTCertificate *issuerCert = NULL; CERTValidity *validity; CERTCertificate *cert = NULL; PRExplodedTime printableTime; PRTime now, after; if (!selfsign) { issuerCert = CERT_FindCertByNameString(handle, issuerNameStr); if (!issuerCert) { error_out("ERROR: Could not find issuer's certificate"); return NULL; } } if (find_field_bool(data, "manValidity", PR_TRUE)) { (void)DER_AsciiToTime(&now, find_field(data, "notBefore", PR_TRUE)); } else { 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); } if (find_field_bool(data, "manValidity", PR_TRUE)) { (void)DER_AsciiToTime(&after, find_field(data, "notAfter", PR_TRUE)); PR_ExplodeTime(after, PR_GMTParameters, &printableTime); } else { printableTime.tm_month += 3; after = PR_ImplodeTime(&printableTime); } /* note that the time is now in micro-second unit */ validity = CERT_CreateValidity(now, after); if (selfsign) { cert = CERT_CreateCertificate(serialNumber, &(req->subject), validity, req); } else { cert = CERT_CreateCertificate(serialNumber, &(issuerCert->subject), validity, req); } CERT_DestroyValidity(validity); if (issuerCert) { CERT_DestroyCertificate(issuerCert); } return (cert); } static int get_serial_number(Pair *data) { int serial = 0; int error; char *filename = SERIAL_FILE; char *SN; FILE *serialFile; if (find_field_bool(data, "serial-auto", PR_TRUE)) { serialFile = fopen(filename, "r"); if (serialFile != NULL) { size_t nread = fread(&serial, sizeof(int), 1, serialFile); if (ferror(serialFile) != 0 || nread != 1) { error_out("Error: Unable to read serial number file"); } if (serial == -1) { serial = 21; } fclose(serialFile); ++serial; serialFile = fopen(filename, "w"); if (serialFile == NULL) { error_out("ERROR: Unable to open serial number file for writing"); } fwrite(&serial, sizeof(int), 1, serialFile); if (ferror(serialFile) != 0) { error_out("Error: Unable to write to serial number file"); } } else { fclose(serialFile); serialFile = fopen(filename, "w"); if (serialFile == NULL) { error_out("ERROR: Unable to open serial number file"); } serial = 21; fwrite(&serial, sizeof(int), 1, serialFile); if (ferror(serialFile) != 0) { error_out("Error: Unable to write to serial number file"); } error = ferror(serialFile); if (error != 0) { error_out("ERROR: Unable to write to serial file"); } } fclose(serialFile); } else { SN = find_field(data, "serial_value", PR_TRUE); while (*SN != '\0') { serial = serial * 16; if ((*SN >= 'A') && (*SN <= 'F')) { serial += *SN - 'A' + 10; } else { if ((*SN >= 'a') && (*SN <= 'f')) { serial += *SN - 'a' + 10; } else { serial += *SN - '0'; } } ++SN; } } return serial; } typedef SECStatus (*EXTEN_VALUE_ENCODER)(PLArenaPool *extHandle, void *value, SECItem *encodedValue); static SECStatus EncodeAndAddExtensionValue( PLArenaPool *arena, void *extHandle, void *value, PRBool criticality, int extenType, EXTEN_VALUE_ENCODER EncodeValueFn) { SECItem encodedValue; SECStatus rv; encodedValue.data = NULL; encodedValue.len = 0; rv = (*EncodeValueFn)(arena, value, &encodedValue); if (rv != SECSuccess) { error_out("ERROR: Unable to encode extension value"); } rv = CERT_AddExtension(extHandle, extenType, &encodedValue, criticality, PR_TRUE); return (rv); } static SECStatus AddKeyUsage(void *extHandle, Pair *data) { SECItem bitStringValue; unsigned char keyUsage = 0x0; if (find_field_bool(data, "keyUsage-digitalSignature", PR_TRUE)) { keyUsage |= (0x80 >> 0); } if (find_field_bool(data, "keyUsage-nonRepudiation", PR_TRUE)) { keyUsage |= (0x80 >> 1); } if (find_field_bool(data, "keyUsage-keyEncipherment", PR_TRUE)) { keyUsage |= (0x80 >> 2); } if (find_field_bool(data, "keyUsage-dataEncipherment", PR_TRUE)) { keyUsage |= (0x80 >> 3); } if (find_field_bool(data, "keyUsage-keyAgreement", PR_TRUE)) { keyUsage |= (0x80 >> 4); } if (find_field_bool(data, "keyUsage-keyCertSign", PR_TRUE)) { keyUsage |= (0x80 >> 5); } if (find_field_bool(data, "keyUsage-cRLSign", PR_TRUE)) { keyUsage |= (0x80 >> 6); } bitStringValue.data = &keyUsage; bitStringValue.len = 1; return (CERT_EncodeAndAddBitStrExtension(extHandle, SEC_OID_X509_KEY_USAGE, &bitStringValue, (find_field_bool(data, "keyUsage-crit", PR_TRUE)))); } 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_ArenaZAlloc(arena, sizeof(CERTOidSequence)); if ((CERTOidSequence *)NULL == rv) { goto loser; } rv->oids = (SECItem **)PORT_ArenaZAlloc(arena, sizeof(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 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++) { count++; } /* ArenaZRealloc */ { PRUint32 i; oids = (SECItem **)PORT_ArenaZAlloc(os->arena, sizeof(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; } static SECItem * EncodeOidSequence(CERTOidSequence *os) { SECItem *rv; extern const SEC_ASN1Template CERT_OidSeqTemplate[]; rv = (SECItem *)PORT_ArenaZAlloc(os->arena, sizeof(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 SECStatus AddExtKeyUsage(void *extHandle, Pair *data) { SECStatus rv; CERTOidSequence *os; SECItem *value; PRBool crit; os = CreateOidSequence(); if ((CERTOidSequence *)NULL == os) { return SECFailure; } if (find_field_bool(data, "extKeyUsage-serverAuth", PR_TRUE)) { rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH); if (SECSuccess != rv) goto loser; } if (find_field_bool(data, "extKeyUsage-msTrustListSign", PR_TRUE)) { rv = AddOidToSequence(os, SEC_OID_MS_EXT_KEY_USAGE_CTL_SIGNING); if (SECSuccess != rv) goto loser; } if (find_field_bool(data, "extKeyUsage-clientAuth", PR_TRUE)) { rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH); if (SECSuccess != rv) goto loser; } if (find_field_bool(data, "extKeyUsage-codeSign", PR_TRUE)) { rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_CODE_SIGN); if (SECSuccess != rv) goto loser; } if (find_field_bool(data, "extKeyUsage-emailProtect", PR_TRUE)) { rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT); if (SECSuccess != rv) goto loser; } if (find_field_bool(data, "extKeyUsage-timeStamp", PR_TRUE)) { rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_TIME_STAMP); if (SECSuccess != rv) goto loser; } if (find_field_bool(data, "extKeyUsage-ocspResponder", PR_TRUE)) { rv = AddOidToSequence(os, SEC_OID_OCSP_RESPONDER); if (SECSuccess != rv) goto loser; } if (find_field_bool(data, "extKeyUsage-NS-govtApproved", PR_TRUE)) { rv = AddOidToSequence(os, SEC_OID_NS_KEY_USAGE_GOVT_APPROVED); if (SECSuccess != rv) goto loser; } value = EncodeOidSequence(os); crit = find_field_bool(data, "extKeyUsage-crit", PR_TRUE); rv = CERT_AddExtension(extHandle, SEC_OID_X509_EXT_KEY_USAGE, value, crit, PR_TRUE); /*FALLTHROUGH*/ loser: CERT_DestroyOidSequence(os); return rv; } static SECStatus AddSubKeyID(void *extHandle, Pair *data, CERTCertificate *subjectCert) { SECItem encodedValue; SECStatus rv; char *read; char *write; char *first; char character; int high_digit = 0, low_digit = 0; int len; PRBool odd = PR_FALSE; encodedValue.data = NULL; encodedValue.len = 0; first = read = write = find_field(data, "subjectKeyIdentifier-text", PR_TRUE); len = PORT_Strlen(first); odd = ((len % 2) != 0) ? PR_TRUE : PR_FALSE; if (find_field_bool(data, "subjectKeyIdentifier-radio-hex", PR_TRUE)) { if (odd) { error_out("ERROR: Improperly formated subject key identifier, hex values must be expressed as an octet string"); } while (*read != '\0') { if (!isxdigit(*read)) { error_out("ERROR: Improperly formated subject key identifier"); } *read = toupper(*read); if ((*read >= 'A') && (*read <= 'F')) { high_digit = *read - 'A' + 10; } else { high_digit = *read - '0'; } ++read; if (!isxdigit(*read)) { error_out("ERROR: Improperly formated subject key identifier"); } *read = toupper(*read); if ((*read >= 'A') && (*read <= 'F')) { low_digit = *(read) - 'A' + 10; } else { low_digit = *(read) - '0'; } character = (high_digit << 4) | low_digit; *write = character; ++write; ++read; } *write = '\0'; len = write - first; } subjectCert->subjectKeyID.data = (unsigned char *)find_field(data, "subjectKeyIdentifier-text", PR_TRUE); subjectCert->subjectKeyID.len = len; rv = CERT_EncodeSubjectKeyID(NULL, &subjectCert->subjectKeyID, &encodedValue); if (rv) { return (rv); } return (CERT_AddExtension(extHandle, SEC_OID_X509_SUBJECT_KEY_ID, &encodedValue, PR_FALSE, PR_TRUE)); } static SECStatus AddAuthKeyID(void *extHandle, Pair *data, char *issuerNameStr, CERTCertDBHandle *handle) { CERTAuthKeyID *authKeyID = NULL; PLArenaPool *arena = NULL; SECStatus rv = SECSuccess; CERTCertificate *issuerCert = NULL; CERTGeneralName *genNames; CERTName *directoryName = NULL; issuerCert = CERT_FindCertByNameString(handle, issuerNameStr); arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { error_allocate(); } authKeyID = PORT_ArenaZAlloc(arena, sizeof(CERTAuthKeyID)); if (authKeyID == NULL) { error_allocate(); } if (find_field_bool(data, "authorityKeyIdentifier-radio-keyIdentifier", PR_TRUE)) { authKeyID->keyID.data = PORT_ArenaAlloc(arena, PORT_Strlen((char *)issuerCert->subjectKeyID.data)); if (authKeyID->keyID.data == NULL) { error_allocate(); } PORT_Memcpy(authKeyID->keyID.data, issuerCert->subjectKeyID.data, authKeyID->keyID.len = PORT_Strlen((char *)issuerCert->subjectKeyID.data)); } else { PORT_Assert(arena); genNames = (CERTGeneralName *)PORT_ArenaZAlloc(arena, (sizeof(CERTGeneralName))); if (genNames == NULL) { error_allocate(); } genNames->l.next = genNames->l.prev = &(genNames->l); genNames->type = certDirectoryName; directoryName = CERT_AsciiToName(issuerCert->subjectName); if (!directoryName) { error_out("ERROR: Unable to create Directory Name"); } rv = CERT_CopyName(arena, &genNames->name.directoryName, directoryName); CERT_DestroyName(directoryName); if (rv != SECSuccess) { error_out("ERROR: Unable to copy Directory Name"); } authKeyID->authCertIssuer = genNames; if (authKeyID->authCertIssuer == NULL && SECFailure == PORT_GetError()) { error_out("ERROR: Unable to get Issuer General Name for Authority Key ID Extension"); } authKeyID->authCertSerialNumber = issuerCert->serialNumber; } rv = EncodeAndAddExtensionValue(arena, extHandle, authKeyID, PR_FALSE, SEC_OID_X509_AUTH_KEY_ID, (EXTEN_VALUE_ENCODER) CERT_EncodeAuthKeyID); if (arena) { PORT_FreeArena(arena, PR_FALSE); } return (rv); } static SECStatus AddPrivKeyUsagePeriod(void *extHandle, Pair *data, CERTCertificate *cert) { char *notBeforeStr; char *notAfterStr; PLArenaPool *arena = NULL; SECStatus rv = SECSuccess; CERTPrivKeyUsagePeriod *pkup; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { error_allocate(); } pkup = PORT_ArenaZNew(arena, CERTPrivKeyUsagePeriod); if (pkup == NULL) { error_allocate(); } notBeforeStr = (char *)PORT_Alloc(16); if (notBeforeStr == NULL) { error_allocate(); } notAfterStr = (char *)PORT_Alloc(16); if (notAfterStr == NULL) { error_allocate(); } *notBeforeStr = '\0'; *notAfterStr = '\0'; pkup->arena = arena; pkup->notBefore.len = 0; pkup->notBefore.data = NULL; pkup->notAfter.len = 0; pkup->notAfter.data = NULL; if (find_field_bool(data, "privKeyUsagePeriod-radio-notBefore", PR_TRUE) || find_field_bool(data, "privKeyUsagePeriod-radio-both", PR_TRUE)) { pkup->notBefore.len = 15; pkup->notBefore.data = (unsigned char *)notBeforeStr; if (find_field_bool(data, "privKeyUsagePeriod-notBefore-radio-manual", PR_TRUE)) { PORT_Strcat(notBeforeStr, find_field(data, "privKeyUsagePeriod-notBefore-year", PR_TRUE)); PORT_Strcat(notBeforeStr, find_field(data, "privKeyUsagePeriod-notBefore-month", PR_TRUE)); PORT_Strcat(notBeforeStr, find_field(data, "privKeyUsagePeriod-notBefore-day", PR_TRUE)); PORT_Strcat(notBeforeStr, find_field(data, "privKeyUsagePeriod-notBefore-hour", PR_TRUE)); PORT_Strcat(notBeforeStr, find_field(data, "privKeyUsagePeriod-notBefore-minute", PR_TRUE)); PORT_Strcat(notBeforeStr, find_field(data, "privKeyUsagePeriod-notBefore-second", PR_TRUE)); if ((*(notBeforeStr + 14) != '\0') || (!isdigit(*(notBeforeStr + 13))) || (*(notBeforeStr + 12) >= '5' && *(notBeforeStr + 12) <= '0') || (!isdigit(*(notBeforeStr + 11))) || (*(notBeforeStr + 10) >= '5' && *(notBeforeStr + 10) <= '0') || (!isdigit(*(notBeforeStr + 9))) || (*(notBeforeStr + 8) >= '2' && *(notBeforeStr + 8) <= '0') || (!isdigit(*(notBeforeStr + 7))) || (*(notBeforeStr + 6) >= '3' && *(notBeforeStr + 6) <= '0') || (!isdigit(*(notBeforeStr + 5))) || (*(notBeforeStr + 4) >= '1' && *(notBeforeStr + 4) <= '0') || (!isdigit(*(notBeforeStr + 3))) || (!isdigit(*(notBeforeStr + 2))) || (!isdigit(*(notBeforeStr + 1))) || (!isdigit(*(notBeforeStr + 0))) || (*(notBeforeStr + 8) == '2' && *(notBeforeStr + 9) >= '4') || (*(notBeforeStr + 6) == '3' && *(notBeforeStr + 7) >= '1') || (*(notBeforeStr + 4) == '1' && *(notBeforeStr + 5) >= '2')) { error_out("ERROR: Improperly formated private key usage period"); } *(notBeforeStr + 14) = 'Z'; *(notBeforeStr + 15) = '\0'; } else { if ((*(cert->validity.notBefore.data) > '5') || ((*(cert->validity.notBefore.data) == '5') && (*(cert->validity.notBefore.data + 1) != '0'))) { PORT_Strcat(notBeforeStr, "19"); } else { PORT_Strcat(notBeforeStr, "20"); } PORT_Strcat(notBeforeStr, (char *)cert->validity.notBefore.data); } } if (find_field_bool(data, "privKeyUsagePeriod-radio-notAfter", PR_TRUE) || find_field_bool(data, "privKeyUsagePeriod-radio-both", PR_TRUE)) { pkup->notAfter.len = 15; pkup->notAfter.data = (unsigned char *)notAfterStr; PORT_Strcat(notAfterStr, find_field(data, "privKeyUsagePeriod-notAfter-year", PR_TRUE)); PORT_Strcat(notAfterStr, find_field(data, "privKeyUsagePeriod-notAfter-month", PR_TRUE)); PORT_Strcat(notAfterStr, find_field(data, "privKeyUsagePeriod-notAfter-day", PR_TRUE)); PORT_Strcat(notAfterStr, find_field(data, "privKeyUsagePeriod-notAfter-hour", PR_TRUE)); PORT_Strcat(notAfterStr, find_field(data, "privKeyUsagePeriod-notAfter-minute", PR_TRUE)); PORT_Strcat(notAfterStr, find_field(data, "privKeyUsagePeriod-notAfter-second", PR_TRUE)); if ((*(notAfterStr + 14) != '\0') || (!isdigit(*(notAfterStr + 13))) || (*(notAfterStr + 12) >= '5' && *(notAfterStr + 12) <= '0') || (!isdigit(*(notAfterStr + 11))) || (*(notAfterStr + 10) >= '5' && *(notAfterStr + 10) <= '0') || (!isdigit(*(notAfterStr + 9))) || (*(notAfterStr + 8) >= '2' && *(notAfterStr + 8) <= '0') || (!isdigit(*(notAfterStr + 7))) || (*(notAfterStr + 6) >= '3' && *(notAfterStr + 6) <= '0') || (!isdigit(*(notAfterStr + 5))) || (*(notAfterStr + 4) >= '1' && *(notAfterStr + 4) <= '0') || (!isdigit(*(notAfterStr + 3))) || (!isdigit(*(notAfterStr + 2))) || (!isdigit(*(notAfterStr + 1))) || (!isdigit(*(notAfterStr + 0))) || (*(notAfterStr + 8) == '2' && *(notAfterStr + 9) >= '4') || (*(notAfterStr + 6) == '3' && *(notAfterStr + 7) >= '1') || (*(notAfterStr + 4) == '1' && *(notAfterStr + 5) >= '2')) { error_out("ERROR: Improperly formated private key usage period"); } *(notAfterStr + 14) = 'Z'; *(notAfterStr + 15) = '\0'; } PORT_Assert(arena); rv = EncodeAndAddExtensionValue(arena, extHandle, pkup, find_field_bool(data, "privKeyUsagePeriod-crit", PR_TRUE), SEC_OID_X509_PRIVATE_KEY_USAGE_PERIOD, (EXTEN_VALUE_ENCODER) CERT_EncodePrivateKeyUsagePeriod); PORT_FreeArena(arena, PR_FALSE); PORT_Free(notBeforeStr); PORT_Free(notAfterStr); return (rv); } static SECStatus AddBasicConstraint(void *extHandle, Pair *data) { CERTBasicConstraints basicConstraint; SECItem encodedValue; SECStatus rv; encodedValue.data = NULL; encodedValue.len = 0; basicConstraint.pathLenConstraint = CERT_UNLIMITED_PATH_CONSTRAINT; basicConstraint.isCA = (find_field_bool(data, "basicConstraints-cA-radio-CA", PR_TRUE)); if (find_field_bool(data, "basicConstraints-pathLengthConstraint", PR_TRUE)) { basicConstraint.pathLenConstraint = atoi(find_field(data, "basicConstraints-pathLengthConstraint-text", PR_TRUE)); } rv = CERT_EncodeBasicConstraintValue(NULL, &basicConstraint, &encodedValue); if (rv) return (rv); rv = CERT_AddExtension(extHandle, SEC_OID_X509_BASIC_CONSTRAINTS, &encodedValue, (find_field_bool(data, "basicConstraints-crit", PR_TRUE)), PR_TRUE); PORT_Free(encodedValue.data); return (rv); } static SECStatus AddNscpCertType(void *extHandle, Pair *data) { SECItem bitStringValue; unsigned char CertType = 0x0; if (find_field_bool(data, "netscape-cert-type-ssl-client", PR_TRUE)) { CertType |= (0x80 >> 0); } if (find_field_bool(data, "netscape-cert-type-ssl-server", PR_TRUE)) { CertType |= (0x80 >> 1); } if (find_field_bool(data, "netscape-cert-type-smime", PR_TRUE)) { CertType |= (0x80 >> 2); } if (find_field_bool(data, "netscape-cert-type-object-signing", PR_TRUE)) { CertType |= (0x80 >> 3); } if (find_field_bool(data, "netscape-cert-type-reserved", PR_TRUE)) { CertType |= (0x80 >> 4); } if (find_field_bool(data, "netscape-cert-type-ssl-ca", PR_TRUE)) { CertType |= (0x80 >> 5); } if (find_field_bool(data, "netscape-cert-type-smime-ca", PR_TRUE)) { CertType |= (0x80 >> 6); } if (find_field_bool(data, "netscape-cert-type-object-signing-ca", PR_TRUE)) { CertType |= (0x80 >> 7); } bitStringValue.data = &CertType; bitStringValue.len = 1; return (CERT_EncodeAndAddBitStrExtension(extHandle, SEC_OID_NS_CERT_EXT_CERT_TYPE, &bitStringValue, (find_field_bool(data, "netscape-cert-type-crit", PR_TRUE)))); } static SECStatus add_IA5StringExtension(void *extHandle, char *string, PRBool crit, int idtag) { SECItem encodedValue; SECStatus rv; encodedValue.data = NULL; encodedValue.len = 0; rv = CERT_EncodeIA5TypeExtension(NULL, string, &encodedValue); if (rv) { return (rv); } return (CERT_AddExtension(extHandle, idtag, &encodedValue, crit, PR_TRUE)); } static SECItem * string_to_oid(char *string) { int i; int length = 20; int remaining; int first_value; int second_value; int value; int oidLength; unsigned char *oidString; unsigned char *write; unsigned char *read; unsigned char *temp; SECItem *oid; remaining = length; i = 0; while (*string == ' ') { string++; } while (isdigit(*(string + i))) { i++; } if (*(string + i) == '.') { *(string + i) = '\0'; } else { error_out("ERROR: Improperly formated OID"); } first_value = atoi(string); if (first_value < 0 || first_value > 2) { error_out("ERROR: Improperly formated OID"); } string += i + 1; i = 0; while (isdigit(*(string + i))) { i++; } if (*(string + i) == '.') { *(string + i) = '\0'; } else { error_out("ERROR: Improperly formated OID"); } second_value = atoi(string); if (second_value < 0 || second_value > 39) { error_out("ERROR: Improperly formated OID"); } oidString = PORT_ZAlloc(2); *oidString = (first_value * 40) + second_value; *(oidString + 1) = '\0'; oidLength = 1; string += i + 1; i = 0; temp = write = PORT_ZAlloc(length); while (*string != '\0') { value = 0; while (isdigit(*(string + i))) { i++; } if (*(string + i) == '\0') { value = atoi(string); string += i; } else { if (*(string + i) == '.') { *(string + i) = '\0'; value = atoi(string); string += i + 1; } else { *(string + i) = '\0'; i++; value = atoi(string); while (*(string + i) == ' ') i++; if (*(string + i) != '\0') { error_out("ERROR: Improperly formated OID"); } } } i = 0; while (value != 0) { if (remaining < 1) { remaining += length; length = length * 2; temp = PORT_Realloc(temp, length); write = temp + length - remaining; } *write = (value & 0x7f) | (0x80); write++; remaining--; value = value >> 7; } *temp = *temp & (0x7f); oidLength += write - temp; oidString = PORT_Realloc(oidString, (oidLength + 1)); read = write - 1; write = oidLength + oidString - 1; for (i = 0; i < (length - remaining); i++) { *write = *read; write--; read++; } write = temp; remaining = length; } *(oidString + oidLength) = '\0'; oid = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); oid->data = oidString; oid->len = oidLength; PORT_Free(temp); return oid; } static SECItem * string_to_ipaddress(char *string) { int i = 0; int value; int j = 0; SECItem *ipaddress; while (*string == ' ') { string++; } ipaddress = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); ipaddress->data = PORT_ZAlloc(9); while (*string != '\0' && j < 8) { while (isdigit(*(string + i))) { i++; } if (*(string + i) == '.') { *(string + i) = '\0'; value = atoi(string); string = string + i + 1; i = 0; } else { if (*(string + i) == '\0') { value = atoi(string); string = string + i; i = 0; } else { *(string + i) = '\0'; while (*(string + i) == ' ') { i++; } if (*(string + i) == '\0') { value = atoi(string); string = string + i; i = 0; } else { error_out("ERROR: Improperly formated IP Address"); } } } if (value >= 0 && value < 256) { *(ipaddress->data + j) = value; } else { error_out("ERROR: Improperly formated IP Address"); } j++; } *(ipaddress->data + j) = '\0'; if (j != 4 && j != 8) { error_out("ERROR: Improperly formated IP Address"); } ipaddress->len = j; return ipaddress; } static int chr_to_hex(char c) { if (isdigit(c)) { return c - '0'; } if (isxdigit(c)) { return toupper(c) - 'A' + 10; } return -1; } static SECItem * string_to_binary(char *string) { SECItem *rv; rv = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); if (rv == NULL) { error_allocate(); } rv->data = (unsigned char *)PORT_ZAlloc((PORT_Strlen(string)) / 3 + 2); rv->len = 0; while (*string && !isxdigit(*string)) { string++; } while (*string) { int high, low; high = chr_to_hex(*string++); low = chr_to_hex(*string++); if (high < 0 || low < 0) { error_out("ERROR: Improperly formated binary encoding"); } rv->data[(rv->len)++] = high << 4 | low; if (*string != ':') { break; } ++string; } while (*string == ' ') { ++string; } if (*string) { error_out("ERROR: Junk after binary encoding"); } return rv; } static SECStatus MakeGeneralName(char *name, CERTGeneralName *genName, PLArenaPool *arena) { SECItem *oid; SECOidData *oidData; SECItem *ipaddress; SECItem *temp = NULL; int i; int nameType; PRBool binary = PR_FALSE; SECStatus rv = SECSuccess; PRBool nickname = PR_FALSE; PORT_Assert(genName); PORT_Assert(arena); nameType = *(name + PORT_Strlen(name) - 1) - '0'; if (nameType == 0 && *(name + PORT_Strlen(name) - 2) == '1') { nickname = PR_TRUE; nameType = certOtherName; } if (nameType < 1 || nameType > 9) { error_out("ERROR: Unknown General Name Type"); } *(name + PORT_Strlen(name) - 4) = '\0'; genName->type = nameType; switch (genName->type) { case certURI: case certRFC822Name: case certDNSName: { genName->name.other.data = (unsigned char *)name; genName->name.other.len = PORT_Strlen(name); break; } case certIPAddress: { ipaddress = string_to_ipaddress(name); genName->name.other.data = ipaddress->data; genName->name.other.len = ipaddress->len; break; } case certRegisterID: { oid = string_to_oid(name); genName->name.other.data = oid->data; genName->name.other.len = oid->len; break; } case certEDIPartyName: case certX400Address: { genName->name.other.data = PORT_ArenaAlloc(arena, PORT_Strlen(name) + 2); if (genName->name.other.data == NULL) { error_allocate(); } PORT_Memcpy(genName->name.other.data + 2, name, PORT_Strlen(name)); /* This may not be accurate for all cases. For now, use this tag type */ genName->name.other.data[0] = (char)(((genName->type - 1) & 0x1f) | 0x80); genName->name.other.data[1] = (char)PORT_Strlen(name); genName->name.other.len = PORT_Strlen(name) + 2; break; } case certOtherName: { i = 0; if (!nickname) { while (!isdigit(*(name + PORT_Strlen(name) - i))) { i++; } if (*(name + PORT_Strlen(name) - i) == '1') { binary = PR_TRUE; } else { binary = PR_FALSE; } while (*(name + PORT_Strlen(name) - i) != '-') { i++; } *(name + PORT_Strlen(name) - i - 1) = '\0'; i = 0; while (*(name + i) != '-') { i++; } *(name + i - 1) = '\0'; oid = string_to_oid(name + i + 2); } else { oidData = SECOID_FindOIDByTag(SEC_OID_NETSCAPE_NICKNAME); oid = &oidData->oid; while (*(name + PORT_Strlen(name) - i) != '-') { i++; } *(name + PORT_Strlen(name) - i) = '\0'; } genName->name.OthName.oid.data = oid->data; genName->name.OthName.oid.len = oid->len; if (binary) { temp = string_to_binary(name); genName->name.OthName.name.data = temp->data; genName->name.OthName.name.len = temp->len; } else { temp = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); if (temp == NULL) { error_allocate(); } temp->data = (unsigned char *)name; temp->len = PORT_Strlen(name); SEC_ASN1EncodeItem(arena, &(genName->name.OthName.name), temp, CERTIA5TypeTemplate); } PORT_Free(temp); break; } case certDirectoryName: { CERTName *directoryName = NULL; directoryName = CERT_AsciiToName(name); if (!directoryName) { error_out("ERROR: Improperly formated alternative name"); break; } rv = CERT_CopyName(arena, &genName->name.directoryName, directoryName); CERT_DestroyName(directoryName); break; } } genName->l.next = &(genName->l); genName->l.prev = &(genName->l); return rv; } static CERTGeneralName * MakeAltName(Pair *data, char *which, PLArenaPool *arena) { CERTGeneralName *SubAltName; CERTGeneralName *current; CERTGeneralName *newname; char *name = NULL; SECStatus rv = SECSuccess; int len; len = PORT_Strlen(which); name = find_field(data, which, PR_TRUE); SubAltName = current = (CERTGeneralName *)PORT_ZAlloc(sizeof(CERTGeneralName)); if (current == NULL) { error_allocate(); } while (name != NULL) { rv = MakeGeneralName(name, current, arena); if (rv != SECSuccess) { break; } if (*(which + len - 1) < '9') { *(which + len - 1) = *(which + len - 1) + 1; } else { if (isdigit(*(which + len - 2))) { *(which + len - 2) = *(which + len - 2) + 1; *(which + len - 1) = '0'; } else { *(which + len - 1) = '1'; *(which + len) = '0'; *(which + len + 1) = '\0'; len++; } } len = PORT_Strlen(which); name = find_field(data, which, PR_TRUE); if (name != NULL) { newname = (CERTGeneralName *)PORT_ZAlloc(sizeof(CERTGeneralName)); if (newname == NULL) { error_allocate(); } current->l.next = &(newname->l); newname->l.prev = &(current->l); current = newname; newname = NULL; } else { current->l.next = &(SubAltName->l); SubAltName->l.prev = &(current->l); } } if (rv == SECFailure) { return NULL; } return SubAltName; } static CERTNameConstraints * MakeNameConstraints(Pair *data, PLArenaPool *arena) { CERTNameConstraints *NameConstraints; CERTNameConstraint *current = NULL; CERTNameConstraint *last_permited = NULL; CERTNameConstraint *last_excluded = NULL; char *constraint = NULL; char *which; SECStatus rv = SECSuccess; int len; int i; long max; long min; PRBool permited; NameConstraints = (CERTNameConstraints *)PORT_ZAlloc(sizeof(CERTNameConstraints)); which = make_copy_string("NameConstraintSelect0", 25, '\0'); len = PORT_Strlen(which); constraint = find_field(data, which, PR_TRUE); NameConstraints->permited = NameConstraints->excluded = NULL; while (constraint != NULL) { current = (CERTNameConstraint *)PORT_ZAlloc(sizeof(CERTNameConstraint)); if (current == NULL) { error_allocate(); } i = 0; while (*(constraint + PORT_Strlen(constraint) - i) != '-') { i++; } *(constraint + PORT_Strlen(constraint) - i - 1) = '\0'; max = (long)atoi(constraint + PORT_Strlen(constraint) + 3); if (max > 0) { (void)SEC_ASN1EncodeInteger(arena, ¤t->max, max); } i = 0; while (*(constraint + PORT_Strlen(constraint) - i) != '-') { i++; } *(constraint + PORT_Strlen(constraint) - i - 1) = '\0'; min = (long)atoi(constraint + PORT_Strlen(constraint) + 3); (void)SEC_ASN1EncodeInteger(arena, ¤t->min, min); while (*(constraint + PORT_Strlen(constraint) - i) != '-') { i++; } *(constraint + PORT_Strlen(constraint) - i - 1) = '\0'; if (*(constraint + PORT_Strlen(constraint) + 3) == 'p') { permited = PR_TRUE; } else { permited = PR_FALSE; } rv = MakeGeneralName(constraint, &(current->name), arena); if (rv != SECSuccess) { break; } if (*(which + len - 1) < '9') { *(which + len - 1) = *(which + len - 1) + 1; } else { if (isdigit(*(which + len - 2))) { *(which + len - 2) = *(which + len - 2) + 1; *(which + len - 1) = '0'; } else { *(which + len - 1) = '1'; *(which + len) = '0'; *(which + len + 1) = '\0'; len++; } } len = PORT_Strlen(which); if (permited) { if (NameConstraints->permited == NULL) { NameConstraints->permited = last_permited = current; } last_permited->l.next = &(current->l); current->l.prev = &(last_permited->l); last_permited = current; } else { if (NameConstraints->excluded == NULL) { NameConstraints->excluded = last_excluded = current; } last_excluded->l.next = &(current->l); current->l.prev = &(last_excluded->l); last_excluded = current; } constraint = find_field(data, which, PR_TRUE); if (constraint != NULL) { current = (CERTNameConstraint *)PORT_ZAlloc(sizeof(CERTNameConstraint)); if (current == NULL) { error_allocate(); } } } if (NameConstraints->permited != NULL) { last_permited->l.next = &(NameConstraints->permited->l); NameConstraints->permited->l.prev = &(last_permited->l); } if (NameConstraints->excluded != NULL) { last_excluded->l.next = &(NameConstraints->excluded->l); NameConstraints->excluded->l.prev = &(last_excluded->l); } if (which != NULL) { PORT_Free(which); } if (rv == SECFailure) { return NULL; } return NameConstraints; } static SECStatus AddAltName(void *extHandle, Pair *data, char *issuerNameStr, CERTCertDBHandle *handle, int type) { PRBool autoIssuer = PR_FALSE; PLArenaPool *arena = NULL; CERTGeneralName *genName = NULL; char *which = NULL; char *name = NULL; SECStatus rv = SECSuccess; SECItem *issuersAltName = NULL; CERTCertificate *issuerCert = NULL; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { error_allocate(); } if (type == 0) { which = make_copy_string("SubAltNameSelect0", 20, '\0'); genName = MakeAltName(data, which, arena); } else { if (autoIssuer) { autoIssuer = find_field_bool(data, "IssuerAltNameSourceRadio-auto", PR_TRUE); issuerCert = CERT_FindCertByNameString(handle, issuerNameStr); rv = cert_FindExtension((*issuerCert).extensions, SEC_OID_X509_SUBJECT_ALT_NAME, issuersAltName); if (issuersAltName == NULL) { name = PORT_Alloc(PORT_Strlen((*issuerCert).subjectName) + 4); PORT_Strcpy(name, (*issuerCert).subjectName); PORT_Strcat(name, " - 5"); } } else { which = make_copy_string("IssuerAltNameSelect0", 20, '\0'); genName = MakeAltName(data, which, arena); } } if (type == 0) { EncodeAndAddExtensionValue(arena, extHandle, genName, find_field_bool(data, "SubAltName-crit", PR_TRUE), SEC_OID_X509_SUBJECT_ALT_NAME, (EXTEN_VALUE_ENCODER) CERT_EncodeAltNameExtension); } else { if (autoIssuer && (name == NULL)) { rv = CERT_AddExtension(extHandle, SEC_OID_X509_ISSUER_ALT_NAME, issuersAltName, find_field_bool(data, "IssuerAltName-crit", PR_TRUE), PR_TRUE); } else { EncodeAndAddExtensionValue(arena, extHandle, genName, find_field_bool(data, "IssuerAltName-crit", PR_TRUE), SEC_OID_X509_ISSUER_ALT_NAME, (EXTEN_VALUE_ENCODER) CERT_EncodeAltNameExtension); } } if (which != NULL) { PORT_Free(which); } if (issuerCert != NULL) { CERT_DestroyCertificate(issuerCert); } return rv; } static SECStatus AddNameConstraints(void *extHandle, Pair *data) { PLArenaPool *arena = NULL; CERTNameConstraints *constraints = NULL; SECStatus rv = SECSuccess; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { error_allocate(); } constraints = MakeNameConstraints(data, arena); if (constraints != NULL) { EncodeAndAddExtensionValue(arena, extHandle, constraints, PR_TRUE, SEC_OID_X509_NAME_CONSTRAINTS, (EXTEN_VALUE_ENCODER) CERT_EncodeNameConstraintsExtension); } if (arena != NULL) { PORT_ArenaRelease(arena, NULL); } return rv; } static SECStatus add_extensions(CERTCertificate *subjectCert, Pair *data, char *issuerNameStr, CERTCertDBHandle *handle) { void *extHandle; SECStatus rv = SECSuccess; extHandle = CERT_StartCertExtensions(subjectCert); if (extHandle == NULL) { error_out("ERROR: Unable to get certificates extension handle"); } if (find_field_bool(data, "keyUsage", PR_TRUE)) { rv = AddKeyUsage(extHandle, data); if (rv != SECSuccess) { error_out("ERROR: Unable to add Key Usage extension"); } } if (find_field_bool(data, "extKeyUsage", PR_TRUE)) { rv = AddExtKeyUsage(extHandle, data); if (SECSuccess != rv) { error_out("ERROR: Unable to add Extended Key Usage extension"); } } if (find_field_bool(data, "basicConstraints", PR_TRUE)) { rv = AddBasicConstraint(extHandle, data); if (rv != SECSuccess) { error_out("ERROR: Unable to add Basic Constraint extension"); } } if (find_field_bool(data, "subjectKeyIdentifier", PR_TRUE)) { rv = AddSubKeyID(extHandle, data, subjectCert); if (rv != SECSuccess) { error_out("ERROR: Unable to add Subject Key Identifier Extension"); } } if (find_field_bool(data, "authorityKeyIdentifier", PR_TRUE)) { rv = AddAuthKeyID(extHandle, data, issuerNameStr, handle); if (rv != SECSuccess) { error_out("ERROR: Unable to add Authority Key Identifier extension"); } } if (find_field_bool(data, "privKeyUsagePeriod", PR_TRUE)) { rv = AddPrivKeyUsagePeriod(extHandle, data, subjectCert); if (rv != SECSuccess) { error_out("ERROR: Unable to add Private Key Usage Period extension"); } } if (find_field_bool(data, "SubAltName", PR_TRUE)) { rv = AddAltName(extHandle, data, NULL, NULL, 0); if (rv != SECSuccess) { error_out("ERROR: Unable to add Subject Alternative Name extension"); } } if (find_field_bool(data, "IssuerAltName", PR_TRUE)) { rv = AddAltName(extHandle, data, issuerNameStr, handle, 1); if (rv != SECSuccess) { error_out("ERROR: Unable to add Issuer Alternative Name Extension"); } } if (find_field_bool(data, "NameConstraints", PR_TRUE)) { rv = AddNameConstraints(extHandle, data); if (rv != SECSuccess) { error_out("ERROR: Unable to add Name Constraints Extension"); } } if (find_field_bool(data, "netscape-cert-type", PR_TRUE)) { rv = AddNscpCertType(extHandle, data); if (rv != SECSuccess) { error_out("ERROR: Unable to add Netscape Certificate Type Extension"); } } if (find_field_bool(data, "netscape-base-url", PR_TRUE)) { rv = add_IA5StringExtension(extHandle, find_field(data, "netscape-base-url-text", PR_TRUE), find_field_bool(data, "netscape-base-url-crit", PR_TRUE), SEC_OID_NS_CERT_EXT_BASE_URL); if (rv != SECSuccess) { error_out("ERROR: Unable to add Netscape Base URL Extension"); } } if (find_field_bool(data, "netscape-revocation-url", PR_TRUE)) { rv = add_IA5StringExtension(extHandle, find_field(data, "netscape-revocation-url-text", PR_TRUE), find_field_bool(data, "netscape-revocation-url-crit", PR_TRUE), SEC_OID_NS_CERT_EXT_REVOCATION_URL); if (rv != SECSuccess) { error_out("ERROR: Unable to add Netscape Revocation URL Extension"); } } if (find_field_bool(data, "netscape-ca-revocation-url", PR_TRUE)) { rv = add_IA5StringExtension(extHandle, find_field(data, "netscape-ca-revocation-url-text", PR_TRUE), find_field_bool(data, "netscape-ca-revocation-url-crit", PR_TRUE), SEC_OID_NS_CERT_EXT_CA_REVOCATION_URL); if (rv != SECSuccess) { error_out("ERROR: Unable to add Netscape CA Revocation URL Extension"); } } if (find_field_bool(data, "netscape-cert-renewal-url", PR_TRUE)) { rv = add_IA5StringExtension(extHandle, find_field(data, "netscape-cert-renewal-url-text", PR_TRUE), find_field_bool(data, "netscape-cert-renewal-url-crit", PR_TRUE), SEC_OID_NS_CERT_EXT_CERT_RENEWAL_URL); if (rv != SECSuccess) { error_out("ERROR: Unable to add Netscape Certificate Renewal URL Extension"); } } if (find_field_bool(data, "netscape-ca-policy-url", PR_TRUE)) { rv = add_IA5StringExtension(extHandle, find_field(data, "netscape-ca-policy-url-text", PR_TRUE), find_field_bool(data, "netscape-ca-policy-url-crit", PR_TRUE), SEC_OID_NS_CERT_EXT_CA_POLICY_URL); if (rv != SECSuccess) { error_out("ERROR: Unable to add Netscape CA Policy URL Extension"); } } if (find_field_bool(data, "netscape-ssl-server-name", PR_TRUE)) { rv = add_IA5StringExtension(extHandle, find_field(data, "netscape-ssl-server-name-text", PR_TRUE), find_field_bool(data, "netscape-ssl-server-name-crit", PR_TRUE), SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME); if (rv != SECSuccess) { error_out("ERROR: Unable to add Netscape SSL Server Name Extension"); } } if (find_field_bool(data, "netscape-comment", PR_TRUE)) { rv = add_IA5StringExtension(extHandle, find_field(data, "netscape-comment-text", PR_TRUE), find_field_bool(data, "netscape-comment-crit", PR_TRUE), SEC_OID_NS_CERT_EXT_COMMENT); if (rv != SECSuccess) { error_out("ERROR: Unable to add Netscape Comment Extension"); } } CERT_FinishExtensions(extHandle); return (rv); } char * return_dbpasswd(PK11SlotInfo *slot, PRBool retry, void *data) { char *rv; /* don't clobber our poor smart card */ if (retry == PR_TRUE) { return NULL; } rv = PORT_Alloc(4); PORT_Strcpy(rv, "foo"); return rv; } SECKEYPrivateKey * FindPrivateKeyFromNameStr(char *name, CERTCertDBHandle *certHandle) { SECKEYPrivateKey *key; CERTCertificate *cert; CERTCertificate *p11Cert; /* We don't presently have a PK11 function to find a cert by ** subject name. ** We do have a function to find a cert in the internal slot's ** cert db by subject name, but it doesn't setup the slot info. ** So, this HACK works, but should be replaced as soon as we ** have a function to search for certs accross slots by subject name. */ cert = CERT_FindCertByNameString(certHandle, name); if (cert == NULL || cert->nickname == NULL) { error_out("ERROR: Unable to retrieve issuers certificate"); } p11Cert = PK11_FindCertFromNickname(cert->nickname, NULL); if (p11Cert == NULL) { error_out("ERROR: Unable to retrieve issuers certificate"); } key = PK11_FindKeyByAnyCert(p11Cert, NULL); return key; } static SECItem * SignCert(CERTCertificate *cert, char *issuerNameStr, Pair *data, CERTCertDBHandle *handle, int which_key) { SECItem der; SECKEYPrivateKey *caPrivateKey = NULL; SECStatus rv; PLArenaPool *arena; SECOidTag algID; if (which_key == 0) { caPrivateKey = FindPrivateKeyFromNameStr(issuerNameStr, handle); } else { caPrivateKey = privkeys[which_key - 1]; } if (caPrivateKey == NULL) { error_out("ERROR: unable to retrieve issuers key"); } arena = cert->arena; algID = SEC_GetSignatureAlgorithmOidTag(caPrivateKey->keyType, SEC_OID_UNKNOWN); if (algID == SEC_OID_UNKNOWN) { error_out("ERROR: Unknown key type for issuer."); goto done; } rv = SECOID_SetAlgorithmID(arena, &cert->signature, algID, 0); if (rv != SECSuccess) { error_out("ERROR: Could not set signature algorithm id."); } if (find_field_bool(data, "ver-1", PR_TRUE)) { *(cert->version.data) = 0; cert->version.len = 1; } else { *(cert->version.data) = 2; cert->version.len = 1; } der.data = NULL; der.len = 0; (void)SEC_ASN1EncodeItem(arena, &der, cert, CERT_CertificateTemplate); if (der.data == NULL) { error_out("ERROR: Could not encode certificate.\n"); } rv = SEC_DerSignData(arena, &(cert->derCert), der.data, der.len, caPrivateKey, algID); if (rv != SECSuccess) { error_out("ERROR: Could not sign encoded certificate data.\n"); } done: SECKEY_DestroyPrivateKey(caPrivateKey); return &(cert->derCert); } int main(int argc, char **argv) { int length = 500; int remaining = 500; int n; int i; int serial; int chainLen; int which_key; char *pos; #ifdef OFFLINE char *form_output = "key=MIIBPTCBpzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA7" "SLqjWBL9Wl11Vlg%0AaMqZCvcQOL%2FnvSqYPPRP0XZy9SoAeyWzQnBOiCm2t8H5mK7r2" "jnKdAQOmfhjaJil%0A3hNVu3SekHOXF6Ze7bkWa6%2FSGVcY%2FojkydxFSgY43nd1iyd" "zPQDp8WWLL%2BpVpt%2B%2B%0ATRhFtVXbF0fQI03j9h3BoTgP2lkCAwEAARYDZm9vMA0" "GCSqGSIb3DQEBBAUAA4GB%0AAJ8UfRKJ0GtG%2B%2BufCC6tAfTzKrq3CTBHnom55EyXc" "sAsv6WbDqI%2F0rLAPkn2Xo1r%0AnNhtMxIuj441blMt%2Fa3AGLOy5zmC7Qawt8IytvQ" "ikQ1XTpTBCXevytrmLjCmlURr%0ANJryTM48WaMQHiMiJpbXCqVJC1d%2FpEWBtqvALzZ" "aOOIy&subject=CN%3D%22test%22%26serial-auto%3Dtrue%26serial_value%3D%" "26ver-1%3Dtrue%26ver-3%3Dfalse%26caChoiceradio-SignWithDefaultkey%3Dt" "rue%26caChoiceradio-SignWithRandomChain%3Dfalse%26autoCAs%3D%26caChoi" "ceradio-SignWithSpecifiedChain%3Dfalse%26manCAs%3D%26%24"; #else char *form_output; #endif char *issuerNameStr; char *certName; char *DBdir = DB_DIRECTORY; char *prefixs[10] = { "CA#1-", "CA#2-", "CA#3-", "CA#4-", "CA#5-", "CA#6-", "CA#7-", "CA#8-", "CA#9-", "" }; Pair *form_data; CERTCertificate *cert; CERTCertDBHandle *handle; CERTCertificateRequest *certReq = NULL; int warpmonths = 0; SECItem *certDER; #ifdef FILEOUT FILE *outfile; #endif SECStatus status = SECSuccess; extern char prefix[PREFIX_LEN]; SEC_PKCS7ContentInfo *certChain; SECItem *encodedCertChain; PRBool UChain = PR_FALSE; progName = strrchr(argv[0], '/'); progName = progName ? progName + 1 : argv[0]; #ifdef TEST sleep(20); #endif SECU_ConfigDirectory(DBdir); PK11_SetPasswordFunc(return_dbpasswd); status = NSS_InitReadWrite(DBdir); if (status != SECSuccess) { SECU_PrintPRandOSError(progName); return -1; } handle = CERT_GetDefaultCertDB(); prefix[0] = '\0'; #if !defined(OFFLINE) form_output = (char *)PORT_Alloc(length); if (form_output == NULL) { error_allocate(); } pos = form_output; while (feof(stdin) == 0) { if (remaining <= 1) { remaining += length; length = length * 2; form_output = PORT_Realloc(form_output, (length)); if (form_output == NULL) { error_allocate(); } pos = form_output + length - remaining; } n = fread(pos, 1, (size_t)(remaining - 1), stdin); pos += n; remaining -= n; } *pos = '&'; pos++; length = pos - form_output; #else length = PORT_Strlen(form_output); #endif #ifdef FILEOUT printf("Content-type: text/plain\n\n"); fwrite(form_output, 1, (size_t)length, stdout); printf("\n"); #endif #ifdef FILEOUT fwrite(form_output, 1, (size_t)length, stdout); printf("\n"); fflush(stdout); #endif form_data = make_datastruct(form_output, length); status = clean_input(form_data); #if !defined(OFFLINE) PORT_Free(form_output); #endif #ifdef FILEOUT i = 0; while (return_name(form_data, i) != NULL) { printf("%s", return_name(form_data, i)); printf("=\n"); printf("%s", return_data(form_data, i)); printf("\n"); i++; } printf("I got that done, woo hoo\n"); fflush(stdout); #endif issuerNameStr = PORT_Alloc(200); if (find_field_bool(form_data, "caChoiceradio-SignWithSpecifiedChain", PR_FALSE)) { UChain = PR_TRUE; chainLen = atoi(find_field(form_data, "manCAs", PR_FALSE)); PORT_Strcpy(prefix, prefixs[0]); issuerNameStr = PORT_Strcpy(issuerNameStr, "CN=Cert-O-Matic II, O=Cert-O-Matic II"); if (chainLen == 0) { UChain = PR_FALSE; } } else { if (find_field_bool(form_data, "caChoiceradio-SignWithRandomChain", PR_FALSE)) { PORT_Strcpy(prefix, prefixs[9]); chainLen = atoi(find_field(form_data, "autoCAs", PR_FALSE)); if (chainLen < 1 || chainLen > 18) { issuerNameStr = PORT_Strcpy(issuerNameStr, "CN=CA18, O=Cert-O-Matic II"); } issuerNameStr = PORT_Strcpy(issuerNameStr, "CN=CA"); issuerNameStr = PORT_Strcat(issuerNameStr, find_field(form_data, "autoCAs", PR_FALSE)); issuerNameStr = PORT_Strcat(issuerNameStr, ", O=Cert-O-Matic II"); } else { issuerNameStr = PORT_Strcpy(issuerNameStr, "CN=Cert-O-Matic II, O=Cert-O-Matic II"); } chainLen = 0; } i = -1; which_key = 0; do { extern SECStatus cert_GetKeyID(CERTCertificate * cert); i++; if (i != 0 && UChain) { PORT_Strcpy(prefix, prefixs[i]); } /* find_field(form_data,"subject", PR_TRUE); */ certReq = makeCertReq(form_data, which_key); #ifdef OFFLINE serial = 900; #else serial = get_serial_number(form_data); #endif cert = MakeV1Cert(handle, certReq, issuerNameStr, PR_FALSE, serial, warpmonths, form_data); if (certReq != NULL) { CERT_DestroyCertificateRequest(certReq); } if (find_field_bool(form_data, "ver-3", PR_TRUE)) { status = add_extensions(cert, form_data, issuerNameStr, handle); if (status != SECSuccess) { error_out("ERROR: Unable to add extensions"); } } status = cert_GetKeyID(cert); if (status == SECFailure) { error_out("ERROR: Unable to get Key ID."); } certDER = SignCert(cert, issuerNameStr, form_data, handle, which_key); CERT_NewTempCertificate(handle, certDER, NULL, PR_FALSE, PR_TRUE); issuerNameStr = find_field(form_data, "subject", PR_TRUE); /* SECITEM_FreeItem(certDER, PR_TRUE); */ CERT_DestroyCertificate(cert); if (i == (chainLen - 1)) { i = 8; } ++which_key; } while (i < 9 && UChain); #ifdef FILEOUT outfile = fopen("../certout", "wb"); #endif certName = find_field(form_data, "subject", PR_FALSE); cert = CERT_FindCertByNameString(handle, certName); certChain = SEC_PKCS7CreateCertsOnly(cert, PR_TRUE, handle); if (certChain == NULL) { error_out("ERROR: No certificates in cert chain"); } encodedCertChain = SEC_PKCS7EncodeItem(NULL, NULL, certChain, NULL, NULL, NULL); if (encodedCertChain) { #if !defined(FILEOUT) printf("Content-type: application/x-x509-user-cert\r\n"); printf("Content-length: %d\r\n\r\n", encodedCertChain->len); fwrite(encodedCertChain->data, 1, encodedCertChain->len, stdout); #else fwrite(encodedCertChain->data, 1, encodedCertChain->len, outfile); #endif } else { error_out("Error: Unable to DER encode certificate"); } #ifdef FILEOUT printf("\nI got here!\n"); fflush(outfile); fclose(outfile); #endif fflush(stdout); if (NSS_Shutdown() != SECSuccess) { exit(1); } return 0; }