2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
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.
10 #include "helpers/defines.h"
11 #include "ssl/certificate_db.h"
12 #include "ssl/crtd_message.h"
24 \defgroup ssl_crtd ssl_crtd
25 \ingroup ExternalPrograms
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.
40 \section ssl_crtdInterface Command Line Interface
42 usage: ssl_crtd -hv -s ssl_storage_path -M storage_max_size
45 -s ssl_storage_path Path to specific disk storage of ssl server
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
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".
58 new_certificate xxx host=host.dom
59 -----BEGIN CERTIFICATE-----
61 -----END CERTIFICATE-----
62 -----BEGIN RSA PRIVATE KEY-----
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.
68 usage: ssl_crtd -c -s ssl_store_path\n
69 -c Init ssl db directories and exit.
74 static const char *const B_KBYTES_STR
= "KB";
75 static const char *const B_MBYTES_STR
= "MB";
76 static const char *const B_GBYTES_STR
= "GB";
77 static const char *const B_BYTES_STR
= "B";
83 time_t getCurrentTime(void)
85 struct timeval current_time
;
86 #if GETTIMEOFDAY_NO_TZP
87 gettimeofday(¤t_time
);
89 gettimeofday(¤t_time
, NULL
);
91 return current_time
.tv_sec
;
96 * Parse bytes unit. It would be one of the next value: MB, GB, KB or B.
97 * This function is caseinsensitive.
99 static size_t parseBytesUnits(const char * unit
)
101 if (!strncasecmp(unit
, B_BYTES_STR
, strlen(B_BYTES_STR
)) ||
102 !strncasecmp(unit
, "", strlen(unit
)))
105 if (!strncasecmp(unit
, B_KBYTES_STR
, strlen(B_KBYTES_STR
)))
108 if (!strncasecmp(unit
, B_MBYTES_STR
, strlen(B_MBYTES_STR
)))
111 if (!strncasecmp(unit
, B_GBYTES_STR
, strlen(B_GBYTES_STR
)))
114 std::cerr
<< "WARNING: Unknown bytes unit '" << unit
<< "'" << std::endl
;
121 * Parse uninterrapted string of bytes value. It looks like "4MB".
123 static bool parseBytesOptionValue(size_t * bptr
, char const * value
)
125 // Find number from string beginning.
126 char const * number_begin
= value
;
127 char const * number_end
= value
;
129 while ((*number_end
>= '0' && *number_end
<= '9')) {
133 std::string
number(number_begin
, number_end
- number_begin
);
134 std::istringstream
in(number
);
140 if ((m
= parseBytesUnits(number_end
)) == 0) {
144 *bptr
= static_cast<size_t>(m
* d
);
145 if (static_cast<long>(*bptr
* 2) != m
* d
* 2)
153 * Print help using response code.
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"
165 "\t-s ssl_storage_path Path to specific disk storage of ssl server\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"
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"
180 "-----END CERTIFICATE-----\n"
181 "-----BEGIN RSA PRIVATE KEY-----\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"
186 "usage: ssl_crtd -c -s ssl_store_path\n"
187 "\t-c Init ssl db directories and exit.\n";
188 std::cerr
<< help_string
<< std::endl
;
193 * Proccess new request message.
195 static bool proccessNewRequest(Ssl::CrtdMessage
& request_message
, std::string
const & db_path
, size_t max_db_size
, size_t fs_block_size
)
197 Ssl::CertificateProperties certProperties
;
199 if (!request_message
.parseRequest(certProperties
, error
))
200 throw std::runtime_error("Error while parsing the crtd request: " + error
);
202 Ssl::CertificateDb
db(db_path
, max_db_size
, fs_block_size
);
204 Ssl::X509_Pointer cert
;
205 Ssl::EVP_PKEY_Pointer pkey
;
206 std::string
&cert_subject
= certProperties
.dbKey();
208 db
.find(cert_subject
, cert
, pkey
);
211 if (!Ssl::certificateMatchesProperties(cert
.get(), certProperties
)) {
212 // The certificate changed (renewed or other reason).
213 // Generete a new one with the updated fields.
216 db
.purgeCert(cert_subject
);
220 if (!cert
|| !pkey
) {
221 if (!Ssl::generateSslCertificate(cert
, pkey
, certProperties
))
222 throw std::runtime_error("Cannot create ssl certificate or private key.");
224 if (!db
.addCertAndPrivateKey(cert
, pkey
, cert_subject
) && db
.IsEnabledDiskStore())
225 throw std::runtime_error("Cannot add certificate to db.");
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.");
232 Ssl::CrtdMessage
response_message(Ssl::CrtdMessage::REPLY
);
233 response_message
.setCode("OK");
234 response_message
.setBody(bufferToWrite
);
236 // Use the '\1' char as end-of-message character
237 std::cout
<< response_message
.compose() << '\1' << std::flush
;
244 * This is the external ssl_crtd process.
246 int main(int argc
, char *argv
[])
249 size_t max_db_size
= 0;
250 size_t fs_block_size
= 2048;
252 bool create_new_db
= false;
255 while ((c
= getopt(argc
, argv
, "dcghvs:M:b:n:")) != -1) {
261 if (!parseBytesOptionValue(&fs_block_size
, optarg
)) {
262 throw std::runtime_error("Error when parsing -b options value");
269 if (!parseBytesOptionValue(&max_db_size
, optarg
)) {
270 throw std::runtime_error("Error when parsing -M options value");
274 std::cout
<< "ssl_crtd version " << VERSION
<< std::endl
;
278 create_new_db
= true;
289 std::cout
<< "Initialization SSL db..." << std::endl
;
290 Ssl::CertificateDb::create(db_path
);
291 std::cout
<< "Done" << std::endl
;
296 Ssl::CertificateDb::check(db_path
, max_db_size
, fs_block_size
);
298 // Initialize SSL subsystem
299 SSL_load_error_strings();
300 SSLeay_add_ssl_algorithms();
303 char request
[HELPER_INPUT_BUFFER
];
304 Ssl::CrtdMessage
request_message(Ssl::CrtdMessage::REQUEST
);
305 Ssl::CrtdMessage::ParseResult parse_result
= Ssl::CrtdMessage::INCOMPLETE
;
307 while (parse_result
== Ssl::CrtdMessage::INCOMPLETE
) {
308 if (fgets(request
, HELPER_INPUT_BUFFER
, stdin
) == NULL
)
310 size_t gcount
= strlen(request
);
311 parse_result
= request_message
.parse(request
, gcount
);
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
);
319 throw std::runtime_error("Unknown request code: \"" + request_message
.getCode() + "\".");
323 } catch (std::runtime_error
& error
) {
324 std::cerr
<< argv
[0] << ": " << error
.what() << std::endl
;