001/* 002 * Copyright 2012-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2012-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) 2015-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.sdk.unboundidds; 037 038 039 040import java.util.ArrayList; 041import java.util.List; 042 043import com.unboundid.asn1.ASN1OctetString; 044import com.unboundid.ldap.sdk.BindResult; 045import com.unboundid.ldap.sdk.Control; 046import com.unboundid.ldap.sdk.InternalSDKHelper; 047import com.unboundid.ldap.sdk.LDAPConnection; 048import com.unboundid.ldap.sdk.LDAPException; 049import com.unboundid.ldap.sdk.SASLBindRequest; 050import com.unboundid.ldap.sdk.ToCodeArgHelper; 051import com.unboundid.ldap.sdk.ToCodeHelper; 052import com.unboundid.util.ThreadSafety; 053import com.unboundid.util.ThreadSafetyLevel; 054import com.unboundid.util.Validator; 055 056 057 058/** 059 * This class provides support for an UnboundID-proprietary SASL mechanism that 060 * provides multifactor authentication using the combination of a client 061 * certificate (presented during SSL/TLS negotiation) and a static password. 062 * <BR> 063 * <BLOCKQUOTE> 064 * <B>NOTE:</B> This class, and other classes within the 065 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 066 * supported for use against Ping Identity, UnboundID, and 067 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 068 * for proprietary functionality or for external specifications that are not 069 * considered stable or mature enough to be guaranteed to work in an 070 * interoperable way with other types of LDAP servers. 071 * </BLOCKQUOTE> 072 * <BR> 073 * The name for this SASL mechanism is "UNBOUNDID-CERTIFICATE-PLUS-PASSWORD". 074 * The SASL credentials consist simply of the static password for the user 075 * identified by the certificate, to make the SASL mechanism as easy as possible 076 * to use from other client APIs. 077 */ 078@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 079public final class UnboundIDCertificatePlusPasswordBindRequest 080 extends SASLBindRequest 081{ 082 /** 083 * The name for the UnboundID certificate plus password SASL mechanism. 084 */ 085 public static final String UNBOUNDID_CERT_PLUS_PW_MECHANISM_NAME = 086 "UNBOUNDID-CERTIFICATE-PLUS-PASSWORD"; 087 088 089 090 /** 091 * The serial version UID for this serializable class. 092 */ 093 private static final long serialVersionUID = 8863298749835036708L; 094 095 096 097 // The password to use to authenticate. 098 private final ASN1OctetString password; 099 100 // The message ID from the last LDAP message sent from this request. 101 private volatile int messageID = -1; 102 103 104 105 /** 106 * Creates a new certificate plus password bind request with the provided 107 * information. 108 * 109 * @param password The password to use to authenticate as user identified by 110 * the certificate. It must not be {@code null} or empty. 111 * @param controls The set of controls to include in the bind request. It 112 * may be {@code null} or empty if no request controls are 113 * needed. 114 */ 115 public UnboundIDCertificatePlusPasswordBindRequest(final String password, 116 final Control... controls) 117 { 118 this(new ASN1OctetString(CRED_TYPE_SASL, password), controls); 119 } 120 121 122 123 /** 124 * Creates a new certificate plus password bind request with the provided 125 * information. 126 * 127 * @param password The password to use to authenticate as user identified by 128 * the certificate. It must not be {@code null} or empty. 129 * @param controls The set of controls to include in the bind request. It 130 * may be {@code null} or empty if no request controls are 131 * needed. 132 */ 133 public UnboundIDCertificatePlusPasswordBindRequest(final byte[] password, 134 final Control... controls) 135 { 136 this(new ASN1OctetString(CRED_TYPE_SASL, password), controls); 137 } 138 139 140 141 /** 142 * Creates a new certificate plus password bind request with the provided 143 * information. 144 * 145 * @param password The password to use to authenticate as user identified by 146 * the certificate. It must not be {@code null} or empty. 147 * @param controls The set of controls to include in the bind request. It 148 * may be {@code null} or empty if no request controls are 149 * needed. 150 */ 151 private UnboundIDCertificatePlusPasswordBindRequest( 152 final ASN1OctetString password, final Control... controls) 153 { 154 super(controls); 155 156 Validator.ensureFalse((password.getValueLength() == 0), 157 "The bind password must not be empty"); 158 159 this.password = password; 160 } 161 162 163 164 /** 165 * Retrieves the password to use to authenticate as the user identified by the 166 * certificate. 167 * 168 * @return The password to use to authenticate as the user identified by the 169 * certificate. 170 */ 171 public ASN1OctetString getPassword() 172 { 173 return password; 174 } 175 176 177 178 /** 179 * {@inheritDoc} 180 */ 181 @Override() 182 public String getSASLMechanismName() 183 { 184 return UNBOUNDID_CERT_PLUS_PW_MECHANISM_NAME; 185 } 186 187 188 189 /** 190 * {@inheritDoc} 191 */ 192 @Override() 193 protected BindResult process(final LDAPConnection connection, final int depth) 194 throws LDAPException 195 { 196 messageID = InternalSDKHelper.nextMessageID(connection); 197 return sendBindRequest(connection, "", password, getControls(), 198 getResponseTimeoutMillis(connection)); 199 } 200 201 202 203 /** 204 * {@inheritDoc} 205 */ 206 @Override() 207 public int getLastMessageID() 208 { 209 return messageID; 210 } 211 212 213 214 /** 215 * {@inheritDoc} 216 */ 217 @Override() 218 public UnboundIDCertificatePlusPasswordBindRequest duplicate() 219 { 220 return duplicate(getControls()); 221 } 222 223 224 225 /** 226 * {@inheritDoc} 227 */ 228 @Override() 229 public UnboundIDCertificatePlusPasswordBindRequest duplicate( 230 final Control[] controls) 231 { 232 final UnboundIDCertificatePlusPasswordBindRequest bindRequest = 233 new UnboundIDCertificatePlusPasswordBindRequest(password, controls); 234 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 235 return bindRequest; 236 } 237 238 239 240 /** 241 * {@inheritDoc} 242 */ 243 @Override() 244 public UnboundIDCertificatePlusPasswordBindRequest getRebindRequest( 245 final String host, final int port) 246 { 247 return duplicate(); 248 } 249 250 251 252 /** 253 * {@inheritDoc} 254 */ 255 @Override() 256 public void toString(final StringBuilder buffer) 257 { 258 buffer.append("UnboundIDCertificatePlusPasswordBindRequest("); 259 260 final Control[] controls = getControls(); 261 if (controls.length > 0) 262 { 263 buffer.append("controls={"); 264 for (int i=0; i < controls.length; i++) 265 { 266 if (i > 0) 267 { 268 buffer.append(", "); 269 } 270 271 buffer.append(controls[i]); 272 } 273 buffer.append('}'); 274 } 275 276 buffer.append(')'); 277 } 278 279 280 281 /** 282 * {@inheritDoc} 283 */ 284 @Override() 285 public void toCode(final List<String> lineList, final String requestID, 286 final int indentSpaces, final boolean includeProcessing) 287 { 288 // Create the request variable. 289 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(2); 290 constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---", 291 "Bind Password")); 292 293 final Control[] controls = getControls(); 294 if (controls.length > 0) 295 { 296 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 297 "Bind Controls")); 298 } 299 300 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 301 "UnboundIDCertificatePlusPasswordBindRequest", requestID + "Request", 302 "new UnboundIDCertificatePlusPasswordBindRequest", constructorArgs); 303 304 305 // Add lines for processing the request and obtaining the result. 306 if (includeProcessing) 307 { 308 // Generate a string with the appropriate indent. 309 final StringBuilder buffer = new StringBuilder(); 310 for (int i=0; i < indentSpaces; i++) 311 { 312 buffer.append(' '); 313 } 314 final String indent = buffer.toString(); 315 316 lineList.add(""); 317 lineList.add(indent + "try"); 318 lineList.add(indent + '{'); 319 lineList.add(indent + " BindResult " + requestID + 320 "Result = connection.bind(" + requestID + "Request);"); 321 lineList.add(indent + " // The bind was processed successfully."); 322 lineList.add(indent + '}'); 323 lineList.add(indent + "catch (LDAPException e)"); 324 lineList.add(indent + '{'); 325 lineList.add(indent + " // The bind failed. Maybe the following will " + 326 "help explain why."); 327 lineList.add(indent + " // Note that the connection is now likely in " + 328 "an unauthenticated state."); 329 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 330 lineList.add(indent + " String message = e.getMessage();"); 331 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 332 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 333 lineList.add(indent + " Control[] responseControls = " + 334 "e.getResponseControls();"); 335 lineList.add(indent + '}'); 336 } 337 } 338}