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.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 // The client connection with which this request handler is associated. 084 @Nullable private final LDAPListenerClientConnection connection; 085 086 // The request handler that will be used to process all operations except the 087 // StartTLS extended operation. 088 @NotNull private final LDAPListenerRequestHandler requestHandler; 089 090 // The SSL socket factory that will be used to SSL-enable the existing socket. 091 @NotNull private final SSLSocketFactory sslSocketFactory; 092 093 094 095 /** 096 * Creates a new StartTLS request handler with the provided information. 097 * 098 * @param sslSocketFactory The SSL socket factory that will be used to 099 * convert the existing socket to use SSL 100 * encryption. 101 * @param requestHandler The request handler that will be used to process 102 * all operations except StartTLS extended 103 * operations. 104 */ 105 public StartTLSRequestHandler( 106 @NotNull final SSLSocketFactory sslSocketFactory, 107 @NotNull final LDAPListenerRequestHandler requestHandler) 108 { 109 this.sslSocketFactory = sslSocketFactory; 110 this.requestHandler = requestHandler; 111 112 connection = null; 113 } 114 115 116 117 /** 118 * Creates a new StartTLS request handler with the provided information. 119 * 120 * @param sslSocketFactory The SSL socket factory that will be used to 121 * convert the existing socket to use SSL 122 * encryption. 123 * @param requestHandler The request handler that will be used to process 124 * all operations except StartTLS extended 125 * operations. 126 * @param connection The connection to the associated client. 127 */ 128 private StartTLSRequestHandler( 129 @NotNull final SSLSocketFactory sslSocketFactory, 130 @NotNull final LDAPListenerRequestHandler requestHandler, 131 @NotNull final LDAPListenerClientConnection connection) 132 { 133 this.sslSocketFactory = sslSocketFactory; 134 this.requestHandler = requestHandler; 135 this.connection = connection; 136 } 137 138 139 140 /** 141 * {@inheritDoc} 142 */ 143 @Override() 144 @NotNull() 145 public StartTLSRequestHandler newInstance( 146 @NotNull final LDAPListenerClientConnection connection) 147 throws LDAPException 148 { 149 return new StartTLSRequestHandler(sslSocketFactory, 150 requestHandler.newInstance(connection), connection); 151 } 152 153 154 155 /** 156 * {@inheritDoc} 157 */ 158 @Override() 159 public void closeInstance() 160 { 161 requestHandler.closeInstance(); 162 } 163 164 165 166 /** 167 * {@inheritDoc} 168 */ 169 @Override() 170 public void processAbandonRequest(final int messageID, 171 @NotNull final AbandonRequestProtocolOp request, 172 @NotNull final List<Control> controls) 173 { 174 requestHandler.processAbandonRequest(messageID, request, controls); 175 } 176 177 178 179 /** 180 * {@inheritDoc} 181 */ 182 @Override() 183 @NotNull() 184 public LDAPMessage processAddRequest(final int messageID, 185 @NotNull final AddRequestProtocolOp request, 186 @NotNull final List<Control> controls) 187 { 188 return requestHandler.processAddRequest(messageID, request, controls); 189 } 190 191 192 193 /** 194 * {@inheritDoc} 195 */ 196 @Override() 197 @NotNull() 198 public LDAPMessage processBindRequest(final int messageID, 199 @NotNull final BindRequestProtocolOp request, 200 @NotNull final List<Control> controls) 201 { 202 return requestHandler.processBindRequest(messageID, request, controls); 203 } 204 205 206 207 /** 208 * {@inheritDoc} 209 */ 210 @Override() 211 @NotNull() 212 public LDAPMessage processCompareRequest(final int messageID, 213 @NotNull final CompareRequestProtocolOp request, 214 @NotNull final List<Control> controls) 215 { 216 return requestHandler.processCompareRequest(messageID, request, controls); 217 } 218 219 220 221 /** 222 * {@inheritDoc} 223 */ 224 @Override() 225 @NotNull() 226 public LDAPMessage processDeleteRequest(final int messageID, 227 @NotNull final DeleteRequestProtocolOp request, 228 @NotNull final List<Control> controls) 229 { 230 return requestHandler.processDeleteRequest(messageID, request, controls); 231 } 232 233 234 235 /** 236 * {@inheritDoc} 237 */ 238 @Override() 239 @NotNull() 240 public LDAPMessage processExtendedRequest(final int messageID, 241 @NotNull final ExtendedRequestProtocolOp request, 242 @NotNull final List<Control> controls) 243 { 244 if (request.getOID().equals(StartTLSExtendedRequest.STARTTLS_REQUEST_OID)) 245 { 246 try 247 { 248 // Make sure we can decode the request as a valid StartTLS request. 249 final StartTLSExtendedRequest startTLSRequest = 250 new StartTLSExtendedRequest(new ExtendedRequest(request.getOID(), 251 request.getValue())); 252 253 final OutputStream clearOutputStream = 254 connection.convertToTLS(sslSocketFactory); 255 256 final LDAPMessage responseMessage = new LDAPMessage(messageID, 257 new ExtendedResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null, 258 null, null, null, null)); 259 final ASN1Buffer buffer = new ASN1Buffer(); 260 responseMessage.writeTo(buffer); 261 262 try 263 { 264 buffer.writeTo(clearOutputStream); 265 clearOutputStream.flush(); 266 } 267 catch (final Exception e) 268 { 269 Debug.debugException(e); 270 final LDAPException le = new LDAPException(ResultCode.LOCAL_ERROR, 271 ERR_START_TLS_REQUEST_HANDLER_WRITE_RESPONSE_FAILURE.get( 272 StaticUtils.getExceptionMessage(e)), 273 e); 274 connection.close(le); 275 throw le; 276 } 277 278 return responseMessage; 279 } 280 catch (final LDAPException le) 281 { 282 Debug.debugException(le); 283 284 return new LDAPMessage(messageID, 285 new ExtendedResponseProtocolOp(le.getResultCode().intValue(), 286 le.getMatchedDN(), le.getDiagnosticMessage(), 287 StaticUtils.toList(le.getReferralURLs()), null, null), 288 le.getResponseControls()); 289 } 290 } 291 else 292 { 293 return requestHandler.processExtendedRequest(messageID, request, 294 controls); 295 } 296 } 297 298 299 300 /** 301 * {@inheritDoc} 302 */ 303 @Override() 304 @NotNull() 305 public LDAPMessage processModifyRequest(final int messageID, 306 @NotNull final ModifyRequestProtocolOp request, 307 @NotNull final List<Control> controls) 308 { 309 return requestHandler.processModifyRequest(messageID, request, controls); 310 } 311 312 313 314 /** 315 * {@inheritDoc} 316 */ 317 @Override() 318 @NotNull() 319 public LDAPMessage processModifyDNRequest(final int messageID, 320 @NotNull final ModifyDNRequestProtocolOp request, 321 @NotNull final List<Control> controls) 322 { 323 return requestHandler.processModifyDNRequest(messageID, request, controls); 324 } 325 326 327 328 /** 329 * {@inheritDoc} 330 */ 331 @Override() 332 @NotNull() 333 public LDAPMessage processSearchRequest(final int messageID, 334 @NotNull final SearchRequestProtocolOp request, 335 @NotNull final List<Control> controls) 336 { 337 return requestHandler.processSearchRequest(messageID, request, controls); 338 } 339 340 341 342 /** 343 * {@inheritDoc} 344 */ 345 @Override() 346 public void processUnbindRequest(final int messageID, 347 @NotNull final UnbindRequestProtocolOp request, 348 @NotNull final List<Control> controls) 349 { 350 requestHandler.processUnbindRequest(messageID, request, controls); 351 } 352}