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