View Javadoc
1   /*
2    * Copyright 2018 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.ssl;
17  
18  import io.netty.util.internal.EmptyArrays;
19  import io.netty.util.internal.PlatformDependent;
20  import io.netty.util.internal.SuppressJava6Requirement;
21  import io.netty.util.internal.logging.InternalLogger;
22  import io.netty.util.internal.logging.InternalLoggerFactory;
23  
24  import javax.net.ssl.SSLContext;
25  import javax.net.ssl.TrustManager;
26  import javax.net.ssl.X509ExtendedTrustManager;
27  import javax.net.ssl.X509TrustManager;
28  import java.lang.reflect.Field;
29  import java.security.AccessController;
30  import java.security.KeyManagementException;
31  import java.security.NoSuchAlgorithmException;
32  import java.security.NoSuchProviderException;
33  import java.security.PrivilegedAction;
34  import java.security.cert.CertificateException;
35  import java.security.cert.X509Certificate;
36  
37  /**
38   * Utility which allows to wrap {@link X509TrustManager} implementations with the internal implementation used by
39   * {@code SSLContextImpl} that provides extended verification.
40   *
41   * This is really a "hack" until there is an official API as requested on the in
42   * <a href="https://bugs.openjdk.java.net/projects/JDK/issues/JDK-8210843">JDK-8210843</a>.
43   */
44  @SuppressJava6Requirement(reason = "Usage guarded by java version check")
45  final class OpenSslX509TrustManagerWrapper {
46      private static final InternalLogger LOGGER = InternalLoggerFactory
47              .getInstance(OpenSslX509TrustManagerWrapper.class);
48      private static final TrustManagerWrapper WRAPPER;
49  
50      static {
51          // By default we will not do any wrapping but just return the passed in manager.
52          TrustManagerWrapper wrapper = new TrustManagerWrapper() {
53              @Override
54              public X509TrustManager wrapIfNeeded(X509TrustManager manager) {
55                  return manager;
56              }
57          };
58  
59          Throwable cause = null;
60          Throwable unsafeCause = PlatformDependent.getUnsafeUnavailabilityCause();
61          if (unsafeCause == null) {
62              SSLContext context;
63              try {
64                  context = newSSLContext();
65                  // Now init with an array that only holds a X509TrustManager. This should be wrapped into an
66                  // AbstractTrustManagerWrapper which will delegate the TrustManager itself but also do extra
67                  // validations.
68                  //
69                  // See:
70                  // - https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/
71                  //          cadea780bc76/src/share/classes/sun/security/ssl/SSLContextImpl.java#l127
72                  context.init(null, new TrustManager[] {
73                          new X509TrustManager() {
74                              @Override
75                              public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
76                                      throws CertificateException {
77                                  throw new CertificateException();
78                              }
79  
80                              @Override
81                              public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
82                                      throws CertificateException {
83                                  throw new CertificateException();
84                              }
85  
86                              @Override
87                              public X509Certificate[] getAcceptedIssuers() {
88                                  return EmptyArrays.EMPTY_X509_CERTIFICATES;
89                              }
90                          }
91                  }, null);
92              } catch (Throwable error) {
93                  context = null;
94                  cause = error;
95              }
96              if (cause != null) {
97                  LOGGER.debug("Unable to access wrapped TrustManager", cause);
98              } else {
99                  final SSLContext finalContext = context;
100                 Object maybeWrapper = AccessController.doPrivileged(new PrivilegedAction<Object>() {
101                     @Override
102                     public Object run() {
103                         try {
104                             Field contextSpiField = SSLContext.class.getDeclaredField("contextSpi");
105                             final long spiOffset = PlatformDependent.objectFieldOffset(contextSpiField);
106                             Object spi = PlatformDependent.getObject(finalContext, spiOffset);
107                             if (spi != null) {
108                                 Class<?> clazz = spi.getClass();
109 
110                                 // Let's cycle through the whole hierarchy until we find what we are looking for or
111                                 // there is nothing left in which case we will not wrap at all.
112                                 do {
113                                     try {
114                                         Field trustManagerField = clazz.getDeclaredField("trustManager");
115                                         final long tmOffset = PlatformDependent.objectFieldOffset(trustManagerField);
116                                         Object trustManager = PlatformDependent.getObject(spi, tmOffset);
117                                         if (trustManager instanceof X509ExtendedTrustManager) {
118                                             return new UnsafeTrustManagerWrapper(spiOffset, tmOffset);
119                                         }
120                                     } catch (NoSuchFieldException ignore) {
121                                         // try next
122                                     }
123                                     clazz = clazz.getSuperclass();
124                                 } while (clazz != null);
125                             }
126                             throw new NoSuchFieldException();
127                         } catch (NoSuchFieldException e) {
128                             return e;
129                         } catch (SecurityException e) {
130                             return e;
131                         }
132                     }
133                 });
134                 if (maybeWrapper instanceof Throwable) {
135                     LOGGER.debug("Unable to access wrapped TrustManager", (Throwable) maybeWrapper);
136                 } else {
137                     wrapper = (TrustManagerWrapper) maybeWrapper;
138                 }
139             }
140         } else {
141             LOGGER.debug("Unable to access wrapped TrustManager", cause);
142         }
143         WRAPPER = wrapper;
144     }
145 
146     private OpenSslX509TrustManagerWrapper() { }
147 
148     static X509TrustManager wrapIfNeeded(X509TrustManager trustManager) {
149         return WRAPPER.wrapIfNeeded(trustManager);
150     }
151 
152     private interface TrustManagerWrapper {
153         X509TrustManager wrapIfNeeded(X509TrustManager manager);
154     }
155 
156     private static SSLContext newSSLContext() throws NoSuchAlgorithmException, NoSuchProviderException {
157         // As this depends on the implementation detail we should explicit select the correct provider.
158         // See https://github.com/netty/netty/issues/10374
159         return SSLContext.getInstance("TLS", "SunJSSE");
160     }
161 
162     private static final class UnsafeTrustManagerWrapper implements TrustManagerWrapper {
163         private final long spiOffset;
164         private final long tmOffset;
165 
166         UnsafeTrustManagerWrapper(long spiOffset, long tmOffset) {
167             this.spiOffset = spiOffset;
168             this.tmOffset = tmOffset;
169         }
170 
171         @SuppressJava6Requirement(reason = "Usage guarded by java version check")
172         @Override
173         public X509TrustManager wrapIfNeeded(X509TrustManager manager) {
174             if (!(manager instanceof X509ExtendedTrustManager)) {
175                 try {
176                     SSLContext ctx = newSSLContext();
177                     ctx.init(null, new TrustManager[] { manager }, null);
178                     Object spi = PlatformDependent.getObject(ctx, spiOffset);
179                     if (spi != null) {
180                         Object tm = PlatformDependent.getObject(spi, tmOffset);
181                         if (tm instanceof X509ExtendedTrustManager) {
182                             return (X509TrustManager) tm;
183                         }
184                     }
185                 } catch (NoSuchAlgorithmException e) {
186                     // This should never happen as we did the same in the static block
187                     // before.
188                     PlatformDependent.throwException(e);
189                 } catch (KeyManagementException e) {
190                     // This should never happen as we did the same in the static block
191                     // before.
192                     PlatformDependent.throwException(e);
193                 } catch (NoSuchProviderException e) {
194                     // This should never happen as we did the same in the static block
195                     // before.
196                     PlatformDependent.throwException(e);
197                 }
198             }
199             return manager;
200         }
201     }
202 }