tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

crmfcgi.c (32799B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "seccomon.h"
      6 #include "nss.h"
      7 #include "keyhi.h"
      8 #include "cert.h"
      9 #include "pk11func.h"
     10 #include "secmod.h"
     11 #include "cmmf.h"
     12 #include "crmf.h"
     13 #include "base64.h"
     14 #include "secasn1.h"
     15 #include "cryptohi.h"
     16 #include <string.h>
     17 #include <stdlib.h>
     18 #include <stdio.h>
     19 
     20 #define DEFAULT_ALLOC_SIZE 200
     21 #define DEFAULT_CGI_VARS 20
     22 
     23 typedef struct CGIVariableStr {
     24    char *name;
     25    char *value;
     26 } CGIVariable;
     27 
     28 typedef struct CGIVarTableStr {
     29    CGIVariable **variables;
     30    int numVars;
     31    int numAlloc;
     32 } CGIVarTable;
     33 
     34 typedef struct CertResponseInfoStr {
     35    CERTCertificate *cert;
     36    long certReqID;
     37 } CertResponseInfo;
     38 
     39 typedef struct ChallengeCreationInfoStr {
     40    long random;
     41    SECKEYPublicKey *pubKey;
     42 } ChallengeCreationInfo;
     43 
     44 char *missingVar = NULL;
     45 
     46 /*
     47 * Error values.
     48 */
     49 typedef enum {
     50    NO_ERROR = 0,
     51    NSS_INIT_FAILED,
     52    AUTH_FAILED,
     53    REQ_CGI_VAR_NOT_PRESENT,
     54    CRMF_REQ_NOT_PRESENT,
     55    BAD_ASCII_FOR_REQ,
     56    CGI_VAR_MISSING,
     57    COULD_NOT_FIND_CA,
     58    COULD_NOT_DECODE_REQS,
     59    OUT_OF_MEMORY,
     60    ERROR_RETRIEVING_REQUEST_MSG,
     61    ERROR_RETRIEVING_CERT_REQUEST,
     62    ERROR_RETRIEVING_SUBJECT_FROM_REQ,
     63    ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ,
     64    ERROR_CREATING_NEW_CERTIFICATE,
     65    COULD_NOT_START_EXTENSIONS,
     66    ERROR_RETRIEVING_EXT_FROM_REQ,
     67    ERROR_ADDING_EXT_TO_CERT,
     68    ERROR_ENDING_EXTENSIONS,
     69    COULD_NOT_FIND_ISSUER_PRIVATE_KEY,
     70    UNSUPPORTED_SIGN_OPERATION_FOR_ISSUER,
     71    ERROR_SETTING_SIGN_ALG,
     72    ERROR_ENCODING_NEW_CERT,
     73    ERROR_SIGNING_NEW_CERT,
     74    ERROR_CREATING_CERT_REP_CONTENT,
     75    ERROR_CREATING_SINGLE_CERT_RESPONSE,
     76    ERROR_SETTING_CERT_RESPONSES,
     77    ERROR_CREATING_CA_LIST,
     78    ERROR_ADDING_ISSUER_TO_CA_LIST,
     79    ERROR_ENCODING_CERT_REP_CONTENT,
     80    NO_POP_FOR_REQUEST,
     81    UNSUPPORTED_POP,
     82    ERROR_RETRIEVING_POP_SIGN_KEY,
     83    ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY,
     84    ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY,
     85    DO_CHALLENGE_RESPONSE,
     86    ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT,
     87    ERROR_ENCODING_CERT_REQ_FOR_POP,
     88    ERROR_VERIFYING_SIGNATURE_POP,
     89    ERROR_RETRIEVING_PUB_KEY_FOR_CHALL,
     90    ERROR_CREATING_EMPTY_CHAL_CONTENT,
     91    ERROR_EXTRACTING_GEN_NAME_FROM_ISSUER,
     92    ERROR_SETTING_CHALLENGE,
     93    ERROR_ENCODING_CHALL,
     94    ERROR_CONVERTING_CHALL_TO_BASE64,
     95    ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN,
     96    ERROR_CREATING_KEY_RESP_FROM_DER,
     97    ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE,
     98    ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED,
     99    ERROR_GETTING_KEY_ENCIPHERMENT,
    100    ERROR_NO_POP_FOR_PRIVKEY,
    101    ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE
    102 } ErrorCode;
    103 
    104 const char *
    105 CGITableFindValue(CGIVarTable *varTable, const char *key);
    106 
    107 void
    108 spitOutHeaders(void)
    109 {
    110    printf("Content-type: text/html\n\n");
    111 }
    112 
    113 void
    114 dumpRequest(CGIVarTable *varTable)
    115 {
    116    int i;
    117    CGIVariable *var;
    118 
    119    printf("<table border=1 cellpadding=1 cellspacing=1 width=\"100%%\">\n");
    120    printf("<tr><td><b><center>Variable Name<center></b></td>"
    121           "<td><b><center>Value</center></b></td></tr>\n");
    122    for (i = 0; i < varTable->numVars; i++) {
    123        var = varTable->variables[i];
    124        printf("<tr><td><pre>%s</pre></td><td><pre>%s</pre></td></tr>\n",
    125               var->name, var->value);
    126    }
    127    printf("</table>\n");
    128 }
    129 
    130 void
    131 echo_request(CGIVarTable *varTable)
    132 {
    133    spitOutHeaders();
    134    printf("<html><head><title>CGI Echo Page</title></head>\n"
    135           "<body><h1>Got the following request</h1>\n");
    136    dumpRequest(varTable);
    137    printf("</body></html>");
    138 }
    139 
    140 void
    141 processVariable(CGIVariable *var)
    142 {
    143    char *plusSign, *percentSign;
    144 
    145    /*First look for all of the '+' and convert them to spaces */
    146    plusSign = var->value;
    147    while ((plusSign = strchr(plusSign, '+')) != NULL) {
    148        *plusSign = ' ';
    149    }
    150    percentSign = var->value;
    151    while ((percentSign = strchr(percentSign, '%')) != NULL) {
    152        char string[3];
    153        int value;
    154 
    155        string[0] = percentSign[1];
    156        string[1] = percentSign[2];
    157        string[2] = '\0';
    158 
    159        sscanf(string, "%x", &value);
    160        *percentSign = (char)value;
    161        memmove(&percentSign[1], &percentSign[3], 1 + strlen(&percentSign[3]));
    162    }
    163 }
    164 
    165 char *
    166 parseNextVariable(CGIVarTable *varTable, char *form_output)
    167 {
    168    char *ampersand, *equal;
    169    CGIVariable *var;
    170 
    171    if (varTable->numVars == varTable->numAlloc) {
    172        CGIVariable **newArr = realloc(varTable->variables,
    173                                       (varTable->numAlloc + DEFAULT_CGI_VARS) * sizeof(CGIVariable *));
    174        if (newArr == NULL) {
    175            return NULL;
    176        }
    177        varTable->variables = newArr;
    178        varTable->numAlloc += DEFAULT_CGI_VARS;
    179    }
    180    equal = strchr(form_output, '=');
    181    if (equal == NULL) {
    182        return NULL;
    183    }
    184    ampersand = strchr(equal, '&');
    185    if (ampersand == NULL) {
    186        return NULL;
    187    }
    188    equal[0] = '\0';
    189    if (ampersand != NULL) {
    190        ampersand[0] = '\0';
    191    }
    192    var = malloc(sizeof(CGIVariable));
    193    var->name = form_output;
    194    var->value = &equal[1];
    195    varTable->variables[varTable->numVars] = var;
    196    varTable->numVars++;
    197    processVariable(var);
    198    return (ampersand != NULL) ? &ampersand[1] : NULL;
    199 }
    200 
    201 void
    202 ParseInputVariables(CGIVarTable *varTable, char *form_output)
    203 {
    204    varTable->variables = malloc(sizeof(CGIVariable *) * DEFAULT_CGI_VARS);
    205    varTable->numVars = 0;
    206    varTable->numAlloc = DEFAULT_CGI_VARS;
    207    while (form_output && form_output[0] != '\0') {
    208        form_output = parseNextVariable(varTable, form_output);
    209    }
    210 }
    211 
    212 const char *
    213 CGITableFindValue(CGIVarTable *varTable, const char *key)
    214 {
    215    const char *retVal = NULL;
    216    int i;
    217 
    218    for (i = 0; i < varTable->numVars; i++) {
    219        if (strcmp(varTable->variables[i]->name, key) == 0) {
    220            retVal = varTable->variables[i]->value;
    221            break;
    222        }
    223    }
    224    return retVal;
    225 }
    226 
    227 char *
    228 passwordCallback(PK11SlotInfo *slot, PRBool retry, void *arg)
    229 {
    230    const char *passwd;
    231    if (retry) {
    232        return NULL;
    233    }
    234    passwd = CGITableFindValue((CGIVarTable *)arg, "dbPassword");
    235    if (passwd == NULL) {
    236        return NULL;
    237    }
    238    return PORT_Strdup(passwd);
    239 }
    240 
    241 ErrorCode
    242 initNSS(CGIVarTable *varTable)
    243 {
    244    const char *nssDir;
    245    PK11SlotInfo *keySlot;
    246    SECStatus rv;
    247 
    248    nssDir = CGITableFindValue(varTable, "NSSDirectory");
    249    if (nssDir == NULL) {
    250        missingVar = "NSSDirectory";
    251        return REQ_CGI_VAR_NOT_PRESENT;
    252    }
    253    rv = NSS_Init(nssDir);
    254    if (rv != SECSuccess) {
    255        return NSS_INIT_FAILED;
    256    }
    257    PK11_SetPasswordFunc(passwordCallback);
    258    keySlot = PK11_GetInternalKeySlot();
    259    rv = PK11_Authenticate(keySlot, PR_FALSE, varTable);
    260    PK11_FreeSlot(keySlot);
    261    if (rv != SECSuccess) {
    262        return AUTH_FAILED;
    263    }
    264    return NO_ERROR;
    265 }
    266 
    267 void
    268 dumpErrorMessage(ErrorCode errNum)
    269 {
    270    spitOutHeaders();
    271    printf("<html><head><title>Error</title></head><body><h1>Error processing "
    272           "data</h1> Received the error %d<p>",
    273           errNum);
    274    if (errNum == REQ_CGI_VAR_NOT_PRESENT) {
    275        printf("The missing variable is %s.", missingVar);
    276    }
    277    printf("<i>More useful information here in the future.</i></body></html>");
    278 }
    279 
    280 ErrorCode
    281 initOldCertReq(CERTCertificateRequest *oldCertReq,
    282               CERTName *subject, CERTSubjectPublicKeyInfo *spki)
    283 {
    284    PLArenaPool *poolp;
    285 
    286    poolp = oldCertReq->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    287    SEC_ASN1EncodeInteger(poolp, &oldCertReq->version,
    288                          SEC_CERTIFICATE_VERSION_3);
    289    CERT_CopyName(poolp, &oldCertReq->subject, subject);
    290    SECKEY_CopySubjectPublicKeyInfo(poolp, &oldCertReq->subjectPublicKeyInfo,
    291                                    spki);
    292    oldCertReq->attributes = NULL;
    293    return NO_ERROR;
    294 }
    295 
    296 ErrorCode
    297 addExtensions(CERTCertificate *newCert, CRMFCertRequest *certReq)
    298 {
    299    int numExtensions, i;
    300    void *extHandle;
    301    ErrorCode rv = NO_ERROR;
    302    CRMFCertExtension *ext;
    303    SECStatus srv;
    304 
    305    numExtensions = CRMF_CertRequestGetNumberOfExtensions(certReq);
    306    if (numExtensions == 0) {
    307        /* No extensions to add */
    308        return NO_ERROR;
    309    }
    310    extHandle = CERT_StartCertExtensions(newCert);
    311    if (extHandle == NULL) {
    312        rv = COULD_NOT_START_EXTENSIONS;
    313        goto loser;
    314    }
    315    for (i = 0; i < numExtensions; i++) {
    316        ext = CRMF_CertRequestGetExtensionAtIndex(certReq, i);
    317        if (ext == NULL) {
    318            rv = ERROR_RETRIEVING_EXT_FROM_REQ;
    319        }
    320        srv = CERT_AddExtension(extHandle, CRMF_CertExtensionGetOidTag(ext),
    321                                CRMF_CertExtensionGetValue(ext),
    322                                CRMF_CertExtensionGetIsCritical(ext), PR_FALSE);
    323        if (srv != SECSuccess) {
    324            rv = ERROR_ADDING_EXT_TO_CERT;
    325        }
    326    }
    327    srv = CERT_FinishExtensions(extHandle);
    328    if (srv != SECSuccess) {
    329        rv = ERROR_ENDING_EXTENSIONS;
    330        goto loser;
    331    }
    332    return NO_ERROR;
    333 loser:
    334    return rv;
    335 }
    336 
    337 void
    338 writeOutItem(const char *filePath, SECItem *der)
    339 {
    340    PRFileDesc *outfile;
    341 
    342    outfile = PR_Open(filePath,
    343                      PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
    344                      0666);
    345    PR_Write(outfile, der->data, der->len);
    346    PR_Close(outfile);
    347 }
    348 
    349 ErrorCode
    350 createNewCert(CERTCertificate **issuedCert, CERTCertificateRequest *oldCertReq,
    351              CRMFCertReqMsg *currReq, CRMFCertRequest *certReq,
    352              CERTCertificate *issuerCert, CGIVarTable *varTable)
    353 {
    354    CERTCertificate *newCert = NULL;
    355    CERTValidity *validity;
    356    PRExplodedTime printableTime;
    357    PRTime now, after;
    358    ErrorCode rv = NO_ERROR;
    359    SECKEYPrivateKey *issuerPrivKey;
    360    SECItem derCert = { 0 };
    361    SECOidTag signTag;
    362    SECStatus srv;
    363    long version;
    364 
    365    now = PR_Now();
    366    PR_ExplodeTime(now, PR_GMTParameters, &printableTime);
    367    printableTime.tm_month += 9;
    368    after = PR_ImplodeTime(&printableTime);
    369    validity = CERT_CreateValidity(now, after);
    370    newCert = *issuedCert =
    371        CERT_CreateCertificate(rand(), &(issuerCert->subject), validity,
    372                               oldCertReq);
    373    if (newCert == NULL) {
    374        rv = ERROR_CREATING_NEW_CERTIFICATE;
    375        goto loser;
    376    }
    377    rv = addExtensions(newCert, certReq);
    378    if (rv != NO_ERROR) {
    379        goto loser;
    380    }
    381    issuerPrivKey = PK11_FindKeyByAnyCert(issuerCert, varTable);
    382    if (issuerPrivKey == NULL) {
    383        rv = COULD_NOT_FIND_ISSUER_PRIVATE_KEY;
    384    }
    385    srv = SEC_CreateSignatureAlgorithmID(newCert->arena, &newcert->signature,
    386                                         SEC_OID_UNKNOWN, SEC_OID_UNKNOWN,
    387                                         NULL, issuerPrivatekey, NULL);
    388    if (srv != SECSuccess) {
    389        rv = ERROR_SETTING_SIGN_ALG;
    390        goto loser;
    391    }
    392    srv = CRMF_CertRequestGetCertTemplateVersion(certReq, &version);
    393    if (srv != SECSuccess) {
    394        /* No version included in the request */
    395        *(newCert->version.data) = SEC_CERTIFICATE_VERSION_3;
    396    } else {
    397        SECITEM_FreeItem(&newCert->version, PR_FALSE);
    398        SEC_ASN1EncodeInteger(newCert->arena, &newCert->version, version);
    399    }
    400    SEC_ASN1EncodeItem(newCert->arena, &derCert, newCert,
    401                       CERT_CertificateTemplate);
    402    if (derCert.data == NULL) {
    403        rv = ERROR_ENCODING_NEW_CERT;
    404        goto loser;
    405    }
    406    srv = SEC_DerSignData(newCert->arena, &(newCert->derCert), derCert.data,
    407                          derCert.len, issuerPrivKey, signTag);
    408    if (srv != SECSuccess) {
    409        rv = ERROR_SIGNING_NEW_CERT;
    410        goto loser;
    411    }
    412 #ifdef WRITE_OUT_RESPONSE
    413    writeOutItem("newcert.der", &newCert->derCert);
    414 #endif
    415    return NO_ERROR;
    416 loser:
    417    *issuedCert = NULL;
    418    if (newCert) {
    419        CERT_DestroyCertificate(newCert);
    420    }
    421    return rv;
    422 }
    423 
    424 void
    425 formatCMMFResponse(char *nickname, char *base64Response)
    426 {
    427    char *currLine, *nextLine;
    428 
    429    printf("var retVal = crypto.importUserCertificates(\"%s\",\n", nickname);
    430    currLine = base64Response;
    431    while (1) {
    432        nextLine = strchr(currLine, '\n');
    433        if (nextLine == NULL) {
    434            /* print out the last line here. */
    435            printf("\"%s\",\n", currLine);
    436            break;
    437        }
    438        nextLine[0] = '\0';
    439        printf("\"%s\\n\"+\n", currLine);
    440        currLine = nextLine + 1;
    441    }
    442    printf("true);\n"
    443           "if(retVal == '') {\n"
    444           "\tdocument.write(\"<h1>New Certificate Successfully Imported.</h1>\");\n"
    445           "} else {\n"
    446           "\tdocument.write(\"<h2>Unable to import New Certificate</h2>\");\n"
    447           "\tdocument.write(\"crypto.importUserCertificates returned <b>\");\n"
    448           "\tdocument.write(retVal);\n"
    449           "\tdocument.write(\"</b>\");\n"
    450           "}\n");
    451 }
    452 
    453 void
    454 spitOutCMMFResponse(char *nickname, char *base64Response)
    455 {
    456    spitOutHeaders();
    457    printf("<html>\n<head>\n<title>CMMF Resonse Page</title>\n</head>\n\n"
    458           "<body><h1>CMMF Response Page</h1>\n"
    459           "<script language=\"JavaScript\">\n"
    460           "<!--\n");
    461    formatCMMFResponse(nickname, base64Response);
    462    printf("// -->\n"
    463           "</script>\n</body>\n</html>");
    464 }
    465 
    466 char *
    467 getNickname(CERTCertificate *cert)
    468 {
    469    char *nickname;
    470 
    471    if (cert->nickname != NULL) {
    472        return cert->nickname;
    473    }
    474    nickname = CERT_GetCommonName(&cert->subject);
    475    if (nickname != NULL) {
    476        return nickname;
    477    }
    478    return CERT_NameToAscii(&cert->subject);
    479 }
    480 
    481 ErrorCode
    482 createCMMFResponse(CertResponseInfo *issuedCerts, int numCerts,
    483                   CERTCertificate *issuerCert, char **base64der)
    484 {
    485    CMMFCertRepContent *certRepContent = NULL;
    486    ErrorCode rv = NO_ERROR;
    487    CMMFCertResponse **responses, *currResponse;
    488    CERTCertList *caList;
    489    int i;
    490    SECStatus srv;
    491    PLArenaPool *poolp;
    492    SECItem *der;
    493 
    494    certRepContent = CMMF_CreateCertRepContent();
    495    if (certRepContent == NULL) {
    496        rv = ERROR_CREATING_CERT_REP_CONTENT;
    497        goto loser;
    498    }
    499    responses = PORT_NewArray(CMMFCertResponse *, numCerts);
    500    if (responses == NULL) {
    501        rv = OUT_OF_MEMORY;
    502        goto loser;
    503    }
    504    for (i = 0; i < numCerts; i++) {
    505        responses[i] = currResponse =
    506            CMMF_CreateCertResponse(issuedCerts[i].certReqID);
    507        if (currResponse == NULL) {
    508            rv = ERROR_CREATING_SINGLE_CERT_RESPONSE;
    509            goto loser;
    510        }
    511        CMMF_CertResponseSetPKIStatusInfoStatus(currResponse, cmmfGranted);
    512        CMMF_CertResponseSetCertificate(currResponse, issuedCerts[i].cert);
    513    }
    514    srv = CMMF_CertRepContentSetCertResponses(certRepContent, responses,
    515                                              numCerts);
    516    if (srv != SECSuccess) {
    517        rv = ERROR_SETTING_CERT_RESPONSES;
    518        goto loser;
    519    }
    520    caList = CERT_NewCertList();
    521    if (caList == NULL) {
    522        rv = ERROR_CREATING_CA_LIST;
    523        goto loser;
    524    }
    525    srv = CERT_AddCertToListTail(caList, issuerCert);
    526    if (srv != SECSuccess) {
    527        rv = ERROR_ADDING_ISSUER_TO_CA_LIST;
    528        goto loser;
    529    }
    530    srv = CMMF_CertRepContentSetCAPubs(certRepContent, caList);
    531    CERT_DestroyCertList(caList);
    532    poolp = PORT_NewArena(1024);
    533    der = SEC_ASN1EncodeItem(poolp, NULL, certRepContent,
    534                             CMMFCertRepContentTemplate);
    535    if (der == NULL) {
    536        rv = ERROR_ENCODING_CERT_REP_CONTENT;
    537        goto loser;
    538    }
    539 #ifdef WRITE_OUT_RESPONSE
    540    writeOutItem("CertRepContent.der", der);
    541 #endif
    542    *base64der = BTOA_DataToAscii(der->data, der->len);
    543    return NO_ERROR;
    544 loser:
    545    return rv;
    546 }
    547 
    548 ErrorCode
    549 issueCerts(CertResponseInfo *issuedCerts, int numCerts,
    550           CERTCertificate *issuerCert)
    551 {
    552    ErrorCode rv;
    553    char *base64Response;
    554 
    555    rv = createCMMFResponse(issuedCerts, numCerts, issuerCert, &base64Response);
    556    if (rv != NO_ERROR) {
    557        goto loser;
    558    }
    559    spitOutCMMFResponse(getNickname(issuedCerts[0].cert), base64Response);
    560    return NO_ERROR;
    561 loser:
    562    return rv;
    563 }
    564 
    565 ErrorCode
    566 verifySignature(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
    567                CRMFCertRequest *certReq, CERTCertificate *newCert)
    568 {
    569    SECStatus srv;
    570    ErrorCode rv = NO_ERROR;
    571    CRMFPOPOSigningKey *signKey = NULL;
    572    SECAlgorithmID *algID = NULL;
    573    SECItem *signature = NULL;
    574    SECKEYPublicKey *pubKey = NULL;
    575    SECItem *reqDER = NULL;
    576 
    577    srv = CRMF_CertReqMsgGetPOPOSigningKey(currReq, &signKey);
    578    if (srv != SECSuccess || signKey == NULL) {
    579        rv = ERROR_RETRIEVING_POP_SIGN_KEY;
    580        goto loser;
    581    }
    582    algID = CRMF_POPOSigningKeyGetAlgID(signKey);
    583    if (algID == NULL) {
    584        rv = ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY;
    585        goto loser;
    586    }
    587    signature = CRMF_POPOSigningKeyGetSignature(signKey);
    588    if (signature == NULL) {
    589        rv = ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY;
    590        goto loser;
    591    }
    592    /* Make the length the number of bytes instead of bits */
    593    signature->len = (signature->len + 7) / 8;
    594    pubKey = CERT_ExtractPublicKey(newCert);
    595    if (pubKey == NULL) {
    596        rv = ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT;
    597        goto loser;
    598    }
    599    reqDER = SEC_ASN1EncodeItem(NULL, NULL, certReq, CRMFCertRequestTemplate);
    600    if (reqDER == NULL) {
    601        rv = ERROR_ENCODING_CERT_REQ_FOR_POP;
    602        goto loser;
    603    }
    604    srv = VFY_VerifyDataWithAlgorithmID(reqDER->data, reqDER->len, pubKey,
    605                                        signature, &algID->algorithm, NULL, varTable);
    606    if (srv != SECSuccess) {
    607        rv = ERROR_VERIFYING_SIGNATURE_POP;
    608        goto loser;
    609    }
    610 /* Fall thru in successfull case. */
    611 loser:
    612    if (pubKey != NULL) {
    613        SECKEY_DestroyPublicKey(pubKey);
    614    }
    615    if (reqDER != NULL) {
    616        SECITEM_FreeItem(reqDER, PR_TRUE);
    617    }
    618    if (signature != NULL) {
    619        SECITEM_FreeItem(signature, PR_TRUE);
    620    }
    621    if (algID != NULL) {
    622        SECOID_DestroyAlgorithmID(algID, PR_TRUE);
    623    }
    624    if (signKey != NULL) {
    625        CRMF_DestroyPOPOSigningKey(signKey);
    626    }
    627    return rv;
    628 }
    629 
    630 ErrorCode
    631 doChallengeResponse(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
    632                    CRMFCertRequest *certReq, CERTCertificate *newCert,
    633                    ChallengeCreationInfo *challs, int *numChall)
    634 {
    635    CRMFPOPOPrivKey *privKey = NULL;
    636    CRMFPOPOPrivKeyChoice privKeyChoice;
    637    SECStatus srv;
    638    ErrorCode rv = NO_ERROR;
    639 
    640    srv = CRMF_CertReqMsgGetPOPKeyEncipherment(currReq, &privKey);
    641    if (srv != SECSuccess || privKey == NULL) {
    642        rv = ERROR_GETTING_KEY_ENCIPHERMENT;
    643        goto loser;
    644    }
    645    privKeyChoice = CRMF_POPOPrivKeyGetChoice(privKey);
    646    CRMF_DestroyPOPOPrivKey(privKey);
    647    switch (privKeyChoice) {
    648        case crmfSubsequentMessage:
    649            challs = &challs[*numChall];
    650            challs->random = rand();
    651            challs->pubKey = CERT_ExtractPublicKey(newCert);
    652            if (challs->pubKey == NULL) {
    653                rv =
    654                    ERROR_RETRIEVING_PUB_KEY_FOR_CHALL;
    655                goto loser;
    656            }
    657            (*numChall)++;
    658            rv = DO_CHALLENGE_RESPONSE;
    659            break;
    660        case crmfThisMessage:
    661            /* There'd better be a PKIArchiveControl in this message */
    662            if (!CRMF_CertRequestIsControlPresent(certReq,
    663                                                  crmfPKIArchiveOptionsControl)) {
    664                rv =
    665                    ERROR_NO_POP_FOR_PRIVKEY;
    666                goto loser;
    667            }
    668            break;
    669        default:
    670            rv = ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE;
    671            goto loser;
    672    }
    673 loser:
    674    return rv;
    675 }
    676 
    677 ErrorCode
    678 doProofOfPossession(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
    679                    CRMFCertRequest *certReq, CERTCertificate *newCert,
    680                    ChallengeCreationInfo *challs, int *numChall)
    681 {
    682    CRMFPOPChoice popChoice;
    683    ErrorCode rv = NO_ERROR;
    684 
    685    popChoice = CRMF_CertReqMsgGetPOPType(currReq);
    686    if (popChoice == crmfNoPOPChoice) {
    687        rv = NO_POP_FOR_REQUEST;
    688        goto loser;
    689    }
    690    switch (popChoice) {
    691        case crmfSignature:
    692            rv = verifySignature(varTable, currReq, certReq, newCert);
    693            break;
    694        case crmfKeyEncipherment:
    695            rv = doChallengeResponse(varTable, currReq, certReq, newCert,
    696                                     challs, numChall);
    697            break;
    698        case crmfRAVerified:
    699        case crmfKeyAgreement:
    700        default:
    701            rv = UNSUPPORTED_POP;
    702            goto loser;
    703    }
    704 loser:
    705    return rv;
    706 }
    707 
    708 void
    709 convertB64ToJS(char *base64)
    710 {
    711    int i;
    712 
    713    for (i = 0; base64[i] != '\0'; i++) {
    714        if (base64[i] == '\n') {
    715            printf("\\n");
    716        } else {
    717            printf("%c", base64[i]);
    718        }
    719    }
    720 }
    721 
    722 void
    723 formatChallenge(char *chall64, char *certRepContentDER,
    724                ChallengeCreationInfo *challInfo, int numChalls)
    725 {
    726    printf("function respondToChallenge() {\n"
    727           "  var chalForm = document.chalForm;\n\n"
    728           "  chalForm.CertRepContent.value = '");
    729    convertB64ToJS(certRepContentDER);
    730    printf("';\n"
    731           "  chalForm.ChallResponse.value = crypto.popChallengeResponse('");
    732    convertB64ToJS(chall64);
    733    printf("');\n"
    734           "  chalForm.submit();\n"
    735           "}\n");
    736 }
    737 
    738 void
    739 spitOutChallenge(char *chall64, char *certRepContentDER,
    740                 ChallengeCreationInfo *challInfo, int numChalls,
    741                 char *nickname)
    742 {
    743    int i;
    744 
    745    spitOutHeaders();
    746    printf("<html>\n"
    747           "<head>\n"
    748           "<title>Challenge Page</title>\n"
    749           "<script language=\"JavaScript\">\n"
    750           "<!--\n");
    751    /* The JavaScript function actually gets defined within
    752     * this function call
    753     */
    754    formatChallenge(chall64, certRepContentDER, challInfo, numChalls);
    755    printf("// -->\n"
    756           "</script>\n"
    757           "</head>\n"
    758           "<body onLoad='respondToChallenge()'>\n"
    759           "<h1>Cartman is now responding to the Challenge "
    760           "presented by the CGI</h1>\n"
    761           "<form action='crmfcgi' method='post' name='chalForm'>\n"
    762           "<input type='hidden' name=CertRepContent value=''>\n"
    763           "<input type='hidden' name=ChallResponse value=''>\n");
    764    for (i = 0; i < numChalls; i++) {
    765        printf("<input type='hidden' name='chal%d' value='%d'>\n",
    766               i + 1, challInfo[i].random);
    767    }
    768    printf("<input type='hidden' name='nickname' value='%s'>\n", nickname);
    769    printf("</form>\n</body>\n</html>");
    770 }
    771 
    772 ErrorCode
    773 issueChallenge(CertResponseInfo *issuedCerts, int numCerts,
    774               ChallengeCreationInfo *challInfo, int numChalls,
    775               CERTCertificate *issuer, CGIVarTable *varTable)
    776 {
    777    ErrorCode rv = NO_ERROR;
    778    CMMFPOPODecKeyChallContent *chalContent = NULL;
    779    int i;
    780    SECStatus srv;
    781    PLArenaPool *poolp;
    782    CERTGeneralName *genName;
    783    SECItem *challDER = NULL;
    784    char *chall64, *certRepContentDER;
    785 
    786    rv = createCMMFResponse(issuedCerts, numCerts, issuer,
    787                            &certRepContentDER);
    788    if (rv != NO_ERROR) {
    789        goto loser;
    790    }
    791    chalContent = CMMF_CreatePOPODecKeyChallContent();
    792    if (chalContent == NULL) {
    793        rv = ERROR_CREATING_EMPTY_CHAL_CONTENT;
    794        goto loser;
    795    }
    796    poolp = PORT_NewArena(1024);
    797    if (poolp == NULL) {
    798        rv = OUT_OF_MEMORY;
    799        goto loser;
    800    }
    801    genName = CERT_GetCertificateNames(issuer, poolp);
    802    if (genName == NULL) {
    803        rv = ERROR_EXTRACTING_GEN_NAME_FROM_ISSUER;
    804        goto loser;
    805    }
    806    for (i = 0; i < numChalls; i++) {
    807        srv = CMMF_POPODecKeyChallContentSetNextChallenge(chalContent,
    808                                                          challInfo[i].random,
    809                                                          genName,
    810                                                          challInfo[i].pubKey,
    811                                                          varTable);
    812        SECKEY_DestroyPublicKey(challInfo[i].pubKey);
    813        if (srv != SECSuccess) {
    814            rv = ERROR_SETTING_CHALLENGE;
    815            goto loser;
    816        }
    817    }
    818    challDER = SEC_ASN1EncodeItem(NULL, NULL, chalContent,
    819                                  CMMFPOPODecKeyChallContentTemplate);
    820    if (challDER == NULL) {
    821        rv = ERROR_ENCODING_CHALL;
    822        goto loser;
    823    }
    824    chall64 = BTOA_DataToAscii(challDER->data, challDER->len);
    825    SECITEM_FreeItem(challDER, PR_TRUE);
    826    if (chall64 == NULL) {
    827        rv = ERROR_CONVERTING_CHALL_TO_BASE64;
    828        goto loser;
    829    }
    830    spitOutChallenge(chall64, certRepContentDER, challInfo, numChalls,
    831                     getNickname(issuedCerts[0].cert));
    832 loser:
    833    return rv;
    834 }
    835 
    836 ErrorCode
    837 processRequest(CGIVarTable *varTable)
    838 {
    839    CERTCertDBHandle *certdb;
    840    SECKEYKeyDBHandle *keydb;
    841    CRMFCertReqMessages *certReqs = NULL;
    842    const char *crmfReq;
    843    const char *caNickname;
    844    CERTCertificate *caCert = NULL;
    845    CertResponseInfo *issuedCerts = NULL;
    846    CERTSubjectPublicKeyInfo spki = { 0 };
    847    ErrorCode rv = NO_ERROR;
    848    PRBool doChallengeResponse = PR_FALSE;
    849    SECItem der = { 0 };
    850    SECStatus srv;
    851    CERTCertificateRequest oldCertReq = { 0 };
    852    CRMFCertReqMsg **reqMsgs = NULL, *currReq = NULL;
    853    CRMFCertRequest **reqs = NULL, *certReq = NULL;
    854    CERTName subject = { 0 };
    855    int numReqs, i;
    856    ChallengeCreationInfo *challInfo = NULL;
    857    int numChalls = 0;
    858 
    859    certdb = CERT_GetDefaultCertDB();
    860    keydb = SECKEY_GetDefaultKeyDB();
    861    crmfReq = CGITableFindValue(varTable, "CRMFRequest");
    862    if (crmfReq == NULL) {
    863        rv = CGI_VAR_MISSING;
    864        missingVar = "CRMFRequest";
    865        goto loser;
    866    }
    867    caNickname = CGITableFindValue(varTable, "CANickname");
    868    if (caNickname == NULL) {
    869        rv = CGI_VAR_MISSING;
    870        missingVar = "CANickname";
    871        goto loser;
    872    }
    873    caCert = CERT_FindCertByNickname(certdb, caNickname);
    874    if (caCert == NULL) {
    875        rv = COULD_NOT_FIND_CA;
    876        goto loser;
    877    }
    878    srv = ATOB_ConvertAsciiToItem(&der, crmfReq);
    879    if (srv != SECSuccess) {
    880        rv = BAD_ASCII_FOR_REQ;
    881        goto loser;
    882    }
    883    certReqs = CRMF_CreateCertReqMessagesFromDER(der.data, der.len);
    884    SECITEM_FreeItem(&der, PR_FALSE);
    885    if (certReqs == NULL) {
    886        rv = COULD_NOT_DECODE_REQS;
    887        goto loser;
    888    }
    889    numReqs = CRMF_CertReqMessagesGetNumMessages(certReqs);
    890    issuedCerts = PORT_ZNewArray(CertResponseInfo, numReqs);
    891    challInfo = PORT_ZNewArray(ChallengeCreationInfo, numReqs);
    892    if (issuedCerts == NULL || challInfo == NULL) {
    893        rv = OUT_OF_MEMORY;
    894        goto loser;
    895    }
    896    reqMsgs = PORT_ZNewArray(CRMFCertReqMsg *, numReqs);
    897    reqs = PORT_ZNewArray(CRMFCertRequest *, numReqs);
    898    if (reqMsgs == NULL || reqs == NULL) {
    899        rv = OUT_OF_MEMORY;
    900        goto loser;
    901    }
    902    for (i = 0; i < numReqs; i++) {
    903        currReq = reqMsgs[i] =
    904            CRMF_CertReqMessagesGetCertReqMsgAtIndex(certReqs, i);
    905        if (currReq == NULL) {
    906            rv = ERROR_RETRIEVING_REQUEST_MSG;
    907            goto loser;
    908        }
    909        certReq = reqs[i] = CRMF_CertReqMsgGetCertRequest(currReq);
    910        if (certReq == NULL) {
    911            rv = ERROR_RETRIEVING_CERT_REQUEST;
    912            goto loser;
    913        }
    914        srv = CRMF_CertRequestGetCertTemplateSubject(certReq, &subject);
    915        if (srv != SECSuccess) {
    916            rv = ERROR_RETRIEVING_SUBJECT_FROM_REQ;
    917            goto loser;
    918        }
    919        srv = CRMF_CertRequestGetCertTemplatePublicKey(certReq, &spki);
    920        if (srv != SECSuccess) {
    921            rv = ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ;
    922            goto loser;
    923        }
    924        rv = initOldCertReq(&oldCertReq, &subject, &spki);
    925        if (rv != NO_ERROR) {
    926            goto loser;
    927        }
    928        rv = createNewCert(&issuedCerts[i].cert, &oldCertReq, currReq, certReq,
    929                           caCert, varTable);
    930        if (rv != NO_ERROR) {
    931            goto loser;
    932        }
    933        rv = doProofOfPossession(varTable, currReq, certReq, issuedCerts[i].cert,
    934                                 challInfo, &numChalls);
    935        if (rv != NO_ERROR) {
    936            if (rv == DO_CHALLENGE_RESPONSE) {
    937                doChallengeResponse = PR_TRUE;
    938            } else {
    939                goto loser;
    940            }
    941        }
    942        CRMF_CertReqMsgGetID(currReq, &issuedCerts[i].certReqID);
    943        CRMF_DestroyCertReqMsg(currReq);
    944        CRMF_DestroyCertRequest(certReq);
    945    }
    946    if (doChallengeResponse) {
    947        rv = issueChallenge(issuedCerts, numReqs, challInfo, numChalls, caCert,
    948                            varTable);
    949    } else {
    950        rv = issueCerts(issuedCerts, numReqs, caCert);
    951    }
    952 loser:
    953    if (certReqs != NULL) {
    954        CRMF_DestroyCertReqMessages(certReqs);
    955    }
    956    return rv;
    957 }
    958 
    959 ErrorCode
    960 processChallengeResponse(CGIVarTable *varTable, const char *certRepContent)
    961 {
    962    SECItem binDER = { 0 };
    963    SECStatus srv;
    964    ErrorCode rv = NO_ERROR;
    965    const char *clientResponse;
    966    const char *formChalValue;
    967    const char *nickname;
    968    CMMFPOPODecKeyRespContent *respContent = NULL;
    969    int numResponses, i;
    970    long curResponse, expectedResponse;
    971    char cgiChalVar[10];
    972 #ifdef WRITE_OUT_RESPONSE
    973    SECItem certRepBinDER = { 0 };
    974 
    975    ATOB_ConvertAsciiToItem(&certRepBinDER, certRepContent);
    976    writeOutItem("challCertRepContent.der", &certRepBinDER);
    977    PORT_Free(certRepBinDER.data);
    978 #endif
    979    clientResponse = CGITableFindValue(varTable, "ChallResponse");
    980    if (clientResponse == NULL) {
    981        rv = REQ_CGI_VAR_NOT_PRESENT;
    982        missingVar = "ChallResponse";
    983        goto loser;
    984    }
    985    srv = ATOB_ConvertAsciiToItem(&binDER, clientResponse);
    986    if (srv != SECSuccess) {
    987        rv = ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN;
    988        goto loser;
    989    }
    990    respContent = CMMF_CreatePOPODecKeyRespContentFromDER(binDER.data,
    991                                                          binDER.len);
    992    SECITEM_FreeItem(&binDER, PR_FALSE);
    993    binDER.data = NULL;
    994    if (respContent == NULL) {
    995        rv = ERROR_CREATING_KEY_RESP_FROM_DER;
    996        goto loser;
    997    }
    998    numResponses = CMMF_POPODecKeyRespContentGetNumResponses(respContent);
    999    for (i = 0; i < numResponses; i++) {
   1000        srv = CMMF_POPODecKeyRespContentGetResponse(respContent, i, &curResponse);
   1001        if (srv != SECSuccess) {
   1002            rv = ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE;
   1003            goto loser;
   1004        }
   1005        snprintf(cgiChalVar, sizeof(cgiChalVar), "chal%d", i + 1);
   1006        formChalValue = CGITableFindValue(varTable, cgiChalVar);
   1007        if (formChalValue == NULL) {
   1008            rv = REQ_CGI_VAR_NOT_PRESENT;
   1009            missingVar = strdup(cgiChalVar);
   1010            goto loser;
   1011        }
   1012        sscanf(formChalValue, "%ld", &expectedResponse);
   1013        if (expectedResponse != curResponse) {
   1014            rv = ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED;
   1015            goto loser;
   1016        }
   1017    }
   1018    nickname = CGITableFindValue(varTable, "nickname");
   1019    if (nickname == NULL) {
   1020        rv = REQ_CGI_VAR_NOT_PRESENT;
   1021        missingVar = "nickname";
   1022        goto loser;
   1023    }
   1024    spitOutCMMFResponse(nickname, certRepContent);
   1025 loser:
   1026    if (respContent != NULL) {
   1027        CMMF_DestroyPOPODecKeyRespContent(respContent);
   1028    }
   1029    return rv;
   1030 }
   1031 
   1032 int
   1033 main()
   1034 {
   1035    char *form_output = NULL;
   1036    int form_output_len, form_output_used;
   1037    CGIVarTable varTable = { 0 };
   1038    ErrorCode errNum = 0;
   1039    char *certRepContent;
   1040 
   1041 #ifdef ATTACH_CGI
   1042    /* Put an ifinite loop in here so I can attach to
   1043     * the process after the process is spun off
   1044     */
   1045    {
   1046        int stupid = 1;
   1047        while (stupid)
   1048            ;
   1049    }
   1050 #endif
   1051 
   1052    form_output_used = 0;
   1053    srand(time(NULL));
   1054    while (feof(stdin) == 0) {
   1055        if (form_output == NULL) {
   1056            form_output = PORT_NewArray(char, DEFAULT_ALLOC_SIZE + 1);
   1057            form_output_len = DEFAULT_ALLOC_SIZE;
   1058        } else if ((form_output_used + DEFAULT_ALLOC_SIZE) >= form_output_len) {
   1059            form_output_len += DEFAULT_ALLOC_SIZE;
   1060            form_output = PORT_Realloc(form_output, form_output_len + 1);
   1061        }
   1062        form_output_used += fread(&form_output[form_output_used], sizeof(char),
   1063                                  DEFAULT_ALLOC_SIZE, stdin);
   1064    }
   1065    ParseInputVariables(&varTable, form_output);
   1066    certRepContent = CGITableFindValue(&varTable, "CertRepContent");
   1067    if (certRepContent == NULL) {
   1068        errNum = initNSS(&varTable);
   1069        if (errNum != 0) {
   1070            goto loser;
   1071        }
   1072        errNum = processRequest(&varTable);
   1073    } else {
   1074        errNum = processChallengeResponse(&varTable, certRepContent);
   1075    }
   1076    if (errNum != NO_ERROR) {
   1077        goto loser;
   1078    }
   1079    goto done;
   1080 loser:
   1081    dumpErrorMessage(errNum);
   1082 done:
   1083    free(form_output);
   1084    return 0;
   1085 }