001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with this
004 * work for additional information regarding copyright ownership. The ASF
005 * licenses this file to you under the Apache License, Version 2.0 (the
006 * "License"); you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 * License for the specific language governing permissions and limitations under
015 * the License.
016 */
017 package org.apache.activemq.transport.https;
018
019 import java.io.IOException;
020 import java.net.ServerSocket;
021 import java.security.KeyManagementException;
022 import java.security.NoSuchAlgorithmException;
023 import java.security.NoSuchProviderException;
024 import java.security.Principal;
025 import java.util.Collections;
026 import java.util.List;
027 import java.util.Random;
028 import javax.net.ssl.SSLContext;
029 import javax.net.ssl.SSLServerSocket;
030 import javax.net.ssl.SSLSocket;
031
032 import org.eclipse.jetty.http.HttpSchemes;
033 import org.eclipse.jetty.io.EndPoint;
034 import org.eclipse.jetty.server.Request;
035 import org.eclipse.jetty.server.ssl.ServletSSL;
036 import org.eclipse.jetty.server.ssl.SslSocketConnector;
037 import org.eclipse.jetty.util.ssl.SslContextFactory;
038 import org.slf4j.Logger;
039 import org.slf4j.LoggerFactory;
040
041 /**
042 * Extend Jetty's {@link SslSocketConnector} to optionally also provide
043 * Kerberos5ized SSL sockets. The only change in behavior from superclass is
044 * that we no longer honor requests to turn off NeedAuthentication when running
045 * with Kerberos support.
046 */
047 public class Krb5AndCertsSslSocketConnector extends SslSocketConnector {
048 public static final List<String> KRB5_CIPHER_SUITES = Collections.unmodifiableList(Collections.singletonList("TLS_KRB5_WITH_3DES_EDE_CBC_SHA"));
049 static {
050 System.setProperty("https.cipherSuites", KRB5_CIPHER_SUITES.get(0));
051 }
052
053 private static final Logger LOG = LoggerFactory.getLogger(Krb5AndCertsSslSocketConnector.class);
054
055 private static final String REMOTE_PRINCIPAL = "remote_principal";
056
057 public enum MODE {
058 KRB, CERTS, BOTH
059 } // Support Kerberos, certificates or both?
060
061 private boolean useKrb;
062 private boolean useCerts;
063
064 public Krb5AndCertsSslSocketConnector() {
065 // By default, stick to cert based authentication
066 super();
067 useKrb = false;
068 useCerts = true;
069 setPasswords();
070 }
071
072 public static boolean isKrb(String mode) {
073 return mode == MODE.KRB.toString() || mode == MODE.BOTH.toString();
074 }
075
076 public void setMode(String mode) {
077 useKrb = mode == MODE.KRB.toString() || mode == MODE.BOTH.toString();
078 useCerts = mode == MODE.CERTS.toString() || mode == MODE.BOTH.toString();
079 logIfDebug("useKerb = " + useKrb + ", useCerts = " + useCerts);
080 }
081
082 // If not using Certs, set passwords to random gibberish or else
083 // Jetty will actually prompt the user for some.
084 private void setPasswords() {
085 if (!useCerts) {
086 Random r = new Random();
087 System.setProperty("jetty.ssl.password", String.valueOf(r.nextLong()));
088 System.setProperty("jetty.ssl.keypassword", String.valueOf(r.nextLong()));
089 }
090 }
091
092 @Override
093 public SslContextFactory getSslContextFactory() {
094 final SslContextFactory factory = super.getSslContextFactory();
095
096 if (useCerts) {
097 return factory;
098 }
099
100 try {
101 SSLContext context = factory.getProvider() == null ? SSLContext.getInstance(factory.getProtocol()) : SSLContext.getInstance(factory.getProtocol(),
102 factory.getProvider());
103 context.init(null, null, null);
104 factory.setSslContext(context);
105 } catch (NoSuchAlgorithmException e) {
106 } catch (NoSuchProviderException e) {
107 } catch (KeyManagementException e) {
108 }
109
110 return factory;
111 }
112
113 /*
114 * (non-Javadoc)
115 *
116 * @see
117 * org.mortbay.jetty.security.SslSocketConnector#newServerSocket(java.lang
118 * .String, int, int)
119 */
120 @Override
121 protected ServerSocket newServerSocket(String host, int port, int backlog) throws IOException {
122 logIfDebug("Creating new KrbServerSocket for: " + host);
123 SSLServerSocket ss = null;
124
125 if (useCerts) // Get the server socket from the SSL super impl
126 ss = (SSLServerSocket) super.newServerSocket(host, port, backlog);
127 else { // Create a default server socket
128 try {
129 ss = (SSLServerSocket) super.newServerSocket(host, port, backlog);
130 } catch (Exception e) {
131 LOG.warn("Could not create KRB5 Listener", e);
132 throw new IOException("Could not create KRB5 Listener: " + e.toString());
133 }
134 }
135
136 // Add Kerberos ciphers to this socket server if needed.
137 if (useKrb) {
138 ss.setNeedClientAuth(true);
139 String[] combined;
140 if (useCerts) { // combine the cipher suites
141 String[] certs = ss.getEnabledCipherSuites();
142 combined = new String[certs.length + KRB5_CIPHER_SUITES.size()];
143 System.arraycopy(certs, 0, combined, 0, certs.length);
144 System.arraycopy(KRB5_CIPHER_SUITES.toArray(new String[0]), 0, combined, certs.length, KRB5_CIPHER_SUITES.size());
145 } else { // Just enable Kerberos auth
146 combined = KRB5_CIPHER_SUITES.toArray(new String[0]);
147 }
148
149 ss.setEnabledCipherSuites(combined);
150 }
151 return ss;
152 };
153
154 @Override
155 public void customize(EndPoint endpoint, Request request) throws IOException {
156 if (useKrb) { // Add Kerberos-specific info
157 SSLSocket sslSocket = (SSLSocket) endpoint.getTransport();
158 Principal remotePrincipal = sslSocket.getSession().getPeerPrincipal();
159 logIfDebug("Remote principal = " + remotePrincipal);
160 request.setScheme(HttpSchemes.HTTPS);
161 request.setAttribute(REMOTE_PRINCIPAL, remotePrincipal);
162
163 if (!useCerts) { // Add extra info that would have been added by
164 // super
165 String cipherSuite = sslSocket.getSession().getCipherSuite();
166 Integer keySize = Integer.valueOf(ServletSSL.deduceKeyLength(cipherSuite));
167 ;
168
169 request.setAttribute("javax.servlet.request.cipher_suite", cipherSuite);
170 request.setAttribute("javax.servlet.request.key_size", keySize);
171 }
172 }
173
174 if (useCerts)
175 super.customize(endpoint, request);
176 }
177
178 private void logIfDebug(String s) {
179 if (LOG.isDebugEnabled())
180 LOG.debug(s);
181 }
182 }