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 #ifndef _WIN32
00015 # if HAVE_ARGZ_H
00016 # include <argz.h>
00017 # else
00018 # if HAVE_ERRNO_H
00019 # include <errno.h>
00020 # endif
00021 # endif
00022 #else
00023 #undef errno
00024 #define errno GetLastError()
00025
00026
00027 #undef EAGAIN
00028 #define EAGAIN WSAEWOULDBLOCK
00029 #endif
00030
00031 static IWvStream *creator(WvStringParm s)
00032 {
00033 return new WvSSLStream(wvcreate<IWvStream>(s), NULL, 0, false);
00034 }
00035
00036 static IWvStream *screator(WvStringParm s)
00037 {
00038 return new WvSSLStream(wvcreate<IWvStream>(s), NULL, 0, true);
00039 }
00040
00041 static WvMoniker<IWvStream> reg("ssl", creator);
00042 static WvMoniker<IWvStream> sreg("sslserv", screator);
00043
00044
00045 #define MAX_BOUNCE_AMOUNT (16384) // 1 SSLv3/TLSv1 record
00046
00047 static int wv_verify_cb(int preverify_ok, X509_STORE_CTX *ctx)
00048 {
00049
00050
00051 return 1;
00052 }
00053
00054 WvSSLStream::WvSSLStream(IWvStream *_slave, WvX509Mgr *_x509,
00055 WvSSLValidateCallback _vcb, bool _is_server) :
00056 WvStreamClone(_slave), debug("WvSSLStream", WvLog::Debug5),
00057 write_bouncebuf(MAX_BOUNCE_AMOUNT), write_eat(0),
00058 read_bouncebuf(MAX_BOUNCE_AMOUNT), read_pending(false)
00059 {
00060 x509 = _x509;
00061 if (x509)
00062 x509->addRef();
00063
00064 vcb = _vcb;
00065 is_server = _is_server;
00066 ctx = NULL;
00067 ssl = NULL;
00068 meth = NULL;
00069 sslconnected = ssl_stop_read = ssl_stop_write = false;
00070
00071 wvssl_init();
00072
00073 if (x509 && !x509->isok())
00074 {
00075 seterr("Cert: %s", x509->errstr());
00076 return;
00077 }
00078
00079 if (is_server && !x509)
00080 {
00081 seterr("Certificate not available: server mode not possible!");
00082 return;
00083 }
00084
00085 if (is_server)
00086 {
00087 meth = SSLv23_server_method();
00088 debug("Configured algorithms and methods for server mode.\n");
00089
00090 ctx = SSL_CTX_new(meth);
00091 if (!ctx)
00092 {
00093 seterr("Can't get SSL context!");
00094 return;
00095 }
00096
00097
00098 SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
00099
00100
00101
00102 SSL_CTX_set_cipher_list(ctx, "HIGH");
00103
00104
00105
00106 SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
00107
00108 if (!x509->bind_ssl(ctx))
00109 {
00110 seterr("Unable to bind Certificate to SSL Context!");
00111 return;
00112 }
00113
00114 if (!!vcb)
00115 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
00116 wv_verify_cb);
00117
00118 debug("Server mode ready.\n");
00119 }
00120 else
00121 {
00122 meth = SSLv23_client_method();
00123 debug("Configured algorithms and methods for client mode.\n");
00124
00125 ctx = SSL_CTX_new(meth);
00126 if (!ctx)
00127 {
00128 seterr("Can't get SSL context!");
00129 return;
00130 }
00131 if (x509 && !x509->bind_ssl(ctx))
00132 {
00133 seterr("Unable to bind Certificate to SSL Context!");
00134 return;
00135 }
00136 }
00137
00138
00139
00140 ERR_clear_error();
00141 ssl = SSL_new(ctx);
00142 if (!ssl)
00143 {
00144 seterr("Can't create SSL object!");
00145 return;
00146 }
00147
00148 if (!!vcb)
00149 SSL_set_verify(ssl, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
00150 wv_verify_cb);
00151
00152
00153
00154 debug("SSL stream initialized.\n");
00155
00156
00157 force_select(false, true);
00158 }
00159
00160
00161 WvSSLStream::~WvSSLStream()
00162 {
00163 close();
00164
00165 debug("Deleting SSL connection.\n");
00166 if (geterr())
00167 debug("Error was: %s\n", errstr());
00168
00169 WVRELEASE(x509);
00170 wvssl_free();
00171 }
00172
00173
00174 void WvSSLStream::printerr(WvStringParm func)
00175 {
00176 unsigned long l = ERR_get_error();
00177 char buf[121];
00178
00179 SSL_load_error_strings();
00180 while (l)
00181 {
00182 ERR_error_string(l, buf);
00183 debug("%s error: %s\n", func, buf);
00184 l = ERR_get_error();
00185 }
00186 ERR_free_strings();
00187 }
00188
00189
00190 size_t WvSSLStream::uread(void *buf, size_t len)
00191 {
00192 if (!sslconnected)
00193 return 0;
00194 if (len == 0) return 0;
00195
00196
00197
00198 read_pending = true;
00199
00200 size_t total = 0;
00201 for (;;)
00202 {
00203
00204 if (read_bouncebuf.used() != 0)
00205 {
00206
00207 size_t amount = len < read_bouncebuf.used() ?
00208 len : read_bouncebuf.used();
00209 read_bouncebuf.move(buf, amount);
00210
00211
00212 len -= amount;
00213 total += amount;
00214 if (len == 0)
00215 {
00216 read_pending = false;
00217 break;
00218 }
00219 buf = (unsigned char *)buf + amount;
00220
00221
00222
00223
00224
00225 break;
00226 }
00227
00228
00229 read_bouncebuf.zap();
00230 size_t avail = read_bouncebuf.free();
00231 unsigned char *data = read_bouncebuf.alloc(avail);
00232
00233 ERR_clear_error();
00234 int result = SSL_read(ssl, data, avail);
00235
00236
00237 if (result <= 0)
00238 {
00239 error_t err = errno;
00240 read_bouncebuf.unalloc(avail);
00241 int sslerrcode = SSL_get_error(ssl, result);
00242 switch (sslerrcode)
00243 {
00244 case SSL_ERROR_WANT_READ:
00245 debug("<< SSL_read() needs to wait for writable.\n");
00246 break;
00247 case SSL_ERROR_WANT_WRITE:
00248 debug("<< SSL_read() needs to wait for readable.\n");
00249 break;
00250
00251 case SSL_ERROR_NONE:
00252 break;
00253
00254 case SSL_ERROR_ZERO_RETURN:
00255 debug("<< EOF: zero return\n");
00256
00257
00258
00259
00260 if (!total) { noread(); nowrite(); }
00261 break;
00262
00263 case SSL_ERROR_SYSCALL:
00264 if (!err)
00265 {
00266 if (result == 0)
00267 {
00268 debug("<< EOF: syscall error "
00269 "(%s/%s, %s/%s) total=%s\n",
00270 stop_read, stop_write,
00271 isok(), cloned && cloned->isok(), total);
00272
00273
00274
00275
00276
00277 if (!total) { noread(); nowrite(); }
00278 }
00279 }
00280 else
00281 {
00282 debug("<< SSL_read() err=%s (%s)\n",
00283 err, strerror(err));
00284 seterr_both(err, WvString("SSL read: %s",
00285 strerror(err)));
00286 }
00287 break;
00288
00289 default:
00290 printerr("SSL_read");
00291 seterr("SSL read error #%s", sslerrcode);
00292 break;
00293 }
00294 read_pending = false;
00295 break;
00296 }
00297
00298
00299 if (result < 0)
00300 result = 0;
00301 read_bouncebuf.unalloc(avail - result);
00302 }
00303
00304
00305
00306 return total;
00307 }
00308
00309
00310 size_t WvSSLStream::uwrite(const void *buf, size_t len)
00311 {
00312 if (!sslconnected)
00313 {
00314 debug(">> writing, but not connected yet (%s); enqueue.\n", getwfd());
00315 unconnected_buf.put(buf, len);
00316 return len;
00317 }
00318
00319 if (len == 0) return 0;
00320
00321
00322
00323 size_t total = 0;
00324
00325
00326 if (write_eat >= len)
00327 {
00328 write_eat -= len;
00329 total = len;
00330 len = 0;
00331 }
00332 else
00333 {
00334 buf = (const unsigned char *)buf + write_eat;
00335 total = write_eat;
00336 len -= write_eat;
00337 write_eat = 0;
00338 }
00339
00340
00341
00342 for (;;)
00343 {
00344
00345 if (write_bouncebuf.used() == 0)
00346 {
00347 if (len == 0) break;
00348
00349
00350
00351
00352
00353 size_t amount = len < write_bouncebuf.free() ?
00354 len : write_bouncebuf.free();
00355 write_bouncebuf.put(buf, amount);
00356
00357 }
00358
00359
00360 size_t used = write_bouncebuf.used();
00361 const unsigned char *data = write_bouncebuf.get(used);
00362
00363 ERR_clear_error();
00364 int result = SSL_write(ssl, data, used);
00365
00366
00367 if (result <= 0)
00368 {
00369 int sslerrcode = SSL_get_error(ssl, result);
00370 write_bouncebuf.unget(used);
00371 switch (sslerrcode)
00372 {
00373 case SSL_ERROR_WANT_READ:
00374 debug(">> SSL_write() needs to wait for readable.\n");
00375 break;
00376 case SSL_ERROR_WANT_WRITE:
00377
00378 break;
00379
00380 case SSL_ERROR_SYSCALL:
00381 debug(">> ERROR: SSL_write() failed on socket error.\n");
00382 seterr(WvString("SSL write error: %s", strerror(errno)));
00383 break;
00384
00385
00386 case SSL_ERROR_SSL:
00387 debug(">> ERROR: SSL_write() failed on internal error.\n");
00388 seterr(WvString("SSL write error: %s",
00389 ERR_error_string(ERR_get_error(), NULL)));
00390 break;
00391
00392 case SSL_ERROR_NONE:
00393 break;
00394
00395 case SSL_ERROR_ZERO_RETURN:
00396 debug(">> SSL_write zero return: EOF\n");
00397 close();
00398 break;
00399
00400 default:
00401 printerr("SSL_write");
00402 seterr(WvString("SSL write error #%s", sslerrcode));
00403 break;
00404 }
00405 break;
00406 }
00407 else
00408 assert((size_t)result == used);
00409 write_bouncebuf.zap();
00410
00411
00412
00413
00414
00415
00416 if (size_t(result) >= len)
00417 {
00418
00419
00420 write_eat = result - len;
00421 total += len;
00422 break;
00423 }
00424 total += size_t(result);
00425 len -= size_t(result);
00426 buf = (const unsigned char *)buf + size_t(result);
00427 }
00428
00429
00430 return total;
00431 }
00432
00433 void WvSSLStream::close()
00434 {
00435 debug("Closing SSL connection (ok=%s,sr=%s,sw=%s,child=%s).\n",
00436 isok(), stop_read, stop_write, cloned && cloned->isok());
00437
00438 if (ssl)
00439 {
00440 ERR_clear_error();
00441 SSL_shutdown(ssl);
00442 SSL_free(ssl);
00443 ssl = NULL;
00444 sslconnected = false;
00445 }
00446
00447 WvStreamClone::close();
00448
00449 if (ctx)
00450 {
00451 SSL_CTX_free(ctx);
00452 ctx = NULL;
00453 }
00454 }
00455
00456
00457 bool WvSSLStream::isok() const
00458 {
00459 return ssl && WvStreamClone::isok();
00460 }
00461
00462
00463 void WvSSLStream::noread()
00464 {
00465
00466
00467
00468
00469
00470 ssl_stop_read = true;
00471 if (ssl_stop_write)
00472 {
00473 WvStreamClone::nowrite();
00474 WvStreamClone::noread();
00475 }
00476 }
00477
00478
00479 void WvSSLStream::nowrite()
00480 {
00481
00482 ssl_stop_write = true;
00483 if (ssl_stop_read)
00484 {
00485 WvStreamClone::noread();
00486 WvStreamClone::nowrite();
00487 }
00488 }
00489
00490
00491 bool WvSSLStream::pre_select(SelectInfo &si)
00492 {
00493 bool result = WvStreamClone::pre_select(si);
00494
00495
00496 if (si.wants.readable && (read_pending || read_bouncebuf.used()))
00497 {
00498
00499 return true;
00500 }
00501
00502
00503
00504
00505 bool oldwr = si.wants.writable;
00506 if (!sslconnected)
00507 si.wants.writable = !!writecb;
00508 result = WvStreamClone::pre_select(si);
00509 si.wants.writable = oldwr;
00510
00511
00512 return result;
00513 }
00514
00515
00516 bool WvSSLStream::post_select(SelectInfo &si)
00517 {
00518 bool result = WvStreamClone::post_select(si);
00519
00520
00521
00522
00523
00524 if (!sslconnected && cloned && cloned->isok() && result)
00525 {
00526 debug("!sslconnected in post_select (r=%s/%s, w=%s/%s, t=%s)\n",
00527 cloned->isreadable(), si.wants.readable,
00528 cloned->iswritable(), si.wants.writable,
00529 si.msec_timeout);
00530
00531 undo_force_select(false, true, false);
00532
00533
00534
00535 WvFDStream *fdstream = static_cast<WvFDStream*>(cloned);
00536 int fd = fdstream->getfd();
00537 assert(fd >= 0);
00538 ERR_clear_error();
00539 SSL_set_fd(ssl, fd);
00540
00541
00542 int err;
00543
00544 if (is_server)
00545 {
00546
00547
00548 err = SSL_accept(ssl);
00549 }
00550 else
00551 err = SSL_connect(ssl);
00552
00553 if (err < 0)
00554 {
00555 if (errno == EAGAIN)
00556 debug("Still waiting for SSL negotiation.\n");
00557 else if (!errno)
00558 {
00559 printerr(is_server ? "SSL_accept" : "SSL_connect");
00560 seterr(WvString("SSL negotiation failed (%s)!", err));
00561 }
00562 else
00563 {
00564 printerr(is_server ? "SSL_accept" : "SSL_connect");
00565 seterr(errno);
00566 }
00567 }
00568 else
00569 {
00570 debug("SSL connection using cipher %s.\n", SSL_get_cipher(ssl));
00571 if (!!vcb)
00572 {
00573 WvX509Mgr *peercert = new WvX509Mgr(SSL_get_peer_certificate(ssl));
00574 debug("SSL Peer is: %s\n", peercert->get_subject());
00575 if (peercert->isok() && peercert->validate() && vcb(peercert))
00576 {
00577 setconnected(true);
00578 debug("SSL finished negotiating - certificate is valid.\n");
00579 }
00580 else
00581 {
00582 if (!peercert->isok())
00583 seterr("Peer cert: %s", peercert->errstr());
00584 else
00585 seterr("Peer certificate is invalid!");
00586 }
00587 WVRELEASE(peercert);
00588 }
00589 else
00590 {
00591 setconnected(true);
00592 debug("SSL finished negotiating "
00593 "- certificate validation disabled.\n");
00594 }
00595 }
00596
00597 return false;
00598 }
00599 else
00600 return result;
00601 }
00602
00603
00604 void WvSSLStream::setconnected(bool conn)
00605 {
00606 sslconnected = conn;
00607 if (conn) write(unconnected_buf);
00608 }
00609