1 /***************************************************************************
2                 ulxr_ssl_connection.cpp  -  ssl connection
3                              -------------------
4     begin                : Mon May 3 2004
5     copyright            : (C) 2002-2007 by Ewald Arnold
6     email                : ulxmlrpcpp@ewald-arnold.de
7 
8     $Id: ulxr_ssl_connection.cpp 1063 2007-08-19 11:46:20Z ewald-arnold $
9 
10  ***************************************************************************/
11 
12 /**************************************************************************
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Lesser General Public License as
16  * published by the Free Software Foundation; either version 2 of the License,
17  * or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27  *
28  ***************************************************************************/
29 
30 // #define ULXR_SHOW_TRACE
31 // #define ULXR_DEBUG_OUTPUT
32 // #define ULXR_SHOW_READ
33 // #define ULXR_SHOW_WRITE
34 
35 #define ULXR_NEED_EXPORTS
36 #include <ulxmlrpcpp/ulxmlrpcpp.h>  // always first header
37 
38 #ifdef ULXR_INCLUDE_SSL_STUFF
39 
40 #include <openssl/err.h>
41 #include <ulxmlrpcpp/ulxr_ssl_connection.h>
42 #include <ulxmlrpcpp/ulxr_except.h>
43 #include <cstring>
44 
45 
46 static int s_server_session_id_context      = 1;
47 static int s_server_auth_session_id_context = 2;
48 
49 namespace ulxr {
50 
51 
52 bool SSLConnection::ssl_initialized = false;
53 
54 
password_cb(char * buf,int num,int,void * userdata)55 static int password_cb(char *buf,int num, int /*rwflag*/, void *userdata)
56 {
57   ULXR_TRACE(ULXR_PCHAR("password_cb"));
58   SSLConnection *conn = (SSLConnection *)userdata;
59   std::string pass = conn->getPassword();
60 
61   if(num < (int)pass.length()+1)
62     return 0;
63 
64   strcpy(buf, pass.c_str());
65   return(strlen(buf));
66 }
67 
68 
SSLConnection(bool I_am_server,const CppString & domain,unsigned port)69 ULXR_API_IMPL0 SSLConnection::SSLConnection(bool I_am_server, const CppString &domain, unsigned port)
70  : TcpIpConnection(I_am_server, domain, port)
71 {
72   ULXR_TRACE(ULXR_PCHAR("SSLConnection"));
73   init();
74 }
75 
76 
SSLConnection(bool I_am_server,long adr,unsigned port)77 ULXR_API_IMPL0 SSLConnection::SSLConnection(bool I_am_server, long adr, unsigned port)
78  : TcpIpConnection(I_am_server, adr, port)
79 {
80   ULXR_TRACE(ULXR_PCHAR("SSLConnection"));
81   init();
82 }
83 
84 
ULXR_API_IMPL(void)85 ULXR_API_IMPL(void)
86   SSLConnection::setCryptographyData (const std::string &in_password,
87                                       const std::string &in_certfile,
88                                       const std::string &in_keyfile)
89 {
90   password = in_password;
91   keyfile = in_keyfile;
92   certfile = in_certfile;
93 }
94 
95 
ULXR_API_IMPL(void)96 ULXR_API_IMPL(void) SSLConnection::initializeCTX()
97 {
98   ULXR_TRACE(ULXR_PCHAR("initializeCTX"));
99 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10000000L
100   const SSL_METHOD *meth = SSLv23_method();
101 #else
102   SSL_METHOD *meth = SSLv23_method();
103 #endif
104   ssl_ctx = SSL_CTX_new (meth);
105   if (!ssl_ctx)
106   {
107     ERR_print_errors_fp(stderr);
108     exit(2);
109   }
110 
111   SSL_CTX_set_default_passwd_cb(ssl_ctx, password_cb);
112   SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, this);
113 
114   ssl = 0;
115 
116   if (isServerMode())
117   {
118     if (0 >= SSL_CTX_set_session_id_context(ssl_ctx,
119       (const unsigned char *)&s_server_session_id_context,
120       sizeof s_server_session_id_context))
121     {
122       ERR_print_errors_fp(stderr);
123       exit(2);
124     }
125   }
126 }
127 
128 
ULXR_API_IMPL(void)129 ULXR_API_IMPL(void) SSLConnection::init()
130 {
131   ULXR_TRACE(ULXR_PCHAR("init"));
132   session = 0;
133 
134   if (!ssl_initialized)
135   {
136     SSL_library_init();
137     SSLeay_add_ssl_algorithms();
138     SSL_load_error_strings();
139     ssl_initialized = true;
140   }
141 
142   initializeCTX();
143 }
144 
145 
~SSLConnection()146 ULXR_API_IMPL0 SSLConnection::~SSLConnection()
147 {
148   ULXR_TRACE(ULXR_PCHAR("~SSLConnection"));
149   if (ssl_ctx != 0)
150     SSL_CTX_free(ssl_ctx);
151   ssl_ctx = 0;
152 
153   ULXR_TRACE(ULXR_PCHAR("~SSLConnection 2"));
154 
155   if (0 != session)
156     SSL_SESSION_free(session);
157   session = 0;
158 }
159 
160 
ULXR_API_IMPL(void)161 ULXR_API_IMPL(void) SSLConnection::close()
162 {
163   ULXR_TRACE(ULXR_PCHAR("close"));
164 
165   if (!isServerMode()) // clients keep session
166   {
167     if (0 != session)
168       SSL_SESSION_free(session);
169 
170     session = SSL_get1_session(ssl);
171   }
172 
173   ULXR_TRACE(ULXR_PCHAR("close 2"));
174 
175   TcpIpConnection::close();
176   if (ssl != 0)
177     SSL_free(ssl);
178   ssl = 0;
179 }
180 
181 
ULXR_API_IMPL(ssize_t)182 ULXR_API_IMPL(ssize_t) SSLConnection::low_level_write(char const *buff, long len)
183 {
184   ULXR_TRACE(ULXR_PCHAR("low_level_write"));
185   ssize_t ret;
186 
187   if (isConnecting())
188     return TcpIpConnection::low_level_write(buff, len);
189 
190   while (true)
191   {
192     ULXR_TRACE(ULXR_PCHAR("low_level_write 2"));
193     ret = SSL_write(ssl, buff, len);
194     ULXR_TRACE(ULXR_PCHAR("low_level_write 3 ") << ret);
195 
196     if (ret >= 0)
197       break;
198 
199     ULXR_TRACE(ULXR_PCHAR("low_level_write 4"));
200     switch (SSL_get_error(ssl, ret))
201     {
202       case SSL_ERROR_NONE:
203         ULXR_TRACE(ULXR_PCHAR("SSL_ERROR_NONE"));
204         break;
205 
206       case SSL_ERROR_WANT_WRITE:
207         ULXR_TRACE(ULXR_PCHAR("SSL_ERROR_WANT_WRITE"));
208         continue;
209 
210       default:
211         ULXR_TRACE(ULXR_PCHAR("default"));
212         throw ConnectionException(SystemError,
213                                   ulxr_i18n(ULXR_PCHAR("Could not perform SSL_write() call: ")), 500);
214     }
215   }
216   ULXR_TRACE(ULXR_PCHAR("/low_level_write ") << ret);
217   return ret;
218 }
219 
220 
ULXR_API_IMPL(bool)221 ULXR_API_IMPL(bool) SSLConnection::hasPendingInput() const
222 {
223   ULXR_TRACE(ULXR_PCHAR("hasPendingInput "));
224 
225   if (isConnecting())
226     return TcpIpConnection::hasPendingInput();
227 
228   int avail = SSL_pending(ssl);
229   ULXR_TRACE(ULXR_PCHAR("hasPendingInput ") << avail);
230   return avail != 0;
231 }
232 
233 
ULXR_API_IMPL(ssize_t)234 ULXR_API_IMPL(ssize_t) SSLConnection::low_level_read(char *buff, long len)
235 {
236   ULXR_TRACE(ULXR_PCHAR("low_level_read"));
237   ssize_t ret;
238 
239   if (isConnecting())
240     return TcpIpConnection::low_level_read(buff, len);
241 
242   while (true)
243   {
244     ULXR_TRACE(ULXR_PCHAR("low_level_read 2"));
245     ret = SSL_read(ssl, buff, len);
246     ULXR_TRACE(ULXR_PCHAR("low_level_read 3 ") << ret);
247 
248     if (ret >= 0)
249       break;
250 
251     ULXR_TRACE(ULXR_PCHAR("low_level_read 4"));
252     switch (SSL_get_error(ssl, ret))
253     {
254       case SSL_ERROR_NONE:
255         ULXR_TRACE(ULXR_PCHAR("SSL_ERROR_NONE"));
256         break;
257 
258       case SSL_ERROR_WANT_READ:
259         ULXR_TRACE(ULXR_PCHAR("SSL_ERROR_WANT_READ"));
260         continue;
261 
262       default:
263         ULXR_TRACE(ULXR_PCHAR("default"));
264         throw ConnectionException(SystemError,
265                                   ulxr_i18n(ULXR_PCHAR("Could not perform SSL_read() call: ")), 500);
266     }
267   }
268   ULXR_TRACE(ULXR_PCHAR("/low_level_read ") << ret);
269   return ret;
270 }
271 
272 
makeClone()273 TcpIpConnection *ULXR_API_IMPL0 SSLConnection::makeClone()
274 {
275   return new SSLConnection(*this); // shallow copy !!
276 }
277 
278 
ULXR_API_IMPL(void)279 ULXR_API_IMPL(void) SSLConnection::createSSL()
280 {
281   ULXR_TRACE(ULXR_PCHAR("createSSL"));
282   ssl = SSL_new (ssl_ctx);
283   if (ssl == 0)
284     throw ConnectionException(SystemError,
285                           ulxr_i18n(ULXR_PCHAR("problem creating SSL conext object")), 500);
286 
287   int err = SSL_set_fd (ssl, getHandle());
288   if (err == 0)
289     throw ConnectionException(SystemError,
290                           ulxr_i18n(ULXR_PCHAR("problem set file descriptor for SSL")), 500);
291 
292   if (isServerMode())
293   {
294     if (0 >= SSL_set_session_id_context(ssl,
295       (const unsigned char *)&s_server_auth_session_id_context,
296       sizeof(s_server_auth_session_id_context)))
297     {
298       ERR_print_errors_fp(stderr);
299       exit(2);
300     }
301   }
302 }
303 
304 
ULXR_API_IMPL(void)305 ULXR_API_IMPL(void) SSLConnection::open()
306 {
307   ULXR_TRACE(ULXR_PCHAR("open"));
308 
309   TcpIpConnection::open();
310 
311   doConnect();  // CONNECT in non-SSL mode!
312 
313   int err;
314   createSSL();
315 
316   if (0 != session)
317   {
318     ULXR_TRACE(ULXR_PCHAR("SSL_set_session"));
319     SSL_set_session(ssl, session);
320   }
321 
322   err = SSL_connect (ssl);
323   if (err == 0)
324     throw ConnectionException(SystemError,
325                           ulxr_i18n(ULXR_PCHAR("problem starting SSL connection (client mode)")), 500);
326 }
327 
328 
ULXR_API_IMPL(bool)329 ULXR_API_IMPL(bool) SSLConnection::accept(int in_timeout)
330 {
331   ULXR_TRACE(ULXR_PCHAR("accept"));
332 
333   if (SSL_CTX_use_certificate_file(ssl_ctx, certfile.c_str(), SSL_FILETYPE_PEM) <= 0) {
334 //    ERR_print_errors_fp(stderr);
335     throw ConnectionException(SystemError,
336                           ulxr_i18n(ULXR_PCHAR("problem setting up certificate")), 500);
337   }
338 
339   if (SSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile.c_str(), SSL_FILETYPE_PEM) <= 0)
340   {
341 //    ERR_print_errors_fp(stderr);
342     throw ConnectionException(SystemError,
343                           ulxr_i18n(ULXR_PCHAR("problem setting up private key")), 500);
344   }
345 
346 
347   if (!TcpIpConnection::accept(in_timeout))
348     return false;
349 
350   createSSL();
351   int err = SSL_accept (ssl);
352   if (err == 0)
353     throw ConnectionException(SystemError,
354                           ulxr_i18n(ULXR_PCHAR("problem starting SSL connection (server mode)")), 500);
355 
356   /* Get the cipher - opt */
357   ULXR_TRACE(ULXR_PCHAR("SSL connection using ") << ULXR_GET_STRING(SSL_get_cipher (ssl)));
358   return true;
359 }
360 
361 
ULXR_API_IMPL(CppString)362 ULXR_API_IMPL(CppString) SSLConnection::getInterfaceName()
363 {
364   ULXR_TRACE(ULXR_PCHAR("getInterfaceName"));
365   return ULXR_PCHAR("ssl");
366 }
367 
368 
ULXR_API_IMPL(void)369 ULXR_API_IMPL(void) SSLConnection::cut()
370 {
371   ULXR_TRACE(ULXR_PCHAR("cut"));
372   TcpIpConnection::cut();
373   initializeCTX();
374 }
375 
376 
ULXR_API_IMPL(std::string)377 ULXR_API_IMPL(std::string) SSLConnection::getPassword() const
378 {
379   return password;
380 }
381 
382 
ULXR_API_IMPL(SSL *)383 ULXR_API_IMPL(SSL *) SSLConnection::getSslObject() const
384 {
385   return ssl;
386 }
387 
388 
ULXR_API_IMPL(SSL_CTX *)389 ULXR_API_IMPL(SSL_CTX *) SSLConnection::getSslContextObject() const
390 {
391   return ssl_ctx;
392 }
393 
394 
ULXR_API_IMPL(SSL_SESSION *)395 ULXR_API_IMPL(SSL_SESSION *) SSLConnection::getSslSessionObject() const
396 {
397   return session;
398 }
399 
400 
401 }  // namespace ulxr
402 
403 #endif // ULXR_INCLUDE_SSL_STUFF
404