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 "wvx509mgr.h"
00008 #include "wvcrypto.h"
00009 #include "wvmoniker.h"
00010 #include "wvlinkerhack.h"
00011 #include <openssl/ssl.h>
00012 #include <openssl/err.h>
00013 #include <assert.h>
00014 
00015 #ifndef _WIN32
00016 # if HAVE_ARGZ_H
00017 #  include <argz.h>
00018 # else
00019 #  if HAVE_ERRNO_H
00020 #   include <errno.h>
00021 #  endif
00022 # endif
00023 #else
00024 #undef errno
00025 #define errno GetLastError()
00026 #undef EAGAIN
00027 #define EAGAIN WSAEWOULDBLOCK
00028 #define error_t long
00029 #endif
00030 
00031 WV_LINK(WvSSLStream);
00032 
00033 static IWvStream *creator(WvStringParm s)
00034 {
00035     return new WvSSLStream(wvcreate<IWvStream>(s), NULL, 0, false);
00036 }
00037 
00038 static IWvStream *screator(WvStringParm s)
00039 {
00040     return new WvSSLStream(wvcreate<IWvStream>(s), NULL, 0, true);
00041 }
00042 
00043 static WvMoniker<IWvStream> reg("ssl", creator);
00044 static WvMoniker<IWvStream> sreg("sslserv", screator);
00045 
00046 
00047 #define MAX_BOUNCE_AMOUNT (16384) // 1 SSLv3/TLSv1 record
00048 
00049 static int ssl_stream_count = 0;
00050 
00051 static int wv_verify_cb(int preverify_ok, X509_STORE_CTX *ctx) 
00052 {
00053    // This is just returns true, since what we really want
00054    // is for the WvSSLValidateCallback to do this work
00055    return 1;
00056 }
00057 
00058 WvSSLStream::WvSSLStream(IWvStream *_slave, WvX509Mgr *_x509,
00059     WvSSLValidateCallback _vcb, bool _is_server) :
00060     WvStreamClone(_slave),
00061     debug(WvString("WvSSLStream %s", ++ssl_stream_count), WvLog::Debug5),
00062     write_bouncebuf(MAX_BOUNCE_AMOUNT), write_eat(0),
00063     read_bouncebuf(MAX_BOUNCE_AMOUNT), read_pending(false)
00064 {
00065     x509 = _x509;
00066     if (x509)
00067         x509->addRef(); // openssl may keep a pointer to this object
00068     
00069     vcb = _vcb;
00070     is_server = _is_server;
00071     ctx = NULL;
00072     ssl = NULL;
00073     //meth = NULL;
00074     sslconnected = ssl_stop_read = ssl_stop_write = false;
00075     
00076     wvssl_init();
00077     
00078     if (x509 && !x509->isok())
00079     {
00080         seterr("Certificate + key pair invalid.");
00081         return;
00082     }
00083 
00084     if (is_server && !x509)
00085     {
00086         seterr("Certificate not available: server mode not possible!");
00087         return;
00088     }
00089 
00090     if (is_server)
00091     {
00092         debug("Configured algorithms and methods for server mode.\n");
00093 
00094         ctx = SSL_CTX_new(SSLv23_server_method());
00095         if (!ctx)
00096         {
00097             ERR_print_errors_fp(stderr);
00098             debug("Can't get SSL context! Error: %s\n", 
00099                   ERR_reason_error_string(ERR_get_error()));
00100             seterr("Can't get SSL context!");
00101             return;
00102         }
00103         
00104         // Allow SSL Writes to only write part of a request...
00105         SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
00106 
00107         // Tell SSL to use 128 bit or better ciphers - this appears to
00108         // be necessary for some reason... *sigh*
00109         SSL_CTX_set_cipher_list(ctx, "HIGH");
00110 
00111         // Enable the workarounds for broken clients and servers
00112         // and disable the insecure SSLv2 protocol
00113         SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
00114 
00115         if (!x509->bind_ssl(ctx))
00116         {
00117             seterr("Unable to bind Certificate to SSL Context!");
00118             return;
00119         }
00120         
00121         if (!!vcb)
00122             SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, 
00123                                wv_verify_cb);
00124         
00125         debug("Server mode ready.\n");
00126     }
00127     else
00128     {
00129         debug("Configured algorithms and methods for client mode.\n");
00130     
00131         ctx = SSL_CTX_new(SSLv23_client_method());
00132         if (!ctx)
00133         {
00134             seterr("Can't get SSL context!");
00135             return;
00136         }
00137         if (x509 && !x509->bind_ssl(ctx))
00138         {
00139             seterr("Unable to bind Certificate to SSL Context!");
00140             return;
00141         }
00142     }
00143     
00144     //SSL_CTX_set_read_ahead(ctx, 1);
00145 
00146     ERR_clear_error();
00147     ssl = SSL_new(ctx);
00148     if (!ssl)
00149     {
00150         seterr("Can't create SSL object!");
00151         return;
00152     }
00153 
00154     if (!!vcb)
00155         SSL_set_verify(ssl, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, 
00156                        wv_verify_cb);
00157 
00158     connect_wants.readable = true;
00159     connect_wants.writable = true; // force ssl initiation ASAP
00160     connect_wants.isexception = false;
00161     debug("SSL stream initialized.\n");
00162 }
00163 
00164 
00165 WvSSLStream::~WvSSLStream()
00166 {
00167     close();
00168     
00169     debug("Deleting SSL connection.\n");
00170     if (geterr())
00171         debug("Error was: %s\n", errstr());
00172     
00173     WVRELEASE(x509);
00174     wvssl_free();
00175 }
00176 
00177 
00178 void WvSSLStream::printerr(WvStringParm func)
00179 {
00180     unsigned long l = ERR_get_error();
00181     char buf[121];      // man ERR_error_string says must be > 120.
00182 
00183     SSL_load_error_strings();
00184     while (l)
00185     {
00186         ERR_error_string(l, buf);
00187         debug("%s error: %s\n", func, buf);
00188         l = ERR_get_error();
00189     }
00190     ERR_free_strings();
00191 }
00192 
00193  
00194 size_t WvSSLStream::uread(void *buf, size_t len)
00195 {
00196     if (!sslconnected)
00197         return 0;
00198     if (len == 0) return 0;
00199 
00200     // if SSL buffers stuff on its own, select() may not wake us up
00201     // the next time around unless we're sure there is nothing left
00202     read_pending = true;
00203     
00204     size_t total = 0;
00205     for (;;)
00206     {
00207         // handle SSL_read quirk
00208         if (read_bouncebuf.used() != 0)
00209         {
00210             // copy out cached data
00211             size_t amount = len < read_bouncebuf.used() ?
00212                 len : read_bouncebuf.used();
00213             read_bouncebuf.move(buf, amount);
00214 
00215             // locate next chunk in buffer
00216             len -= amount;
00217             total += amount;
00218             if (len == 0)
00219             {
00220                 read_pending = false;
00221                 break;
00222             }
00223             buf = (unsigned char *)buf + amount;
00224             
00225             // FIXME: this shouldn't be necessary, but it resolves weird
00226             // problems when the other end disconnects in the middle of
00227             // SSL negotiation, but only on emakela's machine.  I don't
00228             // know why.  -- apenwarr (2004/02/10)
00229             break;
00230         }
00231 
00232         // attempt to read
00233         read_bouncebuf.zap(); // force use of same position in buffer
00234         size_t avail = read_bouncebuf.free();
00235         unsigned char *data = read_bouncebuf.alloc(avail);
00236         
00237         ERR_clear_error();
00238         int result = SSL_read(ssl, data, avail);
00239         // debug("<< SSL_read result %s for %s bytes (wanted %s)\n",
00240         //      result, avail, len);
00241         if (result <= 0)
00242         {
00243             error_t err = errno;
00244             read_bouncebuf.unalloc(avail);
00245             int sslerrcode = SSL_get_error(ssl, result);
00246             switch (sslerrcode)
00247             {
00248                 case SSL_ERROR_WANT_READ:
00249                     debug("<< SSL_read() needs to wait for writable.\n");
00250                     break; // wait for later
00251                 case SSL_ERROR_WANT_WRITE:
00252                     debug("<< SSL_read() needs to wait for readable.\n");
00253                     break; // wait for later
00254                     
00255                 case SSL_ERROR_NONE:
00256                     break; // no error, but can't make progress
00257                     
00258                 case SSL_ERROR_ZERO_RETURN:
00259                     debug("<< EOF: zero return\n");
00260                 
00261                     // don't do this if we're returning nonzero!
00262                     // (SSL has no way to do a one-way shutdown, so if SSL
00263                     // detects a read problem, it's also a write problem.)
00264                     if (!total) { noread(); nowrite(); }
00265                     break;
00266 
00267                 case SSL_ERROR_SYSCALL:
00268                     if (!err)
00269                     {
00270                         if (result == 0)
00271                         {
00272                             debug("<< EOF: syscall error "
00273                                   "(%s/%s, %s/%s) total=%s\n",
00274                                   stop_read, stop_write,
00275                                   isok(), cloned && cloned->isok(), total);
00276                             
00277                             // don't do this if we're returning nonzero!
00278                             // (SSL has no way to do a one-way shutdown, so
00279                             // if SSL detects a read problem, it's also a
00280                             // write problem.)
00281                             if (!total) { noread(); nowrite(); }
00282                         }
00283                     }
00284                     else
00285                     {
00286                         debug("<< SSL_read() err=%s (%s)\n",
00287                             err, strerror(err));
00288                         seterr_both(err, WvString("SSL read: %s",
00289                             strerror(err)));
00290                     }
00291                     break;
00292                     
00293                 default:
00294                     printerr("SSL_read");
00295                     seterr("SSL read error #%s", sslerrcode);
00296                     break;
00297             }
00298             read_pending = false;
00299             break; // wait for next iteration
00300         }
00301         // debug("<< read result was %s\n", result);
00302         
00303         if (result < 0)
00304             result = 0;
00305         read_bouncebuf.unalloc(avail - result);
00306     }
00307 
00308     // debug("<< read %s bytes (%s, %s)\n",
00309     //    total, isok(), cloned && cloned->isok());
00310     return total;
00311 }
00312 
00313 
00314 size_t WvSSLStream::uwrite(const void *buf, size_t len)
00315 {
00316     if (!sslconnected)
00317     {
00318         debug(">> writing, but not connected yet (%s); enqueue.\n", getwfd());
00319         unconnected_buf.put(buf, len);
00320         return len;
00321     }
00322 
00323     if (len == 0) return 0;
00324 
00325 //    debug(">> I want to write %s bytes.\n", len);
00326 
00327     size_t total = 0;
00328     
00329     // eat any data that was precached and already written
00330     if (write_eat >= len)
00331     {
00332         write_eat -= len;
00333         total = len;
00334         len = 0;
00335     }
00336     else
00337     {
00338         buf = (const unsigned char *)buf + write_eat;
00339         total = write_eat;
00340         len -= write_eat;
00341         write_eat = 0;
00342     }
00343 
00344     // FIXME: WOW!!! Ummm... hope this never spins...
00345     // 
00346     for (;;) 
00347     {
00348         // handle SSL_write quirk
00349         if (write_bouncebuf.used() == 0)
00350         {
00351             if (len == 0) break;
00352 
00353             // copy new data into the bounce buffer only if empty
00354             // if it were not empty, then SSL_write probably returned
00355             // SSL_ERROR_WANT_WRITE on the previous call and we
00356             // must invoke it with precisely the same arguments
00357             size_t amount = len < write_bouncebuf.free() ?
00358                 len : write_bouncebuf.free();
00359             write_bouncebuf.put(buf, amount);
00360             // note: we don't adjust the total yet...
00361         } // otherwise we use what we cached last time in bounce buffer
00362         
00363         // attempt to write
00364         size_t used = write_bouncebuf.used();
00365         const unsigned char *data = write_bouncebuf.get(used);
00366 
00367         ERR_clear_error();
00368         int result = SSL_write(ssl, data, used);
00369         // debug("<< SSL_write result %s for %s bytes\n",
00370         //      result, used);
00371         if (result <= 0)
00372         {
00373             int sslerrcode = SSL_get_error(ssl, result);
00374             write_bouncebuf.unget(used);
00375             switch (sslerrcode)
00376             {
00377                 case SSL_ERROR_WANT_READ:
00378                     debug(">> SSL_write() needs to wait for readable.\n");
00379                     break; // wait for later
00380                 case SSL_ERROR_WANT_WRITE:
00381                     // debug(">> SSL_write() needs to wait for writable.\n");
00382                     break; // wait for later
00383                     
00384                 case SSL_ERROR_SYSCALL:
00385                     debug(">> ERROR: SSL_write() failed on socket error.\n");
00386                     seterr(WvString("SSL write error: %s", strerror(errno)));
00387                     break;
00388             
00389                 // This case can cause truncated web pages... give more info
00390                 case SSL_ERROR_SSL:
00391                     debug(">> ERROR: SSL_write() failed on internal error.\n");
00392                     seterr(WvString("SSL write error: %s", 
00393                                     ERR_error_string(ERR_get_error(), NULL)));
00394                     break;
00395                 
00396                 case SSL_ERROR_NONE:
00397                     break; // no error, but can't make progress
00398                     
00399                 case SSL_ERROR_ZERO_RETURN:
00400                     debug(">> SSL_write zero return: EOF\n");
00401                     close(); // EOF
00402                     break;
00403                     
00404                 default:
00405                     printerr("SSL_write");
00406                     seterr(WvString("SSL write error #%s", sslerrcode));
00407                     break;
00408             }
00409             break; // wait for next iteration
00410         }
00411         else
00412             assert((size_t)result == used);
00413         write_bouncebuf.zap(); // force use of same position in buffer
00414         
00415         // locate next chunk to be written
00416         // note: we assume that initial contents of buf and of the
00417         //       bouncebuf match since if we got SSL_ERROR_WANT_WRITE
00418         //       we did not claim to actually have written the chunk
00419         //       that we cached so we will have gotten it again here
00420         if (size_t(result) >= len)
00421         {
00422             // if we cached more previously than we were given, claim
00423             // we wrote what we got and remember to eat the rest later
00424             write_eat = result - len;
00425             total += len;
00426             break;
00427         }
00428         total += size_t(result);
00429         len -= size_t(result);
00430         buf = (const unsigned char *)buf + size_t(result);
00431     }
00432     
00433     //debug(">> wrote %s bytes\n", total);
00434     return total;
00435 }
00436 
00437 void WvSSLStream::close()
00438 {
00439     debug("Closing SSL connection (ok=%s,sr=%s,sw=%s,child=%s).\n",
00440           isok(), stop_read, stop_write, cloned && cloned->isok());
00441     
00442     if (ssl)
00443     {
00444         ERR_clear_error();
00445         SSL_shutdown(ssl);
00446         SSL_free(ssl);
00447         ssl = NULL;
00448         sslconnected = false;
00449     }
00450     
00451     WvStreamClone::close();
00452     
00453     if (ctx)
00454     {
00455         SSL_CTX_free(ctx);
00456         ctx = NULL;
00457     }
00458 }
00459 
00460 
00461 bool WvSSLStream::isok() const
00462 {
00463     return ssl && WvStreamClone::isok();
00464 }
00465 
00466 
00467 void WvSSLStream::noread()
00468 {
00469     // WARNING: openssl always needs two-way socket communications even for
00470     // one-way encrypted communications, so we don't pass noread/nowrite
00471     // along to the child stream.  This should be mostly okay, though,
00472     // because we'll still send it close() once we have both noread() and
00473     // nowrite().
00474     ssl_stop_read = true;
00475     if (ssl_stop_write)
00476     {
00477         WvStreamClone::nowrite();
00478         WvStreamClone::noread();
00479     }
00480 }
00481 
00482 
00483 void WvSSLStream::nowrite()
00484 {
00485     // WARNING: see note in noread()
00486     ssl_stop_write = true;
00487     if (ssl_stop_read)
00488     {
00489         WvStreamClone::noread();
00490         WvStreamClone::nowrite();
00491     }
00492 }
00493 
00494 
00495 void WvSSLStream::pre_select(SelectInfo &si)
00496 {
00497     SelectRequest oldwant = si.wants;
00498     bool oldinherit = si.inherit_request;
00499     if (!sslconnected)
00500     {
00501         si.wants = connect_wants;
00502         si.inherit_request = true; // ignore force_select() until connected
00503     }
00504     
00505     // the SSL library might be keeping its own internal buffers
00506     // or we might have left buffered data behind deliberately
00507     if (si.wants.readable && (read_pending || read_bouncebuf.used()))
00508     {
00509         // debug("pre_select: try reading again immediately.\n");
00510         si.msec_timeout = 0;
00511         si.inherit_request = oldinherit;
00512         si.wants = oldwant;
00513         return;
00514     }
00515 
00516     WvStreamClone::pre_select(si);
00517     si.inherit_request = oldinherit;
00518     si.wants = oldwant;
00519 }
00520 
00521  
00522 bool WvSSLStream::post_select(SelectInfo &si)
00523 {
00524     SelectRequest oldwant = si.wants;
00525     bool oldinherit = si.inherit_request;
00526     
00527     if (!sslconnected)
00528     {
00529         si.wants = connect_wants;
00530         si.inherit_request = true; // ignore force_select() until connected
00531     }
00532     
00533     bool result = WvStreamClone::post_select(si);
00534     si.wants = oldwant;
00535     si.inherit_request = oldinherit;
00536 
00537     // SSL takes a few round trips to
00538     // initialize itself, and we mustn't block in the constructor, so keep
00539     // trying here... it is also turning into a rather cool place
00540     // to do the validation of the connection ;)
00541     if (!sslconnected && cloned && cloned->isok() && result)
00542     {
00543         debug("!sslconnected in post_select (r=%s/%s, w=%s/%s, t=%s)\n",
00544             cloned->isreadable(), si.wants.readable,
00545             cloned->iswritable(), si.wants.writable,
00546             si.msec_timeout);
00547         
00548         connect_wants.writable = false;
00549         
00550         // for ssl streams to work, we have to be cloning a stream that
00551         // actually uses a single, valid fd.
00552         WvFDStream *fdstream = static_cast<WvFDStream*>(cloned);
00553         int fd = fdstream->getfd();
00554         assert(fd >= 0);
00555         ERR_clear_error();
00556         SSL_set_fd(ssl, fd);
00557 //      debug("SSL connected on fd %s.\n", fd);
00558         
00559         int err;
00560     
00561         if (is_server)
00562         {
00563             // If we are a server, get ready to accept an incoming SSL
00564             // connection
00565             err = SSL_accept(ssl);
00566         }
00567         else
00568             err = SSL_connect(ssl);
00569         
00570         if (err < 0)
00571         {
00572             if (errno == EAGAIN)
00573                 debug("Still waiting for SSL negotiation.\n");
00574             else if (!errno)
00575             {
00576                 printerr(is_server ? "SSL_accept" : "SSL_connect");
00577                 seterr(WvString("SSL negotiation failed (%s)!", err));
00578             }
00579             else
00580             {
00581                 printerr(is_server ? "SSL_accept" : "SSL_connect");
00582                 seterr(errno);
00583             }
00584         }
00585         else  // We're connected, so let's do some checks ;)
00586         {
00587             debug("SSL connection using cipher %s.\n", SSL_get_cipher(ssl));
00588             if (!!vcb)
00589             {
00590                 WvX509 *peercert = new WvX509(SSL_get_peer_certificate(ssl));
00591                 debug("SSL Peer is: %s\n", peercert->get_subject());
00592                 if (peercert->isok() && peercert->validate() && vcb(peercert))
00593                 {
00594                     setconnected(true);
00595                     debug("SSL finished negotiating - certificate is valid.\n");
00596                 }
00597                 else
00598                 {
00599                     if (!peercert->isok())
00600                         seterr("Peer cert: %s", peercert->errstr());
00601                     else
00602                         seterr("Peer certificate is invalid!");
00603                 }
00604                 WVRELEASE(peercert);
00605             }
00606             else
00607             {
00608                 setconnected(true);
00609                 debug("SSL finished negotiating "
00610                       "- certificate validation disabled.\n");
00611             }   
00612         } 
00613         
00614         return false;
00615     }
00616 
00617     if ((si.wants.readable || readcb)
00618         && (read_pending || read_bouncebuf.used()))
00619         result = true;
00620 
00621     return result;
00622 }
00623 
00624 
00625 void WvSSLStream::setconnected(bool conn)
00626 {
00627     sslconnected = conn;
00628     if (conn) write(unconnected_buf);
00629 }
00630     

Generated on Thu Jan 24 16:50:56 2008 for WvStreams by  doxygen 1.5.4