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

Generated on Sun Sep 24 20:10:51 2006 for WvStreams by  doxygen 1.4.7