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