Bug 122433 - Server Name Identification support
Summary: Server Name Identification support
Status: RESOLVED FIXED
Alias: None
Product: kio
Classification: Frameworks and Libraries
Component: kssl (show other bugs)
Version: unspecified
Platform: Debian testing Linux
: HI wishlist
Target Milestone: ---
Assignee: David Faure
URL:
Keywords:
: 174933 189584 207021 (view as bug list)
Depends on:
Blocks:
 
Reported: 2006-02-21 18:53 UTC by Aaron Johnson
Modified: 2011-03-18 00:22 UTC (History)
12 users (show)

See Also:
Latest Commit:
Version Fixed In: 4.7


Attachments
adds client CNI support to QSslSocket (3.04 KB, patch)
2009-09-21 17:56 UTC, Daniel Black
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Aaron Johnson 2006-02-21 18:53:16 UTC
Version:            (using KDE KDE 3.5.1)
Installed from:    Debian testing/unstable Packages

This is a request to support Server Name Identification as specified in Section 3.1 of RFC3546 (http://www.ietf.org/rfc/rfc3546.txt), and written about in http://blog.goolamabbas.org/?p=34 and http://blog.ebrahim.org/archives/2006/02/21/server_name_indication_sni.php
Comment 1 Thiago Macieira 2006-02-25 19:22:04 UTC
Does anyone know if OpenSSL and QCA support this?
Comment 2 George Staikos 2006-02-26 14:33:07 UTC
On Saturday 25 February 2006 13:22, Thiago Macieira wrote:
> Does anyone know if OpenSSL and QCA support this?


   Unsure at the moment.
Comment 3 George Staikos 2006-03-09 16:07:34 UTC
Supported in 0.9.9.  Will add for 4.0
Comment 4 Brad Hards 2006-10-12 13:09:52 UTC
SVN commit 594800 by bhards:

Implement the framework for RFC3546 Server Name Indication.

This allows a virtual host to support a range of HTTPS connections.

CCBUG: 122433


 M  +4 -0      include/QtCrypto/qca_securelayer.h  
 M  +2 -1      include/QtCrypto/qcaprovider.h  
 M  +1 -1      src/qca_securelayer.cpp  


--- trunk/kdesupport/qca/include/QtCrypto/qca_securelayer.h #594799:594800
@@ -404,6 +404,10 @@
 		   Start the TLS/SSL connection as a client.
 
 		   \param host the hostname that you want to connect to
+
+		   \note This hostname will be used for Server Name Indication extension (see 
+		   <a href="http://www.ietf.org/rfc/rfc3546.txt">RFC 3546</a> Section 3.1)
+		   if supported by the backend provider.
 		*/
 		void startClient(const QString &host = QString());
 
--- trunk/kdesupport/qca/include/QtCrypto/qcaprovider.h #594799:594800
@@ -463,7 +463,8 @@
 
 	virtual void setConstraints(int minSSF, int maxSSF) = 0;
 	virtual void setConstraints(const QStringList &cipherSuiteList) = 0;
-	virtual void setup(const CertificateCollection &trusted, const CertificateChain &cert, const PrivateKey &key, bool server, bool compress, bool dtls) = 0;
+	virtual void setup(const CertificateCollection &trusted, const CertificateChain &cert, const PrivateKey &key, bool server,
+			   const QString &hostName, bool compress, bool dtls) = 0;
 
 	virtual void shutdown() = 0; // flag for shutdown, call update next
 	virtual void setMTU(int size); // for dtls
--- trunk/kdesupport/qca/src/qca_securelayer.cpp #594799:594800
@@ -142,7 +142,7 @@
 		else
 			c->setConstraints(con_cipherSuites);
 
-		c->setup(trusted, localCert, localKey, serverMode, tryCompress, false);
+		c->setup(trusted, localCert, localKey, serverMode, host, tryCompress, false);
 
 		bool ok;
 		c->start();
Comment 5 Brad Hards 2006-10-12 13:14:23 UTC
SVN commit 594801 by bhards:

Implement the backend part of the Server Name Identification
(RFC3546).

Note that this will only work if your underlying OpenSSL implementation
also supports it, which is true for the 0.9.9 development (essentially
CVS HEAD for OpenSSL), but not for 0.9.8 or earlier.

This version should build with either 0.9.9 or 0.9.8, and should also
be OK with at least 0.9.7.

Also has some gratuitous whitespace changes courtesy of my new
xemacs configuration.

CCBUG: 122433


 M  +49 -30    qca-openssl.cpp  


--- trunk/kdesupport/qca/plugins/qca-openssl/qca-openssl.cpp #594800:594801
@@ -971,7 +971,7 @@
 	m_algorithm = algorithm;
 	EVP_DigestInit( &m_context, m_algorithm );
     };
