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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors