1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 tw=80 et: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* This simple program takes a database directory, and one or more tuples like
8  * <typeOfResponse> <CertNick> <ExtraCertNick> <outPutFilename>
9  * to generate (one or more) ocsp responses.
10  */
11 
12 #include <stdio.h>
13 #include <string>
14 #include <vector>
15 
16 #include "mozilla/ArrayUtils.h"
17 
18 #include "cert.h"
19 #include "nspr.h"
20 #include "nss.h"
21 #include "plarenas.h"
22 #include "prerror.h"
23 #include "ssl.h"
24 #include "secerr.h"
25 
26 #include "OCSPCommon.h"
27 #include "ScopedNSSTypes.h"
28 #include "TLSServer.h"
29 
30 using namespace mozilla;
31 using namespace mozilla::test;
32 
33 struct OCSPResponseName {
34   const char* mTypeString;
35   const OCSPResponseType mORT;
36 };
37 
38 const static OCSPResponseName kOCSPResponseNameList[] = {
39     {"good", ORTGood},                         // the certificate is good
40     {"good-delegated", ORTDelegatedIncluded},  // the certificate is good, using
41                                                // a delegated signer
42     {"revoked", ORTRevoked},                // the certificate has been revoked
43     {"unknown", ORTUnknown},                // the responder doesn't know if the
44                                             //   cert is good
45     {"goodotherca", ORTGoodOtherCA},        // the wrong CA has signed the
46                                             //   response
47     {"expiredresponse", ORTExpired},        // the signature on the response has
48                                             //   expired
49     {"oldvalidperiod", ORTExpiredFreshCA},  // fresh signature, but old validity
50                                             //   period
51     {"empty", ORTEmpty},                    // an empty stapled response
52 
53     {"malformed", ORTMalformed},         // the response from the responder
54                                          //   was malformed
55     {"serverr", ORTSrverr},              // the response indicates there was a
56                                          //   server error
57     {"trylater", ORTTryLater},           // the responder replied with
58                                          //   "try again later"
59     {"resp-unsigned", ORTNeedsSig},      // the response needs a signature
60     {"unauthorized", ORTUnauthorized},   // the responder does not know about
61                                          //   the cert
62     {"bad-signature", ORTBadSignature},  // the response has a bad signature
63     {"longvalidityalmostold",
64      ORTLongValidityAlmostExpired},  // the response is
65                                      // still valid, but the generation
66                                      // is almost a year old
67     {"ancientstillvalid", ORTAncientAlmostExpired},  // The response is still
68                                                      // valid but the generation
69                                                      // is almost two years old
70 };
71 
StringToOCSPResponseType(const char * respText,OCSPResponseType * OCSPType)72 bool StringToOCSPResponseType(const char* respText,
73                               /*out*/ OCSPResponseType* OCSPType) {
74   if (!OCSPType) {
75     return false;
76   }
77   for (auto ocspResponseName : kOCSPResponseNameList) {
78     if (strcmp(respText, ocspResponseName.mTypeString) == 0) {
79       *OCSPType = ocspResponseName.mORT;
80       return true;
81     }
82   }
83   return false;
84 }
85 
WriteResponse(const char * filename,const SECItem * item)86 bool WriteResponse(const char* filename, const SECItem* item) {
87   if (!filename || !item || !item->data) {
88     PR_fprintf(PR_STDERR, "invalid parameters to WriteResponse");
89     return false;
90   }
91 
92   UniquePRFileDesc outFile(
93       PR_Open(filename, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0644));
94   if (!outFile) {
95     PrintPRError("cannot open file for writing");
96     return false;
97   }
98   int32_t rv = PR_Write(outFile.get(), item->data, item->len);
99   if (rv < 0 || (uint32_t)rv != item->len) {
100     PrintPRError("File write failure");
101     return false;
102   }
103 
104   return true;
105 }
106 
main(int argc,char * argv[])107 int main(int argc, char* argv[]) {
108   if (argc < 7 || (argc - 7) % 5 != 0) {
109     PR_fprintf(
110         PR_STDERR,
111         "usage: %s <NSS DB directory> <responsetype> "
112         "<cert_nick> <extranick> <this_update_skew> <outfilename> [<resptype> "
113         "<cert_nick> <extranick> <this_update_skew> <outfilename>]* \n",
114         argv[0]);
115     exit(EXIT_FAILURE);
116   }
117   SECStatus rv = InitializeNSS(argv[1]);
118   if (rv != SECSuccess) {
119     PR_fprintf(PR_STDERR, "Failed to initialize NSS\n");
120     exit(EXIT_FAILURE);
121   }
122   UniquePLArenaPool arena(PORT_NewArena(256 * argc));
123   if (!arena) {
124     PrintPRError("PORT_NewArena failed");
125     exit(EXIT_FAILURE);
126   }
127 
128   for (int i = 2; i + 3 < argc; i += 5) {
129     const char* ocspTypeText = argv[i];
130     const char* certNick = argv[i + 1];
131     const char* extraCertname = argv[i + 2];
132     const char* skewChars = argv[i + 3];
133     const char* filename = argv[i + 4];
134 
135     OCSPResponseType ORT;
136     if (!StringToOCSPResponseType(ocspTypeText, &ORT)) {
137       PR_fprintf(PR_STDERR, "Cannot generate OCSP response of type %s\n",
138                  ocspTypeText);
139       exit(EXIT_FAILURE);
140     }
141 
142     UniqueCERTCertificate cert(PK11_FindCertFromNickname(certNick, nullptr));
143     if (!cert) {
144       PrintPRError("PK11_FindCertFromNickname failed");
145       PR_fprintf(PR_STDERR, "Failed to find certificate with nick '%s'\n",
146                  certNick);
147       exit(EXIT_FAILURE);
148     }
149 
150     time_t skew = static_cast<time_t>(atoll(skewChars));
151 
152     SECItemArray* response =
153         GetOCSPResponseForType(ORT, cert, arena, extraCertname, skew);
154     if (!response) {
155       PR_fprintf(PR_STDERR,
156                  "Failed to generate OCSP response of type %s "
157                  "for %s\n",
158                  ocspTypeText, certNick);
159       exit(EXIT_FAILURE);
160     }
161 
162     if (!WriteResponse(filename, &response->items[0])) {
163       PR_fprintf(PR_STDERR, "Failed to write file %s\n", filename);
164       exit(EXIT_FAILURE);
165     }
166   }
167   return 0;
168 }
169