Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

wvsslstream.cc

Go to the documentation of this file.
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2002 Net Integration Technologies, Inc. 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 // Allow SSL Writes to only write part of a request... 00078 SSL_CTX_set_mode(ctx,SSL_MODE_ENABLE_PARTIAL_WRITE); 00079 00080 // Tell SSL to use 128 bit ciphers - this appears to 00081 // be necessary for some reason... *sigh* 00082 SSL_CTX_set_cipher_list(ctx,"HIGH"); 00083 00084 // Enable the workarounds for broken clients and servers 00085 // and disable the insecure SSLv2 protocol 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 // make sure we run the SSL_connect once, after our stream is writable 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]; // man ERR_error_string says must be > 120. 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 // if SSL buffers stuff on its own, select() may not wake us up 00166 // the next time around unless we're sure there is nothing left 00167 read_pending = true; 00168 00169 size_t total = 0; 00170 for (;;) 00171 { 00172 // handle SSL_read quirk 00173 if (read_bouncebuf.used() != 0) 00174 { 00175 // copy out cached data 00176 size_t amount = len < read_bouncebuf.used() ? 00177 len : read_bouncebuf.used(); 00178 read_bouncebuf.move(buf, amount); 00179 00180 // locate next chunk in buffer 00181 len -= amount; 00182 total += amount; 00183 if (len == 0) 00184 break; 00185 buf = (unsigned char *)buf + amount; 00186 } 00187 00188 // attempt to read 00189 read_bouncebuf.zap(); // force use of same position in buffer 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 // debug("<< SSL_read() needs to wait for readable.\n"); 00205 break; // wait for later 00206 00207 case SSL_ERROR_NONE: 00208 break; // no error, but can't make progress 00209 00210 case SSL_ERROR_ZERO_RETURN: 00211 debug("<< EOF: zero return\n"); 00212 close(); // EOF 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; // wait for next iteration 00234 } 00235 // debug("<< read result was %s\n", result); 00236 00237 if (result < 0) 00238 result = 0; 00239 read_bouncebuf.unalloc(avail - result); 00240 } 00241 00242 // debug("<< read %s bytes\n", total); 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 // debug(">> I want to write %s bytes.\n", len); 00259 00260 size_t total = 0; 00261 00262 // eat any data that was precached and already written 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 // FIXME: WOW!!! Ummm... hope this never spins... 00278 // 00279 for (;;) 00280 { 00281 // handle SSL_write quirk 00282 if (write_bouncebuf.used() == 0) 00283 { 00284 if (len == 0) break; 00285 00286 // copy new data into the bounce buffer only if empty 00287 // if it were not empty, then SSL_write probably returned 00288 // SSL_ERROR_WANT_WRITE on the previous call and we 00289 // must invoke it with precisely the same arguments 00290 size_t amount = len < write_bouncebuf.free() ? 00291 len : write_bouncebuf.free(); 00292 write_bouncebuf.put(buf, amount); 00293 // note: we don't adjust the total yet... 00294 } // otherwise we use what we cached last time in bounce buffer 00295 00296 // attempt to write 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; // wait for later 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 // This case can cause truncated web pages... give more info 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; // no error, but can't make progress 00327 00328 case SSL_ERROR_ZERO_RETURN: 00329 close(); // EOF 00330 break; 00331 00332 default: 00333 printerr("SSL_write"); 00334 seterr(WvString("SSL write error #%s", errcode)); 00335 break; 00336 } 00337 break; // wait for next iteration 00338 } 00339 write_bouncebuf.zap(); // force use of same position in buffer 00340 00341 // locate next chunk to be written 00342 // note: we assume that initial contents of buf and of the 00343 // bouncebuf match since if we got SSL_ERROR_WANT_WRITE 00344 // we did not claim to actually have written the chunk 00345 // that we cached so we will have gotten it again here 00346 if (size_t(result) >= len) 00347 { 00348 // if we cached more previously than we were given, claim 00349 // we wrote what we got and remember to eat the rest later 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 // debug(">> wrote %s bytes\n", total); 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 // the SSL library might be keeping its own internal buffers 00393 // or we might have left buffered data behind deliberately 00394 if (si.wants.readable && (read_pending || read_bouncebuf.used())) 00395 { 00396 // debug("pre_select: try reading again immediately.\n"); 00397 return true; 00398 } 00399 00400 bool result = WvStreamClone::pre_select(si); 00401 // debug("in pre_select (%s)\n", result); 00402 return result; 00403 } 00404 00405 00406 bool WvSSLStream::post_select(SelectInfo &si) 00407 { 00408 bool result = WvStreamClone::post_select(si); 00409 00410 // debug("in post_select (%s)\n", result); 00411 00412 // SSL takes a few round trips to 00413 // initialize itself, and we mustn't block in the constructor, so keep 00414 // trying here... it is also turning into a rather cool place 00415 // to do the validation of the connection ;) 00416 if (!sslconnected && cloned && cloned->isok() && result) 00417 { 00418 // debug("!sslconnected in post_select\n"); 00419 00420 undo_force_select(false, true, false); 00421 00422 // for ssl streams to work, we have to be cloning a stream that 00423 // actually uses a single, valid fd. 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 // debug("SSL connected on fd %s.\n", fd); 00430 00431 int err; 00432 00433 if (is_server) 00434 { 00435 // If we are a server, get ready to accept an incoming SSL 00436 // connection 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 // We're connected, so let's do some checks ;) 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

Generated on Tue Oct 5 01:09:21 2004 for WvStreams by doxygen 1.3.7