Panda3D
openSSLWrapper.cxx
1 // Filename: openSSLWrapper.cxx
2 // Created by: drose (05Sep09)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "openSSLWrapper.h"
16 
17 #ifdef HAVE_OPENSSL
18 
19 #include "virtualFileSystem.h"
20 #include "ca_bundle_data_src.c"
21 
22 OpenSSLWrapper *OpenSSLWrapper::_global_ptr = NULL;
23 
24 ////////////////////////////////////////////////////////////////////
25 // Function: OpenSSLWrapper::Constructor
26 // Access: Private
27 // Description:
28 ////////////////////////////////////////////////////////////////////
29 OpenSSLWrapper::
30 OpenSSLWrapper() {
31  // It is necessary to call this before making any other OpenSSL
32  // call, per the docs. Also, the docs say that making this call
33  // will seed the random number generator. Apparently you can get
34  // away with not calling it in versions prior to 0.9.8, however.
35  SSL_library_init();
36 
37  OpenSSL_add_all_algorithms();
38 
39  _x509_store = X509_STORE_new();
40  X509_STORE_set_default_paths(_x509_store);
41 
42  // Load in the well-known certificate authorities compiled into this
43  // program.
44  load_certificates_from_der_ram((const char *)ca_bundle_data, ca_bundle_data_len);
45 
46  // Load in any default certificates listed in the Config.prc file.
47  ConfigVariableFilename ca_bundle_filename
48  ("ca-bundle-filename", "",
49  PRC_DESC("This names the certificate authority file for OpenSSL "
50  "to use to verify whether SSL certificates are trusted or not. "
51  "The file named by this setting should contain one or more "
52  "PEM-formatted certificates from trusted certificate "
53  "authorities. This is a fairly standard file; a copy of "
54  "ca-bundle.crt is included in the OpenSSL distribution, and "
55  "is also included with Panda."));
56 
57  if (!ca_bundle_filename.empty()) {
58  load_certificates(ca_bundle_filename);
59  }
60 
61  ConfigVariableList ssl_certificates
62  ("ssl-certificates",
63  PRC_DESC("This variable lists additional filenames, on top of the file "
64  "named by ca-bundle-filename, that contain trusted SSL "
65  "certificates or certificate authorities."));
66 
67  int num_certs = ssl_certificates.get_num_unique_values();
68  for (int ci = 0; ci < num_certs; ci++) {
69  string cert_file = ssl_certificates.get_unique_value(ci);
70  Filename filename = Filename::expand_from(cert_file);
71  load_certificates(filename);
72  }
73 }
74 
75 ////////////////////////////////////////////////////////////////////
76 // Function: OpenSSLWrapper::Destructor
77 // Access: Private
78 // Description:
79 ////////////////////////////////////////////////////////////////////
80 OpenSSLWrapper::
81 ~OpenSSLWrapper() {
82  // Actually, the destructor is never called.
83  X509_STORE_free(_x509_store);
84 }
85 
86 ////////////////////////////////////////////////////////////////////
87 // Function: OpenSSLWrapper::clear_certificates
88 // Access: Public
89 // Description: Removes all the certificates from the global store,
90 // including the compiled-in certificates loaded from
91 // ca_bundle_data.c. You can add new certificates by
92 // calling load_certificates().
93 ////////////////////////////////////////////////////////////////////
94 void OpenSSLWrapper::
95 clear_certificates() {
96  // We do this by deleting the store and creating a new one.
97  X509_STORE_free(_x509_store);
98  _x509_store = X509_STORE_new();
99 
100  // We don't set the default path either. We want a squeaky-clean store.
101  //X509_STORE_set_default_paths(_x509_store);
102 }
103 
104 ////////////////////////////////////////////////////////////////////
105 // Function: OpenSSLWrapper::load_certificates
106 // Access: Public
107 // Description: Reads the PEM-formatted certificate(s) (delimited by
108 // -----BEGIN CERTIFICATE----- and -----END
109 // CERTIFICATE-----) from the indicated file and adds
110 // them to the global store object, retrieved via
111 // get_x509_store().
112 //
113 // Returns the number of certificates read on success,
114 // or 0 on failure.
115 //
116 // You should call this only with trusted,
117 // locally-stored certificates; not with certificates
118 // received from an untrusted source.
119 ////////////////////////////////////////////////////////////////////
120 int OpenSSLWrapper::
121 load_certificates(const Filename &filename) {
123 
124  // First, read the complete file into memory.
125  string data;
126  if (!vfs->read_file(filename, data, true)) {
127  // Could not find or read file.
128  express_cat.info()
129  << "Could not read " << filename << ".\n";
130  return 0;
131  }
132 
133  int result = load_certificates_from_pem_ram(data.data(), data.size());
134 
135  if (result <= 0) {
136  express_cat.info()
137  << "Could not load certificates from " << filename << ".\n";
138  notify_ssl_errors();
139  return 0;
140  }
141 
142  if (express_cat.is_debug()) {
143  express_cat.debug()
144  << "Appending " << result << " SSL certificates from "
145  << filename << "\n";
146  }
147 
148  return result;
149 }
150 
151 ////////////////////////////////////////////////////////////////////
152 // Function: OpenSSLWrapper::load_certificates_from_pem_ram
153 // Access: Public
154 // Description: Reads a chain of trusted certificates from the
155 // indicated data buffer and adds them to the X509_STORE
156 // object. The data buffer should be PEM-formatted.
157 // Returns the number of certificates read on success,
158 // or 0 on failure.
159 //
160 // You should call this only with trusted,
161 // locally-stored certificates; not with certificates
162 // received from an untrusted source.
163 ////////////////////////////////////////////////////////////////////
164 int OpenSSLWrapper::
165 load_certificates_from_pem_ram(const char *data, size_t data_size) {
166  STACK_OF(X509_INFO) *inf;
167 
168  // Create an in-memory BIO to read the "file" from the buffer, and
169  // call the low-level routines to read the certificates from the
170  // BIO.
171  BIO *mbio = BIO_new_mem_buf((void *)data, data_size);
172 
173  // We have to be sure and clear the OpenSSL error state before we
174  // call this function, or it will get confused.
175  ERR_clear_error();
176  inf = PEM_X509_INFO_read_bio(mbio, NULL, NULL, NULL);
177  BIO_free(mbio);
178 
179  if (!inf) {
180  // Could not scan certificates.
181  express_cat.info()
182  << "PEM_X509_INFO_read_bio() returned NULL.\n";
183  notify_ssl_errors();
184  return 0;
185  }
186 
187  if (express_cat.is_spam()) {
188  express_cat.spam()
189  << "PEM_X509_INFO_read_bio() found " << sk_X509_INFO_num(inf)
190  << " entries.\n";
191  }
192 
193  // Now add the certificates to the store.
194 
195  int count = 0;
196  int num_entries = sk_X509_INFO_num(inf);
197  for (int i = 0; i < num_entries; i++) {
198  X509_INFO *itmp = sk_X509_INFO_value(inf, i);
199 
200  if (itmp->x509) {
201  int result = X509_STORE_add_cert(_x509_store, itmp->x509);
202  if (result == 0) {
203  notify_debug_ssl_errors();
204  } else {
205  ++count;
206  }
207 
208  if (express_cat.is_spam()) {
209  express_cat.spam()
210  << "Entry " << i << " is x509\n";
211  }
212 
213  } else if (itmp->crl) {
214  int result = X509_STORE_add_crl(_x509_store, itmp->crl);
215  if (result == 0) {
216  notify_debug_ssl_errors();
217  } else {
218  ++count;
219  }
220 
221  if (express_cat.is_spam()) {
222  express_cat.spam()
223  << "Entry " << i << " is crl\n";
224  }
225 
226  } else if (itmp->x_pkey) {
227  if (express_cat.is_spam()) {
228  express_cat.spam()
229  << "Entry " << i << " is pkey\n";
230  }
231 
232  } else {
233  if (express_cat.is_spam()) {
234  express_cat.spam()
235  << "Entry " << i << " is unknown type\n";
236  }
237  }
238  }
239  sk_X509_INFO_pop_free(inf, X509_INFO_free);
240 
241  if (express_cat.is_spam()) {
242  express_cat.spam()
243  << "successfully loaded " << count << " entries.\n";
244  }
245 
246  return count;
247 }
248 
249 ////////////////////////////////////////////////////////////////////
250 // Function: OpenSSLWrapper::load_certificates_from_der_ram
251 // Access: Public
252 // Description: Reads a chain of trusted certificates from the
253 // indicated data buffer and adds them to the X509_STORE
254 // object. The data buffer should be DER-formatted.
255 // Returns the number of certificates read on success,
256 // or 0 on failure.
257 //
258 // You should call this only with trusted,
259 // locally-stored certificates; not with certificates
260 // received from an untrusted source.
261 ////////////////////////////////////////////////////////////////////
262 int OpenSSLWrapper::
263 load_certificates_from_der_ram(const char *data, size_t data_size) {
264  if (express_cat.is_spam()) {
265  express_cat.spam()
266  << "load_certificates_from_der_ram(" << (void *)data
267  << ", " << data_size << ")\n";
268  }
269 
270  int count = 0;
271 
272 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
273  // Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **.
274  const unsigned char *bp, *bp_end;
275 #else
276  // Prior to 0.9.8, d2i_X509() accepted an unsigned char **.
277  unsigned char *bp, *bp_end;
278 #endif
279 
280  bp = (unsigned char *)data;
281  bp_end = bp + data_size;
282  while (bp < bp_end) {
283  X509 *x509 = d2i_X509(NULL, &bp, bp_end - bp);
284  if (x509 == NULL) {
285  notify_ssl_errors();
286  break;
287  }
288 
289  int result = X509_STORE_add_cert(_x509_store, x509);
290  if (result == 0) {
291  notify_debug_ssl_errors();
292  } else {
293  ++count;
294  }
295  }
296 
297 
298  if (express_cat.is_spam()) {
299  express_cat.spam()
300  << "loaded " << count << " certificates\n";
301  }
302 
303  return count;
304 }
305 
306 ////////////////////////////////////////////////////////////////////
307 // Function: OpenSSLWrapper::get_x509_store
308 // Access: Public
309 // Description: Returns the global X509_STORE object.
310 //
311 // It has to be a global object, because OpenSSL seems
312 // to store some global pointers associated with this
313 // object whether you want it to or not, and keeping
314 // independent copies of a local X509_STORE object
315 // doesn't seem to work that well. So, we have one
316 // store that keeps all certificates the application
317 // might need.
318 ////////////////////////////////////////////////////////////////////
319 X509_STORE *OpenSSLWrapper::
320 get_x509_store() {
321  return _x509_store;
322 }
323 
324 ////////////////////////////////////////////////////////////////////
325 // Function: OpenSSLWrapper::notify_ssl_errors
326 // Access: Public
327 // Description: A convenience function that is itself a wrapper
328 // around the OpenSSL convenience function to output the
329 // recent OpenSSL errors. This function sends the error
330 // string to express_cat.warning(). If
331 // REPORT_OPENSSL_ERRORS is not defined, the function
332 // does nothing.
333 ////////////////////////////////////////////////////////////////////
334 void OpenSSLWrapper::
335 notify_ssl_errors() {
336 #ifdef REPORT_OPENSSL_ERRORS
337  static bool strings_loaded = false;
338  if (!strings_loaded) {
339  SSL_load_error_strings();
340  strings_loaded = true;
341  }
342 
343  unsigned long e = ERR_get_error();
344  while (e != 0) {
345  static const size_t buffer_len = 256;
346  char buffer[buffer_len];
347  ERR_error_string_n(e, buffer, buffer_len);
348  express_cat.warning() << buffer << "\n";
349  e = ERR_get_error();
350  }
351 #endif // REPORT_OPENSSL_ERRORS
352 }
353 
354 ////////////////////////////////////////////////////////////////////
355 // Function: OpenSSLWrapper::notify_debug_ssl_errors
356 // Access: Public
357 // Description: As notify_ssl_errors(), but sends the output to debug
358 // instead of warning.
359 ////////////////////////////////////////////////////////////////////
360 void OpenSSLWrapper::
361 notify_debug_ssl_errors() {
362 #ifdef REPORT_OPENSSL_ERRORS
363  static bool strings_loaded = false;
364  if (!strings_loaded) {
365  SSL_load_error_strings();
366  strings_loaded = true;
367  }
368 
369  unsigned long e = ERR_get_error();
370  while (e != 0) {
371  if (express_cat.is_debug()) {
372  static const size_t buffer_len = 256;
373  char buffer[buffer_len];
374  ERR_error_string_n(e, buffer, buffer_len);
375  express_cat.debug() << buffer << "\n";
376  }
377  e = ERR_get_error();
378  }
379 #endif // REPORT_OPENSSL_ERRORS
380 }
381 
382 ////////////////////////////////////////////////////////////////////
383 // Function: OpenSSLWrapper::get_global_ptr
384 // Access: Public, Static
385 // Description:
386 ////////////////////////////////////////////////////////////////////
387 OpenSSLWrapper *OpenSSLWrapper::
388 get_global_ptr() {
389  if (_global_ptr == NULL) {
390  _global_ptr = new OpenSSLWrapper;
391  }
392  return _global_ptr;
393 }
394 
395 #endif // HAVE_OPENSSL
This is a convenience class to specialize ConfigVariable as a Filename type.
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
static Filename expand_from(const string &user_string, Type type=T_general)
Returns the same thing as from_os_specific(), but embedded environment variable references (e...
Definition: filename.cxx:418
This class is similar to ConfigVariable, but it reports its value as a list of strings.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.