-    
+
     ~opensslHashContext()
     {
 	EVP_MD_CTX_cleanup(&m_context);
@@ -982,12 +982,12 @@
 	EVP_MD_CTX_cleanup(&m_context);
 	EVP_DigestInit( &m_context, m_algorithm );
     }
-    
+
     void update(const QSecureArray &a)
     {
 	EVP_DigestUpdate( &m_context, (unsigned char*)a.data(), a.size() );
     }
-    
+
     QSecureArray final()
     {
 	QSecureArray a( EVP_MD_size( m_algorithm ) );
@@ -999,11 +999,11 @@
     {
 	return new opensslHashContext(*this);
     }
-    
+
 protected:
     const EVP_MD *m_algorithm;
     EVP_MD_CTX m_context;
-};	
+};
 
 
 class opensslPbkdf1Context : public KDFContext
@@ -1019,13 +1019,13 @@
     {
 	return new opensslPbkdf1Context( *this );
     }
-    
+
     SymmetricKey makeKey(const QSecureArray &secret, const InitializationVector &salt,
 			      unsigned int keyLength, unsigned int iterationCount)
     {
 	/* from RFC2898:
 	   Steps:
-	   
+
 	   1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output
 	   "derived key too long" and stop.
 	*/
@@ -1038,7 +1038,7 @@
 	   2. Apply the underlying hash function Hash for c iterations to the
 	   concatenation of the password P and the salt S, then extract
 	   the first dkLen octets to produce a derived key DK:
-	   
+
 	   T_1 = Hash (P || S) ,
 	   T_2 = Hash (T_1) ,
 	   ...
@@ -1085,7 +1085,7 @@
     {
 	HMAC_Init_ex( &m_context, key.data(), key.size(), m_algorithm, 0 );
     }
-    
+
     KeyLength keyLength() const
     {
 	return anyKeyLength();
@@ -1095,7 +1095,7 @@
     {
 	HMAC_Update( &m_context, (unsigned char *)a.data(), a.size() );
     }
-    
+
     void final( QSecureArray *out)
     {
 	out->resize( EVP_MD_size( m_algorithm ) );
@@ -1107,7 +1107,7 @@
     {
 	return new opensslHMACContext(*this);
     }
-    
+
 protected:
     HMAC_CTX m_context;
     const EVP_MD *m_algorithm;
@@ -1254,7 +1254,7 @@
 	"E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9"
 	"DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510"
 	"15728E5A 8AACAA68 FFFFFFFF FFFFFFFF";
-	
+
 const char* IETF_4096_PRIME =
 	"FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1"
 	"29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD"
@@ -3017,7 +3017,7 @@
 		X509_set_subject_name(x, name);
 
 		// issuer == subject
-		X509_set_issuer_name(x, name); 
+		X509_set_issuer_name(x, name);
 
 		// subject key id
 		ex = new_subject_key_id(x);
@@ -3238,7 +3238,7 @@
 		default:
 		    qDebug() << "Unknown signature value: " << OBJ_obj2nid(x->cert_info->signature->algorithm);
 		    p.sigalgo = QCA::SignatureUnknown;
-		}    
+		}
 
 		pos = X509_get_ext_by_NID(x, NID_subject_key_identifier, -1);
 		if(pos != -1)
@@ -3525,7 +3525,7 @@
 		default:
 		    qDebug() << "Unknown signature value: " << OBJ_obj2nid(x->sig_alg->algorithm);
 		    p.sigalgo = QCA::SignatureUnknown;
-		}    
+		}
 		_props = p;
 	}
 };
@@ -3674,7 +3674,7 @@
 		default:
 		    qWarning() << "Unknown signature value: " << OBJ_obj2nid(x->sig_alg->algorithm);
 		    p.sigalgo = QCA::SignatureUnknown;
-		}    
+		}
 
 		int pos = X509_CRL_get_ext_by_NID(x, NID_authority_key_identifier, -1);
 		if(pos != -1)
@@ -4024,7 +4024,7 @@
 		case 0x0005:
 			// RFC 2246 A.5
 			return QString("TLS_RSA_WITH_RC4_128_SHA");
-			break;			
+			break;
 		case 0x0006:
 			// RFC 2246 A.5
 			return QString("TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5");
