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