netty5/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java
Ilya Maykov 1cd9f5e17b [openssl] fix refcount bug in OpenSslPrivateKeyMaterial ctor
Motivation:

Subclasses of `OpenSslKeyMaterial` implement `ReferenceCounted`. This means that a new object should have an initial refcount of 1. An `OpenSslPrivateKey.OpenSslPrivateKeyMaterial` object shares its refcount with the enclosing `OpenSslPrivateKey` object. This means the enclosing object's refcount must be incremented by 1 when an instance of `OpenSslPrivateKey.OpenSslPrivateKeyMaterial` is created. Otherwise, when the key material object is `release()`-ed, the refcount on the enclosing object will drop to 0 while it is still in use.

Modification:

- Increment the refcount in the constructor of `OpenSslPrivateKey.OpenSslPrivateKeyMaterial`
- Ensure we also always release the native certificates as well.

Result:

Refcount is now correct.
2019-04-29 23:11:53 +02:00

155 lines
5.0 KiB
Java

/*
* Copyright 2018 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.ssl;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.internal.tcnative.SSL;
import javax.net.ssl.SSLException;
import javax.net.ssl.X509KeyManager;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.toBIO;
/**
* Provides {@link OpenSslKeyMaterial} for a given alias.
*/
class OpenSslKeyMaterialProvider {
private final X509KeyManager keyManager;
private final String password;
OpenSslKeyMaterialProvider(X509KeyManager keyManager, String password) {
this.keyManager = keyManager;
this.password = password;
}
static void validateKeyMaterialSupported(X509Certificate[] keyCertChain, PrivateKey key, String keyPassword)
throws SSLException {
validateSupported(keyCertChain);
validateSupported(key, keyPassword);
}
private static void validateSupported(PrivateKey key, String password) throws SSLException {
if (key == null) {
return;
}
long pkeyBio = 0;
long pkey = 0;
try {
pkeyBio = toBIO(UnpooledByteBufAllocator.DEFAULT, key);
pkey = SSL.parsePrivateKey(pkeyBio, password);
} catch (Exception e) {
throw new SSLException("PrivateKey type not supported " + key.getFormat(), e);
} finally {
SSL.freeBIO(pkeyBio);
if (pkey != 0) {
SSL.freePrivateKey(pkey);
}
}
}
private static void validateSupported(X509Certificate[] certificates) throws SSLException {
if (certificates == null || certificates.length == 0) {
return;
}
long chainBio = 0;
long chain = 0;
PemEncoded encoded = null;
try {
encoded = PemX509Certificate.toPEM(UnpooledByteBufAllocator.DEFAULT, true, certificates);
chainBio = toBIO(UnpooledByteBufAllocator.DEFAULT, encoded.retain());
chain = SSL.parseX509Chain(chainBio);
} catch (Exception e) {
throw new SSLException("Certificate type not supported", e);
} finally {
SSL.freeBIO(chainBio);
if (chain != 0) {
SSL.freeX509Chain(chain);
}
if (encoded != null) {
encoded.release();
}
}
}
/**
* Returns the underlying {@link X509KeyManager} that is used.
*/
X509KeyManager keyManager() {
return keyManager;
}
/**
* Returns the {@link OpenSslKeyMaterial} or {@code null} (if none) that should be used during the handshake by
* OpenSSL.
*/
OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception {
X509Certificate[] certificates = keyManager.getCertificateChain(alias);
if (certificates == null || certificates.length == 0) {
return null;
}
PrivateKey key = keyManager.getPrivateKey(alias);
PemEncoded encoded = PemX509Certificate.toPEM(allocator, true, certificates);
long chainBio = 0;
long pkeyBio = 0;
long chain = 0;
long pkey = 0;
try {
chainBio = toBIO(allocator, encoded.retain());
chain = SSL.parseX509Chain(chainBio);
OpenSslKeyMaterial keyMaterial;
if (key instanceof OpenSslPrivateKey) {
keyMaterial = ((OpenSslPrivateKey) key).newKeyMaterial(chain, certificates);
} else {
pkeyBio = toBIO(allocator, key);
pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password);
keyMaterial = new DefaultOpenSslKeyMaterial(chain, pkey, certificates);
}
// See the chain and pkey to 0 so we will not release it as the ownership was
// transferred to OpenSslKeyMaterial.
chain = 0;
pkey = 0;
return keyMaterial;
} finally {
SSL.freeBIO(chainBio);
SSL.freeBIO(pkeyBio);
if (chain != 0) {
SSL.freeX509Chain(chain);
}
if (pkey != 0) {
SSL.freePrivateKey(pkey);
}
encoded.release();
}
}
/**
* Will be invoked once the provider should be destroyed.
*/
void destroy() {
// NOOP.
}
}