@@ -4261,7 +4261,7 @@
 		default:
 			return QString("TLS algo to be added: %1").arg(cipherID & 0xffff, 0, 16);
 			break;
-		} 
+		}
 	} else if (TLS::SSL_v3 == version) {
 		switch( cipherID & 0xFFFF ) {
 		case 0x0000:
@@ -4543,7 +4543,7 @@
 	enum { Good, TryAgain, Bad };
 	enum { Idle, Connect, Accept, Handshake, Active, Closing };
 
-	bool serv;
+        bool serv; // true if we are acting as a server
 	int mode;
 	QByteArray sendQueue;
 	QByteArray recvQueue;
@@ -4551,6 +4551,7 @@
 	CertificateCollection trusted;
 	Certificate cert, peercert; // TODO: support cert chains
 	PrivateKey key;
+        QString targetHostName;
 
 	Result result_result;
 	QByteArray result_to_net;
@@ -4558,7 +4559,11 @@
 	QByteArray result_plain;
 
 	SSL *ssl;
-	SSL_METHOD *method;
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+        const SSL_METHOD *method;
+#else
+    SSL_METHOD *method;
+#endif
 	SSL_CTX *context;
 	BIO *rbio, *wbio;
 	Validity vr;
@@ -4632,7 +4637,7 @@
 			qWarning("Unexpected enum in cipherSuites");
 			ctx = 0;
 		}
-		if (NULL == ctx) 
+		if (NULL == ctx)
 			return QStringList();
 
 		SSL *ssl = SSL_new(ctx);
@@ -4647,7 +4652,7 @@
 			SSL_CIPHER *thisCipher = sk_SSL_CIPHER_value(sk, i);
 			cipherList += cipherIDtoString(version, thisCipher->id);
 		}
-			
+
 		SSL_free(ssl);
 		SSL_CTX_free(ctx);
 
@@ -4679,13 +4684,18 @@
 		Q_UNUSED(cipherSuiteList);
 	}
 
-	virtual void setup(const CertificateCollection &_trusted, const CertificateChain &_cert, const PrivateKey &_key, bool serverMode, bool compress, bool)
+	virtual void setup(const CertificateCollection &_trusted, const CertificateChain &_cert, const PrivateKey &_key, bool serverMode,
+                           const QString &hostName, bool compress, bool)
 	{
 		trusted = _trusted;
 		if(!_cert.isEmpty())
 			cert = _cert.primary(); // TODO: take the whole chain
 		key = _key;
 		serv = serverMode;
+                if ( false == serverMode ) {
+                    // client
+                    targetHostName = hostName;
+                }
 		Q_UNUSED(compress); // TODO
 	}
 
@@ -5055,6 +5065,15 @@
 		}
 		SSL_set_ssl_method(ssl, method); // can this return error?
 
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+                if ( targetHostName.isEmpty() == false ) {
+                        // we have a target
+                        // this might fail, but we ignore that for now
+                        char *hostname = targetHostName.toAscii().data();
+                        SSL_set_tlsext_host_name( ssl, hostname );
+                }
+#endif
+
 		// setup the memory bio
 		rbio = BIO_new(BIO_s_mem());
 		wbio = BIO_new(BIO_s_mem());
@@ -5437,7 +5456,7 @@
 				i2d_PKCS7_bio(bo, p7);
 				if (SecureMessage::Detached == signMode)
 					sig = bio2ba(bo);
-				else 
+				else
 					out = bio2ba(bo);
 			}
 			else
@@ -5727,12 +5746,12 @@
 	{
 		return new opensslCipherContext( *this );
 	}
-	
+
 	unsigned int blockSize() const
 	{
 		return EVP_CIPHER_CTX_block_size(&m_context);
 	}
-    
+
 	bool update(const QSecureArray &in, QSecureArray *out)
 	{
 		// This works around a problem in OpenSSL, where it asserts if
@@ -5762,7 +5781,7 @@
 		out->resize(resultLength);
 		return true;
 	}
-	
+
 	bool final(QSecureArray *out)
 	{
 		out->resize(blockSize());
@@ -5772,18 +5791,18 @@
 						     (unsigned char*)out->data(),
 						     &resultLength)) {
 				return false;
-			} 
+			}
 		} else {
 			if (0 == EVP_DecryptFinal_ex(&m_context,
 						     (unsigned char*)out->data(),
 						     &resultLength)) {
 				return false;
-			} 
+			}
 		}
 		out->resize(resultLength);
 		return true;
 	}
