nss: import at 3.0.1 beta 1
[mozilla-nss.git] / security / nss / cmd / crmf-cgi / crmfcgi.c
blob8a33a01ee70c6ee05122f8c8158fb4e093138c6e
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "seccomon.h"
38 #include "nss.h"
39 #include "key.h"
40 #include "cert.h"
41 #include "pk11func.h"
42 #include "secmod.h"
43 #include "cmmf.h"
44 #include "crmf.h"
45 #include "base64.h"
46 #include "secasn1.h"
47 #include "cryptohi.h"
48 #include <string.h>
49 #include <stdlib.h>
50 #include <stdio.h>
52 #define DEFAULT_ALLOC_SIZE 200
53 #define DEFAULT_CGI_VARS 20
55 typedef struct CGIVariableStr {
56 char *name;
57 char *value;
58 } CGIVariable;
60 typedef struct CGIVarTableStr {
61 CGIVariable **variables;
62 int numVars;
63 int numAlloc;
64 } CGIVarTable;
66 typedef struct CertResponseInfoStr {
67 CERTCertificate *cert;
68 long certReqID;
69 } CertResponseInfo;
71 typedef struct ChallengeCreationInfoStr {
72 long random;
73 SECKEYPublicKey *pubKey;
74 } ChallengeCreationInfo;
76 char *missingVar = NULL;
79 * Error values.
81 typedef enum {
82 NO_ERROR = 0,
83 NSS_INIT_FAILED,
84 AUTH_FAILED,
85 REQ_CGI_VAR_NOT_PRESENT,
86 CRMF_REQ_NOT_PRESENT,
87 BAD_ASCII_FOR_REQ,
88 CGI_VAR_MISSING,
89 COULD_NOT_FIND_CA,
90 COULD_NOT_DECODE_REQS,
91 OUT_OF_MEMORY,
92 ERROR_RETRIEVING_REQUEST_MSG,
93 ERROR_RETRIEVING_CERT_REQUEST,
94 ERROR_RETRIEVING_SUBJECT_FROM_REQ,
95 ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ,
96 ERROR_CREATING_NEW_CERTIFICATE,
97 COULD_NOT_START_EXTENSIONS,
98 ERROR_RETRIEVING_EXT_FROM_REQ,
99 ERROR_ADDING_EXT_TO_CERT,
100 ERROR_ENDING_EXTENSIONS,
101 COULD_NOT_FIND_ISSUER_PRIVATE_KEY,
102 UNSUPPORTED_SIGN_OPERATION_FOR_ISSUER,
103 ERROR_SETTING_SIGN_ALG,
104 ERROR_ENCODING_NEW_CERT,
105 ERROR_SIGNING_NEW_CERT,
106 ERROR_CREATING_CERT_REP_CONTENT,
107 ERROR_CREATING_SINGLE_CERT_RESPONSE,
108 ERROR_SETTING_CERT_RESPONSES,
109 ERROR_CREATING_CA_LIST,
110 ERROR_ADDING_ISSUER_TO_CA_LIST,
111 ERROR_ENCODING_CERT_REP_CONTENT,
112 NO_POP_FOR_REQUEST,
113 UNSUPPORTED_POP,
114 ERROR_RETRIEVING_POP_SIGN_KEY,
115 ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY,
116 ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY,
117 DO_CHALLENGE_RESPONSE,
118 ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT,
119 ERROR_ENCODING_CERT_REQ_FOR_POP,
120 ERROR_VERIFYING_SIGNATURE_POP,
121 ERROR_RETRIEVING_PUB_KEY_FOR_CHALL,
122 ERROR_CREATING_EMPTY_CHAL_CONTENT,
123 ERROR_EXTRACTING_GEN_NAME_FROM_ISSUER,
124 ERROR_SETTING_CHALLENGE,
125 ERROR_ENCODING_CHALL,
126 ERROR_CONVERTING_CHALL_TO_BASE64,
127 ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN,
128 ERROR_CREATING_KEY_RESP_FROM_DER,
129 ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE,
130 ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED,
131 ERROR_GETTING_KEY_ENCIPHERMENT,
132 ERROR_NO_POP_FOR_PRIVKEY,
133 ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE
134 } ErrorCode;
136 const char *
137 CGITableFindValue(CGIVarTable *varTable, const char *key);
139 void
140 spitOutHeaders(void)
142 printf("Content-type: text/html\n\n");
145 void
146 dumpRequest(CGIVarTable *varTable)
148 int i;
149 CGIVariable *var;
151 printf ("<table border=1 cellpadding=1 cellspacing=1 width=\"100%%\">\n");
152 printf ("<tr><td><b><center>Variable Name<center></b></td>"
153 "<td><b><center>Value</center></b></td></tr>\n");
154 for (i=0; i<varTable->numVars; i++) {
155 var = varTable->variables[i];
156 printf ("<tr><td><pre>%s</pre></td><td><pre>%s</pre></td></tr>\n",
157 var->name, var->value);
159 printf("</table>\n");
162 void
163 echo_request(CGIVarTable *varTable)
165 spitOutHeaders();
166 printf("<html><head><title>CGI Echo Page</title></head>\n"
167 "<body><h1>Got the following request</h1>\n");
168 dumpRequest(varTable);
169 printf("</body></html>");
172 void
173 processVariable(CGIVariable *var)
175 char *plusSign, *percentSign;
177 /*First look for all of the '+' and convert them to spaces */
178 plusSign = var->value;
179 while ((plusSign=strchr(plusSign, '+')) != NULL) {
180 *plusSign = ' ';
182 percentSign = var->value;
183 while ((percentSign=strchr(percentSign, '%')) != NULL) {
184 char string[3];
185 int value;
187 string[0] = percentSign[1];
188 string[1] = percentSign[2];
189 string[2] = '\0';
191 sscanf(string,"%x", &value);
192 *percentSign = (char)value;
193 memmove(&percentSign[1], &percentSign[3], 1+strlen(&percentSign[3]));
197 char *
198 parseNextVariable(CGIVarTable *varTable, char *form_output)
200 char *ampersand, *equal;
201 CGIVariable *var;
203 if (varTable->numVars == varTable->numAlloc) {
204 CGIVariable **newArr = realloc(varTable->variables,
205 (varTable->numAlloc + DEFAULT_CGI_VARS)*sizeof(CGIVariable*));
206 if (newArr == NULL) {
207 return NULL;
209 varTable->variables = newArr;
210 varTable->numAlloc += DEFAULT_CGI_VARS;
212 equal = strchr(form_output, '=');
213 if (equal == NULL) {
214 return NULL;
216 ampersand = strchr(equal, '&');
217 if (ampersand == NULL) {
218 return NULL;
220 equal[0] = '\0';
221 if (ampersand != NULL) {
222 ampersand[0] = '\0';
224 var = malloc(sizeof(CGIVariable));
225 var->name = form_output;
226 var->value = &equal[1];
227 varTable->variables[varTable->numVars] = var;
228 varTable->numVars++;
229 processVariable(var);
230 return (ampersand != NULL) ? &ampersand[1] : NULL;
233 void
234 ParseInputVariables(CGIVarTable *varTable, char *form_output)
236 varTable->variables = malloc(sizeof(CGIVariable*)*DEFAULT_CGI_VARS);
237 varTable->numVars = 0;
238 varTable->numAlloc = DEFAULT_CGI_VARS;
239 while (form_output && form_output[0] != '\0') {
240 form_output = parseNextVariable(varTable, form_output);
244 const char *
245 CGITableFindValue(CGIVarTable *varTable, const char *key)
247 const char *retVal = NULL;
248 int i;
250 for (i=0; i<varTable->numVars; i++) {
251 if (strcmp(varTable->variables[i]->name, key) == 0) {
252 retVal = varTable->variables[i]->value;
253 break;
256 return retVal;
259 char*
260 passwordCallback(PK11SlotInfo *slot, PRBool retry, void *arg)
262 const char *passwd;
263 if (retry) {
264 return NULL;
266 passwd = CGITableFindValue((CGIVarTable*)arg, "dbPassword");
267 if (passwd == NULL) {
268 return NULL;
270 return PORT_Strdup(passwd);
273 ErrorCode
274 initNSS(CGIVarTable *varTable)
276 const char *nssDir;
277 PK11SlotInfo *keySlot;
278 SECStatus rv;
280 nssDir = CGITableFindValue(varTable,"NSSDirectory");
281 if (nssDir == NULL) {
282 missingVar = "NSSDirectory";
283 return REQ_CGI_VAR_NOT_PRESENT;
285 rv = NSS_Init(nssDir);
286 if (rv != SECSuccess) {
287 return NSS_INIT_FAILED;
289 PK11_SetPasswordFunc(passwordCallback);
290 keySlot = PK11_GetInternalKeySlot();
291 rv = PK11_Authenticate(keySlot, PR_FALSE, varTable);
292 PK11_FreeSlot(keySlot);
293 if (rv != SECSuccess) {
294 return AUTH_FAILED;
296 return NO_ERROR;
299 void
300 dumpErrorMessage(ErrorCode errNum)
302 spitOutHeaders();
303 printf("<html><head><title>Error</title></head><body><h1>Error processing "
304 "data</h1> Received the error %d<p>", errNum);
305 if (errNum == REQ_CGI_VAR_NOT_PRESENT) {
306 printf ("The missing variable is %s.", missingVar);
308 printf ("<i>More useful information here in the future.</i></body></html>");
311 ErrorCode
312 initOldCertReq(CERTCertificateRequest *oldCertReq,
313 CERTName *subject, CERTSubjectPublicKeyInfo *spki)
315 PRArenaPool *poolp;
317 poolp = oldCertReq->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
318 SEC_ASN1EncodeInteger(poolp, &oldCertReq->version,
319 SEC_CERTIFICATE_VERSION_3);
320 CERT_CopyName(poolp, &oldCertReq->subject, subject);
321 SECKEY_CopySubjectPublicKeyInfo(poolp, &oldCertReq->subjectPublicKeyInfo,
322 spki);
323 oldCertReq->attributes = NULL;
324 return NO_ERROR;
327 ErrorCode
328 addExtensions(CERTCertificate *newCert, CRMFCertRequest *certReq)
330 int numExtensions, i;
331 void *extHandle;
332 ErrorCode rv = NO_ERROR;
333 CRMFCertExtension *ext;
334 SECStatus srv;
336 numExtensions = CRMF_CertRequestGetNumberOfExtensions(certReq);
337 if (numExtensions == 0) {
338 /* No extensions to add */
339 return NO_ERROR;
341 extHandle = CERT_StartCertExtensions(newCert);
342 if (extHandle == NULL) {
343 rv = COULD_NOT_START_EXTENSIONS;
344 goto loser;
346 for (i=0; i<numExtensions; i++) {
347 ext = CRMF_CertRequestGetExtensionAtIndex(certReq, i);
348 if (ext == NULL) {
349 rv = ERROR_RETRIEVING_EXT_FROM_REQ;
351 srv = CERT_AddExtension(extHandle, CRMF_CertExtensionGetOidTag(ext),
352 CRMF_CertExtensionGetValue(ext),
353 CRMF_CertExtensionGetIsCritical(ext), PR_FALSE);
354 if (srv != SECSuccess) {
355 rv = ERROR_ADDING_EXT_TO_CERT;
358 srv = CERT_FinishExtensions(extHandle);
359 if (srv != SECSuccess) {
360 rv = ERROR_ENDING_EXTENSIONS;
361 goto loser;
363 return NO_ERROR;
364 loser:
365 return rv;
368 void
369 writeOutItem(const char *filePath, SECItem *der)
371 PRFileDesc *outfile;
373 outfile = PR_Open (filePath,
374 PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
375 0666);
376 PR_Write(outfile, der->data, der->len);
377 PR_Close(outfile);
381 ErrorCode
382 createNewCert(CERTCertificate**issuedCert,CERTCertificateRequest *oldCertReq,
383 CRMFCertReqMsg *currReq, CRMFCertRequest *certReq,
384 CERTCertificate *issuerCert, CGIVarTable *varTable)
386 CERTCertificate *newCert = NULL;
387 CERTValidity *validity;
388 PRExplodedTime printableTime;
389 PRTime now, after;
390 ErrorCode rv=NO_ERROR;
391 SECKEYPrivateKey *issuerPrivKey;
392 SECItem derCert = { 0 };
393 SECOidTag signTag;
394 SECStatus srv;
395 long version;
397 now = PR_Now();
398 PR_ExplodeTime(now, PR_GMTParameters, &printableTime);
399 printableTime.tm_month += 9;
400 after = PR_ImplodeTime(&printableTime);
401 validity = CERT_CreateValidity(now, after);
402 newCert = *issuedCert =
403 CERT_CreateCertificate(rand(), &(issuerCert->subject), validity,
404 oldCertReq);
405 if (newCert == NULL) {
406 rv = ERROR_CREATING_NEW_CERTIFICATE;
407 goto loser;
409 rv = addExtensions(newCert, certReq);
410 if (rv != NO_ERROR) {
411 goto loser;
413 issuerPrivKey = PK11_FindKeyByAnyCert(issuerCert, varTable);
414 if (issuerPrivKey == NULL) {
415 rv = COULD_NOT_FIND_ISSUER_PRIVATE_KEY;
417 signTag = SEC_GetSignatureAlgorithmOidTag(issuerPrivatekey->keytype,
418 SEC_OID_UNKNOWN);
419 if (signTag == SEC_OID_UNKNOWN) {
420 rv = UNSUPPORTED_SIGN_OPERATION_FOR_ISSUER;
421 goto loser;
423 srv = SECOID_SetAlgorithmID(newCert->arena, &newCert->signature,
424 signTag, 0);
425 if (srv != SECSuccess) {
426 rv = ERROR_SETTING_SIGN_ALG;
427 goto loser;
429 srv = CRMF_CertRequestGetCertTemplateVersion(certReq, &version);
430 if (srv != SECSuccess) {
431 /* No version included in the request */
432 *(newCert->version.data) = SEC_CERTIFICATE_VERSION_3;
433 } else {
434 SECITEM_FreeItem(&newCert->version, PR_FALSE);
435 SEC_ASN1EncodeInteger(newCert->arena, &newCert->version, version);
437 SEC_ASN1EncodeItem(newCert->arena, &derCert, newCert,
438 CERT_CertificateTemplate);
439 if (derCert.data == NULL) {
440 rv = ERROR_ENCODING_NEW_CERT;
441 goto loser;
443 srv = SEC_DerSignData(newCert->arena, &(newCert->derCert), derCert.data,
444 derCert.len, issuerPrivKey, signTag);
445 if (srv != SECSuccess) {
446 rv = ERROR_SIGNING_NEW_CERT;
447 goto loser;
449 #ifdef WRITE_OUT_RESPONSE
450 writeOutItem("newcert.der", &newCert->derCert);
451 #endif
452 return NO_ERROR;
453 loser:
454 *issuedCert = NULL;
455 if (newCert) {
456 CERT_DestroyCertificate(newCert);
458 return rv;
462 void
463 formatCMMFResponse(char *nickname, char *base64Response)
465 char *currLine, *nextLine;
467 printf("var retVal = crypto.importUserCertificates(\"%s\",\n", nickname);
468 currLine = base64Response;
469 while (1) {
470 nextLine = strchr(currLine, '\n');
471 if (nextLine == NULL) {
472 /* print out the last line here. */
473 printf ("\"%s\",\n", currLine);
474 break;
476 nextLine[0] = '\0';
477 printf("\"%s\\n\"+\n", currLine);
478 currLine = nextLine+1;
480 printf("true);\n"
481 "if(retVal == '') {\n"
482 "\tdocument.write(\"<h1>New Certificate Succesfully Imported.</h1>\");\n"
483 "} else {\n"
484 "\tdocument.write(\"<h2>Unable to import New Certificate</h2>\");\n"
485 "\tdocument.write(\"crypto.importUserCertificates returned <b>\");\n"
486 "\tdocument.write(retVal);\n"
487 "\tdocument.write(\"</b>\");\n"
488 "}\n");
491 void
492 spitOutCMMFResponse(char *nickname, char *base64Response)
494 spitOutHeaders();
495 printf("<html>\n<head>\n<title>CMMF Resonse Page</title>\n</head>\n\n"
496 "<body><h1>CMMF Response Page</h1>\n"
497 "<script language=\"JavaScript\">\n"
498 "<!--\n");
499 formatCMMFResponse(nickname, base64Response);
500 printf("// -->\n"
501 "</script>\n</body>\n</html>");
504 char*
505 getNickname(CERTCertificate *cert)
507 char *nickname;
509 if (cert->nickname != NULL) {
510 return cert->nickname;
512 nickname = CERT_GetCommonName(&cert->subject);
513 if (nickname != NULL) {
514 return nickname;
516 return CERT_NameToAscii(&cert->subject);
519 ErrorCode
520 createCMMFResponse(CertResponseInfo *issuedCerts, int numCerts,
521 CERTCertificate *issuerCert, char **base64der)
523 CMMFCertRepContent *certRepContent=NULL;
524 ErrorCode rv = NO_ERROR;
525 CMMFCertResponse **responses, *currResponse;
526 CERTCertList *caList;
527 int i;
528 SECStatus srv;
529 PRArenaPool *poolp;
530 SECItem *der;
532 certRepContent = CMMF_CreateCertRepContent();
533 if (certRepContent == NULL) {
534 rv = ERROR_CREATING_CERT_REP_CONTENT;
535 goto loser;
537 responses = PORT_NewArray(CMMFCertResponse*, numCerts);
538 if (responses == NULL) {
539 rv = OUT_OF_MEMORY;
540 goto loser;
542 for (i=0; i<numCerts;i++) {
543 responses[i] = currResponse =
544 CMMF_CreateCertResponse(issuedCerts[i].certReqID);
545 if (currResponse == NULL) {
546 rv = ERROR_CREATING_SINGLE_CERT_RESPONSE;
547 goto loser;
549 CMMF_CertResponseSetPKIStatusInfoStatus(currResponse, cmmfGranted);
550 CMMF_CertResponseSetCertificate(currResponse, issuedCerts[i].cert);
552 srv = CMMF_CertRepContentSetCertResponses(certRepContent, responses,
553 numCerts);
554 if (srv != SECSuccess) {
555 rv = ERROR_SETTING_CERT_RESPONSES;
556 goto loser;
558 caList = CERT_NewCertList();
559 if (caList == NULL) {
560 rv = ERROR_CREATING_CA_LIST;
561 goto loser;
563 srv = CERT_AddCertToListTail(caList, issuerCert);
564 if (srv != SECSuccess) {
565 rv = ERROR_ADDING_ISSUER_TO_CA_LIST;
566 goto loser;
568 srv = CMMF_CertRepContentSetCAPubs(certRepContent, caList);
569 CERT_DestroyCertList(caList);
570 poolp = PORT_NewArena(1024);
571 der = SEC_ASN1EncodeItem(poolp, NULL, certRepContent,
572 CMMFCertRepContentTemplate);
573 if (der == NULL) {
574 rv = ERROR_ENCODING_CERT_REP_CONTENT;
575 goto loser;
577 #ifdef WRITE_OUT_RESPONSE
578 writeOutItem("CertRepContent.der", der);
579 #endif
580 *base64der = BTOA_DataToAscii(der->data, der->len);
581 return NO_ERROR;
582 loser:
583 return rv;
586 ErrorCode
587 issueCerts(CertResponseInfo *issuedCerts, int numCerts,
588 CERTCertificate *issuerCert)
590 ErrorCode rv;
591 char *base64Response;
593 rv = createCMMFResponse(issuedCerts, numCerts, issuerCert, &base64Response);
594 if (rv != NO_ERROR) {
595 goto loser;
597 spitOutCMMFResponse(getNickname(issuedCerts[0].cert),base64Response);
598 return NO_ERROR;
599 loser:
600 return rv;
603 ErrorCode
604 verifySignature(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
605 CRMFCertRequest *certReq, CERTCertificate *newCert)
607 SECStatus srv;
608 ErrorCode rv = NO_ERROR;
609 CRMFPOPOSigningKey *signKey = NULL;
610 SECAlgorithmID *algID = NULL;
611 SECItem *signature = NULL;
612 SECKEYPublicKey *pubKey = NULL;
613 SECItem *reqDER = NULL;
615 srv = CRMF_CertReqMsgGetPOPOSigningKey(currReq, &signKey);
616 if (srv != SECSuccess || signKey == NULL) {
617 rv = ERROR_RETRIEVING_POP_SIGN_KEY;
618 goto loser;
620 algID = CRMF_POPOSigningKeyGetAlgID(signKey);
621 if (algID == NULL) {
622 rv = ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY;
623 goto loser;
625 signature = CRMF_POPOSigningKeyGetSignature(signKey);
626 if (signature == NULL) {
627 rv = ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY;
628 goto loser;
630 /* Make the length the number of bytes instead of bits */
631 signature->len = (signature->len+7)/8;
632 pubKey = CERT_ExtractPublicKey(newCert);
633 if (pubKey == NULL) {
634 rv = ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT;
635 goto loser;
637 reqDER = SEC_ASN1EncodeItem(NULL, NULL, certReq, CRMFCertRequestTemplate);
638 if (reqDER == NULL) {
639 rv = ERROR_ENCODING_CERT_REQ_FOR_POP;
640 goto loser;
642 srv = VFY_VerifyDataWithAlgorithmID(reqDER->data, reqDER->len, pubKey,
643 signature, &algID->algorithm, NULL, varTable);
644 if (srv != SECSuccess) {
645 rv = ERROR_VERIFYING_SIGNATURE_POP;
646 goto loser;
648 /* Fall thru in successfull case. */
649 loser:
650 if (pubKey != NULL) {
651 SECKEY_DestroyPublicKey(pubKey);
653 if (reqDER != NULL) {
654 SECITEM_FreeItem(reqDER, PR_TRUE);
656 if (signature != NULL) {
657 SECITEM_FreeItem(signature, PR_TRUE);
659 if (algID != NULL) {
660 SECOID_DestroyAlgorithmID(algID, PR_TRUE);
662 if (signKey != NULL) {
663 CRMF_DestroyPOPOSigningKey(signKey);
665 return rv;
668 ErrorCode
669 doChallengeResponse(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
670 CRMFCertRequest *certReq, CERTCertificate *newCert,
671 ChallengeCreationInfo *challs, int *numChall)
673 CRMFPOPOPrivKey *privKey = NULL;
674 CRMFPOPOPrivKeyChoice privKeyChoice;
675 SECStatus srv;
676 ErrorCode rv = NO_ERROR;
678 srv = CRMF_CertReqMsgGetPOPKeyEncipherment(currReq, &privKey);
679 if (srv != SECSuccess || privKey == NULL) {
680 rv = ERROR_GETTING_KEY_ENCIPHERMENT;
681 goto loser;
683 privKeyChoice = CRMF_POPOPrivKeyGetChoice(privKey);
684 CRMF_DestroyPOPOPrivKey(privKey);
685 switch (privKeyChoice) {
686 case crmfSubsequentMessage:
687 challs = &challs[*numChall];
688 challs->random = rand();
689 challs->pubKey = CERT_ExtractPublicKey(newCert);
690 if (challs->pubKey == NULL) {
691 rv = ERROR_RETRIEVING_PUB_KEY_FOR_CHALL;
692 goto loser;
694 (*numChall)++;
695 rv = DO_CHALLENGE_RESPONSE;
696 break;
697 case crmfThisMessage:
698 /* There'd better be a PKIArchiveControl in this message */
699 if (!CRMF_CertRequestIsControlPresent(certReq,
700 crmfPKIArchiveOptionsControl)) {
701 rv = ERROR_NO_POP_FOR_PRIVKEY;
702 goto loser;
704 break;
705 default:
706 rv = ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE;
707 goto loser;
709 loser:
710 return rv;
713 ErrorCode
714 doProofOfPossession(CGIVarTable *varTable, CRMFCertReqMsg *currReq,
715 CRMFCertRequest *certReq, CERTCertificate *newCert,
716 ChallengeCreationInfo *challs, int *numChall)
718 CRMFPOPChoice popChoice;
719 ErrorCode rv = NO_ERROR;
721 popChoice = CRMF_CertReqMsgGetPOPType(currReq);
722 if (popChoice == crmfNoPOPChoice) {
723 rv = NO_POP_FOR_REQUEST;
724 goto loser;
726 switch (popChoice) {
727 case crmfSignature:
728 rv = verifySignature(varTable, currReq, certReq, newCert);
729 break;
730 case crmfKeyEncipherment:
731 rv = doChallengeResponse(varTable, currReq, certReq, newCert,
732 challs, numChall);
733 break;
734 case crmfRAVerified:
735 case crmfKeyAgreement:
736 default:
737 rv = UNSUPPORTED_POP;
738 goto loser;
740 loser:
741 return rv;
744 void
745 convertB64ToJS(char *base64)
747 int i;
749 for (i=0; base64[i] != '\0'; i++) {
750 if (base64[i] == '\n') {
751 printf ("\\n");
752 }else {
753 printf ("%c", base64[i]);
758 void
759 formatChallenge(char *chall64, char *certRepContentDER,
760 ChallengeCreationInfo *challInfo, int numChalls)
762 printf ("function respondToChallenge() {\n"
763 " var chalForm = document.chalForm;\n\n"
764 " chalForm.CertRepContent.value = '");
765 convertB64ToJS(certRepContentDER);
766 printf ("';\n"
767 " chalForm.ChallResponse.value = crypto.popChallengeResponse('");
768 convertB64ToJS(chall64);
769 printf("');\n"
770 " chalForm.submit();\n"
771 "}\n");
775 void
776 spitOutChallenge(char *chall64, char *certRepContentDER,
777 ChallengeCreationInfo *challInfo, int numChalls,
778 char *nickname)
780 int i;
782 spitOutHeaders();
783 printf("<html>\n"
784 "<head>\n"
785 "<title>Challenge Page</title>\n"
786 "<script language=\"JavaScript\">\n"
787 "<!--\n");
788 /* The JavaScript function actually gets defined within
789 * this function call
791 formatChallenge(chall64, certRepContentDER, challInfo, numChalls);
792 printf("// -->\n"
793 "</script>\n"
794 "</head>\n"
795 "<body onLoad='respondToChallenge()'>\n"
796 "<h1>Cartman is now responding to the Challenge "
797 "presented by the CGI</h1>\n"
798 "<form action='crmfcgi' method='post' name='chalForm'>\n"
799 "<input type='hidden' name=CertRepContent value=''>\n"
800 "<input type='hidden' name=ChallResponse value=''>\n");
801 for (i=0;i<numChalls; i++) {
802 printf("<input type='hidden' name='chal%d' value='%d'>\n",
803 i+1, challInfo[i].random);
805 printf("<input type='hidden' name='nickname' value='%s'>\n", nickname);
806 printf("</form>\n</body>\n</html>");
809 ErrorCode
810 issueChallenge(CertResponseInfo *issuedCerts, int numCerts,
811 ChallengeCreationInfo *challInfo, int numChalls,
812 CERTCertificate *issuer, CGIVarTable *varTable)
814 ErrorCode rv = NO_ERROR;
815 CMMFPOPODecKeyChallContent *chalContent = NULL;
816 int i;
817 SECStatus srv;
818 PRArenaPool *poolp;
819 CERTGeneralName *genName;
820 SECItem *challDER = NULL;
821 char *chall64, *certRepContentDER;
823 rv = createCMMFResponse(issuedCerts, numCerts, issuer,
824 &certRepContentDER);
825 if (rv != NO_ERROR) {
826 goto loser;
828 chalContent = CMMF_CreatePOPODecKeyChallContent();
829 if (chalContent == NULL) {
830 rv = ERROR_CREATING_EMPTY_CHAL_CONTENT;
831 goto loser;
833 poolp = PORT_NewArena(1024);
834 if (poolp == NULL) {
835 rv = OUT_OF_MEMORY;
836 goto loser;
838 genName = CERT_GetCertificateNames(issuer, poolp);
839 if (genName == NULL) {
840 rv = ERROR_EXTRACTING_GEN_NAME_FROM_ISSUER;
841 goto loser;
843 for (i=0;i<numChalls;i++) {
844 srv = CMMF_POPODecKeyChallContentSetNextChallenge(chalContent,
845 challInfo[i].random,
846 genName,
847 challInfo[i].pubKey,
848 varTable);
849 SECKEY_DestroyPublicKey(challInfo[i].pubKey);
850 if (srv != SECSuccess) {
851 rv = ERROR_SETTING_CHALLENGE;
852 goto loser;
855 challDER = SEC_ASN1EncodeItem(NULL, NULL, chalContent,
856 CMMFPOPODecKeyChallContentTemplate);
857 if (challDER == NULL) {
858 rv = ERROR_ENCODING_CHALL;
859 goto loser;
861 chall64 = BTOA_DataToAscii(challDER->data, challDER->len);
862 SECITEM_FreeItem(challDER, PR_TRUE);
863 if (chall64 == NULL) {
864 rv = ERROR_CONVERTING_CHALL_TO_BASE64;
865 goto loser;
867 spitOutChallenge(chall64, certRepContentDER, challInfo, numChalls,
868 getNickname(issuedCerts[0].cert));
869 loser:
870 return rv;
874 ErrorCode
875 processRequest(CGIVarTable *varTable)
877 CERTCertDBHandle *certdb;
878 SECKEYKeyDBHandle *keydb;
879 CRMFCertReqMessages *certReqs = NULL;
880 const char *crmfReq;
881 const char *caNickname;
882 CERTCertificate *caCert = NULL;
883 CertResponseInfo *issuedCerts = NULL;
884 CERTSubjectPublicKeyInfo spki = { 0 };
885 ErrorCode rv=NO_ERROR;
886 PRBool doChallengeResponse = PR_FALSE;
887 SECItem der = { 0 };
888 SECStatus srv;
889 CERTCertificateRequest oldCertReq = { 0 };
890 CRMFCertReqMsg **reqMsgs = NULL,*currReq = NULL;
891 CRMFCertRequest **reqs = NULL, *certReq = NULL;
892 CERTName subject = { 0 };
893 int numReqs,i;
894 ChallengeCreationInfo *challInfo=NULL;
895 int numChalls = 0;
897 certdb = CERT_GetDefaultCertDB();
898 keydb = SECKEY_GetDefaultKeyDB();
899 crmfReq = CGITableFindValue(varTable, "CRMFRequest");
900 if (crmfReq == NULL) {
901 rv = CGI_VAR_MISSING;
902 missingVar = "CRMFRequest";
903 goto loser;
905 caNickname = CGITableFindValue(varTable, "CANickname");
906 if (caNickname == NULL) {
907 rv = CGI_VAR_MISSING;
908 missingVar = "CANickname";
909 goto loser;
911 caCert = CERT_FindCertByNickname(certdb, caNickname);
912 if (caCert == NULL) {
913 rv = COULD_NOT_FIND_CA;
914 goto loser;
916 srv = ATOB_ConvertAsciiToItem(&der, crmfReq);
917 if (srv != SECSuccess) {
918 rv = BAD_ASCII_FOR_REQ;
919 goto loser;
921 certReqs = CRMF_CreateCertReqMessagesFromDER(der.data, der.len);
922 SECITEM_FreeItem(&der, PR_FALSE);
923 if (certReqs == NULL) {
924 rv = COULD_NOT_DECODE_REQS;
925 goto loser;
927 numReqs = CRMF_CertReqMessagesGetNumMessages(certReqs);
928 issuedCerts = PORT_ZNewArray(CertResponseInfo, numReqs);
929 challInfo = PORT_ZNewArray(ChallengeCreationInfo, numReqs);
930 if (issuedCerts == NULL || challInfo == NULL) {
931 rv = OUT_OF_MEMORY;
932 goto loser;
934 reqMsgs = PORT_ZNewArray(CRMFCertReqMsg*, numReqs);
935 reqs = PORT_ZNewArray(CRMFCertRequest*, numReqs);
936 if (reqMsgs == NULL || reqs == NULL) {
937 rv = OUT_OF_MEMORY;
938 goto loser;
940 for (i=0; i<numReqs; i++) {
941 currReq = reqMsgs[i] =
942 CRMF_CertReqMessagesGetCertReqMsgAtIndex(certReqs, i);
943 if (currReq == NULL) {
944 rv = ERROR_RETRIEVING_REQUEST_MSG;
945 goto loser;
947 certReq = reqs[i] = CRMF_CertReqMsgGetCertRequest(currReq);
948 if (certReq == NULL) {
949 rv = ERROR_RETRIEVING_CERT_REQUEST;
950 goto loser;
952 srv = CRMF_CertRequestGetCertTemplateSubject(certReq, &subject);
953 if (srv != SECSuccess) {
954 rv = ERROR_RETRIEVING_SUBJECT_FROM_REQ;
955 goto loser;
957 srv = CRMF_CertRequestGetCertTemplatePublicKey(certReq, &spki);
958 if (srv != SECSuccess) {
959 rv = ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ;
960 goto loser;
962 rv = initOldCertReq(&oldCertReq, &subject, &spki);
963 if (rv != NO_ERROR) {
964 goto loser;
966 rv = createNewCert(&issuedCerts[i].cert, &oldCertReq, currReq, certReq,
967 caCert, varTable);
968 if (rv != NO_ERROR) {
969 goto loser;
971 rv = doProofOfPossession(varTable, currReq, certReq, issuedCerts[i].cert,
972 challInfo, &numChalls);
973 if (rv != NO_ERROR) {
974 if (rv == DO_CHALLENGE_RESPONSE) {
975 doChallengeResponse = PR_TRUE;
976 } else {
977 goto loser;
980 CRMF_CertReqMsgGetID(currReq, &issuedCerts[i].certReqID);
981 CRMF_DestroyCertReqMsg(currReq);
982 CRMF_DestroyCertRequest(certReq);
984 if (doChallengeResponse) {
985 rv = issueChallenge(issuedCerts, numReqs, challInfo, numChalls, caCert,
986 varTable);
987 } else {
988 rv = issueCerts(issuedCerts, numReqs, caCert);
990 loser:
991 if (certReqs != NULL) {
992 CRMF_DestroyCertReqMessages(certReqs);
994 return rv;
997 ErrorCode
998 processChallengeResponse(CGIVarTable *varTable, const char *certRepContent)
1000 SECItem binDER = { 0 };
1001 SECStatus srv;
1002 ErrorCode rv = NO_ERROR;
1003 const char *clientResponse;
1004 const char *formChalValue;
1005 const char *nickname;
1006 CMMFPOPODecKeyRespContent *respContent = NULL;
1007 int numResponses,i;
1008 long curResponse, expectedResponse;
1009 char cgiChalVar[10];
1010 #ifdef WRITE_OUT_RESPONSE
1011 SECItem certRepBinDER = { 0 };
1013 ATOB_ConvertAsciiToItem(&certRepBinDER, certRepContent);
1014 writeOutItem("challCertRepContent.der", &certRepBinDER);
1015 PORT_Free(certRepBinDER.data);
1016 #endif
1017 clientResponse = CGITableFindValue(varTable, "ChallResponse");
1018 if (clientResponse == NULL) {
1019 rv = REQ_CGI_VAR_NOT_PRESENT;
1020 missingVar = "ChallResponse";
1021 goto loser;
1023 srv = ATOB_ConvertAsciiToItem(&binDER, clientResponse);
1024 if (srv != SECSuccess) {
1025 rv = ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN;
1026 goto loser;
1028 respContent = CMMF_CreatePOPODecKeyRespContentFromDER(binDER.data,
1029 binDER.len);
1030 SECITEM_FreeItem(&binDER, PR_FALSE);
1031 binDER.data = NULL;
1032 if (respContent == NULL) {
1033 rv = ERROR_CREATING_KEY_RESP_FROM_DER;
1034 goto loser;
1036 numResponses = CMMF_POPODecKeyRespContentGetNumResponses(respContent);
1037 for (i=0;i<numResponses;i++){
1038 srv = CMMF_POPODecKeyRespContentGetResponse(respContent,i,&curResponse);
1039 if (srv != SECSuccess) {
1040 rv = ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE;
1041 goto loser;
1043 sprintf(cgiChalVar, "chal%d", i+1);
1044 formChalValue = CGITableFindValue(varTable, cgiChalVar);
1045 if (formChalValue == NULL) {
1046 rv = REQ_CGI_VAR_NOT_PRESENT;
1047 missingVar = strdup(cgiChalVar);
1048 goto loser;
1050 sscanf(formChalValue, "%ld", &expectedResponse);
1051 if (expectedResponse != curResponse) {
1052 rv = ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED;
1053 goto loser;
1056 nickname = CGITableFindValue(varTable, "nickname");
1057 if (nickname == NULL) {
1058 rv = REQ_CGI_VAR_NOT_PRESENT;
1059 missingVar = "nickname";
1060 goto loser;
1062 spitOutCMMFResponse(nickname, certRepContent);
1063 loser:
1064 if (respContent != NULL) {
1065 CMMF_DestroyPOPODecKeyRespContent(respContent);
1067 return rv;
1071 main()
1073 char *form_output = NULL;
1074 int form_output_len, form_output_used;
1075 CGIVarTable varTable = { 0 };
1076 ErrorCode errNum = 0;
1077 char *certRepContent;
1079 #ifdef ATTACH_CGI
1080 /* Put an ifinite loop in here so I can attach to
1081 * the process after the process is spun off
1083 { int stupid = 1;
1084 while (stupid);
1086 #endif
1088 form_output_used = 0;
1089 srand(time(NULL));
1090 while (feof(stdin) == 0) {
1091 if (form_output == NULL) {
1092 form_output = PORT_NewArray(char, DEFAULT_ALLOC_SIZE+1);
1093 form_output_len = DEFAULT_ALLOC_SIZE;
1094 } else if ((form_output_used + DEFAULT_ALLOC_SIZE) >= form_output_len) {
1095 form_output_len += DEFAULT_ALLOC_SIZE;
1096 form_output = PORT_Realloc(form_output, form_output_len+1);
1098 form_output_used += fread(&form_output[form_output_used], sizeof(char),
1099 DEFAULT_ALLOC_SIZE, stdin);
1101 ParseInputVariables(&varTable, form_output);
1102 certRepContent = CGITableFindValue(&varTable, "CertRepContent");
1103 if (certRepContent == NULL) {
1104 errNum = initNSS(&varTable);
1105 if (errNum != 0) {
1106 goto loser;
1108 errNum = processRequest(&varTable);
1109 } else {
1110 errNum = processChallengeResponse(&varTable, certRepContent);
1112 if (errNum != NO_ERROR) {
1113 goto loser;
1115 goto done;
1116 loser:
1117 dumpErrorMessage(errNum);
1118 done:
1119 free (form_output);
1120 return 0;