|
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 "key.h" |
|
8 #include "cert.h" |
|
9 #include "pk11func.h" |
|
10 #include "secmod.h" |
|
11 #include "cmmf.h" |
|
12 #include "crmf.h" |
|
13 #include "base64.h" |
|
14 #include "secasn1.h" |
|
15 #include "cryptohi.h" |
|
16 #include <string.h> |
|
17 #include <stdlib.h> |
|
18 #include <stdio.h> |
|
19 |
|
20 #define DEFAULT_ALLOC_SIZE 200 |
|
21 #define DEFAULT_CGI_VARS 20 |
|
22 |
|
23 typedef struct CGIVariableStr { |
|
24 char *name; |
|
25 char *value; |
|
26 } CGIVariable; |
|
27 |
|
28 typedef struct CGIVarTableStr { |
|
29 CGIVariable **variables; |
|
30 int numVars; |
|
31 int numAlloc; |
|
32 } CGIVarTable; |
|
33 |
|
34 typedef struct CertResponseInfoStr { |
|
35 CERTCertificate *cert; |
|
36 long certReqID; |
|
37 } CertResponseInfo; |
|
38 |
|
39 typedef struct ChallengeCreationInfoStr { |
|
40 long random; |
|
41 SECKEYPublicKey *pubKey; |
|
42 } ChallengeCreationInfo; |
|
43 |
|
44 char *missingVar = NULL; |
|
45 |
|
46 /* |
|
47 * Error values. |
|
48 */ |
|
49 typedef enum { |
|
50 NO_ERROR = 0, |
|
51 NSS_INIT_FAILED, |
|
52 AUTH_FAILED, |
|
53 REQ_CGI_VAR_NOT_PRESENT, |
|
54 CRMF_REQ_NOT_PRESENT, |
|
55 BAD_ASCII_FOR_REQ, |
|
56 CGI_VAR_MISSING, |
|
57 COULD_NOT_FIND_CA, |
|
58 COULD_NOT_DECODE_REQS, |
|
59 OUT_OF_MEMORY, |
|
60 ERROR_RETRIEVING_REQUEST_MSG, |
|
61 ERROR_RETRIEVING_CERT_REQUEST, |
|
62 ERROR_RETRIEVING_SUBJECT_FROM_REQ, |
|
63 ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ, |
|
64 ERROR_CREATING_NEW_CERTIFICATE, |
|
65 COULD_NOT_START_EXTENSIONS, |
|
66 ERROR_RETRIEVING_EXT_FROM_REQ, |
|
67 ERROR_ADDING_EXT_TO_CERT, |
|
68 ERROR_ENDING_EXTENSIONS, |
|
69 COULD_NOT_FIND_ISSUER_PRIVATE_KEY, |
|
70 UNSUPPORTED_SIGN_OPERATION_FOR_ISSUER, |
|
71 ERROR_SETTING_SIGN_ALG, |
|
72 ERROR_ENCODING_NEW_CERT, |
|
73 ERROR_SIGNING_NEW_CERT, |
|
74 ERROR_CREATING_CERT_REP_CONTENT, |
|
75 ERROR_CREATING_SINGLE_CERT_RESPONSE, |
|
76 ERROR_SETTING_CERT_RESPONSES, |
|
77 ERROR_CREATING_CA_LIST, |
|
78 ERROR_ADDING_ISSUER_TO_CA_LIST, |
|
79 ERROR_ENCODING_CERT_REP_CONTENT, |
|
80 NO_POP_FOR_REQUEST, |
|
81 UNSUPPORTED_POP, |
|
82 ERROR_RETRIEVING_POP_SIGN_KEY, |
|
83 ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY, |
|
84 ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY, |
|
85 DO_CHALLENGE_RESPONSE, |
|
86 ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT, |
|
87 ERROR_ENCODING_CERT_REQ_FOR_POP, |
|
88 ERROR_VERIFYING_SIGNATURE_POP, |
|
89 ERROR_RETRIEVING_PUB_KEY_FOR_CHALL, |
|
90 ERROR_CREATING_EMPTY_CHAL_CONTENT, |
|
91 ERROR_EXTRACTING_GEN_NAME_FROM_ISSUER, |
|
92 ERROR_SETTING_CHALLENGE, |
|
93 ERROR_ENCODING_CHALL, |
|
94 ERROR_CONVERTING_CHALL_TO_BASE64, |
|
95 ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN, |
|
96 ERROR_CREATING_KEY_RESP_FROM_DER, |
|
97 ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE, |
|
98 ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED, |
|
99 ERROR_GETTING_KEY_ENCIPHERMENT, |
|
100 ERROR_NO_POP_FOR_PRIVKEY, |
|
101 ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE |
|
102 } ErrorCode; |
|
103 |
|
104 const char * |
|
105 CGITableFindValue(CGIVarTable *varTable, const char *key); |
|
106 |
|
107 void |
|
108 spitOutHeaders(void) |
|
109 { |
|
110 printf("Content-type: text/html\n\n"); |
|
111 } |
|
112 |
|
113 void |
|
114 dumpRequest(CGIVarTable *varTable) |
|
115 { |
|
116 int i; |
|
117 CGIVariable *var; |
|
118 |
|
119 printf ("<table border=1 cellpadding=1 cellspacing=1 width=\"100%%\">\n"); |
|
120 printf ("<tr><td><b><center>Variable Name<center></b></td>" |
|
121 "<td><b><center>Value</center></b></td></tr>\n"); |
|
122 for (i=0; i<varTable->numVars; i++) { |
|
123 var = varTable->variables[i]; |
|
124 printf ("<tr><td><pre>%s</pre></td><td><pre>%s</pre></td></tr>\n", |
|
125 var->name, var->value); |
|
126 } |
|
127 printf("</table>\n"); |
|
128 } |
|
129 |
|
130 void |
|
131 echo_request(CGIVarTable *varTable) |
|
132 { |
|
133 spitOutHeaders(); |
|
134 printf("<html><head><title>CGI Echo Page</title></head>\n" |
|
135 "<body><h1>Got the following request</h1>\n"); |
|
136 dumpRequest(varTable); |
|
137 printf("</body></html>"); |
|
138 } |
|
139 |
|
140 void |
|
141 processVariable(CGIVariable *var) |
|
142 { |
|
143 char *plusSign, *percentSign; |
|
144 |
|
145 /*First look for all of the '+' and convert them to spaces */ |
|
146 plusSign = var->value; |
|
147 while ((plusSign=strchr(plusSign, '+')) != NULL) { |
|
148 *plusSign = ' '; |
|
149 } |
|
150 percentSign = var->value; |
|
151 while ((percentSign=strchr(percentSign, '%')) != NULL) { |
|
152 char string[3]; |
|
153 int value; |
|
154 |
|
155 string[0] = percentSign[1]; |
|
156 string[1] = percentSign[2]; |
|
157 string[2] = '\0'; |
|
158 |
|
159 sscanf(string,"%x", &value); |
|
160 *percentSign = (char)value; |
|
161 memmove(&percentSign[1], &percentSign[3], 1+strlen(&percentSign[3])); |
|
162 } |
|
163 } |
|
164 |
|
165 char * |
|
166 parseNextVariable(CGIVarTable *varTable, char *form_output) |
|
167 { |
|
168 char *ampersand, *equal; |
|
169 CGIVariable *var; |
|
170 |
|
171 if (varTable->numVars == varTable->numAlloc) { |
|
172 CGIVariable **newArr = realloc(varTable->variables, |
|
173 (varTable->numAlloc + DEFAULT_CGI_VARS)*sizeof(CGIVariable*)); |
|
174 if (newArr == NULL) { |
|
175 return NULL; |
|
176 } |
|
177 varTable->variables = newArr; |
|
178 varTable->numAlloc += DEFAULT_CGI_VARS; |
|
179 } |
|
180 equal = strchr(form_output, '='); |
|
181 if (equal == NULL) { |
|
182 return NULL; |
|
183 } |
|
184 ampersand = strchr(equal, '&'); |
|
185 if (ampersand == NULL) { |
|
186 return NULL; |
|
187 } |
|
188 equal[0] = '\0'; |
|
189 if (ampersand != NULL) { |
|
190 ampersand[0] = '\0'; |
|
191 } |
|
192 var = malloc(sizeof(CGIVariable)); |
|
193 var->name = form_output; |
|
194 var->value = &equal[1]; |
|
195 varTable->variables[varTable->numVars] = var; |
|
196 varTable->numVars++; |
|
197 processVariable(var); |
|
198 return (ampersand != NULL) ? &ersand[1] : NULL; |
|
199 } |
|
200 |
|
201 void |
|
202 ParseInputVariables(CGIVarTable *varTable, char *form_output) |
|
203 { |
|
204 varTable->variables = malloc(sizeof(CGIVariable*)*DEFAULT_CGI_VARS); |
|
205 varTable->numVars = 0; |
|
206 varTable->numAlloc = DEFAULT_CGI_VARS; |
|
207 while (form_output && form_output[0] != '\0') { |
|
208 form_output = parseNextVariable(varTable, form_output); |
|
209 } |
|
210 } |
|
211 |
|
212 const char * |
|
213 CGITableFindValue(CGIVarTable *varTable, const char *key) |
|
214 { |
|
215 const char *retVal = NULL; |
|
216 int i; |
|
217 |
|
218 for (i=0; i<varTable->numVars; i++) { |
|
219 if (strcmp(varTable->variables[i]->name, key) == 0) { |
|
220 retVal = varTable->variables[i]->value; |
|
221 break; |
|
222 } |
|
223 } |
|
224 return retVal; |
|
225 } |
|
226 |
|
227 char* |
|
228 passwordCallback(PK11SlotInfo *slot, PRBool retry, void *arg) |
|
229 { |
|
230 const char *passwd; |
|
231 if (retry) { |
|
232 return NULL; |
|
233 } |
|
234 passwd = CGITableFindValue((CGIVarTable*)arg, "dbPassword"); |
|
235 if (passwd == NULL) { |
|
236 return NULL; |
|
237 } |
|
238 return PORT_Strdup(passwd); |
|
239 } |
|
240 |
|
241 ErrorCode |
|
242 initNSS(CGIVarTable *varTable) |
|
243 { |
|
244 const char *nssDir; |
|
245 PK11SlotInfo *keySlot; |
|
246 SECStatus rv; |
|
247 |
|
248 nssDir = CGITableFindValue(varTable,"NSSDirectory"); |
|
249 if (nssDir == NULL) { |
|
250 missingVar = "NSSDirectory"; |
|
251 return REQ_CGI_VAR_NOT_PRESENT; |
|
252 } |
|
253 rv = NSS_Init(nssDir); |
|
254 if (rv != SECSuccess) { |
|
255 return NSS_INIT_FAILED; |
|
256 } |
|
257 PK11_SetPasswordFunc(passwordCallback); |
|
258 keySlot = PK11_GetInternalKeySlot(); |
|
259 rv = PK11_Authenticate(keySlot, PR_FALSE, varTable); |
|
260 PK11_FreeSlot(keySlot); |
|
261 if (rv != SECSuccess) { |
|
262 return AUTH_FAILED; |
|
263 } |
|
264 return NO_ERROR; |
|
265 } |
|
266 |
|
267 void |
|
268 dumpErrorMessage(ErrorCode errNum) |
|
269 { |
|
270 spitOutHeaders(); |
|
271 printf("<html><head><title>Error</title></head><body><h1>Error processing " |
|
272 "data</h1> Received the error %d<p>", errNum); |
|
273 if (errNum == REQ_CGI_VAR_NOT_PRESENT) { |
|
274 printf ("The missing variable is %s.", missingVar); |
|
275 } |
|
276 printf ("<i>More useful information here in the future.</i></body></html>"); |
|
277 } |
|
278 |
|
279 ErrorCode |
|
280 initOldCertReq(CERTCertificateRequest *oldCertReq, |
|
281 CERTName *subject, CERTSubjectPublicKeyInfo *spki) |
|
282 { |
|
283 PLArenaPool *poolp; |
|
284 |
|
285 poolp = oldCertReq->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
|
286 SEC_ASN1EncodeInteger(poolp, &oldCertReq->version, |
|
287 SEC_CERTIFICATE_VERSION_3); |
|
288 CERT_CopyName(poolp, &oldCertReq->subject, subject); |
|
289 SECKEY_CopySubjectPublicKeyInfo(poolp, &oldCertReq->subjectPublicKeyInfo, |
|
290 spki); |
|
291 oldCertReq->attributes = NULL; |
|
292 return NO_ERROR; |
|
293 } |
|
294 |
|
295 ErrorCode |
|
296 addExtensions(CERTCertificate *newCert, CRMFCertRequest *certReq) |
|
297 { |
|
298 int numExtensions, i; |
|
299 void *extHandle; |
|
300 ErrorCode rv = NO_ERROR; |
|
301 CRMFCertExtension *ext; |
|
302 SECStatus srv; |
|
303 |
|
304 numExtensions = CRMF_CertRequestGetNumberOfExtensions(certReq); |
|
305 if (numExtensions == 0) { |
|
306 /* No extensions to add */ |
|
307 return NO_ERROR; |
|
308 } |
|
309 extHandle = CERT_StartCertExtensions(newCert); |
|
310 if (extHandle == NULL) { |
|
311 rv = COULD_NOT_START_EXTENSIONS; |
|
312 goto loser; |
|
313 } |
|
314 for (i=0; i<numExtensions; i++) { |
|
315 ext = CRMF_CertRequestGetExtensionAtIndex(certReq, i); |
|
316 if (ext == NULL) { |
|
317 rv = ERROR_RETRIEVING_EXT_FROM_REQ; |
|
318 } |
|
319 srv = CERT_AddExtension(extHandle, CRMF_CertExtensionGetOidTag(ext), |
|
320 CRMF_CertExtensionGetValue(ext), |
|
321 CRMF_CertExtensionGetIsCritical(ext), PR_FALSE); |
|
322 if (srv != SECSuccess) { |
|
323 rv = ERROR_ADDING_EXT_TO_CERT; |
|
324 } |
|
325 } |
|
326 srv = CERT_FinishExtensions(extHandle); |
|
327 if (srv != SECSuccess) { |
|
328 rv = ERROR_ENDING_EXTENSIONS; |
|
329 goto loser; |
|
330 } |
|
331 return NO_ERROR; |
|
332 loser: |
|
333 return rv; |
|
334 } |
|
335 |
|
336 void |
|
337 writeOutItem(const char *filePath, SECItem *der) |
|
338 { |
|
339 PRFileDesc *outfile; |
|
340 |
|
341 outfile = PR_Open (filePath, |
|
342 PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, |
|
343 0666); |
|
344 PR_Write(outfile, der->data, der->len); |
|
345 PR_Close(outfile); |
|
346 |
|
347 } |
|
348 |
|
349 ErrorCode |
|
350 createNewCert(CERTCertificate**issuedCert,CERTCertificateRequest *oldCertReq, |
|
351 CRMFCertReqMsg *currReq, CRMFCertRequest *certReq, |
|
352 CERTCertificate *issuerCert, CGIVarTable *varTable) |
|
353 { |
|
354 CERTCertificate *newCert = NULL; |
|
355 CERTValidity *validity; |
|
356 PRExplodedTime printableTime; |
|
357 PRTime now, after; |
|
358 ErrorCode rv=NO_ERROR; |
|
359 SECKEYPrivateKey *issuerPrivKey; |
|
360 SECItem derCert = { 0 }; |
|
361 SECOidTag signTag; |
|
362 SECStatus srv; |
|
363 long version; |
|
364 |
|
365 now = PR_Now(); |
|
366 PR_ExplodeTime(now, PR_GMTParameters, &printableTime); |
|
367 printableTime.tm_month += 9; |
|
368 after = PR_ImplodeTime(&printableTime); |
|
369 validity = CERT_CreateValidity(now, after); |
|
370 newCert = *issuedCert = |
|
371 CERT_CreateCertificate(rand(), &(issuerCert->subject), validity, |
|
372 oldCertReq); |
|
373 if (newCert == NULL) { |
|
374 rv = ERROR_CREATING_NEW_CERTIFICATE; |
|
375 goto loser; |
|
376 } |
|
377 rv = addExtensions(newCert, certReq); |
|
378 if (rv != NO_ERROR) { |
|
379 goto loser; |
|
380 } |
|
381 issuerPrivKey = PK11_FindKeyByAnyCert(issuerCert, varTable); |
|
382 if (issuerPrivKey == NULL) { |
|
383 rv = COULD_NOT_FIND_ISSUER_PRIVATE_KEY; |
|
384 } |
|
385 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 |
|
430 void |
|
431 formatCMMFResponse(char *nickname, char *base64Response) |
|
432 { |
|
433 char *currLine, *nextLine; |
|
434 |
|
435 printf("var retVal = crypto.importUserCertificates(\"%s\",\n", nickname); |
|
436 currLine = base64Response; |
|
437 while (1) { |
|
438 nextLine = strchr(currLine, '\n'); |
|
439 if (nextLine == NULL) { |
|
440 /* print out the last line here. */ |
|
441 printf ("\"%s\",\n", currLine); |
|
442 break; |
|
443 } |
|
444 nextLine[0] = '\0'; |
|
445 printf("\"%s\\n\"+\n", currLine); |
|
446 currLine = nextLine+1; |
|
447 } |
|
448 printf("true);\n" |
|
449 "if(retVal == '') {\n" |
|
450 "\tdocument.write(\"<h1>New Certificate Successfully Imported.</h1>\");\n" |
|
451 "} else {\n" |
|
452 "\tdocument.write(\"<h2>Unable to import New Certificate</h2>\");\n" |
|
453 "\tdocument.write(\"crypto.importUserCertificates returned <b>\");\n" |
|
454 "\tdocument.write(retVal);\n" |
|
455 "\tdocument.write(\"</b>\");\n" |
|
456 "}\n"); |
|
457 } |
|
458 |
|
459 void |
|
460 spitOutCMMFResponse(char *nickname, char *base64Response) |
|
461 { |
|
462 spitOutHeaders(); |
|
463 printf("<html>\n<head>\n<title>CMMF Resonse Page</title>\n</head>\n\n" |
|
464 "<body><h1>CMMF Response Page</h1>\n" |
|
465 "<script language=\"JavaScript\">\n" |
|
466 "<!--\n"); |
|
467 formatCMMFResponse(nickname, base64Response); |
|
468 printf("// -->\n" |
|
469 "</script>\n</body>\n</html>"); |
|
470 } |
|
471 |
|
472 char* |
|
473 getNickname(CERTCertificate *cert) |
|
474 { |
|
475 char *nickname; |
|
476 |
|
477 if (cert->nickname != NULL) { |
|
478 return cert->nickname; |
|
479 } |
|
480 nickname = CERT_GetCommonName(&cert->subject); |
|
481 if (nickname != NULL) { |
|
482 return nickname; |
|
483 } |
|
484 return CERT_NameToAscii(&cert->subject); |
|
485 } |
|
486 |
|
487 ErrorCode |
|
488 createCMMFResponse(CertResponseInfo *issuedCerts, int numCerts, |
|
489 CERTCertificate *issuerCert, char **base64der) |
|
490 { |
|
491 CMMFCertRepContent *certRepContent=NULL; |
|
492 ErrorCode rv = NO_ERROR; |
|
493 CMMFCertResponse **responses, *currResponse; |
|
494 CERTCertList *caList; |
|
495 int i; |
|
496 SECStatus srv; |
|
497 PLArenaPool *poolp; |
|
498 SECItem *der; |
|
499 |
|
500 certRepContent = CMMF_CreateCertRepContent(); |
|
501 if (certRepContent == NULL) { |
|
502 rv = ERROR_CREATING_CERT_REP_CONTENT; |
|
503 goto loser; |
|
504 } |
|
505 responses = PORT_NewArray(CMMFCertResponse*, numCerts); |
|
506 if (responses == NULL) { |
|
507 rv = OUT_OF_MEMORY; |
|
508 goto loser; |
|
509 } |
|
510 for (i=0; i<numCerts;i++) { |
|
511 responses[i] = currResponse = |
|
512 CMMF_CreateCertResponse(issuedCerts[i].certReqID); |
|
513 if (currResponse == NULL) { |
|
514 rv = ERROR_CREATING_SINGLE_CERT_RESPONSE; |
|
515 goto loser; |
|
516 } |
|
517 CMMF_CertResponseSetPKIStatusInfoStatus(currResponse, cmmfGranted); |
|
518 CMMF_CertResponseSetCertificate(currResponse, issuedCerts[i].cert); |
|
519 } |
|
520 srv = CMMF_CertRepContentSetCertResponses(certRepContent, responses, |
|
521 numCerts); |
|
522 if (srv != SECSuccess) { |
|
523 rv = ERROR_SETTING_CERT_RESPONSES; |
|
524 goto loser; |
|
525 } |
|
526 caList = CERT_NewCertList(); |
|
527 if (caList == NULL) { |
|
528 rv = ERROR_CREATING_CA_LIST; |
|
529 goto loser; |
|
530 } |
|
531 srv = CERT_AddCertToListTail(caList, issuerCert); |
|
532 if (srv != SECSuccess) { |
|
533 rv = ERROR_ADDING_ISSUER_TO_CA_LIST; |
|
534 goto loser; |
|
535 } |
|
536 srv = CMMF_CertRepContentSetCAPubs(certRepContent, caList); |
|
537 CERT_DestroyCertList(caList); |
|
538 poolp = PORT_NewArena(1024); |
|
539 der = SEC_ASN1EncodeItem(poolp, NULL, certRepContent, |
|
540 CMMFCertRepContentTemplate); |
|
541 if (der == NULL) { |
|
542 rv = ERROR_ENCODING_CERT_REP_CONTENT; |
|
543 goto loser; |
|
544 } |
|
545 #ifdef WRITE_OUT_RESPONSE |
|
546 writeOutItem("CertRepContent.der", der); |
|
547 #endif |
|
548 *base64der = BTOA_DataToAscii(der->data, der->len); |
|
549 return NO_ERROR; |
|
550 loser: |
|
551 return rv; |
|
552 } |
|
553 |
|
554 ErrorCode |
|
555 issueCerts(CertResponseInfo *issuedCerts, int numCerts, |
|
556 CERTCertificate *issuerCert) |
|
557 { |
|
558 ErrorCode rv; |
|
559 char *base64Response; |
|
560 |
|
561 rv = createCMMFResponse(issuedCerts, numCerts, issuerCert, &base64Response); |
|
562 if (rv != NO_ERROR) { |
|
563 goto loser; |
|
564 } |
|
565 spitOutCMMFResponse(getNickname(issuedCerts[0].cert),base64Response); |
|
566 return NO_ERROR; |
|
567 loser: |
|
568 return rv; |
|
569 } |
|
570 |
|
571 ErrorCode |
|
572 verifySignature(CGIVarTable *varTable, CRMFCertReqMsg *currReq, |
|
573 CRMFCertRequest *certReq, CERTCertificate *newCert) |
|
574 { |
|
575 SECStatus srv; |
|
576 ErrorCode rv = NO_ERROR; |
|
577 CRMFPOPOSigningKey *signKey = NULL; |
|
578 SECAlgorithmID *algID = NULL; |
|
579 SECItem *signature = NULL; |
|
580 SECKEYPublicKey *pubKey = NULL; |
|
581 SECItem *reqDER = NULL; |
|
582 |
|
583 srv = CRMF_CertReqMsgGetPOPOSigningKey(currReq, &signKey); |
|
584 if (srv != SECSuccess || signKey == NULL) { |
|
585 rv = ERROR_RETRIEVING_POP_SIGN_KEY; |
|
586 goto loser; |
|
587 } |
|
588 algID = CRMF_POPOSigningKeyGetAlgID(signKey); |
|
589 if (algID == NULL) { |
|
590 rv = ERROR_RETRIEVING_ALG_ID_FROM_SIGN_KEY; |
|
591 goto loser; |
|
592 } |
|
593 signature = CRMF_POPOSigningKeyGetSignature(signKey); |
|
594 if (signature == NULL) { |
|
595 rv = ERROR_RETRIEVING_SIGNATURE_FROM_POP_SIGN_KEY; |
|
596 goto loser; |
|
597 } |
|
598 /* Make the length the number of bytes instead of bits */ |
|
599 signature->len = (signature->len+7)/8; |
|
600 pubKey = CERT_ExtractPublicKey(newCert); |
|
601 if (pubKey == NULL) { |
|
602 rv = ERROR_RETRIEVING_PUB_KEY_FROM_NEW_CERT; |
|
603 goto loser; |
|
604 } |
|
605 reqDER = SEC_ASN1EncodeItem(NULL, NULL, certReq, CRMFCertRequestTemplate); |
|
606 if (reqDER == NULL) { |
|
607 rv = ERROR_ENCODING_CERT_REQ_FOR_POP; |
|
608 goto loser; |
|
609 } |
|
610 srv = VFY_VerifyDataWithAlgorithmID(reqDER->data, reqDER->len, pubKey, |
|
611 signature, &algID->algorithm, NULL, varTable); |
|
612 if (srv != SECSuccess) { |
|
613 rv = ERROR_VERIFYING_SIGNATURE_POP; |
|
614 goto loser; |
|
615 } |
|
616 /* Fall thru in successfull case. */ |
|
617 loser: |
|
618 if (pubKey != NULL) { |
|
619 SECKEY_DestroyPublicKey(pubKey); |
|
620 } |
|
621 if (reqDER != NULL) { |
|
622 SECITEM_FreeItem(reqDER, PR_TRUE); |
|
623 } |
|
624 if (signature != NULL) { |
|
625 SECITEM_FreeItem(signature, PR_TRUE); |
|
626 } |
|
627 if (algID != NULL) { |
|
628 SECOID_DestroyAlgorithmID(algID, PR_TRUE); |
|
629 } |
|
630 if (signKey != NULL) { |
|
631 CRMF_DestroyPOPOSigningKey(signKey); |
|
632 } |
|
633 return rv; |
|
634 } |
|
635 |
|
636 ErrorCode |
|
637 doChallengeResponse(CGIVarTable *varTable, CRMFCertReqMsg *currReq, |
|
638 CRMFCertRequest *certReq, CERTCertificate *newCert, |
|
639 ChallengeCreationInfo *challs, int *numChall) |
|
640 { |
|
641 CRMFPOPOPrivKey *privKey = NULL; |
|
642 CRMFPOPOPrivKeyChoice privKeyChoice; |
|
643 SECStatus srv; |
|
644 ErrorCode rv = NO_ERROR; |
|
645 |
|
646 srv = CRMF_CertReqMsgGetPOPKeyEncipherment(currReq, &privKey); |
|
647 if (srv != SECSuccess || privKey == NULL) { |
|
648 rv = ERROR_GETTING_KEY_ENCIPHERMENT; |
|
649 goto loser; |
|
650 } |
|
651 privKeyChoice = CRMF_POPOPrivKeyGetChoice(privKey); |
|
652 CRMF_DestroyPOPOPrivKey(privKey); |
|
653 switch (privKeyChoice) { |
|
654 case crmfSubsequentMessage: |
|
655 challs = &challs[*numChall]; |
|
656 challs->random = rand(); |
|
657 challs->pubKey = CERT_ExtractPublicKey(newCert); |
|
658 if (challs->pubKey == NULL) { |
|
659 rv = ERROR_RETRIEVING_PUB_KEY_FOR_CHALL; |
|
660 goto loser; |
|
661 } |
|
662 (*numChall)++; |
|
663 rv = DO_CHALLENGE_RESPONSE; |
|
664 break; |
|
665 case crmfThisMessage: |
|
666 /* There'd better be a PKIArchiveControl in this message */ |
|
667 if (!CRMF_CertRequestIsControlPresent(certReq, |
|
668 crmfPKIArchiveOptionsControl)) { |
|
669 rv = ERROR_NO_POP_FOR_PRIVKEY; |
|
670 goto loser; |
|
671 } |
|
672 break; |
|
673 default: |
|
674 rv = ERROR_UNSUPPORTED_POPOPRIVKEY_TYPE; |
|
675 goto loser; |
|
676 } |
|
677 loser: |
|
678 return rv; |
|
679 } |
|
680 |
|
681 ErrorCode |
|
682 doProofOfPossession(CGIVarTable *varTable, CRMFCertReqMsg *currReq, |
|
683 CRMFCertRequest *certReq, CERTCertificate *newCert, |
|
684 ChallengeCreationInfo *challs, int *numChall) |
|
685 { |
|
686 CRMFPOPChoice popChoice; |
|
687 ErrorCode rv = NO_ERROR; |
|
688 |
|
689 popChoice = CRMF_CertReqMsgGetPOPType(currReq); |
|
690 if (popChoice == crmfNoPOPChoice) { |
|
691 rv = NO_POP_FOR_REQUEST; |
|
692 goto loser; |
|
693 } |
|
694 switch (popChoice) { |
|
695 case crmfSignature: |
|
696 rv = verifySignature(varTable, currReq, certReq, newCert); |
|
697 break; |
|
698 case crmfKeyEncipherment: |
|
699 rv = doChallengeResponse(varTable, currReq, certReq, newCert, |
|
700 challs, numChall); |
|
701 break; |
|
702 case crmfRAVerified: |
|
703 case crmfKeyAgreement: |
|
704 default: |
|
705 rv = UNSUPPORTED_POP; |
|
706 goto loser; |
|
707 } |
|
708 loser: |
|
709 return rv; |
|
710 } |
|
711 |
|
712 void |
|
713 convertB64ToJS(char *base64) |
|
714 { |
|
715 int i; |
|
716 |
|
717 for (i=0; base64[i] != '\0'; i++) { |
|
718 if (base64[i] == '\n') { |
|
719 printf ("\\n"); |
|
720 }else { |
|
721 printf ("%c", base64[i]); |
|
722 } |
|
723 } |
|
724 } |
|
725 |
|
726 void |
|
727 formatChallenge(char *chall64, char *certRepContentDER, |
|
728 ChallengeCreationInfo *challInfo, int numChalls) |
|
729 { |
|
730 printf ("function respondToChallenge() {\n" |
|
731 " var chalForm = document.chalForm;\n\n" |
|
732 " chalForm.CertRepContent.value = '"); |
|
733 convertB64ToJS(certRepContentDER); |
|
734 printf ("';\n" |
|
735 " chalForm.ChallResponse.value = crypto.popChallengeResponse('"); |
|
736 convertB64ToJS(chall64); |
|
737 printf("');\n" |
|
738 " chalForm.submit();\n" |
|
739 "}\n"); |
|
740 |
|
741 } |
|
742 |
|
743 void |
|
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 |
|
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 |
|
842 ErrorCode |
|
843 processRequest(CGIVarTable *varTable) |
|
844 { |
|
845 CERTCertDBHandle *certdb; |
|
846 SECKEYKeyDBHandle *keydb; |
|
847 CRMFCertReqMessages *certReqs = NULL; |
|
848 const char *crmfReq; |
|
849 const char *caNickname; |
|
850 CERTCertificate *caCert = NULL; |
|
851 CertResponseInfo *issuedCerts = NULL; |
|
852 CERTSubjectPublicKeyInfo spki = { 0 }; |
|
853 ErrorCode rv=NO_ERROR; |
|
854 PRBool doChallengeResponse = PR_FALSE; |
|
855 SECItem der = { 0 }; |
|
856 SECStatus srv; |
|
857 CERTCertificateRequest oldCertReq = { 0 }; |
|
858 CRMFCertReqMsg **reqMsgs = NULL,*currReq = NULL; |
|
859 CRMFCertRequest **reqs = NULL, *certReq = NULL; |
|
860 CERTName subject = { 0 }; |
|
861 int numReqs,i; |
|
862 ChallengeCreationInfo *challInfo=NULL; |
|
863 int numChalls = 0; |
|
864 |
|
865 certdb = CERT_GetDefaultCertDB(); |
|
866 keydb = SECKEY_GetDefaultKeyDB(); |
|
867 crmfReq = CGITableFindValue(varTable, "CRMFRequest"); |
|
868 if (crmfReq == NULL) { |
|
869 rv = CGI_VAR_MISSING; |
|
870 missingVar = "CRMFRequest"; |
|
871 goto loser; |
|
872 } |
|
873 caNickname = CGITableFindValue(varTable, "CANickname"); |
|
874 if (caNickname == NULL) { |
|
875 rv = CGI_VAR_MISSING; |
|
876 missingVar = "CANickname"; |
|
877 goto loser; |
|
878 } |
|
879 caCert = CERT_FindCertByNickname(certdb, caNickname); |
|
880 if (caCert == NULL) { |
|
881 rv = COULD_NOT_FIND_CA; |
|
882 goto loser; |
|
883 } |
|
884 srv = ATOB_ConvertAsciiToItem(&der, crmfReq); |
|
885 if (srv != SECSuccess) { |
|
886 rv = BAD_ASCII_FOR_REQ; |
|
887 goto loser; |
|
888 } |
|
889 certReqs = CRMF_CreateCertReqMessagesFromDER(der.data, der.len); |
|
890 SECITEM_FreeItem(&der, PR_FALSE); |
|
891 if (certReqs == NULL) { |
|
892 rv = COULD_NOT_DECODE_REQS; |
|
893 goto loser; |
|
894 } |
|
895 numReqs = CRMF_CertReqMessagesGetNumMessages(certReqs); |
|
896 issuedCerts = PORT_ZNewArray(CertResponseInfo, numReqs); |
|
897 challInfo = PORT_ZNewArray(ChallengeCreationInfo, numReqs); |
|
898 if (issuedCerts == NULL || challInfo == NULL) { |
|
899 rv = OUT_OF_MEMORY; |
|
900 goto loser; |
|
901 } |
|
902 reqMsgs = PORT_ZNewArray(CRMFCertReqMsg*, numReqs); |
|
903 reqs = PORT_ZNewArray(CRMFCertRequest*, numReqs); |
|
904 if (reqMsgs == NULL || reqs == NULL) { |
|
905 rv = OUT_OF_MEMORY; |
|
906 goto loser; |
|
907 } |
|
908 for (i=0; i<numReqs; i++) { |
|
909 currReq = reqMsgs[i] = |
|
910 CRMF_CertReqMessagesGetCertReqMsgAtIndex(certReqs, i); |
|
911 if (currReq == NULL) { |
|
912 rv = ERROR_RETRIEVING_REQUEST_MSG; |
|
913 goto loser; |
|
914 } |
|
915 certReq = reqs[i] = CRMF_CertReqMsgGetCertRequest(currReq); |
|
916 if (certReq == NULL) { |
|
917 rv = ERROR_RETRIEVING_CERT_REQUEST; |
|
918 goto loser; |
|
919 } |
|
920 srv = CRMF_CertRequestGetCertTemplateSubject(certReq, &subject); |
|
921 if (srv != SECSuccess) { |
|
922 rv = ERROR_RETRIEVING_SUBJECT_FROM_REQ; |
|
923 goto loser; |
|
924 } |
|
925 srv = CRMF_CertRequestGetCertTemplatePublicKey(certReq, &spki); |
|
926 if (srv != SECSuccess) { |
|
927 rv = ERROR_RETRIEVING_PUBLIC_KEY_FROM_REQ; |
|
928 goto loser; |
|
929 } |
|
930 rv = initOldCertReq(&oldCertReq, &subject, &spki); |
|
931 if (rv != NO_ERROR) { |
|
932 goto loser; |
|
933 } |
|
934 rv = createNewCert(&issuedCerts[i].cert, &oldCertReq, currReq, certReq, |
|
935 caCert, varTable); |
|
936 if (rv != NO_ERROR) { |
|
937 goto loser; |
|
938 } |
|
939 rv = doProofOfPossession(varTable, currReq, certReq, issuedCerts[i].cert, |
|
940 challInfo, &numChalls); |
|
941 if (rv != NO_ERROR) { |
|
942 if (rv == DO_CHALLENGE_RESPONSE) { |
|
943 doChallengeResponse = PR_TRUE; |
|
944 } else { |
|
945 goto loser; |
|
946 } |
|
947 } |
|
948 CRMF_CertReqMsgGetID(currReq, &issuedCerts[i].certReqID); |
|
949 CRMF_DestroyCertReqMsg(currReq); |
|
950 CRMF_DestroyCertRequest(certReq); |
|
951 } |
|
952 if (doChallengeResponse) { |
|
953 rv = issueChallenge(issuedCerts, numReqs, challInfo, numChalls, caCert, |
|
954 varTable); |
|
955 } else { |
|
956 rv = issueCerts(issuedCerts, numReqs, caCert); |
|
957 } |
|
958 loser: |
|
959 if (certReqs != NULL) { |
|
960 CRMF_DestroyCertReqMessages(certReqs); |
|
961 } |
|
962 return rv; |
|
963 } |
|
964 |
|
965 ErrorCode |
|
966 processChallengeResponse(CGIVarTable *varTable, const char *certRepContent) |
|
967 { |
|
968 SECItem binDER = { 0 }; |
|
969 SECStatus srv; |
|
970 ErrorCode rv = NO_ERROR; |
|
971 const char *clientResponse; |
|
972 const char *formChalValue; |
|
973 const char *nickname; |
|
974 CMMFPOPODecKeyRespContent *respContent = NULL; |
|
975 int numResponses,i; |
|
976 long curResponse, expectedResponse; |
|
977 char cgiChalVar[10]; |
|
978 #ifdef WRITE_OUT_RESPONSE |
|
979 SECItem certRepBinDER = { 0 }; |
|
980 |
|
981 ATOB_ConvertAsciiToItem(&certRepBinDER, certRepContent); |
|
982 writeOutItem("challCertRepContent.der", &certRepBinDER); |
|
983 PORT_Free(certRepBinDER.data); |
|
984 #endif |
|
985 clientResponse = CGITableFindValue(varTable, "ChallResponse"); |
|
986 if (clientResponse == NULL) { |
|
987 rv = REQ_CGI_VAR_NOT_PRESENT; |
|
988 missingVar = "ChallResponse"; |
|
989 goto loser; |
|
990 } |
|
991 srv = ATOB_ConvertAsciiToItem(&binDER, clientResponse); |
|
992 if (srv != SECSuccess) { |
|
993 rv = ERROR_CONVERTING_RESP_FROM_CHALL_TO_BIN; |
|
994 goto loser; |
|
995 } |
|
996 respContent = CMMF_CreatePOPODecKeyRespContentFromDER(binDER.data, |
|
997 binDER.len); |
|
998 SECITEM_FreeItem(&binDER, PR_FALSE); |
|
999 binDER.data = NULL; |
|
1000 if (respContent == NULL) { |
|
1001 rv = ERROR_CREATING_KEY_RESP_FROM_DER; |
|
1002 goto loser; |
|
1003 } |
|
1004 numResponses = CMMF_POPODecKeyRespContentGetNumResponses(respContent); |
|
1005 for (i=0;i<numResponses;i++){ |
|
1006 srv = CMMF_POPODecKeyRespContentGetResponse(respContent,i,&curResponse); |
|
1007 if (srv != SECSuccess) { |
|
1008 rv = ERROR_RETRIEVING_CLIENT_RESPONSE_TO_CHALLENGE; |
|
1009 goto loser; |
|
1010 } |
|
1011 sprintf(cgiChalVar, "chal%d", i+1); |
|
1012 formChalValue = CGITableFindValue(varTable, cgiChalVar); |
|
1013 if (formChalValue == NULL) { |
|
1014 rv = REQ_CGI_VAR_NOT_PRESENT; |
|
1015 missingVar = strdup(cgiChalVar); |
|
1016 goto loser; |
|
1017 } |
|
1018 sscanf(formChalValue, "%ld", &expectedResponse); |
|
1019 if (expectedResponse != curResponse) { |
|
1020 rv = ERROR_RETURNED_CHALL_NOT_VALUE_EXPECTED; |
|
1021 goto loser; |
|
1022 } |
|
1023 } |
|
1024 nickname = CGITableFindValue(varTable, "nickname"); |
|
1025 if (nickname == NULL) { |
|
1026 rv = REQ_CGI_VAR_NOT_PRESENT; |
|
1027 missingVar = "nickname"; |
|
1028 goto loser; |
|
1029 } |
|
1030 spitOutCMMFResponse(nickname, certRepContent); |
|
1031 loser: |
|
1032 if (respContent != NULL) { |
|
1033 CMMF_DestroyPOPODecKeyRespContent(respContent); |
|
1034 } |
|
1035 return rv; |
|
1036 } |
|
1037 |
|
1038 int |
|
1039 main() |
|
1040 { |
|
1041 char *form_output = NULL; |
|
1042 int form_output_len, form_output_used; |
|
1043 CGIVarTable varTable = { 0 }; |
|
1044 ErrorCode errNum = 0; |
|
1045 char *certRepContent; |
|
1046 |
|
1047 #ifdef ATTACH_CGI |
|
1048 /* Put an ifinite loop in here so I can attach to |
|
1049 * the process after the process is spun off |
|
1050 */ |
|
1051 { int stupid = 1; |
|
1052 while (stupid); |
|
1053 } |
|
1054 #endif |
|
1055 |
|
1056 form_output_used = 0; |
|
1057 srand(time(NULL)); |
|
1058 while (feof(stdin) == 0) { |
|
1059 if (form_output == NULL) { |
|
1060 form_output = PORT_NewArray(char, DEFAULT_ALLOC_SIZE+1); |
|
1061 form_output_len = DEFAULT_ALLOC_SIZE; |
|
1062 } else if ((form_output_used + DEFAULT_ALLOC_SIZE) >= form_output_len) { |
|
1063 form_output_len += DEFAULT_ALLOC_SIZE; |
|
1064 form_output = PORT_Realloc(form_output, form_output_len+1); |
|
1065 } |
|
1066 form_output_used += fread(&form_output[form_output_used], sizeof(char), |
|
1067 DEFAULT_ALLOC_SIZE, stdin); |
|
1068 } |
|
1069 ParseInputVariables(&varTable, form_output); |
|
1070 certRepContent = CGITableFindValue(&varTable, "CertRepContent"); |
|
1071 if (certRepContent == NULL) { |
|
1072 errNum = initNSS(&varTable); |
|
1073 if (errNum != 0) { |
|
1074 goto loser; |
|
1075 } |
|
1076 errNum = processRequest(&varTable); |
|
1077 } else { |
|
1078 errNum = processChallengeResponse(&varTable, certRepContent); |
|
1079 } |
|
1080 if (errNum != NO_ERROR) { |
|
1081 goto loser; |
|
1082 } |
|
1083 goto done; |
|
1084 loser: |
|
1085 dumpErrorMessage(errNum); |
|
1086 done: |
|
1087 free (form_output); |
|
1088 return 0; |
|
1089 } |
|
1090 |