Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | 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 Wed Dec 15 15:08:11 2004 for WvStreams by  doxygen 1.3.9.1