1 package org.cacert.gigi.ping;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.math.BigInteger;
7 import java.net.InetSocketAddress;
8 import java.net.Socket;
9 import java.nio.ByteBuffer;
10 import java.nio.channels.SocketChannel;
11 import java.security.KeyManagementException;
12 import java.security.KeyStore;
13 import java.security.KeyStoreException;
14 import java.security.NoSuchAlgorithmException;
15 import java.security.SecureRandom;
16 import java.util.Arrays;
18 import javax.net.ssl.SNIHostName;
19 import javax.net.ssl.SNIServerName;
20 import javax.net.ssl.SSLContext;
21 import javax.net.ssl.SSLEngine;
22 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
23 import javax.net.ssl.SSLEngineResult.Status;
24 import javax.net.ssl.SSLException;
25 import javax.net.ssl.SSLParameters;
26 import javax.net.ssl.TrustManagerFactory;
27 import javax.security.cert.X509Certificate;
29 import org.cacert.gigi.dbObjects.Certificate;
30 import org.cacert.gigi.dbObjects.Domain;
31 import org.cacert.gigi.dbObjects.User;
33 public class SSLPinger extends DomainPinger {
35 public static final String[] TYPES = new String[] {
36 "xmpp", "server-xmpp", "smtp", "imap"
39 private KeyStore truststore;
41 public SSLPinger(KeyStore truststore) {
42 this.truststore = truststore;
46 public void ping(Domain domain, String configuration, User u, int confId) {
47 try (SocketChannel sch = SocketChannel.open()) {
48 sch.socket().setSoTimeout(5000);
49 String[] parts = configuration.split(":", 2);
50 sch.socket().connect(new InetSocketAddress(domain.getSuffix(), Integer.parseInt(parts[0])), 5000);
51 if (parts.length == 2) {
54 startXMPP(sch, false, domain.getSuffix());
57 startXMPP(sch, true, domain.getSuffix());
68 String res = test(sch, domain.getSuffix(), u);
69 enterPingResult(confId, res, res, null);
71 } catch (IOException e) {
72 enterPingResult(confId, "error", "connection Failed", null);
78 private void startIMAP(SocketChannel sch) throws IOException {
79 Socket s = sch.socket();
80 InputStream is = s.getInputStream();
81 OutputStream os = s.getOutputStream();
83 os.write("ENABLE STARTTLS\r\n".getBytes("UTF-8"));
88 private void startXMPP(SocketChannel sch, boolean server, String domain) throws IOException {
89 Socket s = sch.socket();
90 InputStream is = s.getInputStream();
91 OutputStream os = s.getOutputStream();
92 os.write(("<stream:stream to=\"" + domain + "\" xmlns=\"jabber:" + (server ? "server" : "client") + "\"" + " xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">").getBytes("UTF-8"));
94 os.write("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>".getBytes("UTF-8"));
96 scanFor(is, "<proceed");
101 private void scanFor(InputStream is, String scanFor) throws IOException {
103 while (pos < scanFor.length()) {
104 if (is.read() == scanFor.charAt(pos)) {
112 private void startSMTP(SocketChannel sch) throws IOException {
113 Socket s = sch.socket();
114 InputStream is = s.getInputStream();
116 s.getOutputStream().write("EHLO ssl.pinger\r\n".getBytes("UTF-8"));
117 s.getOutputStream().flush();
119 s.getOutputStream().write("HELP\r\n".getBytes("UTF-8"));
120 s.getOutputStream().flush();
122 s.getOutputStream().write("STARTTLS\r\n".getBytes("UTF-8"));
123 s.getOutputStream().flush();
127 private void readSMTP(InputStream is) throws IOException {
129 boolean finish = true;
131 char c = (char) is.read();
135 } else if (c == '-') {
138 throw new Error("Invalid smtp: " + c);
152 private String test(SocketChannel sch, String domain, User subject) {
154 sch.socket().setSoTimeout(5000);
155 SSLContext sc = SSLContext.getInstance("SSL");
157 TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
158 tmf.init(truststore);
159 sc.init(null, tmf.getTrustManagers(), new SecureRandom());
160 } catch (KeyManagementException e) {
162 } catch (KeyStoreException e) {
165 SSLEngine se = sc.createSSLEngine();
166 ByteBuffer enc_in = ByteBuffer.allocate(se.getSession().getPacketBufferSize());
167 ByteBuffer enc_out = ByteBuffer.allocate(se.getSession().getPacketBufferSize());
168 ByteBuffer dec_in = ByteBuffer.allocate(se.getSession().getApplicationBufferSize());
169 ByteBuffer dec_out = ByteBuffer.allocate(se.getSession().getApplicationBufferSize());
170 se.setUseClientMode(true);
171 SSLParameters sp = se.getSSLParameters();
172 sp.setServerNames(Arrays.<SNIServerName>asList(new SNIHostName(domain)));
173 se.setSSLParameters(sp);
176 while (se.getHandshakeStatus() != HandshakeStatus.FINISHED && se.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) {
177 switch (se.getHandshakeStatus()) {
180 se.wrap(dec_out, enc_out);
182 while (enc_out.remaining() > 0) {
188 if (enc_in.remaining() == 0) {
193 while (se.unwrap(enc_in, dec_in).getStatus() == Status.BUFFER_UNDERFLOW) {
194 enc_in.position(enc_in.limit());
195 enc_in.limit(enc_in.capacity());
203 se.getDelegatedTask().run();
205 case NOT_HANDSHAKING:
211 X509Certificate[] peerCertificateChain = se.getSession().getPeerCertificateChain();
212 X509Certificate first = peerCertificateChain[0];
214 BigInteger serial = first.getSerialNumber();
215 Certificate c = Certificate.getBySerial(serial.toString(16));
217 return "Certificate not found";
219 if (c.getOwner().getId() != subject.getId()) {
220 return "Owner mismatch";
222 return PING_SUCCEDED;
223 } catch (NoSuchAlgorithmException e) {
224 // e.printStackTrace(); TODO log for user debugging?
225 return "Security failed";
226 } catch (SSLException e) {
227 // e.printStackTrace(); TODO log for user debugging?
228 return "Security failed";
229 } catch (IOException e) {
230 // e.printStackTrace(); TODO log for user debugging?
231 return "Connection closed";