Back to index

lightning-sunbird  0.9+nobinonly
ocspclnt.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * Test program for client-side OCSP.
00039  *
00040  * $Id: ocspclnt.c,v 1.7.28.2 2007/12/06 09:44:02 slavomir.katuscak%sun.com Exp $
00041  */
00042 
00043 #include "secutil.h"
00044 #include "nspr.h"
00045 #include "plgetopt.h"
00046 #include "nss.h"
00047 #include "cert.h"
00048 #include "ocsp.h"
00049 #include "xconst.h"  /*
00050                       * XXX internal header file; needed to get at
00051                       * cert_DecodeAuthInfoAccessExtension -- would be
00052                       * nice to not need this, but that would require
00053                       * better/different APIs.
00054                       */
00055 
00056 #ifndef NO_PP        /*
00057                       * Compile with this every once in a while to be
00058                       * sure that no dependencies on it get added
00059                       * outside of the pretty-printing routines.
00060                       */
00061 #include "ocspti.h"  /* internals for pretty-printing routines *only* */
00062 #endif /* NO_PP */
00063 
00064 #define DEFAULT_DB_DIR      "~/.netscape"
00065 
00066 
00067 static void
00068 synopsis (char *program_name)
00069 {
00070     PRFileDesc *pr_stderr;
00071 
00072     pr_stderr = PR_STDERR;
00073     PR_fprintf (pr_stderr, "Usage:");
00074     PR_fprintf (pr_stderr,
00075               "\t%s -p [-d <dir>]\n",
00076               program_name);
00077     PR_fprintf (pr_stderr,
00078               "\t%s -P [-d <dir>]\n",
00079               program_name);
00080     PR_fprintf (pr_stderr,
00081               "\t%s -r <name> [-L] [-s <name>] [-d <dir>]\n",
00082               program_name);
00083     PR_fprintf (pr_stderr,
00084               "\t%s -R <name> [-l <location>] [-s <name>] [-d <dir>]\n",
00085               program_name);
00086     PR_fprintf (pr_stderr,
00087               "\t%s -S <name> [-l <location> -t <name>]\n",
00088               program_name);
00089     PR_fprintf (pr_stderr,
00090               "\t\t [-s <name>] [-w <time>] [-d <dir>]\n");
00091     PR_fprintf (pr_stderr,
00092               "\t%s -V <name> -u <usage> [-l <location> -t <name>]\n",
00093               program_name);
00094     PR_fprintf (pr_stderr,
00095               "\t\t [-s <name>] [-w <time>] [-d <dir>]\n");
00096 }
00097 
00098 
00099 static void
00100 short_usage (char *program_name)
00101 {
00102     PR_fprintf (PR_STDERR,
00103               "Type %s -H for more detailed descriptions\n",
00104               program_name);
00105     synopsis (program_name);
00106 }
00107 
00108 
00109 static void
00110 long_usage (char *program_name)
00111 {
00112     PRFileDesc *pr_stderr;
00113 
00114     pr_stderr = PR_STDERR;
00115     synopsis (program_name);
00116     PR_fprintf (pr_stderr, "\nCommands (must specify exactly one):\n");
00117     PR_fprintf (pr_stderr,
00118               "  %-13s Pretty-print a binary request read from stdin\n",
00119               "-p");
00120     PR_fprintf (pr_stderr,
00121               "  %-13s Pretty-print a binary response read from stdin\n",
00122               "-P");
00123     PR_fprintf (pr_stderr,
00124               "  %-13s Create a request for cert \"nickname\" on stdout\n",
00125               "-r nickname");
00126     PR_fprintf (pr_stderr,
00127               "  %-13s Get response for cert \"nickname\", dump to stdout\n",
00128               "-R nickname");
00129     PR_fprintf (pr_stderr,
00130               "  %-13s Get status for cert \"nickname\"\n",
00131               "-S nickname");
00132     PR_fprintf (pr_stderr,
00133               "  %-13s Fully verify cert \"nickname\", w/ status check\n",
00134               "-V nickname");
00135     PR_fprintf (pr_stderr, "Options:\n");
00136     PR_fprintf (pr_stderr,
00137               "  %-13s Add the service locator extension to the request\n",
00138               "-L");
00139     PR_fprintf (pr_stderr,
00140               "  %-13s Find security databases in \"dbdir\" (default %s)\n",
00141               "-d dbdir", DEFAULT_DB_DIR);
00142     PR_fprintf (pr_stderr,
00143               "  %-13s Use \"location\" as URL of responder\n",
00144               "-l location");
00145     PR_fprintf (pr_stderr,
00146               "  %-13s Trust cert \"nickname\" as response signer\n",
00147               "-t nickname");
00148     PR_fprintf (pr_stderr,
00149               "  %-13s Sign requests with cert \"nickname\"\n",
00150               "-s nickname");
00151     PR_fprintf (pr_stderr,
00152               "  %-13s Type of certificate usage for verification:\n",
00153               "-u usage");
00154     PR_fprintf (pr_stderr,
00155               "%-17s c   SSL Client\n", "");
00156     PR_fprintf (pr_stderr,
00157               "%-17s s   SSL Server\n", "");
00158     PR_fprintf (pr_stderr,
00159               "%-17s e   Email Recipient\n", "");
00160     PR_fprintf (pr_stderr,
00161               "%-17s E   Email Signer\n", "");
00162     PR_fprintf (pr_stderr,
00163               "%-17s S   Object Signer\n", "");
00164     PR_fprintf (pr_stderr,
00165               "%-17s C   CA\n", "");
00166     PR_fprintf (pr_stderr,
00167               "  %-13s Validity time (default current time), one of:\n",
00168               "-w time");
00169     PR_fprintf (pr_stderr,
00170               "%-17s %-25s (GMT)\n", "", "YYMMDDhhmm[ss]Z");
00171     PR_fprintf (pr_stderr,
00172               "%-17s %-25s (later than GMT)\n", "", "YYMMDDhhmm[ss]+hhmm");
00173     PR_fprintf (pr_stderr,
00174               "%-17s %-25s (earlier than GMT)\n", "", "YYMMDDhhmm[ss]-hhmm");
00175 }
00176 
00177 
00178 /*
00179  * XXX This is a generic function that would probably make a good
00180  * replacement for SECU_DER_Read (which is not at all specific to DER,
00181  * despite its name), but that requires fixing all of the tools...
00182  * Still, it should be done, whenenver I/somebody has the time.
00183  * (Also, consider whether this actually belongs in the security
00184  * library itself, not just in the command library.)
00185  *
00186  * This function takes an open file (a PRFileDesc *) and reads the
00187  * entire file into a SECItem.  (Obviously, the file is intended to
00188  * be small enough that such a thing is advisable.)  Both the SECItem
00189  * and the buffer it points to are allocated from the heap; the caller
00190  * is expected to free them.  ("SECITEM_FreeItem(item, PR_TRUE)")
00191  */
00192 static SECItem *
00193 read_file_into_item (PRFileDesc *in_file, SECItemType si_type)
00194 {
00195     PRStatus   prv;
00196     SECItem   *item;
00197     PRFileInfo        file_info;
00198     PRInt32    bytes_read;
00199 
00200     prv = PR_GetOpenFileInfo (in_file, &file_info);
00201     if (prv != PR_SUCCESS)
00202        return NULL;
00203 
00204     if (file_info.size ==  0) {
00205        /* XXX Need a better error; just grabbed this one for expediency. */
00206        PORT_SetError (SEC_ERROR_INPUT_LEN);
00207        return NULL;
00208     }
00209 
00210     if (file_info.size > 0xffff) { /* I think this is too big. */
00211        PORT_SetError (SEC_ERROR_NO_MEMORY);
00212        return NULL;
00213     }
00214 
00215     item = PORT_Alloc (sizeof (SECItem));
00216     if (item == NULL)
00217        return NULL;
00218 
00219     item->type = si_type;
00220     item->len = (unsigned int) file_info.size;
00221     item->data = PORT_Alloc ((size_t)item->len);
00222     if (item->data == NULL)
00223        goto loser;
00224 
00225     bytes_read = PR_Read (in_file, item->data, (PRInt32) item->len);
00226     if (bytes_read < 0) {
00227        /* Something went wrong; error is already set for us. */
00228        goto loser;
00229     } else if (bytes_read == 0) {
00230        /* Something went wrong; we read nothing.  But no system/nspr error. */
00231        /* XXX Need to set an error here. */
00232        goto loser;
00233     } else if (item->len != (unsigned int)bytes_read) {
00234        /* Something went wrong; we read less (or more!?) than we expected. */
00235        /* XXX Need to set an error here. */
00236        goto loser;
00237     }
00238 
00239     return item;
00240 
00241 loser:
00242     SECITEM_FreeItem (item, PR_TRUE);
00243     return NULL;
00244 }
00245 
00246 
00247 /*
00248  * Create a DER-encoded OCSP request (for the certificate whose nickname
00249  * is "name") and dump it out.
00250  */
00251 static SECStatus
00252 create_request (FILE *out_file, CERTCertDBHandle *handle, const char *cert_name,
00253               PRBool add_service_locator, PRBool add_acceptable_responses)
00254 {
00255     CERTCertificate *cert = NULL;
00256     CERTCertList *certs = NULL;
00257     CERTOCSPRequest *request = NULL;
00258     int64 now = PR_Now();
00259     SECItem *encoding = NULL;
00260     SECStatus rv = SECFailure;
00261 
00262     if (handle == NULL || cert_name == NULL)
00263        goto loser;
00264 
00265     cert = CERT_FindCertByNicknameOrEmailAddr (handle, (char *) cert_name);
00266     if (cert == NULL)
00267        goto loser;
00268 
00269     /*
00270      * We need to create a list of one.
00271      */
00272     certs = CERT_NewCertList();
00273     if (certs == NULL)
00274        goto loser;
00275 
00276     if (CERT_AddCertToListTail (certs, cert) != SECSuccess)
00277        goto loser;
00278 
00279     /*
00280      * Now that cert is included in the list, we need to be careful
00281      * that we do not try to destroy it twice.  This will prevent that.
00282      */
00283     cert = NULL;
00284 
00285     request = CERT_CreateOCSPRequest (certs, now, add_service_locator, NULL);
00286     if (request == NULL)
00287        goto loser;
00288 
00289     if (add_acceptable_responses) {
00290        rv = CERT_AddOCSPAcceptableResponses(request,
00291                                         SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
00292        if (rv != SECSuccess)
00293            goto loser;
00294     }
00295 
00296     encoding = CERT_EncodeOCSPRequest (NULL, request, NULL);
00297     if (encoding == NULL)
00298        goto loser;
00299 
00300     if (fwrite (encoding->data, encoding->len, 1, out_file) != 1)
00301        goto loser;
00302 
00303     rv = SECSuccess;
00304 
00305 loser:
00306     if (encoding != NULL)
00307        SECITEM_FreeItem(encoding, PR_TRUE);
00308     if (request != NULL)
00309        CERT_DestroyOCSPRequest(request);
00310     if (certs != NULL)
00311        CERT_DestroyCertList (certs);
00312     if (cert != NULL)
00313        CERT_DestroyCertificate (cert);
00314 
00315     return rv;
00316 }
00317 
00318 
00319 /*
00320  * Create a DER-encoded OCSP request (for the certificate whose nickname is
00321  * "cert_name"), then get and dump a corresponding response.  The responder
00322  * location is either specified explicitly (as "responder_url") or found
00323  * via the AuthorityInfoAccess URL in the cert.
00324  */
00325 static SECStatus
00326 dump_response (FILE *out_file, CERTCertDBHandle *handle, const char *cert_name,
00327               const char *responder_url)
00328 {
00329     CERTCertificate *cert = NULL;
00330     CERTCertList *certs = NULL;
00331     char *loc = NULL;
00332     int64 now = PR_Now();
00333     SECItem *response = NULL;
00334     SECStatus rv = SECFailure;
00335     PRBool includeServiceLocator;
00336 
00337     if (handle == NULL || cert_name == NULL)
00338        goto loser;
00339 
00340     cert = CERT_FindCertByNicknameOrEmailAddr (handle, (char *) cert_name);
00341     if (cert == NULL)
00342        goto loser;
00343 
00344     if (responder_url != NULL) {
00345        loc = (char *) responder_url;
00346        includeServiceLocator = PR_TRUE;
00347     } else {
00348        loc = CERT_GetOCSPAuthorityInfoAccessLocation (cert);
00349        if (loc == NULL)
00350            goto loser;
00351        includeServiceLocator = PR_FALSE;
00352     }
00353 
00354     /*
00355      * We need to create a list of one.
00356      */
00357     certs = CERT_NewCertList();
00358     if (certs == NULL)
00359        goto loser;
00360 
00361     if (CERT_AddCertToListTail (certs, cert) != SECSuccess)
00362        goto loser;
00363 
00364     /*
00365      * Now that cert is included in the list, we need to be careful
00366      * that we do not try to destroy it twice.  This will prevent that.
00367      */
00368     cert = NULL;
00369 
00370     response = CERT_GetEncodedOCSPResponse (NULL, certs, loc, now,
00371                                        includeServiceLocator,
00372                                        NULL, NULL, NULL);
00373     if (response == NULL)
00374        goto loser;
00375 
00376     if (fwrite (response->data, response->len, 1, out_file) != 1)
00377        goto loser;
00378 
00379     rv = SECSuccess;
00380 
00381 loser:
00382     if (response != NULL)
00383        SECITEM_FreeItem (response, PR_TRUE);
00384     if (certs != NULL)
00385        CERT_DestroyCertList (certs);
00386     if (loc != NULL && loc != responder_url)
00387        PORT_Free (loc);
00388     if (cert != NULL)
00389        CERT_DestroyCertificate (cert);
00390 
00391     return rv;
00392 }
00393 
00394 
00395 /*
00396  * Get the status for the specified certificate (whose nickname is "cert_name").
00397  * Directly use the OCSP function rather than doing a full verification.
00398  */
00399 static SECStatus
00400 get_cert_status (FILE *out_file, CERTCertDBHandle *handle,
00401                const char *cert_name, int64 verify_time)
00402 {
00403     CERTCertificate *cert = NULL;
00404     SECStatus rv = SECFailure;
00405 
00406     if (handle == NULL || cert_name == NULL)
00407        goto loser;
00408 
00409     cert = CERT_FindCertByNicknameOrEmailAddr (handle, (char *) cert_name);
00410     if (cert == NULL)
00411        goto loser;
00412 
00413     rv = CERT_CheckOCSPStatus (handle, cert, verify_time, NULL);
00414 
00415     fprintf (out_file, "Check of certificate \"%s\" ", cert_name); 
00416     if (rv == SECSuccess) {
00417        fprintf (out_file, "succeeded.\n"); 
00418     } else {
00419        const char *error_string = SECU_Strerror(PORT_GetError());
00420        fprintf (out_file, "failed.  Reason:\n");
00421        if (error_string != NULL && PORT_Strlen(error_string) > 0)
00422            fprintf (out_file, "%s\n", error_string);
00423        else
00424            fprintf (out_file, "Unknown\n");
00425     }
00426 
00427     rv = SECSuccess;
00428 
00429 loser:
00430     if (cert != NULL)
00431        CERT_DestroyCertificate (cert);
00432 
00433     return rv;
00434 }
00435 
00436 
00437 /*
00438  * Verify the specified certificate (whose nickname is "cert_name").
00439  * OCSP is already turned on, so we just need to call the standard
00440  * certificate verification API and let it do all the work.
00441  */
00442 static SECStatus
00443 verify_cert (FILE *out_file, CERTCertDBHandle *handle, const char *cert_name,
00444             SECCertUsage cert_usage, int64 verify_time)
00445 {
00446     CERTCertificate *cert = NULL;
00447     SECStatus rv = SECFailure;
00448 
00449     if (handle == NULL || cert_name == NULL)
00450        goto loser;
00451 
00452     cert = CERT_FindCertByNicknameOrEmailAddr (handle, (char *) cert_name);
00453     if (cert == NULL)
00454        goto loser;
00455 
00456     rv = CERT_VerifyCert (handle, cert, PR_TRUE, cert_usage, verify_time,
00457                        NULL, NULL);
00458 
00459     fprintf (out_file, "Verification of certificate \"%s\" ", cert_name); 
00460     if (rv == SECSuccess) {
00461        fprintf (out_file, "succeeded.\n"); 
00462     } else {
00463        const char *error_string = SECU_Strerror(PORT_GetError());
00464        fprintf (out_file, "failed.  Reason:\n");
00465        if (error_string != NULL && PORT_Strlen(error_string) > 0)
00466            fprintf (out_file, "%s\n", error_string);
00467        else
00468            fprintf (out_file, "Unknown\n");
00469     }
00470 
00471     rv = SECSuccess;
00472 
00473 loser:
00474     if (cert != NULL)
00475        CERT_DestroyCertificate (cert);
00476 
00477     return rv;
00478 }
00479 
00480 
00481 #ifdef NO_PP
00482 
00483 static SECStatus
00484 print_request (FILE *out_file, SECItem *data)
00485 {
00486     fprintf (out_file, "Cannot pretty-print request compiled with NO_PP.\n");
00487     return SECSuccess;
00488 }
00489 
00490 static SECStatus
00491 print_response (FILE *out_file, SECItem *data, CERTCertDBHandle *handle)
00492 {
00493     fprintf (out_file, "Cannot pretty-print response compiled with NO_PP.\n");
00494     return SECSuccess;
00495 }
00496 
00497 #else /* NO_PP */
00498 
00499 static void
00500 print_ocsp_version (FILE *out_file, SECItem *version, int level)
00501 {
00502     if (version->len > 0) {
00503        SECU_PrintInteger (out_file, version, "Version", level);
00504     } else {
00505        SECU_Indent (out_file, level);
00506        fprintf (out_file, "Version: DEFAULT\n");
00507     }
00508 }
00509 
00510 
00511 static void
00512 print_ocsp_cert_id (FILE *out_file, CERTOCSPCertID *cert_id, int level)
00513 {
00514     SECU_Indent (out_file, level);
00515     fprintf (out_file, "Cert ID:\n");
00516     level++;
00517 
00518     SECU_PrintAlgorithmID (out_file, &(cert_id->hashAlgorithm),
00519                         "Hash Algorithm", level);
00520     SECU_PrintAsHex (out_file, &(cert_id->issuerNameHash),
00521                    "Issuer Name Hash", level);
00522     SECU_PrintAsHex (out_file, &(cert_id->issuerKeyHash),
00523                    "Issuer Key Hash", level);
00524     SECU_PrintInteger (out_file, &(cert_id->serialNumber),
00525                      "Serial Number", level);
00526     /* XXX lookup the cert; if found, print something nice (nickname?) */
00527 }
00528 
00529 
00530 static void
00531 print_raw_certificates (FILE *out_file, SECItem **raw_certs, int level)
00532 {
00533     SECItem *raw_cert;
00534     int i = 0;
00535     char cert_label[50];
00536 
00537     SECU_Indent (out_file, level);
00538 
00539     if (raw_certs == NULL) {
00540        fprintf (out_file, "No Certificates.\n");
00541        return;
00542     }
00543 
00544     fprintf (out_file, "Certificate List:\n");
00545     while ((raw_cert = raw_certs[i++]) != NULL) {
00546        sprintf (cert_label, "Certificate (%d)", i);
00547        (void) SECU_PrintSignedData (out_file, raw_cert, cert_label, level + 1,
00548                                  SECU_PrintCertificate);
00549     }
00550 }
00551 
00552 
00553 static void
00554 print_ocsp_extensions (FILE *out_file, CERTCertExtension **extensions,
00555                      char *msg, int level)
00556 {
00557     if (extensions) {
00558        SECU_PrintExtensions (out_file, extensions, msg, level);
00559     } else {
00560        SECU_Indent (out_file, level);
00561        fprintf (out_file, "No %s\n", msg);
00562     }
00563 }
00564 
00565 
00566 static void
00567 print_single_request (FILE *out_file, ocspSingleRequest *single, int level)
00568 {
00569     print_ocsp_cert_id (out_file, single->reqCert, level);
00570     print_ocsp_extensions (out_file, single->singleRequestExtensions,
00571                         "Single Request Extensions", level);
00572 }
00573 
00574 
00575 /*
00576  * Decode the DER/BER-encoded item "data" as an OCSP request
00577  * and pretty-print the subfields.
00578  */
00579 static SECStatus
00580 print_request (FILE *out_file, SECItem *data)
00581 {
00582     CERTOCSPRequest *request;
00583     ocspTBSRequest *tbsRequest;
00584     int level = 0;
00585 
00586     PORT_Assert (out_file != NULL);
00587     PORT_Assert (data != NULL);
00588     if (out_file == NULL || data == NULL) {
00589        PORT_SetError (SEC_ERROR_INVALID_ARGS);
00590        return SECFailure;
00591     }
00592 
00593     request = CERT_DecodeOCSPRequest (data);
00594     if (request == NULL || request->tbsRequest == NULL)
00595        return SECFailure;
00596 
00597     tbsRequest = request->tbsRequest;
00598 
00599     fprintf (out_file, "TBS Request:\n");
00600     level++;
00601 
00602     print_ocsp_version (out_file, &(tbsRequest->version), level);
00603 
00604     /*
00605      * XXX Probably should be an interface to get the signer name
00606      * without looking inside the tbsRequest at all.
00607      */
00608     if (tbsRequest->requestorName != NULL) {
00609        SECU_Indent (out_file, level);
00610        fprintf (out_file, "XXX print the requestorName\n");
00611     } else {
00612        SECU_Indent (out_file, level);
00613        fprintf (out_file, "No Requestor Name.\n");
00614     }
00615 
00616     if (tbsRequest->requestList != NULL) {
00617        int i;
00618 
00619        for (i = 0; tbsRequest->requestList[i] != NULL; i++) {
00620            SECU_Indent (out_file, level);
00621            fprintf (out_file, "Request %d:\n", i);
00622            print_single_request (out_file, tbsRequest->requestList[i],
00623                               level + 1);
00624        }
00625     } else {
00626        fprintf (out_file, "Request list is empty.\n");
00627     }
00628 
00629     print_ocsp_extensions (out_file, tbsRequest->requestExtensions,
00630                         "Request Extensions", level);
00631 
00632     if (request->optionalSignature != NULL) {
00633        ocspSignature *whole_sig;
00634        SECItem rawsig;
00635 
00636        fprintf (out_file, "Signature:\n");
00637 
00638        whole_sig = request->optionalSignature;
00639        SECU_PrintAlgorithmID (out_file, &(whole_sig->signatureAlgorithm),
00640                             "Signature Algorithm", level);
00641 
00642        rawsig = whole_sig->signature;
00643        DER_ConvertBitString (&rawsig);
00644        SECU_PrintAsHex (out_file, &rawsig, "Signature", level);
00645 
00646        print_raw_certificates (out_file, whole_sig->derCerts, level);
00647 
00648        fprintf (out_file, "XXX verify the sig and print result\n");
00649     } else {
00650        fprintf (out_file, "No Signature\n");
00651     }
00652 
00653     CERT_DestroyOCSPRequest (request);
00654     return SECSuccess;
00655 }
00656 
00657 
00658 static void
00659 print_revoked_info (FILE *out_file, ocspRevokedInfo *revoked_info, int level)
00660 {
00661     SECU_PrintGeneralizedTime (out_file, &(revoked_info->revocationTime),
00662                             "Revocation Time", level);
00663 
00664     if (revoked_info->revocationReason != NULL) {
00665        SECU_PrintAsHex (out_file, revoked_info->revocationReason,
00666                       "Revocation Reason", level);
00667     } else {
00668        SECU_Indent (out_file, level);
00669        fprintf (out_file, "No Revocation Reason.\n");
00670     }
00671 }
00672 
00673 
00674 static void
00675 print_cert_status (FILE *out_file, ocspCertStatus *status, int level)
00676 {
00677     SECU_Indent (out_file, level);
00678     fprintf (out_file, "Status: ");
00679 
00680     switch (status->certStatusType) {
00681       case ocspCertStatus_good:
00682        fprintf (out_file, "Cert is good.\n");
00683        break;
00684       case ocspCertStatus_revoked:
00685        fprintf (out_file, "Cert has been revoked.\n");
00686        print_revoked_info (out_file, status->certStatusInfo.revokedInfo,
00687                          level + 1);
00688        break;
00689       case ocspCertStatus_unknown:
00690        fprintf (out_file, "Cert is unknown to responder.\n");
00691        break;
00692       default:
00693        fprintf (out_file, "Unrecognized status.\n");
00694        break;
00695     }
00696 }
00697 
00698 
00699 static void
00700 print_single_response (FILE *out_file, CERTOCSPSingleResponse *single,
00701                      int level)
00702 {
00703     print_ocsp_cert_id (out_file, single->certID, level);
00704 
00705     print_cert_status (out_file, single->certStatus, level);
00706 
00707     SECU_PrintGeneralizedTime (out_file, &(single->thisUpdate),
00708                             "This Update", level);
00709 
00710     if (single->nextUpdate != NULL) {
00711        SECU_PrintGeneralizedTime (out_file, single->nextUpdate,
00712                                "Next Update", level);
00713     } else {
00714        SECU_Indent (out_file, level);
00715        fprintf (out_file, "No Next Update\n");
00716     }
00717 
00718     print_ocsp_extensions (out_file, single->singleExtensions,
00719                         "Single Response Extensions", level);
00720 }
00721 
00722 
00723 static void
00724 print_responder_id (FILE *out_file, ocspResponderID *responderID, int level)
00725 {
00726     SECU_Indent (out_file, level);
00727     fprintf (out_file, "Responder ID ");
00728 
00729     switch (responderID->responderIDType) {
00730       case ocspResponderID_byName:
00731        fprintf (out_file, "(byName):\n");
00732        SECU_PrintName (out_file, &(responderID->responderIDValue.name),
00733                      "Name", level + 1);
00734        break;
00735       case ocspResponderID_byKey:
00736        fprintf (out_file, "(byKey):\n");
00737        SECU_PrintAsHex (out_file, &(responderID->responderIDValue.keyHash),
00738                       "Key Hash", level + 1);
00739        break;
00740       default:
00741        fprintf (out_file, "Unrecognized Responder ID Type\n");
00742        break;
00743     }
00744 }
00745 
00746 
00747 static void
00748 print_response_data (FILE *out_file, ocspResponseData *responseData, int level)
00749 {
00750     SECU_Indent (out_file, level);
00751     fprintf (out_file, "Response Data:\n");
00752     level++;
00753 
00754     print_ocsp_version (out_file, &(responseData->version), level);
00755 
00756     print_responder_id (out_file, responseData->responderID, level);
00757 
00758     SECU_PrintGeneralizedTime (out_file, &(responseData->producedAt),
00759                             "Produced At", level);
00760 
00761     if (responseData->responses != NULL) {
00762        int i;
00763 
00764        for (i = 0; responseData->responses[i] != NULL; i++) {
00765            SECU_Indent (out_file, level);
00766            fprintf (out_file, "Response %d:\n", i);
00767            print_single_response (out_file, responseData->responses[i],
00768                                level + 1);
00769        }
00770     } else {
00771        fprintf (out_file, "Response list is empty.\n");
00772     }
00773 
00774     print_ocsp_extensions (out_file, responseData->responseExtensions,
00775                         "Response Extensions", level);
00776 }
00777 
00778 
00779 static void
00780 print_basic_response (FILE *out_file, ocspBasicOCSPResponse *basic, int level)
00781 {
00782     SECItem rawsig;
00783 
00784     SECU_Indent (out_file, level);
00785     fprintf (out_file, "Basic OCSP Response:\n");
00786     level++;
00787 
00788     print_response_data (out_file, basic->tbsResponseData, level);
00789 
00790     SECU_PrintAlgorithmID (out_file,
00791                         &(basic->responseSignature.signatureAlgorithm),
00792                         "Signature Algorithm", level);
00793 
00794     rawsig = basic->responseSignature.signature;
00795     DER_ConvertBitString (&rawsig);
00796     SECU_PrintAsHex (out_file, &rawsig, "Signature", level);
00797 
00798     print_raw_certificates (out_file, basic->responseSignature.derCerts, level);
00799 }
00800 
00801 
00802 /*
00803  * Note this must match (exactly) the enumeration ocspResponseStatus.
00804  */
00805 static char *responseStatusNames[] = {
00806     "successful (Response has valid confirmations)",
00807     "malformedRequest (Illegal confirmation request)",
00808     "internalError (Internal error in issuer)",
00809     "tryLater (Try again later)",
00810     "unused ((4) is not used)",
00811     "sigRequired (Must sign the request)",
00812     "unauthorized (Request unauthorized)",
00813     "other (Status value out of defined range)"
00814 };
00815 
00816 /*
00817  * Decode the DER/BER-encoded item "data" as an OCSP response
00818  * and pretty-print the subfields.
00819  */
00820 static SECStatus
00821 print_response (FILE *out_file, SECItem *data, CERTCertDBHandle *handle)
00822 {
00823     CERTOCSPResponse *response;
00824     int level = 0;
00825 
00826     PORT_Assert (out_file != NULL);
00827     PORT_Assert (data != NULL);
00828     if (out_file == NULL || data == NULL) {
00829        PORT_SetError (SEC_ERROR_INVALID_ARGS);
00830        return SECFailure;
00831     }
00832 
00833     response = CERT_DecodeOCSPResponse (data);
00834     if (response == NULL)
00835        return SECFailure;
00836 
00837     PORT_Assert (response->statusValue <= ocspResponse_other);
00838     fprintf (out_file, "Response Status: %s\n",
00839             responseStatusNames[response->statusValue]);
00840 
00841     if (response->statusValue == ocspResponse_successful) {
00842        ocspResponseBytes *responseBytes = response->responseBytes;
00843        SECStatus sigStatus;
00844        CERTCertificate *signerCert = NULL;
00845 
00846        PORT_Assert (responseBytes != NULL);
00847 
00848        level++;
00849        fprintf (out_file, "Response Bytes:\n");
00850        SECU_PrintObjectID (out_file, &(responseBytes->responseType),
00851                          "Response Type", level);
00852        switch (response->responseBytes->responseTypeTag) {
00853          case SEC_OID_PKIX_OCSP_BASIC_RESPONSE:
00854            print_basic_response (out_file,
00855                               responseBytes->decodedResponse.basic,
00856                               level);
00857            break;
00858          default:
00859            SECU_Indent (out_file, level);
00860            fprintf (out_file, "Unknown response syntax\n");
00861            break;
00862        }
00863 
00864        sigStatus = CERT_VerifyOCSPResponseSignature (response, handle,
00865                                                 NULL, &signerCert, NULL);
00866        SECU_Indent (out_file, level);
00867        fprintf (out_file, "Signature verification ");
00868        if (sigStatus != SECSuccess) {
00869            fprintf (out_file, "failed: %s\n", SECU_Strerror (PORT_GetError()));
00870        } else {
00871            fprintf (out_file, "succeeded.\n");
00872            if (signerCert != NULL) {
00873               SECU_PrintName (out_file, &signerCert->subject, "Signer",
00874                             level);
00875               CERT_DestroyCertificate (signerCert);
00876            } else {
00877               SECU_Indent (out_file, level);
00878               fprintf (out_file, "No signer cert returned?\n");
00879            }
00880        }
00881     } else {
00882        SECU_Indent (out_file, level);
00883        fprintf (out_file, "Unsuccessful response, no more information.\n");
00884     }
00885 
00886     CERT_DestroyOCSPResponse (response);
00887     return SECSuccess;
00888 }
00889 
00890 #endif /* NO_PP */
00891 
00892 
00893 static SECStatus
00894 cert_usage_from_char (const char *cert_usage_str, SECCertUsage *cert_usage)
00895 {
00896     PORT_Assert (cert_usage_str != NULL);
00897     PORT_Assert (cert_usage != NULL);
00898 
00899     if (PORT_Strlen (cert_usage_str) != 1)
00900        return SECFailure;
00901 
00902     switch (*cert_usage_str) {
00903       case 'c':
00904        *cert_usage = certUsageSSLClient;
00905        break;
00906       case 's':
00907        *cert_usage = certUsageSSLServer;
00908        break;
00909       case 'e':
00910        *cert_usage = certUsageEmailRecipient;
00911        break;
00912       case 'E':
00913        *cert_usage = certUsageEmailSigner;
00914        break;
00915       case 'S':
00916        *cert_usage = certUsageObjectSigner;
00917        break;
00918       case 'C':
00919        *cert_usage = certUsageVerifyCA;
00920        break;
00921       default:
00922        return SECFailure;
00923     }
00924 
00925     return SECSuccess;
00926 }
00927 
00928 
00929 int
00930 main (int argc, char **argv)
00931 {
00932     int               retval;
00933     char      *program_name;
00934     PRFileDesc       *in_file;
00935     FILE      *out_file;    /* not PRFileDesc until SECU accepts it */
00936     int               crequest, dresponse;
00937     int               prequest, presponse;
00938     int               ccert, vcert;
00939     const char       *db_dir, *date_str, *cert_usage_str, *name;
00940     const char       *responder_name, *responder_url, *signer_name;
00941     PRBool     add_acceptable_responses, add_service_locator;
00942     SECItem   *data = NULL;
00943     PLOptState       *optstate;
00944     SECStatus  rv;
00945     CERTCertDBHandle *handle = NULL;
00946     SECCertUsage cert_usage;
00947     int64      verify_time;
00948 
00949     retval = -1;            /* what we return/exit with on error */
00950 
00951     program_name = PL_strrchr(argv[0], '/');
00952     program_name = program_name ? (program_name + 1) : argv[0];
00953 
00954     in_file = PR_STDIN;
00955     out_file = stdout;
00956 
00957     crequest = 0;
00958     dresponse = 0;
00959     prequest = 0;
00960     presponse = 0;
00961     ccert = 0;
00962     vcert = 0;
00963 
00964     db_dir = NULL;
00965     date_str = NULL;
00966     cert_usage_str = NULL;
00967     name = NULL;
00968     responder_name = NULL;
00969     responder_url = NULL;
00970     signer_name = NULL;
00971 
00972     add_acceptable_responses = PR_FALSE;
00973     add_service_locator = PR_FALSE;
00974 
00975     optstate = PL_CreateOptState (argc, argv, "AHLPR:S:V:d:l:pr:s:t:u:w:");
00976     if (optstate == NULL) {
00977        SECU_PrintError (program_name, "PL_CreateOptState failed");
00978        return retval;
00979     }
00980 
00981     while (PL_GetNextOpt (optstate) == PL_OPT_OK) {
00982        switch (optstate->option) {
00983          case '?':
00984            short_usage (program_name);
00985            return retval;
00986 
00987          case 'A':
00988            add_acceptable_responses = PR_TRUE;
00989            break;
00990 
00991          case 'H':
00992            long_usage (program_name);
00993            return retval;
00994 
00995          case 'L':
00996            add_service_locator = PR_TRUE;
00997            break;
00998 
00999          case 'P':
01000            presponse = 1;
01001            break;
01002 
01003          case 'R':
01004            dresponse = 1;
01005            name = optstate->value;
01006            break;
01007 
01008          case 'S':
01009            ccert = 1;
01010            name = optstate->value;
01011            break;
01012 
01013          case 'V':
01014            vcert = 1;
01015            name = optstate->value;
01016            break;
01017 
01018          case 'd':
01019            db_dir = optstate->value;
01020            break;
01021 
01022          case 'l':
01023            responder_url = optstate->value;
01024            break;
01025 
01026          case 'p':
01027            prequest = 1;
01028            break;
01029 
01030          case 'r':
01031            crequest = 1;
01032            name = optstate->value;
01033            break;
01034 
01035          case 's':
01036            signer_name = optstate->value;
01037            break;
01038 
01039          case 't':
01040            responder_name = optstate->value;
01041            break;
01042 
01043          case 'u':
01044            cert_usage_str = optstate->value;
01045            break;
01046 
01047          case 'w':
01048            date_str = optstate->value;
01049            break;
01050        }
01051     }
01052 
01053     PL_DestroyOptState(optstate);
01054 
01055     if ((crequest + dresponse + prequest + presponse + ccert + vcert) != 1) {
01056        PR_fprintf (PR_STDERR, "%s: must specify exactly one command\n\n",
01057                   program_name);
01058        short_usage (program_name);
01059        return retval;
01060     }
01061 
01062     if (vcert) {
01063        if (cert_usage_str == NULL) {
01064            PR_fprintf (PR_STDERR, "%s: verification requires cert usage\n\n",
01065                      program_name);
01066            short_usage (program_name);
01067            return retval;
01068        }
01069 
01070        rv = cert_usage_from_char (cert_usage_str, &cert_usage);
01071        if (rv != SECSuccess) {
01072            PR_fprintf (PR_STDERR, "%s: invalid cert usage (\"%s\")\n\n",
01073                      program_name, cert_usage_str);
01074            long_usage (program_name);
01075            return retval;
01076        }
01077     }
01078 
01079     if (ccert + vcert) {
01080        if (responder_url != NULL || responder_name != NULL) {
01081            /*
01082             * To do a full status check, both the URL and the cert name
01083             * of the responder must be specified if either one is.
01084             */
01085            if (responder_url == NULL || responder_name == NULL) {
01086               if (responder_url == NULL)
01087                   PR_fprintf (PR_STDERR,
01088                             "%s: must also specify responder location\n\n",
01089                             program_name);
01090               else
01091                   PR_fprintf (PR_STDERR,
01092                             "%s: must also specify responder name\n\n",
01093                             program_name);
01094               short_usage (program_name);
01095               return retval;
01096            }
01097        }
01098 
01099        if (date_str != NULL) {
01100            rv = DER_AsciiToTime (&verify_time, (char *) date_str);
01101            if (rv != SECSuccess) {
01102               SECU_PrintError (program_name, "error converting time string");
01103               PR_fprintf (PR_STDERR, "\n");
01104               long_usage (program_name);
01105               return retval;
01106            }
01107        } else {
01108            verify_time = PR_Now();
01109        }
01110     }
01111 
01112     retval = -2;            /* errors change from usage to runtime */
01113 
01114     /*
01115      * Initialize the NSPR and Security libraries.
01116      */
01117     PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
01118     db_dir = SECU_ConfigDirectory (db_dir);
01119     rv = NSS_Init (db_dir);
01120     if (rv != SECSuccess) {
01121        SECU_PrintError (program_name, "NSS_Init failed");
01122        goto prdone;
01123     }
01124     SECU_RegisterDynamicOids();
01125 
01126     if (prequest + presponse) {
01127        data = read_file_into_item (in_file, siBuffer);
01128        if (data == NULL) {
01129            SECU_PrintError (program_name, "problem reading input");
01130            goto nssdone;
01131        }
01132     }
01133 
01134     if (crequest + dresponse + presponse + ccert + vcert) {
01135        handle = CERT_GetDefaultCertDB();
01136        if (handle == NULL) {
01137            SECU_PrintError (program_name, "problem getting certdb handle");
01138            goto nssdone;
01139        }
01140 
01141        /*
01142         * It would be fine to do the enable for all of these commands,
01143         * but this way we check that everything but an overall verify
01144         * can be done without it.  That is, that the individual pieces
01145         * work on their own.
01146         */
01147        if (vcert) {
01148            rv = CERT_EnableOCSPChecking (handle);
01149            if (rv != SECSuccess) {
01150               SECU_PrintError (program_name, "error enabling OCSP checking");
01151               goto nssdone;
01152            }
01153        }
01154 
01155        if ((ccert + vcert) && (responder_name != NULL)) {
01156            rv = CERT_SetOCSPDefaultResponder (handle, responder_url,
01157                                           responder_name);
01158            if (rv != SECSuccess) {
01159               SECU_PrintError (program_name,
01160                              "error setting default responder");
01161               goto nssdone;
01162            }
01163 
01164            rv = CERT_EnableOCSPDefaultResponder (handle);
01165            if (rv != SECSuccess) {
01166               SECU_PrintError (program_name,
01167                              "error enabling default responder");
01168               goto nssdone;
01169            }
01170        }
01171     }
01172 
01173 #define NOTYET(opt)                                            \
01174        {                                                       \
01175            PR_fprintf (PR_STDERR, "%s not yet working\n", opt);       \
01176            exit (-1);                                                 \
01177        }
01178 
01179     if (crequest) {
01180        if (signer_name != NULL) {
01181            NOTYET("-s");
01182        }
01183        rv = create_request (out_file, handle, name, add_service_locator,
01184                           add_acceptable_responses);
01185     } else if (dresponse) {
01186        if (signer_name != NULL) {
01187            NOTYET("-s");
01188        }
01189        rv = dump_response (out_file, handle, name, responder_url);
01190     } else if (prequest) {
01191        rv = print_request (out_file, data);
01192     } else if (presponse) {
01193        rv = print_response (out_file, data, handle);
01194     } else if (ccert) {
01195        if (signer_name != NULL) {
01196            NOTYET("-s");
01197        }
01198        rv = get_cert_status (out_file, handle, name, verify_time);
01199     } else if (vcert) {
01200        if (signer_name != NULL) {
01201            NOTYET("-s");
01202        }
01203        rv = verify_cert (out_file, handle, name, cert_usage, verify_time);
01204     }
01205 
01206     if (rv != SECSuccess)
01207        SECU_PrintError (program_name, "error performing requested operation");
01208     else
01209        retval = 0;
01210 
01211 nssdone:
01212     if (data != NULL) {
01213        SECITEM_FreeItem (data, PR_TRUE);
01214     }
01215 
01216     if (handle != NULL) {
01217        CERT_DisableOCSPDefaultResponder(handle);        
01218        CERT_DisableOCSPChecking (handle);
01219     }
01220 
01221     if (NSS_Shutdown () != SECSuccess) {
01222        retval = 1;
01223     }
01224 
01225 prdone:
01226     PR_Cleanup ();
01227     return retval;
01228 }