Merge branch 'master' into release_0_8_9
[gnash.git] / cygnal / libnet / sslclient.cpp
blob36e57f1d7f1052b99470f8263489b601a8a0e6ba
1 // ssl.cpp: HyperText Transport Protocol handler for Cygnal, for Gnash.
2 //
3 // Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "gnashconfig.h"
22 #endif
24 #include <boost/thread/mutex.hpp>
25 #include <boost/shared_ptr.hpp>
26 #include <boost/shared_array.hpp>
27 #include <boost/scoped_array.hpp>
28 #include <boost/cstdint.hpp>
29 #include <boost/array.hpp>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <string>
34 #include <vector>
35 #include <iostream>
36 #include <cstring>
37 #include <sstream>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <algorithm>
42 #include "GnashSystemIOHeaders.h" // read()
43 #include "sslclient.h"
44 #include "amf.h"
45 #include "element.h"
46 #include "cque.h"
47 #include "log.h"
48 #include "network.h"
49 #include "utility.h"
50 #include "buffer.h"
51 #include "diskstream.h"
52 #include "cache.h"
54 #ifdef HAVE_OPENSSL_SSL_H
55 #include <openssl/ssl.h>
56 #include <openssl/err.h>
57 #include <openssl/rand.h>
58 #endif
60 #if defined(_WIN32) || defined(WIN32)
61 # define __PRETTY_FUNCTION__ __FUNCDNAME__
62 # include <winsock2.h>
63 # include <direct.h>
64 #else
65 # include <unistd.h>
66 # include <sys/param.h>
67 #endif
69 using namespace gnash;
70 using namespace std;
72 static boost::mutex stl_mutex;
73 // The debug log used by all the gnash libraries.
75 static LogFile& dbglogfile = LogFile::getDefaultInstance();
76 RcInitFile& rc = RcInitFile::getDefaultInstance();
78 // This is static in this file, instead of being a private variable in
79 // the SSLCLient class, is so it's accessible from the C function callback,
80 // which can't access the private data of the class.
81 // static SSLClient::passwd_t password(SSL_PASSWD_SIZE);
82 static string password;
84 namespace gnash
87 const size_t SSL_PASSWD_SIZE = 1024;
88 static const char *SSL_HOST = "localhost";
89 // static const char *SSL_CA_LIST = "rootcert.pem";
90 // static const char *SSL_CLIENT_CERTFILE = "client.pem";
91 // static const char *SSL_CLIENT_ROOTPATH = "/etc/pki/tls";
93 // const char *RANDOM = "random.pem";
95 SSLClient::SSLClient()
96 : _hostname("localhost"),
97 _calist(rc.getRootCert()),
98 _keyfile(rc.getCertFile()),
99 _rootpath(rc.getCertDir()),
100 _need_server_auth(true)
102 GNASH_REPORT_FUNCTION;
104 // setPort(SSL_PORT);
105 setPassword("foobar");
107 // Initialize SSL library
108 SSL_library_init();
109 RAND_load_file("/dev/urandom", 1024);
111 // Load the error strings so the SSL_error_*() functions work
112 SSL_load_error_strings();
115 SSLClient::~SSLClient()
117 // GNASH_REPORT_FUNCTION;
120 // Read bytes from the already opened SSL connection
122 SSLClient::sslRead(cygnal::Buffer &buf)
124 GNASH_REPORT_FUNCTION;
126 return sslRead(buf.reference(), buf.allocated());
130 SSLClient::sslRead(boost::uint8_t *buf, size_t size)
132 GNASH_REPORT_FUNCTION;
134 ERR_clear_error();
135 int ret = SSL_read(_ssl.get(), buf, size);
136 if (ret < 0) {
137 log_error("Error was: \"%s\"!", ERR_reason_error_string(ERR_get_error()));
140 return ret;
143 // Write bytes to the already opened SSL connection
145 SSLClient::sslWrite(cygnal::Buffer &buf)
147 GNASH_REPORT_FUNCTION;
149 return sslWrite(buf.reference(), buf.allocated());
153 SSLClient::sslWrite(const boost::uint8_t *buf, size_t length)
155 GNASH_REPORT_FUNCTION;
157 ERR_clear_error();
158 int ret = SSL_write(_ssl.get(), buf, length);
159 if (ret < 0) {
160 log_error("Error was: \"%s\"!", ERR_reason_error_string(ERR_get_error()));
162 return ret;
165 // Setup the Context for this connection
166 bool
167 SSLClient::sslSetupCTX()
169 return sslSetupCTX(_keyfile, _calist);
172 bool
173 SSLClient::sslSetupCTX(std::string &keyspec, std::string &caspec)
175 GNASH_REPORT_FUNCTION;
176 // SSL_METHOD *meth;
177 int ret;
178 string keyfile;
179 string cafile;
181 #if 1
182 if (keyspec.find('/', 0) != string::npos) {
183 keyfile = keyspec;
184 } else {
185 keyfile = _rootpath;
186 keyfile += "/";
187 keyfile += keyspec;
191 if (caspec.find('/', 0) != string::npos) {
192 cafile = caspec;
193 } else {
194 cafile = _rootpath;
195 cafile += "/";
196 cafile += caspec;
198 #else
199 keyfile = keyspec;
200 cafile = caspec;
201 #endif
203 // create the context
204 _ctx.reset(SSL_CTX_new( SSLv23_method()));
206 ERR_clear_error();
207 if (!(SSL_CTX_load_verify_locations(_ctx.get(), cafile.c_str(),
208 _rootpath.c_str()))) {
209 log_error("Can't read CA list from \"%s\"!", cafile);
210 log_error("Error was: \"%s\"!", ERR_reason_error_string(ERR_get_error()));
211 return false;
212 } else {
213 log_debug("Read CA list from \"%s\"", cafile);
216 // Load our keys and certificates
217 ERR_clear_error();
218 if ((ret = SSL_CTX_use_certificate_chain_file(_ctx.get(), keyfile.c_str())) != 1) {
219 log_error("Can't read certificate file \"%s\"!", keyfile);
220 return false;
221 } else {
222 log_debug("Read certificate file \"%s\".", keyfile);
225 // Set the password as a callback, otherwise we get prompted for it
226 SSL_CTX_set_default_passwd_cb(_ctx.get(), password_cb);
228 // Add the first private key in the keyfile to the context.
229 ERR_clear_error();
230 if((ret = SSL_CTX_use_PrivateKey_file(_ctx.get(), keyfile.c_str(),
231 SSL_FILETYPE_PEM)) != 1) {
232 log_error("Can't read CERT file \"%s\"!", keyfile);
233 log_error("Error was: \"%s\"!", ERR_reason_error_string(ERR_get_error()));
234 return false;
235 } else {
236 log_debug("Read key file \"%s\".", keyfile);
239 SSL_CTX_set_verify(_ctx.get(), SSL_VERIFY_PEER, verify_callback);
241 #if (OPENSSL_VERSION_NUMBER < 0x00905100L)
242 SSL_CTX_set_verify_depth(_ctx.get(), 4);
243 #endif
245 return true;
248 // Shutdown the Context for this connection
249 bool
250 SSLClient::sslShutdown()
252 // GNASH_REPORT_FUNCTION;
254 // SSL_clear(_ssl.get());
255 SSL_free(_ssl.get());
256 SSL_CTX_free(_ctx.get());
258 return true;
261 // sslConnect() is how the client connects to the server
262 bool
263 SSLClient::sslConnect(int fd)
265 return sslConnect(fd, _hostname, 0);
268 bool
269 SSLClient::sslConnect(int fd, std::string &hostname, short port)
271 GNASH_REPORT_FUNCTION;
272 int ret;
274 if (!_ctx) {
275 if (!sslSetupCTX()) {
276 return false;
280 _ssl.reset(SSL_new(_ctx.get()));
282 // // Make a tcp/ip connect to the server
283 // if (createClient(hostname, getPort()) == false) {
284 // log_error("Can't connect to server %s", hostname);
285 // return false;
286 // }
288 // Handshake the server
289 ERR_clear_error();
290 #if 0
291 _bio.reset(BIO_new_socket(fd, BIO_NOCLOSE));
292 #else
293 // BIO_set_conn_hostname(_bio.get(), _hostname.c_str());
294 _bio.reset(BIO_new_connect(const_cast<char *>(_hostname.c_str())));
296 BIO_set_conn_int_port(_bio.get(), &port);
297 log_debug("PORT is: %d", BIO_get_conn_port(_bio.get()));
299 if (BIO_do_connect(_bio.get()) <= 0) {
300 log_error("Error connecting to remote machine: %s",
301 ERR_reason_error_string(ERR_get_error()));
303 #endif
305 SSL_set_bio(_ssl.get(), _bio.get(), _bio.get());
306 SSL_set_connect_state(_ssl.get());
308 if ((ret = SSL_connect(_ssl.get())) < 0) {
309 log_error("Can't connect to SSL server %s", hostname);
310 log_error("Error was: \"%s\"!", ERR_reason_error_string(ERR_get_error()));
311 return false;
312 } else {
313 log_debug("Connected to SSL server %s", hostname);
316 ERR_clear_error();
317 #if 0
318 if (_need_server_auth) {
319 checkCert(hostname);
321 #endif
323 return true;
326 bool
327 SSLClient::checkCert()
329 GNASH_REPORT_FUNCTION;
330 return checkCert(_hostname);
333 bool
334 SSLClient::checkCert(std::string &hostname)
336 GNASH_REPORT_FUNCTION;
338 if (!_ssl || (hostname.empty())) {
339 return false;
342 X509 *peer;
343 char peer_CN[256];
345 if (SSL_get_verify_result(_ssl.get()) != X509_V_OK) {
346 log_error("Certificate doesn't verify");
347 return false;
348 } else {
349 log_debug("Certificate verified.");
352 // Check the cert chain. The chain length
353 // is automatically checked by OpenSSL when
354 // we set the verify depth in the ctx
356 // Check the common name
357 if ((peer = SSL_get_peer_certificate(_ssl.get())) == 0) {
358 log_debug("Couldn't get Peer certificate!");
359 return false;
360 } else {
361 log_debug("Got Peer certificate.");
364 ERR_clear_error();
365 X509_NAME_get_text_by_NID (X509_get_subject_name(peer),
366 NID_commonName, peer_CN, 256);
368 if (strcasecmp(peer_CN, hostname.c_str())) {
369 log_error("Common name doesn't match host name");
372 return true;
375 void
376 SSLClient::dump() {
377 // GNASH_REPORT_FUNCTION;
379 boost::mutex::scoped_lock lock(stl_mutex);
381 log_debug (_("==== The SSL header breaks down as follows: ===="));
384 // The password is a global variable so it can be set from a C function
385 // callback.
386 void
387 SSLClient::setPassword(std::string pw) {
388 password = pw;
391 std::string &
392 SSLClient::getPassword() {
393 return password;
396 extern "C" {
398 // This is the callback required when setting up the password.
400 password_cb(char *buf, int size, int /* rwflag */, void * /* userdata */)
402 GNASH_REPORT_FUNCTION;
404 log_debug("Callback executed to set the SSL password, size is: %d",
405 password.size());
407 if(size <= static_cast<int>(password.size()+1)) {
408 log_error("The buffer for the password needs to be %d bytes larger",
409 password.size() - size);
410 return(0);
413 // copy the password, we'll need it later
414 // std::copy(buf, buf + size, password.data());
415 std::copy(password.begin(), password.end(), buf);
418 return(password.size());
423 verify_callback(int ok, X509_STORE_CTX *store)
425 GNASH_REPORT_FUNCTION;
427 char data[256];
429 if (!ok) {
430 X509 *cert = X509_STORE_CTX_get_current_cert(store);
431 int depth = X509_STORE_CTX_get_error_depth(store);
432 int err = X509_STORE_CTX_get_error(store);
434 log_error("-Error with certificate at depth: %i\n", depth);
435 X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
436 log_error(" issuer = %s\n", data);
437 X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
438 log_error(" subject = %s\n", data);
439 log_error("err %i:%s\n", err, X509_verify_cert_error_string(err));
442 return ok;
445 } // end of extern C
447 } // end of gnash namespace
450 // local Variables:
451 // mode: C++
452 // indent-tabs-mode: t
453 // End: