]> WPIA git - gigi.git/blob - src/org/cacert/gigi/Launcher.java
add: granting and revoking groups by supporters
[gigi.git] / src / org / cacert / gigi / Launcher.java
1 package org.cacert.gigi;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.IOException;
5 import java.net.InetSocketAddress;
6 import java.security.GeneralSecurityException;
7 import java.security.Key;
8 import java.security.KeyStore;
9 import java.security.KeyStoreException;
10 import java.security.NoSuchAlgorithmException;
11 import java.security.UnrecoverableKeyException;
12 import java.security.cert.Certificate;
13 import java.security.cert.CertificateException;
14 import java.security.cert.CertificateFactory;
15 import java.security.cert.X509Certificate;
16 import java.util.List;
17 import java.util.Locale;
18 import java.util.Properties;
19 import java.util.TimeZone;
20
21 import javax.net.ssl.ExtendedSSLSession;
22 import javax.net.ssl.SNIHostName;
23 import javax.net.ssl.SNIServerName;
24 import javax.net.ssl.SSLEngine;
25 import javax.net.ssl.SSLParameters;
26 import javax.net.ssl.SSLSession;
27 import javax.servlet.http.HttpServletResponse;
28
29 import org.cacert.gigi.api.GigiAPI;
30 import org.cacert.gigi.email.EmailProvider;
31 import org.cacert.gigi.natives.SetUID;
32 import org.cacert.gigi.util.CipherInfo;
33 import org.cacert.gigi.util.PEM;
34 import org.cacert.gigi.util.ServerConstants;
35 import org.eclipse.jetty.http.HttpFields;
36 import org.eclipse.jetty.http.HttpHeader;
37 import org.eclipse.jetty.http.HttpVersion;
38 import org.eclipse.jetty.server.Connector;
39 import org.eclipse.jetty.server.Handler;
40 import org.eclipse.jetty.server.HttpConfiguration;
41 import org.eclipse.jetty.server.HttpConfiguration.Customizer;
42 import org.eclipse.jetty.server.HttpConnectionFactory;
43 import org.eclipse.jetty.server.Request;
44 import org.eclipse.jetty.server.SecureRequestCustomizer;
45 import org.eclipse.jetty.server.Server;
46 import org.eclipse.jetty.server.ServerConnector;
47 import org.eclipse.jetty.server.SessionManager;
48 import org.eclipse.jetty.server.SslConnectionFactory;
49 import org.eclipse.jetty.server.handler.ContextHandler;
50 import org.eclipse.jetty.server.handler.HandlerList;
51 import org.eclipse.jetty.server.handler.HandlerWrapper;
52 import org.eclipse.jetty.server.handler.ResourceHandler;
53 import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
54 import org.eclipse.jetty.servlet.ServletContextHandler;
55 import org.eclipse.jetty.servlet.ServletHolder;
56 import org.eclipse.jetty.util.log.Log;
57 import org.eclipse.jetty.util.resource.Resource;
58 import org.eclipse.jetty.util.ssl.SslContextFactory;
59
60 public class Launcher {
61
62     class ExtendedForwarded implements Customizer {
63
64         @Override
65         public void customize(Connector connector, HttpConfiguration config, Request request) {
66             HttpFields httpFields = request.getHttpFields();
67
68             String ip = httpFields.getStringField("X-Real-IP");
69             String proto = httpFields.getStringField("X-Real-Proto");
70             String cert = httpFields.getStringField("X-Client-Cert");
71             request.setSecure("https".equals(proto));
72             request.setScheme(proto);
73             if ( !"https".equals(proto)) {
74                 cert = null;
75
76             }
77             if (cert != null) {
78                 X509Certificate[] certs = new X509Certificate[1];
79                 try {
80                     certs[0] = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(new ByteArrayInputStream(PEM.decode("CERTIFICATE", cert)));
81                     request.setAttribute("javax.servlet.request.X509Certificate", certs);
82                 } catch (CertificateException e) {
83                     e.printStackTrace();
84                 }
85             }
86             if (ip != null) {
87                 String[] parts = ip.split(":");
88                 if (parts.length == 2) {
89                     request.setRemoteAddr(InetSocketAddress.createUnresolved(parts[0], Integer.parseInt(parts[1])));
90                 }
91             }
92
93         }
94     }
95
96     public static void main(String[] args) throws Exception {
97         System.setProperty("jdk.tls.ephemeralDHKeySize", "4096");
98         new Launcher().boot();
99     }
100
101     Server s;
102
103     GigiConfig conf;
104
105     public synchronized void boot() throws Exception {
106         Locale.setDefault(Locale.ENGLISH);
107         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
108
109         conf = GigiConfig.parse(System.in);
110         ServerConstants.init(conf.getMainProps());
111         initEmails(conf);
112
113         s = new Server();
114
115         initConnectors();
116         initHandlers();
117
118         s.start();
119         if ((ServerConstants.getSecurePort() <= 1024 || ServerConstants.getPort() <= 1024) && !System.getProperty("os.name").toLowerCase().contains("win")) {
120             SetUID uid = new SetUID();
121             if ( !uid.setUid(65536 - 2, 65536 - 2).getSuccess()) {
122                 Log.getLogger(Launcher.class).warn("Couldn't set uid!");
123             }
124         }
125     }
126
127     private HttpConfiguration createHttpConfiguration() {
128         // SSL HTTP Configuration
129         HttpConfiguration httpsConfig = new HttpConfiguration();
130         httpsConfig.setSendServerVersion(false);
131         httpsConfig.setSendXPoweredBy(false);
132         return httpsConfig;
133     }
134
135     private void initConnectors() throws GeneralSecurityException, IOException {
136         HttpConfiguration httpConfig = createHttpConfiguration();
137         if (conf.getMainProps().getProperty("proxy", "false").equals("true")) {
138             httpConfig.addCustomizer(new ExtendedForwarded());
139             s.setConnectors(new Connector[] {
140                 ConnectorsLauncher.createConnector(conf, s, httpConfig, false)
141             });
142         } else {
143             HttpConfiguration httpsConfig = createHttpConfiguration();
144             // for client-cert auth
145             httpsConfig.addCustomizer(new SecureRequestCustomizer());
146             s.setConnectors(new Connector[] {
147                     ConnectorsLauncher.createConnector(conf, s, httpsConfig, true), ConnectorsLauncher.createConnector(conf, s, httpConfig, false)
148             });
149         }
150     }
151
152     private void initEmails(GigiConfig conf) throws GeneralSecurityException, IOException, KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
153         KeyStore privateStore = conf.getPrivateStore();
154         Certificate mail = privateStore.getCertificate("mail");
155         Key k = privateStore.getKey("mail", conf.getPrivateStorePw().toCharArray());
156         EmailProvider.initSystem(conf.getMainProps(), mail, k);
157     }
158
159     private static class ConnectorsLauncher {
160
161         private ConnectorsLauncher() {}
162
163         protected static ServerConnector createConnector(GigiConfig conf, Server s, HttpConfiguration httpConfig, boolean doHttps) throws GeneralSecurityException, IOException {
164             ServerConnector connector;
165             if (doHttps) {
166                 connector = new ServerConnector(s, createConnectionFactory(conf), new HttpConnectionFactory(httpConfig));
167             } else {
168                 connector = new ServerConnector(s, new HttpConnectionFactory(httpConfig));
169             }
170             connector.setHost(conf.getMainProps().getProperty("host"));
171             if (doHttps) {
172                 connector.setPort(ServerConstants.getSecurePort());
173             } else {
174                 connector.setPort(ServerConstants.getPort());
175             }
176             connector.setAcceptQueueSize(100);
177             return connector;
178         }
179
180         private static SslConnectionFactory createConnectionFactory(GigiConfig conf) throws GeneralSecurityException, IOException {
181             final SslContextFactory sslContextFactory = generateSSLContextFactory(conf, "www");
182             final SslContextFactory secureContextFactory = generateSSLContextFactory(conf, "secure");
183             secureContextFactory.setWantClientAuth(true);
184             secureContextFactory.setNeedClientAuth(true);
185             final SslContextFactory staticContextFactory = generateSSLContextFactory(conf, "static");
186             final SslContextFactory apiContextFactory = generateSSLContextFactory(conf, "api");
187             apiContextFactory.setWantClientAuth(true);
188             try {
189                 secureContextFactory.start();
190                 staticContextFactory.start();
191                 apiContextFactory.start();
192             } catch (Exception e) {
193                 e.printStackTrace();
194             }
195             return new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()) {
196
197                 @Override
198                 public boolean shouldRestartSSL() {
199                     return true;
200                 }
201
202                 @Override
203                 public SSLEngine restartSSL(SSLSession sslSession) {
204                     SSLEngine e2 = null;
205                     if (sslSession instanceof ExtendedSSLSession) {
206                         ExtendedSSLSession es = (ExtendedSSLSession) sslSession;
207                         List<SNIServerName> names = es.getRequestedServerNames();
208                         for (SNIServerName sniServerName : names) {
209                             if (sniServerName instanceof SNIHostName) {
210                                 SNIHostName host = (SNIHostName) sniServerName;
211                                 String hostname = host.getAsciiName();
212                                 if (hostname.equals(ServerConstants.getWwwHostName())) {
213                                     e2 = sslContextFactory.newSSLEngine();
214                                 } else if (hostname.equals(ServerConstants.getStaticHostName())) {
215                                     e2 = staticContextFactory.newSSLEngine();
216                                 } else if (hostname.equals(ServerConstants.getSecureHostName())) {
217                                     e2 = secureContextFactory.newSSLEngine();
218                                 } else if (hostname.equals(ServerConstants.getApiHostName())) {
219                                     e2 = apiContextFactory.newSSLEngine();
220                                 }
221                                 break;
222                             }
223                         }
224                     }
225                     if (e2 == null) {
226                         e2 = sslContextFactory.newSSLEngine(sslSession.getPeerHost(), sslSession.getPeerPort());
227                     }
228                     e2.setUseClientMode(false);
229                     return e2;
230                 }
231             };
232         }
233
234         private static SslContextFactory generateSSLContextFactory(GigiConfig conf, String alias) throws GeneralSecurityException, IOException {
235             SslContextFactory scf = new SslContextFactory() {
236
237                 String[] ciphers = null;
238
239                 @Override
240                 public void customize(SSLEngine sslEngine) {
241                     super.customize(sslEngine);
242
243                     SSLParameters ssl = sslEngine.getSSLParameters();
244                     ssl.setUseCipherSuitesOrder(true);
245                     if (ciphers == null) {
246                         ciphers = CipherInfo.filter(sslEngine.getSupportedCipherSuites());
247                     }
248
249                     ssl.setCipherSuites(ciphers);
250                     sslEngine.setSSLParameters(ssl);
251
252                 }
253
254             };
255             scf.setRenegotiationAllowed(false);
256
257             scf.setProtocol("TLS");
258             scf.setIncludeProtocols("TLSv1", "TLSv1.1", "TLSv1.2");
259             scf.setTrustStore(conf.getTrustStore());
260             KeyStore privateStore = conf.getPrivateStore();
261             scf.setKeyStorePassword(conf.getPrivateStorePw());
262             scf.setKeyStore(privateStore);
263             scf.setCertAlias(alias);
264             return scf;
265         }
266     }
267
268     private void initHandlers() throws GeneralSecurityException, IOException {
269         HandlerList hl = new HandlerList();
270         hl.setHandlers(new Handler[] {
271                 ContextLauncher.generateStaticContext(), ContextLauncher.generateGigiContexts(conf.getMainProps(), conf.getTrustStore()), ContextLauncher.generateAPIContext()
272         });
273         s.setHandler(hl);
274     }
275
276     private static class ContextLauncher {
277
278         private ContextLauncher() {}
279
280         protected static Handler generateGigiContexts(Properties conf, KeyStore trust) {
281             ServletHolder webAppServlet = new ServletHolder(new Gigi(conf, trust));
282
283             ContextHandler ch = generateGigiServletContext(webAppServlet);
284             ch.setVirtualHosts(new String[] {
285                 ServerConstants.getWwwHostName()
286             });
287             ContextHandler chSecure = generateGigiServletContext(webAppServlet);
288             chSecure.setVirtualHosts(new String[] {
289                 ServerConstants.getSecureHostName()
290             });
291
292             HandlerList hl = new HandlerList();
293             hl.setHandlers(new Handler[] {
294                     ch, chSecure
295             });
296             return hl;
297         }
298
299         private static ContextHandler generateGigiServletContext(ServletHolder webAppServlet) {
300             final ResourceHandler rh = generateResourceHandler();
301             rh.setResourceBase("static/www");
302
303             HandlerWrapper hw = new PolicyRedirector();
304             hw.setHandler(rh);
305
306             ServletContextHandler servlet = new ServletContextHandler(ServletContextHandler.SESSIONS);
307             servlet.setInitParameter(SessionManager.__SessionCookieProperty, "CACert-Session");
308             servlet.addServlet(webAppServlet, "/*");
309             ErrorPageErrorHandler epeh = new ErrorPageErrorHandler();
310             epeh.addErrorPage(404, "/error");
311             epeh.addErrorPage(403, "/denied");
312             servlet.setErrorHandler(epeh);
313
314             HandlerList hl = new HandlerList();
315             hl.setHandlers(new Handler[] {
316                     hw, servlet
317             });
318
319             ContextHandler ch = new ContextHandler();
320             ch.setHandler(hl);
321             return ch;
322         }
323
324         protected static Handler generateStaticContext() {
325             final ResourceHandler rh = generateResourceHandler();
326             rh.setResourceBase("static/static");
327
328             ContextHandler ch = new ContextHandler();
329             ch.setHandler(rh);
330             ch.setVirtualHosts(new String[] {
331                 ServerConstants.getStaticHostName()
332             });
333
334             return ch;
335         }
336
337         private static ResourceHandler generateResourceHandler() {
338             ResourceHandler rh = new ResourceHandler() {
339
340                 @Override
341                 protected void doResponseHeaders(HttpServletResponse response, Resource resource, String mimeType) {
342                     super.doResponseHeaders(response, resource, mimeType);
343                     response.setDateHeader(HttpHeader.EXPIRES.asString(), System.currentTimeMillis() + 1000L * 60 * 60 * 24 * 7);
344                 }
345             };
346             rh.setEtags(true);
347             return rh;
348         }
349
350         protected static Handler generateAPIContext() {
351             ServletContextHandler sch = new ServletContextHandler();
352
353             sch.addVirtualHosts(new String[] {
354                 ServerConstants.getApiHostName()
355             });
356             sch.addServlet(new ServletHolder(new GigiAPI()), "/*");
357             return sch;
358         }
359
360     }
361
362 }