]> WPIA git - gigi.git/blob - src/org/cacert/gigi/ping/SSLPinger.java
81c739372ca4a2925b2c852f9be92b263d5c9255
[gigi.git] / src / org / cacert / gigi / ping / SSLPinger.java
1 package org.cacert.gigi.ping;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.net.InetSocketAddress;
7 import java.net.Socket;
8 import java.nio.ByteBuffer;
9 import java.nio.channels.SocketChannel;
10 import java.security.NoSuchAlgorithmException;
11 import java.util.Arrays;
12
13 import javax.net.ssl.SNIHostName;
14 import javax.net.ssl.SNIServerName;
15 import javax.net.ssl.SSLContext;
16 import javax.net.ssl.SSLEngine;
17 import javax.net.ssl.SSLEngineResult.Status;
18 import javax.net.ssl.SSLException;
19 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
20 import javax.net.ssl.SSLParameters;
21 import javax.security.cert.X509Certificate;
22
23 import org.cacert.gigi.Domain;
24 import org.cacert.gigi.User;
25
26 public class SSLPinger extends DomainPinger {
27
28     public static final String[] TYPES = new String[] {
29             "xmpp", "server-xmpp", "smtp", "imap"
30     };
31
32     @Override
33     public String ping(Domain domain, String configuration, User u) {
34         try {
35             SocketChannel sch = SocketChannel.open();
36             String[] parts = configuration.split(":", 2);
37             sch.connect(new InetSocketAddress(domain.getSuffix(), Integer.parseInt(parts[0])));
38             if (parts.length == 2) {
39                 switch (parts[1]) {
40                 case "xmpp":
41                     startXMPP(sch, false, domain.getSuffix());
42                     break;
43                 case "server-xmpp":
44                     startXMPP(sch, true, domain.getSuffix());
45                     break;
46                 case "smtp":
47                     startSMTP(sch);
48                     break;
49                 case "imap":
50                     startIMAP(sch);
51                     break;
52
53                 }
54             }
55             return test(sch, domain.getSuffix());
56         } catch (IOException e) {
57             return "Connecton failed";
58         }
59
60     }
61
62     private void startIMAP(SocketChannel sch) throws IOException {
63         Socket s = sch.socket();
64         InputStream is = s.getInputStream();
65         OutputStream os = s.getOutputStream();
66         scanFor(is, "\n");
67         os.write("ENABLE STARTTLS\r\n".getBytes());
68         os.flush();
69         scanFor(is, "\n");
70     }
71
72     private void startXMPP(SocketChannel sch, boolean server, String domain) throws IOException {
73         Socket s = sch.socket();
74         InputStream is = s.getInputStream();
75         OutputStream os = s.getOutputStream();
76         os.write(("<stream:stream to=\"" + domain + "\" xmlns=\"jabber:" + (server ? "server" : "client") + "\"" + " xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">").getBytes());
77         os.flush();
78         os.write("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>".getBytes());
79         os.flush();
80         scanFor(is, "<proceed");
81         scanFor(is, ">");
82
83     }
84
85     private void scanFor(InputStream is, String scanFor) throws IOException {
86         int pos = 0;
87         while (pos < scanFor.length()) {
88             if (is.read() == scanFor.charAt(pos)) {
89                 pos++;
90             } else {
91                 pos = 0;
92             }
93         }
94     }
95
96     private void startSMTP(SocketChannel sch) throws IOException {
97         Socket s = sch.socket();
98         InputStream is = s.getInputStream();
99         readSMTP(is);
100         s.getOutputStream().write("EHLO ssl.pinger\r\n".getBytes());
101         s.getOutputStream().flush();
102         readSMTP(is);
103         s.getOutputStream().write("HELP\r\n".getBytes());
104         s.getOutputStream().flush();
105         readSMTP(is);
106         s.getOutputStream().write("STARTTLS\r\n".getBytes());
107         s.getOutputStream().flush();
108         readSMTP(is);
109     }
110
111     private void readSMTP(InputStream is) throws IOException {
112         int counter = 0;
113         boolean finish = true;
114         while (true) {
115             char c = (char) is.read();
116             if (counter == 3) {
117                 if (c == ' ') {
118                     finish = true;
119                 } else if (c == '-') {
120                     finish = false;
121                 } else {
122                     throw new Error("Invalid smtp: " + c);
123                 }
124             }
125             if (c == '\n') {
126                 if (finish) {
127                     return;
128                 }
129                 counter = 0;
130             } else {
131                 counter++;
132             }
133         }
134     }
135
136     private String test(SocketChannel sch, String domain) {
137         try {
138             SSLContext sc = SSLContext.getDefault();
139             SSLEngine se = sc.createSSLEngine();
140             ByteBuffer enc_in = ByteBuffer.allocate(se.getSession().getPacketBufferSize());
141             ByteBuffer enc_out = ByteBuffer.allocate(se.getSession().getPacketBufferSize());
142             ByteBuffer dec_in = ByteBuffer.allocate(se.getSession().getApplicationBufferSize());
143             ByteBuffer dec_out = ByteBuffer.allocate(se.getSession().getApplicationBufferSize());
144             se.setUseClientMode(true);
145             SSLParameters sp = se.getSSLParameters();
146             sp.setServerNames(Arrays.<SNIServerName>asList(new SNIHostName(domain)));
147             se.setSSLParameters(sp);
148             se.beginHandshake();
149             enc_in.limit(0);
150             while (se.getHandshakeStatus() != HandshakeStatus.FINISHED && se.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) {
151                 switch (se.getHandshakeStatus()) {
152                 case NEED_WRAP:
153                     dec_out.limit(0);
154                     se.wrap(dec_out, enc_out);
155                     enc_out.flip();
156                     while (enc_out.remaining() > 0) {
157                         sch.write(enc_out);
158                     }
159                     enc_out.clear();
160                     break;
161                 case NEED_UNWRAP:
162                     if (enc_in.remaining() == 0) {
163                         enc_in.clear();
164                         sch.read(enc_in);
165                         enc_in.flip();
166                     }
167                     while (se.unwrap(enc_in, dec_in).getStatus() == Status.BUFFER_UNDERFLOW) {
168                         enc_in.position(enc_in.limit());
169                         enc_in.limit(enc_in.capacity());
170                         sch.read(enc_in);
171                         enc_in.flip();
172                     }
173                     enc_in.compact();
174                     enc_in.flip();
175                     break;
176                 case NEED_TASK:
177                     se.getDelegatedTask().run();
178                     break;
179                 case NOT_HANDSHAKING:
180                 case FINISHED:
181
182                 }
183
184             }
185             System.out.println("completed");
186             System.out.println(se.getSession().getCipherSuite());
187             X509Certificate[] peerCertificateChain = se.getSession().getPeerCertificateChain();
188             for (X509Certificate x509Certificate : peerCertificateChain) {
189                 System.out.println(x509Certificate.getSubjectDN().getName());
190             }
191             return PING_SUCCEDED;
192         } catch (NoSuchAlgorithmException e) {
193             e.printStackTrace();
194             return "Security failed";
195         } catch (SSLException e) {
196             e.printStackTrace();
197             return "Security failed";
198         } catch (IOException e) {
199             e.printStackTrace();
200             return "Connection closed";
201         }
202     }
203 }