]> git.ipfire.org Git - thirdparty/squid.git/blame - src/security/cert_generators/file/security_file_certgen.cc
Document lack of slow ACL support in more squid.conf directives. (#76)
[thirdparty/squid.git] / src / security / cert_generators / file / security_file_certgen.cc
CommitLineData
bbc27441 1/*
4ac4a490 2 * Copyright (C) 1996-2017 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
051da40c 42usage: security_file_certgen -hv -s ssl_storage_path -M storage_max_size
95d2589c
CT
43 -h Help
44 -v Version
45 -s ssl_storage_path Path to specific disk storage of ssl server
46 certificates.
47 -M storage_max_size max size of ssl certificates storage.
48 -b fs_block_size File system block size in bytes. Need for processing
49 natural size of certificate on disk. Default value is
50 2048 bytes."
51
52 After running write requests in the next format:
53 <request code><whitespace><body_len><whitespace><body>
54 There are two kind of request now:
55 new_certificate 14 host=host.dom
56 Create new private key and selfsigned certificate for "host.dom".
57
58 new_certificate xxx host=host.dom
59 -----BEGIN CERTIFICATE-----
60 ...
61 -----END CERTIFICATE-----
62 -----BEGIN RSA PRIVATE KEY-----
63 ...
64 -----END RSA PRIVATE KEY-----
65 Create new private key and certificate request for "host.dom".
66 Sign new request by received certificate and private key.
67
051da40c 68usage: security_file_certgen -c -s ssl_store_path\n
95d2589c 69 -c Init ssl db directories and exit.
95d2589c 70
95d2589c
CT
71 \endverbatim
72 */
73
74static const char *const B_KBYTES_STR = "KB";
75static const char *const B_MBYTES_STR = "MB";
76static const char *const B_GBYTES_STR = "GB";
77static const char *const B_BYTES_STR = "B";
78
051da40c 79/// Get current time.
95d2589c
CT
80time_t getCurrentTime(void)
81{
82 struct timeval current_time;
83#if GETTIMEOFDAY_NO_TZP
84 gettimeofday(&current_time);
85#else
86 gettimeofday(&current_time, NULL);
87#endif
88 return current_time.tv_sec;
89}
90
91/**
95d2589c
CT
92 * Parse bytes unit. It would be one of the next value: MB, GB, KB or B.
93 * This function is caseinsensitive.
94 */
95static 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
30c48b1a
AJ
110 std::cerr << "WARNING: Unknown bytes unit '" << unit << "'" << std::endl;
111
95d2589c
CT
112 return 0;
113}
114
051da40c 115/// Parse uninterrapted string of bytes value. It looks like "4MB".
95d2589c
CT
116static 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')) {
d7ae3534 123 ++number_end;
95d2589c
CT
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
051da40c 144/// Print help using response code.
95d2589c
CT
145static 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 =
051da40c 152 "usage: security_file_certgen -hv -s ssl_storage_path -M storage_max_size\n"
95d2589c
CT
153 "\t-h Help\n"
154 "\t-v Version\n"
155 "\t-s ssl_storage_path Path to specific disk storage of ssl server\n"
156 "\t certificates.\n"
157 "\t-M storage_max_size max size of ssl certificates storage.\n"
158 "\t-b fs_block_size File system block size in bytes. Need for processing\n"
159 "\t natural size of certificate on disk. Default value is\n"
160 "\t 2048 bytes.\n"
161 "\n"
162 "After running write requests in the next format:\n"
163 "<request code><whitespace><body_len><whitespace><body>\n"
164 "There are two kind of request now:\n"
165 + Ssl::CrtdMessage::code_new_certificate + " " + request_string_size_stream.str() + " " + request_string + "\n" +
166 "\tCreate new private key and selfsigned certificate for \"host.dom\".\n"
167 + Ssl::CrtdMessage::code_new_certificate + " xxx " + request_string + "\n" +
168 "-----BEGIN CERTIFICATE-----\n"
169 "...\n"
170 "-----END CERTIFICATE-----\n"
171 "-----BEGIN RSA PRIVATE KEY-----\n"
172 "...\n"
173 "-----END RSA PRIVATE KEY-----\n"
174 "\tCreate new private key and certificate request for \"host.dom\"\n"
175 "\tSign new request by received certificate and private key.\n"
051da40c 176 "usage: security_file_certgen -c -s ssl_store_path\n"
a0b971d5 177 "\t-c Init ssl db directories and exit.\n";
95d2589c
CT
178 std::cerr << help_string << std::endl;
179}
180
ba5d55c1
NH
181/// Process new request message.
182static bool processNewRequest(Ssl::CrtdMessage & request_message, std::string const & db_path, size_t max_db_size, size_t fs_block_size)
95d2589c 183{
aebe6888 184 Ssl::CertificateProperties certProperties;
06997a38
CT
185 std::string error;
186 if (!request_message.parseRequest(certProperties, error))
ce394ae1 187 throw std::runtime_error("Error while parsing the crtd request: " + error);
95d2589c
CT
188
189 Ssl::CertificateDb db(db_path, max_db_size, fs_block_size);
190
f97700a0 191 Security::CertPointer cert;
95d2589c 192 Ssl::EVP_PKEY_Pointer pkey;
5107d2c4
CT
193 Security::CertPointer orig;
194 std::string &certKey = Ssl::OnDiskCertificateDbKey(certProperties);
95d2589c 195
541e6ab5
CT
196 bool dbFailed = false;
197 try {
5107d2c4 198 db.find(certKey, certProperties.mimicCert, cert, pkey);
541e6ab5
CT
199 } catch (std::runtime_error &err) {
200 dbFailed = true;
201 error = err.what();
202 }
95d2589c 203
87f237a9 204 if (!cert || !pkey) {
aebe6888 205 if (!Ssl::generateSslCertificate(cert, pkey, certProperties))
95d2589c 206 throw std::runtime_error("Cannot create ssl certificate or private key.");
9a90aace 207
541e6ab5
CT
208 if (!dbFailed && db.IsEnabledDiskStore()) {
209 try {
5107d2c4 210 if (!db.addCertAndPrivateKey(certKey, cert, pkey, certProperties.mimicCert)) {
541e6ab5
CT
211 dbFailed = true;
212 error = "Cannot add certificate to db.";
213 }
214 } catch (const std::runtime_error &err) {
215 dbFailed = true;
216 error = err.what();
217 }
218 }
95d2589c
CT
219 }
220
541e6ab5 221 if (dbFailed)
051da40c 222 std::cerr << "security_file_certgen helper database '" << db_path << "' failed: " << error << std::endl;
541e6ab5 223
95d2589c
CT
224 std::string bufferToWrite;
225 if (!Ssl::writeCertAndPrivateKeyToMemory(cert, pkey, bufferToWrite))
226 throw std::runtime_error("Cannot write ssl certificate or/and private key to memory.");
227
ff2d7d92 228 Ssl::CrtdMessage response_message(Ssl::CrtdMessage::REPLY);
419ac015 229 response_message.setCode("OK");
95d2589c
CT
230 response_message.setBody(bufferToWrite);
231
0af9303a
CT
232 // Use the '\1' char as end-of-message character
233 std::cout << response_message.compose() << '\1' << std::flush;
95d2589c
CT
234
235 return true;
236}
237
051da40c 238/// This is the external security_file_certgen process.
95d2589c
CT
239int main(int argc, char *argv[])
240{
241 try {
95d2589c 242 size_t max_db_size = 0;
ba5d55c1 243 size_t fs_block_size = 0;
c3373ae4 244 int8_t c;
95d2589c 245 bool create_new_db = false;
95d2589c 246 std::string db_path;
ba5d55c1 247 // process options.
95d2589c
CT
248 while ((c = getopt(argc, argv, "dcghvs:M:b:n:")) != -1) {
249 switch (c) {
250 case 'd':
251 debug_enabled = 1;
252 break;
253 case 'b':
254 if (!parseBytesOptionValue(&fs_block_size, optarg)) {
255 throw std::runtime_error("Error when parsing -b options value");
256 }
257 break;
258 case 's':
259 db_path = optarg;
260 break;
95d2589c
CT
261 case 'M':
262 if (!parseBytesOptionValue(&max_db_size, optarg)) {
263 throw std::runtime_error("Error when parsing -M options value");
264 }
265 break;
266 case 'v':
051da40c 267 std::cout << "security_file_certgen version " << VERSION << std::endl;
24885773 268 exit(EXIT_SUCCESS);
95d2589c
CT
269 break;
270 case 'c':
271 create_new_db = true;
272 break;
95d2589c
CT
273 case 'h':
274 usage();
24885773 275 exit(EXIT_SUCCESS);
95d2589c 276 default:
24885773 277 exit(EXIT_FAILURE);
95d2589c
CT
278 }
279 }
280
281 if (create_new_db) {
282 std::cout << "Initialization SSL db..." << std::endl;
5107d2c4 283 Ssl::CertificateDb::Create(db_path);
95d2589c 284 std::cout << "Done" << std::endl;
24885773 285 exit(EXIT_SUCCESS);
95d2589c
CT
286 }
287
ba5d55c1
NH
288 if (fs_block_size == 0) {
289 struct statvfs sfs;
290
291 if (xstatvfs(db_path.c_str(), &sfs)) {
292 fs_block_size = 2048;
293 } else {
294 fs_block_size = sfs.f_frsize;
295 // Sanity check; make sure we have a meaningful value.
296 if (fs_block_size < 512)
297 fs_block_size = 2048;
298 }
299 }
300
95d2589c 301 {
5107d2c4 302 Ssl::CertificateDb::Check(db_path, max_db_size, fs_block_size);
95d2589c 303 }
3c26b00a
CT
304 // Initialize SSL subsystem
305 SSL_load_error_strings();
306 SSLeay_add_ssl_algorithms();
ba5d55c1 307 // process request.
95d2589c
CT
308 for (;;) {
309 char request[HELPER_INPUT_BUFFER];
ff2d7d92 310 Ssl::CrtdMessage request_message(Ssl::CrtdMessage::REQUEST);
95d2589c
CT
311 Ssl::CrtdMessage::ParseResult parse_result = Ssl::CrtdMessage::INCOMPLETE;
312
313 while (parse_result == Ssl::CrtdMessage::INCOMPLETE) {
314 if (fgets(request, HELPER_INPUT_BUFFER, stdin) == NULL)
24885773 315 exit(EXIT_FAILURE);
95d2589c
CT
316 size_t gcount = strlen(request);
317 parse_result = request_message.parse(request, gcount);
318 }
319
320 if (parse_result == Ssl::CrtdMessage::ERROR) {
321 throw std::runtime_error("Cannot parse request message.");
322 } else if (request_message.getCode() == Ssl::CrtdMessage::code_new_certificate) {
ba5d55c1 323 processNewRequest(request_message, db_path, max_db_size, fs_block_size);
95d2589c
CT
324 } else {
325 throw std::runtime_error("Unknown request code: \"" + request_message.getCode() + "\".");
326 }
327 std::cout.flush();
328 }
329 } catch (std::runtime_error & error) {
330 std::cerr << argv[0] << ": " << error.what() << std::endl;
24885773 331 return EXIT_FAILURE;
95d2589c 332 }
24885773 333 return EXIT_SUCCESS;
95d2589c 334}
f53969cc 335