/*
* $ Id $
*
* Copyright ( C ) 2000 Alex Zepeda < zipzippy @ sonic . net
* Copyright ( C ) 2001 - 2003 George Staikos < staikos @ kde . org >
* Copyright ( C ) 2001 Dawit Alemayehu < adawit @ kde . org >
*
* This file is part of the KDE project
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public License
* along with this library ; see the file COPYING . LIB . If not , write to
* the Free Software Foundation , Inc . , 51 Franklin Street , Fifth Floor ,
* Boston , MA 02110 - 1301 , USA .
*/
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <sys/types.h>
# include <sys/uio.h>
# include <sys/time.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <time.h>
# include <netdb.h>
# include <unistd.h>
# include <errno.h>
# include <ksocks.h>
# include <kdebug.h>
# include <ksslall.h>
# include <ksslcertdlg.h>
# include <tdemessagebox.h>
# ifndef Q_WS_WIN //temporary
# include <kresolver.h>
# endif
# include <tdelocale.h>
# include <dcopclient.h>
# include <tqcstring.h>
# include <tqdatastream.h>
# include <tdeapplication.h>
# include <tdeprotocolmanager.h>
# include <kde_file.h>
# include "tdeio/tcpslavebase.h"
using namespace TDEIO ;
class TCPSlaveBase : : TcpSlaveBasePrivate
{
public :
TcpSlaveBasePrivate ( ) : rblockSz ( 256 ) , militantSSL ( false ) , userAborted ( false ) { }
~ TcpSlaveBasePrivate ( ) { }
KSSL * kssl ;
bool usingTLS ;
KSSLCertificateCache * cc ;
TQString host ;
TQString realHost ;
TQString ip ;
DCOPClient * dcc ;
KSSLPKCS12 * pkcs ;
int status ;
int timeout ;
int rblockSz ; // Size for reading blocks in readLine()
bool block ;
bool useSSLTunneling ;
bool needSSLHandShake ;
bool militantSSL ; // If true, we just drop a connection silently
// if SSL certificate check fails in any way.
bool userAborted ;
MetaData savedMetaData ;
} ;
TCPSlaveBase : : TCPSlaveBase ( unsigned short int defaultPort ,
const TQCString & protocol ,
const TQCString & poolSocket ,
const TQCString & appSocket )
: SlaveBase ( protocol , poolSocket , appSocket ) ,
m_iSock ( - 1 ) ,
m_iDefaultPort ( defaultPort ) ,
m_sServiceName ( protocol ) ,
fp ( 0 )
{
// We have to have two constructors, so don't add anything
// else in here. Put it in doConstructorStuff() instead.
doConstructorStuff ( ) ;
m_bIsSSL = false ;
}
TCPSlaveBase : : TCPSlaveBase ( unsigned short int defaultPort ,
const TQCString & protocol ,
const TQCString & poolSocket ,
const TQCString & appSocket ,
bool useSSL )
: SlaveBase ( protocol , poolSocket , appSocket ) ,
m_iSock ( - 1 ) ,
m_bIsSSL ( useSSL ) ,
m_iDefaultPort ( defaultPort ) ,
m_sServiceName ( protocol ) ,
fp ( 0 )
{
doConstructorStuff ( ) ;
if ( useSSL )
m_bIsSSL = initializeSSL ( ) ;
}
// The constructor procedures go here now.
void TCPSlaveBase : : doConstructorStuff ( )
{
d = new TcpSlaveBasePrivate ;
d - > kssl = 0L ;
d - > ip = " " ;
d - > cc = 0L ;
d - > usingTLS = false ;
d - > dcc = 0L ;
d - > pkcs = 0L ;
d - > status = - 1 ;
d - > timeout = KProtocolManager : : connectTimeout ( ) ;
d - > block = false ;
d - > useSSLTunneling = false ;
}
TCPSlaveBase : : ~ TCPSlaveBase ( )
{
cleanSSL ( ) ;
if ( d - > usingTLS ) delete d - > kssl ;
if ( d - > dcc ) delete d - > dcc ;
if ( d - > pkcs ) delete d - > pkcs ;
delete d ;
}
ssize_t TCPSlaveBase : : write ( const void * data , ssize_t len )
{
# ifdef Q_OS_UNIX
if ( ( m_bIsSSL | | d - > usingTLS ) & & ! d - > useSSLTunneling )
{
if ( d - > needSSLHandShake )
( void ) doSSLHandShake ( true ) ;
return d - > kssl - > write ( data , len ) ;
}
return KSocks : : self ( ) - > write ( m_iSock , data , len ) ;
# else
return 0 ;
# endif
}
ssize_t TCPSlaveBase : : read ( void * data , ssize_t len )
{
# ifdef Q_OS_UNIX
if ( ( m_bIsSSL | | d - > usingTLS ) & & ! d - > useSSLTunneling )
{
if ( d - > needSSLHandShake )
( void ) doSSLHandShake ( true ) ;
return d - > kssl - > read ( data , len ) ;
}
return KSocks : : self ( ) - > read ( m_iSock , data , len ) ;
# else
return 0 ;
# endif
}
void TCPSlaveBase : : setBlockSize ( int sz )
{
if ( sz < = 0 )
sz = 1 ;
d - > rblockSz = sz ;
}
ssize_t TCPSlaveBase : : readLine ( char * data , ssize_t len )
{
// Optimization:
// It's small, but it probably results in a gain on very high
// speed connections. I moved 3 if statements out of the while loop
// so that the while loop is as small as possible. (GS)
// let's not segfault!
if ( ! data )
return - 1 ;
char tmpbuf [ 1024 ] ; // 1kb temporary buffer for peeking
* data = 0 ;
ssize_t clen = 0 ;
char * buf = data ;
int rc = 0 ;
if ( ( m_bIsSSL | | d - > usingTLS ) & & ! d - > useSSLTunneling ) { // SSL CASE
if ( d - > needSSLHandShake )
( void ) doSSLHandShake ( true ) ;
while ( clen < len - 1 ) {
rc = d - > kssl - > pending ( ) ;
if ( rc > 0 ) { // Read a chunk
int bytes = rc ;
if ( bytes > d - > rblockSz )
bytes = d - > rblockSz ;
rc = d - > kssl - > peek ( tmpbuf , bytes ) ;
if ( rc < = 0 ) {
// FIXME: this doesn't cover rc == 0 case
return - 1 ;
}
bytes = rc ; // in case it contains no \n
for ( int i = 0 ; i < rc ; i + + ) {
if ( tmpbuf [ i ] = = ' \n ' ) {
bytes = i + 1 ;
break ;
}
}
if ( bytes + clen > = len ) // don't read too much!
bytes = len - clen - 1 ;
rc = d - > kssl - > read ( buf , bytes ) ;
if ( rc > 0 ) {
clen + = rc ;
buf + = ( rc - 1 ) ;
if ( * buf + + = = ' \n ' )
break ;
} else {
// FIXME: different case if rc == 0;
return - 1 ;
}
} else { // Read a byte
rc = d - > kssl - > read ( buf , 1 ) ;
if ( rc < = 0 ) {
return - 1 ;
// hm rc = 0 then
// SSL_read says to call SSL_get_error to see if
// this was an error. FIXME
} else {
clen + + ;
if ( * buf + + = = ' \n ' )
break ;
}
}
}
} else { // NON SSL CASE
while ( clen < len - 1 ) {
# ifdef Q_OS_UNIX
rc = KSocks : : self ( ) - > read ( m_iSock , buf , 1 ) ;
# else
rc = 0 ;
# endif
if ( rc < = 0 ) {
// FIXME: this doesn't cover rc == 0 case
return - 1 ;
} else {
clen + + ;
if ( * buf + + = = ' \n ' )
break ;
}
}
}
// Both cases fall through to here
* buf = 0 ;
return clen ;
}
unsigned short int TCPSlaveBase : : port ( unsigned short int _p )
{
unsigned short int p = _p ;
if ( _p < = 0 )
{
p = m_iDefaultPort ;
}
return p ;
}
// This function is simply a wrapper to establish the connection
// to the server. It's a bit more complicated than ::connect
// because we first have to check to see if the user specified
// a port, and if so use it, otherwise we check to see if there
// is a port specified in /etc/services, and if so use that
// otherwise as a last resort use the supplied default port.
bool TCPSlaveBase : : connectToHost ( const TQString & host ,
unsigned int _port ,
bool sendError )
{
# ifdef Q_OS_UNIX
unsigned short int p ;
KExtendedSocket ks ;
d - > userAborted = false ;
// - leaving SSL - warn before we even connect
if ( metaData ( " main_frame_request " ) = = " TRUE " & &
metaData ( " ssl_activate_warnings " ) = = " TRUE " & &
metaData ( " ssl_was_in_use " ) = = " TRUE " & &
! m_bIsSSL ) {
KSSLSettings kss ;
if ( kss . warnOnLeave ( ) ) {
int result = messageBox ( i18n ( " You are about to leave secure "
" mode. Transmissions will no "
" longer be encrypted. \n This "
" means that a third party could "
" observe your data in transit. " ) ,
WarningContinueCancel ,
i18n ( " Security Information " ) ,
i18n ( " C&ontinue Loading " ) , TQString : : null ,
" WarnOnLeaveSSLMode " ) ;
// Move this setting into KSSL instead
TDEConfig * config = new TDEConfig ( " tdeioslaverc " ) ;
config - > setGroup ( " Notification Messages " ) ;
if ( ! config - > readBoolEntry ( " WarnOnLeaveSSLMode " , true ) ) {
config - > deleteEntry ( " WarnOnLeaveSSLMode " ) ;
config - > sync ( ) ;
kss . setWarnOnLeave ( false ) ;
kss . save ( ) ;
}
delete config ;
if ( result = = KMessageBox : : Cancel ) {
d - > userAborted = true ;
return false ;
}
}
}
d - > status = - 1 ;
d - > host = host ;
d - > needSSLHandShake = m_bIsSSL ;
p = port ( _port ) ;
ks . setAddress ( host , p ) ;
if ( d - > timeout > - 1 )
ks . setTimeout ( d - > timeout ) ;
if ( ks . connect ( ) < 0 )
{
d - > status = ks . status ( ) ;
if ( sendError )
{
if ( d - > status = = IO_LookupError )
error ( ERR_UNKNOWN_HOST , host ) ;
else if ( d - > status ! = - 1 )
error ( ERR_COULD_NOT_CONNECT , host ) ;
}
return false ;
}
m_iSock = ks . fd ( ) ;
// store the IP for later
const TDESocketAddress * sa = ks . peerAddress ( ) ;
if ( sa )
d - > ip = sa - > nodeName ( ) ;
else
d - > ip = " " ;
ks . release ( ) ; // KExtendedSocket no longer applicable
if ( d - > block ! = ks . blockingMode ( ) )
ks . setBlockingMode ( d - > block ) ;
m_iPort = p ;
if ( m_bIsSSL & & ! d - > useSSLTunneling ) {
if ( ! doSSLHandShake ( sendError ) )
return false ;
}
else
setMetaData ( " ssl_in_use " , " FALSE " ) ;
// Since we want to use stdio on the socket,
// we must fdopen it to get a file pointer,
// if it fails, close everything up
if ( ( fp = KDE_fdopen ( m_iSock , " w+ " ) ) = = 0 ) {
closeDescriptor ( ) ;
return false ;
}
return true ;
# else //!Q_OS_UNIX
return false ;
# endif //Q_OS_UNIX
}
void TCPSlaveBase : : closeDescriptor ( )
{
stopTLS ( ) ;
if ( fp ) {
fclose ( fp ) ;
fp = 0 ;
m_iSock = - 1 ;
if ( m_bIsSSL )
d - > kssl - > close ( ) ;
}
if ( m_iSock ! = - 1 ) {
close ( m_iSock ) ;
m_iSock = - 1 ;
}
d - > ip = " " ;
d - > host = " " ;
}
bool TCPSlaveBase : : initializeSSL ( )
{
if ( m_bIsSSL ) {
if ( KSSL : : doesSSLWork ( ) ) {
d - > kssl = new KSSL ;
return true ;
}
}
return false ;
}
void TCPSlaveBase : : cleanSSL ( )
{
delete d - > cc ;
if ( m_bIsSSL ) {
delete d - > kssl ;
d - > kssl = 0 ;
}
d - > militantSSL = false ;
}
bool TCPSlaveBase : : atEnd ( )
{
return feof ( fp ) ;
}
int TCPSlaveBase : : startTLS ( )
{
if ( d - > usingTLS | | d - > useSSLTunneling | | m_bIsSSL | | ! KSSL : : doesSSLWork ( ) )
return false ;
d - > kssl = new KSSL ( false ) ;
if ( ! d - > kssl - > TLSInit ( ) ) {
delete d - > kssl ;
return - 1 ;
}
if ( ! d - > realHost . isEmpty ( ) )
{
kdDebug ( 7029 ) < < " Setting real hostname: " < < d - > realHost < < endl ;
d - > kssl - > setPeerHost ( d - > realHost ) ;
} else {
kdDebug ( 7029 ) < < " Setting real hostname: " < < d - > host < < endl ;
d - > kssl - > setPeerHost ( d - > host ) ;
}
if ( hasMetaData ( " ssl_session_id " ) ) {
KSSLSession * s = KSSLSession : : fromString ( metaData ( " ssl_session_id " ) ) ;
if ( s ) {
d - > kssl - > takeSession ( s ) ;
delete s ;
}
}
certificatePrompt ( ) ;
int rc = d - > kssl - > connect ( m_iSock ) ;
if ( rc < 0 ) {
delete d - > kssl ;
return - 2 ;
}
setMetaData ( " ssl_session_id " , d - > kssl - > session ( ) - > toString ( ) ) ;
d - > usingTLS = true ;
setMetaData ( " ssl_in_use " , " TRUE " ) ;
if ( ! d - > kssl - > reusingSession ( ) ) {
rc = verifyCertificate ( ) ;
if ( rc ! = 1 ) {
setMetaData ( " ssl_in_use " , " FALSE " ) ;
d - > usingTLS = false ;
delete d - > kssl ;
return - 3 ;
}
}
d - > savedMetaData = mOutgoingMetaData ;
return ( d - > usingTLS ? 1 : 0 ) ;
}
void TCPSlaveBase : : stopTLS ( )
{
if ( d - > usingTLS ) {
delete d - > kssl ;
d - > usingTLS = false ;
setMetaData ( " ssl_in_use " , " FALSE " ) ;
}
}
void TCPSlaveBase : : setSSLMetaData ( ) {
if ( ! ( d - > usingTLS | | d - > useSSLTunneling | | m_bIsSSL ) )
return ;
mOutgoingMetaData = d - > savedMetaData ;
}
bool TCPSlaveBase : : canUseTLS ( )
{
if ( m_bIsSSL | | d - > needSSLHandShake | | ! KSSL : : doesSSLWork ( ) )
return false ;
KSSLSettings kss ;
return kss . tlsv1 ( ) ;
}
void TCPSlaveBase : : certificatePrompt ( )
{
TQString certname ; // the cert to use this session
bool send = false , prompt = false , save = false , forcePrompt = false ;
KSSLCertificateHome : : KSSLAuthAction aa ;
setMetaData ( " ssl_using_client_cert " , " FALSE " ) ; // we change this if needed
if ( metaData ( " ssl_no_client_cert " ) = = " TRUE " ) return ;
forcePrompt = ( metaData ( " ssl_force_cert_prompt " ) = = " TRUE " ) ;
// Delete the old cert since we're certainly done with it now
if ( d - > pkcs ) {
delete d - > pkcs ;
d - > pkcs = NULL ;
}
if ( ! d - > kssl ) return ;
// Look for a general certificate
if ( ! forcePrompt ) {
certname = KSSLCertificateHome : : getDefaultCertificateName ( & aa ) ;
switch ( aa ) {
case KSSLCertificateHome : : AuthSend :
send = true ; prompt = false ;
break ;
case KSSLCertificateHome : : AuthDont :
send = false ; prompt = false ;
certname = TQString : : null ;
break ;
case KSSLCertificateHome : : AuthPrompt :
send = false ; prompt = true ;
break ;
default :
break ;
}
}
TQString ourHost ;
if ( ! d - > realHost . isEmpty ( ) ) {
ourHost = d - > realHost ;
} else {
ourHost = d - > host ;
}
// Look for a certificate on a per-host basis as an override
TQString tmpcn = KSSLCertificateHome : : getDefaultCertificateName ( ourHost , & aa ) ;
if ( aa ! = KSSLCertificateHome : : AuthNone ) { // we must override
switch ( aa ) {
case KSSLCertificateHome : : AuthSend :
send = true ;
prompt = false ;
certname = tmpcn ;
break ;
case KSSLCertificateHome : : AuthDont :
send = false ;
prompt = false ;
certname = TQString : : null ;
break ;
case KSSLCertificateHome : : AuthPrompt :
send = false ;
prompt = true ;
certname = tmpcn ;
break ;
default :
break ;
}
}
// Finally, we allow the application to override anything.
if ( hasMetaData ( " ssl_demand_certificate " ) ) {
certname = metaData ( " ssl_demand_certificate " ) ;
if ( ! certname . isEmpty ( ) ) {
forcePrompt = false ;
prompt = false ;
send = true ;
}
}
if ( certname . isEmpty ( ) & & ! prompt & & ! forcePrompt ) return ;
// Ok, we're supposed to prompt the user....
if ( prompt | | forcePrompt ) {
TQStringList certs = KSSLCertificateHome : : getCertificateList ( ) ;
for ( TQStringList : : Iterator it = certs . begin ( ) ; it ! = certs . end ( ) ; + + it ) {
KSSLPKCS12 * pkcs = KSSLCertificateHome : : getCertificateByName ( * it ) ;
if ( pkcs & & ( ! pkcs - > getCertificate ( ) | |
! pkcs - > getCertificate ( ) - > x509V3Extensions ( ) . certTypeSSLClient ( ) ) ) {
certs . remove ( * it ) ;
}
delete pkcs ;
}
if ( certs . isEmpty ( ) ) return ; // we had nothing else, and prompt failed
if ( ! d - > dcc ) {
d - > dcc = new DCOPClient ;
d - > dcc - > attach ( ) ;
if ( ! d - > dcc - > isApplicationRegistered ( " tdeio_uiserver " ) ) {
TDEApplication : : startServiceByDesktopPath ( " tdeio_uiserver.desktop " ,
TQStringList ( ) ) ;
}
}
TQByteArray data , retval ;
TQCString rettype ;
TQDataStream arg ( data , IO_WriteOnly ) ;
arg < < ourHost ;
arg < < certs ;
arg < < metaData ( " window-id " ) . toInt ( ) ;
bool rc = d - > dcc - > call ( " tdeio_uiserver " , " UIServer " ,
" showSSLCertDialog(TQString, TQStringList,int) " ,
data , rettype , retval ) ;
if ( rc & & rettype = = " KSSLCertDlgRet " ) {
TQDataStream retStream ( retval , IO_ReadOnly ) ;
KSSLCertDlgRet drc ;
retStream > > drc ;
if ( drc . ok ) {
send = drc . send ;
save = drc . save ;
certname = drc . choice ;
}
}
}
// The user may have said to not send the certificate,
// but to save the choice
if ( ! send ) {
if ( save ) {
KSSLCertificateHome : : setDefaultCertificate ( certname , ourHost ,
false , false ) ;
}
return ;
}
// We're almost committed. If we can read the cert, we'll send it now.
KSSLPKCS12 * pkcs = KSSLCertificateHome : : getCertificateByName ( certname ) ;
if ( ! pkcs & & KSSLCertificateHome : : hasCertificateByName ( certname ) ) { // We need the password
TDEIO : : AuthInfo ai ;
bool first = true ;
do {
ai . prompt = i18n ( " Enter the certificate password: " ) ;
ai . caption = i18n ( " SSL Certificate Password " ) ;
ai . url . setProtocol ( " kssl " ) ;
ai . url . setHost ( certname ) ;
ai . username = certname ;
ai . keepPassword = true ;
bool showprompt ;
if ( first )
showprompt = ! checkCachedAuthentication ( ai ) ;
else
showprompt = true ;
if ( showprompt ) {
if ( ! openPassDlg ( ai , first ? TQString : : null :
i18n ( " Unable to open the certificate. Try a new password? " ) ) )
break ;
}
first = false ;
pkcs = KSSLCertificateHome : : getCertificateByName ( certname , ai . password ) ;
} while ( ! pkcs ) ;
}
// If we could open the certificate, let's send it
if ( pkcs ) {
if ( ! d - > kssl - > setClientCertificate ( pkcs ) ) {
messageBox ( Information , i18n ( " The procedure to set the "
" client certificate for the session "
" failed. " ) , i18n ( " SSL " ) ) ;
delete pkcs ; // we don't need this anymore
pkcs = 0L ;
} else {
kdDebug ( 7029 ) < < " Client SSL certificate is being used. " < < endl ;
setMetaData ( " ssl_using_client_cert " , " TRUE " ) ;
if ( save ) {
KSSLCertificateHome : : setDefaultCertificate ( certname , ourHost ,
true , false ) ;
}
}
d - > pkcs = pkcs ;
}
}
bool TCPSlaveBase : : usingTLS ( ) const
{
return d - > usingTLS ;
}
// ### remove this for KDE4 (misses const):
bool TCPSlaveBase : : usingTLS ( )
{
return d - > usingTLS ;
}
// Returns 0 for failed verification, -1 for rejected cert and 1 for ok
int TCPSlaveBase : : verifyCertificate ( )
{
int rc = 0 ;
bool permacache = false ;
bool isChild = false ;
bool _IPmatchesCN = false ;
int result ;
bool doAddHost = false ;
TQString ourHost ;
if ( ! d - > realHost . isEmpty ( ) )
ourHost = d - > realHost ;
else ourHost = d - > host ;
TQString theurl = TQString ( m_sServiceName ) + " :// " + ourHost + " : " + TQString : : number ( m_iPort ) ;
if ( ! hasMetaData ( " ssl_militant " ) | | metaData ( " ssl_militant " ) = = " FALSE " )
d - > militantSSL = false ;
else if ( metaData ( " ssl_militant " ) = = " TRUE " )
d - > militantSSL = true ;
if ( ! d - > cc ) d - > cc = new KSSLCertificateCache ;
KSSLCertificate & pc = d - > kssl - > peerInfo ( ) . getPeerCertificate ( ) ;
KSSLCertificate : : KSSLValidationList ksvl = pc . validateVerbose ( KSSLCertificate : : SSLServer ) ;
_IPmatchesCN = d - > kssl - > peerInfo ( ) . certMatchesAddress ( ) ;
if ( ! _IPmatchesCN ) {
# ifndef Q_WS_WIN //temporary
KNetwork : : KResolverResults res = KNetwork : : KResolver : : resolve ( d - > kssl - > peerInfo ( ) . peerHost ( ) , " 80 " , KNetwork : : KResolver : : CanonName ) ;
if ( ! res . isEmpty ( ) ) {
TQString old = d - > kssl - > peerInfo ( ) . peerHost ( ) ;
d - > kssl - > peerInfo ( ) . setPeerHost ( res [ 0 ] . canonicalName ( ) ) ;
_IPmatchesCN = d - > kssl - > peerInfo ( ) . certMatchesAddress ( ) ;
if ( ! _IPmatchesCN ) {
d - > kssl - > peerInfo ( ) . setPeerHost ( old ) ;
}
}
# endif
if ( ! _IPmatchesCN & & ! d - > militantSSL ) { // force this if the user wants it
if ( d - > cc - > getHostList ( pc ) . contains ( ourHost ) ) {
_IPmatchesCN = true ;
}
}
}
if ( ! _IPmatchesCN ) {
ksvl < < KSSLCertificate : : InvalidHost ;
}
KSSLCertificate : : KSSLValidation ksv = KSSLCertificate : : Ok ;
if ( ! ksvl . isEmpty ( ) )
ksv = ksvl . first ( ) ;
/* Setting the various bits of meta-info that will be needed. */
setMetaData ( " ssl_cipher " , d - > kssl - > connectionInfo ( ) . getCipher ( ) ) ;
setMetaData ( " ssl_cipher_desc " ,
d - > kssl - > connectionInfo ( ) . getCipherDescription ( ) ) ;
setMetaData ( " ssl_cipher_version " ,
d - > kssl - > connectionInfo ( ) . getCipherVersion ( ) ) ;
setMetaData ( " ssl_cipher_used_bits " ,
TQString : : number ( d - > kssl - > connectionInfo ( ) . getCipherUsedBits ( ) ) ) ;
setMetaData ( " ssl_cipher_bits " ,
TQString : : number ( d - > kssl - > connectionInfo ( ) . getCipherBits ( ) ) ) ;
setMetaData ( " ssl_peer_ip " , d - > ip ) ;
if ( ! d - > realHost . isEmpty ( ) ) {
setMetaData ( " ssl_proxied " , " true " ) ;
}
TQString errorStr ;
for ( KSSLCertificate : : KSSLValidationList : : ConstIterator it = ksvl . begin ( ) ;
it ! = ksvl . end ( ) ; + + it )
{
errorStr + = TQString : : number ( * it ) + " : " ;
}
setMetaData ( " ssl_cert_errors " , errorStr ) ;
setMetaData ( " ssl_peer_certificate " , pc . toString ( ) ) ;
if ( pc . chain ( ) . isValid ( ) & & pc . chain ( ) . depth ( ) > 1 ) {
TQString theChain ;
TQPtrList < KSSLCertificate > chain = pc . chain ( ) . getChain ( ) ;
chain . setAutoDelete ( true ) ;
for ( KSSLCertificate * c = chain . first ( ) ; c ; c = chain . next ( ) ) {
theChain + = c - > toString ( ) ;
theChain + = " \n " ;
}
setMetaData ( " ssl_peer_chain " , theChain ) ;
} else setMetaData ( " ssl_peer_chain " , " " ) ;
setMetaData ( " ssl_cert_state " , TQString : : number ( ksv ) ) ;
if ( ksv = = KSSLCertificate : : Ok ) {
rc = 1 ;
setMetaData ( " ssl_action " , " accept " ) ;
}
kdDebug ( 7029 ) < < " SSL HTTP frame the parent? " < < metaData ( " main_frame_request " ) < < endl ;
if ( ! hasMetaData ( " main_frame_request " ) | | metaData ( " main_frame_request " ) = = " TRUE " ) {
// Since we're the parent, we need to teach the child.
setMetaData ( " ssl_parent_ip " , d - > ip ) ;
setMetaData ( " ssl_parent_cert " , pc . toString ( ) ) ;
// - Read from cache and see if there is a policy for this
KSSLCertificateCache : : KSSLCertificatePolicy cp =
d - > cc - > getPolicyByCertificate ( pc ) ;
// - validation code
if ( ksv ! = KSSLCertificate : : Ok ) {
if ( d - > militantSSL ) {
return - 1 ;
}
if ( cp = = KSSLCertificateCache : : Unknown | |
cp = = KSSLCertificateCache : : Ambiguous ) {
cp = KSSLCertificateCache : : Prompt ;
} else {
// A policy was already set so let's honor that.
permacache = d - > cc - > isPermanent ( pc ) ;
}
/*
if ( ! _IPmatchesCN & & cp = = KSSLCertificateCache : : Accept ) {
cp = KSSLCertificateCache : : Prompt ;
// ksv = KSSLCertificate::Ok;
}
*/
// Precondition: cp is one of Reject, Accept or Prompt
switch ( cp ) {
case KSSLCertificateCache : : Accept :
rc = 1 ;
setMetaData ( " ssl_action " , " accept " ) ;
break ;
case KSSLCertificateCache : : Reject :
rc = - 1 ;
setMetaData ( " ssl_action " , " reject " ) ;
break ;
case KSSLCertificateCache : : Prompt :
{
do {
if ( ksv = = KSSLCertificate : : InvalidHost ) {
TQString msg = i18n ( " The IP address of the host %1 "
" does not match the one the "
" certificate was issued to. " ) ;
result = messageBox ( WarningYesNoCancel ,
msg . arg ( ourHost ) ,
i18n ( " Server Authentication " ) ,
i18n ( " &Details " ) ,
i18n ( " Co&ntinue " ) ) ;
} else {
TQString msg = i18n ( " The server certificate failed the "
" authenticity test (%1). " ) ;
result = messageBox ( WarningYesNoCancel ,
msg . arg ( ourHost ) ,
i18n ( " Server Authentication " ) ,
i18n ( " &Details " ) ,
i18n ( " Co&ntinue " ) ) ;
}
if ( result = = KMessageBox : : Yes ) {
if ( ! d - > dcc ) {
d - > dcc = new DCOPClient ;
d - > dcc - > attach ( ) ;
if ( ! d - > dcc - > isApplicationRegistered ( " tdeio_uiserver " ) ) {
TDEApplication : : startServiceByDesktopPath ( " tdeio_uiserver.desktop " ,
TQStringList ( ) ) ;
}
}
TQByteArray data , ignore ;
TQCString ignoretype ;
TQDataStream arg ( data , IO_WriteOnly ) ;
arg < < theurl < < mOutgoingMetaData ;
arg < < metaData ( " window-id " ) . toInt ( ) ;
d - > dcc - > call ( " tdeio_uiserver " , " UIServer " ,
" showSSLInfoDialog(TQString,TDEIO::MetaData,int) " ,
data , ignoretype , ignore ) ;
}
} while ( result = = KMessageBox : : Yes ) ;
if ( result = = KMessageBox : : No ) {
setMetaData ( " ssl_action " , " accept " ) ;
rc = 1 ;
cp = KSSLCertificateCache : : Accept ;
doAddHost = true ;
result = messageBox ( WarningYesNo ,
i18n ( " Would you like to accept this "
" certificate forever without "
" being prompted? " ) ,
i18n ( " Server Authentication " ) ,
i18n ( " &Forever " ) ,
i18n ( " &Current Sessions Only " ) ) ;
if ( result = = KMessageBox : : Yes )
permacache = true ;
else
permacache = false ;
} else {
setMetaData ( " ssl_action " , " reject " ) ;
rc = - 1 ;
cp = KSSLCertificateCache : : Prompt ;
}
break ;
}
default :
kdDebug ( 7029 ) < < " TCPSlaveBase/SSL error in cert code. "
< < " Please report this to kfm-devel@kde.org. "
< < endl ;
break ;
}
}
// - cache the results
d - > cc - > addCertificate ( pc , cp , permacache ) ;
if ( doAddHost ) d - > cc - > addHost ( pc , ourHost ) ;
} else { // Child frame
// - Read from cache and see if there is a policy for this
KSSLCertificateCache : : KSSLCertificatePolicy cp =
d - > cc - > getPolicyByCertificate ( pc ) ;
isChild = true ;
// Check the cert and IP to make sure they're the same
// as the parent frame
bool certAndIPTheSame = ( d - > ip = = metaData ( " ssl_parent_ip " ) & &
pc . toString ( ) = = metaData ( " ssl_parent_cert " ) ) ;
if ( ksv = = KSSLCertificate : : Ok ) {
if ( certAndIPTheSame ) { // success
rc = 1 ;
setMetaData ( " ssl_action " , " accept " ) ;
} else {
/*
if ( d - > militantSSL ) {
return - 1 ;
}
result = messageBox ( WarningYesNo ,
i18n ( " The certificate is valid but does not appear to have been assigned to this server. Do you wish to continue loading? " ) ,
i18n ( " Server Authentication " ) ) ;
if ( result = = KMessageBox : : Yes ) { // success
rc = 1 ;
setMetaData ( " ssl_action " , " accept " ) ;
} else { // fail
rc = - 1 ;
setMetaData ( " ssl_action " , " reject " ) ;
}
*/
setMetaData ( " ssl_action " , " accept " ) ;
rc = 1 ; // Let's accept this now. It's bad, but at least the user
// will see potential attacks in KDE3 with the pseudo-lock
// icon on the toolbar, and can investigate with the RMB
}
} else {
if ( d - > militantSSL ) {
return - 1 ;
}
if ( cp = = KSSLCertificateCache : : Accept ) {
if ( certAndIPTheSame ) { // success
rc = 1 ;
setMetaData ( " ssl_action " , " accept " ) ;
} else { // fail
result = messageBox ( WarningYesNo ,
i18n ( " You have indicated that you wish to accept this certificate, but it is not issued to the server who is presenting it. Do you wish to continue loading? " ) ,
i18n ( " Server Authentication " ) ) ;
if ( result = = KMessageBox : : Yes ) {
rc = 1 ;
setMetaData ( " ssl_action " , " accept " ) ;
d - > cc - > addHost ( pc , ourHost ) ;
} else {
rc = - 1 ;
setMetaData ( " ssl_action " , " reject " ) ;
}
}
} else if ( cp = = KSSLCertificateCache : : Reject ) { // fail
messageBox ( Information , i18n ( " SSL certificate is being rejected as requested. You can disable this in the Trinity Control Center. " ) ,
i18n ( " Server Authentication " ) ) ;
rc = - 1 ;
setMetaData ( " ssl_action " , " reject " ) ;
} else {
do {
TQString msg = i18n ( " The server certificate failed the "
" authenticity test (%1). " ) ;
result = messageBox ( WarningYesNoCancel ,
msg . arg ( ourHost ) ,
i18n ( " Server Authentication " ) ,
i18n ( " &Details " ) ,
i18n ( " Co&nnect " ) ) ;
if ( result = = KMessageBox : : Yes ) {
if ( ! d - > dcc ) {
d - > dcc = new DCOPClient ;
d - > dcc - > attach ( ) ;
if ( ! d - > dcc - > isApplicationRegistered ( " tdeio_uiserver " ) ) {
TDEApplication : : startServiceByDesktopPath ( " tdeio_uiserver.desktop " ,
TQStringList ( ) ) ;
}
}
TQByteArray data , ignore ;
TQCString ignoretype ;
TQDataStream arg ( data , IO_WriteOnly ) ;
arg < < theurl < < mOutgoingMetaData ;
arg < < metaData ( " window-id " ) . toInt ( ) ;
d - > dcc - > call ( " tdeio_uiserver " , " UIServer " ,
" showSSLInfoDialog(TQString,TDEIO::MetaData,int) " ,
data , ignoretype , ignore ) ;
}
} while ( result = = KMessageBox : : Yes ) ;
if ( result = = KMessageBox : : No ) {
setMetaData ( " ssl_action " , " accept " ) ;
rc = 1 ;
cp = KSSLCertificateCache : : Accept ;
result = messageBox ( WarningYesNo ,
i18n ( " Would you like to accept this "
" certificate forever without "
" being prompted? " ) ,
i18n ( " Server Authentication " ) ,
i18n ( " &Forever " ) ,
i18n ( " &Current Sessions Only " ) ) ;
permacache = ( result = = KMessageBox : : Yes ) ;
d - > cc - > addCertificate ( pc , cp , permacache ) ;
d - > cc - > addHost ( pc , ourHost ) ;
} else {
setMetaData ( " ssl_action " , " reject " ) ;
rc = - 1 ;
cp = KSSLCertificateCache : : Prompt ;
d - > cc - > addCertificate ( pc , cp , permacache ) ;
}
}
}
}
if ( rc = = - 1 ) {
return rc ;
}
if ( metaData ( " ssl_activate_warnings " ) = = " TRUE " ) {
// - entering SSL
if ( ! isChild & & metaData ( " ssl_was_in_use " ) = = " FALSE " & &
d - > kssl - > settings ( ) - > warnOnEnter ( ) ) {
int result ;
do {
result = messageBox ( i18n ( " You are about to "
" enter secure mode. "
" All transmissions "
" will be encrypted "
" unless otherwise "
" noted. \n This means "
" that no third party "
" will be able to "
" easily observe your "
" data in transit. " ) ,
WarningYesNo ,
i18n ( " Security Information " ) ,
i18n ( " Display SSL "
" &Information " ) ,
i18n ( " C&onnect " ) ,
" WarnOnEnterSSLMode " ) ;
// Move this setting into KSSL instead
TDEConfig * config = new TDEConfig ( " tdeioslaverc " ) ;
config - > setGroup ( " Notification Messages " ) ;
bool dialogBoxStatus = false ;
if ( config - > hasKey ( " WarnOnEnterSSLMode " ) ) {
dialogBoxStatus = true ;
}
bool keyStatus = config - > readBoolEntry ( " WarnOnEnterSSLMode " , true ) ;
dialogBoxStatus = dialogBoxStatus & & keyStatus ;
if ( ! keyStatus ) {
config - > deleteEntry ( " WarnOnEnterSSLMode " ) ;
config - > sync ( ) ;
d - > kssl - > settings ( ) - > setWarnOnEnter ( false ) ;
d - > kssl - > settings ( ) - > save ( ) ;
}
delete config ;
if ( result = = KMessageBox : : Yes )
{
if ( ! d - > dcc ) {
d - > dcc = new DCOPClient ;
d - > dcc - > attach ( ) ;
if ( ! d - > dcc - > isApplicationRegistered ( " tdeio_uiserver " ) ) {
TDEApplication : : startServiceByDesktopPath ( " tdeio_uiserver.desktop " ,
TQStringList ( ) ) ;
}
}
TQByteArray data , ignore ;
TQCString ignoretype ;
TQDataStream arg ( data , IO_WriteOnly ) ;
arg < < theurl < < mOutgoingMetaData ;
arg < < metaData ( " window-id " ) . toInt ( ) ;
d - > dcc - > call ( " tdeio_uiserver " , " UIServer " ,
" showSSLInfoDialog(TQString,TDEIO::MetaData,int) " ,
data , ignoretype , ignore ) ;
}
//Laurent: If we disable message box we can't click on KMessageBox::No
if ( dialogBoxStatus ) {
break ;
}
} while ( result ! = KMessageBox : : No ) ;
}
} // if ssl_activate_warnings
kdDebug ( 7029 ) < < " SSL connection information follows: " < < endl
< < " +----------------------------------------------- " < < endl
< < " | Cipher: " < < d - > kssl - > connectionInfo ( ) . getCipher ( ) < < endl
< < " | Description: " < < d - > kssl - > connectionInfo ( ) . getCipherDescription ( ) < < endl
< < " | Version: " < < d - > kssl - > connectionInfo ( ) . getCipherVersion ( ) < < endl
< < " | Strength: " < < d - > kssl - > connectionInfo ( ) . getCipherUsedBits ( )
< < " of " < < d - > kssl - > connectionInfo ( ) . getCipherBits ( )
< < " bits used. " < < endl
< < " | PEER: " < < endl
< < " | Subject: " < < d - > kssl - > peerInfo ( ) . getPeerCertificate ( ) . getSubject ( ) < < endl
< < " | Issuer: " < < d - > kssl - > peerInfo ( ) . getPeerCertificate ( ) . getIssuer ( ) < < endl
< < " | Validation: " < < ( int ) ksv < < endl
< < " | Certificate matches IP: " < < _IPmatchesCN < < endl
< < " +----------------------------------------------- "
< < endl ;
// sendMetaData(); Do not call this function!!
return rc ;
}
bool TCPSlaveBase : : isConnectionValid ( )
{
if ( m_iSock = = - 1 )
return false ;
fd_set rdfs ;
FD_ZERO ( & rdfs ) ;
FD_SET ( m_iSock , & rdfs ) ;
struct timeval tv ;
tv . tv_usec = 0 ;
tv . tv_sec = 0 ;
int retval ;
# ifdef Q_OS_UNIX
do {
retval = KSocks : : self ( ) - > select ( m_iSock + 1 , & rdfs , NULL , NULL , & tv ) ;
if ( wasKilled ( ) )
return false ; // Beam us out of here
} while ( ( retval = = - 1 ) & & ( errno = = EAGAIN ) ) ;
# else
retval = - 1 ;
# endif
// retval == -1 ==> Error
// retval == 0 ==> Connection Idle
// retval >= 1 ==> Connection Active
//kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: "
// << retval << endl;
if ( retval = = - 1 )
return false ;
if ( retval = = 0 )
return true ;
// Connection is active, check if it has closed.
char buffer [ 100 ] ;
# ifdef Q_OS_UNIX
do {
retval = KSocks : : self ( ) - > recv ( m_iSock , buffer , 80 , MSG_PEEK ) ;
} while ( ( retval = = - 1 ) & & ( errno = = EAGAIN ) ) ;
# else
retval = - 1 ;
# endif
//kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: "
// << retval << endl;
if ( retval < = 0 )
return false ; // Error or connection closed.
return true ; // Connection still valid.
}
bool TCPSlaveBase : : waitForResponse ( int t )
{
fd_set rd ;
struct timeval timeout ;
if ( ( m_bIsSSL | | d - > usingTLS ) & & ! d - > useSSLTunneling & & d - > kssl )
if ( d - > kssl - > pending ( ) > 0 )
return true ;
FD_ZERO ( & rd ) ;
FD_SET ( m_iSock , & rd ) ;
timeout . tv_usec = 0 ;
timeout . tv_sec = t ;
time_t startTime ;
int rc ;
int n = t ;
reSelect :
startTime = time ( NULL ) ;
# ifdef Q_OS_UNIX
rc = KSocks : : self ( ) - > select ( m_iSock + 1 , & rd , NULL , NULL , & timeout ) ;
# else
rc = - 1 ;
# endif
if ( wasKilled ( ) )
return false ; // We're dead.
if ( rc = = - 1 )
return false ;
if ( FD_ISSET ( m_iSock , & rd ) )
return true ;
// Well it returned but it wasn't set. Let's see if it
// returned too early (perhaps from an errant signal) and
// start over with the remaining time
int timeDone = time ( NULL ) - startTime ;
if ( timeDone < n )
{
n - = timeDone ;
timeout . tv_sec = n ;
goto reSelect ;
}
return false ; // Timed out!
}
int TCPSlaveBase : : connectResult ( )
{
return d - > status ;
}
void TCPSlaveBase : : setBlockConnection ( bool b )
{
d - > block = b ;
}
void TCPSlaveBase : : setConnectTimeout ( int t )
{
d - > timeout = t ;
}
bool TCPSlaveBase : : isSSLTunnelEnabled ( )
{
return d - > useSSLTunneling ;
}
void TCPSlaveBase : : setEnableSSLTunnel ( bool enable )
{
d - > useSSLTunneling = enable ;
}
void TCPSlaveBase : : setRealHost ( const TQString & realHost )
{
d - > realHost = realHost ;
}
bool TCPSlaveBase : : doSSLHandShake ( bool sendError )
{
kdDebug ( 7029 ) < < " TCPSlaveBase::doSSLHandShake: " < < endl ;
TQString msgHost = d - > host ;
d - > kssl - > reInitialize ( ) ;
if ( hasMetaData ( " ssl_session_id " ) ) {
KSSLSession * s = KSSLSession : : fromString ( metaData ( " ssl_session_id " ) ) ;
if ( s ) {
d - > kssl - > takeSession ( s ) ;
delete s ;
}
}
certificatePrompt ( ) ;
if ( ! d - > realHost . isEmpty ( ) )
{
msgHost = d - > realHost ;
}
kdDebug ( 7029 ) < < " Setting real hostname: " < < msgHost < < endl ;
d - > kssl - > setPeerHost ( msgHost ) ;
d - > status = d - > kssl - > connect ( m_iSock ) ;
if ( d - > status < 0 )
{
closeDescriptor ( ) ;
if ( sendError )
error ( ERR_COULD_NOT_CONNECT , msgHost ) ;
return false ;
}
setMetaData ( " ssl_session_id " , d - > kssl - > session ( ) - > toString ( ) ) ;
setMetaData ( " ssl_in_use " , " TRUE " ) ;
if ( ! d - > kssl - > reusingSession ( ) ) {
int rc = verifyCertificate ( ) ;
if ( rc ! = 1 ) {
d - > status = - 1 ;
closeDescriptor ( ) ;
if ( sendError )
error ( ERR_COULD_NOT_CONNECT , msgHost ) ;
return false ;
}
}
d - > needSSLHandShake = false ;
d - > savedMetaData = mOutgoingMetaData ;
return true ;
}
bool TCPSlaveBase : : userAborted ( ) const
{
return d - > userAborted ;
}
void TCPSlaveBase : : virtual_hook ( int id , void * data )
{ SlaveBase : : virtual_hook ( id , data ) ; }