]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ssl/ssl_crtd.cc
Boilerplate: update copyright blurbs on src/
[thirdparty/squid.git] / src / ssl / ssl_crtd.cc
CommitLineData
bbc27441
AJ
1/*
2 * Copyright (C) 1996-2014 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
f7f3304a 9#include "squid.h"
95d2589c 10#include "helpers/defines.h"
95d2589c 11#include "ssl/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/**
24 \defgroup ssl_crtd ssl_crtd
25 \ingroup ExternalPrograms
26 \par
27 Because the standart generation of ssl certificate for
28 sslBump feature, Squid must use external proccess to
29 actually make these calls. This process generate new ssl
30 certificates and worked with ssl certificates disk cache.
31 Typically there will be five ssl_crtd processes spawned
32 from Squid. Communication occurs via TCP sockets bound
33 to the loopback interface. The class in helper.h are
34 primally concerned with starting and stopping the ssl_crtd.
35 Reading and writing to and from the ssl_crtd occurs in the
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
42usage: ssl_crtd -hv -s ssl_storage_path -M storage_max_size
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
a0b971d5 68usage: ssl_crtd -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
79/**
80 \ingroup ssl_crtd
81 * Get current time.
82*/
83time_t getCurrentTime(void)
84{
85 struct timeval current_time;
86#if GETTIMEOFDAY_NO_TZP
87 gettimeofday(&current_time);
88#else
89 gettimeofday(&current_time, NULL);
90#endif
91 return current_time.tv_sec;
92}
93
94/**
95 \ingroup ssl_crtd
96 * Parse bytes unit. It would be one of the next value: MB, GB, KB or B.
97 * This function is caseinsensitive.
98 */
99static size_t parseBytesUnits(const char * unit)
100{
101 if (!strncasecmp(unit, B_BYTES_STR, strlen(B_BYTES_STR)) ||
102 !strncasecmp(unit, "", strlen(unit)))
103 return 1;
104
105 if (!strncasecmp(unit, B_KBYTES_STR, strlen(B_KBYTES_STR)))
106 return 1 << 10;
107
108 if (!strncasecmp(unit, B_MBYTES_STR, strlen(B_MBYTES_STR)))
109 return 1 << 20;
110
111 if (!strncasecmp(unit, B_GBYTES_STR, strlen(B_GBYTES_STR)))
112 return 1 << 30;
113
30c48b1a
AJ
114 std::cerr << "WARNING: Unknown bytes unit '" << unit << "'" << std::endl;
115
95d2589c
CT
116 return 0;
117}
118
119/**
120 \ingroup ssl_crtd
121 * Parse uninterrapted string of bytes value. It looks like "4MB".
122 */
123static bool parseBytesOptionValue(size_t * bptr, char const * value)
124{
125 // Find number from string beginning.
126 char const * number_begin = value;
127 char const * number_end = value;
128
129 while ((*number_end >= '0' && *number_end <= '9')) {
d7ae3534 130 ++number_end;
95d2589c
CT
131 }
132
133 std::string number(number_begin, number_end - number_begin);
134 std::istringstream in(number);
135 int d = 0;
136 if (!(in >> d))
137 return false;
138
139 int m;
140 if ((m = parseBytesUnits(number_end)) == 0) {
141 return false;
142 }
143
144 *bptr = static_cast<size_t>(m * d);
145 if (static_cast<long>(*bptr * 2) != m * d * 2)
146 return false;
147
148 return true;
149}
150
151/**
152 \ingroup ssl_crtd
153 * Print help using response code.
154 */
155static void usage()
156{
157 std::string example_host_name = "host.dom";
158 std::string request_string = Ssl::CrtdMessage::param_host + "=" + example_host_name;
159 std::stringstream request_string_size_stream;
160 request_string_size_stream << request_string.length();
161 std::string help_string =
162 "usage: ssl_crtd -hv -s ssl_storage_path -M storage_max_size\n"
163 "\t-h Help\n"
164 "\t-v Version\n"
165 "\t-s ssl_storage_path Path to specific disk storage of ssl server\n"
166 "\t certificates.\n"
167 "\t-M storage_max_size max size of ssl certificates storage.\n"
168 "\t-b fs_block_size File system block size in bytes. Need for processing\n"
169 "\t natural size of certificate on disk. Default value is\n"
170 "\t 2048 bytes.\n"
171 "\n"
172 "After running write requests in the next format:\n"
173 "<request code><whitespace><body_len><whitespace><body>\n"
174 "There are two kind of request now:\n"
175 + Ssl::CrtdMessage::code_new_certificate + " " + request_string_size_stream.str() + " " + request_string + "\n" +
176 "\tCreate new private key and selfsigned certificate for \"host.dom\".\n"
177 + Ssl::CrtdMessage::code_new_certificate + " xxx " + request_string + "\n" +
178 "-----BEGIN CERTIFICATE-----\n"
179 "...\n"
180 "-----END CERTIFICATE-----\n"
181 "-----BEGIN RSA PRIVATE KEY-----\n"
182 "...\n"
183 "-----END RSA PRIVATE KEY-----\n"
184 "\tCreate new private key and certificate request for \"host.dom\"\n"
185 "\tSign new request by received certificate and private key.\n"
a0b971d5
CT
186 "usage: ssl_crtd -c -s ssl_store_path\n"
187 "\t-c Init ssl db directories and exit.\n";
95d2589c
CT
188 std::cerr << help_string << std::endl;
189}
190
191/**
192 \ingroup ssl_crtd
193 * Proccess new request message.
194 */
06997a38 195static bool proccessNewRequest(Ssl::CrtdMessage & request_message, std::string const & db_path, size_t max_db_size, size_t fs_block_size)
95d2589c 196{
aebe6888 197 Ssl::CertificateProperties certProperties;
06997a38
CT
198 std::string error;
199 if (!request_message.parseRequest(certProperties, error))
ce394ae1 200 throw std::runtime_error("Error while parsing the crtd request: " + error);
95d2589c
CT
201
202 Ssl::CertificateDb db(db_path, max_db_size, fs_block_size);
203
204 Ssl::X509_Pointer cert;
205 Ssl::EVP_PKEY_Pointer pkey;
06997a38 206 std::string &cert_subject = certProperties.dbKey();
95d2589c 207
9a90aace 208 db.find(cert_subject, cert, pkey);
95d2589c 209
4ece76b2
CT
210 if (cert.get()) {
211 if (!Ssl::certificateMatchesProperties(cert.get(), certProperties)) {
e7bcc25f
CT
212 // The certificate changed (renewed or other reason).
213 // Generete a new one with the updated fields.
214 cert.reset(NULL);
215 pkey.reset(NULL);
4ece76b2 216 db.purgeCert(cert_subject);
e7bcc25f
CT
217 }
218 }
95d2589c 219
87f237a9 220 if (!cert || !pkey) {
aebe6888 221 if (!Ssl::generateSslCertificate(cert, pkey, certProperties))
95d2589c 222 throw std::runtime_error("Cannot create ssl certificate or private key.");
9a90aace 223
fb2178bb 224 if (!db.addCertAndPrivateKey(cert, pkey, cert_subject) && db.IsEnabledDiskStore())
95d2589c
CT
225 throw std::runtime_error("Cannot add certificate to db.");
226 }
227
228 std::string bufferToWrite;
229 if (!Ssl::writeCertAndPrivateKeyToMemory(cert, pkey, bufferToWrite))
230 throw std::runtime_error("Cannot write ssl certificate or/and private key to memory.");
231
ff2d7d92 232 Ssl::CrtdMessage response_message(Ssl::CrtdMessage::REPLY);
419ac015 233 response_message.setCode("OK");
95d2589c
CT
234 response_message.setBody(bufferToWrite);
235
0af9303a
CT
236 // Use the '\1' char as end-of-message character
237 std::cout << response_message.compose() << '\1' << std::flush;
95d2589c
CT
238
239 return true;
240}
241
242/**
243 \ingroup ssl_crtd
244 * This is the external ssl_crtd process.
245 */
246int main(int argc, char *argv[])
247{
248 try {
95d2589c
CT
249 size_t max_db_size = 0;
250 size_t fs_block_size = 2048;
c3373ae4 251 int8_t c;
95d2589c 252 bool create_new_db = false;
95d2589c
CT
253 std::string db_path;
254 // proccess options.
255 while ((c = getopt(argc, argv, "dcghvs:M:b:n:")) != -1) {
256 switch (c) {
257 case 'd':
258 debug_enabled = 1;
259 break;
260 case 'b':
261 if (!parseBytesOptionValue(&fs_block_size, optarg)) {
262 throw std::runtime_error("Error when parsing -b options value");
263 }
264 break;
265 case 's':
266 db_path = optarg;
267 break;
95d2589c
CT
268 case 'M':
269 if (!parseBytesOptionValue(&max_db_size, optarg)) {
270 throw std::runtime_error("Error when parsing -M options value");
271 }
272 break;
273 case 'v':
274 std::cout << "ssl_crtd version " << VERSION << std::endl;
275 exit(0);
276 break;
277 case 'c':
278 create_new_db = true;
279 break;
95d2589c
CT
280 case 'h':
281 usage();
282 exit(0);
283 default:
284 exit(0);
285 }
286 }
287
288 if (create_new_db) {
289 std::cout << "Initialization SSL db..." << std::endl;
a0b971d5 290 Ssl::CertificateDb::create(db_path);
95d2589c
CT
291 std::cout << "Done" << std::endl;
292 exit(0);
293 }
294
95d2589c
CT
295 {
296 Ssl::CertificateDb::check(db_path, max_db_size);
297 }
298 // proccess request.
299 for (;;) {
300 char request[HELPER_INPUT_BUFFER];
ff2d7d92 301 Ssl::CrtdMessage request_message(Ssl::CrtdMessage::REQUEST);
95d2589c
CT
302 Ssl::CrtdMessage::ParseResult parse_result = Ssl::CrtdMessage::INCOMPLETE;
303
304 while (parse_result == Ssl::CrtdMessage::INCOMPLETE) {
305 if (fgets(request, HELPER_INPUT_BUFFER, stdin) == NULL)
306 return 1;
307 size_t gcount = strlen(request);
308 parse_result = request_message.parse(request, gcount);
309 }
310
311 if (parse_result == Ssl::CrtdMessage::ERROR) {
312 throw std::runtime_error("Cannot parse request message.");
313 } else if (request_message.getCode() == Ssl::CrtdMessage::code_new_certificate) {
314 proccessNewRequest(request_message, db_path, max_db_size, fs_block_size);
315 } else {
316 throw std::runtime_error("Unknown request code: \"" + request_message.getCode() + "\".");
317 }
318 std::cout.flush();
319 }
320 } catch (std::runtime_error & error) {
321 std::cerr << argv[0] << ": " << error.what() << std::endl;
322 return 0;
323 }
324 return 0;
325}