-	
+
 	// Change cipher names
 	KeyLength keyLength() const
 	{
Comment 6 Brad Hards 2006-10-12 13:16:53 UTC
I think that this completes the QCA part of this bug, although there may be some additional work when I get around to providing the GnuTLS backend.
Comment 7 Jan Kriho 2009-08-25 15:34:04 UTC
Just tested in on 4.3, with https://sni.velox.ch/ SNI test site - not working. What is the current progress with the bug?
Comment 8 Daniel Black 2009-09-11 02:26:47 UTC
see bug 174933
Comment 9 Daniel Black 2009-09-11 03:42:59 UTC
upstream feature request: http://qt.nokia.com/developer/task-tracker/index_html?method=entry&id=188841
Comment 10 Daniel Black 2009-09-21 17:56:14 UTC
Created attachment 37101 [details]
adds client CNI support to QSslSocket

still todo: kio/kio/tcpslavebase.cpp iterates over the IP addresses of the hostname returned. As such the QSslSocket never sees the hostname being used which is required for SNI.
Comment 11 Daniel Black 2009-09-21 19:23:52 UTC
*** Bug 207021 has been marked as a duplicate of this bug. ***
Comment 12 Daniel Black 2009-09-22 08:51:11 UTC
Looking at kio/tcpslavebase.cpp:

TCPSlaveBase::connectToHost seems to attempt a hostname resolution and for each IP resolved attempt to connect to the host. If this connection times out it returns and grabs another hostname resolution, not necessary different, and tries connecting to all IPs again.

It seems to me like this could just pass the hostname to connectToHost and hand the errors for DNS resolution failure etc. This would make the QSslSocket class correctly handle SNI (once patched).

Also FYI: Attached Qt patch has been pushed upstream:
http://qt.gitorious.org/~grooverdan/qt/grooverdans-clone/commit/1bbe69d892fe0f65b21e87316ccf568a41510c76
Comment 13 Thiago Macieira 2009-09-22 17:18:23 UTC
The reason why KIO does what it does is to cope with broken routers. Unfortunately, we had to add this patch which cached the hostname resolutions in the application (instead of the ioslave) because a very popular brand of routers in Germany was getting confused by the multiple DNS queries done by KDE.

Personally, I would prefer that the system concentrates and caches the DNS resolution, not the application. There's a replacement for Network Manager called Connection Manager (ConnMan) which does it. If distributions agree to ditch NM in favour of connman, we can drop the patch in KIO too.
Comment 14 Daniel Black 2009-09-23 17:14:49 UTC
> The reason why KIO does...
ok. makes sense.

If accounting for this is important perhaps Nokia can use their role in ConnMan to move concepts into Qt.

This is of course off the topic of SNI a bit. Looking at the Qt code there is no way of setting a hostname and doing a QAbstractSocket::connect* call and having the hostname preserved. Even the PeerName seems to be overwritten on connect*.

What way do you suggest for making KIO SNI capable?
Comment 15 Stephan Wienczny 2010-02-17 14:16:48 UTC
*** Bug 189584 has been marked as a duplicate of this bug. ***
Comment 16 David Faure 2011-02-15 21:05:34 UTC
*** Bug 174933 has been marked as a duplicate of this bug. ***
Comment 17 David Faure 2011-02-15 21:07:21 UTC
See http://bugreports.qt.nokia.com/browse/QTBUG-1352 for the current discussion about the necessary Qt patches for SNI support.

The kdelibs side of it is rather easy: http://www.davidfaure.fr/2011/kdelibs_SNI.diff -- but only once the Qt patches have been applied and we can depend on Qt-4.8.
Comment 18 David Faure 2011-03-18 00:22:06 UTC
Git commit b41cfc59283023cd2ebf4ecf5b885db76b50bc7e by David Faure.
Committed on 21/02/2011 at 20:43.
Pushed by dfaure into branch 'master'.

SNI support in KTcpSocket.

SNI = Server Name Identification (RFC4366 section 3.1)
Needs new API in Qt, commit 2053ac7 merged to Qt 2 weeks ago, for Qt-4.8.0.

FIXED-IN: 4.7
BUG: 122433

M  +8    -0    kdecore/network/ktcpsocket.cpp     
M  +1    -0    kdecore/network/ktcpsocket.h     
M  +2    -0    kio/kio/tcpslavebase.cpp     

http://commits.kde.org/kdelibs/b41cfc59283023cd2ebf4ecf5b885db76b50bc7e