Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

wvsslstream.cc

Go to the documentation of this file.
00001 #include "wvsslstream.h"
00002 #include <ssl.h>
00003 #include <err.h>
00004 #include <assert.h>
00005 
00006 WvSSLStream::WvSSLStream(WvStream *_slave, WvX509Mgr *x509, bool _verify,
00007                          bool _is_server)
00008     : WvStreamClone(&slave), debug("WvSSLStream",WvLog::Debug5)
00009 {
00010     slave = _slave;
00011     verify = _verify;
00012     is_server = _is_server;
00013     read_again = false;
00014     writeonly = 1400;
00015     
00016     if (is_server && (x509 == NULL))
00017     {
00018         seterr("Certificate not available, so server mode not possible");
00019         return;
00020     }
00021 
00022     SSL_load_error_strings();
00023     SSL_library_init();
00024     debug("SSL Library Initialization Finished\n");
00025 
00026     ctx = NULL;
00027     ssl = NULL;
00028     sslconnected = false;
00029 
00030     if (is_server)
00031     {
00032         meth = SSLv23_server_method();
00033         debug("Setting Algorithms, and Methods Complete\n");
00034 
00035         ctx = SSL_CTX_new(meth);
00036         if (!ctx)
00037         {
00038             seterr("Can't get SSL context!");
00039             return;
00040         }
00041         // Allow SSL Writes to only write part of a request...
00042         SSL_CTX_set_mode(ctx,SSL_MODE_ENABLE_PARTIAL_WRITE);
00043 
00044         if (SSL_CTX_use_certificate(ctx, x509->cert) <= 0)
00045         {
00046             seterr("Error loading Certificate!");
00047             return;
00048         }
00049         debug("Setting Certificate Complete\n");
00050 
00051         if (SSL_CTX_use_RSAPrivateKey(ctx, x509->keypair->rsa) <= 0)
00052         {
00053             seterr("Error loading RSA Private Key!");
00054             return;
00055         }
00056         debug("Setting Private Key Complete\n");
00057         debug("Ready to go for Server mode\n");
00058     }
00059     else
00060     {
00061         meth = SSLv23_client_method();
00062         debug("Setting Algorithms, and Methods Complete\n");
00063     
00064         ctx = SSL_CTX_new(meth);
00065         if (!ctx)
00066         {
00067             seterr("Can't get SSL context!");
00068             return;
00069         }
00070 
00071         debug("SSL Context Set up\n");
00072     
00073     }
00074     ssl = SSL_new(ctx);
00075     if (!ssl)
00076     {
00077         seterr("Can't create SSL object!");
00078         return;
00079     }
00080 
00081     debug("SSL Connector initialized\n");
00082 
00083     // make sure we run the SSL_connect once, after our stream is writable
00084     slave->force_select(false, true);
00085 }
00086 
00087 
00088 WvSSLStream::~WvSSLStream()
00089 {
00090     debug("Shutting down SSL Connection\n");
00091     close();
00092     
00093     if (slave)
00094         delete slave;
00095 }
00096 
00097  
00098 size_t WvSSLStream::uread(void *buf, size_t len)
00099 {
00100     if (!sslconnected)
00101         return 0;
00102     
00103     int result = SSL_read(ssl, (char *)buf, len);
00104     
00105     if (len > 0 && result == 0)
00106         close();
00107     else if (result < 0)
00108     {
00109         if (errno != EAGAIN)
00110             seterr(errno);
00111         return 0;
00112     }
00113     
00114     if (len)
00115     {
00116         if ((size_t)result == len)
00117             read_again = true;
00118         else
00119             read_again = false;
00120     }
00121     
00122     debug("<< %s bytes\n", result);
00123     
00124     return result;
00125 }
00126 
00127 
00128 size_t WvSSLStream::uwrite(const void *buf, size_t len)
00129 {
00130 
00131     if (!sslconnected)
00132     {
00133         debug(">> Attempting to write to a non-ready SSL Connection/n");
00134         return 0;
00135     }
00136 
00137     debug(">> I want to write %s bytes\n",len);
00138 
00139     // copy buf into the bounce buffer...
00140 
00141     memcpy(bouncebuffer,buf,(writeonly < len) ? writeonly : len); 
00142 
00143     int result = SSL_write(ssl, bouncebuffer, (writeonly < len) ? writeonly : len);
00144 
00145     if (len > 0 && result == 0)
00146         close();
00147     else if (result < 0)
00148     {
00149         switch(SSL_get_error(ssl,result))
00150         {
00151            case SSL_ERROR_WANT_WRITE:
00152                 debug(">> ERROR: SSL_write() cannot complete at this time...retry!\n");
00153                 writeonly = len;
00154                 break;
00155            case SSL_ERROR_NONE:
00156                 // We shouldn't ever get here, but handle it nicely anyway... 
00157                 debug(">> Hmmm... something got confused... no SSL Errors!\n");
00158                 break;
00159            default:
00160                 debug(">> ERROR: SSL_write() call failed\n");
00161                 seterr("SSL Write failed - bailing out of the SSL Session");
00162                 break;
00163         }
00164         return 0;
00165     }
00166     else
00167     {
00168         writeonly = 1400;
00169     }
00170 
00171     debug(">> SSL wrote %s bytes \t WvStreams wanted to write %s bytes\n", 
00172            result, len);
00173 
00174     return result;
00175 }
00176  
00177 
00178 void WvSSLStream::close()
00179 {
00180     if (ssl)
00181     {
00182         SSL_shutdown(ssl);
00183         SSL_free(ssl);
00184         ssl = NULL;
00185     }
00186     
00187     WvStreamClone::close();
00188     
00189     if (ctx)
00190     {
00191         SSL_CTX_free(ctx);
00192         ctx = NULL;
00193     }
00194 }
00195 
00196 
00197 bool WvSSLStream::pre_select(SelectInfo &si)
00198 {
00199     // the SSL library might be keeping its own internal buffers - try
00200     // reading again if we were full the last time.
00201     if (si.wants.readable && read_again)
00202     {
00203         debug("Have to try reading again!\n");
00204         return true;
00205     }
00206 
00207     return WvStreamClone::pre_select(si);
00208 
00209 }
00210 
00211  
00212 bool WvSSLStream::post_select(SelectInfo &si)
00213 {
00214     bool result = WvStreamClone::post_select(si);
00215 
00216     // SSL takes a few round trips to
00217     // initialize itself, and we mustn't block in the constructor, so keep
00218     // trying here... it is also turning into a rather cool place
00219     // to do the validation of the connection ;)
00220     if (!sslconnected && slave && slave->isok() && result)
00221     {
00222         slave->undo_force_select(false, true, false);
00223         
00224         // for ssl streams to work, we have to be cloning a stream that
00225         // actually uses a single, valid fd.
00226         assert(getrfd() >= 0);
00227         assert(getwfd() >= 0);
00228         assert(getwfd() == getrfd());
00229         SSL_set_fd(ssl, getrfd());
00230         debug("SSL Connected to WvStream %s\n", getrfd());
00231         
00232         int err;
00233     
00234         if (is_server)
00235         {
00236             // If we are a server, get ready to accept an incoming SSL
00237             // connection
00238             err = SSL_accept(ssl);
00239         }
00240         else
00241             err = SSL_connect(ssl);
00242         
00243         if (err < 0)
00244         {
00245             if (errno == EAGAIN)
00246                 debug("Still Waiting for SSL Connection\n");
00247             else
00248                 seterr("SSL negotiation failed!");
00249         }
00250         else  // We're connected, so let's do some checks ;)
00251         {
00252             debug("SSL Connection using %s\n", SSL_get_cipher(ssl));
00253             if (verify)
00254             {
00255                 WvX509Mgr peercert(SSL_get_peer_certificate(ssl));
00256                 if (peercert.validate() && !peercert.err)
00257                 {
00258                     sslconnected = true;
00259                     debug("SSL Connection now running\n");
00260                 }
00261                 else
00262                 {
00263                     if (peercert.err)
00264                         seterr(peercert.errstr);
00265                     else
00266                         seterr("Peer Certificate cannot be validated");
00267                 }
00268             }
00269             else
00270             {
00271                 sslconnected = true;
00272                 debug("SSL Connection now running\n");
00273             }   
00274         } 
00275         
00276         return false;
00277     }
00278     else
00279         return result;
00280 }
00281 

Generated on Sat Aug 24 21:37:01 2002 for WvStreams by doxygen1.2.15