security/nss/cmd/ocspclnt/ocspclnt.c

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:812e687df286
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 /*
6 * Test program for client-side OCSP.
7 */
8
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 */
21
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 */
29
30 #if defined(_WIN32)
31 #include "fcntl.h"
32 #include "io.h"
33 #endif
34
35 #define DEFAULT_DB_DIR "~/.netscape"
36
37 /* global */
38 char *program_name;
39
40
41 static void
42 synopsis (char *program_name)
43 {
44 PRFileDesc *pr_stderr;
45
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 }
71
72
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 }
81
82
83 static void
84 long_usage (char *program_name)
85 {
86 PRFileDesc *pr_stderr;
87
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 }
156
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
176
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;
198
199 prv = PR_GetOpenFileInfo (in_file, &file_info);
200 if (prv != PR_SUCCESS)
201 return NULL;
202
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 }
208
209 if (file_info.size > 0xffff) { /* I think this is too big. */
210 PORT_SetError (SEC_ERROR_NO_MEMORY);
211 return NULL;
212 }
213
214 item = PORT_Alloc (sizeof (SECItem));
215 if (item == NULL)
216 return NULL;
217
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;
223
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 }
237
238 return item;
239
240 loser:
241 SECITEM_FreeItem (item, PR_TRUE);
242 return NULL;
243 }
244
245
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;
260
261 if (handle == NULL || cert == NULL)
262 return rv;
263
264 myCert = CERT_DupCertificate(cert);
265 if (myCert == NULL)
266 goto loser;
267
268 /*
269 * We need to create a list of one.
270 */
271 certs = CERT_NewCertList();
272 if (certs == NULL)
273 goto loser;
274
275 if (CERT_AddCertToListTail (certs, myCert) != SECSuccess)
276 goto loser;
277
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;
283
284 request = CERT_CreateOCSPRequest (certs, now, add_service_locator, NULL);
285 if (request == NULL)
286 goto loser;
287
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 }
294
295 encoding = CERT_EncodeOCSPRequest (NULL, request, NULL);
296 if (encoding == NULL)
297 goto loser;
298
299 MAKE_FILE_BINARY(out_file);
300 if (fwrite (encoding->data, encoding->len, 1, out_file) != 1)
301 goto loser;
302
303 rv = SECSuccess;
304
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);
314
315 return rv;
316 }
317
318
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;
336
337 if (handle == NULL || cert == NULL)
338 return rv;
339
340 myCert = CERT_DupCertificate(cert);
341 if (myCert == NULL)
342 goto loser;
343
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 }
353
354 /*
355 * We need to create a list of one.
356 */
357 certs = CERT_NewCertList();
358 if (certs == NULL)
359 goto loser;
360
361 if (CERT_AddCertToListTail (certs, myCert) != SECSuccess)
362 goto loser;
363
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;
369
370 response = CERT_GetEncodedOCSPResponse (NULL, certs, loc, now,
371 includeServiceLocator,
372 NULL, NULL, NULL);
373 if (response == NULL)
374 goto loser;
375
376 MAKE_FILE_BINARY(out_file);
377 if (fwrite (response->data, response->len, 1, out_file) != 1)
378 goto loser;
379
380 rv = SECSuccess;
381
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);
391
392 return rv;
393 }
394
395
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;
406
407 if (handle == NULL || cert == NULL)
408 goto loser;
409
410 rv = CERT_CheckOCSPStatus (handle, cert, verify_time, NULL);
411
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 }
423
424 rv = SECSuccess;
425
426 loser:
427
428 return rv;
429 }
430
431
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;
442
443 if (handle == NULL || cert == NULL)
444 return rv;
445
446 rv = CERT_VerifyCert (handle, cert, PR_TRUE, cert_usage, verify_time,
447 NULL, NULL);
448
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 }
460
461 rv = SECSuccess;
462
463 return rv;
464 }
465
466 CERTCertificate*
467 find_certificate(CERTCertDBHandle *handle, const char *name, PRBool ascii)
468 {
469 CERTCertificate *cert = NULL;
470 SECItem der;
471 PRFileDesc *certFile;
472
473 if (handle == NULL || name == NULL)
474 return NULL;
475
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 }
482
483 certFile = PR_Open(name, PR_RDONLY, 0);
484 if (certFile == NULL) {
485 return NULL;
486 }
487
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);
493
494 return cert;
495 }
496
497
498 #ifdef NO_PP
499
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 }
506
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 }
513
514 #else /* NO_PP */
515
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 }
526
527
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++;
534
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 }
545
546
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];
553
554 SECU_Indent (out_file, level);
555
556 if (raw_certs == NULL) {
557 fprintf (out_file, "No Certificates.\n");
558 return;
559 }
560
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 }
568
569
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 }
581
582
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 }
590
591
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;
602
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 }
609
610 request = CERT_DecodeOCSPRequest (data);
611 if (request == NULL || request->tbsRequest == NULL)
612 return SECFailure;
613
614 tbsRequest = request->tbsRequest;
615
616 fprintf (out_file, "TBS Request:\n");
617 level++;
618
619 print_ocsp_version (out_file, &(tbsRequest->version), level);
620
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 }
632
633 if (tbsRequest->requestList != NULL) {
634 int i;
635
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 }
645
646 print_ocsp_extensions (out_file, tbsRequest->requestExtensions,
647 "Request Extensions", level);
648
649 if (request->optionalSignature != NULL) {
650 ocspSignature *whole_sig;
651 SECItem rawsig;
652
653 fprintf (out_file, "Signature:\n");
654
655 whole_sig = request->optionalSignature;
656 SECU_PrintAlgorithmID (out_file, &(whole_sig->signatureAlgorithm),
657 "Signature Algorithm", level);
658
659 rawsig = whole_sig->signature;
660 DER_ConvertBitString (&rawsig);
661 SECU_PrintAsHex (out_file, &rawsig, "Signature", level);
662
663 print_raw_certificates (out_file, whole_sig->derCerts, level);
664
665 fprintf (out_file, "XXX verify the sig and print result\n");
666 } else {
667 fprintf (out_file, "No Signature\n");
668 }
669
670 CERT_DestroyOCSPRequest (request);
671 return SECSuccess;
672 }
673
674
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);
680
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 }
689
690
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: ");
696
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 }
714
715
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);
721
722 print_cert_status (out_file, single->certStatus, level);
723
724 SECU_PrintGeneralizedTime (out_file, &(single->thisUpdate),
725 "This Update", level);
726
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 }
734
735 print_ocsp_extensions (out_file, single->singleExtensions,
736 "Single Response Extensions", level);
737 }
738
739
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 ");
745
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 }
762
763
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++;
770
771 print_ocsp_version (out_file, &(responseData->version), level);
772
773 print_responder_id (out_file, responseData->responderID, level);
774
775 SECU_PrintGeneralizedTime (out_file, &(responseData->producedAt),
776 "Produced At", level);
777
778 if (responseData->responses != NULL) {
779 int i;
780
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 }
790
791 print_ocsp_extensions (out_file, responseData->responseExtensions,
792 "Response Extensions", level);
793 }
794
795
796 static void
797 print_basic_response (FILE *out_file, ocspBasicOCSPResponse *basic, int level)
798 {
799 SECItem rawsig;
800
801 SECU_Indent (out_file, level);
802 fprintf (out_file, "Basic OCSP Response:\n");
803 level++;
804
805 print_response_data (out_file, basic->tbsResponseData, level);
806
807 SECU_PrintAlgorithmID (out_file,
808 &(basic->responseSignature.signatureAlgorithm),
809 "Signature Algorithm", level);
810
811 rawsig = basic->responseSignature.signature;
812 DER_ConvertBitString (&rawsig);
813 SECU_PrintAsHex (out_file, &rawsig, "Signature", level);
814
815 print_raw_certificates (out_file, basic->responseSignature.derCerts, level);
816 }
817
818
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 };
831
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;
841
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 }
848
849 response = CERT_DecodeOCSPResponse (data);
850 if (response == NULL)
851 return SECFailure;
852
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 }
862
863 if (response->statusValue == ocspResponse_successful) {
864 ocspResponseBytes *responseBytes = response->responseBytes;
865 SECStatus sigStatus;
866 CERTCertificate *signerCert = NULL;
867
868 PORT_Assert (responseBytes != NULL);
869
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 }
885
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 }
907
908 CERT_DestroyOCSPResponse (response);
909 return SECSuccess;
910 }
911
912 #endif /* NO_PP */
913
914
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);
920
921 if (PORT_Strlen (cert_usage_str) != 1)
922 return SECFailure;
923
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 }
946
947 return SECSuccess;
948 }
949
950
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;
971
972 retval = -1; /* what we return/exit with on error */
973
974 program_name = PL_strrchr(argv[0], '/');
975 program_name = program_name ? (program_name + 1) : argv[0];
976
977 in_file = PR_STDIN;
978 out_file = stdout;
979
980 crequest = 0;
981 dresponse = 0;
982 prequest = 0;
983 presponse = 0;
984 ccert = 0;
985 vcert = 0;
986
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;
994
995 add_acceptable_responses = PR_FALSE;
996 add_service_locator = PR_FALSE;
997
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 }
1003
1004 while (PL_GetNextOpt (optstate) == PL_OPT_OK) {
1005 switch (optstate->option) {
1006 case '?':
1007 short_usage (program_name);
1008 return retval;
1009
1010 case 'A':
1011 add_acceptable_responses = PR_TRUE;
1012 break;
1013
1014 case 'H':
1015 long_usage (program_name);
1016 return retval;
1017
1018 case 'L':
1019 add_service_locator = PR_TRUE;
1020 break;
1021
1022 case 'P':
1023 presponse = 1;
1024 break;
1025
1026 case 'R':
1027 dresponse = 1;
1028 name = optstate->value;
1029 break;
1030
1031 case 'S':
1032 ccert = 1;
1033 name = optstate->value;
1034 break;
1035
1036 case 'V':
1037 vcert = 1;
1038 name = optstate->value;
1039 break;
1040
1041 case 'a':
1042 ascii = PR_TRUE;
1043 break;
1044
1045 case 'd':
1046 db_dir = optstate->value;
1047 break;
1048
1049 case 'l':
1050 responder_url = optstate->value;
1051 break;
1052
1053 case 'p':
1054 prequest = 1;
1055 break;
1056
1057 case 'r':
1058 crequest = 1;
1059 name = optstate->value;
1060 break;
1061
1062 case 's':
1063 signer_name = optstate->value;
1064 break;
1065
1066 case 't':
1067 responder_name = optstate->value;
1068 break;
1069
1070 case 'u':
1071 cert_usage_str = optstate->value;
1072 break;
1073
1074 case 'w':
1075 date_str = optstate->value;
1076 break;
1077 }
1078 }
1079
1080 PL_DestroyOptState(optstate);
1081
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 }
1088
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 }
1096
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 }
1105
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 }
1125
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 }
1138
1139 retval = -2; /* errors change from usage to runtime */
1140
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();
1152
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 }
1161
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 }
1168
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 }
1182
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 }
1191
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 }
1200
1201 #define NOTYET(opt) \
1202 { \
1203 PR_fprintf (PR_STDERR, "%s not yet working\n", opt); \
1204 exit (-1); \
1205 }
1206
1207 if (name) {
1208 cert = find_certificate(handle, name, ascii);
1209 }
1210
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 }
1237
1238 if (rv != SECSuccess)
1239 SECU_PrintError (program_name, "error performing requested operation");
1240 else
1241 retval = 0;
1242
1243 nssdone:
1244 if (cert) {
1245 CERT_DestroyCertificate(cert);
1246 }
1247
1248 if (data != NULL) {
1249 SECITEM_FreeItem (data, PR_TRUE);
1250 }
1251
1252 if (handle != NULL) {
1253 CERT_DisableOCSPDefaultResponder(handle);
1254 CERT_DisableOCSPChecking (handle);
1255 }
1256
1257 if (NSS_Shutdown () != SECSuccess) {
1258 retval = 1;
1259 }
1260
1261 prdone:
1262 PR_Cleanup ();
1263 return retval;
1264 }

mercurial