security/nss/cmd/crmf-cgi/crmfcgi.c

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     5 #include "seccomon.h"
     6 #include "nss.h"
     7 #include "key.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>
    20 #define DEFAULT_ALLOC_SIZE 200
    21 #define DEFAULT_CGI_VARS   20
    23 typedef struct CGIVariableStr {
    24   char *name;
    25   char *value;
    26 } CGIVariable;
    28 typedef struct CGIVarTableStr {
    29   CGIVariable **variables;
    30   int           numVars;
    31   int           numAlloc; 
    32 } CGIVarTable;
    34 typedef struct CertResponseInfoStr {
    35   CERTCertificate *cert;
    36   long             certReqID;
    37 } CertResponseInfo;
    39 typedef struct ChallengeCreationInfoStr {
    40   long             random;
    41   SECKEYPublicKey *pubKey;
    42 } ChallengeCreationInfo;
    44 char *missingVar = NULL;
    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;
   104 const char *
   105 CGITableFindValue(CGIVarTable *varTable, const char *key);
   107 void
   108 spitOutHeaders(void)
   109 {
   110   printf("Content-type: text/html\n\n");
   111 }
   113 void
   114 dumpRequest(CGIVarTable *varTable)
   115 {
   116   int i;
   117   CGIVariable *var;
   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 }
   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 }
   140 void
   141 processVariable(CGIVariable *var)
   142 {
   143   char *plusSign, *percentSign;
   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;
   155     string[0] = percentSign[1];
   156     string[1] = percentSign[2];
   157     string[2] = '\0';
   159     sscanf(string,"%x", &value);
   160     *percentSign = (char)value;
   161     memmove(&percentSign[1], &percentSign[3], 1+strlen(&percentSign[3]));
   162   }
   163 }
   165 char *
   166 parseNextVariable(CGIVarTable *varTable, char *form_output)
   167 {
   168   char *ampersand, *equal;
   169   CGIVariable *var;
   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 }
   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 }
   212 const char *
   213 CGITableFindValue(CGIVarTable *varTable, const char *key)
   214 {
   215   const char *retVal = NULL;
   216   int i;
   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 }
   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 }
   241 ErrorCode
   242 initNSS(CGIVarTable *varTable)
   243 {
   244   const char *nssDir;
   245   PK11SlotInfo *keySlot;
   246   SECStatus rv;
   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 }
   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>", errNum);
   273   if (errNum  ==   REQ_CGI_VAR_NOT_PRESENT) {
   274     printf ("The missing variable is %s.", missingVar);
   275   }
   276   printf ("<i>More useful information here in the future.</i></body></html>");
   277 }
   279 ErrorCode
   280 initOldCertReq(CERTCertificateRequest *oldCertReq,
   281 	       CERTName *subject, CERTSubjectPublicKeyInfo *spki)
   282 {
   283   PLArenaPool *poolp;
   285   poolp = oldCertReq->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   286   SEC_ASN1EncodeInteger(poolp, &oldCertReq->version, 
   287 			SEC_CERTIFICATE_VERSION_3);
   288   CERT_CopyName(poolp, &oldCertReq->subject, subject);
   289   SECKEY_CopySubjectPublicKeyInfo(poolp, &oldCertReq->subjectPublicKeyInfo,
   290 				  spki);
   291   oldCertReq->attributes = NULL;
   292   return NO_ERROR;
   293 }
   295 ErrorCode
   296 addExtensions(CERTCertificate *newCert, CRMFCertRequest *certReq)
   297 {
   298   int numExtensions, i;
   299   void *extHandle;
   300   ErrorCode rv = NO_ERROR;
   301   CRMFCertExtension *ext;
   302   SECStatus srv;
   304   numExtensions = CRMF_CertRequestGetNumberOfExtensions(certReq);
   305   if (numExtensions == 0) {
   306     /* No extensions to add */
   307     return NO_ERROR;
   308   }
   309   extHandle = CERT_StartCertExtensions(newCert);
   310   if (extHandle == NULL) {
   311     rv = COULD_NOT_START_EXTENSIONS;
   312     goto loser;
   313   }
   314   for (i=0; i<numExtensions; i++) {
   315     ext = CRMF_CertRequestGetExtensionAtIndex(certReq, i);
   316     if (ext == NULL) {
   317       rv = ERROR_RETRIEVING_EXT_FROM_REQ;
   318     }
   319     srv = CERT_AddExtension(extHandle, CRMF_CertExtensionGetOidTag(ext),
   320 			    CRMF_CertExtensionGetValue(ext),
   321 			    CRMF_CertExtensionGetIsCritical(ext), PR_FALSE);
   322     if (srv != SECSuccess) {
   323       rv = ERROR_ADDING_EXT_TO_CERT;
   324     }
   325   }
   326   srv = CERT_FinishExtensions(extHandle);
   327   if (srv != SECSuccess) {
   328     rv = ERROR_ENDING_EXTENSIONS;
   329     goto loser;
   330   }
   331   return NO_ERROR;
   332  loser:
   333   return rv;
   334 }
   336 void
   337 writeOutItem(const char *filePath, SECItem *der)
   338 {
   339   PRFileDesc *outfile;
   341   outfile = PR_Open (filePath,
   342 		     PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
   343 		     0666);
   344   PR_Write(outfile, der->data, der->len);
   345   PR_Close(outfile);
   347 }
   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;
   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   signTag = SEC_GetSignatureAlgorithmOidTag(issuerPrivatekey->keytype, 
   386 					    SEC_OID_UNKNOWN);
   387   if (signTag == SEC_OID_UNKNOWN) {
   388     rv = UNSUPPORTED_SIGN_OPERATION_FOR_ISSUER;
   389     goto loser;
   390   }
   391   srv = SECOID_SetAlgorithmID(newCert->arena, &newCert->signature, 
   392 			      signTag, 0);
   393   if (srv != SECSuccess) {
   394     rv = ERROR_SETTING_SIGN_ALG;
   395     goto loser;
   396   }
   397   srv = CRMF_CertRequestGetCertTemplateVersion(certReq, &version);
   398   if (srv != SECSuccess) {
   399     /* No version included in the request */
   400     *(newCert->version.data) = SEC_CERTIFICATE_VERSION_3;
   401   } else {
   402     SECITEM_FreeItem(&newCert->version, PR_FALSE);
   403     SEC_ASN1EncodeInteger(newCert->arena, &newCert->version, version);
   404   }
   405   SEC_ASN1EncodeItem(newCert->arena, &derCert, newCert, 
   406 		     CERT_CertificateTemplate);
   407   if (derCert.data == NULL) {
   408     rv = ERROR_ENCODING_NEW_CERT;
   409     goto loser;
   410   }
   411   srv = SEC_DerSignData(newCert->arena, &(newCert->derCert), derCert.data,
   412 			derCert.len, issuerPrivKey, signTag);
   413   if (srv != SECSuccess) {
   414     rv = ERROR_SIGNING_NEW_CERT;
   415     goto loser;
   416   }
   417 #ifdef WRITE_OUT_RESPONSE
   418   writeOutItem("newcert.der", &newCert->derCert);
   419 #endif
   420   return NO_ERROR;
   421  loser:
   422   *issuedCert = NULL;
   423   if (newCert) {
   424     CERT_DestroyCertificate(newCert);
   425   }
   426   return rv;
   428 }
   430 void
   431 formatCMMFResponse(char *nickname, char *base64Response)
   432 {
   433   char *currLine, *nextLine;
   435   printf("var retVal = crypto.importUserCertificates(\"%s\",\n", nickname);
   436   currLine = base64Response;
   437   while (1) {
   438     nextLine = strchr(currLine, '\n');
   439     if (nextLine == NULL) {
   440       /* print out the last line here. */
   441       printf ("\"%s\",\n", currLine);
   442       break;
   443     }
   444     nextLine[0] = '\0';
   445     printf("\"%s\\n\"+\n", currLine);
   446     currLine = nextLine+1;
   447   }
   448   printf("true);\n"
   449 	 "if(retVal == '') {\n"
   450 	 "\tdocument.write(\"<h1>New Certificate Successfully Imported.</h1>\");\n"
   451 	 "} else {\n"
   452 	 "\tdocument.write(\"<h2>Unable to import New Certificate</h2>\");\n"
   453 	 "\tdocument.write(\"crypto.importUserCertificates returned <b>\");\n"
   454 	 "\tdocument.write(retVal);\n"
   455 	 "\tdocument.write(\"</b>\");\n"
   456 	 "}\n");
   457 }
   459 void
   460 spitOutCMMFResponse(char *nickname, char *base64Response)
   461 {
   462   spitOutHeaders();
   463   printf("<html>\n<head>\n<title>CMMF Resonse Page</title>\n</head>\n\n"
   464 	 "<body><h1>CMMF Response Page</h1>\n"
   465 	 "<script language=\"JavaScript\">\n"
   466 	 "<!--\n");
   467   formatCMMFResponse(nickname, base64Response);
   468   printf("// -->\n"
   469 	 "</script>\n</body>\n</html>");
   470 }
   472 char*
   473 getNickname(CERTCertificate *cert)
   474 {
   475   char *nickname;
   477   if (cert->nickname != NULL) {
   478     return cert->nickname;
   479   }
   480   nickname = CERT_GetCommonName(&cert->subject);
   481   if (nickname != NULL) {
   482     return nickname;
   483   }
   484   return CERT_NameToAscii(&cert->subject);
   485 }
   487 ErrorCode
   488 createCMMFResponse(CertResponseInfo *issuedCerts, int numCerts, 
   489 		   CERTCertificate *issuerCert, char **base64der)
   490 {
   491  CMMFCertRepContent *certRepContent=NULL;
   492   ErrorCode rv = NO_ERROR;
   493   CMMFCertResponse **responses, *currResponse;
   494   CERTCertList *caList;
   495   int i;
   496   SECStatus srv;
   497   PLArenaPool *poolp;
   498   SECItem *der;
   500   certRepContent = CMMF_CreateCertRepContent();
   501   if (certRepContent == NULL) {
   502     rv = ERROR_CREATING_CERT_REP_CONTENT;
   503     goto loser;
   504   }
   505   responses = PORT_NewArray(CMMFCertResponse*, numCerts);
   506   if (responses == NULL) {
   507     rv = OUT_OF_MEMORY;
   508     goto loser;
   509   }
   510   for (i=0; i<numCerts;i++) {
   511     responses[i] = currResponse = 
   512       CMMF_CreateCertResponse(issuedCerts[i].certReqID);
   513     if (currResponse == NULL) {
   514       rv = ERROR_CREATING_SINGLE_CERT_RESPONSE;
   515       goto loser;
   516     }
   517     CMMF_CertResponseSetPKIStatusInfoStatus(currResponse, cmmfGranted);
   518     CMMF_CertResponseSetCertificate(currResponse, issuedCerts[i].cert);
   519   }
   520   srv = CMMF_CertRepContentSetCertResponses(certRepContent, responses,
   521 					    numCerts);
   522   if (srv != SECSuccess) {
   523     rv = ERROR_SETTING_CERT_RESPONSES;
   524     goto loser;
   525   }
   526   caList = CERT_NewCertList();
   527   if (caList == NULL) {
   528     rv = ERROR_CREATING_CA_LIST;
   529     goto loser;
   530   }
   531   srv = CERT_AddCertToListTail(caList, issuerCert);
   532   if (srv != SECSuccess) {
   533     rv = ERROR_ADDING_ISSUER_TO_CA_LIST;
   534     goto loser;
   535   }
   536   srv = CMMF_CertRepContentSetCAPubs(certRepContent, caList);
   537   CERT_DestroyCertList(caList);
   538   poolp = PORT_NewArena(1024);
   539   der = SEC_ASN1EncodeItem(poolp, NULL, certRepContent, 
   540 			   CMMFCertRepContentTemplate);
   541   if (der == NULL) {
   542     rv = ERROR_ENCODING_CERT_REP_CONTENT;
   543     goto loser;
   544   }
   545 #ifdef WRITE_OUT_RESPONSE
   546   writeOutItem("CertRepContent.der", der);
   547 #endif
   548   *base64der = BTOA_DataToAscii(der->data, der->len);
   549   return NO_ERROR;
   550  loser:
   551   return rv;
   552 }
   554 ErrorCode
   555 issueCerts(CertResponseInfo *issuedCerts, int numCerts, 
   556 	   CERTCertificate *issuerCert)
   557 {
   558   ErrorCode rv;
   559   char *base64Response;
   561   rv = createCMMFResponse(issuedCerts, numCerts, issuerCert, &base64Response);
   562   if (rv != NO_ERROR) {
   563     goto loser;
   564   }
   565   spitOutCMMFResponse(getNickname(issuedCerts[0].cert),base64Response);
   566   return NO_ERROR;
   567  loser:
   568   return rv;
   569 }
   571 ErrorCode
   572 verifySignature(CGIVarTable *varTable, CRMFCertReqMsg *currReq, 
   573 		CRMFCertRequest *certReq, CERTCertificate *newCert)
   574 {
   575   SECStatus srv;
   576   ErrorCode rv = NO_ERROR;
   577   CRMFPOPOSigningKey *signKey   = NULL;
   578   SECAlgorithmID     *algID     = NULL;
   579   SECItem            *signature = NULL;
   580   SECKEYPublicKey    *pubKey    = NULL;
   581   SECItem            *reqDER    = NULL;
   583   srv = CRMF_CertReqMsgGetPOPOSigningKey(currReq, &signKey);
   584   if (srv != SECSuccess || signKey == NULL) {
   585     rv = ERROR_RETRIEVING_POP_SIGN_KEY;
   586     goto loser;
   587   }
   588   algID = CRMF_POPOSigningKeyGetAlgID(signKey);
   589   if (algID == NULL) {
   590     rv = ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY;
   591     goto loser;
   592   }
   593   signature = CRMF_POPOSigningKeyGetSignature(signKey);
   594   if (signature == NULL) {
   595     rv = ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY;
   596     goto loser;
   597   }
   598   /* Make the length the number of bytes instead of bits */
   599   signature->len = (signature->len+7)/8;
   600   pubKey = CERT_ExtractPublicKey(newCert);
   601   if (pubKey == NULL) {
   602     rv = ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT;
   603     goto loser;
   604   }
   605   reqDER = SEC_ASN1EncodeItem(NULL, NULL, certReq, CRMFCertRequestTemplate);
   606   if (reqDER == NULL) {
   607     rv = ERROR_ENCODING_CERT_REQ_FOR_POP;
   608     goto loser;
   609   }
   610   srv = VFY_VerifyDataWithAlgorithmID(reqDER->data, reqDER->len, pubKey, 
   611 			signature, &algID->algorithm, NULL, varTable);
   612   if (srv != SECSuccess) {
   613     rv = ERROR_VERIFYING_SIGNATURE_POP;
   614     goto loser;
   615   }
   616   /* Fall thru in successfull case. */
   617  loser:
   618   if (pubKey != NULL) {
   619     SECKEY_DestroyPublicKey(pubKey);
   620   }
   621   if (reqDER != NULL) {
   622     SECITEM_FreeItem(reqDER, PR_TRUE);
   623   }
   624   if (signature != NULL) {
   625     SECITEM_FreeItem(signature, PR_TRUE);
   626   }
   627   if (algID != NULL) {
   628     SECOID_DestroyAlgorithmID(algID, PR_TRUE);
   629   }
   630   if (signKey != NULL) {
   631     CRMF_DestroyPOPOSigningKey(signKey);
   632   }  
   633   return rv;
   634 }
   636 ErrorCode
   637 doChallengeResponse(CGIVarTable *varTable, CRMFCertReqMsg *currReq, 
   638 		    CRMFCertRequest *certReq, CERTCertificate *newCert,
   639 		    ChallengeCreationInfo *challs, int *numChall)
   640 {
   641   CRMFPOPOPrivKey *privKey = NULL;
   642   CRMFPOPOPrivKeyChoice privKeyChoice;
   643   SECStatus srv;
   644   ErrorCode rv = NO_ERROR;
   646   srv = CRMF_CertReqMsgGetPOPKeyEncipherment(currReq, &privKey);
   647   if (srv != SECSuccess || privKey == NULL) {
   648     rv = ERROR_GETTING_KEY_ENCIPHERMENT;
   649     goto loser;
   650   } 
   651   privKeyChoice = CRMF_POPOPrivKeyGetChoice(privKey);
   652   CRMF_DestroyPOPOPrivKey(privKey);
   653   switch (privKeyChoice) {
   654   case crmfSubsequentMessage:
   655     challs = &challs[*numChall];
   656     challs->random = rand();
   657     challs->pubKey = CERT_ExtractPublicKey(newCert);
   658     if (challs->pubKey == NULL) {
   659       rv = ERROR_RETRIEVING_PUB_KEY_FOR_CHALL;
   660       goto loser;
   661     }
   662     (*numChall)++;
   663     rv = DO_CHALLENGE_RESPONSE;
   664     break;
   665   case crmfThisMessage:
   666     /* There'd better be a PKIArchiveControl in this message */
   667     if (!CRMF_CertRequestIsControlPresent(certReq, 
   668 					  crmfPKIArchiveOptionsControl)) {
   669       rv = ERROR_NO_POP_FOR_PRIVKEY;
   670       goto loser;
   671     }
   672     break;
   673   default:
   674     rv = ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE;
   675     goto loser;
   676   }
   677 loser:
   678   return rv;
   679 }
   681 ErrorCode
   682 doProofOfPossession(CGIVarTable *varTable, CRMFCertReqMsg *currReq, 
   683 		    CRMFCertRequest *certReq, CERTCertificate *newCert,
   684 		    ChallengeCreationInfo *challs, int *numChall)
   685 {
   686   CRMFPOPChoice popChoice;
   687   ErrorCode rv = NO_ERROR;
   689   popChoice = CRMF_CertReqMsgGetPOPType(currReq);
   690   if (popChoice == crmfNoPOPChoice) {
   691     rv = NO_POP_FOR_REQUEST;
   692     goto loser;
   693   }
   694   switch (popChoice) {
   695   case crmfSignature:
   696     rv = verifySignature(varTable, currReq, certReq, newCert);
   697     break;
   698   case crmfKeyEncipherment:
   699     rv = doChallengeResponse(varTable, currReq, certReq, newCert,
   700 			     challs, numChall);
   701     break;
   702   case crmfRAVerified:
   703   case crmfKeyAgreement:
   704   default:
   705     rv = UNSUPPORTED_POP;
   706     goto loser;
   707   }
   708  loser:
   709   return rv;
   710 }
   712 void
   713 convertB64ToJS(char *base64)
   714 {
   715   int i;
   717   for (i=0; base64[i] != '\0'; i++) {
   718     if (base64[i] == '\n') {
   719       printf ("\\n");
   720     }else {
   721       printf ("%c", base64[i]);
   722     }
   723   }
   724 }
   726 void
   727 formatChallenge(char *chall64, char *certRepContentDER,
   728 		 ChallengeCreationInfo *challInfo, int numChalls)
   729 {
   730   printf ("function respondToChallenge() {\n"
   731 	  "  var chalForm = document.chalForm;\n\n"
   732 	  "  chalForm.CertRepContent.value = '");
   733   convertB64ToJS(certRepContentDER);
   734   printf ("';\n"
   735 	  "  chalForm.ChallResponse.value = crypto.popChallengeResponse('");
   736   convertB64ToJS(chall64);
   737   printf("');\n"
   738 	 "  chalForm.submit();\n"
   739 	 "}\n");
   741 }
   743 void
   744 spitOutChallenge(char *chall64, char *certRepContentDER,
   745 		 ChallengeCreationInfo *challInfo, int numChalls,
   746 		 char *nickname)
   747 {
   748   int i;
   750   spitOutHeaders();
   751   printf("<html>\n"
   752 	 "<head>\n"
   753 	 "<title>Challenge Page</title>\n"
   754 	 "<script language=\"JavaScript\">\n"
   755 	 "<!--\n");
   756   /* The JavaScript function actually gets defined within
   757    * this function call
   758    */
   759   formatChallenge(chall64, certRepContentDER, challInfo, numChalls);
   760   printf("// -->\n"
   761 	 "</script>\n"
   762 	 "</head>\n"
   763 	 "<body onLoad='respondToChallenge()'>\n"
   764 	 "<h1>Cartman is now responding to the Challenge "
   765 	 "presented by the CGI</h1>\n"
   766 	 "<form action='crmfcgi' method='post' name='chalForm'>\n"
   767 	 "<input type='hidden' name=CertRepContent value=''>\n"
   768 	 "<input type='hidden' name=ChallResponse value=''>\n");
   769   for (i=0;i<numChalls; i++) {
   770     printf("<input type='hidden' name='chal%d' value='%d'>\n",
   771 	   i+1, challInfo[i].random);
   772   }
   773   printf("<input type='hidden' name='nickname' value='%s'>\n", nickname);
   774   printf("</form>\n</body>\n</html>");
   775 }
   777 ErrorCode
   778 issueChallenge(CertResponseInfo *issuedCerts, int numCerts, 
   779 	       ChallengeCreationInfo *challInfo, int numChalls,
   780 	       CERTCertificate *issuer, CGIVarTable *varTable)
   781 {
   782   ErrorCode rv = NO_ERROR;
   783   CMMFPOPODecKeyChallContent *chalContent = NULL;
   784   int i;
   785   SECStatus srv;
   786   PLArenaPool *poolp;
   787   CERTGeneralName *genName;
   788   SECItem *challDER = NULL;
   789   char *chall64, *certRepContentDER;
   791   rv = createCMMFResponse(issuedCerts, numCerts, issuer, 
   792 			  &certRepContentDER);
   793   if (rv != NO_ERROR) {
   794     goto loser;
   795   }
   796   chalContent = CMMF_CreatePOPODecKeyChallContent();
   797   if (chalContent == NULL) {
   798     rv = ERROR_CREATING_EMPTY_CHAL_CONTENT;
   799     goto loser;
   800   }
   801   poolp = PORT_NewArena(1024);
   802   if (poolp == NULL) {
   803     rv = OUT_OF_MEMORY;
   804     goto loser;
   805   }
   806   genName = CERT_GetCertificateNames(issuer, poolp);
   807   if (genName == NULL) {
   808     rv = ERROR_EXTRACTING_GEN_NAME_FROM_ISSUER;
   809     goto loser;
   810   }
   811   for (i=0;i<numChalls;i++) {
   812     srv = CMMF_POPODecKeyChallContentSetNextChallenge(chalContent,
   813 						      challInfo[i].random,
   814 						      genName,
   815 						      challInfo[i].pubKey,
   816 						      varTable);
   817     SECKEY_DestroyPublicKey(challInfo[i].pubKey);
   818     if (srv != SECSuccess) {
   819       rv = ERROR_SETTING_CHALLENGE;
   820       goto loser;
   821     }
   822   }
   823   challDER = SEC_ASN1EncodeItem(NULL, NULL, chalContent, 
   824 				CMMFPOPODecKeyChallContentTemplate);
   825   if (challDER == NULL) {
   826     rv = ERROR_ENCODING_CHALL;
   827     goto loser;
   828   }
   829   chall64 = BTOA_DataToAscii(challDER->data, challDER->len);
   830   SECITEM_FreeItem(challDER, PR_TRUE);
   831   if (chall64 == NULL) {
   832     rv = ERROR_CONVERTING_CHALL_TO_BASE64;
   833     goto loser;
   834   }
   835   spitOutChallenge(chall64, certRepContentDER, challInfo, numChalls,
   836 		   getNickname(issuedCerts[0].cert));
   837  loser:
   838   return rv;
   839 }
   842 ErrorCode
   843 processRequest(CGIVarTable *varTable)
   844 {
   845   CERTCertDBHandle *certdb;
   846   SECKEYKeyDBHandle *keydb;
   847   CRMFCertReqMessages *certReqs = NULL;
   848   const char *crmfReq;
   849   const char *caNickname;
   850   CERTCertificate *caCert = NULL;
   851   CertResponseInfo *issuedCerts = NULL;
   852   CERTSubjectPublicKeyInfo spki = { 0 };
   853   ErrorCode rv=NO_ERROR;
   854   PRBool doChallengeResponse = PR_FALSE;
   855   SECItem der = { 0 };
   856   SECStatus srv;
   857   CERTCertificateRequest oldCertReq = { 0 };
   858   CRMFCertReqMsg **reqMsgs = NULL,*currReq = NULL;
   859   CRMFCertRequest **reqs = NULL, *certReq = NULL;
   860   CERTName         subject = { 0 };
   861   int numReqs,i;
   862   ChallengeCreationInfo *challInfo=NULL;
   863   int numChalls = 0;
   865   certdb = CERT_GetDefaultCertDB();
   866   keydb = SECKEY_GetDefaultKeyDB();
   867   crmfReq = CGITableFindValue(varTable, "CRMFRequest");
   868   if (crmfReq == NULL) {
   869     rv = CGI_VAR_MISSING;
   870     missingVar = "CRMFRequest";
   871     goto loser;
   872   }
   873   caNickname = CGITableFindValue(varTable, "CANickname");
   874   if (caNickname == NULL) {
   875     rv = CGI_VAR_MISSING;
   876     missingVar = "CANickname";
   877     goto loser;
   878   }
   879   caCert = CERT_FindCertByNickname(certdb, caNickname);
   880   if (caCert == NULL) {
   881     rv = COULD_NOT_FIND_CA;
   882     goto loser;
   883   }
   884   srv = ATOB_ConvertAsciiToItem(&der, crmfReq);
   885   if (srv != SECSuccess) {
   886     rv = BAD_ASCII_FOR_REQ;
   887     goto loser;
   888   }
   889   certReqs = CRMF_CreateCertReqMessagesFromDER(der.data, der.len);
   890   SECITEM_FreeItem(&der, PR_FALSE);
   891   if (certReqs == NULL) {
   892     rv = COULD_NOT_DECODE_REQS;
   893     goto loser;
   894   }
   895   numReqs = CRMF_CertReqMessagesGetNumMessages(certReqs);
   896   issuedCerts = PORT_ZNewArray(CertResponseInfo, numReqs);
   897   challInfo   = PORT_ZNewArray(ChallengeCreationInfo, numReqs);
   898   if (issuedCerts == NULL || challInfo == NULL) {
   899     rv = OUT_OF_MEMORY;
   900     goto loser;
   901   }
   902   reqMsgs = PORT_ZNewArray(CRMFCertReqMsg*,   numReqs);
   903   reqs    = PORT_ZNewArray(CRMFCertRequest*, numReqs);
   904   if (reqMsgs == NULL || reqs == NULL) {
   905     rv =   OUT_OF_MEMORY;
   906     goto loser;
   907   }
   908   for (i=0; i<numReqs; i++) {
   909     currReq = reqMsgs[i] = 
   910       CRMF_CertReqMessagesGetCertReqMsgAtIndex(certReqs, i);
   911     if (currReq == NULL) {
   912       rv = ERROR_RETRIEVING_REQUEST_MSG;
   913       goto loser;
   914     }
   915     certReq = reqs[i] = CRMF_CertReqMsgGetCertRequest(currReq);
   916     if (certReq == NULL) {
   917       rv = ERROR_RETRIEVING_CERT_REQUEST;
   918       goto loser;
   919     }
   920     srv = CRMF_CertRequestGetCertTemplateSubject(certReq, &subject);
   921     if (srv != SECSuccess) {
   922       rv = ERROR_RETRIEVING_SUBJECT_FROM_REQ;
   923       goto loser;
   924     }
   925     srv = CRMF_CertRequestGetCertTemplatePublicKey(certReq, &spki);
   926     if (srv != SECSuccess) {
   927       rv = ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ;
   928       goto loser;
   929     }
   930     rv = initOldCertReq(&oldCertReq, &subject, &spki);
   931     if (rv != NO_ERROR) {
   932       goto loser;
   933     }
   934     rv = createNewCert(&issuedCerts[i].cert, &oldCertReq, currReq, certReq, 
   935 		       caCert, varTable);
   936     if (rv != NO_ERROR) {
   937       goto loser;
   938     }
   939     rv = doProofOfPossession(varTable, currReq, certReq, issuedCerts[i].cert,
   940 			     challInfo, &numChalls);
   941     if (rv != NO_ERROR) {
   942       if (rv == DO_CHALLENGE_RESPONSE) {
   943 	doChallengeResponse = PR_TRUE;
   944       } else {
   945 	goto loser;
   946       }
   947     }
   948     CRMF_CertReqMsgGetID(currReq, &issuedCerts[i].certReqID);
   949     CRMF_DestroyCertReqMsg(currReq);
   950     CRMF_DestroyCertRequest(certReq);
   951   }
   952   if (doChallengeResponse) {
   953     rv = issueChallenge(issuedCerts, numReqs, challInfo, numChalls, caCert,
   954 			varTable);
   955   } else {
   956     rv = issueCerts(issuedCerts, numReqs, caCert);
   957   }
   958  loser:
   959   if (certReqs != NULL) {
   960     CRMF_DestroyCertReqMessages(certReqs);
   961   }
   962   return rv;
   963 }
   965 ErrorCode
   966 processChallengeResponse(CGIVarTable *varTable, const char *certRepContent)
   967 {
   968   SECItem binDER = { 0 };
   969   SECStatus srv;
   970   ErrorCode rv = NO_ERROR;
   971   const char *clientResponse;
   972   const char *formChalValue;
   973   const char *nickname;
   974   CMMFPOPODecKeyRespContent *respContent = NULL;
   975   int numResponses,i;
   976   long curResponse, expectedResponse;
   977   char cgiChalVar[10];
   978 #ifdef WRITE_OUT_RESPONSE
   979   SECItem certRepBinDER = { 0 };
   981   ATOB_ConvertAsciiToItem(&certRepBinDER, certRepContent);
   982   writeOutItem("challCertRepContent.der", &certRepBinDER);
   983   PORT_Free(certRepBinDER.data);
   984 #endif  
   985   clientResponse = CGITableFindValue(varTable, "ChallResponse");
   986   if (clientResponse == NULL) {
   987     rv =   REQ_CGI_VAR_NOT_PRESENT;
   988     missingVar = "ChallResponse";
   989     goto loser;
   990   }
   991   srv = ATOB_ConvertAsciiToItem(&binDER, clientResponse);
   992   if (srv != SECSuccess) {
   993     rv = ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN;
   994     goto loser;
   995   }
   996   respContent = CMMF_CreatePOPODecKeyRespContentFromDER(binDER.data,
   997 							binDER.len);
   998   SECITEM_FreeItem(&binDER, PR_FALSE);
   999   binDER.data = NULL;
  1000   if (respContent == NULL) {
  1001     rv = ERROR_CREATING_KEY_RESP_FROM_DER;
  1002     goto loser;
  1004   numResponses = CMMF_POPODecKeyRespContentGetNumResponses(respContent);
  1005   for (i=0;i<numResponses;i++){
  1006     srv = CMMF_POPODecKeyRespContentGetResponse(respContent,i,&curResponse);
  1007     if (srv != SECSuccess) {
  1008       rv = ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE;
  1009       goto loser;
  1011     sprintf(cgiChalVar, "chal%d", i+1);
  1012     formChalValue = CGITableFindValue(varTable, cgiChalVar);
  1013     if (formChalValue == NULL) {
  1014       rv = REQ_CGI_VAR_NOT_PRESENT;
  1015       missingVar = strdup(cgiChalVar);
  1016       goto loser;
  1018     sscanf(formChalValue, "%ld", &expectedResponse);
  1019     if (expectedResponse != curResponse) {
  1020       rv = ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED;
  1021       goto loser;
  1024   nickname = CGITableFindValue(varTable, "nickname");
  1025   if (nickname == NULL) {
  1026     rv = REQ_CGI_VAR_NOT_PRESENT;
  1027     missingVar = "nickname";
  1028     goto loser;
  1030   spitOutCMMFResponse(nickname, certRepContent);
  1031  loser:
  1032   if (respContent != NULL) {
  1033     CMMF_DestroyPOPODecKeyRespContent(respContent);
  1035   return rv;
  1038 int
  1039 main()
  1041   char *form_output = NULL;
  1042   int   form_output_len, form_output_used;
  1043   CGIVarTable varTable = { 0 };
  1044   ErrorCode errNum = 0;
  1045   char *certRepContent;
  1047 #ifdef ATTACH_CGI
  1048   /* Put an ifinite loop in here so I can attach to 
  1049    * the process after the process is spun off
  1050    */
  1051   { int stupid = 1;
  1052     while (stupid);
  1054 #endif
  1056   form_output_used = 0;
  1057   srand(time(NULL));
  1058   while (feof(stdin) == 0) {
  1059     if (form_output == NULL) {
  1060       form_output = PORT_NewArray(char, DEFAULT_ALLOC_SIZE+1);
  1061       form_output_len  = DEFAULT_ALLOC_SIZE;
  1062     } else if ((form_output_used + DEFAULT_ALLOC_SIZE) >= form_output_len) {
  1063       form_output_len += DEFAULT_ALLOC_SIZE;
  1064       form_output = PORT_Realloc(form_output, form_output_len+1);
  1066     form_output_used += fread(&form_output[form_output_used], sizeof(char), 
  1067 			      DEFAULT_ALLOC_SIZE, stdin);
  1069   ParseInputVariables(&varTable, form_output);
  1070   certRepContent = CGITableFindValue(&varTable, "CertRepContent");
  1071   if (certRepContent == NULL) {
  1072     errNum = initNSS(&varTable);
  1073     if (errNum != 0) {
  1074       goto loser;
  1076     errNum = processRequest(&varTable);
  1077   } else {
  1078     errNum = processChallengeResponse(&varTable, certRepContent);
  1080   if (errNum != NO_ERROR) {
  1081     goto loser;
  1083   goto done;
  1084 loser:
  1085   dumpErrorMessage(errNum);
  1086 done:
  1087   free (form_output);
  1088   return 0;

mercurial