/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "seccomon.h"
#include "nss.h"
#include "keyhi.h"
#include "cert.h"
#include "pk11func.h"
#include "secmod.h"
#include "cmmf.h"
#include "crmf.h"
#include "base64.h"
#include "secasn1.h"
#include "cryptohi.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define DEFAULT_ALLOC_SIZE 200
#define DEFAULT_CGI_VARS 20
typedef struct CGIVariableStr {
char *name;
char *value;
} CGIVariable;
typedef struct CGIVarTableStr {
CGIVariable **variables;
int numVars;
int numAlloc;
} CGIVarTable;
typedef struct CertResponseInfoStr {
CERTCertificate *cert;
long certReqID;
} CertResponseInfo;
typedef struct ChallengeCreationInfoStr {
long random;
SECKEYPublicKey *pubKey;
} ChallengeCreationInfo;
char *missingVar = NULL;
/*
* Error values.
*/
typedef enum {
NO_ERROR = 0,
NSS_INIT_FAILED,
AUTH_FAILED,
REQ_CGI_VAR_NOT_PRESENT,
CRMF_REQ_NOT_PRESENT,
BAD_ASCII_FOR_REQ,
CGI_VAR_MISSING,
COULD_NOT_FIND_CA,
COULD_NOT_DECODE_REQS,
OUT_OF_MEMORY,
ERROR_RETRIEVING_REQUEST_MSG,
ERROR_RETRIEVING_CERT_REQUEST,
ERROR_RETRIEVING_SUBJECT_FROM_REQ,
ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ,
ERROR_CREATING_NEW_CERTIFICATE,
COULD_NOT_START_EXTENSIONS,
ERROR_RETRIEVING_EXT_FROM_REQ,
ERROR_ADDING_EXT_TO_CERT,
ERROR_ENDING_EXTENSIONS,
COULD_NOT_FIND_ISSUER_PRIVATE_KEY,
UNSUPPORTED_SIGN_OPERATION_FOR_ISSUER,
ERROR_SETTING_SIGN_ALG,
ERROR_ENCODING_NEW_CERT,
ERROR_SIGNING_NEW_CERT,
ERROR_CREATING_CERT_REP_CONTENT,
ERROR_CREATING_SINGLE_CERT_RESPONSE,
ERROR_SETTING_CERT_RESPONSES,
ERROR_CREATING_CA_LIST,
ERROR_ADDING_ISSUER_TO_CA_LIST,
ERROR_ENCODING_CERT_REP_CONTENT,
NO_POP_FOR_REQUEST,
UNSUPPORTED_POP,
ERROR_RETRIEVING_POP_SIGN_KEY,
ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY,
ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY,
DO_CHALLENGE_RESPONSE,
ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT,
ERROR_ENCODING_CERT_REQ_FOR_POP,
ERROR_VERIFYING_SIGNATURE_POP,
ERROR_RETRIEVING_PUB_KEY_FOR_CHALL,
ERROR_CREATING_EMPTY_CHAL_CONTENT,
ERROR_EXTRACTING_GEN_NAME_FROM_ISSUER,
ERROR_SETTING_CHALLENGE,
ERROR_ENCODING_CHALL,
ERROR_CONVERTING_CHALL_TO_BASE64,
ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN,
ERROR_CREATING_KEY_RESP_FROM_DER,
ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE,
ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED,
ERROR_GETTING_KEY_ENCIPHERMENT,
ERROR_NO_POP_FOR_PRIVKEY,
ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE
} ErrorCode;
const char *
CGITableFindValue(CGIVarTable *varTable,
const char *key);
void
spitOutHeaders(
void )
{
printf(
"Content-type: text/html\n\n" );
}
void
dumpRequest(CGIVarTable *varTable)
{
int i;
CGIVariable *var;
printf(
"100%%\">\n" );
printf("Variable Name "
" Value \n" );
for (i = 0; i < varTable->numVars; i++) {
var = varTable->variables[i];
printf("%s %s \n" ,
var->name, var->value);
}
printf("
\n" );
}
void
echo_request(CGIVarTable *varTable)
{
spitOutHeaders();
printf(
"CGI Echo Page \n"
"Got the following request \n" );
dumpRequest(varTable);
printf(
"" );
}
void
processVariable(CGIVariable *var)
{
char *plusSign, *percentSign;
/*First look for all of the '+' and convert them to spaces */
plusSign = var->value;
while ((plusSign = strchr(plusSign,
'+' )) != NULL) {
*plusSign =
' ' ;
}
percentSign = var->value;
while ((percentSign = strchr(percentSign,
'%' )) != NULL) {
char string[3];
int value;
string[0] = percentSign[1];
string[1] = percentSign[2];
string[2] =
'\0' ;
sscanf(string,
"%x" , &value);
*percentSign = (
char )value;
memmove(&percentSign[1], &percentSign[3], 1 + strlen(&percentSign[3]));
}
}
char *
parseNextVariable(CGIVarTable *varTable,
char *form_output)
{
char *ampersand, *equal;
CGIVariable *var;
if (varTable->numVars == varTable->numAlloc) {
CGIVariable **newArr = realloc(varTable->variables,
(varTable->numAlloc + DEFAULT_CGI_VARS) *
sizeof (CGIVariable *));
if (newArr == NULL) {
return NULL;
}
varTable->variables = newArr;
varTable->numAlloc += DEFAULT_CGI_VARS;
}
equal = strchr(form_output,
'=' );
if (equal == NULL) {
return NULL;
}
ampersand = strchr(equal,
'&' );
if (ampersand == NULL) {
return NULL;
}
equal[0] =
'\0' ;
if (ampersand != NULL) {
ampersand[0] =
'\0' ;
}
var = malloc(
sizeof (CGIVariable));
var->name = form_output;
var->value = &equal[1];
varTable->variables[varTable->numVars] = var;
varTable->numVars++;
processVariable(var);
return (ampersand != NULL) ? &ersand[1] : NULL;
}
void
ParseInputVariables(CGIVarTable *varTable,
char *form_output)
{
varTable->variables = malloc(
sizeof (CGIVariable *) * DEFAULT_CGI_VARS);
varTable->numVars = 0;
varTable->numAlloc = DEFAULT_CGI_VARS;
while (form_output && form_output[0] !=
'\0' ) {
form_output = parseNextVariable(varTable, form_output);
}
}
const char *
CGITableFindValue(CGIVarTable *varTable,
const char *key)
{
const char *retVal = NULL;
int i;
for (i = 0; i < varTable->numVars; i++) {
if (strcmp(varTable->variables[i]->name, key) == 0) {
retVal = varTable->variables[i]->value;
break ;
}
}
return retVal;
}
char *
passwordCallback(PK11SlotInfo *slot, PRBool retry,
void *arg)
{
const char *passwd;
if (retry) {
return NULL;
}
passwd = CGITableFindValue((CGIVarTable *)arg,
"dbPassword" );
if (passwd == NULL) {
return NULL;
}
return PORT_Strdup(passwd);
}
ErrorCode
initNSS(CGIVarTable *varTable)
{
const char *nssDir;
PK11SlotInfo *keySlot;
SECStatus rv;
nssDir = CGITableFindValue(varTable,
"NSSDirectory" );
if (nssDir == NULL) {
missingVar =
"NSSDirectory" ;
return REQ_CGI_VAR_NOT_PRESENT;
}
rv = NSS_Init(nssDir);
if (rv != SECSuccess) {
return NSS_INIT_FAILED;
}
PK11_SetPasswordFunc(passwordCallback);
keySlot = PK11_GetInternalKeySlot();
rv = PK11_Authenticate(keySlot, PR_FALSE, varTable);
PK11_FreeSlot(keySlot);
if (rv != SECSuccess) {
return AUTH_FAILED;
}
return NO_ERROR;
}
void
dumpErrorMessage(ErrorCode errNum)
{
spitOutHeaders();
printf(
"Error Error processing "
"data Received the error %d"
,
errNum);
if (errNum == REQ_CGI_VAR_NOT_PRESENT) {
printf(
"The missing variable is %s." , missingVar);
}
printf(
"More useful information here in the future. " );
}
ErrorCode
initOldCertReq(CERTCertificateRequest *oldCertReq,
CERTName *subject, CERTSubjectPublicKeyInfo *spki)
{
PLArenaPool *poolp;
poolp = oldCertReq->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
SEC_ASN1EncodeInteger(poolp, &oldCertReq->version,
SEC_CERTIFICATE_VERSION_3);
CERT_CopyName(poolp, &oldCertReq->subject, subject);
SECKEY_CopySubjectPublicKeyInfo(poolp, &oldCertReq->subjectPublicKeyInfo,
spki);
oldCertReq->attributes = NULL;
return NO_ERROR;
}
ErrorCode
addExtensions(CERTCertificate *newCert, CRMFCertRequest *certReq)
{
int numExtensions, i;
void *extHandle;
ErrorCode rv = NO_ERROR;
CRMFCertExtension *ext;
SECStatus srv;
numExtensions = CRMF_CertRequestGetNumberOfExtensions(certReq);
if (numExtensions == 0) {
/* No extensions to add */
return NO_ERROR;
}
extHandle = CERT_StartCertExtensions(newCert);
if (extHandle == NULL) {
rv = COULD_NOT_START_EXTENSIONS;
goto loser;
}
for (i = 0; i < numExtensions; i++) {
ext = CRMF_CertRequestGetExtensionAtIndex(certReq, i);
if (ext == NULL) {
rv = ERROR_RETRIEVING_EXT_FROM_REQ;
}
srv = CERT_AddExtension(extHandle, CRMF_CertExtensionGetOidTag(ext),
CRMF_CertExtensionGetValue(ext),
CRMF_CertExtensionGetIsCritical(ext), PR_FALSE);
if (srv != SECSuccess) {
rv = ERROR_ADDING_EXT_TO_CERT;
}
}
srv = CERT_FinishExtensions(extHandle);
if (srv != SECSuccess) {
rv = ERROR_ENDING_EXTENSIONS;
goto loser;
}
return NO_ERROR;
loser:
return rv;
}
void
writeOutItem(
const char *filePath, SECItem *der)
{
PRFileDesc *outfile;
outfile = PR_Open(filePath,
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
0666);
PR_Write(outfile, der->data, der->len);
PR_Close(outfile);
}
ErrorCode
createNewCert(CERTCertificate **issuedCert, CERTCertificateRequest *oldCertReq,
CRMFCertReqMsg *currReq, CRMFCertRequest *certReq,
CERTCertificate *issuerCert, CGIVarTable *varTable)
{
CERTCertificate *newCert = NULL;
CERTValidity *validity;
PRExplodedTime printableTime;
PRTime now, after;
ErrorCode rv = NO_ERROR;
SECKEYPrivateKey *issuerPrivKey;
SECItem derCert = { 0 };
SECOidTag signTag;
SECStatus srv;
long version;
now = PR_Now();
PR_ExplodeTime(now, PR_GMTParameters, &printableTime);
printableTime.tm_month += 9;
after = PR_ImplodeTime(&printableTime);
validity = CERT_CreateValidity(now, after);
newCert = *issuedCert =
CERT_CreateCertificate(rand(), &(issuerCert->subject), validity,
oldCertReq);
if (newCert == NULL) {
rv = ERROR_CREATING_NEW_CERTIFICATE;
goto loser;
}
rv = addExtensions(newCert, certReq);
if (rv != NO_ERROR) {
goto loser;
}
issuerPrivKey = PK11_FindKeyByAnyCert(issuerCert, varTable);
if (issuerPrivKey == NULL) {
rv = COULD_NOT_FIND_ISSUER_PRIVATE_KEY;
}
signTag = SEC_GetSignatureAlgorithmOidTag(issuerPrivatekey->keytype,
SEC_OID_UNKNOWN);
if (signTag == SEC_OID_UNKNOWN) {
rv = UNSUPPORTED_SIGN_OPERATION_FOR_ISSUER;
goto loser;
}
srv = SECOID_SetAlgorithmID(newCert->arena, &newCert->signature,
signTag, 0);
if (srv != SECSuccess) {
rv = ERROR_SETTING_SIGN_ALG;
goto loser;
}
srv = CRMF_CertRequestGetCertTemplateVersion(certReq, &version);
if (srv != SECSuccess) {
/* No version included in the request */
*(newCert->version.data) = SEC_CERTIFICATE_VERSION_3;
}
else {
SECITEM_FreeItem(&newCert->version, PR_FALSE);
SEC_ASN1EncodeInteger(newCert->arena, &newCert->version, version);
}
SEC_ASN1EncodeItem(newCert->arena, &derCert, newCert,
CERT_CertificateTemplate);
if (derCert.data == NULL) {
rv = ERROR_ENCODING_NEW_CERT;
goto loser;
}
srv = SEC_DerSignData(newCert->arena, &(newCert->derCert), derCert.data,
derCert.len, issuerPrivKey, signTag);
if (srv != SECSuccess) {
rv = ERROR_SIGNING_NEW_CERT;
goto loser;
}
#ifdef WRITE_OUT_RESPONSE
writeOutItem(
"newcert.der" , &newCert->derCert);
#endif
return NO_ERROR;
loser:
*issuedCert = NULL;
if (newCert) {
CERT_DestroyCertificate(newCert);
}
return rv;
}
void
formatCMMFResponse(
char *nickname,
char *base64Response)
{
char *currLine, *nextLine;
printf(
"var retVal = crypto.importUserCertificates(\" %s\
",\n" , nickname);
currLine = base64Response;
while (1) {
nextLine = strchr(currLine,
'\n' );
if (nextLine == NULL) {
/* print out the last line here. */
printf(
"\" %s\
",\n" , currLine);
break ;
}
nextLine[0] =
'\0' ;
printf(
"\" %s\\n\
"+\n" , currLine);
currLine = nextLine + 1;
}
printf(
"true);\n"
"if(retVal == '') {\n"
"\tdocument.write(\" <h1>
New Certificate Successfully Imported.</h1>\
");\n"
"} else {\n"
"\tdocument.write(\" <h2>Unable to import
New Certificate</h2>\
");\n"
"\tdocument.write(\" crypto.importUserCertificates returned <b>\
");\n"
"\tdocument.write(retVal);\n"
"\tdocument.write(\" </b>\
");\n"
"}\n" );
}
void
spitOutCMMFResponse(
char *nickname,
char *base64Response)
{
spitOutHeaders();
printf(
"\n\nCMMF Resonse Page \n\n\n"
"CMMF Response Page \n"
"\n\n" );
}
char *
getNickname(CERTCertificate *cert)
{
char *nickname;
if (cert->nickname != NULL) {
return cert->nickname;
}
nickname = CERT_GetCommonName(&cert->subject);
if (nickname != NULL) {
return nickname;
}
return CERT_NameToAscii(&cert->subject);
}
ErrorCode
createCMMFResponse(CertResponseInfo *issuedCerts,
int numCerts,
CERTCertificate *issuerCert,
char **base64der)
{
CMMFCertRepContent *certRepContent = NULL;
ErrorCode rv = NO_ERROR;
CMMFCertResponse **responses, *currResponse;
CERTCertList *caList;
int i;
SECStatus srv;
PLArenaPool *poolp;
SECItem *der;
certRepContent = CMMF_CreateCertRepContent();
if (certRepContent == NULL) {
rv = ERROR_CREATING_CERT_REP_CONTENT;
goto loser;
}
responses = PORT_NewArray(CMMFCertResponse *, numCerts);
if (responses == NULL) {
rv = OUT_OF_MEMORY;
goto loser;
}
for (i = 0; i < numCerts; i++) {
responses[i] = currResponse =
CMMF_CreateCertResponse(issuedCerts[i].certReqID);
if (currResponse == NULL) {
rv = ERROR_CREATING_SINGLE_CERT_RESPONSE;
goto loser;
}
CMMF_CertResponseSetPKIStatusInfoStatus(currResponse, cmmfGranted);
CMMF_CertResponseSetCertificate(currResponse, issuedCerts[i].cert);
}
srv = CMMF_CertRepContentSetCertResponses(certRepContent, responses,
numCerts);
if (srv != SECSuccess) {
rv = ERROR_SETTING_CERT_RESPONSES;
goto loser;
}
caList = CERT_NewCertList();
if (caList == NULL) {
rv = ERROR_CREATING_CA_LIST;
goto loser;
}
srv = CERT_AddCertToListTail(caList, issuerCert);
if (srv != SECSuccess) {
rv = ERROR_ADDING_ISSUER_TO_CA_LIST;
goto loser;
}
srv = CMMF_CertRepContentSetCAPubs(certRepContent, caList);
CERT_DestroyCertList(caList);
poolp = PORT_NewArena(1024);
der = SEC_ASN1EncodeItem(poolp, NULL, certRepContent,
CMMFCertRepContentTemplate);
if (der == NULL) {
rv = ERROR_ENCODING_CERT_REP_CONTENT;
goto loser;
}
#ifdef WRITE_OUT_RESPONSE
writeOutItem(
"CertRepContent.der" , der);
#endif
*base64der = BTOA_DataToAscii(der->data, der->len);
return NO_ERROR;
loser:
return rv;
}
ErrorCode
issueCerts(CertResponseInfo *issuedCerts,
int numCerts,
CERTCertificate *issuerCert)
{
ErrorCode rv;
char *base64Response;
rv = createCMMFResponse(issuedCerts, numCerts, issuerCert, &base64Response);
if (rv != NO_ERROR) {
goto loser;
}
spitOutCMMFResponse(getNickname(issuedCerts[0].cert), base64Response);
return NO_ERROR;
loser:
return rv;
}
ErrorCode
verifySignature(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
CRMFCertRequest *certReq, CERTCertificate *newCert)
{
SECStatus srv;
ErrorCode rv = NO_ERROR;
CRMFPOPOSigningKey *signKey = NULL;
SECAlgorithmID *algID = NULL;
SECItem *signature = NULL;
SECKEYPublicKey *pubKey = NULL;
SECItem *reqDER = NULL;
srv = CRMF_CertReqMsgGetPOPOSigningKey(currReq, &signKey);
if (srv != SECSuccess || signKey == NULL) {
rv = ERROR_RETRIEVING_POP_SIGN_KEY;
goto loser;
}
algID = CRMF_POPOSigningKeyGetAlgID(signKey);
if (algID == NULL) {
rv = ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY;
goto loser;
}
signature = CRMF_POPOSigningKeyGetSignature(signKey);
if (signature == NULL) {
rv = ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY;
goto loser;
}
/* Make the length the number of bytes instead of bits */
signature->len = (signature->len + 7) / 8;
pubKey = CERT_ExtractPublicKey(newCert);
if (pubKey == NULL) {
rv = ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT;
goto loser;
}
reqDER = SEC_ASN1EncodeItem(NULL, NULL, certReq, CRMFCertRequestTemplate);
if (reqDER == NULL) {
rv = ERROR_ENCODING_CERT_REQ_FOR_POP;
goto loser;
}
srv = VFY_VerifyDataWithAlgorithmID(reqDER->data, reqDER->len, pubKey,
signature, &algID->algorithm, NULL, varTable);
if (srv != SECSuccess) {
rv = ERROR_VERIFYING_SIGNATURE_POP;
goto loser;
}
/* Fall thru in successfull case. */
loser:
if (pubKey != NULL) {
SECKEY_DestroyPublicKey(pubKey);
}
if (reqDER != NULL) {
SECITEM_FreeItem(reqDER, PR_TRUE);
}
if (signature != NULL) {
SECITEM_FreeItem(signature, PR_TRUE);
}
if (algID != NULL) {
SECOID_DestroyAlgorithmID(algID, PR_TRUE);
}
if (signKey != NULL) {
CRMF_DestroyPOPOSigningKey(signKey);
}
return rv;
}
ErrorCode
doChallengeResponse(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
CRMFCertRequest *certReq, CERTCertificate *newCert,
ChallengeCreationInfo *challs,
int *numChall)
{
CRMFPOPOPrivKey *privKey = NULL;
CRMFPOPOPrivKeyChoice privKeyChoice;
SECStatus srv;
ErrorCode rv = NO_ERROR;
srv = CRMF_CertReqMsgGetPOPKeyEncipherment(currReq, &privKey);
if (srv != SECSuccess || privKey == NULL) {
rv = ERROR_GETTING_KEY_ENCIPHERMENT;
goto loser;
}
privKeyChoice = CRMF_POPOPrivKeyGetChoice(privKey);
CRMF_DestroyPOPOPrivKey(privKey);
switch (privKeyChoice) {
case crmfSubsequentMessage:
challs = &challs[*numChall];
challs->random = rand();
challs->pubKey = CERT_ExtractPublicKey(newCert);
if (challs->pubKey == NULL) {
rv =
ERROR_RETRIEVING_PUB_KEY_FOR_CHALL;
goto loser;
}
(*numChall)++;
rv = DO_CHALLENGE_RESPONSE;
break ;
case crmfThisMessage:
/* There'd better be a PKIArchiveControl in this message */
if (!CRMF_CertRequestIsControlPresent(certReq,
crmfPKIArchiveOptionsControl)) {
rv =
ERROR_NO_POP_FOR_PRIVKEY;
goto loser;
}
break ;
default :
rv = ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE;
goto loser;
}
loser:
return rv;
}
ErrorCode
doProofOfPossession(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
CRMFCertRequest *certReq, CERTCertificate *newCert,
ChallengeCreationInfo *challs,
int *numChall)
{
CRMFPOPChoice popChoice;
ErrorCode rv = NO_ERROR;
popChoice = CRMF_CertReqMsgGetPOPType(currReq);
if (popChoice == crmfNoPOPChoice) {
rv = NO_POP_FOR_REQUEST;
goto loser;
}
switch (popChoice) {
case crmfSignature:
rv = verifySignature(varTable, currReq, certReq, newCert);
break ;
case crmfKeyEncipherment:
rv = doChallengeResponse(varTable, currReq, certReq, newCert,
challs, numChall);
break ;
case crmfRAVerified:
case crmfKeyAgreement:
default :
rv = UNSUPPORTED_POP;
goto loser;
}
loser:
return rv;
}
void
convertB64ToJS(
char *base64)
{
int i;
for (i = 0; base64[i] !=
'\0' ; i++) {
if (base64[i] ==
'\n' ) {
printf(
"\\n" );
}
else {
printf(
"%c" , base64[i]);
}
}
}
void
formatChallenge(
char *chall64,
char *certRepContentDER,
ChallengeCreationInfo *challInfo,
int numChalls)
{
printf(
"function respondToChallenge() {\n"
" var chalForm = document.chalForm;\n\n"
" chalForm.CertRepContent.value = '" );
convertB64ToJS(certRepContentDER);
printf(
"';\n"
" chalForm.ChallResponse.value = crypto.popChallengeResponse('" );
convertB64ToJS(chall64);
printf(
"');\n"
" chalForm.submit();\n"
"}\n" );
}
void
spitOutChallenge(
char *chall64,
char *certRepContentDER,
ChallengeCreationInfo *challInfo,
int numChalls,
char *nickname)
{
int i;
spitOutHeaders();
printf(
"\n"
"\n"
"Challenge Page \n"
"\n"
"\n"
"\n"
"Cartman is now responding to the Challenge "
"presented by the CGI\n"
"
" \n"
" \n" );
for (i = 0; i < numChalls; i++) {
printf(
" \n" ,
i + 1, challInfo[i].random);
}
printf(
" \n" , nickname);
printf(
"\n\n" );
}
ErrorCode
issueChallenge(CertResponseInfo *issuedCerts,
int numCerts,
ChallengeCreationInfo *challInfo,
int numChalls,
CERTCertificate *issuer, CGIVarTable *varTable)
{
ErrorCode rv = NO_ERROR;
CMMFPOPODecKeyChallContent *chalContent = NULL;
int i;
SECStatus srv;
PLArenaPool *poolp;
CERTGeneralName *genName;
SECItem *challDER = NULL;
char *chall64, *certRepContentDER;
rv = createCMMFResponse(issuedCerts, numCerts, issuer,
&certRepContentDER);
if (rv != NO_ERROR) {
goto loser;
}
chalContent = CMMF_CreatePOPODecKeyChallContent();
if (chalContent == NULL) {
rv = ERROR_CREATING_EMPTY_CHAL_CONTENT;
goto loser;
}
poolp = PORT_NewArena(1024);
if (poolp == NULL) {
rv = OUT_OF_MEMORY;
goto loser;
}
genName = CERT_GetCertificateNames(issuer, poolp);
if (genName == NULL) {
rv = ERROR_EXTRACTING_GEN_NAME_FROM_ISSUER;
goto loser;
}
for (i = 0; i < numChalls; i++) {
srv = CMMF_POPODecKeyChallContentSetNextChallenge(chalContent,
challInfo[i].random,
genName,
challInfo[i].pubKey,
varTable);
SECKEY_DestroyPublicKey(challInfo[i].pubKey);
if (srv != SECSuccess) {
rv = ERROR_SETTING_CHALLENGE;
goto loser;
}
}
challDER = SEC_ASN1EncodeItem(NULL, NULL, chalContent,
CMMFPOPODecKeyChallContentTemplate);
if (challDER == NULL) {
rv = ERROR_ENCODING_CHALL;
goto loser;
}
chall64 = BTOA_DataToAscii(challDER->data, challDER->len);
SECITEM_FreeItem(challDER, PR_TRUE);
if (chall64 == NULL) {
rv = ERROR_CONVERTING_CHALL_TO_BASE64;
goto loser;
}
spitOutChallenge(chall64, certRepContentDER, challInfo, numChalls,
getNickname(issuedCerts[0].cert));
loser:
return rv;
}
ErrorCode
processRequest(CGIVarTable *varTable)
{
CERTCertDBHandle *certdb;
SECKEYKeyDBHandle *keydb;
CRMFCertReqMessages *certReqs = NULL;
const char *crmfReq;
const char *caNickname;
CERTCertificate *caCert = NULL;
CertResponseInfo *issuedCerts = NULL;
CERTSubjectPublicKeyInfo spki = { 0 };
ErrorCode rv = NO_ERROR;
PRBool doChallengeResponse = PR_FALSE;
SECItem der = { 0 };
SECStatus srv;
CERTCertificateRequest oldCertReq = { 0 };
CRMFCertReqMsg **reqMsgs = NULL, *currReq = NULL;
CRMFCertRequest **reqs = NULL, *certReq = NULL;
CERTName subject = { 0 };
int numReqs, i;
ChallengeCreationInfo *challInfo = NULL;
int numChalls = 0;
certdb = CERT_GetDefaultCertDB();
keydb = SECKEY_GetDefaultKeyDB();
crmfReq = CGITableFindValue(varTable,
"CRMFRequest" );
if (crmfReq == NULL) {
rv = CGI_VAR_MISSING;
missingVar =
"CRMFRequest" ;
goto loser;
}
caNickname = CGITableFindValue(varTable,
"CANickname" );
if (caNickname == NULL) {
rv = CGI_VAR_MISSING;
missingVar =
"CANickname" ;
goto loser;
}
caCert = CERT_FindCertByNickname(certdb, caNickname);
if (caCert == NULL) {
rv = COULD_NOT_FIND_CA;
goto loser;
}
srv = ATOB_ConvertAsciiToItem(&der, crmfReq);
if (srv != SECSuccess) {
rv = BAD_ASCII_FOR_REQ;
goto loser;
}
certReqs = CRMF_CreateCertReqMessagesFromDER(der.data, der.len);
SECITEM_FreeItem(&der, PR_FALSE);
if (certReqs == NULL) {
rv = COULD_NOT_DECODE_REQS;
goto loser;
}
numReqs = CRMF_CertReqMessagesGetNumMessages(certReqs);
issuedCerts = PORT_ZNewArray(CertResponseInfo, numReqs);
challInfo = PORT_ZNewArray(ChallengeCreationInfo, numReqs);
if (issuedCerts == NULL || challInfo == NULL) {
rv = OUT_OF_MEMORY;
goto loser;
}
reqMsgs = PORT_ZNewArray(CRMFCertReqMsg *, numReqs);
reqs = PORT_ZNewArray(CRMFCertRequest *, numReqs);
if (reqMsgs == NULL || reqs == NULL) {
rv = OUT_OF_MEMORY;
goto loser;
}
for (i = 0; i < numReqs; i++) {
currReq = reqMsgs[i] =
CRMF_CertReqMessagesGetCertReqMsgAtIndex(certReqs, i);
if (currReq == NULL) {
rv = ERROR_RETRIEVING_REQUEST_MSG;
goto loser;
}
certReq = reqs[i] = CRMF_CertReqMsgGetCertRequest(currReq);
if (certReq == NULL) {
rv = ERROR_RETRIEVING_CERT_REQUEST;
goto loser;
}
srv = CRMF_CertRequestGetCertTemplateSubject(certReq, &subject);
if (srv != SECSuccess) {
rv = ERROR_RETRIEVING_SUBJECT_FROM_REQ;
goto loser;
}
srv = CRMF_CertRequestGetCertTemplatePublicKey(certReq, &spki);
if (srv != SECSuccess) {
rv = ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ;
goto loser;
}
rv = initOldCertReq(&oldCertReq, &subject, &spki);
if (rv != NO_ERROR) {
goto loser;
}
rv = createNewCert(&issuedCerts[i].cert, &oldCertReq, currReq, certReq,
caCert, varTable);
if (rv != NO_ERROR) {
goto loser;
}
rv = doProofOfPossession(varTable, currReq, certReq, issuedCerts[i].cert,
challInfo, &numChalls);
if (rv != NO_ERROR) {
if (rv == DO_CHALLENGE_RESPONSE) {
doChallengeResponse = PR_TRUE;
}
else {
goto loser;
}
}
CRMF_CertReqMsgGetID(currReq, &issuedCerts[i].certReqID);
CRMF_DestroyCertReqMsg(currReq);
CRMF_DestroyCertRequest(certReq);
}
if (doChallengeResponse) {
rv = issueChallenge(issuedCerts, numReqs, challInfo, numChalls, caCert,
varTable);
}
else {
rv = issueCerts(issuedCerts, numReqs, caCert);
}
loser:
if (certReqs != NULL) {
CRMF_DestroyCertReqMessages(certReqs);
}
return rv;
}
ErrorCode
processChallengeResponse(CGIVarTable *varTable,
const char *certRepContent)
{
SECItem binDER = { 0 };
SECStatus srv;
ErrorCode rv = NO_ERROR;
const char *clientResponse;
const char *formChalValue;
const char *nickname;
CMMFPOPODecKeyRespContent *respContent = NULL;
int numResponses, i;
long curResponse, expectedResponse;
char cgiChalVar[10];
#ifdef WRITE_OUT_RESPONSE
SECItem certRepBinDER = { 0 };
ATOB_ConvertAsciiToItem(&certRepBinDER, certRepContent);
writeOutItem(
"challCertRepContent.der" , &certRepBinDER);
PORT_Free(certRepBinDER.data);
#endif
clientResponse = CGITableFindValue(varTable,
"ChallResponse" );
if (clientResponse == NULL) {
rv = REQ_CGI_VAR_NOT_PRESENT;
missingVar =
"ChallResponse" ;
goto loser;
}
srv = ATOB_ConvertAsciiToItem(&binDER, clientResponse);
if (srv != SECSuccess) {
rv = ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN;
goto loser;
}
respContent = CMMF_CreatePOPODecKeyRespContentFromDER(binDER.data,
binDER.len);
SECITEM_FreeItem(&binDER, PR_FALSE);
binDER.data = NULL;
if (respContent == NULL) {
rv = ERROR_CREATING_KEY_RESP_FROM_DER;
goto loser;
}
numResponses = CMMF_POPODecKeyRespContentGetNumResponses(respContent);
for (i = 0; i < numResponses; i++) {
srv = CMMF_POPODecKeyRespContentGetResponse(respContent, i, &curResponse);
if (srv != SECSuccess) {
rv = ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE;
goto loser;
}
snprintf(cgiChalVar,
sizeof (cgiChalVar),
"chal%d" , i + 1);
formChalValue = CGITableFindValue(varTable, cgiChalVar);
if (formChalValue == NULL) {
rv = REQ_CGI_VAR_NOT_PRESENT;
missingVar = strdup(cgiChalVar);
goto loser;
}
sscanf(formChalValue,
"%ld" , &expectedResponse);
if (expectedResponse != curResponse) {
rv = ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED;
goto loser;
}
}
nickname = CGITableFindValue(varTable,
"nickname" );
if (nickname == NULL) {
rv = REQ_CGI_VAR_NOT_PRESENT;
missingVar =
"nickname" ;
goto loser;
}
spitOutCMMFResponse(nickname, certRepContent);
loser:
if (respContent != NULL) {
CMMF_DestroyPOPODecKeyRespContent(respContent);
}
return rv;
}
int
main()
{
char *form_output = NULL;
int form_output_len, form_output_used;
CGIVarTable varTable = { 0 };
ErrorCode errNum = 0;
char *certRepContent;
#ifdef ATTACH_CGI
/* Put an ifinite loop in here so I can attach to
* the process after the process is spun off
*/
{
int stupid = 1;
while (stupid)
;
}
#endif
form_output_used = 0;
srand(time(NULL));
while (feof(stdin) == 0) {
if (form_output == NULL) {
form_output = PORT_NewArray(
char , DEFAULT_ALLOC_SIZE + 1);
form_output_len = DEFAULT_ALLOC_SIZE;
}
else if ((form_output_used + DEFAULT_ALLOC_SIZE) >= form_output_len) {
form_output_len += DEFAULT_ALLOC_SIZE;
form_output = PORT_Realloc(form_output, form_output_len + 1);
}
form_output_used += fread(&form_output[form_output_used],
sizeof (
char ),
DEFAULT_ALLOC_SIZE, stdin);
}
ParseInputVariables(&varTable, form_output);
certRepContent = CGITableFindValue(&varTable,
"CertRepContent" );
if (certRepContent == NULL) {
errNum = initNSS(&varTable);
if (errNum != 0) {
goto loser;
}
errNum = processRequest(&varTable);
}
else {
errNum = processChallengeResponse(&varTable, certRepContent);
}
if (errNum != NO_ERROR) {
goto loser;
}
goto done;
loser:
dumpErrorMessage(errNum);
done:
free(form_output);
return 0;
}