security_file_certgen.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9#include "squid.h"
10#include "base/TextException.h"
11#include "debug/Stream.h"
13#include "sbuf/Stream.h"
15#include "ssl/crtd_message.h"
16#include "time/gadgets.h"
17
18#include <cstring>
19#include <iostream>
20#include <sstream>
21#include <stdexcept>
22#include <string>
23#if HAVE_GETOPT_H
24#include <getopt.h>
25#endif
26
77static const char *const B_KBYTES_STR = "KB";
78static const char *const B_MBYTES_STR = "MB";
79static const char *const B_GBYTES_STR = "GB";
80static const char *const B_BYTES_STR = "B";
81
86static size_t parseBytesUnits(const char * unit)
87{
88 if (!strncasecmp(unit, B_BYTES_STR, strlen(B_BYTES_STR)) ||
89 !strncasecmp(unit, "", strlen(unit)))
90 return 1;
91
92 if (!strncasecmp(unit, B_KBYTES_STR, strlen(B_KBYTES_STR)))
93 return 1 << 10;
94
95 if (!strncasecmp(unit, B_MBYTES_STR, strlen(B_MBYTES_STR)))
96 return 1 << 20;
97
98 if (!strncasecmp(unit, B_GBYTES_STR, strlen(B_GBYTES_STR)))
99 return 1 << 30;
100
101 throw TextException(ToSBuf("Unknown bytes unit: ", unit), Here());
102}
103
106static size_t
107parseBytesOptionValue(const char * const name, const char * const value)
108{
109 // Find number from string beginning.
110 char const * number_begin = value;
111 char const * number_end = value;
112
113 while ((*number_end >= '0' && *number_end <= '9')) {
114 ++number_end;
115 }
116
117 if (number_end <= number_begin)
118 throw TextException(ToSBuf("expecting a decimal number at the beginning of ", name, " value but got: ", value), Here());
119
120 std::string number(number_begin, number_end - number_begin);
121 std::istringstream in(number);
122 size_t base = 0;
123 if (!(in >> base) || !in.eof())
124 throw TextException(ToSBuf("unsupported integer part of ", name, " value: ", number), Here());
125
126 const auto multiplier = parseBytesUnits(number_end);
127 static_assert(std::is_unsigned<decltype(multiplier * base)>::value, "no signed overflows");
128 const auto product = multiplier * base;
129 if (base && multiplier != product / base)
130 throw TextException(ToSBuf(name, " size too large: ", value), Here());
131
132 return product;
133}
134
136static void usage()
137{
138 std::string example_host_name = "host.dom";
139 std::string request_string = Ssl::CrtdMessage::param_host + "=" + example_host_name;
140 std::stringstream request_string_size_stream;
141 request_string_size_stream << request_string.length();
142 std::string help_string =
143 "usage: security_file_certgen -hv -s directory -M size -b fs_block_size\n"
144 "\t-h Help\n"
145 "\t-v Version\n"
146 "\t-s directory Directory path of SSL storage database.\n"
147 "\t-M size Maximum size of SSL certificate disk storage.\n"
148 "\t-b fs_block_size File system block size in bytes. Need for processing\n"
149 "\t natural size of certificate on disk. Default value is\n"
150 "\t 2048 bytes.\n"
151 "\n"
152 "After running write requests in the next format:\n"
153 "<request code><whitespace><body_len><whitespace><body>\n"
154 "There are two kind of request now:\n"
155 + Ssl::CrtdMessage::code_new_certificate + " " + request_string_size_stream.str() + " " + request_string + "\n" +
156 "\tCreate new private key and selfsigned certificate for \"host.dom\".\n"
157 + Ssl::CrtdMessage::code_new_certificate + " xxx " + request_string + "\n" +
158 "-----BEGIN CERTIFICATE-----\n"
159 "...\n"
160 "-----END CERTIFICATE-----\n"
161 "-----BEGIN RSA PRIVATE KEY-----\n"
162 "...\n"
163 "-----END RSA PRIVATE KEY-----\n"
164 "\tCreate new private key and certificate request for \"host.dom\"\n"
165 "\tSign new request by received certificate and private key.\n"
166 "usage: security_file_certgen -c -s ssl_store_path\n"
167 "\t-c Init ssl db directories and exit.\n";
168 std::cerr << help_string << std::endl;
169}
170
172static bool processNewRequest(Ssl::CrtdMessage & request_message, std::string const & db_path, size_t max_db_size, size_t fs_block_size)
173{
174 Ssl::CertificateProperties certProperties;
175 request_message.parseRequest(certProperties);
176
177 // TODO: create a DB object only once, instead re-allocating here on every call.
178 std::unique_ptr<Ssl::CertificateDb> db;
179 if (!db_path.empty())
180 db.reset(new Ssl::CertificateDb(db_path, max_db_size, fs_block_size));
181
183 Security::PrivateKeyPointer pkey;
185 std::string &certKey = Ssl::OnDiskCertificateDbKey(certProperties);
186
187 bool dbFailed = false;
188 try {
189 if (db)
190 db->find(certKey, certProperties.mimicCert, cert, pkey);
191
192 } catch (...) {
193 dbFailed = true;
194 debugs(83, DBG_IMPORTANT, "ERROR: Database search failure: " << CurrentException <<
195 Debug::Extra << "database location: " << db_path);
196 }
197
198 if (!cert || !pkey) {
199 if (!Ssl::generateSslCertificate(cert, pkey, certProperties))
200 throw TextException("Cannot create ssl certificate or private key.", Here());
201
202 try {
203 /* XXX: this !dbFailed condition prevents the helper fixing DB issues
204 by adding cleanly generated certs. Which is not consistent with other
205 data caches used by Squid - they purge broken entries and allow clean
206 entries to later try and fix the issue.
207 We leave it in place now only to avoid breaking existing installations
208 behaviour with version 1.x of the helper.
209
210 TODO: remove the !dbFailed condition when fixing the CertificateDb
211 object lifecycle and formally altering the helper behaviour.
212 */
213 if (!dbFailed && db && !db->addCertAndPrivateKey(certKey, cert, pkey, certProperties.mimicCert))
214 throw TextException("Cannot add certificate to db.", Here());
215
216 } catch (...) {
217 dbFailed = true;
218 debugs(83, DBG_IMPORTANT, "ERROR: Database update failure: " << CurrentException <<
219 Debug::Extra << "database location: " << db_path);
220 }
221 }
222
223 std::string bufferToWrite;
224 if (!Ssl::writeCertAndPrivateKeyToMemory(cert, pkey, bufferToWrite))
225 throw TextException("Cannot write ssl certificate or/and private key to memory.", Here());
226
228 response_message.setCode("OK");
229 response_message.setBody(bufferToWrite);
230
231 // Use the '\1' char as end-of-message character
232 std::cout << response_message.compose() << '\1' << std::flush;
233
234 return true;
235}
236
238int main(int argc, char *argv[])
239{
240 try {
241 Debug::NameThisHelper("sslcrtd_program");
242
243 size_t max_db_size = 0;
244 size_t fs_block_size = 0;
245 int8_t c;
246 bool create_new_db = false;
247 std::string db_path;
248 // process options.
249 while ((c = getopt(argc, argv, "dchvs:M:b:")) != -1) {
250 switch (c) {
251 case 'd':
252 debug_enabled = 1;
253 break;
254 case 'b':
255 fs_block_size = parseBytesOptionValue("-b", optarg);
256 break;
257 case 's':
258 db_path = optarg;
259 break;
260 case 'M':
261 // use of -M without -s is probably an admin mistake, so make it an error
262 if (db_path.empty()) {
263 throw TextException("Error -M option requires an -s parameter be set first.", Here());
264 }
265 max_db_size = parseBytesOptionValue("-M", optarg);
266 break;
267 case 'v':
268 std::cout << "security_file_certgen version " << VERSION << std::endl;
269 exit(EXIT_SUCCESS);
270 break;
271 case 'c':
272 create_new_db = true;
273 break;
274 case 'h':
275 usage();
276 exit(EXIT_SUCCESS);
277 default:
278 exit(EXIT_FAILURE);
279 }
280 }
281
282 // when -s is used, -M is required
283 if (!db_path.empty() && max_db_size == 0)
284 throw TextException("security_file_certgen -s requires an -M parameter", Here());
285
286 if (create_new_db) {
287 // when -c is used, -s is required (implying also -M, which is checked above)
288 if (db_path.empty())
289 throw TextException("security_file_certgen is missing the required parameter. There should be -s and -M parameters when -c is used.", Here());
290
291 std::cout << "Initialization SSL db..." << std::endl;
293 std::cout << "Done" << std::endl;
294 exit(EXIT_SUCCESS);
295 }
296
297 // only do filesystem checks when a path (-s) is given
298 if (!db_path.empty()) {
299 if (fs_block_size == 0) {
300 struct statvfs sfs;
301
302 if (xstatvfs(db_path.c_str(), &sfs)) {
303 fs_block_size = 2048;
304 } else {
305 fs_block_size = sfs.f_frsize;
306 // Sanity check; make sure we have a meaningful value.
307 if (fs_block_size < 512)
308 fs_block_size = 2048;
309 }
310 }
311 Ssl::CertificateDb::Check(db_path, max_db_size, fs_block_size);
312 }
313
314 // Initialize SSL subsystem
316 // process request.
317 for (;;) {
321
322 while (parse_result == Ssl::CrtdMessage::INCOMPLETE) {
323 if (fgets(request, HELPER_INPUT_BUFFER, stdin) == nullptr)
324 exit(EXIT_FAILURE);
325 size_t gcount = strlen(request);
326 parse_result = request_message.parse(request, gcount);
327 }
328
329 if (parse_result == Ssl::CrtdMessage::ERROR) {
330 throw TextException("Cannot parse request message.", Here());
331 } else if (request_message.getCode() == Ssl::CrtdMessage::code_new_certificate) {
332 processNewRequest(request_message, db_path, max_db_size, fs_block_size);
333 } else {
334 throw TextException(ToSBuf("Unknown request code: \"", request_message.getCode(), "\"."), Here());
335 }
336 std::cout.flush();
337 }
338 } catch (...) {
339 debugs(83, DBG_CRITICAL, "FATAL: Cannot generate certificates: " << CurrentException);
340 return EXIT_FAILURE;
341 }
342 return EXIT_SUCCESS;
343}
344
#define Here()
source code location of the caller
Definition: Here.h:15
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define HELPER_INPUT_BUFFER
Definition: UserRequest.cc:24
static void NameThisHelper(const char *name)
Definition: debug.cc:382
static std::ostream & Extra(std::ostream &os)
prefixes each grouped debugs() line after the first one in the group
Definition: Stream.h:117
static void Create(std::string const &db_path)
Create and initialize a database under the db_path.
static void Check(std::string const &db_path, size_t max_db_size, size_t fs_block_size)
Check the database stored under the db_path.
Security::CertPointer mimicCert
Certificate to mimic.
Definition: gadgets.h:229
void setCode(std::string const &aCode)
Set new request/reply code to compose.
void setBody(std::string const &aBody)
Set new body to encode.
static const std::string code_new_certificate
String code for "new_certificate" messages.
Definition: crtd_message.h:76
ParseResult
Parse result codes.
Definition: crtd_message.h:29
static const std::string param_host
Parameter name for passing hostname.
Definition: crtd_message.h:78
std::string const & getCode() const
Current response/request code. If parsing is not finished the method may return incompleted code.
ParseResult parse(const char *buffer, size_t len)
Definition: crtd_message.cc:23
void parseRequest(CertificateProperties &)
orchestrates entire request parsing
std::string compose() const
an std::runtime_error with thrower location info
Definition: TextException.h:21
int debug_enabled
Definition: debug.cc:13
#define DBG_IMPORTANT
Definition: Stream.h:41
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
#define DBG_CRITICAL
Definition: Stream.h:40
char * db_path
TDB_CONTEXT * db
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition: getopt.c:62
char * optarg
Definition: getopt.c:51
std::string & OnDiskCertificateDbKey(const CertificateProperties &)
Definition: gadgets.cc:267
bool generateSslCertificate(Security::CertPointer &cert, Security::PrivateKeyPointer &pkey, CertificateProperties const &properties)
Definition: gadgets.cc:711
bool writeCertAndPrivateKeyToMemory(Security::CertPointer const &cert, Security::PrivateKeyPointer const &pkey, std::string &bufferToWrite)
Definition: gadgets.cc:97
#define VERSION
void SQUID_OPENSSL_init_ssl(void)
Definition: openssl.h:308
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
int main(int argc, char *argv[])
This is the external security_file_certgen process.
static const char *const B_MBYTES_STR
static size_t parseBytesOptionValue(const char *const name, const char *const value)
static const char *const B_GBYTES_STR
static void usage()
Print help using response code.
static const char *const B_KBYTES_STR
static const char *const B_BYTES_STR
static size_t parseBytesUnits(const char *unit)
static bool processNewRequest(Ssl::CrtdMessage &request_message, std::string const &db_path, size_t max_db_size, size_t fs_block_size)
Process new request message.
int xstatvfs(const char *path, struct statvfs *sfs)
Definition: statvfs.cc:22
unsigned long f_frsize
Definition: statvfs.h:45
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
number
Definition: testStatHist.cc:16

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors