2 * Copyright (C) 1996-2017 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 "helper/protocol_defines.h"
11 #include "security/cert_generators/file/certificate_db.h"
12 #include "ssl/crtd_message.h"
24 \defgroup ssl_crtd security_file_certgen
25 \ingroup ExternalPrograms
27 Because the standard generation of SSL certificates for
28 sslBump feature, Squid must use external process 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 certificate generator processes
32 spawned from Squid. Communication occurs via TCP sockets
33 bound to the loopback interface. The class in helper.h are
34 primally concerned with starting and stopping the helpers.
35 Reading and writing to and from the helpers 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: security_file_certgen -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: security_file_certgen -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";
80 time_t getCurrentTime(void)
82 struct timeval current_time
;
83 #if GETTIMEOFDAY_NO_TZP
84 gettimeofday(¤t_time
);
86 gettimeofday(¤t_time
, NULL
);
88 return current_time
.tv_sec
;
92 * Parse bytes unit. It would be one of the next value: MB, GB, KB or B.
93 * This function is caseinsensitive.
95 static size_t parseBytesUnits(const char * unit
)
97 if (!strncasecmp(unit
, B_BYTES_STR
, strlen(B_BYTES_STR
)) ||
98 !strncasecmp(unit
, "", strlen(unit
)))
101 if (!strncasecmp(unit
, B_KBYTES_STR
, strlen(B_KBYTES_STR
)))
104 if (!strncasecmp(unit
, B_MBYTES_STR
, strlen(B_MBYTES_STR
)))
107 if (!strncasecmp(unit
, B_GBYTES_STR
, strlen(B_GBYTES_STR
)))
110 std::cerr
<< "WARNING: Unknown bytes unit '" << unit
<< "'" << std::endl
;
115 /// Parse uninterrapted string of bytes value. It looks like "4MB".
116 static bool parseBytesOptionValue(size_t * bptr
, char const * value
)
118 // Find number from string beginning.
119 char const * number_begin
= value
;
120 char const * number_end
= value
;
122 while ((*number_end
>= '0' && *number_end
<= '9')) {
126 std::string
number(number_begin
, number_end
- number_begin
);
127 std::istringstream
in(number
);
133 if ((m
= parseBytesUnits(number_end
)) == 0) {
137 *bptr
= static_cast<size_t>(m
* d
);
138 if (static_cast<long>(*bptr
* 2) != m
* d
* 2)
144 /// Print help using response code.
147 std::string example_host_name
= "host.dom";
148 std::string request_string
= Ssl::CrtdMessage::param_host
+ "=" + example_host_name
;
149 std::stringstream request_string_size_stream
;
150 request_string_size_stream
<< request_string
.length();
151 std::string help_string
=
152 "usage: security_file_certgen -hv -s ssl_storage_path -M storage_max_size\n"
155 "\t-s ssl_storage_path Path to specific disk storage of ssl server\n"
157 "\t-M storage_max_size max size of ssl certificates storage.\n"
158 "\t-b fs_block_size File system block size in bytes. Need for processing\n"
159 "\t natural size of certificate on disk. Default value is\n"
162 "After running write requests in the next format:\n"
163 "<request code><whitespace><body_len><whitespace><body>\n"
164 "There are two kind of request now:\n"
165 + Ssl::CrtdMessage::code_new_certificate
+ " " + request_string_size_stream
.str() + " " + request_string
+ "\n" +
166 "\tCreate new private key and selfsigned certificate for \"host.dom\".\n"
167 + Ssl::CrtdMessage::code_new_certificate
+ " xxx " + request_string
+ "\n" +
168 "-----BEGIN CERTIFICATE-----\n"
170 "-----END CERTIFICATE-----\n"
171 "-----BEGIN RSA PRIVATE KEY-----\n"
173 "-----END RSA PRIVATE KEY-----\n"
174 "\tCreate new private key and certificate request for \"host.dom\"\n"
175 "\tSign new request by received certificate and private key.\n"
176 "usage: security_file_certgen -c -s ssl_store_path\n"
177 "\t-c Init ssl db directories and exit.\n";
178 std::cerr
<< help_string
<< std::endl
;
181 /// Process new request message.
182 static bool processNewRequest(Ssl::CrtdMessage
& request_message
, std::string
const & db_path
, size_t max_db_size
, size_t fs_block_size
)
184 Ssl::CertificateProperties certProperties
;
186 if (!request_message
.parseRequest(certProperties
, error
))
187 throw std::runtime_error("Error while parsing the crtd request: " + error
);
189 Ssl::CertificateDb
db(db_path
, max_db_size
, fs_block_size
);
191 Security::CertPointer cert
;
192 Ssl::EVP_PKEY_Pointer pkey
;
193 std::string
&cert_subject
= certProperties
.dbKey();
195 bool dbFailed
= false;
197 db
.find(cert_subject
, cert
, pkey
);
198 } catch (std::runtime_error
&err
) {
204 if (!Ssl::certificateMatchesProperties(cert
.get(), certProperties
)) {
205 // The certificate changed (renewed or other reason).
206 // Generete a new one with the updated fields.
209 db
.purgeCert(cert_subject
);
213 if (!cert
|| !pkey
) {
214 if (!Ssl::generateSslCertificate(cert
, pkey
, certProperties
))
215 throw std::runtime_error("Cannot create ssl certificate or private key.");
217 if (!dbFailed
&& db
.IsEnabledDiskStore()) {
219 if (!db
.addCertAndPrivateKey(cert
, pkey
, cert_subject
)) {
221 error
= "Cannot add certificate to db.";
223 } catch (const std::runtime_error
&err
) {
231 std::cerr
<< "security_file_certgen helper database '" << db_path
<< "' failed: " << error
<< std::endl
;
233 std::string bufferToWrite
;
234 if (!Ssl::writeCertAndPrivateKeyToMemory(cert
, pkey
, bufferToWrite
))
235 throw std::runtime_error("Cannot write ssl certificate or/and private key to memory.");
237 Ssl::CrtdMessage
response_message(Ssl::CrtdMessage::REPLY
);
238 response_message
.setCode("OK");
239 response_message
.setBody(bufferToWrite
);
241 // Use the '\1' char as end-of-message character
242 std::cout
<< response_message
.compose() << '\1' << std::flush
;
247 /// This is the external security_file_certgen process.
248 int main(int argc
, char *argv
[])
251 size_t max_db_size
= 0;
252 size_t fs_block_size
= 0;
254 bool create_new_db
= false;
257 while ((c
= getopt(argc
, argv
, "dcghvs:M:b:n:")) != -1) {
263 if (!parseBytesOptionValue(&fs_block_size
, optarg
)) {
264 throw std::runtime_error("Error when parsing -b options value");
271 if (!parseBytesOptionValue(&max_db_size
, optarg
)) {
272 throw std::runtime_error("Error when parsing -M options value");
276 std::cout
<< "security_file_certgen version " << VERSION
<< std::endl
;
280 create_new_db
= true;
291 std::cout
<< "Initialization SSL db..." << std::endl
;
292 Ssl::CertificateDb::create(db_path
);
293 std::cout
<< "Done" << std::endl
;
297 if (fs_block_size
== 0) {
300 if (xstatvfs(db_path
.c_str(), &sfs
)) {
301 fs_block_size
= 2048;
303 fs_block_size
= sfs
.f_frsize
;
304 // Sanity check; make sure we have a meaningful value.
305 if (fs_block_size
< 512)
306 fs_block_size
= 2048;
311 Ssl::CertificateDb::check(db_path
, max_db_size
, fs_block_size
);
313 // Initialize SSL subsystem
314 SSL_load_error_strings();
315 SSLeay_add_ssl_algorithms();
318 char request
[HELPER_INPUT_BUFFER
];
319 Ssl::CrtdMessage
request_message(Ssl::CrtdMessage::REQUEST
);
320 Ssl::CrtdMessage::ParseResult parse_result
= Ssl::CrtdMessage::INCOMPLETE
;
322 while (parse_result
== Ssl::CrtdMessage::INCOMPLETE
) {
323 if (fgets(request
, HELPER_INPUT_BUFFER
, stdin
) == NULL
)
325 size_t gcount
= strlen(request
);
326 parse_result
= request_message
.parse(request
, gcount
);
329 if (parse_result
== Ssl::CrtdMessage::ERROR
) {
330 throw std::runtime_error("Cannot parse request message.");
331 } else if (request_message
.getCode() == Ssl::CrtdMessage::code_new_certificate
) {
332 processNewRequest(request_message
, db_path
, max_db_size
, fs_block_size
);
334 throw std::runtime_error("Unknown request code: \"" + request_message
.getCode() + "\".");
338 } catch (std::runtime_error
& error
) {
339 std::cerr
<< argv
[0] << ": " << error
.what() << std::endl
;