
io.apigee.trireme.node12.modules.TLSWrapStream Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2015 Apigee Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.apigee.trireme.node12.modules;
import io.apigee.trireme.core.NodeRuntime;
import io.apigee.trireme.core.Utils;
import io.apigee.trireme.core.internal.AbstractIdObject;
import io.apigee.trireme.core.internal.CertificateParser;
import io.apigee.trireme.core.internal.IdPropertyMap;
import io.apigee.trireme.core.modules.Buffer;
import io.apigee.trireme.core.modules.crypto.SecureContextImpl;
import io.apigee.trireme.kernel.Callback;
import io.apigee.trireme.kernel.crypto.SSLCiphers;
import io.apigee.trireme.kernel.handles.SocketHandle;
import io.apigee.trireme.kernel.handles.TLSHandle;
import io.apigee.trireme.kernel.tls.TLSConnection;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.Undefined;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import java.security.cert.X509Certificate;
import static io.apigee.trireme.core.ArgUtils.*;
public class TLSWrapStream
extends AbstractIdObject
{
private static final Logger log = LoggerFactory.getLogger(TLSWrapStream.class);
public static final String CLASS_NAME = "TLSWrap";
private static final IdPropertyMap props = new IdPropertyMap(CLASS_NAME);
private static final int
Id_receive = 2,
Id_start = 3,
Id_setVerifyMode = 4,
Id_enableSessionCallbacks = 5,
Id_enableHelloParser = 6,
Id_getPeerCertificate = 7,
Id_getSession = 8,
Id_setSession = 9,
Id_loadSession = 10,
Id_isSessionReused = 11,
Id_isInitFinished = 12,
Id_verifyError = 13,
Id_getCurrentCipher = 14,
Id_endParser = 15,
Id_renegotiate = 16,
Id_shutdown = 17,
Id_getTLSTicket = 18,
Id_newSessionDone = 19,
Id_setOCSPResponse = 20,
Id_requestOCSP = 21,
Id_setServerName = 22,
Id_getServerName = 23,
Id_onhandshakestart = 1,
Id_onhandshakedone = 2,
Id_onclienthello = 3,
Id_onnewsession = 4,
Id_onerror = 5;
static {
props.addMethod("start", Id_start, 0);
props.addMethod("receive", Id_receive, 1);
props.addMethod("setVerifyMode", Id_setVerifyMode, 2);
props.addMethod("enableSessionCallbacks", Id_enableSessionCallbacks, 0);
props.addMethod("enableHelloParser", Id_enableHelloParser, 0);
props.addMethod("getPeerCertificate", Id_getPeerCertificate, 0);
props.addMethod("getSession", Id_getSession, 0);
props.addMethod("setSession", Id_setSession, 1);
props.addMethod("loadSession", Id_loadSession, 0);
props.addMethod("isSessionReused", Id_isSessionReused, 0);
props.addMethod("isInitFinished", Id_isInitFinished, 0);
props.addMethod("verifyError", Id_verifyError, 0);
props.addMethod("getCurrentCipher", Id_getCurrentCipher, 0);
props.addMethod("endParser", Id_endParser, 0);
props.addMethod("renegotiate", Id_renegotiate, 0);
props.addMethod("shutdown", Id_shutdown, 0);
props.addMethod("getTLSTicket", Id_getTLSTicket, 0);
props.addMethod("newSessionDone", Id_newSessionDone, 0);
props.addMethod("setOCSPResponse", Id_setOCSPResponse, 1);
props.addMethod("requestOCSP", Id_requestOCSP, 0);
props.addMethod("setServername", Id_setServerName, 1);
props.addMethod("getServername", Id_getServerName, 0);
props.addProperty("onhandshakestart", Id_onhandshakestart, 0);
props.addProperty("onhandshakedone", Id_onhandshakedone, 0);
props.addProperty("onclienthello", Id_onclienthello, 0);
props.addProperty("onnewsession", Id_onnewsession, 0);
props.addProperty("onerror", Id_onerror, 0);
}
private final TCPWrap.TCPImpl stream;
private final SecureContextImpl ctx;
private final boolean isServer;
private TLSConnection tls;
private Function onHandshakeStart;
private Function onHandshakeDone;
private Function onClientHello;
private Function onNewSession;
private Function onError;
private String serverName;
@Override
protected TLSWrapStream defaultConstructor()
{
throw new AssertionError();
}
@Override
protected TLSWrapStream defaultConstructor(Context cx, Object[] args)
{
TCPWrap.TCPImpl tcp =
objArg(cx, this, args, 0, TCPWrap.TCPImpl.class, true);
SecureContextImpl ctx =
objArg(cx, this, args, 1, SecureContextImpl.class, true);
boolean isServer =
booleanArg(args, 2, false);
return new TLSWrapStream(tcp, ctx, isServer);
}
/**
* Called after constructor to actually set things up.
*/
void init(Context cx, NodeRuntime runtime)
{
tls = new TLSConnection(runtime, isServer,
// TODO serverName
null,
// TODO port
0);
SSLContext tlsCtx = ctx.makeContext(cx, this);
tls.init(tlsCtx, ctx.getCiphers(), ctx.getTrustManager());
SocketHandle handle = (SocketHandle)stream.getHandle();
TLSHandle newHandle = new TLSHandle(handle, tls);
stream.setSocketHandle(newHandle);
}
public TLSWrapStream()
{
super(props);
stream = null;
ctx = null;
isServer = false;
}
private TLSWrapStream(TCPWrap.TCPImpl stream, SecureContextImpl ctx, boolean isServer)
{
super(props);
this.stream = stream;
this.ctx = ctx;
this.isServer = isServer;
}
@Override
public Object getInstanceIdValue(int id)
{
switch (id)
{
case Id_onhandshakestart:
return onHandshakeStart;
case Id_onhandshakedone:
return onHandshakeDone;
case Id_onclienthello:
return onClientHello;
case Id_onnewsession:
return onNewSession;
case Id_onerror:
return onError;
default:
return super.getInstanceIdValue(id);
}
}
@Override
public void setInstanceIdValue(int id, Object val)
{
Function f;
switch (id)
{
case Id_onhandshakestart:
f = (Function)val;
onHandshakeStart = f;
if (f == null) {
tls.setHandshakeStartCallback(null);
} else {
tls.setHandshakeStartCallback(new FunctionCallerCallback("handshakeStart", this, f));
}
break;
case Id_onhandshakedone:
f = (Function)val;
onHandshakeDone = f;
if (f == null) {
tls.setHandshakeDoneCallback(null);
} else {
tls.setHandshakeDoneCallback(new FunctionCallerCallback("handshakeEnd", this, f));
}
break;
case Id_onerror:
setOnError((Function)val);
break;
case Id_onclienthello:
onClientHello = (Function)val;
break;
case Id_onnewsession:
onNewSession = (Function)val;
break;
default:
super.setInstanceIdValue(id, val);
break;
}
}
private void setOnError(final Function f)
{
onError = f;
if (f == null) {
tls.setErrorCallback(null);
} else {
tls.setErrorCallback(new Callback() {
@Override
public void call(SSLException ex)
{
if (log.isDebugEnabled()) {
log.debug("Received TLS error {}", ex);
}
Context cx = Context.getCurrentContext();
Scriptable err = Utils.makeErrorObject(cx, TLSWrapStream.this, ex.toString());
f.call(Context.getCurrentContext(),
TLSWrapStream.this, TLSWrapStream.this,
new Object[] { err });
}
});
}
}
@Override
protected Object prototypeCall(int id, Context cx, Scriptable scope, Object[] args)
{
switch (id) {
case Id_start:
start(cx);
break;
case Id_receive:
receive(cx, args);
break;
case Id_setVerifyMode:
setVerifyMode(cx, args);
break;
case Id_getPeerCertificate:
return getPeerCertificate(cx);
case Id_isInitFinished:
return tls.isInitFinished();
case Id_verifyError:
return verifyError(cx);
case Id_getCurrentCipher:
return getCurrentCipher(cx);
case Id_shutdown:
tls.shutdown(null);
break;
case Id_setServerName:
this.serverName = stringArg(args, 0);
break;
case Id_getServerName:
return serverName;
case Id_endParser:
case Id_renegotiate:
// Not sure what if anything to do
break;
// Not implemented or won't be implemented:
case Id_isSessionReused:
return false;
case Id_enableSessionCallbacks:
case Id_enableHelloParser:
case Id_getSession:
case Id_setSession:
case Id_loadSession:
case Id_setOCSPResponse:
case Id_requestOCSP:
case Id_newSessionDone:
case Id_getTLSTicket:
throw Utils.makeError(cx, this, "Feature not implemented");
default:
return super.prototypeCall(id, cx, scope, args);
}
return Undefined.instance;
}
/**
* Called to start the handshake.
*/
private void start(Context cx)
{
tls.start();
}
/**
* Called by tls_wrap when there appears to be extra data on the socket to process.
*/
private void receive(Context cx, Object[] args)
{
Buffer.BufferImpl buf = objArg(cx, this, args, 0, Buffer.BufferImpl.class, true);
if (log.isTraceEnabled()) {
log.trace("Received {} bytes directly from network", buf.getLength());
}
tls.unwrap(buf.getBuffer(), null);
}
private void setVerifyMode(Context cx, Object[] args)
{
boolean requestCert = booleanArg(args, 0);
boolean rejectUnauthorized = booleanArg(args, 1);
tls.setVerificationMode(requestCert, rejectUnauthorized);
}
private Object getPeerCertificate(Context cx)
{
X509Certificate cert = tls.getPeerCertificate();
if (cert == null) {
return Undefined.instance;
}
return CertificateParser.get().parseWithStrings(cx, this, cert);
}
private Object verifyError(Context cx)
{
SSLException ve = tls.getVerifyError();
if (ve == null) {
return Undefined.instance;
}
return Utils.makeErrorObject(cx, this, ve.toString());
}
private Object getCurrentCipher(Context cx)
{
String cipherSuite = tls.getCipherSuite();
if (cipherSuite == null) {
return Undefined.instance;
}
SSLCiphers.Ciph cipher = SSLCiphers.get().getJavaCipher(cipherSuite);
Scriptable c = cx.newObject(this);
c.put("name", c, (cipher == null ? "unknown" : cipher.getSslName()));
c.put("version", c, tls.getProtocol());
c.put("javaCipher", c, cipherSuite);
return c;
}
private static final class FunctionCallerCallback
implements Callback
{
private final String name;
private final Function f;
private final Scriptable scope;
FunctionCallerCallback(String name, Scriptable scope, Function f)
{
this.name = name;
this.scope = scope;
this.f = f;
}
@Override
public void call(Void val)
{
if (log.isDebugEnabled()) {
log.debug("Received TLS callback for \"{}\"", name);
}
f.call(Context.getCurrentContext(), scope, scope, Context.emptyArgs);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy