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