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) ? &ersand[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 }