Merge branch 'master' of https://github.com/moparisthebest/Conversations into moparisthebest-master

This commit is contained in:
Daniel Gultsch 2016-01-12 15:35:50 +01:00
commit 5f1e30288a
2 changed files with 224 additions and 114 deletions

View file

@ -19,6 +19,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import de.measite.minidns.Client; import de.measite.minidns.Client;
@ -57,7 +58,7 @@ public class DNSHelper {
if (!b.containsKey("values")) { if (!b.containsKey("values")) {
Log.d(Config.LOGTAG,"all dns queries failed. provide fallback A record"); Log.d(Config.LOGTAG,"all dns queries failed. provide fallback A record");
ArrayList<Parcelable> values = new ArrayList<>(); ArrayList<Parcelable> values = new ArrayList<>();
values.add(createNamePortBundle(host,5222)); values.add(createNamePortBundle(host, 5222, false));
b.putParcelableArrayList("values",values); b.putParcelableArrayList("values",values);
} }
return b; return b;
@ -96,27 +97,27 @@ public class DNSHelper {
return servers; return servers;
} }
public static Bundle queryDNS(String host, InetAddress dnsServer) { private static class TlsSrv {
Bundle bundle = new Bundle(); private final SRV srv;
try { private final boolean tls;
client.setTimeout(Config.PING_TIMEOUT * 1000);
String qname = "_xmpp-client._tcp." + host;
Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host);
DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress());
TreeMap<Integer, ArrayList<SRV>> priorities = new TreeMap<>(); public TlsSrv(SRV srv, boolean tls) {
TreeMap<String, ArrayList<String>> ips4 = new TreeMap<>(); this.srv = srv;
TreeMap<String, ArrayList<String>> ips6 = new TreeMap<>(); this.tls = tls;
}
}
private static void fillSrvMaps(final String qname, final InetAddress dnsServer, final Map<Integer, List<TlsSrv>> priorities, final Map<String, List<String>> ips4, final Map<String, List<String>> ips6, final boolean tls) throws IOException {
final DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress());
for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) { for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) {
for (Record rr : rrset) { for (Record rr : rrset) {
Data d = rr.getPayload(); Data d = rr.getPayload();
if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) { if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) {
SRV srv = (SRV) d; SRV srv = (SRV) d;
if (!priorities.containsKey(srv.getPriority())) { if (!priorities.containsKey(srv.getPriority())) {
priorities.put(srv.getPriority(),new ArrayList<SRV>()); priorities.put(srv.getPriority(),new ArrayList<TlsSrv>());
} }
priorities.get(srv.getPriority()).add(srv); priorities.get(srv.getPriority()).add(new TlsSrv(srv, tls));
} }
if (d instanceof A) { if (d instanceof A) {
A a = (A) d; A a = (A) d;
@ -134,19 +135,35 @@ public class DNSHelper {
} }
} }
} }
}
ArrayList<SRV> result = new ArrayList<>(); public static Bundle queryDNS(String host, InetAddress dnsServer) {
for (ArrayList<SRV> s : priorities.values()) { Bundle bundle = new Bundle();
try {
client.setTimeout(Config.PING_TIMEOUT * 1000);
final String qname = "_xmpp-client._tcp." + host;
final String tlsQname = "_xmpps-client._tcp." + host;
Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host);
final Map<Integer, List<TlsSrv>> priorities = new TreeMap<>();
final Map<String, List<String>> ips4 = new TreeMap<>();
final Map<String, List<String>> ips6 = new TreeMap<>();
fillSrvMaps(qname, dnsServer, priorities, ips4, ips6, false);
fillSrvMaps(tlsQname, dnsServer, priorities, ips4, ips6, true);
final List<TlsSrv> result = new ArrayList<>();
for (final List<TlsSrv> s : priorities.values()) {
result.addAll(s); result.addAll(s);
} }
ArrayList<Bundle> values = new ArrayList<>(); final ArrayList<Bundle> values = new ArrayList<>();
if (result.size() == 0) { if (result.size() == 0) {
DNSMessage response; DNSMessage response;
try { try {
response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress()); response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
for (int i = 0; i < response.getAnswers().length; ++i) { for (int i = 0; i < response.getAnswers().length; ++i) {
values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload())); values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
} }
} catch (SocketTimeoutException e) { } catch (SocketTimeoutException e) {
Log.d(Config.LOGTAG,"ignoring timeout exception when querying A record on "+dnsServer.getHostAddress()); Log.d(Config.LOGTAG,"ignoring timeout exception when querying A record on "+dnsServer.getHostAddress());
@ -154,37 +171,38 @@ public class DNSHelper {
try { try {
response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress()); response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
for (int i = 0; i < response.getAnswers().length; ++i) { for (int i = 0; i < response.getAnswers().length; ++i) {
values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload())); values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
} }
} catch (SocketTimeoutException e) { } catch (SocketTimeoutException e) {
Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress()); Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress());
} }
values.add(createNamePortBundle(host,5222)); values.add(createNamePortBundle(host, 5222, false));
bundle.putParcelableArrayList("values", values); bundle.putParcelableArrayList("values", values);
return bundle; return bundle;
} }
for (SRV srv : result) { for (final TlsSrv tlsSrv : result) {
final SRV srv = tlsSrv.srv;
if (ips6.containsKey(srv.getName())) { if (ips6.containsKey(srv.getName())) {
values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6)); values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6, tlsSrv.tls));
} else { } else {
try { try {
DNSMessage response = client.query(srv.getName(), TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress()); DNSMessage response = client.query(srv.getName(), TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
for (int i = 0; i < response.getAnswers().length; ++i) { for (int i = 0; i < response.getAnswers().length; ++i) {
values.add(createNamePortBundle(srv.getName(), srv.getPort(), response.getAnswers()[i].getPayload())); values.add(createNamePortBundle(srv.getName(), srv.getPort(), response.getAnswers()[i].getPayload(), tlsSrv.tls));
} }
} catch (SocketTimeoutException e) { } catch (SocketTimeoutException e) {
Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress()); Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress());
} }
} }
if (ips4.containsKey(srv.getName())) { if (ips4.containsKey(srv.getName())) {
values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4)); values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4, tlsSrv.tls));
} else { } else {
DNSMessage response = client.query(srv.getName(), TYPE.A, CLASS.IN, dnsServer.getHostAddress()); DNSMessage response = client.query(srv.getName(), TYPE.A, CLASS.IN, dnsServer.getHostAddress());
for(int i = 0; i < response.getAnswers().length; ++i) { for(int i = 0; i < response.getAnswers().length; ++i) {
values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[i].getPayload())); values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[i].getPayload(), tlsSrv.tls));
} }
} }
values.add(createNamePortBundle(srv.getName(), srv.getPort())); values.add(createNamePortBundle(srv.getName(), srv.getPort(), tlsSrv.tls));
} }
bundle.putParcelableArrayList("values", values); bundle.putParcelableArrayList("values", values);
} catch (SocketTimeoutException e) { } catch (SocketTimeoutException e) {
@ -195,28 +213,31 @@ public class DNSHelper {
return bundle; return bundle;
} }
private static Bundle createNamePortBundle(String name, int port) { private static Bundle createNamePortBundle(String name, int port, final boolean tls) {
Bundle namePort = new Bundle(); Bundle namePort = new Bundle();
namePort.putString("name", name); namePort.putString("name", name);
namePort.putBoolean("tls", tls);
namePort.putInt("port", port); namePort.putInt("port", port);
return namePort; return namePort;
} }
private static Bundle createNamePortBundle(String name, int port, TreeMap<String, ArrayList<String>> ips) { private static Bundle createNamePortBundle(String name, int port, Map<String, List<String>> ips, final boolean tls) {
Bundle namePort = new Bundle(); Bundle namePort = new Bundle();
namePort.putString("name", name); namePort.putString("name", name);
namePort.putBoolean("tls", tls);
namePort.putInt("port", port); namePort.putInt("port", port);
if (ips!=null) { if (ips!=null) {
ArrayList<String> ip = ips.get(name); List<String> ip = ips.get(name);
Collections.shuffle(ip, new Random()); Collections.shuffle(ip, new Random());
namePort.putString("ip", ip.get(0)); namePort.putString("ip", ip.get(0));
} }
return namePort; return namePort;
} }
private static Bundle createNamePortBundle(String name, int port, Data data) { private static Bundle createNamePortBundle(String name, int port, Data data, final boolean tls) {
Bundle namePort = new Bundle(); Bundle namePort = new Bundle();
namePort.putString("name", name); namePort.putString("name", name);
namePort.putBoolean("tls", tls);
namePort.putInt("port", port); namePort.putInt("port", port);
if (data instanceof A) { if (data instanceof A) {
namePort.putString("ip", data.toString()); namePort.putString("ip", data.toString());

View file

@ -20,7 +20,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.lang.reflect.Method;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.ConnectException; import java.net.ConnectException;
import java.net.IDN; import java.net.IDN;
@ -247,6 +247,7 @@ public class XmppConnection implements Runnable {
} }
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": connect to "+destination+" via TOR"); Log.d(Config.LOGTAG,account.getJid().toBareJid()+": connect to "+destination+" via TOR");
socket = SocksSocketFactory.createSocketOverTor(destination,account.getPort()); socket = SocksSocketFactory.createSocketOverTor(destination,account.getPort());
startXmpp();
} else if (DNSHelper.isIp(account.getServer().toString())) { } else if (DNSHelper.isIp(account.getServer().toString())) {
socket = new Socket(); socket = new Socket();
try { try {
@ -254,13 +255,12 @@ public class XmppConnection implements Runnable {
} catch (IOException e) { } catch (IOException e) {
throw new UnknownHostException(); throw new UnknownHostException();
} }
startXmpp();
} else { } else {
final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService); final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService);
final ArrayList<Parcelable>values = result.getParcelableArrayList("values"); final ArrayList<Parcelable>values = result.getParcelableArrayList("values");
int i = 0; for(Iterator<Parcelable> iterator = values.iterator(); iterator.hasNext();) {
boolean socketError = true; final Bundle namePort = (Bundle) iterator.next();
while (socketError && values.size() > i) {
final Bundle namePort = (Bundle) values.get(i);
try { try {
String srvRecordServer; String srvRecordServer;
try { try {
@ -271,48 +271,55 @@ public class XmppConnection implements Runnable {
} }
final int srvRecordPort = namePort.getInt("port"); final int srvRecordPort = namePort.getInt("port");
final String srvIpServer = namePort.getString("ip"); final String srvIpServer = namePort.getString("ip");
// if tls is true, encryption is implied and must not be started
features.encryptionEnabled = namePort.getBoolean("tls");
final InetSocketAddress addr; final InetSocketAddress addr;
if (srvIpServer != null) { if (srvIpServer != null) {
addr = new InetSocketAddress(srvIpServer, srvRecordPort); addr = new InetSocketAddress(srvIpServer, srvRecordPort);
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ ": using values from dns " + srvRecordServer + ": using values from dns " + srvRecordServer
+ "[" + srvIpServer + "]:" + srvRecordPort); + "[" + srvIpServer + "]:" + srvRecordPort + " tls: " + features.encryptionEnabled);
} else { } else {
addr = new InetSocketAddress(srvRecordServer, srvRecordPort); addr = new InetSocketAddress(srvRecordServer, srvRecordPort);
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ ": using values from dns " + ": using values from dns "
+ srvRecordServer + ":" + srvRecordPort); + srvRecordServer + ":" + srvRecordPort + " tls: " + features.encryptionEnabled);
} }
if (!features.encryptionEnabled) {
socket = new Socket(); socket = new Socket();
socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
socketError = false; } else {
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
socket = tlsFactoryVerifier.factory.createSocket();
if (socket == null) {
throw new IOException("could not initialize ssl socket");
}
setSSLSocketSecurity((SSLSocket) socket);
this.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) socket, account.getServer().getDomainpart());
this.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) socket, "xmpp-client");
socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) socket).getSession())) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
throw new SecurityException();
}
}
if(startXmpp())
break; // successfully connected to server that speaks xmpp
} catch (final Throwable e) { } catch (final Throwable e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() +"("+e.getClass().getName()+")"); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() +"("+e.getClass().getName()+")");
i++; if (!iterator.hasNext()) {
}
}
if (socketError) {
throw new UnknownHostException(); throw new UnknownHostException();
} }
} }
final OutputStream out = socket.getOutputStream(); }
tagWriter.setOutputStream(out); }
final InputStream in = socket.getInputStream();
tagReader.setInputStream(in);
tagWriter.beginDocument();
sendStartStream();
Tag nextTag;
while ((nextTag = tagReader.readTag()) != null) {
if (nextTag.isStart("stream")) {
processStream(); processStream();
break;
} else {
throw new IOException("unknown tag on connect");
}
}
if (socket.isConnected()) {
socket.close();
}
} catch (final IncompatibleServerException e) { } catch (final IncompatibleServerException e) {
this.changeStatus(Account.State.INCOMPATIBLE_SERVER); this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
} catch (final SecurityException e) { } catch (final SecurityException e) {
@ -344,6 +351,99 @@ public class XmppConnection implements Runnable {
} }
} }
/**
* Starts xmpp protocol, call after connecting to socket
* @return true if server returns with valid xmpp, false otherwise
* @throws IOException Unknown tag on connect
* @throws XmlPullParserException Bad Xml
* @throws NoSuchAlgorithmException Other error
*/
private boolean startXmpp() throws IOException, XmlPullParserException, NoSuchAlgorithmException {
tagWriter.setOutputStream(socket.getOutputStream());
tagReader.setInputStream(socket.getInputStream());
tagWriter.beginDocument();
sendStartStream();
Tag nextTag;
while ((nextTag = tagReader.readTag()) != null) {
if (nextTag.isStart("stream")) {
return true;
} else {
throw new IOException("unknown tag on connect");
}
}
if (socket.isConnected()) {
socket.close();
}
return false;
}
private void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) {
if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname);
} else {
try {
socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
} catch (Throwable e) {
// ignore any error, we just can't set the hostname...
}
}
}
private void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) {
try {
if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
// can't call directly because of @hide?
//((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")});
android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}});
} else {
final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class);
// the concatenation of 8-bit, length prefixed protocol names, just one in our case...
// http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8");
final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1];
lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow
System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length);
method.invoke(socket, new Object[]{lengthPrefixedProtocols});
}
} catch (Throwable e) {
// ignore any error, we just can't set the alpn protocol...
}
}
private static class TlsFactoryVerifier {
private final SSLSocketFactory factory;
private final HostnameVerifier verifier;
public TlsFactoryVerifier(final SSLSocketFactory factory, final HostnameVerifier verifier) throws IOException {
this.factory = factory;
this.verifier = verifier;
if (factory == null || verifier == null) {
throw new IOException("could not setup ssl");
}
}
}
private TlsFactoryVerifier getTlsFactoryVerifier() throws NoSuchAlgorithmException, KeyManagementException, IOException {
final SSLContext sc = SSLContext.getInstance("TLS");
MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
KeyManager[] keyManager;
if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
keyManager = new KeyManager[]{mKeyManager};
} else {
keyManager = null;
}
sc.init(keyManager, new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()}, mXmppConnectionService.getRNG());
final SSLSocketFactory factory = sc.getSocketFactory();
final HostnameVerifier verifier;
if (mInteractive) {
verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier());
} else {
verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier());
}
return new TlsFactoryVerifier(factory, verifier);
}
@Override @Override
public void run() { public void run() {
try { try {
@ -605,37 +705,7 @@ public class XmppConnection implements Runnable {
tagWriter.writeTag(startTLS); tagWriter.writeTag(startTLS);
} }
private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException { private void setSSLSocketSecurity(final SSLSocket sslSocket) throws NoSuchAlgorithmException {
tagReader.readTag();
try {
final SSLContext sc = SSLContext.getInstance("TLS");
MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
KeyManager[] keyManager;
if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
keyManager = new KeyManager[]{ mKeyManager };
} else {
keyManager = null;
}
sc.init(keyManager,new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()},mXmppConnectionService.getRNG());
final SSLSocketFactory factory = sc.getSocketFactory();
final HostnameVerifier verifier;
if (mInteractive) {
verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier());
} else {
verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier());
}
final InetAddress address = socket == null ? null : socket.getInetAddress();
if (factory == null || address == null || verifier == null) {
throw new IOException("could not setup ssl");
}
final SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket,address.getHostAddress(), socket.getPort(),true);
if (sslSocket == null) {
throw new IOException("could not initialize ssl socket");
}
final String[] supportProtocols; final String[] supportProtocols;
final Collection<String> supportedProtocols = new LinkedList<>( final Collection<String> supportedProtocols = new LinkedList<>(
Arrays.asList(sslSocket.getSupportedProtocols())); Arrays.asList(sslSocket.getSupportedProtocols()));
@ -650,8 +720,27 @@ public class XmppConnection implements Runnable {
if (cipherSuites.length > 0) { if (cipherSuites.length > 0) {
sslSocket.setEnabledCipherSuites(cipherSuites); sslSocket.setEnabledCipherSuites(cipherSuites);
} }
}
if (!verifier.verify(account.getServer().getDomainpart(),sslSocket.getSession())) { private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
tagReader.readTag();
try {
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
final InetAddress address = socket == null ? null : socket.getInetAddress();
if (address == null) {
throw new IOException("could not setup ssl");
}
final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true);
if (sslSocket == null) {
throw new IOException("could not initialize ssl socket");
}
setSSLSocketSecurity(sslSocket);
if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed"); Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
throw new SecurityException(); throw new SecurityException();
} }