001/* 002 * Copyright 2011-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2011-2024 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-2024 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.NotNull; 064import com.unboundid.util.Nullable; 065import com.unboundid.util.StaticUtils; 066import com.unboundid.util.ThreadSafety; 067import com.unboundid.util.ThreadSafetyLevel; 068 069import static com.unboundid.ldap.listener.ListenerMessages.*; 070 071 072 073/** 074 * This class provides a request handler implementation that can be used to 075 * convert an existing connection to use TLS encryption. It will handle 076 * StartTLS extended operations directly, but will pass all other requests and 077 * responses through to another request handler. 078 */ 079@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 080public final class StartTLSRequestHandler 081 extends LDAPListenerRequestHandler 082{ 083 // Indicates whether the listener should request that the client provide a 084 // certificate. 085 private final boolean requestClientCertificate; 086 087 // Indicates whether the listener should require that the client provide a 088 // certificate. 089 private final boolean requireClientCertificate; 090 091 // The client connection with which this request handler is associated. 092 @Nullable private final LDAPListenerClientConnection connection; 093 094 // The request handler that will be used to process all operations except the 095 // StartTLS extended operation. 096 @NotNull private final LDAPListenerRequestHandler requestHandler; 097 098 // The SSL socket factory that will be used to SSL-enable the existing socket. 099 @NotNull private final SSLSocketFactory sslSocketFactory; 100 101 102 103 /** 104 * Creates a new StartTLS request handler with the provided information. 105 * 106 * @param sslSocketFactory The SSL socket factory that will be used to 107 * convert the existing socket to use SSL 108 * encryption. 109 * @param requestHandler The request handler that will be used to process 110 * all operations except StartTLS extended 111 * operations. 112 */ 113 public StartTLSRequestHandler( 114 @NotNull final SSLSocketFactory sslSocketFactory, 115 @NotNull final LDAPListenerRequestHandler requestHandler) 116 { 117 this(sslSocketFactory, requestHandler, false, false); 118 } 119 120 121 122 /** 123 * Creates a new StartTLS request handler with the provided information. 124 * 125 * @param sslSocketFactory The SSL socket factory that will be used 126 * to convert the existing socket to use SSL 127 * encryption. 128 * @param requestHandler The request handler that will be used to 129 * process all operations except StartTLS 130 * extended operations. 131 * @param requestClientCertificate Indicates whether the listener should 132 * request that the client present its own 133 * certificate chain during TLS negotiation. 134 * This will be ignored for non-TLS-based 135 * connections. 136 * @param requireClientCertificate Indicates whether the listener should 137 * require that the client present its own 138 * certificate chain during TLS negotiation, 139 * and should fail negotiation if the client 140 * does not present one. This will be 141 * ignored for non-TLS-based connections or 142 * if {@code requestClientCertificate} is 143 * {@code false}. 144 */ 145 public StartTLSRequestHandler( 146 @NotNull final SSLSocketFactory sslSocketFactory, 147 @NotNull final LDAPListenerRequestHandler requestHandler, 148 final boolean requestClientCertificate, 149 final boolean requireClientCertificate) 150 { 151 this.sslSocketFactory = sslSocketFactory; 152 this.requestHandler = requestHandler; 153 this.requestClientCertificate = requestClientCertificate; 154 this.requireClientCertificate = requireClientCertificate; 155 156 connection = null; 157 } 158 159 160 161 /** 162 * Creates a new StartTLS request handler with the provided information. 163 * 164 * @param sslSocketFactory The SSL socket factory that will be used 165 * to convert the existing socket to use SSL 166 * encryption. 167 * @param requestHandler The request handler that will be used to 168 * process all operations except StartTLS 169 * extended operations. 170 * @param connection The connection to the associated client. 171 * @param requestClientCertificate Indicates whether the listener should 172 * request that the client present its own 173 * certificate chain during TLS negotiation. 174 * This will be ignored for non-TLS-based 175 * connections. 176 * @param requireClientCertificate Indicates whether the listener should 177 * require that the client present its own 178 * certificate chain during TLS negotiation, 179 * and should fail negotiation if the client 180 * does not present one. This will be 181 * ignored for non-TLS-based connections or 182 * if {@code requestClientCertificate} is 183 * {@code false}. 184 */ 185 private StartTLSRequestHandler( 186 @NotNull final SSLSocketFactory sslSocketFactory, 187 @NotNull final LDAPListenerRequestHandler requestHandler, 188 @NotNull final LDAPListenerClientConnection connection, 189 final boolean requestClientCertificate, 190 final boolean requireClientCertificate) 191 { 192 this.sslSocketFactory = sslSocketFactory; 193 this.requestHandler = requestHandler; 194 this.connection = connection; 195 this.requestClientCertificate = requestClientCertificate; 196 this.requireClientCertificate = requireClientCertificate; 197 } 198 199 200 201 /** 202 * {@inheritDoc} 203 */ 204 @Override() 205 @NotNull() 206 public StartTLSRequestHandler newInstance( 207 @NotNull final LDAPListenerClientConnection connection) 208 throws LDAPException 209 { 210 return new StartTLSRequestHandler(sslSocketFactory, 211 requestHandler.newInstance(connection), connection, 212 requestClientCertificate, requireClientCertificate); 213 } 214 215 216 217 /** 218 * {@inheritDoc} 219 */ 220 @Override() 221 public void closeInstance() 222 { 223 requestHandler.closeInstance(); 224 } 225 226 227 228 /** 229 * {@inheritDoc} 230 */ 231 @Override() 232 public void processAbandonRequest(final int messageID, 233 @NotNull final AbandonRequestProtocolOp request, 234 @NotNull final List<Control> controls) 235 { 236 requestHandler.processAbandonRequest(messageID, request, controls); 237 } 238 239 240 241 /** 242 * {@inheritDoc} 243 */ 244 @Override() 245 @NotNull() 246 public LDAPMessage processAddRequest(final int messageID, 247 @NotNull final AddRequestProtocolOp request, 248 @NotNull final List<Control> controls) 249 { 250 return requestHandler.processAddRequest(messageID, request, controls); 251 } 252 253 254 255 /** 256 * {@inheritDoc} 257 */ 258 @Override() 259 @NotNull() 260 public LDAPMessage processBindRequest(final int messageID, 261 @NotNull final BindRequestProtocolOp request, 262 @NotNull final List<Control> controls) 263 { 264 return requestHandler.processBindRequest(messageID, request, controls); 265 } 266 267 268 269 /** 270 * {@inheritDoc} 271 */ 272 @Override() 273 @NotNull() 274 public LDAPMessage processCompareRequest(final int messageID, 275 @NotNull final CompareRequestProtocolOp request, 276 @NotNull final List<Control> controls) 277 { 278 return requestHandler.processCompareRequest(messageID, request, controls); 279 } 280 281 282 283 /** 284 * {@inheritDoc} 285 */ 286 @Override() 287 @NotNull() 288 public LDAPMessage processDeleteRequest(final int messageID, 289 @NotNull final DeleteRequestProtocolOp request, 290 @NotNull final List<Control> controls) 291 { 292 return requestHandler.processDeleteRequest(messageID, request, controls); 293 } 294 295 296 297 /** 298 * {@inheritDoc} 299 */ 300 @Override() 301 @NotNull() 302 public LDAPMessage processExtendedRequest(final int messageID, 303 @NotNull final ExtendedRequestProtocolOp request, 304 @NotNull final List<Control> controls) 305 { 306 if (request.getOID().equals(StartTLSExtendedRequest.STARTTLS_REQUEST_OID)) 307 { 308 try 309 { 310 // Make sure we can decode the request as a valid StartTLS request. 311 final StartTLSExtendedRequest startTLSRequest = 312 new StartTLSExtendedRequest(new ExtendedRequest(request.getOID(), 313 request.getValue())); 314 315 final OutputStream clearOutputStream = connection.convertToTLS( 316 sslSocketFactory, requestClientCertificate, 317 requireClientCertificate); 318 319 final LDAPMessage responseMessage = new LDAPMessage(messageID, 320 new ExtendedResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null, 321 null, null, null, null)); 322 final ASN1Buffer buffer = new ASN1Buffer(); 323 responseMessage.writeTo(buffer); 324 325 try 326 { 327 buffer.writeTo(clearOutputStream); 328 clearOutputStream.flush(); 329 } 330 catch (final Exception e) 331 { 332 Debug.debugException(e); 333 final LDAPException le = new LDAPException(ResultCode.LOCAL_ERROR, 334 ERR_START_TLS_REQUEST_HANDLER_WRITE_RESPONSE_FAILURE.get( 335 StaticUtils.getExceptionMessage(e)), 336 e); 337 connection.close(le); 338 throw le; 339 } 340 341 return responseMessage; 342 } 343 catch (final LDAPException le) 344 { 345 Debug.debugException(le); 346 347 return new LDAPMessage(messageID, 348 new ExtendedResponseProtocolOp(le.getResultCode().intValue(), 349 le.getMatchedDN(), le.getDiagnosticMessage(), 350 StaticUtils.toList(le.getReferralURLs()), null, null), 351 le.getResponseControls()); 352 } 353 } 354 else 355 { 356 return requestHandler.processExtendedRequest(messageID, request, 357 controls); 358 } 359 } 360 361 362 363 /** 364 * {@inheritDoc} 365 */ 366 @Override() 367 @NotNull() 368 public LDAPMessage processModifyRequest(final int messageID, 369 @NotNull final ModifyRequestProtocolOp request, 370 @NotNull final List<Control> controls) 371 { 372 return requestHandler.processModifyRequest(messageID, request, controls); 373 } 374 375 376 377 /** 378 * {@inheritDoc} 379 */ 380 @Override() 381 @NotNull() 382 public LDAPMessage processModifyDNRequest(final int messageID, 383 @NotNull final ModifyDNRequestProtocolOp request, 384 @NotNull final List<Control> controls) 385 { 386 return requestHandler.processModifyDNRequest(messageID, request, controls); 387 } 388 389 390 391 /** 392 * {@inheritDoc} 393 */ 394 @Override() 395 @NotNull() 396 public LDAPMessage processSearchRequest(final int messageID, 397 @NotNull final SearchRequestProtocolOp request, 398 @NotNull final List<Control> controls) 399 { 400 return requestHandler.processSearchRequest(messageID, request, controls); 401 } 402 403 404 405 /** 406 * {@inheritDoc} 407 */ 408 @Override() 409 public void processUnbindRequest(final int messageID, 410 @NotNull final UnbindRequestProtocolOp request, 411 @NotNull final List<Control> controls) 412 { 413 requestHandler.processUnbindRequest(messageID, request, controls); 414 } 415}