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