diff --git a/security/jss/org/mozilla/jss/ssl/SSLServerSocket.c b/security/jss/org/mozilla/jss/ssl/SSLServerSocket.c new file mode 100644 index 000000000000..e6bffdcf676e --- /dev/null +++ b/security/jss/org/mozilla/jss/ssl/SSLServerSocket.c @@ -0,0 +1,258 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Security Services for Java. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2001 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "jssl.h" + +JNIEXPORT jbyteArray JNICALL +Java_org_mozilla_jss_ssl_SSLServerSocket_socketCreate + (JNIEnv *env, jobject self) +{ + return JSSL_socketCreate(env, self, NULL, NULL); +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLServerSocket_socketBind + (JNIEnv *env, jobject self, jbyteArray addrBA, jint port) +{ + JSSL_socketBind(env, self, addrBA, port); +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLServerSocket_socketListen + (JNIEnv *env, jobject self, jint backlog) +{ + JSSL_SocketData *sock; + + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; + + if( PR_Listen(sock->fd, backlog) != PR_SUCCESS ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Failed to set listen backlog on socket"); + goto finish; + } + +finish: +} + +JNIEXPORT jbyteArray JNICALL +Java_org_mozilla_jss_ssl_SSLServerSocket_socketAccept + (JNIEnv *env, jobject self, jobject newSock, jint timeout) +{ + JSSL_SocketData *sock; + PRNetAddr addr; + PRFileDesc *newFD=NULL; + PRIntervalTime ivtimeout; + JSSL_SocketData *newSD=NULL; + jbyteArray sdArray = NULL; + SECStatus status; + + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; + + ivtimeout = (timeout > 0) ? PR_MillisecondsToInterval(timeout) + : PR_INTERVAL_NO_TIMEOUT; + + for(;;) { + newFD = PR_Accept(sock->fd, &addr, ivtimeout); + + if( newFD != NULL ) { + /* success! */ + break; + } else { + switch( PR_GetError() ) { + case PR_PENDING_INTERRUPT_ERROR: + case PR_IO_PENDING_ERROR: + break; /* out of the switch and loop again */ + default: + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Failed to accept new connection"); + goto finish; + } + } + } + + newSD = JSSL_CreateSocketData(env, newSock, newFD); + newFD = NULL; + if( newSD == NULL ) { + goto finish; + } + + /* setup the handshake callback */ + status = SSL_HandshakeCallback(newSD->fd, JSSL_HandshakeCallback, + newSD); + if( status != PR_SUCCESS ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Unable to install handshake callback"); + } + + /* pass the pointer back to Java */ + sdArray = JSS_ptrToByteArray(env, (void*) newSD); + if( sdArray == NULL ) { + /* exception was thrown */ + goto finish; + } + +finish: + if( (*env)->ExceptionOccurred(env) != NULL ) { + if( newSD != NULL ) { + JSSL_DestroySocketData(env, newSD); + } + if( newFD != NULL ) { + PR_Close(newFD); + } + } + return sdArray; +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLServerSocket_clearSessionCache( + JNIEnv *env, jclass clazz) +{ + SSL_ClearSessionCache(); +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLServerSocket_socketClose(JNIEnv *env, jobject self) +{ + JSSL_socketClose(env, self); +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLServerSocket_configServerSessionIDCache( + JNIEnv *env, jclass myClass, jint maxEntries, jint ssl2Timeout, + jint ssl3Timeout, jstring nameString) +{ + const char* dirName = NULL; + SECStatus status; + + if (nameString != NULL) { + dirName = (*env)->GetStringUTFChars(env, nameString, NULL); + } + + status = SSL_ConfigServerSessionIDCache( + maxEntries, ssl2Timeout, ssl3Timeout, dirName); + if (status != SECSuccess) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Failed to configure server session ID cache"); + goto finish; + } + +finish: + if(dirName != NULL) { + (*env)->ReleaseStringUTFChars(env, nameString, dirName); + } +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLServerSocket_setServerCertNickname( + JNIEnv *env, jobject self, jstring nicknameStr) +{ + JSSL_SocketData *sock; + const char *nickname=NULL; + CERTCertificate* cert=NULL; + SECKEYPrivateKey* privKey=NULL; + SECStatus status; + + if(nicknameStr == NULL) { + goto finish; + } + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; + + nickname = (*env)->GetStringUTFChars(env, nicknameStr, NULL); + if( nickname == NULL ) goto finish; + + cert = PK11_FindCertFromNickname((char *)nickname, NULL); /* CONST */ + if (cert != NULL) { + privKey = PK11_FindKeyByAnyCert(cert, NULL); + if (privKey != NULL) { + status = SSL_ConfigSecureServer(sock->fd, cert, privKey, kt_rsa); + if( status != SECSuccess) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Failed to configure secure server certificate and key"); + goto finish; + } + } else { + JSS_throwMsg(env, SOCKET_EXCEPTION, "Failed to locate private key"); + goto finish; + } + } else { + JSS_throwMsg(env, SOCKET_EXCEPTION, "Failed to locate private key"); + goto finish; + } + +finish: + if(privKey!=NULL) { + SECKEY_DestroyPrivateKey(privKey); + } + if(cert!=NULL) { + CERT_DestroyCertificate(cert); + } + if( nickname != NULL ) { + (*env)->ReleaseStringUTFChars(env, nicknameStr, nickname); + } +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLServerSocket_setNeedClientAuthNoExpiryCheck( + JNIEnv *env, jobject self, jboolean b) +{ + JSSL_setNeedClientAuthNoExpiryCheck(env, self, b); +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLServerSocket_setNeedClientAuth( + JNIEnv *env, jobject self, jboolean b) +{ + JSSL_SocketData *sock; + SECStatus status; + + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; + + status = SSL_OptionSet(sock->fd, SSL_REQUEST_CERTIFICATE, b); + if( status != SECSuccess ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, "Failed to set socket option"); + goto finish; + } + +finish: +} diff --git a/security/jss/org/mozilla/jss/ssl/SSLServerSocket.java b/security/jss/org/mozilla/jss/ssl/SSLServerSocket.java new file mode 100644 index 000000000000..c7cde7689745 --- /dev/null +++ b/security/jss/org/mozilla/jss/ssl/SSLServerSocket.java @@ -0,0 +1,138 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Security Services for Java. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2001 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +package org.mozilla.jss.ssl; + +import java.net.InetAddress; +import java.io.IOException; +import java.net.Socket; +import java.net.SocketException; + +public class SSLServerSocket { + + private static final int DEFAULT_BACKLOG = 50; + + public SSLServerSocket(int port) throws IOException { + this(port, DEFAULT_BACKLOG, null); + } + + public SSLServerSocket(int port, int backlog) throws IOException { + this(port, backlog, null); + } + + public SSLServerSocket(int port, int backlog, InetAddress bindAddr) + throws IOException + { + // Dance the dance of fools. The superclass doesn't have a default + // constructor, so we have to trick it here. This is an example + // of WHY WE SHOULDN'T BE EXTENDING SERVERSOCKET. + //super(0); + //super.close(); + + // create the socket + sockProxy = new SocketProxy( socketCreate() ); + + // bind it to the local address and port + if( bindAddr == null ) { + bindAddr = anyLocalAddr; + } + socketBind(bindAddr.getAddress(), port); + socketListen(backlog); + } + + private SocketProxy sockProxy; + private boolean handshakeAsClient=false; + + private native byte[] socketCreate() throws SocketException; + + private native void socketBind(byte[] addrBA, int port) + throws SocketException; + + private native void socketListen(int backlog) throws SocketException; + + private static InetAddress anyLocalAddr; + static { + try { + anyLocalAddr = InetAddress.getByName("0.0.0.0"); + } catch (java.net.UnknownHostException e) { } + } + + public Socket accept() throws IOException { + SSLSocket s = new SSLSocket(); + s.setSockProxy( new SocketProxy( socketAccept(s, timeout) ) ); + return s; + } + + private int timeout; + + public void setSoTimeout(int timeout) { + this.timeout = timeout; + } + + public int getSoTimeout() { + return timeout; + } + + private native byte[] socketAccept(SSLSocket s, int timeout) + throws SocketException; + + public static native void clearSessionCache(); + + protected void finalize() throws Throwable { + close(); + } + + public void close() throws IOException { + if( sockProxy != null ) { + socketClose(); + sockProxy = null; + } + } + + private native void socketClose() throws IOException; + + // This directory is used as the default for the Session ID cache + private final static String UNIX_TEMP_DIR = "/tmp"; + private final static String WINDOWS_TEMP_DIR = "\\temp"; + + public static native void configServerSessionIDCache(int maxSidEntries, + int ssl2EntryTimeout, int ssl3EntryTimeout, String cacheFileDirectory); + + public native void setServerCertNickname(String nickname) + throws SocketException; + + public native void setNeedClientAuth(boolean b) throws SocketException; + + public native void setNeedClientAuthNoExpiryCheck(boolean b) + throws SocketException; +} diff --git a/security/jss/org/mozilla/jss/ssl/SSLSocket.c b/security/jss/org/mozilla/jss/ssl/SSLSocket.c index 829c1a9f007d..294a45ff6301 100644 --- a/security/jss/org/mozilla/jss/ssl/SSLSocket.c +++ b/security/jss/org/mozilla/jss/ssl/SSLSocket.c @@ -42,94 +42,20 @@ #include #include "jssl.h" -#ifdef JDK1_2 -/* JDK 1.2 and higher provide weak references in JNI. */ - -#define NEW_WEAK_GLOBAL_REF(env, obj) \ - ((*env)->NewWeakGlobalRef((env), (obj))) -#define DELETE_WEAK_GLOBAL_REF(env, obj) \ - ((*env)->DeleteWeakGlobalRef((env), (obj))) - -#else -/* JDK 1.1 doesn't have weak references, so we'll have to use regular ones */ - -#define NEW_WEAK_GLOBAL_REF(env, obj) \ - ((*env)->NewGlobalRef((env), (obj))) -#define DELETE_WEAK_GLOBAL_REF(env, obj) \ - ((*env)->DeleteGlobalRef((env), (obj))) - -#endif - -static PRStatus -JSS_SSL_getSockData(JNIEnv *env, jobject sockObject, JSSL_SocketData **sd); - -SECStatus -JSSL_JavaCertAuthCallback(void *arg, PRFileDesc *fd, PRBool checkSig, - PRBool isServer); - -void -JSSL_HandshakeCallback(PRFileDesc *fd, void *arg); - -int -JSSL_DefaultCertAuthCallback(void *arg, PRFileDesc *fd, PRBool checkSig, - PRBool isServer); - -int -JSSL_CallCertSelectionCallback( void * arg, - PRFileDesc * fd, - CERTDistNames * caNames, - CERTCertificate ** pRetCert, - SECKEYPrivateKey ** pRetKey); - -static void -DestroyJSSL_SocketData(JNIEnv *env, JSSL_SocketData *sd) -{ - PR_ASSERT(sd != NULL); - - if( sd->fd != NULL ) { - PR_Close(sd->fd); - } - if( sd->socketObject != NULL ) { - DELETE_WEAK_GLOBAL_REF(env, sd->socketObject ); - } - if( sd->certApprovalCallback != NULL ) { - (*env)->DeleteGlobalRef(env, sd->certApprovalCallback); - } - if( sd->clientCertSelectionCallback != NULL ) { - (*env)->DeleteGlobalRef(env, sd->clientCertSelectionCallback); - } - PR_Free(sd); -} - - -/* - * These must match up with the constants defined in SSLSocket.java. - */ -static PRInt32 enums[] = { - SSL_ENABLE_SSL2, /* 0 */ - SSL_ENABLE_SSL3, /* 1 */ - PR_SockOpt_NoDelay, /* 2 */ - PR_SockOpt_Keepalive, /* 3 */ - PR_SHUTDOWN_RCV, /* 4 */ - PR_SHUTDOWN_SEND, /* 5 */ - - 0 -}; - JNIEXPORT void JNICALL Java_org_mozilla_jss_ssl_SSLSocket_setSSLOption(JNIEnv *env, jobject self, - jint joption, jboolean on) + jint joption, jint on) { SECStatus status; JSSL_SocketData *sock; /* get my fd */ - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } /* set the option */ - status = SSL_OptionSet(sock->fd, enums[joption], on); + status = SSL_OptionSet(sock->fd, JSSL_enums[joption], on); if( status != SECSuccess ) { JSS_throwMsg(env, SOCKET_EXCEPTION, "SSL_OptionSet failed"); goto finish; @@ -141,12 +67,12 @@ finish: JNIEXPORT void JNICALL Java_org_mozilla_jss_ssl_SSLSocket_setSSLDefaultOption(JNIEnv *env, - jclass clazz, jint joption, jboolean on) + jclass clazz, jint joption, jint on) { SECStatus status; /* set the option */ - status = SSL_OptionSetDefault(enums[joption], on); + status = SSL_OptionSetDefault(JSSL_enums[joption], on); if( status != SECSuccess ) { JSS_throwMsg(env, SOCKET_EXCEPTION, "SSL_OptionSet failed"); goto finish; @@ -163,7 +89,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_forceHandshake(JNIEnv *env, jobject self) int rv; /* get my fd */ - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) goto finish; + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) goto finish; /* do the work */ rv = SSL_ForceHandshake(sock->fd); @@ -188,7 +114,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_setSoLInger(JNIEnv *env, jobject self, PRStatus status; JSSL_SocketData *sock; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -216,7 +142,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_getTcpNoDelay(JNIEnv *env, jobject self) JSSL_SocketData *sock; PRStatus status; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -240,7 +166,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_setTcpNoDelay(JNIEnv *env, jobject self, PRStatus status; JSSL_SocketData *sock; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -264,7 +190,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_getSendBufferSize(JNIEnv *env, jobject self) JSSL_SocketData *sock; PRStatus status; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -288,7 +214,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_setSendBufferSize(JNIEnv *env, jobject self, PRStatus status; JSSL_SocketData *sock; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -312,7 +238,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_getKeepAlive(JNIEnv *env, jobject self) JSSL_SocketData *sock; PRStatus status; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -336,7 +262,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_getReceiveBufferSize( JSSL_SocketData *sock; PRStatus status; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -360,7 +286,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_setReceiveBufferSize( PRStatus status; JSSL_SocketData *sock; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -385,7 +311,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_setKeepAlive(JNIEnv *env, jobject self, PRStatus status; JSSL_SocketData *sock; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -410,7 +336,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_getSoLinger(JNIEnv *env, jobject self) jint retval; PRStatus status; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -441,7 +367,7 @@ getSockAddr(JNIEnv *env, jobject self, PRNetAddr *addr, LocalOrPeer localOrPeer) PRStatus status; /* get my fd */ - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { return; } @@ -497,147 +423,15 @@ Java_org_mozilla_jss_ssl_SSLSocket_getPort(JNIEnv *env, return addr.inet.port; } -static PRStatus -JSS_SSL_getSockData(JNIEnv *env, jobject sockObject, JSSL_SocketData **sd) -{ - PR_ASSERT(env!=NULL && sockObject!=NULL && sd!=NULL); - - return JSS_getPtrFromProxyOwner(env, sockObject, SSLSOCKET_PROXY_FIELD, - SSLSOCKET_PROXY_SIG, (void**)sd); -} - JNIEXPORT jbyteArray JNICALL Java_org_mozilla_jss_ssl_SSLSocket_socketCreate (JNIEnv *env, jobject self, jobject certApprovalCallback, jobject clientCertSelectionCallback) { - jbyteArray sdArray = NULL; - JSSL_SocketData *sockdata = NULL; - PRStatus status; - - /* make a JSSL_SocketData structure */ - sockdata = PR_Malloc( sizeof(JSSL_SocketData) ); - sockdata->fd = NULL; - sockdata->socketObject = NULL; - sockdata->certApprovalCallback = NULL; - sockdata->clientCertSelectionCallback = NULL; - - /* create a TCP socket */ - sockdata->fd = PR_NewTCPSocket(); - if( sockdata->fd == NULL ) { - JSS_throwMsg(env, SOCKET_EXCEPTION, "PR_NewTCPSocket() returned NULL"); - goto finish; - } - - /* enable SSL on the socket */ - sockdata->fd = SSL_ImportFD(NULL, sockdata->fd); - if( sockdata->fd == NULL ) { - JSS_throwMsg(env, SOCKET_EXCEPTION, "SSL_ImportFD() returned NULL"); - goto finish; - } - - status = SSL_OptionSet(sockdata->fd, SSL_SECURITY, PR_TRUE); - if( status != SECSuccess ) { - JSS_throwMsg(env, SOCKET_EXCEPTION, - "Unable to enable SSL security on socket"); - goto finish; - } - - /* - * Make a global ref to the socket. Since it is a weak reference, it will - * get garbage collected if this is the only reference that remains. - * We do this so that sockets will get closed when they go out of scope - * in the Java layer. - */ - sockdata->socketObject = NEW_WEAK_GLOBAL_REF(env, self); - if( sockdata->socketObject == NULL ) goto finish; - - /* setup the handshake callback */ - status = SSL_HandshakeCallback(sockdata->fd, JSSL_HandshakeCallback, - sockdata); - if( status != PR_SUCCESS ) { - JSS_throwMsg(env, SOCKET_EXCEPTION, - "Unable to install handshake callback"); - } - - /* setup the cert authentication callback */ - if( certApprovalCallback != NULL ) { - /* create global reference to the callback object */ - sockdata->certApprovalCallback = - (*env)->NewGlobalRef(env, certApprovalCallback); - if( sockdata->certApprovalCallback == NULL ) goto finish; - - /* install the Java callback */ - status = SSL_AuthCertificateHook( - sockdata->fd, JSSL_JavaCertAuthCallback, - (void*) sockdata->certApprovalCallback); - } else { - /* install the default callback */ - status = SSL_AuthCertificateHook( - sockdata->fd, JSSL_DefaultCertAuthCallback, NULL); - } - if( status != PR_SUCCESS ) { - JSS_throwMsg(env, SOCKET_EXCEPTION, - "Unable to install certificate authentication callback"); - goto finish; - } - - /* setup the client cert selection callback */ - if( clientCertSelectionCallback != NULL ) { - /* create a new global ref */ - sockdata->clientCertSelectionCallback = - (*env)->NewGlobalRef(env, clientCertSelectionCallback); - if(sockdata->clientCertSelectionCallback == NULL) goto finish; - - /* install the Java callback */ - status = SSL_GetClientAuthDataHook( - sockdata->fd, JSSL_CallCertSelectionCallback, - (void*) sockdata->clientCertSelectionCallback); - if( status != PR_SUCCESS ) { - JSS_throwMsg(env, SOCKET_EXCEPTION, - "Unable to install client certificate selection callback"); - goto finish; - } - } - - /* pass the pointer back to Java */ - sdArray = JSS_ptrToByteArray(env, (void*) sockdata); - if( sdArray == NULL ) { - /* exception was thrown */ - goto finish; - } - -finish: - if( (*env)->ExceptionOccurred(env) != NULL ) { - if( sockdata != NULL ) { - DestroyJSSL_SocketData(env, sockdata); - } - } else { - PR_ASSERT( sdArray != NULL ); - } - return sdArray; + return JSSL_socketCreate(env, self, certApprovalCallback, + clientCertSelectionCallback); } -#if 0 -static void -throwMsgPRErr(JNIEnv *env, char* exceptionClassName, char* msg) -{ - PRErrorCode errcode; - char *errstr; - int len; - - len = strlen(msg) + 26; - errstr = (char*) PR_Malloc(len); - errcode = PR_GetError(); - - snprintf(errstr, len, "%s (error code %d)", msg, errcode); - - JSS_throwMsg(env, exceptionClassName, errstr); - - PR_Free(errstr); -} -#endif - /* * This function assumes 4-byte IP addresses. It will need to be tweaked * for IPv6. @@ -650,74 +444,13 @@ JNIEXPORT void JNICALL Java_org_mozilla_jss_ssl_SSLSocket_socketBind (JNIEnv *env, jobject self, jbyteArray addrBA, jint port) { - JSSL_SocketData *sock; - PRNetAddr addr; - jbyte *addrBAelems = NULL; - PRStatus status; - - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS) { - /* exception was thrown */ - goto finish; - } - - /* - * setup the PRNetAddr structure - */ - addr.inet.family = AF_INET; - addr.inet.port = port; - PR_ASSERT(sizeof(addr.inet.ip) == 4); - PR_ASSERT( (*env)->GetArrayLength(env, addrBA) == 4); - addrBAelems = (*env)->GetByteArrayElements(env, addrBA, NULL); - if( addrBAelems == NULL ) { - ASSERT_OUTOFMEM(env); - goto finish; - } - memcpy(&addr.inet.ip, addrBAelems, 4); - - /* do the bind() call */ - status = PR_Bind(sock->fd, &addr); - if( status != PR_SUCCESS ) { - PRErrorCode err = PR_GetError(); - switch( err ) { - case PR_ADDRESS_NOT_AVAILABLE_ERROR: - JSS_throwMsg(env, SOCKET_EXCEPTION, - "Binding exception: address not available"); - break; - case PR_ADDRESS_IN_USE_ERROR: - JSS_throwMsg(env, SOCKET_EXCEPTION, - "Binding exception: address in use"); - break; - default: - JSS_throwMsg(env, SOCKET_EXCEPTION, - "Binding exception"); - break; - } - goto finish; - } - -finish: - if( addrBAelems != NULL ) { - (*env)->ReleaseByteArrayElements(env, addrBA, addrBAelems, JNI_ABORT); - } + JSSL_socketBind(env, self, addrBA, port); } JNIEXPORT void JNICALL Java_org_mozilla_jss_ssl_SSLSocket_socketClose(JNIEnv *env, jobject self) { - JSSL_SocketData *sock; - - printf("***\nClosing socket\n***\n"); - - /* get the FD */ - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS) { - /* exception was thrown */ - goto finish; - } - - /* destroy the FD and any supporting data */ - DestroyJSSL_SocketData(env, sock); - -finish: + JSSL_socketClose(env, self); } JNIEXPORT void JNICALL @@ -731,7 +464,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketConnect int stat; const char *hostnameStr=NULL; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) { /* exception was thrown */ goto finish; } @@ -802,7 +535,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_getStatus jobject serialNumObj = NULL; /* get the fd */ - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) { /* exception was thrown */ goto finish; } @@ -923,7 +656,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketRead(JNIEnv *env, jobject self, jint nread; /* get the socket */ - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -982,7 +715,7 @@ Java_com_netscape_jss_ssl_SSLSocketImpl_socketAvailable( jint available; JSSL_SocketData *sock; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -1013,7 +746,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketWrite(JNIEnv *env, jobject self, goto finish; } - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS ) { + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) { goto finish; } @@ -1064,9 +797,9 @@ Java_org_mozilla_jss_ssl_SSLSocket_shutdownNative( JSSL_SocketData *sock; PRStatus status; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; - status = PR_Shutdown(sock->fd, enums[how]); + status = PR_Shutdown(sock->fd, JSSL_enums[how]); if( status != PR_SUCCESS) { JSS_throwMsg(env, SOCKET_EXCEPTION, "Failed to shutdown socket"); goto finish; @@ -1081,7 +814,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_invalidateSession(JNIEnv *env, jobject self) JSSL_SocketData *sock; SECStatus status; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; status = SSL_InvalidateSession(sock->fd); if(status != SECSuccess) { @@ -1099,7 +832,7 @@ Java_org_mozilla_jss_ssl_SSLSocket_redoHandshake( JSSL_SocketData *sock; SECStatus status; - if( JSS_SSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; status = SSL_ReHandshake(sock->fd, flushCache); if(status != SECSuccess) { @@ -1109,3 +842,66 @@ Java_org_mozilla_jss_ssl_SSLSocket_redoHandshake( finish: } + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLSocket_resetHandshake( + JNIEnv *env, jobject self, jboolean asClient) +{ + JSSL_SocketData *sock; + SECStatus status; + + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; + + status = SSL_ResetHandshake(sock->fd, !asClient); + if(status != SECSuccess) { + JSS_throwMsg(env, SOCKET_EXCEPTION, "Failed to redo handshake"); + goto finish; + } + +finish: +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLSocket_setClientCertNicknameNative( + JNIEnv *env, jobject self, jstring nickStr) +{ + JSSL_SocketData *sock; + const char *nick=NULL; + SECStatus status; + + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; + + /* + * Store the nickname in the SocketData. + */ + nick = (*env)->GetStringUTFChars(env, nickStr, NULL); + if( nick == NULL ) goto finish; + if( sock->clientCertNickname != NULL ) { + PR_Free(sock->clientCertNickname); + } + sock->clientCertNickname = PL_strdup(nick); + + /* + * Install the callback. + */ + status = SSL_GetClientAuthDataHook(sock->fd, JSSL_GetClientAuthData, + (void*)sock); + if(status != SECSuccess) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Unable to set client auth data hook"); + goto finish; + } + +finish: + if( nick != NULL ) { + (*env)->ReleaseStringUTFChars(env, nickStr, nick); + } +} + + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_ssl_SSLSocket_setNeedClientAuthNoExpiryCheck( + JNIEnv *env, jobject self, jboolean b) +{ + JSSL_setNeedClientAuthNoExpiryCheck(env, self, b); +} diff --git a/security/jss/org/mozilla/jss/ssl/SSLSocket.java b/security/jss/org/mozilla/jss/ssl/SSLSocket.java index b177a5a97ac6..4b905da3a02c 100644 --- a/security/jss/org/mozilla/jss/ssl/SSLSocket.java +++ b/security/jss/org/mozilla/jss/ssl/SSLSocket.java @@ -34,7 +34,9 @@ package org.mozilla.jss.ssl; import java.net.*; +import java.net.SocketException; import java.io.*; +import java.io.IOException; import java.util.LinkedList; import java.util.ListIterator; @@ -46,6 +48,14 @@ public class SSLSocket extends java.net.Socket { SSLSocket() throws IOException { } + /** + * Should only be called by SSLServerSocket after a successful + * accept(). + */ + void setSockProxy(SocketProxy sp) { + sockProxy = sp; + } + public SSLSocket(String host, int port) throws UnknownHostException, IOException { @@ -194,7 +204,6 @@ public class SSLSocket extends java.net.Socket { return new SSLOutputStream(this); } - public native void setTcpNoDelay(boolean on) throws SocketException; public native boolean getTcpNoDelay() throws SocketException; @@ -292,14 +301,66 @@ public class SSLSocket extends java.net.Socket { setSSLDefaultOption(SSL_ENABLE_SSL3, enable); } + public void requireClientAuth(boolean require, boolean onRedo) + throws SocketException + { + setSSLOption(SSL_REQUIRE_CERTIFICATE, require ? (onRedo ? 1 : 2) : 0); + } + + public void requireClientAuthDefault(boolean require, boolean onRedo) + throws SocketException + { + setSSLDefaultOption(SSL_REQUIRE_CERTIFICATE, + require ? (onRedo ? 1 : 2) : 0); + } + public native void forceHandshake() throws SocketException; + public void setUseClientMode(boolean b) { + handshakeAsClient = b; + } + + public boolean getUseClientMode() { + return handshakeAsClient; + } + + public void resetHandshake() throws SocketException { + resetHandshakeNative(handshakeAsClient); + } + + private native void resetHandshakeNative(boolean asClient) + throws SocketException; + public native SSLSecurityStatus getStatus() throws SocketException; + public void setClientCertNickname(String nick) throws SocketException { + if( nick != null && nick.length() > 0 ) { + setClientCertNicknameNative(nick); + } + } + public native void setClientCertNicknameNative(String nick) + throws SocketException; + + public void setNeedClientAuth(boolean b) throws SocketException { + setSSLOption(SSL_REQUEST_CERTIFICATE, b); + } + + public native void setNeedClientAuthNoExpiryCheck(boolean b) + throws SocketException; + + public void useCache(boolean b) throws SocketException { + setSSLOption(SSL_NO_CACHE, !b); + } + + public void useCacheDefault(boolean b) throws SocketException { + setSSLDefaultOption(SSL_NO_CACHE, !b); + } + private InetAddress inetAddress; private int port; private SocketProxy sockProxy; private boolean open = false; + private boolean handshakeAsClient=true; /** * Enums. These must match the enums table in SSLSocket.c. This is @@ -312,6 +373,9 @@ public class SSLSocket extends java.net.Socket { private static final int SO_KEEPALIVE = 3; private static final int PR_SHUTDOWN_RCV = 4; private static final int PR_SHUTDOWN_SEND = 5; + private static final int SSL_REQUIRE_CERTIFICATE = 6; + private static final int SSL_REQUEST_CERTIFICATE = 7; + private static final int SSL_NO_CACHE = 8; /** * SO_TIMEOUT timeout in millis. I don't know why we have to keep it here @@ -319,9 +383,19 @@ public class SSLSocket extends java.net.Socket { */ private int timeout; - private native void setSSLOption(int option, boolean on) + private void setSSLOption(int option, boolean on) + throws SocketException + { + setSSLOption(option, on ? 1 : 0); + } + private native void setSSLOption(int option, int on) throws SocketException; - private static native void setSSLDefaultOption(int option, boolean on) + private static void setSSLDefaultOption(int option, boolean on) + throws SocketException + { + setSSLDefaultOption(option, on ? 1 : 0); + } + private static native void setSSLDefaultOption(int option, int on) throws SocketException; private native byte[] socketCreate( @@ -424,20 +498,3 @@ public class SSLSocket extends java.net.Socket { } -class SocketProxy extends org.mozilla.jss.util.NativeProxy { - - public SocketProxy(byte[] pointer) { - super(pointer); - } - - /** - * Theoretically, we don't need to do anything, since SSLSocket should - * call close() when it finalizes. When a socket is closed all its - * resources are freed, and there's nothing left to release. - */ - protected void releaseNativeResources() { } - - protected void finalize() throws Throwable { - super.finalize(); - } -} diff --git a/security/jss/org/mozilla/jss/ssl/SocketProxy.java b/security/jss/org/mozilla/jss/ssl/SocketProxy.java new file mode 100644 index 000000000000..f1bb8a4ebe57 --- /dev/null +++ b/security/jss/org/mozilla/jss/ssl/SocketProxy.java @@ -0,0 +1,52 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Security Services for Java. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2001 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +package org.mozilla.jss.ssl; + +class SocketProxy extends org.mozilla.jss.util.NativeProxy { + + public SocketProxy(byte[] pointer) { + super(pointer); + } + + /** + * Theoretically, we don't need to do anything, since SSLSocket should + * call close() when it finalizes. When a socket is closed all its + * resources are freed, and there's nothing left to release. + */ + protected void releaseNativeResources() { } + + protected void finalize() throws Throwable { + super.finalize(); + } +} diff --git a/security/jss/org/mozilla/jss/ssl/callbacks.c b/security/jss/org/mozilla/jss/ssl/callbacks.c index d705876c3fd8..49d4473b4dd1 100644 --- a/security/jss/org/mozilla/jss/ssl/callbacks.c +++ b/security/jss/org/mozilla/jss/ssl/callbacks.c @@ -607,3 +607,94 @@ finish: return retval; } +PR_IMPLEMENT( int ) +JSSL_GetClientAuthData( void * arg, + PRFileDesc * fd, + CERTDistNames * caNames, + CERTCertificate ** pRetCert, + SECKEYPrivateKey ** pRetKey) +{ + CERTCertificate * cert; + SECKEYPrivateKey * privkey; + JSSL_SocketData * sock; + SECStatus rv = SECFailure; + + PR_ASSERT(arg != NULL); + sock = (JSSL_SocketData*) arg; + + if (sock->clientCertNickname) { + cert = PK11_FindCertFromNickname(sock->clientCertNickname, + NULL /*pinarg*/); + if ( cert ) { + privkey = PK11_FindKeyByAnyCert(cert, NULL /*pinarg*/); + if ( privkey ) { + rv = SECSuccess; + } else { + CERT_DestroyCertificate(cert); + } + } + } + + if (rv == SECSuccess) { + *pRetCert = cert; + *pRetKey = privkey; + } + + return rv; +} + +/* + * Callback from SSL for checking a (possibly) expired + * certificate the peer presents. + */ +PR_IMPLEMENT( int ) +JSSL_ConfirmExpiredPeerCert(void *arg, PRFileDesc *fd, PRBool checkSig, + PRBool isServer) +{ + char* hostname; + SECStatus rv=SECFailure; + SECCertUsage certUsage; + CERTCertificate* peerCert=NULL; + int64 notAfter, notBefore; + + certUsage = isServer ? certUsageSSLClient : certUsageSSLServer; + + peerCert = SSL_PeerCertificate(fd); + + if (peerCert) { + rv = CERT_GetCertTimes(peerCert, ¬Before, ¬After); + if (rv != SECSuccess) goto finish; + + /* + * Verify the certificate based on it's expiry date. This should + * always succeed, if the cert is trusted. It doesn't care if + * the cert has expired. + */ + rv = CERT_VerifyCert(CERT_GetDefaultCertDB(), peerCert, + checkSig, certUsage, + notAfter, NULL /*pinarg*/, + NULL /* log */); + } + if ( rv != SECSuccess ) goto finish; + + if( ! isServer ) { + /* This is the client side of an SSL connection. + * Now check the name field in the cert against the desired hostname. + * NB: This is our only defense against Man-In-The-Middle (MITM) attacks! + */ + if( peerCert == NULL ) { + rv = SECFailure; + } else { + hostname = SSL_RevealURL(fd); /* really is a hostname, not a URL */ + if (hostname && hostname[0]) { + rv = CERT_VerifyCertName(peerCert, hostname); + } else { + rv = SECFailure; + } + } + } + +finish: + if (peerCert!=NULL) CERT_DestroyCertificate(peerCert); + return (int)rv; +} diff --git a/security/jss/org/mozilla/jss/ssl/common.c b/security/jss/org/mozilla/jss/ssl/common.c new file mode 100644 index 000000000000..05c5d331bfda --- /dev/null +++ b/security/jss/org/mozilla/jss/ssl/common.c @@ -0,0 +1,329 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Security Services for Java. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2001 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "jssl.h" + +/* + * This is done for regular sockets that we connect() and server sockets, + * but not for sockets that come from accept. + */ +jbyteArray +JSSL_socketCreate(JNIEnv *env, jobject self, + jobject certApprovalCallback, jobject clientCertSelectionCallback) +{ + jbyteArray sdArray = NULL; + JSSL_SocketData *sockdata; + PRStatus status; + PRFileDesc *newFD; + + /* create a TCP socket */ + newFD = PR_NewTCPSocket(); + if( newFD == NULL ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, "PR_NewTCPSocket() returned NULL"); + goto finish; + } + + sockdata = JSSL_CreateSocketData(env, self, newFD); + if( sockdata == NULL ) { + goto finish; + } + + /* enable SSL on the socket */ + sockdata->fd = SSL_ImportFD(NULL, sockdata->fd); + if( sockdata->fd == NULL ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, "SSL_ImportFD() returned NULL"); + goto finish; + } + + status = SSL_OptionSet(sockdata->fd, SSL_SECURITY, PR_TRUE); + if( status != SECSuccess ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Unable to enable SSL security on socket"); + goto finish; + } + + /* setup the handshake callback */ + status = SSL_HandshakeCallback(sockdata->fd, JSSL_HandshakeCallback, + sockdata); + if( status != PR_SUCCESS ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Unable to install handshake callback"); + } + + /* setup the cert authentication callback */ + if( certApprovalCallback != NULL ) { + /* create global reference to the callback object */ + sockdata->certApprovalCallback = + (*env)->NewGlobalRef(env, certApprovalCallback); + if( sockdata->certApprovalCallback == NULL ) goto finish; + + /* install the Java callback */ + status = SSL_AuthCertificateHook( + sockdata->fd, JSSL_JavaCertAuthCallback, + (void*) sockdata->certApprovalCallback); + } else { + /* install the default callback */ + status = SSL_AuthCertificateHook( + sockdata->fd, JSSL_DefaultCertAuthCallback, NULL); + } + if( status != PR_SUCCESS ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Unable to install certificate authentication callback"); + goto finish; + } + + /* setup the client cert selection callback */ + if( clientCertSelectionCallback != NULL ) { + /* create a new global ref */ + sockdata->clientCertSelectionCallback = + (*env)->NewGlobalRef(env, clientCertSelectionCallback); + if(sockdata->clientCertSelectionCallback == NULL) goto finish; + + /* install the Java callback */ + status = SSL_GetClientAuthDataHook( + sockdata->fd, JSSL_CallCertSelectionCallback, + (void*) sockdata->clientCertSelectionCallback); + if( status != PR_SUCCESS ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Unable to install client certificate selection callback"); + goto finish; + } + } + + /* pass the pointer back to Java */ + sdArray = JSS_ptrToByteArray(env, (void*) sockdata); + if( sdArray == NULL ) { + /* exception was thrown */ + goto finish; + } + +finish: + if( (*env)->ExceptionOccurred(env) != NULL ) { + if( sockdata != NULL ) { + JSSL_DestroySocketData(env, sockdata); + } + } else { + PR_ASSERT( sdArray != NULL ); + } + return sdArray; +} + +JSSL_SocketData* +JSSL_CreateSocketData(JNIEnv *env, jobject sockObj, PRFileDesc* newFD) +{ + JSSL_SocketData *sockdata = NULL; + + /* make a JSSL_SocketData structure */ + sockdata = PR_Malloc( sizeof(JSSL_SocketData) ); + sockdata->fd = NULL; + sockdata->socketObject = NULL; + sockdata->certApprovalCallback = NULL; + sockdata->clientCertSelectionCallback = NULL; + sockdata->clientCertNickname = NULL; + + sockdata->fd = newFD; + + /* + * Make a global ref to the socket. Since it is a weak reference, it will + * get garbage collected if this is the only reference that remains. + * We do this so that sockets will get closed when they go out of scope + * in the Java layer. + */ + sockdata->socketObject = NEW_WEAK_GLOBAL_REF(env, sockObj); + if( sockdata->socketObject == NULL ) goto finish; + +finish: + if( (*env)->ExceptionOccurred(env) != NULL ) { + if( sockdata != NULL ) { + JSSL_DestroySocketData(env, sockdata); + sockdata = NULL; + } else { + PR_ASSERT( sockdata != NULL ); + } + } + return sockdata; +} + +void +JSSL_DestroySocketData(JNIEnv *env, JSSL_SocketData *sd) +{ + PR_ASSERT(sd != NULL); + + if( sd->fd != NULL ) { + PR_Close(sd->fd); + } + if( sd->socketObject != NULL ) { + DELETE_WEAK_GLOBAL_REF(env, sd->socketObject ); + } + if( sd->certApprovalCallback != NULL ) { + (*env)->DeleteGlobalRef(env, sd->certApprovalCallback); + } + if( sd->clientCertSelectionCallback != NULL ) { + (*env)->DeleteGlobalRef(env, sd->clientCertSelectionCallback); + } + if( sd->clientCertNickname != NULL ) { + PR_Free(sd->clientCertNickname); + } + PR_Free(sd); +} + +/* + * These must match up with the constants defined in SSLSocket.java. + */ +PRInt32 JSSL_enums[] = { + SSL_ENABLE_SSL2, /* 0 */ + SSL_ENABLE_SSL3, /* 1 */ + PR_SockOpt_NoDelay, /* 2 */ + PR_SockOpt_Keepalive, /* 3 */ + PR_SHUTDOWN_RCV, /* 4 */ + PR_SHUTDOWN_SEND, /* 5 */ + SSL_REQUIRE_CERTIFICATE, /* 6 */ + SSL_REQUEST_CERTIFICATE, /* 7 */ + SSL_NO_CACHE, /* 8 */ + + 0 +}; + +void +JSSL_socketBind(JNIEnv *env, jobject self, jbyteArray addrBA, jint port) +{ + JSSL_SocketData *sock; + PRNetAddr addr; + jbyte *addrBAelems = NULL; + PRStatus status; + + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) { + /* exception was thrown */ + goto finish; + } + + /* + * setup the PRNetAddr structure + */ + addr.inet.family = AF_INET; + addr.inet.port = htons(port); + PR_ASSERT(sizeof(addr.inet.ip) == 4); + PR_ASSERT( (*env)->GetArrayLength(env, addrBA) == 4); + addrBAelems = (*env)->GetByteArrayElements(env, addrBA, NULL); + if( addrBAelems == NULL ) { + ASSERT_OUTOFMEM(env); + goto finish; + } + memcpy(&addr.inet.ip, addrBAelems, 4); + + /* do the bind() call */ + status = PR_Bind(sock->fd, &addr); + if( status != PR_SUCCESS ) { + PRErrorCode err = PR_GetError(); + switch( err ) { + case PR_ADDRESS_NOT_AVAILABLE_ERROR: + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Binding exception: address not available"); + break; + case PR_ADDRESS_IN_USE_ERROR: + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Binding exception: address in use"); + break; + default: + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Binding exception"); + break; + } + goto finish; + } + +finish: + if( addrBAelems != NULL ) { + (*env)->ReleaseByteArrayElements(env, addrBA, addrBAelems, JNI_ABORT); + } +} + +void +JSSL_socketClose(JNIEnv *env, jobject self) +{ + JSSL_SocketData *sock; + + /* get the FD */ + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) { + /* exception was thrown */ + goto finish; + } + + /* destroy the FD and any supporting data */ + JSSL_DestroySocketData(env, sock); + +finish: +} + +void +JSSL_setNeedClientAuthNoExpiryCheck(JNIEnv *env, jobject self, jboolean b) +{ + JSSL_SocketData *sock; + SECStatus status; + + if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish; + + /* + * Set the option on the socket + */ + status = SSL_OptionSet(sock->fd, SSL_REQUEST_CERTIFICATE, b); + if( status != SECSuccess ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Failed to set REQUEST_CERTIFICATE option on socket"); + goto finish; + } + + if(b) { + /* + * Set the callback function + */ + status = SSL_AuthCertificateHook(sock->fd, + JSSL_ConfirmExpiredPeerCert, NULL /*cx*/); + if( status != SECSuccess ) { + JSS_throwMsg(env, SOCKET_EXCEPTION, + "Failed to set certificate authentication callback"); + goto finish; + } + } + +finish: +} diff --git a/security/jss/org/mozilla/jss/ssl/jssl.h b/security/jss/org/mozilla/jss/ssl/jssl.h index 767c704d2a21..c66838de2160 100644 --- a/security/jss/org/mozilla/jss/ssl/jssl.h +++ b/security/jss/org/mozilla/jss/ssl/jssl.h @@ -39,7 +39,83 @@ struct JSSL_SocketData { jobject socketObject; /* weak global ref */ jobject certApprovalCallback; /* global ref */ jobject clientCertSelectionCallback; /* global ref */ + char *clientCertNickname; }; typedef struct JSSL_SocketData JSSL_SocketData; +SECStatus +JSSL_JavaCertAuthCallback(void *arg, PRFileDesc *fd, PRBool checkSig, + PRBool isServer); + +void +JSSL_HandshakeCallback(PRFileDesc *fd, void *arg); + +int +JSSL_DefaultCertAuthCallback(void *arg, PRFileDesc *fd, PRBool checkSig, + PRBool isServer); + +int +JSSL_CallCertSelectionCallback( void * arg, + PRFileDesc * fd, + CERTDistNames * caNames, + CERTCertificate ** pRetCert, + SECKEYPrivateKey ** pRetKey); + +int +JSSL_ConfirmExpiredPeerCert(void *arg, PRFileDesc *fd, PRBool checkSig, + PRBool isServer); + +int +JSSL_GetClientAuthData( void * arg, + PRFileDesc * fd, + CERTDistNames * caNames, + CERTCertificate ** pRetCert, + SECKEYPrivateKey ** pRetKey); + + +#ifdef JDK1_2 +/* JDK 1.2 and higher provide weak references in JNI. */ + +#define NEW_WEAK_GLOBAL_REF(env, obj) \ + ((*env)->NewWeakGlobalRef((env), (obj))) +#define DELETE_WEAK_GLOBAL_REF(env, obj) \ + ((*env)->DeleteWeakGlobalRef((env), (obj))) + +#else +/* JDK 1.1 doesn't have weak references, so we'll have to use regular ones */ + +#define NEW_WEAK_GLOBAL_REF(env, obj) \ + ((*env)->NewGlobalRef((env), (obj))) +#define DELETE_WEAK_GLOBAL_REF(env, obj) \ + ((*env)->DeleteGlobalRef((env), (obj))) + +#endif + +#define JSSL_getSockData(env, sockObject, sdptr) \ + JSS_getPtrFromProxyOwner(env, sockObject, SSLSOCKET_PROXY_FIELD, \ + SSLSOCKET_PROXY_SIG, (void**)sdptr) + + +void +JSSL_DestroySocketData(JNIEnv *env, JSSL_SocketData *sd); + + +extern PRInt32 JSSL_enums[]; + +void +JSSL_socketBind(JNIEnv *env, jobject self, jbyteArray addrBA, jint port); + +void +JSSL_socketClose(JNIEnv *env, jobject self); + +jbyteArray +JSSL_socketCreate(JNIEnv *env, jobject self, + jobject certApprovalCallback, jobject clientCertSelectionCallback); + +JSSL_SocketData* +JSSL_CreateSocketData(JNIEnv *env, jobject sockObj, PRFileDesc* newFD); + +void +JSSL_setNeedClientAuthNoExpiryCheck(JNIEnv *env, jobject self, jboolean b); + #endif diff --git a/security/jss/org/mozilla/jss/ssl/manifest.mn b/security/jss/org/mozilla/jss/ssl/manifest.mn index 7d27fca0cf45..3882749cd434 100644 --- a/security/jss/org/mozilla/jss/ssl/manifest.mn +++ b/security/jss/org/mozilla/jss/ssl/manifest.mn @@ -44,9 +44,12 @@ PACKAGE = org/mozilla/jss/ssl JNI_GEN = org.mozilla.jss.ssl.SSLSocket \ $(NULL) -JSRCS = SSLHandshakeCompletedEvent.java \ +JSRCS = \ + SocketProxy.java \ + SSLHandshakeCompletedEvent.java \ SSLSecurityStatus.java \ SSLHandshakeCompletedListener.java \ + SSLServerSocket.java \ SSLSocket.java \ SSLInputStream.java \ SSLOutputStream.java \ @@ -55,7 +58,9 @@ JSRCS = SSLHandshakeCompletedEvent.java \ SSLClientCertificateSelectionCallback.java \ $(NULL) -PRIVATE_JSRCS = SSLClient.java \ +PRIVATE_JSRCS = \ + SSLClient.java \ + SSLServer.java \ TestCertApprovalCallback.java \ TestClientCertificateSelectionCallback.java \ $(NULL) @@ -69,6 +74,7 @@ PRIVATE_JSRCS = SSLClient.java \ CLASSES = SSLHandshakeCompletedEvent \ SSLSecurityStatus \ SSLHandshakeCompletedListener \ + SSLServerSocket \ SSLSocket \ PrintOutputStreamWriter \ SSLCallbackNotifier \ @@ -76,7 +82,11 @@ CLASSES = SSLHandshakeCompletedEvent \ SSLClientCertificateSelectionCallback \ $(NULL) -#PRIVATE_CLASSES = SSLServer \ +PRIVATE_CLASSES = \ + SSLServer \ + SSLClient \ + $(NULL) + #SSLClient \ #ServerHandshakeCB \ #ClientHandshakeCB \ @@ -84,6 +94,8 @@ CLASSES = SSLHandshakeCompletedEvent \ CSRCS = SSLSocket.c \ callbacks.c \ + SSLServerSocket.c \ + common.c \ $(NULL) LIBRARY_NAME = jssssl