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
spitOutHeaders(void)108 spitOutHeaders(void)
109 {
110 printf("Content-type: text/html\n\n");
111 }
112
113 void
dumpRequest(CGIVarTable * varTable)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
echo_request(CGIVarTable * varTable)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
processVariable(CGIVariable * var)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 *
parseNextVariable(CGIVarTable * varTable,char * form_output)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
ParseInputVariables(CGIVarTable * varTable,char * form_output)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 *
CGITableFindValue(CGIVarTable * varTable,const char * key)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 *
passwordCallback(PK11SlotInfo * slot,PRBool retry,void * arg)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
initNSS(CGIVarTable * varTable)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
dumpErrorMessage(ErrorCode errNum)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
initOldCertReq(CERTCertificateRequest * oldCertReq,CERTName * subject,CERTSubjectPublicKeyInfo * spki)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
addExtensions(CERTCertificate * newCert,CRMFCertRequest * certReq)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
writeOutItem(const char * filePath,SECItem * der)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
createNewCert(CERTCertificate ** issuedCert,CERTCertificateRequest * oldCertReq,CRMFCertReqMsg * currReq,CRMFCertRequest * certReq,CERTCertificate * issuerCert,CGIVarTable * varTable)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 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;
427 }
428
429 void
formatCMMFResponse(char * nickname,char * base64Response)430 formatCMMFResponse(char *nickname, char *base64Response)
431 {
432 char *currLine, *nextLine;
433
434 printf("var retVal = crypto.importUserCertificates(\"%s\",\n", nickname);
435 currLine = base64Response;
436 while (1) {
437 nextLine = strchr(currLine, '\n');
438 if (nextLine == NULL) {
439 /* print out the last line here. */
440 printf("\"%s\",\n", currLine);
441 break;
442 }
443 nextLine[0] = '\0';
444 printf("\"%s\\n\"+\n", currLine);
445 currLine = nextLine + 1;
446 }
447 printf("true);\n"
448 "if(retVal == '') {\n"
449 "\tdocument.write(\"<h1>New Certificate Successfully Imported.</h1>\");\n"
450 "} else {\n"
451 "\tdocument.write(\"<h2>Unable to import New Certificate</h2>\");\n"
452 "\tdocument.write(\"crypto.importUserCertificates returned <b>\");\n"
453 "\tdocument.write(retVal);\n"
454 "\tdocument.write(\"</b>\");\n"
455 "}\n");
456 }
457
458 void
spitOutCMMFResponse(char * nickname,char * base64Response)459 spitOutCMMFResponse(char *nickname, char *base64Response)
460 {
461 spitOutHeaders();
462 printf("<html>\n<head>\n<title>CMMF Resonse Page</title>\n</head>\n\n"
463 "<body><h1>CMMF Response Page</h1>\n"
464 "<script language=\"JavaScript\">\n"
465 "<!--\n");
466 formatCMMFResponse(nickname, base64Response);
467 printf("// -->\n"
468 "</script>\n</body>\n</html>");
469 }
470
471 char *
getNickname(CERTCertificate * cert)472 getNickname(CERTCertificate *cert)
473 {
474 char *nickname;
475
476 if (cert->nickname != NULL) {
477 return cert->nickname;
478 }
479 nickname = CERT_GetCommonName(&cert->subject);
480 if (nickname != NULL) {
481 return nickname;
482 }
483 return CERT_NameToAscii(&cert->subject);
484 }
485
486 ErrorCode
createCMMFResponse(CertResponseInfo * issuedCerts,int numCerts,CERTCertificate * issuerCert,char ** base64der)487 createCMMFResponse(CertResponseInfo *issuedCerts, int numCerts,
488 CERTCertificate *issuerCert, char **base64der)
489 {
490 CMMFCertRepContent *certRepContent = NULL;
491 ErrorCode rv = NO_ERROR;
492 CMMFCertResponse **responses, *currResponse;
493 CERTCertList *caList;
494 int i;
495 SECStatus srv;
496 PLArenaPool *poolp;
497 SECItem *der;
498
499 certRepContent = CMMF_CreateCertRepContent();
500 if (certRepContent == NULL) {
501 rv = ERROR_CREATING_CERT_REP_CONTENT;
502 goto loser;
503 }
504 responses = PORT_NewArray(CMMFCertResponse *, numCerts);
505 if (responses == NULL) {
506 rv = OUT_OF_MEMORY;
507 goto loser;
508 }
509 for (i = 0; i < numCerts; i++) {
510 responses[i] = currResponse =
511 CMMF_CreateCertResponse(issuedCerts[i].certReqID);
512 if (currResponse == NULL) {
513 rv = ERROR_CREATING_SINGLE_CERT_RESPONSE;
514 goto loser;
515 }
516 CMMF_CertResponseSetPKIStatusInfoStatus(currResponse, cmmfGranted);
517 CMMF_CertResponseSetCertificate(currResponse, issuedCerts[i].cert);
518 }
519 srv = CMMF_CertRepContentSetCertResponses(certRepContent, responses,
520 numCerts);
521 if (srv != SECSuccess) {
522 rv = ERROR_SETTING_CERT_RESPONSES;
523 goto loser;
524 }
525 caList = CERT_NewCertList();
526 if (caList == NULL) {
527 rv = ERROR_CREATING_CA_LIST;
528 goto loser;
529 }
530 srv = CERT_AddCertToListTail(caList, issuerCert);
531 if (srv != SECSuccess) {
532 rv = ERROR_ADDING_ISSUER_TO_CA_LIST;
533 goto loser;
534 }
535 srv = CMMF_CertRepContentSetCAPubs(certRepContent, caList);
536 CERT_DestroyCertList(caList);
537 poolp = PORT_NewArena(1024);
538 der = SEC_ASN1EncodeItem(poolp, NULL, certRepContent,
539 CMMFCertRepContentTemplate);
540 if (der == NULL) {
541 rv = ERROR_ENCODING_CERT_REP_CONTENT;
542 goto loser;
543 }
544 #ifdef WRITE_OUT_RESPONSE
545 writeOutItem("CertRepContent.der", der);
546 #endif
547 *base64der = BTOA_DataToAscii(der->data, der->len);
548 return NO_ERROR;
549 loser:
550 return rv;
551 }
552
553 ErrorCode
issueCerts(CertResponseInfo * issuedCerts,int numCerts,CERTCertificate * issuerCert)554 issueCerts(CertResponseInfo *issuedCerts, int numCerts,
555 CERTCertificate *issuerCert)
556 {
557 ErrorCode rv;
558 char *base64Response;
559
560 rv = createCMMFResponse(issuedCerts, numCerts, issuerCert, &base64Response);
561 if (rv != NO_ERROR) {
562 goto loser;
563 }
564 spitOutCMMFResponse(getNickname(issuedCerts[0].cert), base64Response);
565 return NO_ERROR;
566 loser:
567 return rv;
568 }
569
570 ErrorCode
verifySignature(CGIVarTable * varTable,CRMFCertReqMsg * currReq,CRMFCertRequest * certReq,CERTCertificate * newCert)571 verifySignature(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
572 CRMFCertRequest *certReq, CERTCertificate *newCert)
573 {
574 SECStatus srv;
575 ErrorCode rv = NO_ERROR;
576 CRMFPOPOSigningKey *signKey = NULL;
577 SECAlgorithmID *algID = NULL;
578 SECItem *signature = NULL;
579 SECKEYPublicKey *pubKey = NULL;
580 SECItem *reqDER = NULL;
581
582 srv = CRMF_CertReqMsgGetPOPOSigningKey(currReq, &signKey);
583 if (srv != SECSuccess || signKey == NULL) {
584 rv = ERROR_RETRIEVING_POP_SIGN_KEY;
585 goto loser;
586 }
587 algID = CRMF_POPOSigningKeyGetAlgID(signKey);
588 if (algID == NULL) {
589 rv = ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY;
590 goto loser;
591 }
592 signature = CRMF_POPOSigningKeyGetSignature(signKey);
593 if (signature == NULL) {
594 rv = ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY;
595 goto loser;
596 }
597 /* Make the length the number of bytes instead of bits */
598 signature->len = (signature->len + 7) / 8;
599 pubKey = CERT_ExtractPublicKey(newCert);
600 if (pubKey == NULL) {
601 rv = ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT;
602 goto loser;
603 }
604 reqDER = SEC_ASN1EncodeItem(NULL, NULL, certReq, CRMFCertRequestTemplate);
605 if (reqDER == NULL) {
606 rv = ERROR_ENCODING_CERT_REQ_FOR_POP;
607 goto loser;
608 }
609 srv = VFY_VerifyDataWithAlgorithmID(reqDER->data, reqDER->len, pubKey,
610 signature, &algID->algorithm, NULL, varTable);
611 if (srv != SECSuccess) {
612 rv = ERROR_VERIFYING_SIGNATURE_POP;
613 goto loser;
614 }
615 /* Fall thru in successfull case. */
616 loser:
617 if (pubKey != NULL) {
618 SECKEY_DestroyPublicKey(pubKey);
619 }
620 if (reqDER != NULL) {
621 SECITEM_FreeItem(reqDER, PR_TRUE);
622 }
623 if (signature != NULL) {
624 SECITEM_FreeItem(signature, PR_TRUE);
625 }
626 if (algID != NULL) {
627 SECOID_DestroyAlgorithmID(algID, PR_TRUE);
628 }
629 if (signKey != NULL) {
630 CRMF_DestroyPOPOSigningKey(signKey);
631 }
632 return rv;
633 }
634
635 ErrorCode
doChallengeResponse(CGIVarTable * varTable,CRMFCertReqMsg * currReq,CRMFCertRequest * certReq,CERTCertificate * newCert,ChallengeCreationInfo * challs,int * numChall)636 doChallengeResponse(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
637 CRMFCertRequest *certReq, CERTCertificate *newCert,
638 ChallengeCreationInfo *challs, int *numChall)
639 {
640 CRMFPOPOPrivKey *privKey = NULL;
641 CRMFPOPOPrivKeyChoice privKeyChoice;
642 SECStatus srv;
643 ErrorCode rv = NO_ERROR;
644
645 srv = CRMF_CertReqMsgGetPOPKeyEncipherment(currReq, &privKey);
646 if (srv != SECSuccess || privKey == NULL) {
647 rv = ERROR_GETTING_KEY_ENCIPHERMENT;
648 goto loser;
649 }
650 privKeyChoice = CRMF_POPOPrivKeyGetChoice(privKey);
651 CRMF_DestroyPOPOPrivKey(privKey);
652 switch (privKeyChoice) {
653 case crmfSubsequentMessage:
654 challs = &challs[*numChall];
655 challs->random = rand();
656 challs->pubKey = CERT_ExtractPublicKey(newCert);
657 if (challs->pubKey == NULL) {
658 rv =
659 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 =
670 ERROR_NO_POP_FOR_PRIVKEY;
671 goto loser;
672 }
673 break;
674 default:
675 rv = ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE;
676 goto loser;
677 }
678 loser:
679 return rv;
680 }
681
682 ErrorCode
doProofOfPossession(CGIVarTable * varTable,CRMFCertReqMsg * currReq,CRMFCertRequest * certReq,CERTCertificate * newCert,ChallengeCreationInfo * challs,int * numChall)683 doProofOfPossession(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
684 CRMFCertRequest *certReq, CERTCertificate *newCert,
685 ChallengeCreationInfo *challs, int *numChall)
686 {
687 CRMFPOPChoice popChoice;
688 ErrorCode rv = NO_ERROR;
689
690 popChoice = CRMF_CertReqMsgGetPOPType(currReq);
691 if (popChoice == crmfNoPOPChoice) {
692 rv = NO_POP_FOR_REQUEST;
693 goto loser;
694 }
695 switch (popChoice) {
696 case crmfSignature:
697 rv = verifySignature(varTable, currReq, certReq, newCert);
698 break;
699 case crmfKeyEncipherment:
700 rv = doChallengeResponse(varTable, currReq, certReq, newCert,
701 challs, numChall);
702 break;
703 case crmfRAVerified:
704 case crmfKeyAgreement:
705 default:
706 rv = UNSUPPORTED_POP;
707 goto loser;
708 }
709 loser:
710 return rv;
711 }
712
713 void
convertB64ToJS(char * base64)714 convertB64ToJS(char *base64)
715 {
716 int i;
717
718 for (i = 0; base64[i] != '\0'; i++) {
719 if (base64[i] == '\n') {
720 printf("\\n");
721 } else {
722 printf("%c", base64[i]);
723 }
724 }
725 }
726
727 void
formatChallenge(char * chall64,char * certRepContentDER,ChallengeCreationInfo * challInfo,int numChalls)728 formatChallenge(char *chall64, char *certRepContentDER,
729 ChallengeCreationInfo *challInfo, int numChalls)
730 {
731 printf("function respondToChallenge() {\n"
732 " var chalForm = document.chalForm;\n\n"
733 " chalForm.CertRepContent.value = '");
734 convertB64ToJS(certRepContentDER);
735 printf("';\n"
736 " chalForm.ChallResponse.value = crypto.popChallengeResponse('");
737 convertB64ToJS(chall64);
738 printf("');\n"
739 " chalForm.submit();\n"
740 "}\n");
741 }
742
743 void
spitOutChallenge(char * chall64,char * certRepContentDER,ChallengeCreationInfo * challInfo,int numChalls,char * nickname)744 spitOutChallenge(char *chall64, char *certRepContentDER,
745 ChallengeCreationInfo *challInfo, int numChalls,
746 char *nickname)
747 {
748 int i;
749
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 }
776
777 ErrorCode
issueChallenge(CertResponseInfo * issuedCerts,int numCerts,ChallengeCreationInfo * challInfo,int numChalls,CERTCertificate * issuer,CGIVarTable * varTable)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;
790
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 }
840
841 ErrorCode
processRequest(CGIVarTable * varTable)842 processRequest(CGIVarTable *varTable)
843 {
844 CERTCertDBHandle *certdb;
845 SECKEYKeyDBHandle *keydb;
846 CRMFCertReqMessages *certReqs = NULL;
847 const char *crmfReq;
848 const char *caNickname;
849 CERTCertificate *caCert = NULL;
850 CertResponseInfo *issuedCerts = NULL;
851 CERTSubjectPublicKeyInfo spki = { 0 };
852 ErrorCode rv = NO_ERROR;
853 PRBool doChallengeResponse = PR_FALSE;
854 SECItem der = { 0 };
855 SECStatus srv;
856 CERTCertificateRequest oldCertReq = { 0 };
857 CRMFCertReqMsg **reqMsgs = NULL, *currReq = NULL;
858 CRMFCertRequest **reqs = NULL, *certReq = NULL;
859 CERTName subject = { 0 };
860 int numReqs, i;
861 ChallengeCreationInfo *challInfo = NULL;
862 int numChalls = 0;
863
864 certdb = CERT_GetDefaultCertDB();
865 keydb = SECKEY_GetDefaultKeyDB();
866 crmfReq = CGITableFindValue(varTable, "CRMFRequest");
867 if (crmfReq == NULL) {
868 rv = CGI_VAR_MISSING;
869 missingVar = "CRMFRequest";
870 goto loser;
871 }
872 caNickname = CGITableFindValue(varTable, "CANickname");
873 if (caNickname == NULL) {
874 rv = CGI_VAR_MISSING;
875 missingVar = "CANickname";
876 goto loser;
877 }
878 caCert = CERT_FindCertByNickname(certdb, caNickname);
879 if (caCert == NULL) {
880 rv = COULD_NOT_FIND_CA;
881 goto loser;
882 }
883 srv = ATOB_ConvertAsciiToItem(&der, crmfReq);
884 if (srv != SECSuccess) {
885 rv = BAD_ASCII_FOR_REQ;
886 goto loser;
887 }
888 certReqs = CRMF_CreateCertReqMessagesFromDER(der.data, der.len);
889 SECITEM_FreeItem(&der, PR_FALSE);
890 if (certReqs == NULL) {
891 rv = COULD_NOT_DECODE_REQS;
892 goto loser;
893 }
894 numReqs = CRMF_CertReqMessagesGetNumMessages(certReqs);
895 issuedCerts = PORT_ZNewArray(CertResponseInfo, numReqs);
896 challInfo = PORT_ZNewArray(ChallengeCreationInfo, numReqs);
897 if (issuedCerts == NULL || challInfo == NULL) {
898 rv = OUT_OF_MEMORY;
899 goto loser;
900 }
901 reqMsgs = PORT_ZNewArray(CRMFCertReqMsg *, numReqs);
902 reqs = PORT_ZNewArray(CRMFCertRequest *, numReqs);
903 if (reqMsgs == NULL || reqs == NULL) {
904 rv = OUT_OF_MEMORY;
905 goto loser;
906 }
907 for (i = 0; i < numReqs; i++) {
908 currReq = reqMsgs[i] =
909 CRMF_CertReqMessagesGetCertReqMsgAtIndex(certReqs, i);
910 if (currReq == NULL) {
911 rv = ERROR_RETRIEVING_REQUEST_MSG;
912 goto loser;
913 }
914 certReq = reqs[i] = CRMF_CertReqMsgGetCertRequest(currReq);
915 if (certReq == NULL) {
916 rv = ERROR_RETRIEVING_CERT_REQUEST;
917 goto loser;
918 }
919 srv = CRMF_CertRequestGetCertTemplateSubject(certReq, &subject);
920 if (srv != SECSuccess) {
921 rv = ERROR_RETRIEVING_SUBJECT_FROM_REQ;
922 goto loser;
923 }
924 srv = CRMF_CertRequestGetCertTemplatePublicKey(certReq, &spki);
925 if (srv != SECSuccess) {
926 rv = ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ;
927 goto loser;
928 }
929 rv = initOldCertReq(&oldCertReq, &subject, &spki);
930 if (rv != NO_ERROR) {
931 goto loser;
932 }
933 rv = createNewCert(&issuedCerts[i].cert, &oldCertReq, currReq, certReq,
934 caCert, varTable);
935 if (rv != NO_ERROR) {
936 goto loser;
937 }
938 rv = doProofOfPossession(varTable, currReq, certReq, issuedCerts[i].cert,
939 challInfo, &numChalls);
940 if (rv != NO_ERROR) {
941 if (rv == DO_CHALLENGE_RESPONSE) {
942 doChallengeResponse = PR_TRUE;
943 } else {
944 goto loser;
945 }
946 }
947 CRMF_CertReqMsgGetID(currReq, &issuedCerts[i].certReqID);
948 CRMF_DestroyCertReqMsg(currReq);
949 CRMF_DestroyCertRequest(certReq);
950 }
951 if (doChallengeResponse) {
952 rv = issueChallenge(issuedCerts, numReqs, challInfo, numChalls, caCert,
953 varTable);
954 } else {
955 rv = issueCerts(issuedCerts, numReqs, caCert);
956 }
957 loser:
958 if (certReqs != NULL) {
959 CRMF_DestroyCertReqMessages(certReqs);
960 }
961 return rv;
962 }
963
964 ErrorCode
processChallengeResponse(CGIVarTable * varTable,const char * certRepContent)965 processChallengeResponse(CGIVarTable *varTable, const char *certRepContent)
966 {
967 SECItem binDER = { 0 };
968 SECStatus srv;
969 ErrorCode rv = NO_ERROR;
970 const char *clientResponse;
971 const char *formChalValue;
972 const char *nickname;
973 CMMFPOPODecKeyRespContent *respContent = NULL;
974 int numResponses, i;
975 long curResponse, expectedResponse;
976 char cgiChalVar[10];
977 #ifdef WRITE_OUT_RESPONSE
978 SECItem certRepBinDER = { 0 };
979
980 ATOB_ConvertAsciiToItem(&certRepBinDER, certRepContent);
981 writeOutItem("challCertRepContent.der", &certRepBinDER);
982 PORT_Free(certRepBinDER.data);
983 #endif
984 clientResponse = CGITableFindValue(varTable, "ChallResponse");
985 if (clientResponse == NULL) {
986 rv = REQ_CGI_VAR_NOT_PRESENT;
987 missingVar = "ChallResponse";
988 goto loser;
989 }
990 srv = ATOB_ConvertAsciiToItem(&binDER, clientResponse);
991 if (srv != SECSuccess) {
992 rv = ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN;
993 goto loser;
994 }
995 respContent = CMMF_CreatePOPODecKeyRespContentFromDER(binDER.data,
996 binDER.len);
997 SECITEM_FreeItem(&binDER, PR_FALSE);
998 binDER.data = NULL;
999 if (respContent == NULL) {
1000 rv = ERROR_CREATING_KEY_RESP_FROM_DER;
1001 goto loser;
1002 }
1003 numResponses = CMMF_POPODecKeyRespContentGetNumResponses(respContent);
1004 for (i = 0; i < numResponses; i++) {
1005 srv = CMMF_POPODecKeyRespContentGetResponse(respContent, i, &curResponse);
1006 if (srv != SECSuccess) {
1007 rv = ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE;
1008 goto loser;
1009 }
1010 sprintf(cgiChalVar, "chal%d", i + 1);
1011 formChalValue = CGITableFindValue(varTable, cgiChalVar);
1012 if (formChalValue == NULL) {
1013 rv = REQ_CGI_VAR_NOT_PRESENT;
1014 missingVar = strdup(cgiChalVar);
1015 goto loser;
1016 }
1017 sscanf(formChalValue, "%ld", &expectedResponse);
1018 if (expectedResponse != curResponse) {
1019 rv = ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED;
1020 goto loser;
1021 }
1022 }
1023 nickname = CGITableFindValue(varTable, "nickname");
1024 if (nickname == NULL) {
1025 rv = REQ_CGI_VAR_NOT_PRESENT;
1026 missingVar = "nickname";
1027 goto loser;
1028 }
1029 spitOutCMMFResponse(nickname, certRepContent);
1030 loser:
1031 if (respContent != NULL) {
1032 CMMF_DestroyPOPODecKeyRespContent(respContent);
1033 }
1034 return rv;
1035 }
1036
1037 int
main()1038 main()
1039 {
1040 char *form_output = NULL;
1041 int form_output_len, form_output_used;
1042 CGIVarTable varTable = { 0 };
1043 ErrorCode errNum = 0;
1044 char *certRepContent;
1045
1046 #ifdef ATTACH_CGI
1047 /* Put an ifinite loop in here so I can attach to
1048 * the process after the process is spun off
1049 */
1050 {
1051 int stupid = 1;
1052 while (stupid)
1053 ;
1054 }
1055 #endif
1056
1057 form_output_used = 0;
1058 srand(time(NULL));
1059 while (feof(stdin) == 0) {
1060 if (form_output == NULL) {
1061 form_output = PORT_NewArray(char, DEFAULT_ALLOC_SIZE + 1);
1062 form_output_len = DEFAULT_ALLOC_SIZE;
1063 } else if ((form_output_used + DEFAULT_ALLOC_SIZE) >= form_output_len) {
1064 form_output_len += DEFAULT_ALLOC_SIZE;
1065 form_output = PORT_Realloc(form_output, form_output_len + 1);
1066 }
1067 form_output_used += fread(&form_output[form_output_used], sizeof(char),
1068 DEFAULT_ALLOC_SIZE, stdin);
1069 }
1070 ParseInputVariables(&varTable, form_output);
1071 certRepContent = CGITableFindValue(&varTable, "CertRepContent");
1072 if (certRepContent == NULL) {
1073 errNum = initNSS(&varTable);
1074 if (errNum != 0) {
1075 goto loser;
1076 }
1077 errNum = processRequest(&varTable);
1078 } else {
1079 errNum = processChallengeResponse(&varTable, certRepContent);
1080 }
1081 if (errNum != NO_ERROR) {
1082 goto loser;
1083 }
1084 goto done;
1085 loser:
1086 dumpErrorMessage(errNum);
1087 done:
1088 free(form_output);
1089 return 0;
1090 }
1091