Back to index

lightning-sunbird  0.9+nobinonly
crmfcgi.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "seccomon.h"
00038 #include "nss.h"
00039 #include "key.h"
00040 #include "cert.h"
00041 #include "pk11func.h"
00042 #include "secmod.h"
00043 #include "cmmf.h"
00044 #include "crmf.h"
00045 #include "base64.h"
00046 #include "secasn1.h"
00047 #include "cryptohi.h"
00048 #include <string.h>
00049 #include <stdlib.h>
00050 #include <stdio.h>
00051 
00052 #define DEFAULT_ALLOC_SIZE 200
00053 #define DEFAULT_CGI_VARS   20
00054 
00055 typedef struct CGIVariableStr {
00056   char *name;
00057   char *value;
00058 } CGIVariable;
00059 
00060 typedef struct CGIVarTableStr {
00061   CGIVariable **variables;
00062   int           numVars;
00063   int           numAlloc; 
00064 } CGIVarTable;
00065 
00066 typedef struct CertResponseInfoStr {
00067   CERTCertificate *cert;
00068   long             certReqID;
00069 } CertResponseInfo;
00070 
00071 typedef struct ChallengeCreationInfoStr {
00072   long             random;
00073   SECKEYPublicKey *pubKey;
00074 } ChallengeCreationInfo;
00075 
00076 char *missingVar = NULL;
00077 
00078 /*
00079  * Error values.
00080  */
00081 typedef enum {
00082   NO_ERROR = 0,
00083   NSS_INIT_FAILED,
00084   AUTH_FAILED,
00085   REQ_CGI_VAR_NOT_PRESENT,
00086   CRMF_REQ_NOT_PRESENT,
00087   BAD_ASCII_FOR_REQ,
00088   CGI_VAR_MISSING,
00089   COULD_NOT_FIND_CA,
00090   COULD_NOT_DECODE_REQS,
00091   OUT_OF_MEMORY,
00092   ERROR_RETRIEVING_REQUEST_MSG,
00093   ERROR_RETRIEVING_CERT_REQUEST,
00094   ERROR_RETRIEVING_SUBJECT_FROM_REQ,
00095   ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ,
00096   ERROR_CREATING_NEW_CERTIFICATE,
00097   COULD_NOT_START_EXTENSIONS,
00098   ERROR_RETRIEVING_EXT_FROM_REQ,
00099   ERROR_ADDING_EXT_TO_CERT,
00100   ERROR_ENDING_EXTENSIONS,
00101   COULD_NOT_FIND_ISSUER_PRIVATE_KEY,
00102   UNSUPPORTED_SIGN_OPERATION_FOR_ISSUER,
00103   ERROR_SETTING_SIGN_ALG,
00104   ERROR_ENCODING_NEW_CERT,
00105   ERROR_SIGNING_NEW_CERT,
00106   ERROR_CREATING_CERT_REP_CONTENT,
00107   ERROR_CREATING_SINGLE_CERT_RESPONSE,
00108   ERROR_SETTING_CERT_RESPONSES,
00109   ERROR_CREATING_CA_LIST,
00110   ERROR_ADDING_ISSUER_TO_CA_LIST,
00111   ERROR_ENCODING_CERT_REP_CONTENT,
00112   NO_POP_FOR_REQUEST,
00113   UNSUPPORTED_POP,
00114   ERROR_RETRIEVING_POP_SIGN_KEY,
00115   ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY,
00116   ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY,
00117   DO_CHALLENGE_RESPONSE,
00118   ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT,
00119   ERROR_ENCODING_CERT_REQ_FOR_POP,
00120   ERROR_VERIFYING_SIGNATURE_POP,
00121   ERROR_RETRIEVING_PUB_KEY_FOR_CHALL,
00122   ERROR_CREATING_EMPTY_CHAL_CONTENT,
00123   ERROR_EXTRACTING_GEN_NAME_FROM_ISSUER,
00124   ERROR_SETTING_CHALLENGE,
00125   ERROR_ENCODING_CHALL,
00126   ERROR_CONVERTING_CHALL_TO_BASE64,
00127   ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN,
00128   ERROR_CREATING_KEY_RESP_FROM_DER,
00129   ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE,
00130   ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED,
00131   ERROR_GETTING_KEY_ENCIPHERMENT,
00132   ERROR_NO_POP_FOR_PRIVKEY,
00133   ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE
00134 } ErrorCode;
00135 
00136 const char *
00137 CGITableFindValue(CGIVarTable *varTable, const char *key);
00138 
00139 void
00140 spitOutHeaders(void)
00141 {
00142   printf("Content-type: text/html\n\n");
00143 }
00144 
00145 void
00146 dumpRequest(CGIVarTable *varTable)
00147 {
00148   int i;
00149   CGIVariable *var;
00150 
00151   printf ("<table border=1 cellpadding=1 cellspacing=1 width=\"100%%\">\n");
00152   printf ("<tr><td><b><center>Variable Name<center></b></td>"
00153           "<td><b><center>Value</center></b></td></tr>\n");
00154   for (i=0; i<varTable->numVars; i++) {
00155     var = varTable->variables[i];
00156     printf ("<tr><td><pre>%s</pre></td><td><pre>%s</pre></td></tr>\n", 
00157              var->name, var->value);
00158   }
00159   printf("</table>\n");
00160 }
00161 
00162 void
00163 echo_request(CGIVarTable *varTable)
00164 {
00165   spitOutHeaders();
00166   printf("<html><head><title>CGI Echo Page</title></head>\n"
00167         "<body><h1>Got the following request</h1>\n");
00168   dumpRequest(varTable);
00169   printf("</body></html>");
00170 }
00171 
00172 void
00173 processVariable(CGIVariable *var)
00174 {
00175   char *plusSign, *percentSign;
00176 
00177   /*First look for all of the '+' and convert them to spaces */
00178   plusSign = var->value;
00179   while ((plusSign=strchr(plusSign, '+')) != NULL) {
00180     *plusSign = ' ';
00181   }
00182   percentSign = var->value;
00183   while ((percentSign=strchr(percentSign, '%')) != NULL) {
00184     char string[3];
00185     int  value;
00186 
00187     string[0] = percentSign[1];
00188     string[1] = percentSign[2];
00189     string[2] = '\0';
00190 
00191     sscanf(string,"%x", &value);
00192     *percentSign = (char)value;
00193     memmove(&percentSign[1], &percentSign[3], 1+strlen(&percentSign[3]));
00194   }
00195 }
00196 
00197 char *
00198 parseNextVariable(CGIVarTable *varTable, char *form_output)
00199 {
00200   char *ampersand, *equal;
00201   CGIVariable *var;
00202 
00203   if (varTable->numVars == varTable->numAlloc) {
00204     CGIVariable **newArr = realloc(varTable->variables, 
00205                                    (varTable->numAlloc + DEFAULT_CGI_VARS)*sizeof(CGIVariable*));
00206     if (newArr == NULL) {
00207       return NULL;
00208     }
00209     varTable->variables = newArr;
00210     varTable->numAlloc += DEFAULT_CGI_VARS;
00211   }
00212   equal     = strchr(form_output, '=');
00213   if (equal == NULL) {
00214     return NULL;
00215   }
00216   ampersand = strchr(equal, '&');
00217   if (ampersand == NULL) {
00218     return NULL;
00219   }
00220   equal[0] = '\0';
00221   if (ampersand != NULL) {
00222     ampersand[0] = '\0';
00223   }
00224   var = malloc(sizeof(CGIVariable));
00225   var->name = form_output; 
00226   var->value = &equal[1];
00227   varTable->variables[varTable->numVars] = var;
00228   varTable->numVars++;
00229   processVariable(var);
00230   return (ampersand != NULL) ? &ampersand[1] : NULL;
00231 }
00232 
00233 void
00234 ParseInputVariables(CGIVarTable *varTable, char *form_output)
00235 {
00236   varTable->variables = malloc(sizeof(CGIVariable*)*DEFAULT_CGI_VARS);
00237   varTable->numVars = 0;
00238   varTable->numAlloc = DEFAULT_CGI_VARS;
00239   while (form_output && form_output[0] != '\0') {
00240     form_output = parseNextVariable(varTable, form_output);
00241   }
00242 }
00243 
00244 const char *
00245 CGITableFindValue(CGIVarTable *varTable, const char *key)
00246 {
00247   const char *retVal = NULL;
00248   int i;
00249 
00250   for (i=0; i<varTable->numVars; i++) {
00251     if (strcmp(varTable->variables[i]->name, key) == 0) {
00252       retVal = varTable->variables[i]->value;
00253       break;
00254     } 
00255   }
00256   return retVal;
00257 }
00258 
00259 char*
00260 passwordCallback(PK11SlotInfo *slot, PRBool retry, void *arg)
00261 {
00262   const char *passwd;
00263   if (retry) {
00264     return NULL;
00265   }
00266   passwd = CGITableFindValue((CGIVarTable*)arg, "dbPassword");
00267   if (passwd == NULL) {
00268     return NULL;
00269   }
00270   return PORT_Strdup(passwd);
00271 }
00272 
00273 ErrorCode
00274 initNSS(CGIVarTable *varTable)
00275 {
00276   const char *nssDir;
00277   PK11SlotInfo *keySlot;
00278   SECStatus rv;
00279 
00280   nssDir = CGITableFindValue(varTable,"NSSDirectory");
00281   if (nssDir == NULL) {
00282     missingVar = "NSSDirectory";
00283     return REQ_CGI_VAR_NOT_PRESENT;
00284   }
00285   rv = NSS_Init(nssDir);
00286   if (rv != SECSuccess) {
00287     return NSS_INIT_FAILED;
00288   }
00289   PK11_SetPasswordFunc(passwordCallback);
00290   keySlot = PK11_GetInternalKeySlot();
00291   rv = PK11_Authenticate(keySlot, PR_FALSE, varTable);
00292   PK11_FreeSlot(keySlot);
00293   if (rv != SECSuccess) {
00294     return AUTH_FAILED;
00295   }
00296   return NO_ERROR;
00297 }
00298 
00299 void
00300 dumpErrorMessage(ErrorCode errNum)
00301 {
00302   spitOutHeaders();
00303   printf("<html><head><title>Error</title></head><body><h1>Error processing "
00304         "data</h1> Received the error %d<p>", errNum);
00305   if (errNum  ==   REQ_CGI_VAR_NOT_PRESENT) {
00306     printf ("The missing variable is %s.", missingVar);
00307   }
00308   printf ("<i>More useful information here in the future.</i></body></html>");
00309 }
00310 
00311 ErrorCode
00312 initOldCertReq(CERTCertificateRequest *oldCertReq,
00313               CERTName *subject, CERTSubjectPublicKeyInfo *spki)
00314 {
00315   PRArenaPool *poolp;
00316 
00317   poolp = oldCertReq->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00318   SEC_ASN1EncodeInteger(poolp, &oldCertReq->version, 
00319                      SEC_CERTIFICATE_VERSION_3);
00320   CERT_CopyName(poolp, &oldCertReq->subject, subject);
00321   SECKEY_CopySubjectPublicKeyInfo(poolp, &oldCertReq->subjectPublicKeyInfo,
00322                               spki);
00323   oldCertReq->attributes = NULL;
00324   return NO_ERROR;
00325 }
00326 
00327 ErrorCode
00328 addExtensions(CERTCertificate *newCert, CRMFCertRequest *certReq)
00329 {
00330   int numExtensions, i;
00331   void *extHandle;
00332   ErrorCode rv = NO_ERROR;
00333   CRMFCertExtension *ext;
00334   SECStatus srv;
00335 
00336   numExtensions = CRMF_CertRequestGetNumberOfExtensions(certReq);
00337   if (numExtensions == 0) {
00338     /* No extensions to add */
00339     return NO_ERROR;
00340   }
00341   extHandle = CERT_StartCertExtensions(newCert);
00342   if (extHandle == NULL) {
00343     rv = COULD_NOT_START_EXTENSIONS;
00344     goto loser;
00345   }
00346   for (i=0; i<numExtensions; i++) {
00347     ext = CRMF_CertRequestGetExtensionAtIndex(certReq, i);
00348     if (ext == NULL) {
00349       rv = ERROR_RETRIEVING_EXT_FROM_REQ;
00350     }
00351     srv = CERT_AddExtension(extHandle, CRMF_CertExtensionGetOidTag(ext),
00352                          CRMF_CertExtensionGetValue(ext),
00353                          CRMF_CertExtensionGetIsCritical(ext), PR_FALSE);
00354     if (srv != SECSuccess) {
00355       rv = ERROR_ADDING_EXT_TO_CERT;
00356     }
00357   }
00358   srv = CERT_FinishExtensions(extHandle);
00359   if (srv != SECSuccess) {
00360     rv = ERROR_ENDING_EXTENSIONS;
00361     goto loser;
00362   }
00363   return NO_ERROR;
00364  loser:
00365   return rv;
00366 }
00367 
00368 void
00369 writeOutItem(const char *filePath, SECItem *der)
00370 {
00371   PRFileDesc *outfile;
00372 
00373   outfile = PR_Open (filePath,
00374                    PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
00375                    0666);
00376   PR_Write(outfile, der->data, der->len);
00377   PR_Close(outfile);
00378 
00379 }
00380 
00381 ErrorCode
00382 createNewCert(CERTCertificate**issuedCert,CERTCertificateRequest *oldCertReq,
00383              CRMFCertReqMsg *currReq, CRMFCertRequest *certReq, 
00384              CERTCertificate *issuerCert, CGIVarTable *varTable)
00385 {
00386   CERTCertificate *newCert = NULL;
00387   CERTValidity *validity;
00388   PRExplodedTime printableTime;
00389   PRTime now, after;
00390   ErrorCode rv=NO_ERROR;
00391   SECKEYPrivateKey *issuerPrivKey;
00392   SECItem derCert = { 0 };
00393   SECOidTag signTag;
00394   SECStatus srv;
00395   long version;
00396 
00397   now = PR_Now();
00398   PR_ExplodeTime(now, PR_GMTParameters, &printableTime);
00399   printableTime.tm_month += 9;
00400   after = PR_ImplodeTime(&printableTime);
00401   validity = CERT_CreateValidity(now, after);
00402   newCert = *issuedCert = 
00403     CERT_CreateCertificate(rand(), &(issuerCert->subject), validity, 
00404                         oldCertReq);
00405   if (newCert == NULL) {
00406     rv = ERROR_CREATING_NEW_CERTIFICATE;
00407     goto loser;
00408   }
00409   rv = addExtensions(newCert, certReq);
00410   if (rv != NO_ERROR) {
00411     goto loser;
00412   }
00413   issuerPrivKey = PK11_FindKeyByAnyCert(issuerCert, varTable);
00414   if (issuerPrivKey == NULL) {
00415     rv = COULD_NOT_FIND_ISSUER_PRIVATE_KEY;
00416   }
00417   switch(issuerPrivKey->keyType) {
00418   case rsaKey:
00419     signTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
00420     break;
00421   case dsaKey:
00422     signTag = SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST;
00423     break;
00424   default:
00425     rv = UNSUPPORTED_SIGN_OPERATION_FOR_ISSUER;
00426     goto loser;
00427   }
00428   srv = SECOID_SetAlgorithmID(newCert->arena, &newCert->signature, 
00429                            signTag, 0);
00430   if (srv != SECSuccess) {
00431     rv = ERROR_SETTING_SIGN_ALG;
00432     goto loser;
00433   }
00434   srv = CRMF_CertRequestGetCertTemplateVersion(certReq, &version);
00435   if (srv != SECSuccess) {
00436     /* No version included in the request */
00437     *(newCert->version.data) = SEC_CERTIFICATE_VERSION_3;
00438   } else {
00439     SECITEM_FreeItem(&newCert->version, PR_FALSE);
00440     SEC_ASN1EncodeInteger(newCert->arena, &newCert->version, version);
00441   }
00442   SEC_ASN1EncodeItem(newCert->arena, &derCert, newCert, 
00443                    CERT_CertificateTemplate);
00444   if (derCert.data == NULL) {
00445     rv = ERROR_ENCODING_NEW_CERT;
00446     goto loser;
00447   }
00448   srv = SEC_DerSignData(newCert->arena, &(newCert->derCert), derCert.data,
00449                      derCert.len, issuerPrivKey, signTag);
00450   if (srv != SECSuccess) {
00451     rv = ERROR_SIGNING_NEW_CERT;
00452     goto loser;
00453   }
00454 #ifdef WRITE_OUT_RESPONSE
00455   writeOutItem("newcert.der", &newCert->derCert);
00456 #endif
00457   return NO_ERROR;
00458  loser:
00459   *issuedCert = NULL;
00460   if (newCert) {
00461     CERT_DestroyCertificate(newCert);
00462   }
00463   return rv;
00464                    
00465 }
00466 
00467 void
00468 formatCMMFResponse(char *nickname, char *base64Response)
00469 {
00470   char *currLine, *nextLine;
00471 
00472   printf("var retVal = crypto.importUserCertificates(\"%s\",\n", nickname);
00473   currLine = base64Response;
00474   while (1) {
00475     nextLine = strchr(currLine, '\n');
00476     if (nextLine == NULL) {
00477       /* print out the last line here. */
00478       printf ("\"%s\",\n", currLine);
00479       break;
00480     }
00481     nextLine[0] = '\0';
00482     printf("\"%s\\n\"+\n", currLine);
00483     currLine = nextLine+1;
00484   }
00485   printf("true);\n"
00486         "if(retVal == '') {\n"
00487         "\tdocument.write(\"<h1>New Certificate Succesfully Imported.</h1>\");\n"
00488         "} else {\n"
00489         "\tdocument.write(\"<h2>Unable to import New Certificate</h2>\");\n"
00490         "\tdocument.write(\"crypto.importUserCertificates returned <b>\");\n"
00491         "\tdocument.write(retVal);\n"
00492         "\tdocument.write(\"</b>\");\n"
00493         "}\n");
00494 }
00495 
00496 void
00497 spitOutCMMFResponse(char *nickname, char *base64Response)
00498 {
00499   spitOutHeaders();
00500   printf("<html>\n<head>\n<title>CMMF Resonse Page</title>\n</head>\n\n"
00501         "<body><h1>CMMF Response Page</h1>\n"
00502         "<script language=\"JavaScript\">\n"
00503         "<!--\n");
00504   formatCMMFResponse(nickname, base64Response);
00505   printf("// -->\n"
00506         "</script>\n</body>\n</html>");
00507 }
00508 
00509 char*
00510 getNickname(CERTCertificate *cert)
00511 {
00512   char *nickname;
00513 
00514   if (cert->nickname != NULL) {
00515     return cert->nickname;
00516   }
00517   nickname = CERT_GetCommonName(&cert->subject);
00518   if (nickname != NULL) {
00519     return nickname;
00520   }
00521   return CERT_NameToAscii(&cert->subject);
00522 }
00523 
00524 ErrorCode
00525 createCMMFResponse(CertResponseInfo *issuedCerts, int numCerts, 
00526                  CERTCertificate *issuerCert, char **base64der)
00527 {
00528  CMMFCertRepContent *certRepContent=NULL;
00529   ErrorCode rv = NO_ERROR;
00530   CMMFCertResponse **responses, *currResponse;
00531   CERTCertList *caList;
00532   int i;
00533   SECStatus srv;
00534   PRArenaPool *poolp;
00535   SECItem *der;
00536 
00537   certRepContent = CMMF_CreateCertRepContent();
00538   if (certRepContent == NULL) {
00539     rv = ERROR_CREATING_CERT_REP_CONTENT;
00540     goto loser;
00541   }
00542   responses = PORT_NewArray(CMMFCertResponse*, numCerts);
00543   if (responses == NULL) {
00544     rv = OUT_OF_MEMORY;
00545     goto loser;
00546   }
00547   for (i=0; i<numCerts;i++) {
00548     responses[i] = currResponse = 
00549       CMMF_CreateCertResponse(issuedCerts[i].certReqID);
00550     if (currResponse == NULL) {
00551       rv = ERROR_CREATING_SINGLE_CERT_RESPONSE;
00552       goto loser;
00553     }
00554     CMMF_CertResponseSetPKIStatusInfoStatus(currResponse, cmmfGranted);
00555     CMMF_CertResponseSetCertificate(currResponse, issuedCerts[i].cert);
00556   }
00557   srv = CMMF_CertRepContentSetCertResponses(certRepContent, responses,
00558                                        numCerts);
00559   if (srv != SECSuccess) {
00560     rv = ERROR_SETTING_CERT_RESPONSES;
00561     goto loser;
00562   }
00563   caList = CERT_NewCertList();
00564   if (caList == NULL) {
00565     rv = ERROR_CREATING_CA_LIST;
00566     goto loser;
00567   }
00568   srv = CERT_AddCertToListTail(caList, issuerCert);
00569   if (srv != SECSuccess) {
00570     rv = ERROR_ADDING_ISSUER_TO_CA_LIST;
00571     goto loser;
00572   }
00573   srv = CMMF_CertRepContentSetCAPubs(certRepContent, caList);
00574   CERT_DestroyCertList(caList);
00575   poolp = PORT_NewArena(1024);
00576   der = SEC_ASN1EncodeItem(poolp, NULL, certRepContent, 
00577                         CMMFCertRepContentTemplate);
00578   if (der == NULL) {
00579     rv = ERROR_ENCODING_CERT_REP_CONTENT;
00580     goto loser;
00581   }
00582 #ifdef WRITE_OUT_RESPONSE
00583   writeOutItem("CertRepContent.der", der);
00584 #endif
00585   *base64der = BTOA_DataToAscii(der->data, der->len);
00586   return NO_ERROR;
00587  loser:
00588   return rv;
00589 }
00590 
00591 ErrorCode
00592 issueCerts(CertResponseInfo *issuedCerts, int numCerts, 
00593           CERTCertificate *issuerCert)
00594 {
00595   ErrorCode rv;
00596   char *base64Response;
00597 
00598   rv = createCMMFResponse(issuedCerts, numCerts, issuerCert, &base64Response);
00599   if (rv != NO_ERROR) {
00600     goto loser;
00601   }
00602   spitOutCMMFResponse(getNickname(issuedCerts[0].cert),base64Response);
00603   return NO_ERROR;
00604  loser:
00605   return rv;
00606 }
00607 
00608 ErrorCode
00609 verifySignature(CGIVarTable *varTable, CRMFCertReqMsg *currReq, 
00610               CRMFCertRequest *certReq, CERTCertificate *newCert)
00611 {
00612   SECStatus srv;
00613   ErrorCode rv = NO_ERROR;
00614   CRMFPOPOSigningKey *signKey   = NULL;
00615   SECAlgorithmID     *algID     = NULL;
00616   SECItem            *signature = NULL;
00617   SECKEYPublicKey    *pubKey    = NULL;
00618   SECItem            *reqDER    = NULL;
00619 
00620   srv = CRMF_CertReqMsgGetPOPOSigningKey(currReq, &signKey);
00621   if (srv != SECSuccess || signKey == NULL) {
00622     rv = ERROR_RETRIEVING_POP_SIGN_KEY;
00623     goto loser;
00624   }
00625   algID = CRMF_POPOSigningKeyGetAlgID(signKey);
00626   if (algID == NULL) {
00627     rv = ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY;
00628     goto loser;
00629   }
00630   signature = CRMF_POPOSigningKeyGetSignature(signKey);
00631   if (signature == NULL) {
00632     rv = ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY;
00633     goto loser;
00634   }
00635   /* Make the length the number of bytes instead of bits */
00636   signature->len = (signature->len+7)/8;
00637   pubKey = CERT_ExtractPublicKey(newCert);
00638   if (pubKey == NULL) {
00639     rv = ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT;
00640     goto loser;
00641   }
00642   reqDER = SEC_ASN1EncodeItem(NULL, NULL, certReq, CRMFCertRequestTemplate);
00643   if (reqDER == NULL) {
00644     rv = ERROR_ENCODING_CERT_REQ_FOR_POP;
00645     goto loser;
00646   }
00647   srv = VFY_VerifyData(reqDER->data, reqDER->len, pubKey, signature,
00648                      SECOID_FindOIDTag(&algID->algorithm), varTable);
00649   if (srv != SECSuccess) {
00650     rv = ERROR_VERIFYING_SIGNATURE_POP;
00651     goto loser;
00652   }
00653   /* Fall thru in successfull case. */
00654  loser:
00655   if (pubKey != NULL) {
00656     SECKEY_DestroyPublicKey(pubKey);
00657   }
00658   if (reqDER != NULL) {
00659     SECITEM_FreeItem(reqDER, PR_TRUE);
00660   }
00661   if (signature != NULL) {
00662     SECITEM_FreeItem(signature, PR_TRUE);
00663   }
00664   if (algID != NULL) {
00665     SECOID_DestroyAlgorithmID(algID, PR_TRUE);
00666   }
00667   if (signKey != NULL) {
00668     CRMF_DestroyPOPOSigningKey(signKey);
00669   }  
00670   return rv;
00671 }
00672 
00673 ErrorCode
00674 doChallengeResponse(CGIVarTable *varTable, CRMFCertReqMsg *currReq, 
00675                   CRMFCertRequest *certReq, CERTCertificate *newCert,
00676                   ChallengeCreationInfo *challs, int *numChall)
00677 {
00678   CRMFPOPOPrivKey *privKey = NULL;
00679   CRMFPOPOPrivKeyChoice privKeyChoice;
00680   SECStatus srv;
00681   ErrorCode rv = NO_ERROR;
00682  
00683   srv = CRMF_CertReqMsgGetPOPKeyEncipherment(currReq, &privKey);
00684   if (srv != SECSuccess || privKey == NULL) {
00685     rv = ERROR_GETTING_KEY_ENCIPHERMENT;
00686     goto loser;
00687   } 
00688   privKeyChoice = CRMF_POPOPrivKeyGetChoice(privKey);
00689   CRMF_DestroyPOPOPrivKey(privKey);
00690   switch (privKeyChoice) {
00691   case crmfSubsequentMessage:
00692     challs = &challs[*numChall];
00693     challs->random = rand();
00694     challs->pubKey = CERT_ExtractPublicKey(newCert);
00695     if (challs->pubKey == NULL) {
00696       rv = ERROR_RETRIEVING_PUB_KEY_FOR_CHALL;
00697       goto loser;
00698     }
00699     (*numChall)++;
00700     rv = DO_CHALLENGE_RESPONSE;
00701     break;
00702   case crmfThisMessage:
00703     /* There'd better be a PKIArchiveControl in this message */
00704     if (!CRMF_CertRequestIsControlPresent(certReq, 
00705                                      crmfPKIArchiveOptionsControl)) {
00706       rv = ERROR_NO_POP_FOR_PRIVKEY;
00707       goto loser;
00708     }
00709     break;
00710   default:
00711     rv = ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE;
00712     goto loser;
00713   }
00714 loser:
00715   return rv;
00716 }
00717 
00718 ErrorCode
00719 doProofOfPossession(CGIVarTable *varTable, CRMFCertReqMsg *currReq, 
00720                   CRMFCertRequest *certReq, CERTCertificate *newCert,
00721                   ChallengeCreationInfo *challs, int *numChall)
00722 {
00723   CRMFPOPChoice popChoice;
00724   ErrorCode rv = NO_ERROR;
00725 
00726   popChoice = CRMF_CertReqMsgGetPOPType(currReq);
00727   if (popChoice == crmfNoPOPChoice) {
00728     rv = NO_POP_FOR_REQUEST;
00729     goto loser;
00730   }
00731   switch (popChoice) {
00732   case crmfSignature:
00733     rv = verifySignature(varTable, currReq, certReq, newCert);
00734     break;
00735   case crmfKeyEncipherment:
00736     rv = doChallengeResponse(varTable, currReq, certReq, newCert,
00737                           challs, numChall);
00738     break;
00739   case crmfRAVerified:
00740   case crmfKeyAgreement:
00741   default:
00742     rv = UNSUPPORTED_POP;
00743     goto loser;
00744   }
00745  loser:
00746   return rv;
00747 }
00748 
00749 void
00750 convertB64ToJS(char *base64)
00751 {
00752   int i;
00753 
00754   for (i=0; base64[i] != '\0'; i++) {
00755     if (base64[i] == '\n') {
00756       printf ("\\n");
00757     }else {
00758       printf ("%c", base64[i]);
00759     }
00760   }
00761 }
00762 
00763 void
00764 formatChallenge(char *chall64, char *certRepContentDER,
00765                ChallengeCreationInfo *challInfo, int numChalls)
00766 {
00767   printf ("function respondToChallenge() {\n"
00768          "  var chalForm = document.chalForm;\n\n"
00769          "  chalForm.CertRepContent.value = '");
00770   convertB64ToJS(certRepContentDER);
00771   printf ("';\n"
00772          "  chalForm.ChallResponse.value = crypto.popChallengeResponse('");
00773   convertB64ToJS(chall64);
00774   printf("');\n"
00775         "  chalForm.submit();\n"
00776         "}\n");
00777 
00778 }
00779 
00780 void
00781 spitOutChallenge(char *chall64, char *certRepContentDER,
00782                ChallengeCreationInfo *challInfo, int numChalls,
00783                char *nickname)
00784 {
00785   int i;
00786 
00787   spitOutHeaders();
00788   printf("<html>\n"
00789         "<head>\n"
00790         "<title>Challenge Page</title>\n"
00791         "<script language=\"JavaScript\">\n"
00792         "<!--\n");
00793   /* The JavaScript function actually gets defined within
00794    * this function call
00795    */
00796   formatChallenge(chall64, certRepContentDER, challInfo, numChalls);
00797   printf("// -->\n"
00798         "</script>\n"
00799         "</head>\n"
00800         "<body onLoad='respondToChallenge()'>\n"
00801         "<h1>Cartman is now responding to the Challenge "
00802         "presented by the CGI</h1>\n"
00803         "<form action='crmfcgi' method='post' name='chalForm'>\n"
00804         "<input type='hidden' name=CertRepContent value=''>\n"
00805         "<input type='hidden' name=ChallResponse value=''>\n");
00806   for (i=0;i<numChalls; i++) {
00807     printf("<input type='hidden' name='chal%d' value='%d'>\n",
00808           i+1, challInfo[i].random);
00809   }
00810   printf("<input type='hidden' name='nickname' value='%s'>\n", nickname);
00811   printf("</form>\n</body>\n</html>");
00812 }
00813 
00814 ErrorCode
00815 issueChallenge(CertResponseInfo *issuedCerts, int numCerts, 
00816               ChallengeCreationInfo *challInfo, int numChalls,
00817               CERTCertificate *issuer, CGIVarTable *varTable)
00818 {
00819   ErrorCode rv = NO_ERROR;
00820   CMMFPOPODecKeyChallContent *chalContent = NULL;
00821   int i;
00822   SECStatus srv;
00823   PRArenaPool *poolp;
00824   CERTGeneralName *genName;
00825   SECItem *challDER = NULL;
00826   char *chall64, *certRepContentDER;
00827 
00828   rv = createCMMFResponse(issuedCerts, numCerts, issuer, 
00829                        &certRepContentDER);
00830   if (rv != NO_ERROR) {
00831     goto loser;
00832   }
00833   chalContent = CMMF_CreatePOPODecKeyChallContent();
00834   if (chalContent == NULL) {
00835     rv = ERROR_CREATING_EMPTY_CHAL_CONTENT;
00836     goto loser;
00837   }
00838   poolp = PORT_NewArena(1024);
00839   if (poolp == NULL) {
00840     rv = OUT_OF_MEMORY;
00841     goto loser;
00842   }
00843   genName = CERT_GetCertificateNames(issuer, poolp);
00844   if (genName == NULL) {
00845     rv = ERROR_EXTRACTING_GEN_NAME_FROM_ISSUER;
00846     goto loser;
00847   }
00848   for (i=0;i<numChalls;i++) {
00849     srv = CMMF_POPODecKeyChallContentSetNextChallenge(chalContent,
00850                                                 challInfo[i].random,
00851                                                 genName,
00852                                                 challInfo[i].pubKey,
00853                                                 varTable);
00854     SECKEY_DestroyPublicKey(challInfo[i].pubKey);
00855     if (srv != SECSuccess) {
00856       rv = ERROR_SETTING_CHALLENGE;
00857       goto loser;
00858     }
00859   }
00860   challDER = SEC_ASN1EncodeItem(NULL, NULL, chalContent, 
00861                             CMMFPOPODecKeyChallContentTemplate);
00862   if (challDER == NULL) {
00863     rv = ERROR_ENCODING_CHALL;
00864     goto loser;
00865   }
00866   chall64 = BTOA_DataToAscii(challDER->data, challDER->len);
00867   SECITEM_FreeItem(challDER, PR_TRUE);
00868   if (chall64 == NULL) {
00869     rv = ERROR_CONVERTING_CHALL_TO_BASE64;
00870     goto loser;
00871   }
00872   spitOutChallenge(chall64, certRepContentDER, challInfo, numChalls,
00873                  getNickname(issuedCerts[0].cert));
00874  loser:
00875   return rv;
00876 }
00877 
00878 
00879 ErrorCode
00880 processRequest(CGIVarTable *varTable)
00881 {
00882   CERTCertDBHandle *certdb;
00883   SECKEYKeyDBHandle *keydb;
00884   CRMFCertReqMessages *certReqs = NULL;
00885   const char *crmfReq;
00886   const char *caNickname;
00887   CERTCertificate *caCert = NULL;
00888   CertResponseInfo *issuedCerts = NULL;
00889   CERTSubjectPublicKeyInfo spki = { 0 };
00890   ErrorCode rv=NO_ERROR;
00891   PRBool doChallengeResponse = PR_FALSE;
00892   SECItem der = { 0 };
00893   SECStatus srv;
00894   CERTCertificateRequest oldCertReq = { 0 };
00895   CRMFCertReqMsg **reqMsgs = NULL,*currReq = NULL;
00896   CRMFCertRequest **reqs = NULL, *certReq = NULL;
00897   CERTName         subject = { 0 };
00898   int numReqs,i;
00899   ChallengeCreationInfo *challInfo=NULL;
00900   int numChalls = 0;
00901 
00902   certdb = CERT_GetDefaultCertDB();
00903   keydb = SECKEY_GetDefaultKeyDB();
00904   crmfReq = CGITableFindValue(varTable, "CRMFRequest");
00905   if (crmfReq == NULL) {
00906     rv = CGI_VAR_MISSING;
00907     missingVar = "CRMFRequest";
00908     goto loser;
00909   }
00910   caNickname = CGITableFindValue(varTable, "CANickname");
00911   if (caNickname == NULL) {
00912     rv = CGI_VAR_MISSING;
00913     missingVar = "CANickname";
00914     goto loser;
00915   }
00916   caCert = CERT_FindCertByNickname(certdb, caNickname);
00917   if (caCert == NULL) {
00918     rv = COULD_NOT_FIND_CA;
00919     goto loser;
00920   }
00921   srv = ATOB_ConvertAsciiToItem(&der, crmfReq);
00922   if (srv != SECSuccess) {
00923     rv = BAD_ASCII_FOR_REQ;
00924     goto loser;
00925   }
00926   certReqs = CRMF_CreateCertReqMessagesFromDER(der.data, der.len);
00927   SECITEM_FreeItem(&der, PR_FALSE);
00928   if (certReqs == NULL) {
00929     rv = COULD_NOT_DECODE_REQS;
00930     goto loser;
00931   }
00932   numReqs = CRMF_CertReqMessagesGetNumMessages(certReqs);
00933   issuedCerts = PORT_ZNewArray(CertResponseInfo, numReqs);
00934   challInfo   = PORT_ZNewArray(ChallengeCreationInfo, numReqs);
00935   if (issuedCerts == NULL || challInfo == NULL) {
00936     rv = OUT_OF_MEMORY;
00937     goto loser;
00938   }
00939   reqMsgs = PORT_ZNewArray(CRMFCertReqMsg*,   numReqs);
00940   reqs    = PORT_ZNewArray(CRMFCertRequest*, numReqs);
00941   if (reqMsgs == NULL || reqs == NULL) {
00942     rv =   OUT_OF_MEMORY;
00943     goto loser;
00944   }
00945   for (i=0; i<numReqs; i++) {
00946     currReq = reqMsgs[i] = 
00947       CRMF_CertReqMessagesGetCertReqMsgAtIndex(certReqs, i);
00948     if (currReq == NULL) {
00949       rv = ERROR_RETRIEVING_REQUEST_MSG;
00950       goto loser;
00951     }
00952     certReq = reqs[i] = CRMF_CertReqMsgGetCertRequest(currReq);
00953     if (certReq == NULL) {
00954       rv = ERROR_RETRIEVING_CERT_REQUEST;
00955       goto loser;
00956     }
00957     srv = CRMF_CertRequestGetCertTemplateSubject(certReq, &subject);
00958     if (srv != SECSuccess) {
00959       rv = ERROR_RETRIEVING_SUBJECT_FROM_REQ;
00960       goto loser;
00961     }
00962     srv = CRMF_CertRequestGetCertTemplatePublicKey(certReq, &spki);
00963     if (srv != SECSuccess) {
00964       rv = ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ;
00965       goto loser;
00966     }
00967     rv = initOldCertReq(&oldCertReq, &subject, &spki);
00968     if (rv != NO_ERROR) {
00969       goto loser;
00970     }
00971     rv = createNewCert(&issuedCerts[i].cert, &oldCertReq, currReq, certReq, 
00972                      caCert, varTable);
00973     if (rv != NO_ERROR) {
00974       goto loser;
00975     }
00976     rv = doProofOfPossession(varTable, currReq, certReq, issuedCerts[i].cert,
00977                           challInfo, &numChalls);
00978     if (rv != NO_ERROR) {
00979       if (rv == DO_CHALLENGE_RESPONSE) {
00980        doChallengeResponse = PR_TRUE;
00981       } else {
00982        goto loser;
00983       }
00984     }
00985     CRMF_CertReqMsgGetID(currReq, &issuedCerts[i].certReqID);
00986     CRMF_DestroyCertReqMsg(currReq);
00987     CRMF_DestroyCertRequest(certReq);
00988   }
00989   if (doChallengeResponse) {
00990     rv = issueChallenge(issuedCerts, numReqs, challInfo, numChalls, caCert,
00991                      varTable);
00992   } else {
00993     rv = issueCerts(issuedCerts, numReqs, caCert);
00994   }
00995  loser:
00996   if (certReqs != NULL) {
00997     CRMF_DestroyCertReqMessages(certReqs);
00998   }
00999   return rv;
01000 }
01001 
01002 ErrorCode
01003 processChallengeResponse(CGIVarTable *varTable, const char *certRepContent)
01004 {
01005   SECItem binDER = { 0 };
01006   SECStatus srv;
01007   ErrorCode rv = NO_ERROR;
01008   const char *clientResponse;
01009   const char *formChalValue;
01010   const char *nickname;
01011   CMMFPOPODecKeyRespContent *respContent = NULL;
01012   int numResponses,i;
01013   long curResponse, expectedResponse;
01014   char cgiChalVar[10];
01015 #ifdef WRITE_OUT_RESPONSE
01016   SECItem certRepBinDER = { 0 };
01017 
01018   ATOB_ConvertAsciiToItem(&certRepBinDER, certRepContent);
01019   writeOutItem("challCertRepContent.der", &certRepBinDER);
01020   PORT_Free(certRepBinDER.data);
01021 #endif  
01022   clientResponse = CGITableFindValue(varTable, "ChallResponse");
01023   if (clientResponse == NULL) {
01024     rv =   REQ_CGI_VAR_NOT_PRESENT;
01025     missingVar = "ChallResponse";
01026     goto loser;
01027   }
01028   srv = ATOB_ConvertAsciiToItem(&binDER, clientResponse);
01029   if (srv != SECSuccess) {
01030     rv = ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN;
01031     goto loser;
01032   }
01033   respContent = CMMF_CreatePOPODecKeyRespContentFromDER(binDER.data,
01034                                                  binDER.len);
01035   SECITEM_FreeItem(&binDER, PR_FALSE);
01036   binDER.data = NULL;
01037   if (respContent == NULL) {
01038     rv = ERROR_CREATING_KEY_RESP_FROM_DER;
01039     goto loser;
01040   }
01041   numResponses = CMMF_POPODecKeyRespContentGetNumResponses(respContent);
01042   for (i=0;i<numResponses;i++){
01043     srv = CMMF_POPODecKeyRespContentGetResponse(respContent,i,&curResponse);
01044     if (srv != SECSuccess) {
01045       rv = ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE;
01046       goto loser;
01047     }
01048     sprintf(cgiChalVar, "chal%d", i+1);
01049     formChalValue = CGITableFindValue(varTable, cgiChalVar);
01050     if (formChalValue == NULL) {
01051       rv = REQ_CGI_VAR_NOT_PRESENT;
01052       missingVar = strdup(cgiChalVar);
01053       goto loser;
01054     }
01055     sscanf(formChalValue, "%ld", &expectedResponse);
01056     if (expectedResponse != curResponse) {
01057       rv = ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED;
01058       goto loser;
01059     }
01060   }
01061   nickname = CGITableFindValue(varTable, "nickname");
01062   if (nickname == NULL) {
01063     rv = REQ_CGI_VAR_NOT_PRESENT;
01064     missingVar = "nickname";
01065     goto loser;
01066   }
01067   spitOutCMMFResponse(nickname, certRepContent);
01068  loser:
01069   if (respContent != NULL) {
01070     CMMF_DestroyPOPODecKeyRespContent(respContent);
01071   }
01072   return rv;
01073 }
01074 
01075 int
01076 main()
01077 {
01078   char *form_output = NULL;
01079   int   form_output_len, form_output_used;
01080   CGIVarTable varTable = { 0 };
01081   ErrorCode errNum = 0;
01082   char *certRepContent;
01083 
01084 #ifdef ATTACH_CGI
01085   /* Put an ifinite loop in here so I can attach to 
01086    * the process after the process is spun off
01087    */
01088   { int stupid = 1;
01089     while (stupid);
01090   }
01091 #endif
01092 
01093   form_output_used = 0;
01094   srand(time(NULL));
01095   while (feof(stdin) == 0) {
01096     if (form_output == NULL) {
01097       form_output = PORT_NewArray(char, DEFAULT_ALLOC_SIZE+1);
01098       form_output_len  = DEFAULT_ALLOC_SIZE;
01099     } else if ((form_output_used + DEFAULT_ALLOC_SIZE) >= form_output_len) {
01100       form_output_len += DEFAULT_ALLOC_SIZE;
01101       form_output = PORT_Realloc(form_output, form_output_len+1);
01102     }
01103     form_output_used += fread(&form_output[form_output_used], sizeof(char), 
01104                            DEFAULT_ALLOC_SIZE, stdin);
01105   }
01106   ParseInputVariables(&varTable, form_output);
01107   certRepContent = CGITableFindValue(&varTable, "CertRepContent");
01108   if (certRepContent == NULL) {
01109     errNum = initNSS(&varTable);
01110     if (errNum != 0) {
01111       goto loser;
01112     }
01113     errNum = processRequest(&varTable);
01114   } else {
01115     errNum = processChallengeResponse(&varTable, certRepContent);
01116   }
01117   if (errNum != NO_ERROR) {
01118     goto loser;
01119   }
01120   goto done;
01121 loser:
01122   dumpErrorMessage(errNum);
01123 done:
01124   free (form_output);
01125   return 0;
01126 }
01127