29 import java.io.BufferedOutputStream;
30 import java.io.InterruptedIOException;
31 import java.io.IOException;
32 import java.io.OutputStream;
33 import java.io.InputStream;
34 import java.net.InetSocketAddress;
35 import java.net.Socket;
36 import javax.net.ssl.SSLSocket;
37
38 import javax.naming.CommunicationException;
39 import javax.naming.ServiceUnavailableException;
40 import javax.naming.NamingException;
41 import javax.naming.InterruptedNamingException;
42
43 import javax.naming.ldap.Control;
44
45 import java.lang.reflect.Method;
46 import java.lang.reflect.InvocationTargetException;
47 import java.security.AccessController;
48 import java.security.PrivilegedAction;
49 import java.util.Arrays;
50 import javax.net.SocketFactory;
51 import javax.net.ssl.SSLParameters;
52
53 /**
54 * A thread that creates a connection to an LDAP server.
55 * After the connection, the thread reads from the connection.
56 * A caller can invoke methods on the instance to read LDAP responses
57 * and to send LDAP requests.
58 * <p>
59 * There is a one-to-one correspondence between an LdapClient and
60 * a Connection. Access to Connection and its methods is only via
61 * LdapClient with two exceptions: SASL authentication and StartTLS.
62 * SASL needs to access Connection's socket IO streams (in order to do encryption
63 * of the security layer). StartTLS needs to do replace IO streams
64 * and close the IO streams on nonfatal close. The code for SASL
65 * authentication can be treated as being the same as from LdapClient
66 * because the SASL code is only ever called from LdapClient, from
67 * inside LdapClient's synchronized authenticate() method. StartTLS is called
68 * directly by the application but should only occur when the underlying
69 * connection is quiet.
70 * <p>
71 * In terms of synchronization, worry about data structures
92 * cleanup() - sync
93 * readReply() - access to sock sync
94 * unpauseReader() - (indirectly via writeRequest) sync on pauseLock
95 * Members used by SASL auth (main thread):
96 * inStream, outStream - no sync; used to construct new stream; accessed
97 * only when conn is "quiet" and not shared
98 * replaceStreams() - sync method
99 * Members used by StartTLS:
100 * inStream, outStream - no sync; used to record the existing streams;
101 * accessed only when conn is "quiet" and not shared
102 * replaceStreams() - sync method
103 * <p>
104 * Handles anonymous, simple, and SASL bind for v3; anonymous and simple
105 * for v2.
106 * %%% made public for access by LdapSasl %%%
107 *
108 * @author Vincent Ryan
109 * @author Rosanna Lee
110 * @author Jagane Sundar
111 */
112 public final class Connection implements Runnable {
113
114 private static final boolean debug = false;
115 private static final int dump = 0; // > 0 r, > 1 rw
116
117
118 final private Thread worker; // Initialized in constructor
119
120 private boolean v3 = true; // Set in setV3()
121
122 final public String host; // used by LdapClient for generating exception messages
123 // used by StartTlsResponse when creating an SSL socket
124 final public int port; // used by LdapClient for generating exception messages
125 // used by StartTlsResponse when creating an SSL socket
126
127 private boolean bound = false; // Set in setBound()
128
129 // All three are initialized in constructor and read-only afterwards
130 private OutputStream traceFile = null;
131 private String traceTagIn = null;
132 private String traceTagOut = null;
325 if (socket == null) {
326 if (debug) {
327 System.err.println("Connection: creating socket");
328 }
329 // connected socket
330 socket = new Socket(host, port);
331 }
332 }
333
334 // For LDAP connect timeouts on LDAP over SSL connections must treat
335 // the SSL handshake following socket connection as part of the timeout.
336 // So explicitly set a socket read timeout, trigger the SSL handshake,
337 // then reset the timeout.
338 if (socket instanceof SSLSocket) {
339 SSLSocket sslSocket = (SSLSocket) socket;
340 if (!IS_HOSTNAME_VERIFICATION_DISABLED) {
341 SSLParameters param = sslSocket.getSSLParameters();
342 param.setEndpointIdentificationAlgorithm("LDAPS");
343 sslSocket.setSSLParameters(param);
344 }
345 if (connectTimeout > 0) {
346 int socketTimeout = sslSocket.getSoTimeout();
347 sslSocket.setSoTimeout(connectTimeout); // reuse full timeout value
348 sslSocket.startHandshake();
349 sslSocket.setSoTimeout(socketTimeout);
350 }
351 }
352 return socket;
353 }
354
355 ////////////////////////////////////////////////////////////////////////////
356 //
357 // Methods to IO to the LDAP server
358 //
359 ////////////////////////////////////////////////////////////////////////////
360
361 synchronized int getMsgId() {
362 return ++outMsgId;
363 }
364
620 }
621 if (bound) {
622 ldapUnbind(reqCtls);
623 }
624 } finally {
625 try {
626 outStream.flush();
627 sock.close();
628 unpauseReader();
629 } catch (IOException ie) {
630 if (debug)
631 System.err.println("Connection: problem closing socket: " + ie);
632 }
633 if (!notifyParent) {
634 LdapRequest ldr = pendingRequests;
635 while (ldr != null) {
636 ldr.cancel();
637 ldr = ldr.next;
638 }
639 }
640 sock = null;
641 }
642 nparent = notifyParent;
643 }
644 if (nparent) {
645 LdapRequest ldr = pendingRequests;
646 while (ldr != null) {
647 ldr.close();
648 ldr = ldr.next;
649 }
650 }
651 }
652 if (nparent) {
653 parent.processConnectionClosure();
654 }
655 }
656
657
658 // Assume everything is "quiet"
659 // "synchronize" might lead to deadlock so don't synchronize method
955 while (nread < length) {
956 int bytesToRead;
957 if (nread >= buf.length) { // need to allocate a larger buffer
958 bytesToRead = Math.min(length - nread, buf.length + 8192);
959 if (buf.length < nread + bytesToRead) {
960 buf = Arrays.copyOf(buf, nread + bytesToRead);
961 }
962 } else {
963 bytesToRead = buf.length - nread;
964 }
965 int count = is.read(buf, nread, bytesToRead);
966 if (count < 0) {
967 if (buf.length != nread)
968 buf = Arrays.copyOf(buf, nread);
969 break;
970 }
971 nread += count;
972 }
973 return buf;
974 }
975 }
|
29 import java.io.BufferedOutputStream;
30 import java.io.InterruptedIOException;
31 import java.io.IOException;
32 import java.io.OutputStream;
33 import java.io.InputStream;
34 import java.net.InetSocketAddress;
35 import java.net.Socket;
36 import javax.net.ssl.SSLSocket;
37
38 import javax.naming.CommunicationException;
39 import javax.naming.ServiceUnavailableException;
40 import javax.naming.NamingException;
41 import javax.naming.InterruptedNamingException;
42
43 import javax.naming.ldap.Control;
44
45 import java.lang.reflect.Method;
46 import java.lang.reflect.InvocationTargetException;
47 import java.security.AccessController;
48 import java.security.PrivilegedAction;
49 import java.security.cert.Certificate;
50 import java.security.cert.X509Certificate;
51 import java.util.Arrays;
52 import java.util.concurrent.CompletableFuture;
53 import java.util.concurrent.ExecutionException;
54 import javax.net.SocketFactory;
55 import javax.net.ssl.SSLParameters;
56 import javax.net.ssl.HandshakeCompletedEvent;
57 import javax.net.ssl.HandshakeCompletedListener;
58 import javax.net.ssl.SSLPeerUnverifiedException;
59 import javax.security.sasl.SaslException;
60
61 /**
62 * A thread that creates a connection to an LDAP server.
63 * After the connection, the thread reads from the connection.
64 * A caller can invoke methods on the instance to read LDAP responses
65 * and to send LDAP requests.
66 * <p>
67 * There is a one-to-one correspondence between an LdapClient and
68 * a Connection. Access to Connection and its methods is only via
69 * LdapClient with two exceptions: SASL authentication and StartTLS.
70 * SASL needs to access Connection's socket IO streams (in order to do encryption
71 * of the security layer). StartTLS needs to do replace IO streams
72 * and close the IO streams on nonfatal close. The code for SASL
73 * authentication can be treated as being the same as from LdapClient
74 * because the SASL code is only ever called from LdapClient, from
75 * inside LdapClient's synchronized authenticate() method. StartTLS is called
76 * directly by the application but should only occur when the underlying
77 * connection is quiet.
78 * <p>
79 * In terms of synchronization, worry about data structures
100 * cleanup() - sync
101 * readReply() - access to sock sync
102 * unpauseReader() - (indirectly via writeRequest) sync on pauseLock
103 * Members used by SASL auth (main thread):
104 * inStream, outStream - no sync; used to construct new stream; accessed
105 * only when conn is "quiet" and not shared
106 * replaceStreams() - sync method
107 * Members used by StartTLS:
108 * inStream, outStream - no sync; used to record the existing streams;
109 * accessed only when conn is "quiet" and not shared
110 * replaceStreams() - sync method
111 * <p>
112 * Handles anonymous, simple, and SASL bind for v3; anonymous and simple
113 * for v2.
114 * %%% made public for access by LdapSasl %%%
115 *
116 * @author Vincent Ryan
117 * @author Rosanna Lee
118 * @author Jagane Sundar
119 */
120 public final class Connection implements Runnable, HandshakeCompletedListener {
121
122 private static final boolean debug = false;
123 private static final int dump = 0; // > 0 r, > 1 rw
124
125
126 final private Thread worker; // Initialized in constructor
127
128 private boolean v3 = true; // Set in setV3()
129
130 final public String host; // used by LdapClient for generating exception messages
131 // used by StartTlsResponse when creating an SSL socket
132 final public int port; // used by LdapClient for generating exception messages
133 // used by StartTlsResponse when creating an SSL socket
134
135 private boolean bound = false; // Set in setBound()
136
137 // All three are initialized in constructor and read-only afterwards
138 private OutputStream traceFile = null;
139 private String traceTagIn = null;
140 private String traceTagOut = null;
333 if (socket == null) {
334 if (debug) {
335 System.err.println("Connection: creating socket");
336 }
337 // connected socket
338 socket = new Socket(host, port);
339 }
340 }
341
342 // For LDAP connect timeouts on LDAP over SSL connections must treat
343 // the SSL handshake following socket connection as part of the timeout.
344 // So explicitly set a socket read timeout, trigger the SSL handshake,
345 // then reset the timeout.
346 if (socket instanceof SSLSocket) {
347 SSLSocket sslSocket = (SSLSocket) socket;
348 if (!IS_HOSTNAME_VERIFICATION_DISABLED) {
349 SSLParameters param = sslSocket.getSSLParameters();
350 param.setEndpointIdentificationAlgorithm("LDAPS");
351 sslSocket.setSSLParameters(param);
352 }
353 sslSocket.addHandshakeCompletedListener(this);
354 if (connectTimeout > 0) {
355 int socketTimeout = sslSocket.getSoTimeout();
356 sslSocket.setSoTimeout(connectTimeout); // reuse full timeout value
357 sslSocket.startHandshake();
358 sslSocket.setSoTimeout(socketTimeout);
359 }
360 }
361 return socket;
362 }
363
364 ////////////////////////////////////////////////////////////////////////////
365 //
366 // Methods to IO to the LDAP server
367 //
368 ////////////////////////////////////////////////////////////////////////////
369
370 synchronized int getMsgId() {
371 return ++outMsgId;
372 }
373
629 }
630 if (bound) {
631 ldapUnbind(reqCtls);
632 }
633 } finally {
634 try {
635 outStream.flush();
636 sock.close();
637 unpauseReader();
638 } catch (IOException ie) {
639 if (debug)
640 System.err.println("Connection: problem closing socket: " + ie);
641 }
642 if (!notifyParent) {
643 LdapRequest ldr = pendingRequests;
644 while (ldr != null) {
645 ldr.cancel();
646 ldr = ldr.next;
647 }
648 }
649 if (isTlsConnection()) {
650 if (closureReason != null) {
651 CommunicationException ce = new CommunicationException();
652 ce.setRootCause(closureReason);
653 tlsHandshakeCompleted.completeExceptionally(ce);
654 } else {
655 tlsHandshakeCompleted.cancel(false);
656 }
657 }
658 sock = null;
659 }
660 nparent = notifyParent;
661 }
662 if (nparent) {
663 LdapRequest ldr = pendingRequests;
664 while (ldr != null) {
665 ldr.close();
666 ldr = ldr.next;
667 }
668 }
669 }
670 if (nparent) {
671 parent.processConnectionClosure();
672 }
673 }
674
675
676 // Assume everything is "quiet"
677 // "synchronize" might lead to deadlock so don't synchronize method
973 while (nread < length) {
974 int bytesToRead;
975 if (nread >= buf.length) { // need to allocate a larger buffer
976 bytesToRead = Math.min(length - nread, buf.length + 8192);
977 if (buf.length < nread + bytesToRead) {
978 buf = Arrays.copyOf(buf, nread + bytesToRead);
979 }
980 } else {
981 bytesToRead = buf.length - nread;
982 }
983 int count = is.read(buf, nread, bytesToRead);
984 if (count < 0) {
985 if (buf.length != nread)
986 buf = Arrays.copyOf(buf, nread);
987 break;
988 }
989 nread += count;
990 }
991 return buf;
992 }
993
994 private CompletableFuture<X509Certificate> tlsHandshakeCompleted =
995 new CompletableFuture<>();
996
997 @Override
998 public void handshakeCompleted(HandshakeCompletedEvent event) {
999 try {
1000 X509Certificate tlsServerCert = null;
1001 Certificate[] certs;
1002 if (event.getSocket().getUseClientMode()) {
1003 certs = event.getPeerCertificates();
1004 } else {
1005 certs = event.getLocalCertificates();
1006 }
1007 if (certs != null && certs.length > 0 &&
1008 certs[0] instanceof X509Certificate) {
1009 tlsServerCert = (X509Certificate) certs[0];
1010 }
1011 tlsHandshakeCompleted.complete(tlsServerCert);
1012 } catch (SSLPeerUnverifiedException ex) {
1013 CommunicationException ce = new CommunicationException();
1014 ce.setRootCause(closureReason);
1015 tlsHandshakeCompleted.completeExceptionally(ex);
1016 }
1017 }
1018
1019 public boolean isTlsConnection() {
1020 return sock instanceof SSLSocket;
1021 }
1022
1023 public X509Certificate getTlsServerCertificate()
1024 throws SaslException {
1025 try {
1026 if (isTlsConnection())
1027 return tlsHandshakeCompleted.get();
1028 } catch (InterruptedException iex) {
1029 throw new SaslException("TLS Handshake Exception ", iex);
1030 } catch (ExecutionException eex) {
1031 throw new SaslException("TLS Handshake Exception ", eex.getCause());
1032 }
1033 return null;
1034 }
1035 }
|