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