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