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