wvsslstream.cc

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 #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 // FIXME: WLACH: seems to cause an error on mingw32 3.4.2.. is this needed?
00026 //typedef DWORD error_t;
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    // This is just returns true, since what we really want
00050    // is for the WvSSLValidateCallback to do this work
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(); // openssl may keep a pointer to this object
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         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         // Allow SSL Writes to only write part of a request...
00100         SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
00101 
00102         // Tell SSL to use 128 bit or better ciphers - this appears to
00103         // be necessary for some reason... *sigh*
00104         SSL_CTX_set_cipher_list(ctx, "HIGH");
00105 
00106         // Enable the workarounds for broken clients and servers
00107         // and disable the insecure SSLv2 protocol
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     //SSL_CTX_set_read_ahead(ctx, 1);
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     // make sure we run the SSL_connect once, after our stream is writable
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];      // man ERR_error_string says must be > 120.
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     // if SSL buffers stuff on its own, select() may not wake us up
00198     // the next time around unless we're sure there is nothing left
00199     read_pending = true;
00200     
00201     size_t total = 0;
00202     for (;;)
00203     {
00204         // handle SSL_read quirk
00205         if (read_bouncebuf.used() != 0)
00206         {
00207             // copy out cached data
00208             size_t amount = len < read_bouncebuf.used() ?
00209                 len : read_bouncebuf.used();
00210             read_bouncebuf.move(buf, amount);
00211 
00212             // locate next chunk in buffer
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             // FIXME: this shouldn't be necessary, but it resolves weird
00223             // problems when the other end disconnects in the middle of
00224             // SSL negotiation, but only on emakela's machine.  I don't
00225             // know why.  -- apenwarr (2004/02/10)
00226             break;
00227         }
00228 
00229         // attempt to read
00230         read_bouncebuf.zap(); // force use of same position in buffer
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         // debug("<< SSL_read result %s for %s bytes (wanted %s)\n",
00237         //      result, avail, len);
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; // wait for later
00248                 case SSL_ERROR_WANT_WRITE:
00249                     debug("<< SSL_read() needs to wait for readable.\n");
00250                     break; // wait for later
00251                     
00252                 case SSL_ERROR_NONE:
00253                     break; // no error, but can't make progress
00254                     
00255                 case SSL_ERROR_ZERO_RETURN:
00256                     debug("<< EOF: zero return\n");
00257                 
00258                     // don't do this if we're returning nonzero!
00259                     // (SSL has no way to do a one-way shutdown, so if SSL
00260                     // detects a read problem, it's also a write problem.)
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                             // don't do this if we're returning nonzero!
00275                             // (SSL has no way to do a one-way shutdown, so
00276                             // if SSL detects a read problem, it's also a
00277                             // write problem.)
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; // wait for next iteration
00297         }
00298         // debug("<< read result was %s\n", result);
00299         
00300         if (result < 0)
00301             result = 0;
00302         read_bouncebuf.unalloc(avail - result);
00303     }
00304 
00305     // debug("<< read %s bytes (%s, %s)\n",
00306     //    total, isok(), cloned && cloned->isok());
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 //    debug(">> I want to write %s bytes.\n", len);
00323 
00324     size_t total = 0;
00325     
00326     // eat any data that was precached and already written
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     // FIXME: WOW!!! Ummm... hope this never spins...
00342     // 
00343     for (;;) 
00344     {
00345         // handle SSL_write quirk
00346         if (write_bouncebuf.used() == 0)
00347         {
00348             if (len == 0) break;
00349 
00350             // copy new data into the bounce buffer only if empty
00351             // if it were not empty, then SSL_write probably returned
00352             // SSL_ERROR_WANT_WRITE on the previous call and we
00353             // must invoke it with precisely the same arguments
00354             size_t amount = len < write_bouncebuf.free() ?
00355                 len : write_bouncebuf.free();
00356             write_bouncebuf.put(buf, amount);
00357             // note: we don't adjust the total yet...
00358         } // otherwise we use what we cached last time in bounce buffer
00359         
00360         // attempt to write
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         // debug("<< SSL_write result %s for %s bytes\n",
00367         //      result, used);
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; // wait for later
00377                 case SSL_ERROR_WANT_WRITE:
00378                     // debug(">> SSL_write() needs to wait for writable.\n");
00379                     break; // wait for later
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                 // This case can cause truncated web pages... give more info
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; // no error, but can't make progress
00395                     
00396                 case SSL_ERROR_ZERO_RETURN:
00397                     debug(">> SSL_write zero return: EOF\n");
00398                     close(); // EOF
00399                     break;
00400                     
00401                 default:
00402                     printerr("SSL_write");
00403                     seterr(WvString("SSL write error #%s", sslerrcode));
00404                     break;
00405             }
00406             break; // wait for next iteration
00407         }
00408         else
00409             assert((size_t)result == used);
00410         write_bouncebuf.zap(); // force use of same position in buffer
00411         
00412         // locate next chunk to be written
00413         // note: we assume that initial contents of buf and of the
00414         //       bouncebuf match since if we got SSL_ERROR_WANT_WRITE
00415         //       we did not claim to actually have written the chunk
00416         //       that we cached so we will have gotten it again here
00417         if (size_t(result) >= len)
00418         {
00419             // if we cached more previously than we were given, claim
00420             // we wrote what we got and remember to eat the rest later
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     //debug(">> wrote %s bytes\n", total);
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     // WARNING: openssl always needs two-way socket communications even for
00467     // one-way encrypted communications, so we don't pass noread/nowrite
00468     // along to the child stream.  This should be mostly okay, though,
00469     // because we'll still send it close() once we have both noread() and
00470     // nowrite().
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     // WARNING: see note in noread()
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     // the SSL library might be keeping its own internal buffers
00496     // or we might have left buffered data behind deliberately
00497     if (si.wants.readable && (read_pending || read_bouncebuf.used()))
00498     {
00499 //      debug("pre_select: try reading again immediately.\n");
00500         return true;
00501     }
00502 
00503     // if we're not ssl_connected yet, I can guarantee we're not actually
00504     // writable, so don't ask WvStreamClone to wake up just because *he's*
00505     // writable.
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 //    debug("in pre_select (%s)\n", result);
00513     return result;
00514 }
00515 
00516  
00517 bool WvSSLStream::post_select(SelectInfo &si)
00518 {
00519     bool result = WvStreamClone::post_select(si);
00520 
00521     // SSL takes a few round trips to
00522     // initialize itself, and we mustn't block in the constructor, so keep
00523     // trying here... it is also turning into a rather cool place
00524     // to do the validation of the connection ;)
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         // for ssl streams to work, we have to be cloning a stream that
00535         // actually uses a single, valid fd.
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 //      debug("SSL connected on fd %s.\n", fd);
00542         
00543         int err;
00544     
00545         if (is_server)
00546         {
00547             // If we are a server, get ready to accept an incoming SSL
00548             // connection
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  // We're connected, so let's do some checks ;)
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     

Generated on Fri Oct 5 18:20:28 2007 for WvStreams by  doxygen 1.5.3