OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
SSLClient.cc
Go to the documentation of this file.
1 // SSLClient.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include "config.h"
34 
35 #include <openssl/ssl.h>
36 #include <openssl/err.h>
37 #include <sys/socket.h> // for socket connect
38 #include <netinet/in.h> // for inet_addr
39 #include <arpa/inet.h> // for htons
40 #include <netdb.h> // for gethostbyname
41 #include <ctype.h> // for isdigit
42 
43 #include <cstring>
44 #include <iostream>
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48 
49 using std::endl ;
50 
51 #include "SSLClient.h"
52 #include "BESInternalError.h"
53 #include "BESDebug.h"
54 
55 SSLClient::SSLClient( const string &hostStr, int portVal,
56  const string &cert_file, const string &cert_auth_file,
57  const string &key_file )
58  : SSLConnection(),
59  _host( hostStr ),
60  _port( portVal ),
61  _cfile( cert_file ),
62  _cafile( cert_auth_file),
63  _kfile( key_file )
64 {
65 }
66 
68 {
69 }
70 
71 void
73 {
74  BESDEBUG( "ppt", "Loading SSL error strings ... " << endl ) ;
75  SSL_load_error_strings() ;
76  BESDEBUG( "ppt", "OK" << endl ) ;
77 
78  BESDEBUG( "ppt", "Initializing SSL library ... " << endl ) ;
79  SSL_library_init() ;
80  BESDEBUG( "ppt", "OK" << endl ) ;
81 
82 #if OPENSSL_VERSION_NUMBER < 0x10000000L
83  SSL_METHOD *method = NULL ;
84 #else
85  const SSL_METHOD *method = NULL ;
86 #endif
87  SSL_CTX *context = NULL ;
88 
89  BESDEBUG( "ppt", "Creating method and context ... " << endl ) ;
90  method = SSLv3_client_method() ;
91  if( method )
92  {
93  context = SSL_CTX_new( method ) ;
94  }
95  if( !context )
96  {
97  string msg = "Failed to create SSL context\n" ;
98  msg += ERR_error_string( ERR_get_error(), NULL ) ;
99  throw BESInternalError( msg, __FILE__, __LINE__ ) ;
100  }
101  else
102  {
103  BESDEBUG( "ppt", "OK" << endl ) ;
104  }
105 
106  bool ok_2_continue = false ;
107  string err_msg ;
108  BESDEBUG( "ppt", "Setting certificate and key ... " << endl ) ;
109  if( SSL_CTX_use_certificate_file( context, _cfile.c_str(), SSL_FILETYPE_PEM ) <= 0 )
110  {
111  err_msg = "FAILED to use certificate file " + _cfile + "\n" ;
112  err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
113  }
114  else if( SSL_CTX_use_PrivateKey_file( context, _kfile.c_str(), SSL_FILETYPE_PEM ) <= 0 )
115  {
116  err_msg = "FAILED to use private key file " + _kfile + "\n" ;
117  err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
118  }
119  else if( !SSL_CTX_check_private_key( context ) )
120  {
121  err_msg = "FAILED to authenticate private key\n" ;
122  err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
123  }
124  else
125  {
126  ok_2_continue = true ;
127  }
128 
129  if( ok_2_continue )
130  {
131  BESDEBUG( "ppt", "OK" << endl ) ;
132  BESDEBUG( "ppt", "Certificate setup ... " << endl ) ;
133  SSL_CTX_set_verify( context, SSL_VERIFY_PEER, SSLClient::verify_server ) ;
134  SSL_CTX_set_client_CA_list( context, SSL_load_client_CA_file( _cafile.c_str() ));
135  if( ( !SSL_CTX_load_verify_locations( context, _cafile.c_str(), NULL )) ||
136  ( !SSL_CTX_set_default_verify_paths( context ) ) )
137  {
138  err_msg = "Certificate setup failed\n" ;
139  err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
140  ok_2_continue = false ;
141  }
142  }
143 
144  int sock_fd = -1 ;
145  if( ok_2_continue )
146  {
147  BESDEBUG( "ppt", "OK" << endl ) ;
148 
149  BESDEBUG( "ppt", "Establishing TCP connection to "
150  << _host << ":" << _port << " ... " << endl ) ;
151  sock_fd = connect_to_server() ;
152  if( sock_fd < 0 )
153  {
154  err_msg = "Failed to establish TCP connection" ;
155  ok_2_continue = false ;
156  }
157  }
158 
159  if( ok_2_continue )
160  {
161  BESDEBUG( "ppt", "OK" << endl ) ;
162 
163  BESDEBUG( "ppt", "Establishing secure connection ... " << endl ) ;
164  int ssl_ret = 0 ;
165  _connection = SSL_new( context ) ;
166  if( !_connection )
167  {
168  err_msg = "FAILED to create new connection\n" ;
169  err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
170  ok_2_continue = false ;
171  }
172  else if( ( ssl_ret = SSL_set_fd( _connection, sock_fd ) ) < 0 )
173  {
174  err_msg = "FAILED to set the socket descriptor\n" ;
175  err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
176  ok_2_continue = false ;
177  }
178  else if( ( ssl_ret = SSL_connect( _connection ) ) < 0 )
179  {
180  err_msg = "FAILED to create SSL connection\n" ;
181  err_msg += ERR_error_string( SSL_get_error( _connection, ssl_ret ), NULL ) ;
182  ok_2_continue = false ;
183  }
184  else if( verify_connection() < 0 )
185  {
186  err_msg = "FAILED to verify SSL connection\n" ;
187  err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
188  ok_2_continue = false ;
189  }
190  }
191 
192  if( ok_2_continue )
193  {
194  BESDEBUG( "ppt", "OK" << endl ) ;
195  }
196  else
197  {
198  BESDEBUG( "ppt", "FAILED" << endl ) ;
199  if( _context ) SSL_CTX_free( _context ) ; _context = NULL ;
200  throw BESInternalError( err_msg, __FILE__, __LINE__ ) ;
201  }
202 
203  _connected = true ;
204 }
205 
206 int
207 SSLClient::connect_to_server( )
208 {
209  int fd = -1 ;
210  struct sockaddr_in addr ;
211 
212  fd = socket( PF_INET, SOCK_STREAM, 0 ) ;
213  if( fd < 0 ) return -1 ;
214 
215  memset( &addr, 0, sizeof( addr ) ) ;
216  addr.sin_family = AF_INET ;
217  addr.sin_port = htons( _port ) ;
218  if( isdigit( (int)*_host.c_str() ) )
219  {
220  addr.sin_addr.s_addr = inet_addr( _host.c_str() ) ;
221  }
222  else
223  {
224  struct hostent *hostEntry ;
225  if( ( hostEntry = gethostbyname( _host.c_str() ) ) != 0 )
226  {
227  if ( hostEntry->h_length > sizeof(addr.sin_addr) )
228  throw BESInternalError("Memory exception.", __FILE__, __LINE__);
229  memcpy( &addr.sin_addr, hostEntry->h_addr, hostEntry->h_length ) ;
230  }
231  else
232  {
233  close( fd ) ;
234  return -1 ;
235  }
236  }
237  if( connect( fd, (struct sockaddr *)&addr, sizeof( addr ) ) < 0 )
238  {
239  close( fd ) ;
240  return -1 ;
241  }
242 
243  return fd ;
244 }
245 
246 int
247 SSLClient::verify_connection( )
248 {
249  X509 *server_cert = NULL ;
250  char *str = NULL ;
251 
252  /*
253  server_cert = SSL_get_peer_certificate( _connection ) ;
254  if( server_cert == NULL )
255  {
256  cout << "server doesn't have a certificate" << endl ;
257  }
258  */
259 
260  return 1 ;
261 }
262 
263 int
264 SSLClient::verify_server( int ok, X509_STORE_CTX *ctx )
265 {
266  if( ok )
267  {
268  BESDEBUG( "ppt", "VERIFIED " << endl ) ;
269  }
270  else
271  {
272  char mybuf[256] ;
273  X509 *err_cert ;
274  int err ;
275 
276  err_cert = X509_STORE_CTX_get_current_cert( ctx ) ;
277  err = X509_STORE_CTX_get_error( ctx ) ;
278  X509_NAME_oneline( X509_get_subject_name( err_cert ), mybuf, 256 ) ;
279  BESDEBUG( "ppt", "FAILED for " << mybuf << endl ) ;
280  BESDEBUG( "ppt", " " << X509_verify_cert_error_string( err )
281  << endl ) ;
282  switch( ctx->error )
283  {
284  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
285  {
286  X509_NAME_oneline( X509_get_issuer_name( err_cert ), mybuf, 256 ) ;
287  BESDEBUG( "ppt", " issuer = " << mybuf << endl ) ;
288  break ;
289  }
290 
291  case X509_V_ERR_CERT_NOT_YET_VALID:
292  case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
293  {
294  BESDEBUG( "ppt", " not yet valid!" << endl ) ;
295  break ;
296  }
297 
298  case X509_V_ERR_CERT_HAS_EXPIRED:
299  case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
300  {
301  BESDEBUG( "ppt", " expired!" << endl ) ;
302  break ;
303  }
304  }
305  }
306 
307  return 1 ;
308 }
309 
316 void
317 SSLClient::dump( ostream &strm ) const
318 {
319  strm << BESIndent::LMarg << "SSLClient::dump - ("
320  << (void *)this << ")" << endl ;
322  strm << BESIndent::LMarg << "host: " << _host << endl ;
323  strm << BESIndent::LMarg << "port: " << _port << endl ;
324  strm << BESIndent::LMarg << "cert file: " << _cfile << endl ;
325  strm << BESIndent::LMarg << "cert authority file: " << _cafile << endl ;
326  strm << BESIndent::LMarg << "key file: " << _kfile << endl ;
327  SSLConnection::dump( strm ) ;
329 }
330 
SSLClient(const string &hostStr, int portVal, const string &cert_file, const string &cert_auth_file, const string &key_file)
Definition: SSLClient.cc:55
virtual void dump(ostream &strm) const
dumps information about this object
exception thrown if inernal error encountered
virtual void initConnection()
Definition: SSLClient.cc:72
virtual void dump(ostream &strm) const
dumps information about this object
Definition: SSLClient.cc:317
static void Indent()
Definition: BESIndent.cc:38
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
~SSLClient()
Definition: SSLClient.cc:67
SSL_CTX * _context
Definition: SSLConnection.h:50
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static void UnIndent()
Definition: BESIndent.cc:44