]> git.ipfire.org Git - thirdparty/squid.git/blame - src/security/cert_generators/file/security_file_certgen.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / security / cert_generators / file / security_file_certgen.cc
CommitLineData
bbc27441 1/*
5b74111a 2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
bbc27441
AJ
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
f7f3304a 9#include "squid.h"
079b1d0f 10#include "helper/protocol_defines.h"
cb0b3d63 11#include "security/cert_generators/file/certificate_db.h"
602d9612 12#include "ssl/crtd_message.h"
95d2589c 13
95d2589c 14#include <cstring>
95d2589c 15#include <iostream>
074d6a40 16#include <sstream>
95d2589c 17#include <stdexcept>
95d2589c 18#include <string>
95d2589c
CT
19#if HAVE_GETOPT_H
20#include <getopt.h>
21#endif
22
23/**
051da40c 24 \defgroup ssl_crtd security_file_certgen
95d2589c
CT
25 \ingroup ExternalPrograms
26 \par
051da40c 27 Because the standard generation of SSL certificates for
ba5d55c1 28 sslBump feature, Squid must use external process to
95d2589c
CT
29 actually make these calls. This process generate new ssl
30 certificates and worked with ssl certificates disk cache.
051da40c
AJ
31 Typically there will be five certificate generator processes
32 spawned from Squid. Communication occurs via TCP sockets
33 bound to the loopback interface. The class in helper.h are
34 primally concerned with starting and stopping the helpers.
35 Reading and writing to and from the helpers occurs in the
95d2589c
CT
36 \link IPCacheAPI IP\endlink and the dnsservers occurs in
37 the \link IPCacheAPI IP\endlink and \link FQDNCacheAPI
38 FQDN\endlink cache modules.
39
40 \section ssl_crtdInterface Command Line Interface
41 \verbatim
593a978e 42usage: security_file_certgen -hv -s directory -M size -b fs_block_size
95d2589c
CT
43 -h Help
44 -v Version
593a978e
AJ
45 -s directory Directory path of SSL storage database.
46 -M size Maximum size of SSL certificate disk storage.
95d2589c
CT
47 -b fs_block_size File system block size in bytes. Need for processing
48 natural size of certificate on disk. Default value is
593a978e 49 2048 bytes.
95d2589c
CT
50
51 After running write requests in the next format:
52 <request code><whitespace><body_len><whitespace><body>
53 There are two kind of request now:
54 new_certificate 14 host=host.dom
55 Create new private key and selfsigned certificate for "host.dom".
56
57 new_certificate xxx host=host.dom
58 -----BEGIN CERTIFICATE-----
59 ...
60 -----END CERTIFICATE-----
61 -----BEGIN RSA PRIVATE KEY-----
62 ...
63 -----END RSA PRIVATE KEY-----
64 Create new private key and certificate request for "host.dom".
65 Sign new request by received certificate and private key.
66
051da40c 67usage: security_file_certgen -c -s ssl_store_path\n
95d2589c 68 -c Init ssl db directories and exit.
95d2589c 69
95d2589c
CT
70 \endverbatim
71 */
72
73static const char *const B_KBYTES_STR = "KB";
74static const char *const B_MBYTES_STR = "MB";
75static const char *const B_GBYTES_STR = "GB";
76static const char *const B_BYTES_STR = "B";
77
051da40c 78/// Get current time.
95d2589c
CT
79time_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
90/**
95d2589c
CT
91 * Parse bytes unit. It would be one of the next value: MB, GB, KB or B.
92 * This function is caseinsensitive.
93 */
94static 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
30c48b1a
AJ
109 std::cerr << "WARNING: Unknown bytes unit '" << unit << "'" << std::endl;
110
95d2589c
CT
111 return 0;
112}
113
051da40c 114/// Parse uninterrapted string of bytes value. It looks like "4MB".
95d2589c
CT
115static 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')) {
d7ae3534 122 ++number_end;
95d2589c
CT
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
051da40c 143/// Print help using response code.
95d2589c
CT
144static 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 =
593a978e 151 "usage: security_file_certgen -hv -s directory -M size -b fs_block_size\n"
95d2589c
CT
152 "\t-h Help\n"
153 "\t-v Version\n"
593a978e
AJ
154 "\t-s directory Directory path of SSL storage database.\n"
155 "\t-M size Maximum size of SSL certificate disk storage.\n"
95d2589c
CT
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"
051da40c 174 "usage: security_file_certgen -c -s ssl_store_path\n"
a0b971d5 175 "\t-c Init ssl db directories and exit.\n";
95d2589c
CT
176 std::cerr << help_string << std::endl;
177}
178
ba5d55c1
NH
179/// Process new request message.
180static bool processNewRequest(Ssl::CrtdMessage & request_message, std::string const & db_path, size_t max_db_size, size_t fs_block_size)
95d2589c 181{
aebe6888 182 Ssl::CertificateProperties certProperties;
06997a38
CT
183 std::string error;
184 if (!request_message.parseRequest(certProperties, error))
ce394ae1 185 throw std::runtime_error("Error while parsing the crtd request: " + error);
95d2589c
CT
186
187 Ssl::CertificateDb db(db_path, max_db_size, fs_block_size);
188
f97700a0 189 Security::CertPointer cert;
cf487124 190 Security::PrivateKeyPointer pkey;
5107d2c4
CT
191 Security::CertPointer orig;
192 std::string &certKey = Ssl::OnDiskCertificateDbKey(certProperties);
95d2589c 193
541e6ab5
CT
194 bool dbFailed = false;
195 try {
5107d2c4 196 db.find(certKey, certProperties.mimicCert, cert, pkey);
541e6ab5
CT
197 } catch (std::runtime_error &err) {
198 dbFailed = true;
199 error = err.what();
200 }
95d2589c 201
87f237a9 202 if (!cert || !pkey) {
aebe6888 203 if (!Ssl::generateSslCertificate(cert, pkey, certProperties))
95d2589c 204 throw std::runtime_error("Cannot create ssl certificate or private key.");
9a90aace 205
541e6ab5
CT
206 if (!dbFailed && db.IsEnabledDiskStore()) {
207 try {
5107d2c4 208 if (!db.addCertAndPrivateKey(certKey, cert, pkey, certProperties.mimicCert)) {
541e6ab5
CT
209 dbFailed = true;
210 error = "Cannot add certificate to db.";
211 }
212 } catch (const std::runtime_error &err) {
213 dbFailed = true;
214 error = err.what();
215 }
216 }
95d2589c
CT
217 }
218
541e6ab5 219 if (dbFailed)
051da40c 220 std::cerr << "security_file_certgen helper database '" << db_path << "' failed: " << error << std::endl;
541e6ab5 221
95d2589c
CT
222 std::string bufferToWrite;
223 if (!Ssl::writeCertAndPrivateKeyToMemory(cert, pkey, bufferToWrite))
224 throw std::runtime_error("Cannot write ssl certificate or/and private key to memory.");
225
ff2d7d92 226 Ssl::CrtdMessage response_message(Ssl::CrtdMessage::REPLY);
419ac015 227 response_message.setCode("OK");
95d2589c
CT
228 response_message.setBody(bufferToWrite);
229
0af9303a
CT
230 // Use the '\1' char as end-of-message character
231 std::cout << response_message.compose() << '\1' << std::flush;
95d2589c
CT
232
233 return true;
234}
235
051da40c 236/// This is the external security_file_certgen process.
95d2589c
CT
237int main(int argc, char *argv[])
238{
239 try {
95d2589c 240 size_t max_db_size = 0;
ba5d55c1 241 size_t fs_block_size = 0;
c3373ae4 242 int8_t c;
95d2589c 243 bool create_new_db = false;
95d2589c 244 std::string db_path;
ba5d55c1 245 // process options.
593a978e 246 while ((c = getopt(argc, argv, "dchvs:M:b:")) != -1) {
95d2589c
CT
247 switch (c) {
248 case 'd':
249 debug_enabled = 1;
250 break;
251 case 'b':
252 if (!parseBytesOptionValue(&fs_block_size, optarg)) {
253 throw std::runtime_error("Error when parsing -b options value");
254 }
255 break;
256 case 's':
257 db_path = optarg;
258 break;
95d2589c
CT
259 case 'M':
260 if (!parseBytesOptionValue(&max_db_size, optarg)) {
261 throw std::runtime_error("Error when parsing -M options value");
262 }
263 break;
264 case 'v':
051da40c 265 std::cout << "security_file_certgen version " << VERSION << std::endl;
24885773 266 exit(EXIT_SUCCESS);
95d2589c
CT
267 break;
268 case 'c':
269 create_new_db = true;
270 break;
95d2589c
CT
271 case 'h':
272 usage();
24885773 273 exit(EXIT_SUCCESS);
95d2589c 274 default:
24885773 275 exit(EXIT_FAILURE);
95d2589c
CT
276 }
277 }
278
279 if (create_new_db) {
280 std::cout << "Initialization SSL db..." << std::endl;
5107d2c4 281 Ssl::CertificateDb::Create(db_path);
95d2589c 282 std::cout << "Done" << std::endl;
24885773 283 exit(EXIT_SUCCESS);
95d2589c
CT
284 }
285
ba5d55c1
NH
286 if (fs_block_size == 0) {
287 struct statvfs sfs;
288
289 if (xstatvfs(db_path.c_str(), &sfs)) {
290 fs_block_size = 2048;
291 } else {
292 fs_block_size = sfs.f_frsize;
293 // Sanity check; make sure we have a meaningful value.
294 if (fs_block_size < 512)
295 fs_block_size = 2048;
296 }
297 }
298
95d2589c 299 {
5107d2c4 300 Ssl::CertificateDb::Check(db_path, max_db_size, fs_block_size);
95d2589c 301 }
3c26b00a
CT
302 // Initialize SSL subsystem
303 SSL_load_error_strings();
304 SSLeay_add_ssl_algorithms();
ba5d55c1 305 // process request.
95d2589c
CT
306 for (;;) {
307 char request[HELPER_INPUT_BUFFER];
ff2d7d92 308 Ssl::CrtdMessage request_message(Ssl::CrtdMessage::REQUEST);
95d2589c
CT
309 Ssl::CrtdMessage::ParseResult parse_result = Ssl::CrtdMessage::INCOMPLETE;
310
311 while (parse_result == Ssl::CrtdMessage::INCOMPLETE) {
312 if (fgets(request, HELPER_INPUT_BUFFER, stdin) == NULL)
24885773 313 exit(EXIT_FAILURE);
95d2589c
CT
314 size_t gcount = strlen(request);
315 parse_result = request_message.parse(request, gcount);
316 }
317
318 if (parse_result == Ssl::CrtdMessage::ERROR) {
319 throw std::runtime_error("Cannot parse request message.");
320 } else if (request_message.getCode() == Ssl::CrtdMessage::code_new_certificate) {
ba5d55c1 321 processNewRequest(request_message, db_path, max_db_size, fs_block_size);
95d2589c
CT
322 } else {
323 throw std::runtime_error("Unknown request code: \"" + request_message.getCode() + "\".");
324 }
325 std::cout.flush();
326 }
327 } catch (std::runtime_error & error) {
328 std::cerr << argv[0] << ": " << error.what() << std::endl;
24885773 329 return EXIT_FAILURE;
95d2589c 330 }
24885773 331 return EXIT_SUCCESS;
95d2589c 332}
f53969cc 333