001/* 002 * Copyright 2011-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2011-2020 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2011-2020 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.listener; 037 038 039 040import java.io.OutputStream; 041import java.util.List; 042import javax.net.ssl.SSLSocketFactory; 043 044import com.unboundid.asn1.ASN1Buffer; 045import com.unboundid.ldap.protocol.AbandonRequestProtocolOp; 046import com.unboundid.ldap.protocol.AddRequestProtocolOp; 047import com.unboundid.ldap.protocol.BindRequestProtocolOp; 048import com.unboundid.ldap.protocol.CompareRequestProtocolOp; 049import com.unboundid.ldap.protocol.DeleteRequestProtocolOp; 050import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp; 051import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp; 052import com.unboundid.ldap.protocol.ModifyRequestProtocolOp; 053import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp; 054import com.unboundid.ldap.protocol.SearchRequestProtocolOp; 055import com.unboundid.ldap.protocol.UnbindRequestProtocolOp; 056import com.unboundid.ldap.protocol.LDAPMessage; 057import com.unboundid.ldap.sdk.Control; 058import com.unboundid.ldap.sdk.ExtendedRequest; 059import com.unboundid.ldap.sdk.LDAPException; 060import com.unboundid.ldap.sdk.ResultCode; 061import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest; 062import com.unboundid.util.Debug; 063import com.unboundid.util.StaticUtils; 064import com.unboundid.util.ThreadSafety; 065import com.unboundid.util.ThreadSafetyLevel; 066 067import static com.unboundid.ldap.listener.ListenerMessages.*; 068 069 070 071/** 072 * This class provides a request handler implementation that can be used to 073 * convert an existing connection to use TLS encryption. It will handle 074 * StartTLS extended operations directly, but will pass all other requests and 075 * responses through to another request handler. 076 */ 077@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 078public final class StartTLSRequestHandler 079 extends LDAPListenerRequestHandler 080{ 081 // The client connection with which this request handler is associated. 082 private final LDAPListenerClientConnection connection; 083 084 // The request handler that will be used to process all operations except the 085 // StartTLS extended operation. 086 private final LDAPListenerRequestHandler requestHandler; 087 088 // The SSL socket factory that will be used to SSL-enable the existing socket. 089 private final SSLSocketFactory sslSocketFactory; 090 091 092 093 /** 094 * Creates a new StartTLS request handler with the provided information. 095 * 096 * @param sslSocketFactory The SSL socket factory that will be used to 097 * convert the existing socket to use SSL 098 * encryption. 099 * @param requestHandler The request handler that will be used to process 100 * all operations except StartTLS extended 101 * operations. 102 */ 103 public StartTLSRequestHandler(final SSLSocketFactory sslSocketFactory, 104 final LDAPListenerRequestHandler requestHandler) 105 { 106 this.sslSocketFactory = sslSocketFactory; 107 this.requestHandler = requestHandler; 108 109 connection = null; 110 } 111 112 113 114 /** 115 * Creates a new StartTLS request handler with the provided information. 116 * 117 * @param sslSocketFactory The SSL socket factory that will be used to 118 * convert the existing socket to use SSL 119 * encryption. 120 * @param requestHandler The request handler that will be used to process 121 * all operations except StartTLS extended 122 * operations. 123 * @param connection The connection to the associated client. 124 */ 125 private StartTLSRequestHandler(final SSLSocketFactory sslSocketFactory, 126 final LDAPListenerRequestHandler requestHandler, 127 final LDAPListenerClientConnection connection) 128 { 129 this.sslSocketFactory = sslSocketFactory; 130 this.requestHandler = requestHandler; 131 this.connection = connection; 132 } 133 134 135 136 /** 137 * {@inheritDoc} 138 */ 139 @Override() 140 public StartTLSRequestHandler 141 newInstance(final LDAPListenerClientConnection connection) 142 throws LDAPException 143 { 144 return new StartTLSRequestHandler(sslSocketFactory, 145 requestHandler.newInstance(connection), connection); 146 } 147 148 149 150 /** 151 * {@inheritDoc} 152 */ 153 @Override() 154 public void closeInstance() 155 { 156 requestHandler.closeInstance(); 157 } 158 159 160 161 /** 162 * {@inheritDoc} 163 */ 164 @Override() 165 public void processAbandonRequest(final int messageID, 166 final AbandonRequestProtocolOp request, 167 final List<Control> controls) 168 { 169 requestHandler.processAbandonRequest(messageID, request, controls); 170 } 171 172 173 174 /** 175 * {@inheritDoc} 176 */ 177 @Override() 178 public LDAPMessage processAddRequest(final int messageID, 179 final AddRequestProtocolOp request, 180 final List<Control> controls) 181 { 182 return requestHandler.processAddRequest(messageID, request, controls); 183 } 184 185 186 187 /** 188 * {@inheritDoc} 189 */ 190 @Override() 191 public LDAPMessage processBindRequest(final int messageID, 192 final BindRequestProtocolOp request, 193 final List<Control> controls) 194 { 195 return requestHandler.processBindRequest(messageID, request, controls); 196 } 197 198 199 200 /** 201 * {@inheritDoc} 202 */ 203 @Override() 204 public LDAPMessage processCompareRequest(final int messageID, 205 final CompareRequestProtocolOp request, 206 final List<Control> controls) 207 { 208 return requestHandler.processCompareRequest(messageID, request, controls); 209 } 210 211 212 213 /** 214 * {@inheritDoc} 215 */ 216 @Override() 217 public LDAPMessage processDeleteRequest(final int messageID, 218 final DeleteRequestProtocolOp request, 219 final List<Control> controls) 220 { 221 return requestHandler.processDeleteRequest(messageID, request, controls); 222 } 223 224 225 226 /** 227 * {@inheritDoc} 228 */ 229 @Override() 230 public LDAPMessage processExtendedRequest(final int messageID, 231 final ExtendedRequestProtocolOp request, 232 final List<Control> controls) 233 { 234 if (request.getOID().equals(StartTLSExtendedRequest.STARTTLS_REQUEST_OID)) 235 { 236 try 237 { 238 // Make sure we can decode the request as a valid StartTLS request. 239 final StartTLSExtendedRequest startTLSRequest = 240 new StartTLSExtendedRequest(new ExtendedRequest(request.getOID(), 241 request.getValue())); 242 243 final OutputStream clearOutputStream = 244 connection.convertToTLS(sslSocketFactory); 245 246 final LDAPMessage responseMessage = new LDAPMessage(messageID, 247 new ExtendedResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null, 248 null, null, null, null)); 249 final ASN1Buffer buffer = new ASN1Buffer(); 250 responseMessage.writeTo(buffer); 251 252 try 253 { 254 buffer.writeTo(clearOutputStream); 255 clearOutputStream.flush(); 256 } 257 catch (final Exception e) 258 { 259 Debug.debugException(e); 260 final LDAPException le = new LDAPException(ResultCode.LOCAL_ERROR, 261 ERR_START_TLS_REQUEST_HANDLER_WRITE_RESPONSE_FAILURE.get( 262 StaticUtils.getExceptionMessage(e)), 263 e); 264 connection.close(le); 265 throw le; 266 } 267 268 return responseMessage; 269 } 270 catch (final LDAPException le) 271 { 272 Debug.debugException(le); 273 274 return new LDAPMessage(messageID, 275 new ExtendedResponseProtocolOp(le.getResultCode().intValue(), 276 le.getMatchedDN(), le.getDiagnosticMessage(), 277 StaticUtils.toList(le.getReferralURLs()), null, null), 278 le.getResponseControls()); 279 } 280 } 281 else 282 { 283 return requestHandler.processExtendedRequest(messageID, request, 284 controls); 285 } 286 } 287 288 289 290 /** 291 * {@inheritDoc} 292 */ 293 @Override() 294 public LDAPMessage processModifyRequest(final int messageID, 295 final ModifyRequestProtocolOp request, 296 final List<Control> controls) 297 { 298 return requestHandler.processModifyRequest(messageID, request, controls); 299 } 300 301 302 303 /** 304 * {@inheritDoc} 305 */ 306 @Override() 307 public LDAPMessage processModifyDNRequest(final int messageID, 308 final ModifyDNRequestProtocolOp request, 309 final List<Control> controls) 310 { 311 return requestHandler.processModifyDNRequest(messageID, request, controls); 312 } 313 314 315 316 /** 317 * {@inheritDoc} 318 */ 319 @Override() 320 public LDAPMessage processSearchRequest(final int messageID, 321 final SearchRequestProtocolOp request, 322 final List<Control> controls) 323 { 324 return requestHandler.processSearchRequest(messageID, request, controls); 325 } 326 327 328 329 /** 330 * {@inheritDoc} 331 */ 332 @Override() 333 public void processUnbindRequest(final int messageID, 334 final UnbindRequestProtocolOp request, 335 final List<Control> controls) 336 { 337 requestHandler.processUnbindRequest(messageID, request, controls); 338 } 339}