Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /*
6 * Test program for client-side OCSP.
7 */
9 #include "secutil.h"
10 #include "nspr.h"
11 #include "plgetopt.h"
12 #include "nss.h"
13 #include "cert.h"
14 #include "ocsp.h"
15 #include "xconst.h" /*
16 * XXX internal header file; needed to get at
17 * cert_DecodeAuthInfoAccessExtension -- would be
18 * nice to not need this, but that would require
19 * better/different APIs.
20 */
22 #ifndef NO_PP /*
23 * Compile with this every once in a while to be
24 * sure that no dependencies on it get added
25 * outside of the pretty-printing routines.
26 */
27 #include "ocspti.h" /* internals for pretty-printing routines *only* */
28 #endif /* NO_PP */
30 #if defined(_WIN32)
31 #include "fcntl.h"
32 #include "io.h"
33 #endif
35 #define DEFAULT_DB_DIR "~/.netscape"
37 /* global */
38 char *program_name;
41 static void
42 synopsis (char *program_name)
43 {
44 PRFileDesc *pr_stderr;
46 pr_stderr = PR_STDERR;
47 PR_fprintf (pr_stderr, "Usage:");
48 PR_fprintf (pr_stderr,
49 "\t%s -p [-d <dir>]\n",
50 program_name);
51 PR_fprintf (pr_stderr,
52 "\t%s -P [-d <dir>]\n",
53 program_name);
54 PR_fprintf (pr_stderr,
55 "\t%s -r <name> [-a] [-L] [-s <name>] [-d <dir>]\n",
56 program_name);
57 PR_fprintf (pr_stderr,
58 "\t%s -R <name> [-a] [-l <location>] [-s <name>] [-d <dir>]\n",
59 program_name);
60 PR_fprintf (pr_stderr,
61 "\t%s -S <name> [-a] [-l <location> -t <name>]\n",
62 program_name);
63 PR_fprintf (pr_stderr,
64 "\t\t [-s <name>] [-w <time>] [-d <dir>]\n");
65 PR_fprintf (pr_stderr,
66 "\t%s -V <name> [-a] -u <usage> [-l <location> -t <name>]\n",
67 program_name);
68 PR_fprintf (pr_stderr,
69 "\t\t [-s <name>] [-w <time>] [-d <dir>]\n");
70 }
73 static void
74 short_usage (char *program_name)
75 {
76 PR_fprintf (PR_STDERR,
77 "Type %s -H for more detailed descriptions\n",
78 program_name);
79 synopsis (program_name);
80 }
83 static void
84 long_usage (char *program_name)
85 {
86 PRFileDesc *pr_stderr;
88 pr_stderr = PR_STDERR;
89 synopsis (program_name);
90 PR_fprintf (pr_stderr, "\nCommands (must specify exactly one):\n");
91 PR_fprintf (pr_stderr,
92 " %-13s Pretty-print a binary request read from stdin\n",
93 "-p");
94 PR_fprintf (pr_stderr,
95 " %-13s Pretty-print a binary response read from stdin\n",
96 "-P");
97 PR_fprintf (pr_stderr,
98 " %-13s Create a request for cert \"nickname\" on stdout\n",
99 "-r nickname");
100 PR_fprintf (pr_stderr,
101 " %-13s Get response for cert \"nickname\", dump to stdout\n",
102 "-R nickname");
103 PR_fprintf (pr_stderr,
104 " %-13s Get status for cert \"nickname\"\n",
105 "-S nickname");
106 PR_fprintf (pr_stderr,
107 " %-13s Fully verify cert \"nickname\", w/ status check\n",
108 "-V nickname");
109 PR_fprintf (pr_stderr,
110 "\n %-10s also can be the name of the file with DER or\n"
111 " %-13s PEM(use -a option) cert encoding\n", "nickname", "");
112 PR_fprintf (pr_stderr, "Options:\n");
113 PR_fprintf (pr_stderr,
114 " %-13s Decode input cert from PEM format. DER is default\n",
115 "-a");
116 PR_fprintf (pr_stderr,
117 " %-13s Add the service locator extension to the request\n",
118 "-L");
119 PR_fprintf (pr_stderr,
120 " %-13s Find security databases in \"dbdir\" (default %s)\n",
121 "-d dbdir", DEFAULT_DB_DIR);
122 PR_fprintf (pr_stderr,
123 " %-13s Use \"location\" as URL of responder\n",
124 "-l location");
125 PR_fprintf (pr_stderr,
126 " %-13s Trust cert \"nickname\" as response signer\n",
127 "-t nickname");
128 PR_fprintf (pr_stderr,
129 " %-13s Sign requests with cert \"nickname\"\n",
130 "-s nickname");
131 PR_fprintf (pr_stderr,
132 " %-13s Type of certificate usage for verification:\n",
133 "-u usage");
134 PR_fprintf (pr_stderr,
135 "%-17s c SSL Client\n", "");
136 PR_fprintf (pr_stderr,
137 "%-17s s SSL Server\n", "");
138 PR_fprintf (pr_stderr,
139 "%-17s e Email Recipient\n", "");
140 PR_fprintf (pr_stderr,
141 "%-17s E Email Signer\n", "");
142 PR_fprintf (pr_stderr,
143 "%-17s S Object Signer\n", "");
144 PR_fprintf (pr_stderr,
145 "%-17s C CA\n", "");
146 PR_fprintf (pr_stderr,
147 " %-13s Validity time (default current time), one of:\n",
148 "-w time");
149 PR_fprintf (pr_stderr,
150 "%-17s %-25s (GMT)\n", "", "YYMMDDhhmm[ss]Z");
151 PR_fprintf (pr_stderr,
152 "%-17s %-25s (later than GMT)\n", "", "YYMMDDhhmm[ss]+hhmm");
153 PR_fprintf (pr_stderr,
154 "%-17s %-25s (earlier than GMT)\n", "", "YYMMDDhhmm[ss]-hhmm");
155 }
157 #if defined(WIN32)
158 /* We're going to write binary data to stdout, or read binary from stdin.
159 * We must put stdout or stdin into O_BINARY mode or else
160 outgoing \n's will become \r\n's, and incoming \r\n's will become \n's.
161 */
162 static SECStatus
163 make_file_binary(FILE * binfile)
164 {
165 int smrv = _setmode(_fileno(binfile), _O_BINARY);
166 if (smrv == -1) {
167 fprintf(stderr, "%s: Cannot change stdout to binary mode.\n",
168 program_name);
169 }
170 return smrv;
171 }
172 #define MAKE_FILE_BINARY make_file_binary
173 #else
174 #define MAKE_FILE_BINARY(file)
175 #endif
177 /*
178 * XXX This is a generic function that would probably make a good
179 * replacement for SECU_DER_Read (which is not at all specific to DER,
180 * despite its name), but that requires fixing all of the tools...
181 * Still, it should be done, whenenver I/somebody has the time.
182 * (Also, consider whether this actually belongs in the security
183 * library itself, not just in the command library.)
184 *
185 * This function takes an open file (a PRFileDesc *) and reads the
186 * entire file into a SECItem. (Obviously, the file is intended to
187 * be small enough that such a thing is advisable.) Both the SECItem
188 * and the buffer it points to are allocated from the heap; the caller
189 * is expected to free them. ("SECITEM_FreeItem(item, PR_TRUE)")
190 */
191 static SECItem *
192 read_file_into_item (PRFileDesc *in_file, SECItemType si_type)
193 {
194 PRStatus prv;
195 SECItem *item;
196 PRFileInfo file_info;
197 PRInt32 bytes_read;
199 prv = PR_GetOpenFileInfo (in_file, &file_info);
200 if (prv != PR_SUCCESS)
201 return NULL;
203 if (file_info.size == 0) {
204 /* XXX Need a better error; just grabbed this one for expediency. */
205 PORT_SetError (SEC_ERROR_INPUT_LEN);
206 return NULL;
207 }
209 if (file_info.size > 0xffff) { /* I think this is too big. */
210 PORT_SetError (SEC_ERROR_NO_MEMORY);
211 return NULL;
212 }
214 item = PORT_Alloc (sizeof (SECItem));
215 if (item == NULL)
216 return NULL;
218 item->type = si_type;
219 item->len = (unsigned int) file_info.size;
220 item->data = PORT_Alloc ((size_t)item->len);
221 if (item->data == NULL)
222 goto loser;
224 bytes_read = PR_Read (in_file, item->data, (PRInt32) item->len);
225 if (bytes_read < 0) {
226 /* Something went wrong; error is already set for us. */
227 goto loser;
228 } else if (bytes_read == 0) {
229 /* Something went wrong; we read nothing. But no system/nspr error. */
230 /* XXX Need to set an error here. */
231 goto loser;
232 } else if (item->len != (unsigned int)bytes_read) {
233 /* Something went wrong; we read less (or more!?) than we expected. */
234 /* XXX Need to set an error here. */
235 goto loser;
236 }
238 return item;
240 loser:
241 SECITEM_FreeItem (item, PR_TRUE);
242 return NULL;
243 }
246 /*
247 * Create a DER-encoded OCSP request (for the certificate whose nickname
248 * is "name") and dump it out.
249 */
250 static SECStatus
251 create_request (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
252 PRBool add_service_locator, PRBool add_acceptable_responses)
253 {
254 CERTCertList *certs = NULL;
255 CERTCertificate *myCert = NULL;
256 CERTOCSPRequest *request = NULL;
257 PRTime now = PR_Now();
258 SECItem *encoding = NULL;
259 SECStatus rv = SECFailure;
261 if (handle == NULL || cert == NULL)
262 return rv;
264 myCert = CERT_DupCertificate(cert);
265 if (myCert == NULL)
266 goto loser;
268 /*
269 * We need to create a list of one.
270 */
271 certs = CERT_NewCertList();
272 if (certs == NULL)
273 goto loser;
275 if (CERT_AddCertToListTail (certs, myCert) != SECSuccess)
276 goto loser;
278 /*
279 * Now that cert is included in the list, we need to be careful
280 * that we do not try to destroy it twice. This will prevent that.
281 */
282 myCert = NULL;
284 request = CERT_CreateOCSPRequest (certs, now, add_service_locator, NULL);
285 if (request == NULL)
286 goto loser;
288 if (add_acceptable_responses) {
289 rv = CERT_AddOCSPAcceptableResponses(request,
290 SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
291 if (rv != SECSuccess)
292 goto loser;
293 }
295 encoding = CERT_EncodeOCSPRequest (NULL, request, NULL);
296 if (encoding == NULL)
297 goto loser;
299 MAKE_FILE_BINARY(out_file);
300 if (fwrite (encoding->data, encoding->len, 1, out_file) != 1)
301 goto loser;
303 rv = SECSuccess;
305 loser:
306 if (encoding != NULL)
307 SECITEM_FreeItem(encoding, PR_TRUE);
308 if (request != NULL)
309 CERT_DestroyOCSPRequest(request);
310 if (certs != NULL)
311 CERT_DestroyCertList (certs);
312 if (myCert != NULL)
313 CERT_DestroyCertificate(myCert);
315 return rv;
316 }
319 /*
320 * Create a DER-encoded OCSP request (for the certificate whose nickname is
321 * "cert_name"), then get and dump a corresponding response. The responder
322 * location is either specified explicitly (as "responder_url") or found
323 * via the AuthorityInfoAccess URL in the cert.
324 */
325 static SECStatus
326 dump_response (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
327 const char *responder_url)
328 {
329 CERTCertList *certs = NULL;
330 CERTCertificate *myCert = NULL;
331 char *loc = NULL;
332 PRTime now = PR_Now();
333 SECItem *response = NULL;
334 SECStatus rv = SECFailure;
335 PRBool includeServiceLocator;
337 if (handle == NULL || cert == NULL)
338 return rv;
340 myCert = CERT_DupCertificate(cert);
341 if (myCert == NULL)
342 goto loser;
344 if (responder_url != NULL) {
345 loc = (char *) responder_url;
346 includeServiceLocator = PR_TRUE;
347 } else {
348 loc = CERT_GetOCSPAuthorityInfoAccessLocation (cert);
349 if (loc == NULL)
350 goto loser;
351 includeServiceLocator = PR_FALSE;
352 }
354 /*
355 * We need to create a list of one.
356 */
357 certs = CERT_NewCertList();
358 if (certs == NULL)
359 goto loser;
361 if (CERT_AddCertToListTail (certs, myCert) != SECSuccess)
362 goto loser;
364 /*
365 * Now that cert is included in the list, we need to be careful
366 * that we do not try to destroy it twice. This will prevent that.
367 */
368 myCert = NULL;
370 response = CERT_GetEncodedOCSPResponse (NULL, certs, loc, now,
371 includeServiceLocator,
372 NULL, NULL, NULL);
373 if (response == NULL)
374 goto loser;
376 MAKE_FILE_BINARY(out_file);
377 if (fwrite (response->data, response->len, 1, out_file) != 1)
378 goto loser;
380 rv = SECSuccess;
382 loser:
383 if (response != NULL)
384 SECITEM_FreeItem (response, PR_TRUE);
385 if (certs != NULL)
386 CERT_DestroyCertList (certs);
387 if (myCert != NULL)
388 CERT_DestroyCertificate(myCert);
389 if (loc != NULL && loc != responder_url)
390 PORT_Free (loc);
392 return rv;
393 }
396 /*
397 * Get the status for the specified certificate (whose nickname is "cert_name").
398 * Directly use the OCSP function rather than doing a full verification.
399 */
400 static SECStatus
401 get_cert_status (FILE *out_file, CERTCertDBHandle *handle,
402 CERTCertificate *cert, const char *cert_name,
403 PRTime verify_time)
404 {
405 SECStatus rv = SECFailure;
407 if (handle == NULL || cert == NULL)
408 goto loser;
410 rv = CERT_CheckOCSPStatus (handle, cert, verify_time, NULL);
412 fprintf (out_file, "Check of certificate \"%s\" ", cert_name);
413 if (rv == SECSuccess) {
414 fprintf (out_file, "succeeded.\n");
415 } else {
416 const char *error_string = SECU_Strerror(PORT_GetError());
417 fprintf (out_file, "failed. Reason:\n");
418 if (error_string != NULL && PORT_Strlen(error_string) > 0)
419 fprintf (out_file, "%s\n", error_string);
420 else
421 fprintf (out_file, "Unknown\n");
422 }
424 rv = SECSuccess;
426 loser:
428 return rv;
429 }
432 /*
433 * Verify the specified certificate (whose nickname is "cert_name").
434 * OCSP is already turned on, so we just need to call the standard
435 * certificate verification API and let it do all the work.
436 */
437 static SECStatus
438 verify_cert (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
439 const char *cert_name, SECCertUsage cert_usage, PRTime verify_time)
440 {
441 SECStatus rv = SECFailure;
443 if (handle == NULL || cert == NULL)
444 return rv;
446 rv = CERT_VerifyCert (handle, cert, PR_TRUE, cert_usage, verify_time,
447 NULL, NULL);
449 fprintf (out_file, "Verification of certificate \"%s\" ", cert_name);
450 if (rv == SECSuccess) {
451 fprintf (out_file, "succeeded.\n");
452 } else {
453 const char *error_string = SECU_Strerror(PORT_GetError());
454 fprintf (out_file, "failed. Reason:\n");
455 if (error_string != NULL && PORT_Strlen(error_string) > 0)
456 fprintf (out_file, "%s\n", error_string);
457 else
458 fprintf (out_file, "Unknown\n");
459 }
461 rv = SECSuccess;
463 return rv;
464 }
466 CERTCertificate*
467 find_certificate(CERTCertDBHandle *handle, const char *name, PRBool ascii)
468 {
469 CERTCertificate *cert = NULL;
470 SECItem der;
471 PRFileDesc *certFile;
473 if (handle == NULL || name == NULL)
474 return NULL;
476 if (ascii == PR_FALSE) {
477 /* by default need to check if there is cert nick is given */
478 cert = CERT_FindCertByNicknameOrEmailAddr (handle, (char *) name);
479 if (cert != NULL)
480 return cert;
481 }
483 certFile = PR_Open(name, PR_RDONLY, 0);
484 if (certFile == NULL) {
485 return NULL;
486 }
488 if (SECU_ReadDERFromFile(&der, certFile, ascii, PR_FALSE) == SECSuccess) {
489 cert = CERT_DecodeCertFromPackage((char*)der.data, der.len);
490 SECITEM_FreeItem(&der, PR_FALSE);
491 }
492 PR_Close(certFile);
494 return cert;
495 }
498 #ifdef NO_PP
500 static SECStatus
501 print_request (FILE *out_file, SECItem *data)
502 {
503 fprintf (out_file, "Cannot pretty-print request compiled with NO_PP.\n");
504 return SECSuccess;
505 }
507 static SECStatus
508 print_response (FILE *out_file, SECItem *data, CERTCertDBHandle *handle)
509 {
510 fprintf (out_file, "Cannot pretty-print response compiled with NO_PP.\n");
511 return SECSuccess;
512 }
514 #else /* NO_PP */
516 static void
517 print_ocsp_version (FILE *out_file, SECItem *version, int level)
518 {
519 if (version->len > 0) {
520 SECU_PrintInteger (out_file, version, "Version", level);
521 } else {
522 SECU_Indent (out_file, level);
523 fprintf (out_file, "Version: DEFAULT\n");
524 }
525 }
528 static void
529 print_ocsp_cert_id (FILE *out_file, CERTOCSPCertID *cert_id, int level)
530 {
531 SECU_Indent (out_file, level);
532 fprintf (out_file, "Cert ID:\n");
533 level++;
535 SECU_PrintAlgorithmID (out_file, &(cert_id->hashAlgorithm),
536 "Hash Algorithm", level);
537 SECU_PrintAsHex (out_file, &(cert_id->issuerNameHash),
538 "Issuer Name Hash", level);
539 SECU_PrintAsHex (out_file, &(cert_id->issuerKeyHash),
540 "Issuer Key Hash", level);
541 SECU_PrintInteger (out_file, &(cert_id->serialNumber),
542 "Serial Number", level);
543 /* XXX lookup the cert; if found, print something nice (nickname?) */
544 }
547 static void
548 print_raw_certificates (FILE *out_file, SECItem **raw_certs, int level)
549 {
550 SECItem *raw_cert;
551 int i = 0;
552 char cert_label[50];
554 SECU_Indent (out_file, level);
556 if (raw_certs == NULL) {
557 fprintf (out_file, "No Certificates.\n");
558 return;
559 }
561 fprintf (out_file, "Certificate List:\n");
562 while ((raw_cert = raw_certs[i++]) != NULL) {
563 sprintf (cert_label, "Certificate (%d)", i);
564 (void) SECU_PrintSignedData (out_file, raw_cert, cert_label, level + 1,
565 SECU_PrintCertificate);
566 }
567 }
570 static void
571 print_ocsp_extensions (FILE *out_file, CERTCertExtension **extensions,
572 char *msg, int level)
573 {
574 if (extensions) {
575 SECU_PrintExtensions (out_file, extensions, msg, level);
576 } else {
577 SECU_Indent (out_file, level);
578 fprintf (out_file, "No %s\n", msg);
579 }
580 }
583 static void
584 print_single_request (FILE *out_file, ocspSingleRequest *single, int level)
585 {
586 print_ocsp_cert_id (out_file, single->reqCert, level);
587 print_ocsp_extensions (out_file, single->singleRequestExtensions,
588 "Single Request Extensions", level);
589 }
592 /*
593 * Decode the DER/BER-encoded item "data" as an OCSP request
594 * and pretty-print the subfields.
595 */
596 static SECStatus
597 print_request (FILE *out_file, SECItem *data)
598 {
599 CERTOCSPRequest *request;
600 ocspTBSRequest *tbsRequest;
601 int level = 0;
603 PORT_Assert (out_file != NULL);
604 PORT_Assert (data != NULL);
605 if (out_file == NULL || data == NULL) {
606 PORT_SetError (SEC_ERROR_INVALID_ARGS);
607 return SECFailure;
608 }
610 request = CERT_DecodeOCSPRequest (data);
611 if (request == NULL || request->tbsRequest == NULL)
612 return SECFailure;
614 tbsRequest = request->tbsRequest;
616 fprintf (out_file, "TBS Request:\n");
617 level++;
619 print_ocsp_version (out_file, &(tbsRequest->version), level);
621 /*
622 * XXX Probably should be an interface to get the signer name
623 * without looking inside the tbsRequest at all.
624 */
625 if (tbsRequest->requestorName != NULL) {
626 SECU_Indent (out_file, level);
627 fprintf (out_file, "XXX print the requestorName\n");
628 } else {
629 SECU_Indent (out_file, level);
630 fprintf (out_file, "No Requestor Name.\n");
631 }
633 if (tbsRequest->requestList != NULL) {
634 int i;
636 for (i = 0; tbsRequest->requestList[i] != NULL; i++) {
637 SECU_Indent (out_file, level);
638 fprintf (out_file, "Request %d:\n", i);
639 print_single_request (out_file, tbsRequest->requestList[i],
640 level + 1);
641 }
642 } else {
643 fprintf (out_file, "Request list is empty.\n");
644 }
646 print_ocsp_extensions (out_file, tbsRequest->requestExtensions,
647 "Request Extensions", level);
649 if (request->optionalSignature != NULL) {
650 ocspSignature *whole_sig;
651 SECItem rawsig;
653 fprintf (out_file, "Signature:\n");
655 whole_sig = request->optionalSignature;
656 SECU_PrintAlgorithmID (out_file, &(whole_sig->signatureAlgorithm),
657 "Signature Algorithm", level);
659 rawsig = whole_sig->signature;
660 DER_ConvertBitString (&rawsig);
661 SECU_PrintAsHex (out_file, &rawsig, "Signature", level);
663 print_raw_certificates (out_file, whole_sig->derCerts, level);
665 fprintf (out_file, "XXX verify the sig and print result\n");
666 } else {
667 fprintf (out_file, "No Signature\n");
668 }
670 CERT_DestroyOCSPRequest (request);
671 return SECSuccess;
672 }
675 static void
676 print_revoked_info (FILE *out_file, ocspRevokedInfo *revoked_info, int level)
677 {
678 SECU_PrintGeneralizedTime (out_file, &(revoked_info->revocationTime),
679 "Revocation Time", level);
681 if (revoked_info->revocationReason != NULL) {
682 SECU_PrintAsHex (out_file, revoked_info->revocationReason,
683 "Revocation Reason", level);
684 } else {
685 SECU_Indent (out_file, level);
686 fprintf (out_file, "No Revocation Reason.\n");
687 }
688 }
691 static void
692 print_cert_status (FILE *out_file, ocspCertStatus *status, int level)
693 {
694 SECU_Indent (out_file, level);
695 fprintf (out_file, "Status: ");
697 switch (status->certStatusType) {
698 case ocspCertStatus_good:
699 fprintf (out_file, "Cert is good.\n");
700 break;
701 case ocspCertStatus_revoked:
702 fprintf (out_file, "Cert has been revoked.\n");
703 print_revoked_info (out_file, status->certStatusInfo.revokedInfo,
704 level + 1);
705 break;
706 case ocspCertStatus_unknown:
707 fprintf (out_file, "Cert is unknown to responder.\n");
708 break;
709 default:
710 fprintf (out_file, "Unrecognized status.\n");
711 break;
712 }
713 }
716 static void
717 print_single_response (FILE *out_file, CERTOCSPSingleResponse *single,
718 int level)
719 {
720 print_ocsp_cert_id (out_file, single->certID, level);
722 print_cert_status (out_file, single->certStatus, level);
724 SECU_PrintGeneralizedTime (out_file, &(single->thisUpdate),
725 "This Update", level);
727 if (single->nextUpdate != NULL) {
728 SECU_PrintGeneralizedTime (out_file, single->nextUpdate,
729 "Next Update", level);
730 } else {
731 SECU_Indent (out_file, level);
732 fprintf (out_file, "No Next Update\n");
733 }
735 print_ocsp_extensions (out_file, single->singleExtensions,
736 "Single Response Extensions", level);
737 }
740 static void
741 print_responder_id (FILE *out_file, ocspResponderID *responderID, int level)
742 {
743 SECU_Indent (out_file, level);
744 fprintf (out_file, "Responder ID ");
746 switch (responderID->responderIDType) {
747 case ocspResponderID_byName:
748 fprintf (out_file, "(byName):\n");
749 SECU_PrintName (out_file, &(responderID->responderIDValue.name),
750 "Name", level + 1);
751 break;
752 case ocspResponderID_byKey:
753 fprintf (out_file, "(byKey):\n");
754 SECU_PrintAsHex (out_file, &(responderID->responderIDValue.keyHash),
755 "Key Hash", level + 1);
756 break;
757 default:
758 fprintf (out_file, "Unrecognized Responder ID Type\n");
759 break;
760 }
761 }
764 static void
765 print_response_data (FILE *out_file, ocspResponseData *responseData, int level)
766 {
767 SECU_Indent (out_file, level);
768 fprintf (out_file, "Response Data:\n");
769 level++;
771 print_ocsp_version (out_file, &(responseData->version), level);
773 print_responder_id (out_file, responseData->responderID, level);
775 SECU_PrintGeneralizedTime (out_file, &(responseData->producedAt),
776 "Produced At", level);
778 if (responseData->responses != NULL) {
779 int i;
781 for (i = 0; responseData->responses[i] != NULL; i++) {
782 SECU_Indent (out_file, level);
783 fprintf (out_file, "Response %d:\n", i);
784 print_single_response (out_file, responseData->responses[i],
785 level + 1);
786 }
787 } else {
788 fprintf (out_file, "Response list is empty.\n");
789 }
791 print_ocsp_extensions (out_file, responseData->responseExtensions,
792 "Response Extensions", level);
793 }
796 static void
797 print_basic_response (FILE *out_file, ocspBasicOCSPResponse *basic, int level)
798 {
799 SECItem rawsig;
801 SECU_Indent (out_file, level);
802 fprintf (out_file, "Basic OCSP Response:\n");
803 level++;
805 print_response_data (out_file, basic->tbsResponseData, level);
807 SECU_PrintAlgorithmID (out_file,
808 &(basic->responseSignature.signatureAlgorithm),
809 "Signature Algorithm", level);
811 rawsig = basic->responseSignature.signature;
812 DER_ConvertBitString (&rawsig);
813 SECU_PrintAsHex (out_file, &rawsig, "Signature", level);
815 print_raw_certificates (out_file, basic->responseSignature.derCerts, level);
816 }
819 /*
820 * Note this must match (exactly) the enumeration ocspResponseStatus.
821 */
822 static char *responseStatusNames[] = {
823 "successful (Response has valid confirmations)",
824 "malformedRequest (Illegal confirmation request)",
825 "internalError (Internal error in issuer)",
826 "tryLater (Try again later)",
827 "unused ((4) is not used)",
828 "sigRequired (Must sign the request)",
829 "unauthorized (Request unauthorized)"
830 };
832 /*
833 * Decode the DER/BER-encoded item "data" as an OCSP response
834 * and pretty-print the subfields.
835 */
836 static SECStatus
837 print_response (FILE *out_file, SECItem *data, CERTCertDBHandle *handle)
838 {
839 CERTOCSPResponse *response;
840 int level = 0;
842 PORT_Assert (out_file != NULL);
843 PORT_Assert (data != NULL);
844 if (out_file == NULL || data == NULL) {
845 PORT_SetError (SEC_ERROR_INVALID_ARGS);
846 return SECFailure;
847 }
849 response = CERT_DecodeOCSPResponse (data);
850 if (response == NULL)
851 return SECFailure;
853 if (response->statusValue >= ocspResponse_min &&
854 response->statusValue <= ocspResponse_max) {
855 fprintf (out_file, "Response Status: %s\n",
856 responseStatusNames[response->statusValue]);
857 } else {
858 fprintf (out_file,
859 "Response Status: other (Status value %d out of defined range)\n",
860 (int)response->statusValue);
861 }
863 if (response->statusValue == ocspResponse_successful) {
864 ocspResponseBytes *responseBytes = response->responseBytes;
865 SECStatus sigStatus;
866 CERTCertificate *signerCert = NULL;
868 PORT_Assert (responseBytes != NULL);
870 level++;
871 fprintf (out_file, "Response Bytes:\n");
872 SECU_PrintObjectID (out_file, &(responseBytes->responseType),
873 "Response Type", level);
874 switch (response->responseBytes->responseTypeTag) {
875 case SEC_OID_PKIX_OCSP_BASIC_RESPONSE:
876 print_basic_response (out_file,
877 responseBytes->decodedResponse.basic,
878 level);
879 break;
880 default:
881 SECU_Indent (out_file, level);
882 fprintf (out_file, "Unknown response syntax\n");
883 break;
884 }
886 sigStatus = CERT_VerifyOCSPResponseSignature (response, handle,
887 NULL, &signerCert, NULL);
888 SECU_Indent (out_file, level);
889 fprintf (out_file, "Signature verification ");
890 if (sigStatus != SECSuccess) {
891 fprintf (out_file, "failed: %s\n", SECU_Strerror (PORT_GetError()));
892 } else {
893 fprintf (out_file, "succeeded.\n");
894 if (signerCert != NULL) {
895 SECU_PrintName (out_file, &signerCert->subject, "Signer",
896 level);
897 CERT_DestroyCertificate (signerCert);
898 } else {
899 SECU_Indent (out_file, level);
900 fprintf (out_file, "No signer cert returned?\n");
901 }
902 }
903 } else {
904 SECU_Indent (out_file, level);
905 fprintf (out_file, "Unsuccessful response, no more information.\n");
906 }
908 CERT_DestroyOCSPResponse (response);
909 return SECSuccess;
910 }
912 #endif /* NO_PP */
915 static SECStatus
916 cert_usage_from_char (const char *cert_usage_str, SECCertUsage *cert_usage)
917 {
918 PORT_Assert (cert_usage_str != NULL);
919 PORT_Assert (cert_usage != NULL);
921 if (PORT_Strlen (cert_usage_str) != 1)
922 return SECFailure;
924 switch (*cert_usage_str) {
925 case 'c':
926 *cert_usage = certUsageSSLClient;
927 break;
928 case 's':
929 *cert_usage = certUsageSSLServer;
930 break;
931 case 'e':
932 *cert_usage = certUsageEmailRecipient;
933 break;
934 case 'E':
935 *cert_usage = certUsageEmailSigner;
936 break;
937 case 'S':
938 *cert_usage = certUsageObjectSigner;
939 break;
940 case 'C':
941 *cert_usage = certUsageVerifyCA;
942 break;
943 default:
944 return SECFailure;
945 }
947 return SECSuccess;
948 }
951 int
952 main (int argc, char **argv)
953 {
954 int retval;
955 PRFileDesc *in_file;
956 FILE *out_file; /* not PRFileDesc until SECU accepts it */
957 int crequest, dresponse;
958 int prequest, presponse;
959 int ccert, vcert;
960 const char *db_dir, *date_str, *cert_usage_str, *name;
961 const char *responder_name, *responder_url, *signer_name;
962 PRBool add_acceptable_responses, add_service_locator;
963 SECItem *data = NULL;
964 PLOptState *optstate;
965 SECStatus rv;
966 CERTCertDBHandle *handle = NULL;
967 SECCertUsage cert_usage;
968 PRTime verify_time;
969 CERTCertificate *cert = NULL;
970 PRBool ascii = PR_FALSE;
972 retval = -1; /* what we return/exit with on error */
974 program_name = PL_strrchr(argv[0], '/');
975 program_name = program_name ? (program_name + 1) : argv[0];
977 in_file = PR_STDIN;
978 out_file = stdout;
980 crequest = 0;
981 dresponse = 0;
982 prequest = 0;
983 presponse = 0;
984 ccert = 0;
985 vcert = 0;
987 db_dir = NULL;
988 date_str = NULL;
989 cert_usage_str = NULL;
990 name = NULL;
991 responder_name = NULL;
992 responder_url = NULL;
993 signer_name = NULL;
995 add_acceptable_responses = PR_FALSE;
996 add_service_locator = PR_FALSE;
998 optstate = PL_CreateOptState (argc, argv, "AHLPR:S:V:d:l:pr:s:t:u:w:");
999 if (optstate == NULL) {
1000 SECU_PrintError (program_name, "PL_CreateOptState failed");
1001 return retval;
1002 }
1004 while (PL_GetNextOpt (optstate) == PL_OPT_OK) {
1005 switch (optstate->option) {
1006 case '?':
1007 short_usage (program_name);
1008 return retval;
1010 case 'A':
1011 add_acceptable_responses = PR_TRUE;
1012 break;
1014 case 'H':
1015 long_usage (program_name);
1016 return retval;
1018 case 'L':
1019 add_service_locator = PR_TRUE;
1020 break;
1022 case 'P':
1023 presponse = 1;
1024 break;
1026 case 'R':
1027 dresponse = 1;
1028 name = optstate->value;
1029 break;
1031 case 'S':
1032 ccert = 1;
1033 name = optstate->value;
1034 break;
1036 case 'V':
1037 vcert = 1;
1038 name = optstate->value;
1039 break;
1041 case 'a':
1042 ascii = PR_TRUE;
1043 break;
1045 case 'd':
1046 db_dir = optstate->value;
1047 break;
1049 case 'l':
1050 responder_url = optstate->value;
1051 break;
1053 case 'p':
1054 prequest = 1;
1055 break;
1057 case 'r':
1058 crequest = 1;
1059 name = optstate->value;
1060 break;
1062 case 's':
1063 signer_name = optstate->value;
1064 break;
1066 case 't':
1067 responder_name = optstate->value;
1068 break;
1070 case 'u':
1071 cert_usage_str = optstate->value;
1072 break;
1074 case 'w':
1075 date_str = optstate->value;
1076 break;
1077 }
1078 }
1080 PL_DestroyOptState(optstate);
1082 if ((crequest + dresponse + prequest + presponse + ccert + vcert) != 1) {
1083 PR_fprintf (PR_STDERR, "%s: must specify exactly one command\n\n",
1084 program_name);
1085 short_usage (program_name);
1086 return retval;
1087 }
1089 if (vcert) {
1090 if (cert_usage_str == NULL) {
1091 PR_fprintf (PR_STDERR, "%s: verification requires cert usage\n\n",
1092 program_name);
1093 short_usage (program_name);
1094 return retval;
1095 }
1097 rv = cert_usage_from_char (cert_usage_str, &cert_usage);
1098 if (rv != SECSuccess) {
1099 PR_fprintf (PR_STDERR, "%s: invalid cert usage (\"%s\")\n\n",
1100 program_name, cert_usage_str);
1101 long_usage (program_name);
1102 return retval;
1103 }
1104 }
1106 if (ccert + vcert) {
1107 if (responder_url != NULL || responder_name != NULL) {
1108 /*
1109 * To do a full status check, both the URL and the cert name
1110 * of the responder must be specified if either one is.
1111 */
1112 if (responder_url == NULL || responder_name == NULL) {
1113 if (responder_url == NULL)
1114 PR_fprintf (PR_STDERR,
1115 "%s: must also specify responder location\n\n",
1116 program_name);
1117 else
1118 PR_fprintf (PR_STDERR,
1119 "%s: must also specify responder name\n\n",
1120 program_name);
1121 short_usage (program_name);
1122 return retval;
1123 }
1124 }
1126 if (date_str != NULL) {
1127 rv = DER_AsciiToTime (&verify_time, (char *) date_str);
1128 if (rv != SECSuccess) {
1129 SECU_PrintError (program_name, "error converting time string");
1130 PR_fprintf (PR_STDERR, "\n");
1131 long_usage (program_name);
1132 return retval;
1133 }
1134 } else {
1135 verify_time = PR_Now();
1136 }
1137 }
1139 retval = -2; /* errors change from usage to runtime */
1141 /*
1142 * Initialize the NSPR and Security libraries.
1143 */
1144 PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
1145 db_dir = SECU_ConfigDirectory (db_dir);
1146 rv = NSS_Init (db_dir);
1147 if (rv != SECSuccess) {
1148 SECU_PrintError (program_name, "NSS_Init failed");
1149 goto prdone;
1150 }
1151 SECU_RegisterDynamicOids();
1153 if (prequest + presponse) {
1154 MAKE_FILE_BINARY(stdin);
1155 data = read_file_into_item (in_file, siBuffer);
1156 if (data == NULL) {
1157 SECU_PrintError (program_name, "problem reading input");
1158 goto nssdone;
1159 }
1160 }
1162 if (crequest + dresponse + presponse + ccert + vcert) {
1163 handle = CERT_GetDefaultCertDB();
1164 if (handle == NULL) {
1165 SECU_PrintError (program_name, "problem getting certdb handle");
1166 goto nssdone;
1167 }
1169 /*
1170 * It would be fine to do the enable for all of these commands,
1171 * but this way we check that everything but an overall verify
1172 * can be done without it. That is, that the individual pieces
1173 * work on their own.
1174 */
1175 if (vcert) {
1176 rv = CERT_EnableOCSPChecking (handle);
1177 if (rv != SECSuccess) {
1178 SECU_PrintError (program_name, "error enabling OCSP checking");
1179 goto nssdone;
1180 }
1181 }
1183 if ((ccert + vcert) && (responder_name != NULL)) {
1184 rv = CERT_SetOCSPDefaultResponder (handle, responder_url,
1185 responder_name);
1186 if (rv != SECSuccess) {
1187 SECU_PrintError (program_name,
1188 "error setting default responder");
1189 goto nssdone;
1190 }
1192 rv = CERT_EnableOCSPDefaultResponder (handle);
1193 if (rv != SECSuccess) {
1194 SECU_PrintError (program_name,
1195 "error enabling default responder");
1196 goto nssdone;
1197 }
1198 }
1199 }
1201 #define NOTYET(opt) \
1202 { \
1203 PR_fprintf (PR_STDERR, "%s not yet working\n", opt); \
1204 exit (-1); \
1205 }
1207 if (name) {
1208 cert = find_certificate(handle, name, ascii);
1209 }
1211 if (crequest) {
1212 if (signer_name != NULL) {
1213 NOTYET("-s");
1214 }
1215 rv = create_request (out_file, handle, cert, add_service_locator,
1216 add_acceptable_responses);
1217 } else if (dresponse) {
1218 if (signer_name != NULL) {
1219 NOTYET("-s");
1220 }
1221 rv = dump_response (out_file, handle, cert, responder_url);
1222 } else if (prequest) {
1223 rv = print_request (out_file, data);
1224 } else if (presponse) {
1225 rv = print_response (out_file, data, handle);
1226 } else if (ccert) {
1227 if (signer_name != NULL) {
1228 NOTYET("-s");
1229 }
1230 rv = get_cert_status (out_file, handle, cert, name, verify_time);
1231 } else if (vcert) {
1232 if (signer_name != NULL) {
1233 NOTYET("-s");
1234 }
1235 rv = verify_cert (out_file, handle, cert, name, cert_usage, verify_time);
1236 }
1238 if (rv != SECSuccess)
1239 SECU_PrintError (program_name, "error performing requested operation");
1240 else
1241 retval = 0;
1243 nssdone:
1244 if (cert) {
1245 CERT_DestroyCertificate(cert);
1246 }
1248 if (data != NULL) {
1249 SECITEM_FreeItem (data, PR_TRUE);
1250 }
1252 if (handle != NULL) {
1253 CERT_DisableOCSPDefaultResponder(handle);
1254 CERT_DisableOCSPChecking (handle);
1255 }
1257 if (NSS_Shutdown () != SECSuccess) {
1258 retval = 1;
1259 }
1261 prdone:
1262 PR_Cleanup ();
1263 return retval;
1264 }