security_file_certgen.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2021 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"
12 #include "SquidTime.h"
13 #include "ssl/crtd_message.h"
14 
15 #include <cstring>
16 #include <iostream>
17 #include <sstream>
18 #include <stdexcept>
19 #include <string>
20 #if HAVE_GETOPT_H
21 #include <getopt.h>
22 #endif
23 
74 static const char *const B_KBYTES_STR = "KB";
75 static const char *const B_MBYTES_STR = "MB";
76 static const char *const B_GBYTES_STR = "GB";
77 static const char *const B_BYTES_STR = "B";
78 
80 time_t getCurrentTime(void)
81 {
82  struct timeval currentTime;
83 #if GETTIMEOFDAY_NO_TZP
84  gettimeofday(&currentTime);
85 #else
86  gettimeofday(&currentTime, nullptr);
87 #endif
88  return currentTime.tv_sec;
89 }
90 
95 static size_t parseBytesUnits(const char * unit)
96 {
97  if (!strncasecmp(unit, B_BYTES_STR, strlen(B_BYTES_STR)) ||
98  !strncasecmp(unit, "", strlen(unit)))
99  return 1;
100 
101  if (!strncasecmp(unit, B_KBYTES_STR, strlen(B_KBYTES_STR)))
102  return 1 << 10;
103 
104  if (!strncasecmp(unit, B_MBYTES_STR, strlen(B_MBYTES_STR)))
105  return 1 << 20;
106 
107  if (!strncasecmp(unit, B_GBYTES_STR, strlen(B_GBYTES_STR)))
108  return 1 << 30;
109 
110  std::cerr << "WARNING: Unknown bytes unit '" << unit << "'" << std::endl;
111 
112  return 0;
113 }
114 
116 static bool parseBytesOptionValue(size_t * bptr, char const * value)
117 {
118  // Find number from string beginning.
119  char const * number_begin = value;
120  char const * number_end = value;
121 
122  while ((*number_end >= '0' && *number_end <= '9')) {
123  ++number_end;
124  }
125 
126  std::string number(number_begin, number_end - number_begin);
127  std::istringstream in(number);
128  int d = 0;
129  if (!(in >> d))
130  return false;
131 
132  int m;
133  if ((m = parseBytesUnits(number_end)) == 0) {
134  return false;
135  }
136 
137  *bptr = static_cast<size_t>(m * d);
138  if (static_cast<long>(*bptr * 2) != m * d * 2)
139  return false;
140 
141  return true;
142 }
143 
145 static void usage()
146 {
147  std::string example_host_name = "host.dom";
148  std::string request_string = Ssl::CrtdMessage::param_host + "=" + example_host_name;
149  std::stringstream request_string_size_stream;
150  request_string_size_stream << request_string.length();
151  std::string help_string =
152  "usage: security_file_certgen -hv -s directory -M size -b fs_block_size\n"
153  "\t-h Help\n"
154  "\t-v Version\n"
155  "\t-s directory Directory path of SSL storage database.\n"
156  "\t-M size Maximum size of SSL certificate disk storage.\n"
157  "\t-b fs_block_size File system block size in bytes. Need for processing\n"
158  "\t natural size of certificate on disk. Default value is\n"
159  "\t 2048 bytes.\n"
160  "\n"
161  "After running write requests in the next format:\n"
162  "<request code><whitespace><body_len><whitespace><body>\n"
163  "There are two kind of request now:\n"
164  + Ssl::CrtdMessage::code_new_certificate + " " + request_string_size_stream.str() + " " + request_string + "\n" +
165  "\tCreate new private key and selfsigned certificate for \"host.dom\".\n"
166  + Ssl::CrtdMessage::code_new_certificate + " xxx " + request_string + "\n" +
167  "-----BEGIN CERTIFICATE-----\n"
168  "...\n"
169  "-----END CERTIFICATE-----\n"
170  "-----BEGIN RSA PRIVATE KEY-----\n"
171  "...\n"
172  "-----END RSA PRIVATE KEY-----\n"
173  "\tCreate new private key and certificate request for \"host.dom\"\n"
174  "\tSign new request by received certificate and private key.\n"
175  "usage: security_file_certgen -c -s ssl_store_path\n"
176  "\t-c Init ssl db directories and exit.\n";
177  std::cerr << help_string << std::endl;
178 }
179 
181 static bool processNewRequest(Ssl::CrtdMessage & request_message, std::string const & db_path, size_t max_db_size, size_t fs_block_size)
182 {
183  Ssl::CertificateProperties certProperties;
184  std::string error;
185  if (!request_message.parseRequest(certProperties, error))
186  throw std::runtime_error("Error while parsing the crtd request: " + error);
187 
188  // TODO: create a DB object only once, instead re-allocating here on every call.
189  std::unique_ptr<Ssl::CertificateDb> db;
190  if (!db_path.empty())
191  db.reset(new Ssl::CertificateDb(db_path, max_db_size, fs_block_size));
192 
194  Security::PrivateKeyPointer pkey;
196  std::string &certKey = Ssl::OnDiskCertificateDbKey(certProperties);
197 
198  bool dbFailed = false;
199  try {
200  if (db)
201  db->find(certKey, certProperties.mimicCert, cert, pkey);
202 
203  } catch (std::runtime_error &err) {
204  dbFailed = true;
205  error = err.what();
206  }
207 
208  if (!cert || !pkey) {
209  if (!Ssl::generateSslCertificate(cert, pkey, certProperties))
210  throw std::runtime_error("Cannot create ssl certificate or private key.");
211 
212  try {
213  /* XXX: this !dbFailed condition prevents the helper fixing DB issues
214  by adding cleanly generated certs. Which is not consistent with other
215  data caches used by Squid - they purge broken entries and allow clean
216  entries to later try and fix the issue.
217  We leave it in place now only to avoid breaking existing installations
218  behaviour with version 1.x of the helper.
219 
220  TODO: remove the !dbFailed condition when fixing the CertificateDb
221  object lifecycle and formally altering the helper behaviour.
222  */
223  if (!dbFailed && db && !db->addCertAndPrivateKey(certKey, cert, pkey, certProperties.mimicCert))
224  throw std::runtime_error("Cannot add certificate to db.");
225 
226  } catch (const std::runtime_error &err) {
227  dbFailed = true;
228  error = err.what();
229  }
230  }
231 
232  if (dbFailed)
233  std::cerr << "security_file_certgen helper database '" << db_path << "' failed: " << error << std::endl;
234 
235  std::string bufferToWrite;
236  if (!Ssl::writeCertAndPrivateKeyToMemory(cert, pkey, bufferToWrite))
237  throw std::runtime_error("Cannot write ssl certificate or/and private key to memory.");
238 
239  Ssl::CrtdMessage response_message(Ssl::CrtdMessage::REPLY);
240  response_message.setCode("OK");
241  response_message.setBody(bufferToWrite);
242 
243  // Use the '\1' char as end-of-message character
244  std::cout << response_message.compose() << '\1' << std::flush;
245 
246  return true;
247 }
248 
250 int main(int argc, char *argv[])
251 {
252  try {
253  size_t max_db_size = 0;
254  size_t fs_block_size = 0;
255  int8_t c;
256  bool create_new_db = false;
257  std::string db_path;
258  // process options.
259  while ((c = getopt(argc, argv, "dchvs:M:b:")) != -1) {
260  switch (c) {
261  case 'd':
262  debug_enabled = 1;
263  break;
264  case 'b':
265  if (!parseBytesOptionValue(&fs_block_size, optarg)) {
266  throw std::runtime_error("Error when parsing -b options value");
267  }
268  break;
269  case 's':
270  db_path = optarg;
271  break;
272  case 'M':
273  // use of -M without -s is probably an admin mistake, so make it an error
274  if (db_path.empty()) {
275  throw std::runtime_error("Error -M option requires an -s parameter be set first.");
276  }
277  if (!parseBytesOptionValue(&max_db_size, optarg)) {
278  throw std::runtime_error("Error when parsing -M options value");
279  }
280  break;
281  case 'v':
282  std::cout << "security_file_certgen version " << VERSION << std::endl;
283  exit(EXIT_SUCCESS);
284  break;
285  case 'c':
286  create_new_db = true;
287  break;
288  case 'h':
289  usage();
290  exit(EXIT_SUCCESS);
291  default:
292  exit(EXIT_FAILURE);
293  }
294  }
295 
296  // when -s is used, -M is required
297  if (!db_path.empty() && max_db_size == 0)
298  throw std::runtime_error("security_file_certgen -s requires an -M parameter");
299 
300  if (create_new_db) {
301  // when -c is used, -s is required (implying also -M, which is checked above)
302  if (db_path.empty())
303  throw std::runtime_error("security_file_certgen is missing the required parameter. There should be -s and -M parameters when -c is used.");
304 
305  std::cout << "Initialization SSL db..." << std::endl;
307  std::cout << "Done" << std::endl;
308  exit(EXIT_SUCCESS);
309  }
310 
311  // only do filesystem checks when a path (-s) is given
312  if (!db_path.empty()) {
313  if (fs_block_size == 0) {
314  struct statvfs sfs;
315 
316  if (xstatvfs(db_path.c_str(), &sfs)) {
317  fs_block_size = 2048;
318  } else {
319  fs_block_size = sfs.f_frsize;
320  // Sanity check; make sure we have a meaningful value.
321  if (fs_block_size < 512)
322  fs_block_size = 2048;
323  }
324  }
325  Ssl::CertificateDb::Check(db_path, max_db_size, fs_block_size);
326  }
327 
328  // Initialize SSL subsystem
330  // process request.
331  for (;;) {
335 
336  while (parse_result == Ssl::CrtdMessage::INCOMPLETE) {
337  if (fgets(request, HELPER_INPUT_BUFFER, stdin) == NULL)
338  exit(EXIT_FAILURE);
339  size_t gcount = strlen(request);
340  parse_result = request_message.parse(request, gcount);
341  }
342 
343  if (parse_result == Ssl::CrtdMessage::ERROR) {
344  throw std::runtime_error("Cannot parse request message.");
345  } else if (request_message.getCode() == Ssl::CrtdMessage::code_new_certificate) {
346  processNewRequest(request_message, db_path, max_db_size, fs_block_size);
347  } else {
348  throw std::runtime_error("Unknown request code: \"" + request_message.getCode() + "\".");
349  }
350  std::cout.flush();
351  }
352  } catch (std::runtime_error & error) {
353  std::cerr << argv[0] << ": " << error.what() << std::endl;
354  return EXIT_FAILURE;
355  }
356  return EXIT_SUCCESS;
357 }
358 
static const std::string param_host
Parameter name for passing hostname.
Definition: crtd_message.h:78
Security::CertPointer mimicCert
Certificate to mimic.
Definition: gadgets.h:216
static size_t parseBytesUnits(const char *unit)
void error(char *format,...)
TDB_CONTEXT * db
char * optarg
Definition: getopt.c:51
int xstatvfs(const char *path, struct statvfs *sfs)
Definition: statvfs.cc:22
std::string const & getCode() const
Current response/request code. If parsing is not finished the method may return incompleted code.
time_t getCurrentTime(void)
Get current time.
static const char *const B_GBYTES_STR
char * db_path
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition: getopt.c:62
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.
number
Definition: testStatHist.cc:16
static const std::string code_new_certificate
String code for "new_certificate" messages.
Definition: crtd_message.h:76
#define NULL
Definition: types.h:166
static bool parseBytesOptionValue(size_t *bptr, char const *value)
Parse uninterrapted string of bytes value. It looks like "4MB".
std::string compose() const
bool generateSslCertificate(Security::CertPointer &cert, Security::PrivateKeyPointer &pkey, CertificateProperties const &properties)
Definition: gadgets.cc:673
int debug_enabled
Definition: debug.cc:13
static void Create(std::string const &db_path)
Create and initialize a database under the db_path.
ParseResult
Parse result codes.
Definition: crtd_message.h:29
void setCode(std::string const &aCode)
Set new request/reply code to compose.
static const char *const B_BYTES_STR
void SQUID_OPENSSL_init_ssl(void)
Definition: openssl.h:308
bool parseRequest(Ssl::CertificateProperties &, std::string &error)
orchestrates entire request parsing
ParseResult parse(const char *buffer, size_t len)
Definition: crtd_message.cc:21
#define HELPER_INPUT_BUFFER
Definition: UserRequest.cc:25
int main(int argc, char *argv[])
This is the external security_file_certgen process.
static const char *const B_MBYTES_STR
unsigned long f_frsize
Definition: statvfs.h:45
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.
bool writeCertAndPrivateKeyToMemory(Security::CertPointer const &cert, Security::PrivateKeyPointer const &pkey, std::string &bufferToWrite)
Definition: gadgets.cc:68
std::string & OnDiskCertificateDbKey(const CertificateProperties &)
Definition: gadgets.cc:228
#define VERSION
static void usage()
Print help using response code.
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
static const char *const B_KBYTES_STR
void setBody(std::string const &aBody)
Set new body to encode.

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors