00001
00002
00003
00004
00005 #define OPENSSL_NO_KRB5
00006 #include "wvsslstream.h"
00007 #include "wvx509.h"
00008 #include "wvcrypto.h"
00009 #include "wvmoniker.h"
00010 #include <openssl/ssl.h>
00011 #include <openssl/err.h>
00012 #include <assert.h>
00013
00014 #ifdef _WIN32
00015 #undef errno
00016 #define errno GetLastError()
00017 typedef DWORD error_t;
00018 #define EAGAIN WSAEWOULDBLOCK
00019 #endif
00020
00021 static IWvStream *creator(WvStringParm s, IObject *obj, void *userdata)
00022 {
00023 if (!obj)
00024 obj = wvcreate<IWvStream>(s);
00025 return new WvSSLStream(mutate<IWvStream>(obj),
00026 (WvX509Mgr *)userdata, false, false);
00027 }
00028
00029 static IWvStream *screator(WvStringParm s, IObject *obj, void *userdata)
00030 {
00031 if (!obj)
00032 obj = wvcreate<IWvStream>(s);
00033 return new WvSSLStream(mutate<IWvStream>(obj),
00034 (WvX509Mgr *)userdata, false, true);
00035 }
00036
00037 static WvMoniker<IWvStream> reg("ssl", creator);
00038 static WvMoniker<IWvStream> sreg("sslserv", screator);
00039
00040
00041
00042 #define MAX_BOUNCE_AMOUNT (16384) // 1 SSLv3/TLSv1 record
00043
00044 WvSSLStream::WvSSLStream(IWvStream *_slave, WvX509Mgr *x509,
00045 bool _verify, bool _is_server) :
00046 WvStreamClone(_slave), debug("WvSSLStream",WvLog::Debug5),
00047 write_bouncebuf(MAX_BOUNCE_AMOUNT), write_eat(0),
00048 read_bouncebuf(MAX_BOUNCE_AMOUNT), read_pending(false)
00049 {
00050 verify = _verify;
00051 is_server = _is_server;
00052
00053 wvssl_init();
00054
00055 if (is_server && (x509 == NULL))
00056 {
00057 seterr("Certificate not available: server mode not possible!");
00058 return;
00059 }
00060
00061 ctx = NULL;
00062 ssl = NULL;
00063 sslconnected = false;
00064
00065 if (is_server)
00066 {
00067 meth = SSLv23_server_method();
00068 debug("Configured algorithms and methods for server mode.\n");
00069
00070 ctx = SSL_CTX_new(meth);
00071 if (!ctx)
00072 {
00073 seterr("Can't get SSL context!");
00074 return;
00075 }
00076
00077
00078 SSL_CTX_set_mode(ctx,SSL_MODE_ENABLE_PARTIAL_WRITE);
00079
00080
00081
00082 SSL_CTX_set_cipher_list(ctx,"HIGH");
00083
00084
00085
00086 SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
00087
00088 if (SSL_CTX_use_certificate(ctx, x509->cert) <= 0)
00089 {
00090 seterr("Error loading certificate!");
00091 return;
00092 }
00093 debug("Certificate activated.\n");
00094
00095 if (SSL_CTX_use_RSAPrivateKey(ctx, x509->rsa->rsa) <= 0)
00096 {
00097 seterr("Error loading RSA private key!");
00098 return;
00099 }
00100 debug("RSA private key activated.\n");
00101 debug("Server mode ready.\n");
00102 }
00103 else
00104 {
00105 meth = SSLv23_client_method();
00106 debug("Configured algorithms and methods for client mode.\n");
00107
00108 ctx = SSL_CTX_new(meth);
00109 if (!ctx)
00110 {
00111 seterr("Can't get SSL context!");
00112 return;
00113 }
00114 }
00115
00116 ERR_clear_error();
00117 ssl = SSL_new(ctx);
00118 if (!ssl)
00119 {
00120 seterr("Can't create SSL object!");
00121 return;
00122 }
00123
00124 debug("SSL stream initialized.\n");
00125
00126
00127 force_select(false, true);
00128 }
00129
00130
00131 WvSSLStream::~WvSSLStream()
00132 {
00133 close();
00134
00135 debug("Shutting down SSL connection.\n");
00136 if (geterr())
00137 debug("Error was: %s\n", errstr());
00138
00139 wvssl_free();
00140 }
00141
00142
00143 void WvSSLStream::printerr(WvStringParm func)
00144 {
00145 unsigned long l = ERR_get_error();
00146 char buf[121];
00147
00148 SSL_load_error_strings();
00149 while (l)
00150 {
00151 ERR_error_string(l, buf);
00152 debug("%s error: %s\n", func, buf);
00153 l = ERR_get_error();
00154 }
00155 ERR_free_strings();
00156 }
00157
00158
00159 size_t WvSSLStream::uread(void *buf, size_t len)
00160 {
00161 if (!sslconnected)
00162 return 0;
00163 if (len == 0) return 0;
00164
00165
00166
00167 read_pending = true;
00168
00169 size_t total = 0;
00170 for (;;)
00171 {
00172
00173 if (read_bouncebuf.used() != 0)
00174 {
00175
00176 size_t amount = len < read_bouncebuf.used() ?
00177 len : read_bouncebuf.used();
00178 read_bouncebuf.move(buf, amount);
00179
00180
00181 len -= amount;
00182 total += amount;
00183 if (len == 0)
00184 break;
00185 buf = (unsigned char *)buf + amount;
00186 }
00187
00188
00189 read_bouncebuf.zap();
00190 size_t avail = read_bouncebuf.free();
00191 unsigned char *data = read_bouncebuf.alloc(avail);
00192
00193 ERR_clear_error();
00194 int result = SSL_read(ssl, data, avail);
00195 if (result <= 0)
00196 {
00197 error_t err = errno;
00198 read_bouncebuf.unalloc(avail);
00199 int errcode = SSL_get_error(ssl, result);
00200 switch (errcode)
00201 {
00202 case SSL_ERROR_WANT_READ:
00203 case SSL_ERROR_WANT_WRITE:
00204
00205 break;
00206
00207 case SSL_ERROR_NONE:
00208 break;
00209
00210 case SSL_ERROR_ZERO_RETURN:
00211 debug("<< EOF: zero return\n");
00212 close();
00213 break;
00214
00215 case SSL_ERROR_SYSCALL:
00216 if (!err)
00217 {
00218 if (result == 0)
00219 {
00220 debug("<< EOF: syscall error\n");
00221 close();
00222 }
00223 break;
00224 }
00225 debug("<< SSL_read() %s\n", strerror(errno));
00226
00227 default:
00228 printerr("SSL_read");
00229 seterr("SSL read error #%s", errcode);
00230 break;
00231 }
00232 read_pending = false;
00233 break;
00234 }
00235
00236
00237 if (result < 0)
00238 result = 0;
00239 read_bouncebuf.unalloc(avail - result);
00240 }
00241
00242
00243 return total;
00244 }
00245
00246
00247 size_t WvSSLStream::uwrite(const void *buf, size_t len)
00248 {
00249 if (!sslconnected)
00250 {
00251 debug(">> writing, but not connected yet (%s); enqueue.\n", getwfd());
00252 unconnected_buf.put(buf, len);
00253 return len;
00254 }
00255
00256 if (len == 0) return 0;
00257
00258
00259
00260 size_t total = 0;
00261
00262
00263 if (write_eat >= len)
00264 {
00265 write_eat -= len;
00266 total = len;
00267 len = 0;
00268 }
00269 else
00270 {
00271 buf = (const unsigned char *)buf + write_eat;
00272 total = write_eat;
00273 len -= write_eat;
00274 write_eat = 0;
00275 }
00276
00277
00278
00279 for (;;)
00280 {
00281
00282 if (write_bouncebuf.used() == 0)
00283 {
00284 if (len == 0) break;
00285
00286
00287
00288
00289
00290 size_t amount = len < write_bouncebuf.free() ?
00291 len : write_bouncebuf.free();
00292 write_bouncebuf.put(buf, amount);
00293
00294 }
00295
00296
00297 size_t used = write_bouncebuf.used();
00298 const unsigned char *data = write_bouncebuf.get(used);
00299
00300 ERR_clear_error();
00301 int result = SSL_write(ssl, data, used);
00302 if (result <= 0)
00303 {
00304 int errcode = SSL_get_error(ssl, result);
00305 write_bouncebuf.unget(used);
00306 switch (errcode)
00307 {
00308 case SSL_ERROR_WANT_READ:
00309 case SSL_ERROR_WANT_WRITE:
00310 debug(">> SSL_write() needs to wait for writable.\n");
00311 break;
00312
00313 case SSL_ERROR_SYSCALL:
00314 debug(">> ERROR: SSL_write() failed on socket error.\n");
00315 seterr(WvString("SSL write error: %s", strerror(errno)));
00316 break;
00317
00318
00319 case SSL_ERROR_SSL:
00320 debug(">> ERROR: SSL_write() failed on internal error.\n");
00321 seterr(WvString("SSL write error: %s",
00322 ERR_error_string(ERR_get_error(), NULL)));
00323 break;
00324
00325 case SSL_ERROR_NONE:
00326 break;
00327
00328 case SSL_ERROR_ZERO_RETURN:
00329 close();
00330 break;
00331
00332 default:
00333 printerr("SSL_write");
00334 seterr(WvString("SSL write error #%s", errcode));
00335 break;
00336 }
00337 break;
00338 }
00339 write_bouncebuf.zap();
00340
00341
00342
00343
00344
00345
00346 if (size_t(result) >= len)
00347 {
00348
00349
00350 write_eat = result - len;
00351 total += len;
00352 break;
00353 }
00354 total += size_t(result);
00355 len -= size_t(result);
00356 buf = (const unsigned char *)buf + size_t(result);
00357 }
00358
00359
00360 return total;
00361 }
00362
00363 void WvSSLStream::close()
00364 {
00365 if (ssl)
00366 {
00367 ERR_clear_error();
00368 SSL_shutdown(ssl);
00369 SSL_free(ssl);
00370 ssl = NULL;
00371 sslconnected = false;
00372 }
00373
00374 WvStreamClone::close();
00375
00376 if (ctx)
00377 {
00378 SSL_CTX_free(ctx);
00379 ctx = NULL;
00380 }
00381 }
00382
00383
00384 bool WvSSLStream::isok() const
00385 {
00386 return ssl && WvStreamClone::isok();
00387 }
00388
00389
00390 bool WvSSLStream::pre_select(SelectInfo &si)
00391 {
00392
00393
00394 if (si.wants.readable && (read_pending || read_bouncebuf.used()))
00395 {
00396
00397 return true;
00398 }
00399
00400 bool result = WvStreamClone::pre_select(si);
00401
00402 return result;
00403 }
00404
00405
00406 bool WvSSLStream::post_select(SelectInfo &si)
00407 {
00408 bool result = WvStreamClone::post_select(si);
00409
00410
00411
00412
00413
00414
00415
00416 if (!sslconnected && cloned && cloned->isok() && result)
00417 {
00418
00419
00420 undo_force_select(false, true, false);
00421
00422
00423
00424 WvFDStream *fdstream = static_cast<WvFDStream*>(cloned);
00425 int fd = fdstream->getfd();
00426 assert(fd >= 0);
00427 ERR_clear_error();
00428 SSL_set_fd(ssl, fd);
00429
00430
00431 int err;
00432
00433 if (is_server)
00434 {
00435
00436
00437 err = SSL_accept(ssl);
00438 }
00439 else
00440 err = SSL_connect(ssl);
00441
00442 if (err < 0)
00443 {
00444 if (errno == EAGAIN)
00445 debug("Still waiting for SSL negotiation.\n");
00446 else if (!errno)
00447 {
00448 printerr(is_server ? "SSL_accept" : "SSL_connect");
00449 seterr(WvString("SSL negotiation failed (%s)!", err));
00450 }
00451 else
00452 {
00453 printerr(is_server ? "SSL_accept" : "SSL_connect");
00454 seterr(errno);
00455 }
00456 }
00457 else
00458 {
00459 debug("SSL connection using cipher %s.\n", SSL_get_cipher(ssl));
00460 if (verify)
00461 {
00462 WvX509Mgr peercert(SSL_get_peer_certificate(ssl));
00463 if (peercert.isok() && peercert.validate())
00464 {
00465 setconnected(true);
00466 debug("SSL finished negotiating - certificate is valid.\n");
00467 }
00468 else
00469 {
00470 if (!peercert.isok())
00471 seterr(peercert.errstr());
00472 else
00473 seterr("Peer certificate is invalid!");
00474 }
00475 }
00476 else
00477 {
00478 setconnected(true);
00479 debug("SSL finished negotiating "
00480 "- certificate validation disabled.\n");
00481 }
00482 }
00483
00484 return false;
00485 }
00486 else
00487 return result;
00488 }
00489
00490
00491 void WvSSLStream::setconnected(bool conn)
00492 {
00493 sslconnected = conn;
00494 if (conn) write(unconnected_buf);
00495 }
00496