ocspclnt.c (37714B)
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 static void 41 synopsis(char *progname) 42 { 43 PRFileDesc *pr_stderr; 44 45 pr_stderr = PR_STDERR; 46 PR_fprintf(pr_stderr, "Usage:"); 47 PR_fprintf(pr_stderr, 48 "\t%s -p [-d <dir>]\n", 49 progname); 50 PR_fprintf(pr_stderr, 51 "\t%s -P [-d <dir>]\n", 52 progname); 53 PR_fprintf(pr_stderr, 54 "\t%s -r <name> [-a] [-L] [-s <name>] [-d <dir>]\n", 55 progname); 56 PR_fprintf(pr_stderr, 57 "\t%s -R <name> [-a] [-l <location>] [-s <name>] [-d <dir>]\n", 58 progname); 59 PR_fprintf(pr_stderr, 60 "\t%s -S <name> [-a] [-l <location> -t <name>]\n", 61 progname); 62 PR_fprintf(pr_stderr, 63 "\t\t [-s <name>] [-w <time>] [-d <dir>]\n"); 64 PR_fprintf(pr_stderr, 65 "\t%s -V <name> [-a] -u <usage> [-l <location> -t <name>]\n", 66 progname); 67 PR_fprintf(pr_stderr, 68 "\t\t [-s <name>] [-w <time>] [-d <dir>]\n"); 69 } 70 71 static void 72 short_usage(char *progname) 73 { 74 PR_fprintf(PR_STDERR, 75 "Type %s -H for more detailed descriptions\n", 76 progname); 77 synopsis(progname); 78 } 79 80 static void 81 long_usage(char *progname) 82 { 83 PRFileDesc *pr_stderr; 84 85 pr_stderr = PR_STDERR; 86 synopsis(progname); 87 PR_fprintf(pr_stderr, "\nCommands (must specify exactly one):\n"); 88 PR_fprintf(pr_stderr, 89 " %-13s Pretty-print a binary request read from stdin\n", 90 "-p"); 91 PR_fprintf(pr_stderr, 92 " %-13s Pretty-print a binary response read from stdin\n", 93 "-P"); 94 PR_fprintf(pr_stderr, 95 " %-13s Create a request for cert \"nickname\" on stdout\n", 96 "-r nickname"); 97 PR_fprintf(pr_stderr, 98 " %-13s Get response for cert \"nickname\", dump to stdout\n", 99 "-R nickname"); 100 PR_fprintf(pr_stderr, 101 " %-13s Get status for cert \"nickname\"\n", 102 "-S nickname"); 103 PR_fprintf(pr_stderr, 104 " %-13s Fully verify cert \"nickname\", w/ status check\n", 105 "-V nickname"); 106 PR_fprintf(pr_stderr, 107 "\n %-10s also can be the name of the file with DER or\n" 108 " %-13s PEM(use -a option) cert encoding\n", 109 "nickname", ""); 110 PR_fprintf(pr_stderr, "Options:\n"); 111 PR_fprintf(pr_stderr, 112 " %-13s Decode input cert from PEM format. DER is default\n", 113 "-a"); 114 PR_fprintf(pr_stderr, 115 " %-13s Add the service locator extension to the request\n", 116 "-L"); 117 PR_fprintf(pr_stderr, 118 " %-13s Find security databases in \"dbdir\" (default %s)\n", 119 "-d dbdir", DEFAULT_DB_DIR); 120 PR_fprintf(pr_stderr, 121 " %-13s Use \"location\" as URL of responder\n", 122 "-l location"); 123 PR_fprintf(pr_stderr, 124 " %-13s Trust cert \"nickname\" as response signer\n", 125 "-t nickname"); 126 PR_fprintf(pr_stderr, 127 " %-13s Sign requests with cert \"nickname\"\n", 128 "-s nickname"); 129 PR_fprintf(pr_stderr, 130 " %-13s Type of certificate usage for verification:\n", 131 "-u usage"); 132 PR_fprintf(pr_stderr, 133 "%-17s c SSL Client\n", ""); 134 PR_fprintf(pr_stderr, 135 "%-17s s SSL Server\n", ""); 136 PR_fprintf(pr_stderr, 137 "%-17s I IPsec\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 * Create a DER-encoded OCSP request (for the certificate whose nickname 247 * is "name") and dump it out. 248 */ 249 static SECStatus 250 create_request(FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert, 251 PRBool add_service_locator, PRBool add_acceptable_responses) 252 { 253 CERTCertList *certs = NULL; 254 CERTCertificate *myCert = NULL; 255 CERTOCSPRequest *request = NULL; 256 PRTime now = PR_Now(); 257 SECItem *encoding = NULL; 258 SECStatus rv = SECFailure; 259 260 if (handle == NULL || cert == NULL) 261 return rv; 262 263 myCert = CERT_DupCertificate(cert); 264 if (myCert == NULL) 265 goto loser; 266 267 /* 268 * We need to create a list of one. 269 */ 270 certs = CERT_NewCertList(); 271 if (certs == NULL) 272 goto loser; 273 274 if (CERT_AddCertToListTail(certs, myCert) != SECSuccess) 275 goto loser; 276 277 /* 278 * Now that cert is included in the list, we need to be careful 279 * that we do not try to destroy it twice. This will prevent that. 280 */ 281 myCert = NULL; 282 283 request = CERT_CreateOCSPRequest(certs, now, add_service_locator, NULL); 284 if (request == NULL) 285 goto loser; 286 287 if (add_acceptable_responses) { 288 rv = CERT_AddOCSPAcceptableResponses(request, 289 SEC_OID_PKIX_OCSP_BASIC_RESPONSE); 290 if (rv != SECSuccess) 291 goto loser; 292 } 293 294 encoding = CERT_EncodeOCSPRequest(NULL, request, NULL); 295 if (encoding == NULL) 296 goto loser; 297 298 MAKE_FILE_BINARY(out_file); 299 if (fwrite(encoding->data, encoding->len, 1, out_file) != 1) 300 goto loser; 301 302 rv = SECSuccess; 303 304 loser: 305 if (encoding != NULL) 306 SECITEM_FreeItem(encoding, PR_TRUE); 307 if (request != NULL) 308 CERT_DestroyOCSPRequest(request); 309 if (certs != NULL) 310 CERT_DestroyCertList(certs); 311 if (myCert != NULL) 312 CERT_DestroyCertificate(myCert); 313 314 return rv; 315 } 316 317 /* 318 * Create a DER-encoded OCSP request (for the certificate whose nickname is 319 * "cert_name"), then get and dump a corresponding response. The responder 320 * location is either specified explicitly (as "responder_url") or found 321 * via the AuthorityInfoAccess URL in the cert. 322 */ 323 static SECStatus 324 dump_response(FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert, 325 const char *responder_url) 326 { 327 CERTCertList *certs = NULL; 328 CERTCertificate *myCert = NULL; 329 char *loc = NULL; 330 PRTime now = PR_Now(); 331 SECItem *response = NULL; 332 SECStatus rv = SECFailure; 333 PRBool includeServiceLocator; 334 335 if (handle == NULL || cert == NULL) 336 return rv; 337 338 myCert = CERT_DupCertificate(cert); 339 if (myCert == NULL) 340 goto loser; 341 342 if (responder_url != NULL) { 343 loc = (char *)responder_url; 344 includeServiceLocator = PR_TRUE; 345 } else { 346 loc = CERT_GetOCSPAuthorityInfoAccessLocation(cert); 347 if (loc == NULL) 348 goto loser; 349 includeServiceLocator = PR_FALSE; 350 } 351 352 /* 353 * We need to create a list of one. 354 */ 355 certs = CERT_NewCertList(); 356 if (certs == NULL) 357 goto loser; 358 359 if (CERT_AddCertToListTail(certs, myCert) != SECSuccess) 360 goto loser; 361 362 /* 363 * Now that cert is included in the list, we need to be careful 364 * that we do not try to destroy it twice. This will prevent that. 365 */ 366 myCert = NULL; 367 368 response = CERT_GetEncodedOCSPResponse(NULL, certs, loc, now, 369 includeServiceLocator, 370 NULL, NULL, NULL); 371 if (response == NULL) 372 goto loser; 373 374 MAKE_FILE_BINARY(out_file); 375 if (fwrite(response->data, response->len, 1, out_file) != 1) 376 goto loser; 377 378 rv = SECSuccess; 379 380 loser: 381 if (response != NULL) 382 SECITEM_FreeItem(response, PR_TRUE); 383 if (certs != NULL) 384 CERT_DestroyCertList(certs); 385 if (myCert != NULL) 386 CERT_DestroyCertificate(myCert); 387 if (loc != NULL && loc != responder_url) 388 PORT_Free(loc); 389 390 return rv; 391 } 392 393 /* 394 * Get the status for the specified certificate (whose nickname is "cert_name"). 395 * Directly use the OCSP function rather than doing a full verification. 396 */ 397 static SECStatus 398 get_cert_status(FILE *out_file, CERTCertDBHandle *handle, 399 CERTCertificate *cert, const char *cert_name, 400 PRTime verify_time) 401 { 402 SECStatus rv = SECFailure; 403 404 if (handle == NULL || cert == NULL) 405 goto loser; 406 407 rv = CERT_CheckOCSPStatus(handle, cert, verify_time, NULL); 408 409 fprintf(out_file, "Check of certificate \"%s\" ", cert_name); 410 if (rv == SECSuccess) { 411 fprintf(out_file, "succeeded.\n"); 412 } else { 413 const char *error_string = SECU_Strerror(PORT_GetError()); 414 fprintf(out_file, "failed. Reason:\n"); 415 if (error_string != NULL && PORT_Strlen(error_string) > 0) 416 fprintf(out_file, "%s\n", error_string); 417 else 418 fprintf(out_file, "Unknown\n"); 419 } 420 421 rv = SECSuccess; 422 423 loser: 424 425 return rv; 426 } 427 428 /* 429 * Verify the specified certificate (whose nickname is "cert_name"). 430 * OCSP is already turned on, so we just need to call the standard 431 * certificate verification API and let it do all the work. 432 */ 433 static SECStatus 434 verify_cert(FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert, 435 const char *cert_name, SECCertUsage cert_usage, PRTime verify_time) 436 { 437 SECStatus rv = SECFailure; 438 439 if (handle == NULL || cert == NULL) 440 return rv; 441 442 rv = CERT_VerifyCert(handle, cert, PR_TRUE, cert_usage, verify_time, 443 NULL, NULL); 444 445 fprintf(out_file, "Verification of certificate \"%s\" ", cert_name); 446 if (rv == SECSuccess) { 447 fprintf(out_file, "succeeded.\n"); 448 } else { 449 const char *error_string = SECU_Strerror(PORT_GetError()); 450 fprintf(out_file, "failed. Reason:\n"); 451 if (error_string != NULL && PORT_Strlen(error_string) > 0) 452 fprintf(out_file, "%s\n", error_string); 453 else 454 fprintf(out_file, "Unknown\n"); 455 } 456 457 rv = SECSuccess; 458 459 return rv; 460 } 461 462 CERTCertificate * 463 find_certificate(CERTCertDBHandle *handle, const char *name, PRBool ascii) 464 { 465 CERTCertificate *cert = NULL; 466 SECItem der; 467 PRFileDesc *certFile; 468 469 if (handle == NULL || name == NULL) 470 return NULL; 471 472 if (ascii == PR_FALSE) { 473 /* by default need to check if there is cert nick is given */ 474 cert = CERT_FindCertByNicknameOrEmailAddr(handle, (char *)name); 475 if (cert != NULL) 476 return cert; 477 } 478 479 certFile = PR_Open(name, PR_RDONLY, 0); 480 if (certFile == NULL) { 481 return NULL; 482 } 483 484 if (SECU_ReadDERFromFile(&der, certFile, ascii, PR_FALSE) == SECSuccess) { 485 cert = CERT_DecodeCertFromPackage((char *)der.data, der.len); 486 SECITEM_FreeItem(&der, PR_FALSE); 487 } 488 PR_Close(certFile); 489 490 return cert; 491 } 492 493 #ifdef NO_PP 494 495 static SECStatus 496 print_request(FILE *out_file, SECItem *data) 497 { 498 fprintf(out_file, "Cannot pretty-print request compiled with NO_PP.\n"); 499 return SECSuccess; 500 } 501 502 static SECStatus 503 print_response(FILE *out_file, SECItem *data, CERTCertDBHandle *handle) 504 { 505 fprintf(out_file, "Cannot pretty-print response compiled with NO_PP.\n"); 506 return SECSuccess; 507 } 508 509 #else /* NO_PP */ 510 511 static void 512 print_ocsp_version(FILE *out_file, SECItem *version, int level) 513 { 514 if (version->len > 0) { 515 SECU_PrintInteger(out_file, version, "Version", level); 516 } else { 517 SECU_Indent(out_file, level); 518 fprintf(out_file, "Version: DEFAULT\n"); 519 } 520 } 521 522 static void 523 print_ocsp_cert_id(FILE *out_file, CERTOCSPCertID *cert_id, int level) 524 { 525 SECU_Indent(out_file, level); 526 fprintf(out_file, "Cert ID:\n"); 527 level++; 528 529 SECU_PrintAlgorithmID(out_file, &(cert_id->hashAlgorithm), 530 "Hash Algorithm", level); 531 SECU_PrintAsHex(out_file, &(cert_id->issuerNameHash), 532 "Issuer Name Hash", level); 533 SECU_PrintAsHex(out_file, &(cert_id->issuerKeyHash), 534 "Issuer Key Hash", level); 535 SECU_PrintInteger(out_file, &(cert_id->serialNumber), 536 "Serial Number", level); 537 /* XXX lookup the cert; if found, print something nice (nickname?) */ 538 } 539 540 static void 541 print_raw_certificates(FILE *out_file, SECItem **raw_certs, int level) 542 { 543 SECItem *raw_cert; 544 int i = 0; 545 char cert_label[50]; 546 547 SECU_Indent(out_file, level); 548 549 if (raw_certs == NULL) { 550 fprintf(out_file, "No Certificates.\n"); 551 return; 552 } 553 554 fprintf(out_file, "Certificate List:\n"); 555 while ((raw_cert = raw_certs[i++]) != NULL) { 556 snprintf(cert_label, sizeof(cert_label), "Certificate (%d)", i); 557 (void)SECU_PrintSignedData(out_file, raw_cert, cert_label, level + 1, 558 SECU_PrintCertificate); 559 } 560 } 561 562 static void 563 print_ocsp_extensions(FILE *out_file, CERTCertExtension **extensions, 564 char *msg, int level) 565 { 566 if (extensions) { 567 SECU_PrintExtensions(out_file, extensions, msg, level); 568 } else { 569 SECU_Indent(out_file, level); 570 fprintf(out_file, "No %s\n", msg); 571 } 572 } 573 574 static void 575 print_single_request(FILE *out_file, ocspSingleRequest *single, int level) 576 { 577 print_ocsp_cert_id(out_file, single->reqCert, level); 578 print_ocsp_extensions(out_file, single->singleRequestExtensions, 579 "Single Request Extensions", level); 580 } 581 582 /* 583 * Decode the DER/BER-encoded item "data" as an OCSP request 584 * and pretty-print the subfields. 585 */ 586 static SECStatus 587 print_request(FILE *out_file, SECItem *data) 588 { 589 CERTOCSPRequest *request; 590 ocspTBSRequest *tbsRequest; 591 int level = 0; 592 593 PORT_Assert(out_file != NULL); 594 PORT_Assert(data != NULL); 595 if (out_file == NULL || data == NULL) { 596 PORT_SetError(SEC_ERROR_INVALID_ARGS); 597 return SECFailure; 598 } 599 600 request = CERT_DecodeOCSPRequest(data); 601 if (request == NULL || request->tbsRequest == NULL) 602 return SECFailure; 603 604 tbsRequest = request->tbsRequest; 605 606 fprintf(out_file, "TBS Request:\n"); 607 level++; 608 609 print_ocsp_version(out_file, &(tbsRequest->version), level); 610 611 /* 612 * XXX Probably should be an interface to get the signer name 613 * without looking inside the tbsRequest at all. 614 */ 615 if (tbsRequest->requestorName != NULL) { 616 SECU_Indent(out_file, level); 617 fprintf(out_file, "XXX print the requestorName\n"); 618 } else { 619 SECU_Indent(out_file, level); 620 fprintf(out_file, "No Requestor Name.\n"); 621 } 622 623 if (tbsRequest->requestList != NULL) { 624 int i; 625 626 for (i = 0; tbsRequest->requestList[i] != NULL; i++) { 627 SECU_Indent(out_file, level); 628 fprintf(out_file, "Request %d:\n", i); 629 print_single_request(out_file, tbsRequest->requestList[i], 630 level + 1); 631 } 632 } else { 633 fprintf(out_file, "Request list is empty.\n"); 634 } 635 636 print_ocsp_extensions(out_file, tbsRequest->requestExtensions, 637 "Request Extensions", level); 638 639 if (request->optionalSignature != NULL) { 640 ocspSignature *whole_sig; 641 SECItem rawsig; 642 643 fprintf(out_file, "Signature:\n"); 644 645 whole_sig = request->optionalSignature; 646 SECU_PrintAlgorithmID(out_file, &(whole_sig->signatureAlgorithm), 647 "Signature Algorithm", level); 648 649 rawsig = whole_sig->signature; 650 DER_ConvertBitString(&rawsig); 651 SECU_PrintAsHex(out_file, &rawsig, "Signature", level); 652 653 print_raw_certificates(out_file, whole_sig->derCerts, level); 654 655 fprintf(out_file, "XXX verify the sig and print result\n"); 656 } else { 657 fprintf(out_file, "No Signature\n"); 658 } 659 660 CERT_DestroyOCSPRequest(request); 661 return SECSuccess; 662 } 663 664 static void 665 print_revoked_info(FILE *out_file, ocspRevokedInfo *revoked_info, int level) 666 { 667 SECU_PrintGeneralizedTime(out_file, &(revoked_info->revocationTime), 668 "Revocation Time", level); 669 670 if (revoked_info->revocationReason != NULL) { 671 SECU_PrintAsHex(out_file, revoked_info->revocationReason, 672 "Revocation Reason", level); 673 } else { 674 SECU_Indent(out_file, level); 675 fprintf(out_file, "No Revocation Reason.\n"); 676 } 677 } 678 679 static void 680 print_cert_status(FILE *out_file, ocspCertStatus *status, int level) 681 { 682 SECU_Indent(out_file, level); 683 fprintf(out_file, "Status: "); 684 685 switch (status->certStatusType) { 686 case ocspCertStatus_good: 687 fprintf(out_file, "Cert is good.\n"); 688 break; 689 case ocspCertStatus_revoked: 690 fprintf(out_file, "Cert has been revoked.\n"); 691 print_revoked_info(out_file, status->certStatusInfo.revokedInfo, 692 level + 1); 693 break; 694 case ocspCertStatus_unknown: 695 fprintf(out_file, "Cert is unknown to responder.\n"); 696 break; 697 default: 698 fprintf(out_file, "Unrecognized status.\n"); 699 break; 700 } 701 } 702 703 static void 704 print_single_response(FILE *out_file, CERTOCSPSingleResponse *single, 705 int level) 706 { 707 print_ocsp_cert_id(out_file, single->certID, level); 708 709 print_cert_status(out_file, single->certStatus, level); 710 711 SECU_PrintGeneralizedTime(out_file, &(single->thisUpdate), 712 "This Update", level); 713 714 if (single->nextUpdate != NULL) { 715 SECU_PrintGeneralizedTime(out_file, single->nextUpdate, 716 "Next Update", level); 717 } else { 718 SECU_Indent(out_file, level); 719 fprintf(out_file, "No Next Update\n"); 720 } 721 722 print_ocsp_extensions(out_file, single->singleExtensions, 723 "Single Response Extensions", level); 724 } 725 726 static void 727 print_responder_id(FILE *out_file, ocspResponderID *responderID, int level) 728 { 729 SECU_Indent(out_file, level); 730 fprintf(out_file, "Responder ID "); 731 732 switch (responderID->responderIDType) { 733 case ocspResponderID_byName: 734 fprintf(out_file, "(byName):\n"); 735 SECU_PrintName(out_file, &(responderID->responderIDValue.name), 736 "Name", level + 1); 737 break; 738 case ocspResponderID_byKey: 739 fprintf(out_file, "(byKey):\n"); 740 SECU_PrintAsHex(out_file, &(responderID->responderIDValue.keyHash), 741 "Key Hash", level + 1); 742 break; 743 default: 744 fprintf(out_file, "Unrecognized Responder ID Type\n"); 745 break; 746 } 747 } 748 749 static void 750 print_response_data(FILE *out_file, ocspResponseData *responseData, int level) 751 { 752 SECU_Indent(out_file, level); 753 fprintf(out_file, "Response Data:\n"); 754 level++; 755 756 print_ocsp_version(out_file, &(responseData->version), level); 757 758 print_responder_id(out_file, responseData->responderID, level); 759 760 SECU_PrintGeneralizedTime(out_file, &(responseData->producedAt), 761 "Produced At", level); 762 763 if (responseData->responses != NULL) { 764 int i; 765 766 for (i = 0; responseData->responses[i] != NULL; i++) { 767 SECU_Indent(out_file, level); 768 fprintf(out_file, "Response %d:\n", i); 769 print_single_response(out_file, responseData->responses[i], 770 level + 1); 771 } 772 } else { 773 fprintf(out_file, "Response list is empty.\n"); 774 } 775 776 print_ocsp_extensions(out_file, responseData->responseExtensions, 777 "Response Extensions", level); 778 } 779 780 static void 781 print_basic_response(FILE *out_file, ocspBasicOCSPResponse *basic, int level) 782 { 783 SECItem rawsig; 784 785 SECU_Indent(out_file, level); 786 fprintf(out_file, "Basic OCSP Response:\n"); 787 level++; 788 789 print_response_data(out_file, basic->tbsResponseData, level); 790 791 SECU_PrintAlgorithmID(out_file, 792 &(basic->responseSignature.signatureAlgorithm), 793 "Signature Algorithm", level); 794 795 rawsig = basic->responseSignature.signature; 796 DER_ConvertBitString(&rawsig); 797 SECU_PrintAsHex(out_file, &rawsig, "Signature", level); 798 799 print_raw_certificates(out_file, basic->responseSignature.derCerts, level); 800 } 801 802 /* 803 * Note this must match (exactly) the enumeration ocspResponseStatus. 804 */ 805 static char *responseStatusNames[] = { 806 "successful (Response has valid confirmations)", 807 "malformedRequest (Illegal confirmation request)", 808 "internalError (Internal error in issuer)", 809 "tryLater (Try again later)", 810 "unused ((4) is not used)", 811 "sigRequired (Must sign the request)", 812 "unauthorized (Request unauthorized)" 813 }; 814 815 /* 816 * Decode the DER/BER-encoded item "data" as an OCSP response 817 * and pretty-print the subfields. 818 */ 819 static SECStatus 820 print_response(FILE *out_file, SECItem *data, CERTCertDBHandle *handle) 821 { 822 CERTOCSPResponse *response; 823 int level = 0; 824 825 PORT_Assert(out_file != NULL); 826 PORT_Assert(data != NULL); 827 if (out_file == NULL || data == NULL) { 828 PORT_SetError(SEC_ERROR_INVALID_ARGS); 829 return SECFailure; 830 } 831 832 response = CERT_DecodeOCSPResponse(data); 833 if (response == NULL) 834 return SECFailure; 835 836 if (response->statusValue >= ocspResponse_min && 837 response->statusValue <= ocspResponse_max) { 838 fprintf(out_file, "Response Status: %s\n", 839 responseStatusNames[response->statusValue]); 840 } else { 841 fprintf(out_file, 842 "Response Status: other (Status value %d out of defined range)\n", 843 (int)response->statusValue); 844 } 845 846 if (response->statusValue == ocspResponse_successful) { 847 ocspResponseBytes *responseBytes = response->responseBytes; 848 SECStatus sigStatus; 849 CERTCertificate *signerCert = NULL; 850 851 PORT_Assert(responseBytes != NULL); 852 853 level++; 854 fprintf(out_file, "Response Bytes:\n"); 855 SECU_PrintObjectID(out_file, &(responseBytes->responseType), 856 "Response Type", level); 857 switch (response->responseBytes->responseTypeTag) { 858 case SEC_OID_PKIX_OCSP_BASIC_RESPONSE: 859 print_basic_response(out_file, 860 responseBytes->decodedResponse.basic, 861 level); 862 break; 863 default: 864 SECU_Indent(out_file, level); 865 fprintf(out_file, "Unknown response syntax\n"); 866 break; 867 } 868 869 sigStatus = CERT_VerifyOCSPResponseSignature(response, handle, 870 NULL, &signerCert, NULL); 871 SECU_Indent(out_file, level); 872 fprintf(out_file, "Signature verification "); 873 if (sigStatus != SECSuccess) { 874 fprintf(out_file, "failed: %s\n", SECU_Strerror(PORT_GetError())); 875 } else { 876 fprintf(out_file, "succeeded.\n"); 877 if (signerCert != NULL) { 878 SECU_PrintName(out_file, &signerCert->subject, "Signer", 879 level); 880 CERT_DestroyCertificate(signerCert); 881 } else { 882 SECU_Indent(out_file, level); 883 fprintf(out_file, "No signer cert returned?\n"); 884 } 885 } 886 } else { 887 SECU_Indent(out_file, level); 888 fprintf(out_file, "Unsuccessful response, no more information.\n"); 889 } 890 891 CERT_DestroyOCSPResponse(response); 892 return SECSuccess; 893 } 894 895 #endif /* NO_PP */ 896 897 static SECStatus 898 cert_usage_from_char(const char *cert_usage_str, SECCertUsage *cert_usage) 899 { 900 PORT_Assert(cert_usage_str != NULL); 901 PORT_Assert(cert_usage != NULL); 902 903 if (PORT_Strlen(cert_usage_str) != 1) 904 return SECFailure; 905 906 switch (*cert_usage_str) { 907 case 'c': 908 *cert_usage = certUsageSSLClient; 909 break; 910 case 's': 911 *cert_usage = certUsageSSLServer; 912 break; 913 case 'I': 914 *cert_usage = certUsageIPsec; 915 break; 916 case 'e': 917 *cert_usage = certUsageEmailRecipient; 918 break; 919 case 'E': 920 *cert_usage = certUsageEmailSigner; 921 break; 922 case 'S': 923 *cert_usage = certUsageObjectSigner; 924 break; 925 case 'C': 926 *cert_usage = certUsageVerifyCA; 927 break; 928 default: 929 return SECFailure; 930 } 931 932 return SECSuccess; 933 } 934 935 int 936 main(int argc, char **argv) 937 { 938 int retval; 939 PRFileDesc *in_file; 940 FILE *out_file; /* not PRFileDesc until SECU accepts it */ 941 int crequest, dresponse; 942 int prequest, presponse; 943 int ccert, vcert; 944 const char *db_dir, *date_str, *cert_usage_str, *name; 945 const char *responder_name, *responder_url, *signer_name; 946 PRBool add_acceptable_responses, add_service_locator; 947 SECItem *data = NULL; 948 PLOptState *optstate; 949 SECStatus rv; 950 CERTCertDBHandle *handle = NULL; 951 SECCertUsage cert_usage = certUsageSSLClient; 952 PRTime verify_time; 953 CERTCertificate *cert = NULL; 954 PRBool ascii = PR_FALSE; 955 956 retval = -1; /* what we return/exit with on error */ 957 958 program_name = PL_strrchr(argv[0], '/'); 959 program_name = program_name ? (program_name + 1) : argv[0]; 960 961 in_file = PR_STDIN; 962 out_file = stdout; 963 964 crequest = 0; 965 dresponse = 0; 966 prequest = 0; 967 presponse = 0; 968 ccert = 0; 969 vcert = 0; 970 971 db_dir = NULL; 972 date_str = NULL; 973 cert_usage_str = NULL; 974 name = NULL; 975 responder_name = NULL; 976 responder_url = NULL; 977 signer_name = NULL; 978 979 add_acceptable_responses = PR_FALSE; 980 add_service_locator = PR_FALSE; 981 982 optstate = PL_CreateOptState(argc, argv, "AHLPR:S:V:d:l:pr:s:t:u:w:"); 983 if (optstate == NULL) { 984 SECU_PrintError(program_name, "PL_CreateOptState failed"); 985 return retval; 986 } 987 988 while (PL_GetNextOpt(optstate) == PL_OPT_OK) { 989 switch (optstate->option) { 990 case '?': 991 short_usage(program_name); 992 return retval; 993 994 case 'A': 995 add_acceptable_responses = PR_TRUE; 996 break; 997 998 case 'H': 999 long_usage(program_name); 1000 return retval; 1001 1002 case 'L': 1003 add_service_locator = PR_TRUE; 1004 break; 1005 1006 case 'P': 1007 presponse = 1; 1008 break; 1009 1010 case 'R': 1011 dresponse = 1; 1012 name = optstate->value; 1013 break; 1014 1015 case 'S': 1016 ccert = 1; 1017 name = optstate->value; 1018 break; 1019 1020 case 'V': 1021 vcert = 1; 1022 name = optstate->value; 1023 break; 1024 1025 case 'a': 1026 ascii = PR_TRUE; 1027 break; 1028 1029 case 'd': 1030 db_dir = optstate->value; 1031 break; 1032 1033 case 'l': 1034 responder_url = optstate->value; 1035 break; 1036 1037 case 'p': 1038 prequest = 1; 1039 break; 1040 1041 case 'r': 1042 crequest = 1; 1043 name = optstate->value; 1044 break; 1045 1046 case 's': 1047 signer_name = optstate->value; 1048 break; 1049 1050 case 't': 1051 responder_name = optstate->value; 1052 break; 1053 1054 case 'u': 1055 cert_usage_str = optstate->value; 1056 break; 1057 1058 case 'w': 1059 date_str = optstate->value; 1060 break; 1061 } 1062 } 1063 1064 PL_DestroyOptState(optstate); 1065 1066 if ((crequest + dresponse + prequest + presponse + ccert + vcert) != 1) { 1067 PR_fprintf(PR_STDERR, "%s: must specify exactly one command\n\n", 1068 program_name); 1069 short_usage(program_name); 1070 return retval; 1071 } 1072 1073 if (vcert) { 1074 if (cert_usage_str == NULL) { 1075 PR_fprintf(PR_STDERR, "%s: verification requires cert usage\n\n", 1076 program_name); 1077 short_usage(program_name); 1078 return retval; 1079 } 1080 1081 rv = cert_usage_from_char(cert_usage_str, &cert_usage); 1082 if (rv != SECSuccess) { 1083 PR_fprintf(PR_STDERR, "%s: invalid cert usage (\"%s\")\n\n", 1084 program_name, cert_usage_str); 1085 long_usage(program_name); 1086 return retval; 1087 } 1088 } 1089 1090 if (ccert + vcert) { 1091 if (responder_url != NULL || responder_name != NULL) { 1092 /* 1093 * To do a full status check, both the URL and the cert name 1094 * of the responder must be specified if either one is. 1095 */ 1096 if (responder_url == NULL || responder_name == NULL) { 1097 if (responder_url == NULL) 1098 PR_fprintf(PR_STDERR, 1099 "%s: must also specify responder location\n\n", 1100 program_name); 1101 else 1102 PR_fprintf(PR_STDERR, 1103 "%s: must also specify responder name\n\n", 1104 program_name); 1105 short_usage(program_name); 1106 return retval; 1107 } 1108 } 1109 1110 if (date_str != NULL) { 1111 rv = DER_AsciiToTime(&verify_time, (char *)date_str); 1112 if (rv != SECSuccess) { 1113 SECU_PrintError(program_name, "error converting time string"); 1114 PR_fprintf(PR_STDERR, "\n"); 1115 long_usage(program_name); 1116 return retval; 1117 } 1118 } else { 1119 verify_time = PR_Now(); 1120 } 1121 } 1122 1123 retval = -2; /* errors change from usage to runtime */ 1124 1125 /* 1126 * Initialize the NSPR and Security libraries. 1127 */ 1128 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 1129 db_dir = SECU_ConfigDirectory(db_dir); 1130 rv = NSS_Init(db_dir); 1131 if (rv != SECSuccess) { 1132 SECU_PrintError(program_name, "NSS_Init failed"); 1133 goto prdone; 1134 } 1135 SECU_RegisterDynamicOids(); 1136 1137 if (prequest + presponse) { 1138 MAKE_FILE_BINARY(stdin); 1139 data = read_file_into_item(in_file, siBuffer); 1140 if (data == NULL) { 1141 SECU_PrintError(program_name, "problem reading input"); 1142 goto nssdone; 1143 } 1144 } 1145 1146 if (crequest + dresponse + presponse + ccert + vcert) { 1147 handle = CERT_GetDefaultCertDB(); 1148 if (handle == NULL) { 1149 SECU_PrintError(program_name, "problem getting certdb handle"); 1150 goto nssdone; 1151 } 1152 1153 /* 1154 * It would be fine to do the enable for all of these commands, 1155 * but this way we check that everything but an overall verify 1156 * can be done without it. That is, that the individual pieces 1157 * work on their own. 1158 */ 1159 if (vcert) { 1160 rv = CERT_EnableOCSPChecking(handle); 1161 if (rv != SECSuccess) { 1162 SECU_PrintError(program_name, "error enabling OCSP checking"); 1163 goto nssdone; 1164 } 1165 } 1166 1167 if ((ccert + vcert) && (responder_name != NULL)) { 1168 rv = CERT_SetOCSPDefaultResponder(handle, responder_url, 1169 responder_name); 1170 if (rv != SECSuccess) { 1171 SECU_PrintError(program_name, 1172 "error setting default responder"); 1173 goto nssdone; 1174 } 1175 1176 rv = CERT_EnableOCSPDefaultResponder(handle); 1177 if (rv != SECSuccess) { 1178 SECU_PrintError(program_name, 1179 "error enabling default responder"); 1180 goto nssdone; 1181 } 1182 } 1183 } 1184 1185 #define NOTYET(opt) \ 1186 { \ 1187 PR_fprintf(PR_STDERR, "%s not yet working\n", opt); \ 1188 exit(-1); \ 1189 } 1190 1191 if (name) { 1192 cert = find_certificate(handle, name, ascii); 1193 } 1194 1195 if (crequest) { 1196 if (signer_name != NULL) { 1197 NOTYET("-s"); 1198 } 1199 rv = create_request(out_file, handle, cert, add_service_locator, 1200 add_acceptable_responses); 1201 } else if (dresponse) { 1202 if (signer_name != NULL) { 1203 NOTYET("-s"); 1204 } 1205 rv = dump_response(out_file, handle, cert, responder_url); 1206 } else if (prequest) { 1207 rv = print_request(out_file, data); 1208 } else if (presponse) { 1209 rv = print_response(out_file, data, handle); 1210 } else if (ccert) { 1211 if (signer_name != NULL) { 1212 NOTYET("-s"); 1213 } 1214 rv = get_cert_status(out_file, handle, cert, name, verify_time); 1215 } else if (vcert) { 1216 if (signer_name != NULL) { 1217 NOTYET("-s"); 1218 } 1219 rv = verify_cert(out_file, handle, cert, name, cert_usage, verify_time); 1220 } 1221 1222 if (rv != SECSuccess) 1223 SECU_PrintError(program_name, "error performing requested operation"); 1224 else 1225 retval = 0; 1226 1227 nssdone: 1228 if (cert) { 1229 CERT_DestroyCertificate(cert); 1230 } 1231 1232 if (data != NULL) { 1233 SECITEM_FreeItem(data, PR_TRUE); 1234 } 1235 1236 if (handle != NULL) { 1237 CERT_DisableOCSPDefaultResponder(handle); 1238 CERT_DisableOCSPChecking(handle); 1239 } 1240 1241 if (NSS_Shutdown() != SECSuccess) { 1242 retval = 1; 1243 } 1244 1245 prdone: 1246 PR_Cleanup(); 1247 return retval; 1248 }