470351 Fixed SNI matching of wildcard certificates

This commit is contained in:
Greg Wilkins 2015-07-23 19:49:06 +10:00
parent e1827f659e
commit df6b935b94
6 changed files with 76 additions and 12 deletions

View File

@ -488,4 +488,14 @@ public class IOTest
for (int i=0;i<buffers.length;i++)
assertEquals(0,buffers[i].remaining());
}
@Test
public void testDomain()
{
assertTrue(IO.isInDomain("foo.com","foo.com"));
assertTrue(IO.isInDomain("www.foo.com","foo.com"));
assertFalse(IO.isInDomain("foo.com","bar.com"));
assertFalse(IO.isInDomain("www.foo.com","bar.com"));
}
}

View File

@ -29,6 +29,7 @@ import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -107,8 +108,13 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
String sniName = (String)sslSession.getValue("org.eclipse.jetty.util.ssl.sniname");
if (sniName!=null && !sniName.equalsIgnoreCase(request.getServerName()))
{
LOG.warn("Host does not match SNI Name: {}!={}",sniName,request.getServerName());
throw new BadMessageException(400,"Host does not match SNI");
String wild=(String)sslSession.getValue("org.eclipse.jetty.util.ssl.sniwild");
String name=request.getServerName();
if (wild==null || !IO.isInDomain(name,wild))
{
LOG.warn("Host does not match SNI Name: {}/{}!={}",sniName,wild,request.getServerName());
throw new BadMessageException(400,"Host does not match SNI");
}
}
}

View File

@ -50,6 +50,8 @@ import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.ConcurrentArrayQueue;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.ssl.SniX509ExtendedKeyManager;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.hamcrest.Matchers;
import org.junit.After;
@ -146,6 +148,22 @@ public class SniSslConnectionFactoryTest
Assert.assertThat(response,Matchers.containsString("host=www.san.com"));
}
@Test
public void testWildSNIConnect() throws Exception
{
String response;
response= getResponse("domain.com","www.domain.com","*.domain.com");
Assert.assertThat(response,Matchers.containsString("host=www.domain.com"));
response= getResponse("domain.com","domain.com","*.domain.com");
Assert.assertThat(response,Matchers.containsString("host=domain.com"));
response= getResponse("www.domain.com","www.domain.com","*.domain.com");
Assert.assertThat(response,Matchers.containsString("host=www.domain.com"));
}
@Test
public void testBadSNIConnect() throws Exception

View File

@ -463,7 +463,21 @@ public class IO
return total;
}
/* ------------------------------------------------------------ */
/**
* @param name A host name like www.foo.com
* @param domain A domain name like foo.com
* @return True if the host name is in the domain name
*/
public static boolean isInDomain(String name, String domain)
{
if (!name.endsWith(domain))
return false;
if (name.length()==domain.length())
return true;
return name.charAt(name.length()-domain.length()-1)=='.';
}
/* ------------------------------------------------------------ */
/**

View File

@ -44,6 +44,7 @@ public class SniX509ExtendedKeyManager extends X509ExtendedKeyManager
{
static final Logger LOG = Log.getLogger(SniX509ExtendedKeyManager.class);
public final static String SNI_NAME = "org.eclipse.jetty.util.ssl.sniname";
public final static String SNI_WILD = "org.eclipse.jetty.util.ssl.sniwild";
public final static String NO_MATCHERS="No Matchers";
private final X509ExtendedKeyManager _delegate;
@ -81,6 +82,7 @@ public class SniX509ExtendedKeyManager extends X509ExtendedKeyManager
// Look for an SNI alias
String alias=null;
String host=null;
String wild=null;
if (matchers!=null)
{
for (SNIMatcher m : matchers)
@ -90,6 +92,7 @@ public class SniX509ExtendedKeyManager extends X509ExtendedKeyManager
SslContextFactory.AliasSNIMatcher matcher = (SslContextFactory.AliasSNIMatcher)m;
alias=matcher.getAlias();
host=matcher.getServerName();
wild=matcher.getWildDomain();
break;
}
}
@ -106,6 +109,8 @@ public class SniX509ExtendedKeyManager extends X509ExtendedKeyManager
if (a.equals(alias))
{
session.putValue(SNI_NAME,host);
if (wild!=null)
session.putValue(SNI_WILD,wild);
return alias;
}
}

View File

@ -428,7 +428,7 @@ public class SslContextFactory extends AbstractLifeCycle
_certWilds.clear();
for (String name : _certAliases.keySet())
if (name.startsWith("*."))
_certWilds.put(name.substring(1),_certAliases.get(name));
_certWilds.put(name.substring(2),_certAliases.get(name));
LOG.info("x509={} wild={} alias={} for {}",_certAliases,_certWilds,_certAlias,this);
@ -1728,6 +1728,7 @@ public class SslContextFactory extends AbstractLifeCycle
class AliasSNIMatcher extends SNIMatcher
{
private String _alias;
private String _wild;
private SNIHostName _name;
protected AliasSNIMatcher()
@ -1760,18 +1761,23 @@ public class SslContextFactory extends AbstractLifeCycle
// Try wild card matches
String domain = _name.getAsciiName();
int dot=domain.indexOf('.');
if (dot>=0)
_alias = _certWilds.get(domain);
if (_alias==null)
{
domain=domain.substring(dot);
_alias = _certWilds.get(domain);
if (_alias!=null)
int dot=domain.indexOf('.');
if (dot>=0)
{
if (LOG.isDebugEnabled())
LOG.debug("wild match {}->{}",_name.getAsciiName(),_alias);
return true;
domain=domain.substring(dot+1);
_alias = _certWilds.get(domain);
}
}
if (_alias!=null)
{
_wild=domain;
if (LOG.isDebugEnabled())
LOG.debug("wild match {}->{}",_name.getAsciiName(),_alias);
return true;
}
}
if (LOG.isDebugEnabled())
LOG.debug("No match for {}",_name.getAsciiName());
@ -1785,6 +1791,11 @@ public class SslContextFactory extends AbstractLifeCycle
return _alias;
}
public String getWildDomain()
{
return _wild;
}
public String getServerName()
{
return _name==null?null:_name.getAsciiName();