Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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) ? &ersand[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;
1003 }
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;
1010 }
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;
1017 }
1018 sscanf(formChalValue, "%ld", &expectedResponse);
1019 if (expectedResponse != curResponse) {
1020 rv = ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED;
1021 goto loser;
1022 }
1023 }
1024 nickname = CGITableFindValue(varTable, "nickname");
1025 if (nickname == NULL) {
1026 rv = REQ_CGI_VAR_NOT_PRESENT;
1027 missingVar = "nickname";
1028 goto loser;
1029 }
1030 spitOutCMMFResponse(nickname, certRepContent);
1031 loser:
1032 if (respContent != NULL) {
1033 CMMF_DestroyPOPODecKeyRespContent(respContent);
1034 }
1035 return rv;
1036 }
1038 int
1039 main()
1040 {
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);
1053 }
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);
1065 }
1066 form_output_used += fread(&form_output[form_output_used], sizeof(char),
1067 DEFAULT_ALLOC_SIZE, stdin);
1068 }
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;
1075 }
1076 errNum = processRequest(&varTable);
1077 } else {
1078 errNum = processChallengeResponse(&varTable, certRepContent);
1079 }
1080 if (errNum != NO_ERROR) {
1081 goto loser;
1082 }
1083 goto done;
1084 loser:
1085 dumpErrorMessage(errNum);
1086 done:
1087 free (form_output);
1088 return 0;
1089 }