1 /* 2 * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jndi.ldap; 27 28 import java.io.*; 29 import java.util.Locale; 30 import java.util.Vector; 31 import java.util.Hashtable; 32 33 import javax.naming.*; 34 import javax.naming.directory.*; 35 import javax.naming.ldap.*; 36 37 import com.sun.jndi.ldap.pool.PooledConnection; 38 import com.sun.jndi.ldap.pool.PoolCallback; 39 import com.sun.jndi.ldap.sasl.LdapSasl; 40 import com.sun.jndi.ldap.sasl.SaslInputStream; 41 42 /** 43 * LDAP (RFC-1777) and LDAPv3 (RFC-2251) compliant client 44 * 45 * This class represents a connection to an LDAP client. 46 * Callers interact with this class at an LDAP operation level. 47 * That is, the caller invokes a method to do a SEARCH or MODRDN 48 * operation and gets back the result. 49 * The caller uses the constructor to create a connection to the server. 50 * It then needs to use authenticate() to perform an LDAP BIND. 51 * Note that for v3, BIND is optional so authenticate() might not 52 * actually send a BIND. authenticate() can be used later on to issue 53 * a BIND, for example, for a v3 client that wants to change the connection's 54 * credentials. 55 *<p> 56 * Multiple LdapCtx might share the same LdapClient. For example, contexts 57 * derived from the same initial context would share the same LdapClient 58 * until changes to a context's properties necessitates its own LdapClient. 59 * LdapClient methods that access shared data are thread-safe (i.e., caller 60 * does not have to sync). 61 *<p> 62 * Fields: 63 * isLdapv3 - no sync; initialized and updated within sync authenticate(); 64 * always updated when connection is "quiet" and not shared; 65 * read access from outside LdapClient not sync 66 * referenceCount - sync within LdapClient; exception is forceClose() which 67 * is used by Connection thread to close connection upon receiving 68 * an Unsolicited Notification. 69 * access from outside LdapClient must sync; 70 * conn - no sync; Connection takes care of its own sync 71 * unsolicited - sync Vector; multiple operations sync'ed 72 * 73 * @author Vincent Ryan 74 * @author Jagane Sundar 75 * @author Rosanna Lee 76 */ 77 78 public final class LdapClient implements PooledConnection { 79 // ---------------------- Constants ---------------------------------- 80 private static final int debug = 0; 81 static final boolean caseIgnore = true; 82 83 // Default list of binary attributes 84 private static final Hashtable<String, Boolean> defaultBinaryAttrs = 85 new Hashtable<>(23,0.75f); 86 static { 87 defaultBinaryAttrs.put("userpassword", Boolean.TRUE); //2.5.4.35 88 defaultBinaryAttrs.put("javaserializeddata", Boolean.TRUE); 89 //1.3.6.1.4.1.42.2.27.4.1.8 90 defaultBinaryAttrs.put("javaserializedobject", Boolean.TRUE); 91 // 1.3.6.1.4.1.42.2.27.4.1.2 92 defaultBinaryAttrs.put("jpegphoto", Boolean.TRUE); 93 //0.9.2342.19200300.100.1.60 94 defaultBinaryAttrs.put("audio", Boolean.TRUE); //0.9.2342.19200300.100.1.55 95 defaultBinaryAttrs.put("thumbnailphoto", Boolean.TRUE); 96 //1.3.6.1.4.1.1466.101.120.35 97 defaultBinaryAttrs.put("thumbnaillogo", Boolean.TRUE); 98 //1.3.6.1.4.1.1466.101.120.36 99 defaultBinaryAttrs.put("usercertificate", Boolean.TRUE); //2.5.4.36 100 defaultBinaryAttrs.put("cacertificate", Boolean.TRUE); //2.5.4.37 101 defaultBinaryAttrs.put("certificaterevocationlist", Boolean.TRUE); 102 //2.5.4.39 103 defaultBinaryAttrs.put("authorityrevocationlist", Boolean.TRUE); //2.5.4.38 104 defaultBinaryAttrs.put("crosscertificatepair", Boolean.TRUE); //2.5.4.40 105 defaultBinaryAttrs.put("photo", Boolean.TRUE); //0.9.2342.19200300.100.1.7 106 defaultBinaryAttrs.put("personalsignature", Boolean.TRUE); 107 //0.9.2342.19200300.100.1.53 108 defaultBinaryAttrs.put("x500uniqueidentifier", Boolean.TRUE); //2.5.4.45 109 } 110 111 private static final String DISCONNECT_OID = "1.3.6.1.4.1.1466.20036"; 112 113 114 // ----------------------- instance fields ------------------------ 115 boolean isLdapv3; // Used by LdapCtx 116 int referenceCount = 1; // Used by LdapCtx for check for sharing 117 118 final Connection conn; // Connection to server; has reader thread 119 // used by LdapCtx for StartTLS 120 121 private final PoolCallback pcb; 122 private final boolean pooled; 123 private boolean authenticateCalled = false; 124 125 //////////////////////////////////////////////////////////////////////////// 126 // 127 // constructor: Create an authenticated connection to server 128 // 129 //////////////////////////////////////////////////////////////////////////// 130 LdapClient(String host, int port, String socketFactory, int connectTimeout, int readTimeout, OutputStream trace, PoolCallback pcb)131 LdapClient(String host, int port, String socketFactory, 132 int connectTimeout, int readTimeout, OutputStream trace, PoolCallback pcb) 133 throws NamingException { 134 135 if (debug > 0) 136 System.err.println("LdapClient: constructor called " + host + ":" + port ); 137 conn = new Connection(this, host, port, socketFactory, connectTimeout, readTimeout, 138 trace); 139 140 this.pcb = pcb; 141 pooled = (pcb != null); 142 } 143 authenticateCalled()144 synchronized boolean authenticateCalled() { 145 return authenticateCalled; 146 } 147 148 synchronized LdapResult authenticate(boolean initial, String name, Object pw, int version, String authMechanism, Control[] ctls, Hashtable<?,?> env)149 authenticate(boolean initial, String name, Object pw, int version, 150 String authMechanism, Control[] ctls, Hashtable<?,?> env) 151 throws NamingException { 152 153 int readTimeout = conn.readTimeout; 154 conn.readTimeout = conn.connectTimeout; 155 LdapResult res = null; 156 157 try { 158 authenticateCalled = true; 159 160 try { 161 ensureOpen(); 162 } catch (IOException e) { 163 NamingException ne = new CommunicationException(); 164 ne.setRootCause(e); 165 throw ne; 166 } 167 168 switch (version) { 169 case LDAP_VERSION3_VERSION2: 170 case LDAP_VERSION3: 171 isLdapv3 = true; 172 break; 173 case LDAP_VERSION2: 174 isLdapv3 = false; 175 break; 176 default: 177 throw new CommunicationException("Protocol version " + version + 178 " not supported"); 179 } 180 181 if (authMechanism.equalsIgnoreCase("none") || 182 authMechanism.equalsIgnoreCase("anonymous")) { 183 184 // Perform LDAP bind if we are reauthenticating, using LDAPv2, 185 // supporting failover to LDAPv2, or controls have been supplied. 186 if (!initial || 187 (version == LDAP_VERSION2) || 188 (version == LDAP_VERSION3_VERSION2) || 189 ((ctls != null) && (ctls.length > 0))) { 190 try { 191 // anonymous bind; update name/pw for LDAPv2 retry 192 res = ldapBind(name=null, (byte[])(pw=null), ctls, null, 193 false); 194 if (res.status == LdapClient.LDAP_SUCCESS) { 195 conn.setBound(); 196 } 197 } catch (IOException e) { 198 NamingException ne = 199 new CommunicationException("anonymous bind failed: " + 200 conn.host + ":" + conn.port); 201 ne.setRootCause(e); 202 throw ne; 203 } 204 } else { 205 // Skip LDAP bind for LDAPv3 anonymous bind 206 res = new LdapResult(); 207 res.status = LdapClient.LDAP_SUCCESS; 208 } 209 } else if (authMechanism.equalsIgnoreCase("simple")) { 210 // simple authentication 211 byte[] encodedPw = null; 212 try { 213 encodedPw = encodePassword(pw, isLdapv3); 214 res = ldapBind(name, encodedPw, ctls, null, false); 215 if (res.status == LdapClient.LDAP_SUCCESS) { 216 conn.setBound(); 217 } 218 } catch (IOException e) { 219 NamingException ne = 220 new CommunicationException("simple bind failed: " + 221 conn.host + ":" + conn.port); 222 ne.setRootCause(e); 223 throw ne; 224 } finally { 225 // If pw was copied to a new array, clear that array as 226 // a security precaution. 227 if (encodedPw != pw && encodedPw != null) { 228 for (int i = 0; i < encodedPw.length; i++) { 229 encodedPw[i] = 0; 230 } 231 } 232 } 233 } else if (isLdapv3) { 234 // SASL authentication 235 try { 236 res = LdapSasl.saslBind(this, conn, conn.host, name, pw, 237 authMechanism, env, ctls); 238 if (res.status == LdapClient.LDAP_SUCCESS) { 239 conn.setBound(); 240 } 241 } catch (IOException e) { 242 NamingException ne = 243 new CommunicationException("SASL bind failed: " + 244 conn.host + ":" + conn.port); 245 ne.setRootCause(e); 246 throw ne; 247 } 248 } else { 249 throw new AuthenticationNotSupportedException(authMechanism); 250 } 251 252 // 253 // re-try login using v2 if failing over 254 // 255 if (initial && 256 (res.status == LdapClient.LDAP_PROTOCOL_ERROR) && 257 (version == LdapClient.LDAP_VERSION3_VERSION2) && 258 (authMechanism.equalsIgnoreCase("none") || 259 authMechanism.equalsIgnoreCase("anonymous") || 260 authMechanism.equalsIgnoreCase("simple"))) { 261 262 byte[] encodedPw = null; 263 try { 264 isLdapv3 = false; 265 encodedPw = encodePassword(pw, false); 266 res = ldapBind(name, encodedPw, ctls, null, false); 267 if (res.status == LdapClient.LDAP_SUCCESS) { 268 conn.setBound(); 269 } 270 } catch (IOException e) { 271 NamingException ne = 272 new CommunicationException(authMechanism + ":" + 273 conn.host + ":" + conn.port); 274 ne.setRootCause(e); 275 throw ne; 276 } finally { 277 // If pw was copied to a new array, clear that array as 278 // a security precaution. 279 if (encodedPw != pw && encodedPw != null) { 280 for (int i = 0; i < encodedPw.length; i++) { 281 encodedPw[i] = 0; 282 } 283 } 284 } 285 } 286 287 // principal name not found 288 // (map NameNotFoundException to AuthenticationException) 289 // %%% This is a workaround for Netscape servers returning 290 // %%% no such object when the principal name is not found 291 // %%% Note that when this workaround is applied, it does not allow 292 // %%% response controls to be recorded by the calling context 293 if (res.status == LdapClient.LDAP_NO_SUCH_OBJECT) { 294 throw new AuthenticationException( 295 getErrorMessage(res.status, res.errorMessage)); 296 } 297 conn.setV3(isLdapv3); 298 return res; 299 } finally { 300 conn.readTimeout = readTimeout; 301 } 302 } 303 304 /** 305 * Sends an LDAP Bind request. 306 * Cannot be private; called by LdapSasl 307 * @param dn The possibly null DN to use in the BIND request. null if anonymous. 308 * @param toServer The possibly null array of bytes to send to the server. 309 * @param auth The authentication mechanism 310 * 311 */ ldapBind(String dn, byte[]toServer, Control[] bindCtls, String auth, boolean pauseAfterReceipt)312 synchronized public LdapResult ldapBind(String dn, byte[]toServer, 313 Control[] bindCtls, String auth, boolean pauseAfterReceipt) 314 throws java.io.IOException, NamingException { 315 316 ensureOpen(); 317 318 // flush outstanding requests 319 conn.abandonOutstandingReqs(null); 320 321 BerEncoder ber = new BerEncoder(); 322 int curMsgId = conn.getMsgId(); 323 LdapResult res = new LdapResult(); 324 res.status = LDAP_OPERATIONS_ERROR; 325 326 // 327 // build the bind request. 328 // 329 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 330 ber.encodeInt(curMsgId); 331 ber.beginSeq(LdapClient.LDAP_REQ_BIND); 332 ber.encodeInt(isLdapv3 ? LDAP_VERSION3 : LDAP_VERSION2); 333 ber.encodeString(dn, isLdapv3); 334 335 // if authentication mechanism specified, it is SASL 336 if (auth != null) { 337 ber.beginSeq(Ber.ASN_CONTEXT | Ber.ASN_CONSTRUCTOR | 3); 338 ber.encodeString(auth, isLdapv3); // SASL mechanism 339 if (toServer != null) { 340 ber.encodeOctetString(toServer, 341 Ber.ASN_OCTET_STR); 342 } 343 ber.endSeq(); 344 } else { 345 if (toServer != null) { 346 ber.encodeOctetString(toServer, Ber.ASN_CONTEXT); 347 } else { 348 ber.encodeOctetString(null, Ber.ASN_CONTEXT, 0, 0); 349 } 350 } 351 ber.endSeq(); 352 353 // Encode controls 354 if (isLdapv3) { 355 encodeControls(ber, bindCtls); 356 } 357 ber.endSeq(); 358 359 LdapRequest req = conn.writeRequest(ber, curMsgId, pauseAfterReceipt); 360 if (toServer != null) { 361 ber.reset(); // clear internally-stored password 362 } 363 364 // Read reply 365 BerDecoder rber = conn.readReply(req); 366 367 rber.parseSeq(null); // init seq 368 rber.parseInt(); // msg id 369 if (rber.parseByte() != LDAP_REP_BIND) { 370 return res; 371 } 372 373 rber.parseLength(); 374 parseResult(rber, res, isLdapv3); 375 376 // handle server's credentials (if present) 377 if (isLdapv3 && 378 (rber.bytesLeft() > 0) && 379 (rber.peekByte() == (Ber.ASN_CONTEXT | 7))) { 380 res.serverCreds = rber.parseOctetString((Ber.ASN_CONTEXT | 7), null); 381 } 382 383 res.resControls = isLdapv3 ? parseControls(rber) : null; 384 385 conn.removeRequest(req); 386 return res; 387 } 388 389 /** 390 * Determines whether SASL encryption/integrity is in progress. 391 * This check is made prior to reauthentication. You cannot reauthenticate 392 * over an encrypted/integrity-protected SASL channel. You must 393 * close the channel and open a new one. 394 */ usingSaslStreams()395 boolean usingSaslStreams() { 396 return (conn.inStream instanceof SaslInputStream); 397 } 398 399 // Returns true if client connection was upgraded 400 // with STARTTLS extended operation on the server side isUpgradedToStartTls()401 boolean isUpgradedToStartTls() { 402 return conn.isUpgradedToStartTls(); 403 } 404 incRefCount()405 synchronized void incRefCount() { 406 ++referenceCount; 407 if (debug > 1) { 408 System.err.println("LdapClient.incRefCount: " + referenceCount + " " + this); 409 } 410 411 } 412 413 /** 414 * Returns the encoded password. 415 */ encodePassword(Object pw, boolean v3)416 private static byte[] encodePassword(Object pw, boolean v3) throws IOException { 417 418 if (pw instanceof char[]) { 419 pw = new String((char[])pw); 420 } 421 422 if (pw instanceof String) { 423 if (v3) { 424 return ((String)pw).getBytes("UTF8"); 425 } else { 426 return ((String)pw).getBytes("8859_1"); 427 } 428 } else { 429 return (byte[])pw; 430 } 431 } 432 close(Control[] reqCtls, boolean hardClose)433 synchronized void close(Control[] reqCtls, boolean hardClose) { 434 --referenceCount; 435 436 if (debug > 1) { 437 System.err.println("LdapClient: " + this); 438 System.err.println("LdapClient: close() called: " + referenceCount); 439 (new Throwable()).printStackTrace(); 440 } 441 442 if (referenceCount <= 0) { 443 if (debug > 0) System.err.println("LdapClient: closed connection " + this); 444 if (!pooled) { 445 // Not being pooled; continue with closing 446 conn.cleanup(reqCtls, false); 447 } else { 448 // Pooled 449 // Is this a real close or a request to return conn to pool 450 if (hardClose) { 451 conn.cleanup(reqCtls, false); 452 pcb.removePooledConnection(this); 453 } else { 454 pcb.releasePooledConnection(this); 455 } 456 } 457 } 458 } 459 460 // NOTE: Should NOT be synchronized otherwise won't be able to close forceClose(boolean cleanPool)461 private void forceClose(boolean cleanPool) { 462 referenceCount = 0; // force closing of connection 463 464 if (debug > 1) { 465 System.err.println("LdapClient: forceClose() of " + this); 466 } 467 if (debug > 0) { 468 System.err.println( 469 "LdapClient: forced close of connection " + this); 470 } 471 conn.cleanup(null, false); 472 if (cleanPool) { 473 pcb.removePooledConnection(this); 474 } 475 } 476 477 @SuppressWarnings("deprecation") finalize()478 protected void finalize() { 479 if (debug > 0) System.err.println("LdapClient: finalize " + this); 480 forceClose(pooled); 481 } 482 483 /* 484 * Used by connection pooling to close physical connection. 485 */ closeConnection()486 synchronized public void closeConnection() { 487 forceClose(false); // this is a pool callback so no need to clean pool 488 } 489 490 /** 491 * Called by Connection.cleanup(). LdapClient should 492 * notify any unsolicited listeners and removing itself from any pool. 493 * This is almost like forceClose(), except it doesn't call 494 * Connection.cleanup() (because this is called from cleanup()). 495 */ processConnectionClosure()496 void processConnectionClosure() { 497 // Notify listeners 498 if (unsolicited.size() > 0) { 499 String msg; 500 if (conn != null) { 501 msg = conn.host + ":" + conn.port + " connection closed"; 502 } else { 503 msg = "Connection closed"; 504 } 505 notifyUnsolicited(new CommunicationException(msg)); 506 } 507 508 // Remove from pool 509 if (pooled) { 510 pcb.removePooledConnection(this); 511 } 512 } 513 514 //////////////////////////////////////////////////////////////////////////// 515 // 516 // LDAP search. also includes methods to encode rfc 1558 compliant filters 517 // 518 //////////////////////////////////////////////////////////////////////////// 519 520 static final int SCOPE_BASE_OBJECT = 0; 521 static final int SCOPE_ONE_LEVEL = 1; 522 static final int SCOPE_SUBTREE = 2; 523 search(String dn, int scope, int deref, int sizeLimit, int timeLimit, boolean attrsOnly, String attrs[], String filter, int batchSize, Control[] reqCtls, Hashtable<String, Boolean> binaryAttrs, boolean waitFirstReply, int replyQueueCapacity)524 LdapResult search(String dn, int scope, int deref, int sizeLimit, 525 int timeLimit, boolean attrsOnly, String attrs[], 526 String filter, int batchSize, Control[] reqCtls, 527 Hashtable<String, Boolean> binaryAttrs, 528 boolean waitFirstReply, int replyQueueCapacity) 529 throws IOException, NamingException { 530 531 ensureOpen(); 532 533 LdapResult res = new LdapResult(); 534 535 BerEncoder ber = new BerEncoder(); 536 int curMsgId = conn.getMsgId(); 537 538 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 539 ber.encodeInt(curMsgId); 540 ber.beginSeq(LDAP_REQ_SEARCH); 541 ber.encodeString(dn == null ? "" : dn, isLdapv3); 542 ber.encodeInt(scope, LBER_ENUMERATED); 543 ber.encodeInt(deref, LBER_ENUMERATED); 544 ber.encodeInt(sizeLimit); 545 ber.encodeInt(timeLimit); 546 ber.encodeBoolean(attrsOnly); 547 Filter.encodeFilterString(ber, filter, isLdapv3); 548 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 549 ber.encodeStringArray(attrs, isLdapv3); 550 ber.endSeq(); 551 ber.endSeq(); 552 if (isLdapv3) encodeControls(ber, reqCtls); 553 ber.endSeq(); 554 555 LdapRequest req = 556 conn.writeRequest(ber, curMsgId, false, replyQueueCapacity); 557 558 res.msgId = curMsgId; 559 res.status = LdapClient.LDAP_SUCCESS; //optimistic 560 if (waitFirstReply) { 561 // get first reply 562 res = getSearchReply(req, batchSize, res, binaryAttrs); 563 } 564 return res; 565 } 566 567 /* 568 * Abandon the search operation and remove it from the message queue. 569 */ clearSearchReply(LdapResult res, Control[] ctls)570 void clearSearchReply(LdapResult res, Control[] ctls) { 571 if (res != null) { 572 573 // Only send an LDAP abandon operation when clearing the search 574 // reply from a one-level or subtree search. 575 LdapRequest req = conn.findRequest(res.msgId); 576 if (req == null) { 577 return; 578 } 579 580 // OK if req got removed after check; double removal attempt 581 // but otherwise no harm done 582 583 // Send an LDAP abandon only if the search operation has not yet 584 // completed. 585 if (req.hasSearchCompleted()) { 586 conn.removeRequest(req); 587 } else { 588 conn.abandonRequest(req, ctls); 589 } 590 } 591 } 592 593 /* 594 * Retrieve the next batch of entries and/or referrals. 595 */ getSearchReply(int batchSize, LdapResult res, Hashtable<String, Boolean> binaryAttrs)596 LdapResult getSearchReply(int batchSize, LdapResult res, 597 Hashtable<String, Boolean> binaryAttrs) throws IOException, NamingException { 598 599 ensureOpen(); 600 601 LdapRequest req; 602 603 if ((req = conn.findRequest(res.msgId)) == null) { 604 return null; 605 } 606 607 return getSearchReply(req, batchSize, res, binaryAttrs); 608 } 609 getSearchReply(LdapRequest req, int batchSize, LdapResult res, Hashtable<String, Boolean> binaryAttrs)610 private LdapResult getSearchReply(LdapRequest req, 611 int batchSize, LdapResult res, Hashtable<String, Boolean> binaryAttrs) 612 throws IOException, NamingException { 613 614 if (batchSize == 0) 615 batchSize = Integer.MAX_VALUE; 616 617 if (res.entries != null) { 618 res.entries.setSize(0); // clear the (previous) set of entries 619 } else { 620 res.entries = 621 new Vector<>(batchSize == Integer.MAX_VALUE ? 32 : batchSize); 622 } 623 624 if (res.referrals != null) { 625 res.referrals.setSize(0); // clear the (previous) set of referrals 626 } 627 628 BerDecoder replyBer; // Decoder for response 629 int seq; // Request id 630 631 Attributes lattrs; // Attribute set read from response 632 Attribute la; // Attribute read from response 633 String DN; // DN read from response 634 LdapEntry le; // LDAP entry representing response 635 int[] seqlen; // Holder for response length 636 int endseq; // Position of end of response 637 638 for (int i = 0; i < batchSize;) { 639 replyBer = conn.readReply(req); 640 641 // 642 // process search reply 643 // 644 replyBer.parseSeq(null); // init seq 645 replyBer.parseInt(); // req id 646 seq = replyBer.parseSeq(null); 647 648 if (seq == LDAP_REP_SEARCH) { 649 650 // handle LDAPv3 search entries 651 lattrs = new BasicAttributes(caseIgnore); 652 DN = replyBer.parseString(isLdapv3); 653 le = new LdapEntry(DN, lattrs); 654 seqlen = new int[1]; 655 656 replyBer.parseSeq(seqlen); 657 endseq = replyBer.getParsePosition() + seqlen[0]; 658 while ((replyBer.getParsePosition() < endseq) && 659 (replyBer.bytesLeft() > 0)) { 660 la = parseAttribute(replyBer, binaryAttrs); 661 lattrs.put(la); 662 } 663 le.respCtls = isLdapv3 ? parseControls(replyBer) : null; 664 665 res.entries.addElement(le); 666 i++; 667 668 } else if ((seq == LDAP_REP_SEARCH_REF) && isLdapv3) { 669 670 // handle LDAPv3 search reference 671 Vector<String> URLs = new Vector<>(4); 672 673 // %%% Although not strictly correct, some LDAP servers 674 // encode the SEQUENCE OF tag in the SearchResultRef 675 if (replyBer.peekByte() == 676 (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR)) { 677 replyBer.parseSeq(null); 678 } 679 680 while ((replyBer.bytesLeft() > 0) && 681 (replyBer.peekByte() == Ber.ASN_OCTET_STR)) { 682 683 URLs.addElement(replyBer.parseString(isLdapv3)); 684 } 685 686 if (res.referrals == null) { 687 res.referrals = new Vector<>(4); 688 } 689 res.referrals.addElement(URLs); 690 res.resControls = isLdapv3 ? parseControls(replyBer) : null; 691 692 // Save referral and continue to get next search result 693 694 } else if (seq == LDAP_REP_EXTENSION) { 695 696 parseExtResponse(replyBer, res); //%%% ignore for now 697 698 } else if (seq == LDAP_REP_RESULT) { 699 700 parseResult(replyBer, res, isLdapv3); 701 res.resControls = isLdapv3 ? parseControls(replyBer) : null; 702 703 conn.removeRequest(req); 704 return res; // Done with search 705 } 706 } 707 708 return res; 709 } 710 parseAttribute(BerDecoder ber, Hashtable<String, Boolean> binaryAttrs)711 private Attribute parseAttribute(BerDecoder ber, 712 Hashtable<String, Boolean> binaryAttrs) 713 throws IOException { 714 715 int len[] = new int[1]; 716 int seq = ber.parseSeq(null); 717 String attrid = ber.parseString(isLdapv3); 718 boolean hasBinaryValues = isBinaryValued(attrid, binaryAttrs); 719 Attribute la = new LdapAttribute(attrid); 720 721 if ((seq = ber.parseSeq(len)) == LBER_SET) { 722 int attrlen = len[0]; 723 while (ber.bytesLeft() > 0 && attrlen > 0) { 724 try { 725 attrlen -= parseAttributeValue(ber, la, hasBinaryValues); 726 } catch (IOException ex) { 727 ber.seek(attrlen); 728 break; 729 } 730 } 731 } else { 732 // Skip the rest of the sequence because it is not what we want 733 ber.seek(len[0]); 734 } 735 return la; 736 } 737 738 // 739 // returns number of bytes that were parsed. Adds the values to attr 740 // parseAttributeValue(BerDecoder ber, Attribute la, boolean hasBinaryValues)741 private int parseAttributeValue(BerDecoder ber, Attribute la, 742 boolean hasBinaryValues) throws IOException { 743 744 int len[] = new int[1]; 745 746 if (hasBinaryValues) { 747 la.add(ber.parseOctetString(ber.peekByte(), len)); 748 } else { 749 la.add(ber.parseStringWithTag( 750 Ber.ASN_SIMPLE_STRING, isLdapv3, len)); 751 } 752 return len[0]; 753 } 754 isBinaryValued(String attrid, Hashtable<String, Boolean> binaryAttrs)755 private boolean isBinaryValued(String attrid, 756 Hashtable<String, Boolean> binaryAttrs) { 757 String id = attrid.toLowerCase(Locale.ENGLISH); 758 759 return ((id.indexOf(";binary") != -1) || 760 defaultBinaryAttrs.containsKey(id) || 761 ((binaryAttrs != null) && (binaryAttrs.containsKey(id)))); 762 } 763 764 // package entry point; used by Connection parseResult(BerDecoder replyBer, LdapResult res, boolean isLdapv3)765 static void parseResult(BerDecoder replyBer, LdapResult res, 766 boolean isLdapv3) throws IOException { 767 768 res.status = replyBer.parseEnumeration(); 769 res.matchedDN = replyBer.parseString(isLdapv3); 770 res.errorMessage = replyBer.parseString(isLdapv3); 771 772 // handle LDAPv3 referrals (if present) 773 if (isLdapv3 && 774 (replyBer.bytesLeft() > 0) && 775 (replyBer.peekByte() == LDAP_REP_REFERRAL)) { 776 777 Vector<String> URLs = new Vector<>(4); 778 int[] seqlen = new int[1]; 779 780 replyBer.parseSeq(seqlen); 781 int endseq = replyBer.getParsePosition() + seqlen[0]; 782 while ((replyBer.getParsePosition() < endseq) && 783 (replyBer.bytesLeft() > 0)) { 784 785 URLs.addElement(replyBer.parseString(isLdapv3)); 786 } 787 788 if (res.referrals == null) { 789 res.referrals = new Vector<>(4); 790 } 791 res.referrals.addElement(URLs); 792 } 793 } 794 795 // package entry point; used by Connection parseControls(BerDecoder replyBer)796 static Vector<Control> parseControls(BerDecoder replyBer) throws IOException { 797 798 // handle LDAPv3 controls (if present) 799 if ((replyBer.bytesLeft() > 0) && (replyBer.peekByte() == LDAP_CONTROLS)) { 800 Vector<Control> ctls = new Vector<>(4); 801 String controlOID; 802 boolean criticality = false; // default 803 byte[] controlValue = null; // optional 804 int[] seqlen = new int[1]; 805 806 replyBer.parseSeq(seqlen); 807 int endseq = replyBer.getParsePosition() + seqlen[0]; 808 while ((replyBer.getParsePosition() < endseq) && 809 (replyBer.bytesLeft() > 0)) { 810 811 replyBer.parseSeq(null); 812 controlOID = replyBer.parseString(true); 813 814 if ((replyBer.bytesLeft() > 0) && 815 (replyBer.peekByte() == Ber.ASN_BOOLEAN)) { 816 criticality = replyBer.parseBoolean(); 817 } 818 if ((replyBer.bytesLeft() > 0) && 819 (replyBer.peekByte() == Ber.ASN_OCTET_STR)) { 820 controlValue = 821 replyBer.parseOctetString(Ber.ASN_OCTET_STR, null); 822 } 823 if (controlOID != null) { 824 ctls.addElement( 825 new BasicControl(controlOID, criticality, controlValue)); 826 } 827 } 828 return ctls; 829 } else { 830 return null; 831 } 832 } 833 parseExtResponse(BerDecoder replyBer, LdapResult res)834 private void parseExtResponse(BerDecoder replyBer, LdapResult res) 835 throws IOException { 836 837 parseResult(replyBer, res, isLdapv3); 838 839 if ((replyBer.bytesLeft() > 0) && 840 (replyBer.peekByte() == LDAP_REP_EXT_OID)) { 841 res.extensionId = 842 replyBer.parseStringWithTag(LDAP_REP_EXT_OID, isLdapv3, null); 843 } 844 if ((replyBer.bytesLeft() > 0) && 845 (replyBer.peekByte() == LDAP_REP_EXT_VAL)) { 846 res.extensionValue = 847 replyBer.parseOctetString(LDAP_REP_EXT_VAL, null); 848 } 849 850 res.resControls = parseControls(replyBer); 851 } 852 853 // 854 // Encode LDAPv3 controls 855 // encodeControls(BerEncoder ber, Control[] reqCtls)856 static void encodeControls(BerEncoder ber, Control[] reqCtls) 857 throws IOException { 858 859 if ((reqCtls == null) || (reqCtls.length == 0)) { 860 return; 861 } 862 863 byte[] controlVal; 864 865 ber.beginSeq(LdapClient.LDAP_CONTROLS); 866 867 for (int i = 0; i < reqCtls.length; i++) { 868 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 869 ber.encodeString(reqCtls[i].getID(), true); // control OID 870 if (reqCtls[i].isCritical()) { 871 ber.encodeBoolean(true); // critical control 872 } 873 if ((controlVal = reqCtls[i].getEncodedValue()) != null) { 874 ber.encodeOctetString(controlVal, Ber.ASN_OCTET_STR); 875 } 876 ber.endSeq(); 877 } 878 ber.endSeq(); 879 } 880 881 /** 882 * Reads the next reply corresponding to msgId, outstanding on requestBer. 883 * Processes the result and any controls. 884 */ processReply(LdapRequest req, LdapResult res, int responseType)885 private LdapResult processReply(LdapRequest req, 886 LdapResult res, int responseType) throws IOException, NamingException { 887 888 BerDecoder rber = conn.readReply(req); 889 890 rber.parseSeq(null); // init seq 891 rber.parseInt(); // msg id 892 if (rber.parseByte() != responseType) { 893 return res; 894 } 895 896 rber.parseLength(); 897 parseResult(rber, res, isLdapv3); 898 res.resControls = isLdapv3 ? parseControls(rber) : null; 899 900 conn.removeRequest(req); 901 902 return res; // Done with operation 903 } 904 905 //////////////////////////////////////////////////////////////////////////// 906 // 907 // LDAP modify: 908 // Modify the DN dn with the operations on attributes attrs. 909 // ie, operations[0] is the operation to be performed on 910 // attrs[0]; 911 // dn - DN to modify 912 // operations - add, delete or replace 913 // attrs - array of Attribute 914 // reqCtls - array of request controls 915 // 916 //////////////////////////////////////////////////////////////////////////// 917 918 static final int ADD = 0; 919 static final int DELETE = 1; 920 static final int REPLACE = 2; 921 modify(String dn, int operations[], Attribute attrs[], Control[] reqCtls)922 LdapResult modify(String dn, int operations[], Attribute attrs[], 923 Control[] reqCtls) 924 throws IOException, NamingException { 925 926 ensureOpen(); 927 928 LdapResult res = new LdapResult(); 929 res.status = LDAP_OPERATIONS_ERROR; 930 931 if (dn == null || operations.length != attrs.length) 932 return res; 933 934 BerEncoder ber = new BerEncoder(); 935 int curMsgId = conn.getMsgId(); 936 937 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 938 ber.encodeInt(curMsgId); 939 ber.beginSeq(LDAP_REQ_MODIFY); 940 ber.encodeString(dn, isLdapv3); 941 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 942 for (int i = 0; i < operations.length; i++) { 943 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 944 ber.encodeInt(operations[i], LBER_ENUMERATED); 945 946 // zero values is not permitted for the add op. 947 if ((operations[i] == ADD) && hasNoValue(attrs[i])) { 948 throw new InvalidAttributeValueException( 949 "'" + attrs[i].getID() + "' has no values."); 950 } else { 951 encodeAttribute(ber, attrs[i]); 952 } 953 ber.endSeq(); 954 } 955 ber.endSeq(); 956 ber.endSeq(); 957 if (isLdapv3) encodeControls(ber, reqCtls); 958 ber.endSeq(); 959 960 LdapRequest req = conn.writeRequest(ber, curMsgId); 961 962 return processReply(req, res, LDAP_REP_MODIFY); 963 } 964 encodeAttribute(BerEncoder ber, Attribute attr)965 private void encodeAttribute(BerEncoder ber, Attribute attr) 966 throws IOException, NamingException { 967 968 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 969 ber.encodeString(attr.getID(), isLdapv3); 970 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR | 1); 971 NamingEnumeration<?> enum_ = attr.getAll(); 972 Object val; 973 while (enum_.hasMore()) { 974 val = enum_.next(); 975 if (val instanceof String) { 976 ber.encodeString((String)val, isLdapv3); 977 } else if (val instanceof byte[]) { 978 ber.encodeOctetString((byte[])val, Ber.ASN_OCTET_STR); 979 } else if (val == null) { 980 // no attribute value 981 } else { 982 throw new InvalidAttributeValueException( 983 "Malformed '" + attr.getID() + "' attribute value"); 984 } 985 } 986 ber.endSeq(); 987 ber.endSeq(); 988 } 989 hasNoValue(Attribute attr)990 private static boolean hasNoValue(Attribute attr) throws NamingException { 991 return attr.size() == 0 || (attr.size() == 1 && attr.get() == null); 992 } 993 994 //////////////////////////////////////////////////////////////////////////// 995 // 996 // LDAP add 997 // Adds entry to the Directory 998 // 999 //////////////////////////////////////////////////////////////////////////// 1000 add(LdapEntry entry, Control[] reqCtls)1001 LdapResult add(LdapEntry entry, Control[] reqCtls) 1002 throws IOException, NamingException { 1003 1004 ensureOpen(); 1005 1006 LdapResult res = new LdapResult(); 1007 res.status = LDAP_OPERATIONS_ERROR; 1008 1009 if (entry == null || entry.DN == null) 1010 return res; 1011 1012 BerEncoder ber = new BerEncoder(); 1013 int curMsgId = conn.getMsgId(); 1014 Attribute attr; 1015 1016 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1017 ber.encodeInt(curMsgId); 1018 ber.beginSeq(LDAP_REQ_ADD); 1019 ber.encodeString(entry.DN, isLdapv3); 1020 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1021 NamingEnumeration<? extends Attribute> enum_ = 1022 entry.attributes.getAll(); 1023 while (enum_.hasMore()) { 1024 attr = enum_.next(); 1025 1026 // zero values is not permitted 1027 if (hasNoValue(attr)) { 1028 throw new InvalidAttributeValueException( 1029 "'" + attr.getID() + "' has no values."); 1030 } else { 1031 encodeAttribute(ber, attr); 1032 } 1033 } 1034 ber.endSeq(); 1035 ber.endSeq(); 1036 if (isLdapv3) encodeControls(ber, reqCtls); 1037 ber.endSeq(); 1038 1039 LdapRequest req = conn.writeRequest(ber, curMsgId); 1040 return processReply(req, res, LDAP_REP_ADD); 1041 } 1042 1043 //////////////////////////////////////////////////////////////////////////// 1044 // 1045 // LDAP delete 1046 // deletes entry from the Directory 1047 // 1048 //////////////////////////////////////////////////////////////////////////// 1049 delete(String DN, Control[] reqCtls)1050 LdapResult delete(String DN, Control[] reqCtls) 1051 throws IOException, NamingException { 1052 1053 ensureOpen(); 1054 1055 LdapResult res = new LdapResult(); 1056 res.status = LDAP_OPERATIONS_ERROR; 1057 1058 if (DN == null) 1059 return res; 1060 1061 BerEncoder ber = new BerEncoder(); 1062 int curMsgId = conn.getMsgId(); 1063 1064 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1065 ber.encodeInt(curMsgId); 1066 ber.encodeString(DN, LDAP_REQ_DELETE, isLdapv3); 1067 if (isLdapv3) encodeControls(ber, reqCtls); 1068 ber.endSeq(); 1069 1070 LdapRequest req = conn.writeRequest(ber, curMsgId); 1071 1072 return processReply(req, res, LDAP_REP_DELETE); 1073 } 1074 1075 //////////////////////////////////////////////////////////////////////////// 1076 // 1077 // LDAP modrdn 1078 // Changes the last element of DN to newrdn 1079 // dn - DN to change 1080 // newrdn - new RDN to rename to 1081 // deleteoldrdn - boolean whether to delete old attrs or not 1082 // newSuperior - new place to put the entry in the tree 1083 // (ignored if server is LDAPv2) 1084 // reqCtls - array of request controls 1085 // 1086 //////////////////////////////////////////////////////////////////////////// 1087 moddn(String DN, String newrdn, boolean deleteOldRdn, String newSuperior, Control[] reqCtls)1088 LdapResult moddn(String DN, String newrdn, boolean deleteOldRdn, 1089 String newSuperior, Control[] reqCtls) 1090 throws IOException, NamingException { 1091 1092 ensureOpen(); 1093 1094 boolean changeSuperior = (newSuperior != null && 1095 newSuperior.length() > 0); 1096 1097 LdapResult res = new LdapResult(); 1098 res.status = LDAP_OPERATIONS_ERROR; 1099 1100 if (DN == null || newrdn == null) 1101 return res; 1102 1103 BerEncoder ber = new BerEncoder(); 1104 int curMsgId = conn.getMsgId(); 1105 1106 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1107 ber.encodeInt(curMsgId); 1108 ber.beginSeq(LDAP_REQ_MODRDN); 1109 ber.encodeString(DN, isLdapv3); 1110 ber.encodeString(newrdn, isLdapv3); 1111 ber.encodeBoolean(deleteOldRdn); 1112 if(isLdapv3 && changeSuperior) { 1113 //System.err.println("changin superior"); 1114 ber.encodeString(newSuperior, LDAP_SUPERIOR_DN, isLdapv3); 1115 } 1116 ber.endSeq(); 1117 if (isLdapv3) encodeControls(ber, reqCtls); 1118 ber.endSeq(); 1119 1120 1121 LdapRequest req = conn.writeRequest(ber, curMsgId); 1122 1123 return processReply(req, res, LDAP_REP_MODRDN); 1124 } 1125 1126 //////////////////////////////////////////////////////////////////////////// 1127 // 1128 // LDAP compare 1129 // Compare attribute->value pairs in dn 1130 // 1131 //////////////////////////////////////////////////////////////////////////// 1132 compare(String DN, String type, String value, Control[] reqCtls)1133 LdapResult compare(String DN, String type, String value, Control[] reqCtls) 1134 throws IOException, NamingException { 1135 1136 ensureOpen(); 1137 1138 LdapResult res = new LdapResult(); 1139 res.status = LDAP_OPERATIONS_ERROR; 1140 1141 if (DN == null || type == null || value == null) 1142 return res; 1143 1144 BerEncoder ber = new BerEncoder(); 1145 int curMsgId = conn.getMsgId(); 1146 1147 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1148 ber.encodeInt(curMsgId); 1149 ber.beginSeq(LDAP_REQ_COMPARE); 1150 ber.encodeString(DN, isLdapv3); 1151 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1152 ber.encodeString(type, isLdapv3); 1153 1154 // replace any escaped characters in the value 1155 byte[] val = isLdapv3 ? 1156 value.getBytes("UTF8") : value.getBytes("8859_1"); 1157 ber.encodeOctetString( 1158 Filter.unescapeFilterValue(val, 0, val.length), 1159 Ber.ASN_OCTET_STR); 1160 1161 ber.endSeq(); 1162 ber.endSeq(); 1163 if (isLdapv3) encodeControls(ber, reqCtls); 1164 ber.endSeq(); 1165 1166 LdapRequest req = conn.writeRequest(ber, curMsgId); 1167 1168 return processReply(req, res, LDAP_REP_COMPARE); 1169 } 1170 1171 //////////////////////////////////////////////////////////////////////////// 1172 // 1173 // LDAP extended operation 1174 // 1175 //////////////////////////////////////////////////////////////////////////// 1176 extendedOp(String id, byte[] request, Control[] reqCtls, boolean pauseAfterReceipt)1177 LdapResult extendedOp(String id, byte[] request, Control[] reqCtls, 1178 boolean pauseAfterReceipt) throws IOException, NamingException { 1179 1180 ensureOpen(); 1181 1182 LdapResult res = new LdapResult(); 1183 res.status = LDAP_OPERATIONS_ERROR; 1184 1185 if (id == null) 1186 return res; 1187 1188 BerEncoder ber = new BerEncoder(); 1189 int curMsgId = conn.getMsgId(); 1190 1191 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1192 ber.encodeInt(curMsgId); 1193 ber.beginSeq(LDAP_REQ_EXTENSION); 1194 ber.encodeString(id, 1195 Ber.ASN_CONTEXT | 0, isLdapv3);//[0] 1196 if (request != null) { 1197 ber.encodeOctetString(request, 1198 Ber.ASN_CONTEXT | 1);//[1] 1199 } 1200 ber.endSeq(); 1201 encodeControls(ber, reqCtls); // always v3 1202 ber.endSeq(); 1203 1204 LdapRequest req = conn.writeRequest(ber, curMsgId, pauseAfterReceipt); 1205 1206 BerDecoder rber = conn.readReply(req); 1207 1208 rber.parseSeq(null); // init seq 1209 rber.parseInt(); // msg id 1210 if (rber.parseByte() != LDAP_REP_EXTENSION) { 1211 return res; 1212 } 1213 1214 rber.parseLength(); 1215 parseExtResponse(rber, res); 1216 conn.removeRequest(req); 1217 1218 return res; // Done with operation 1219 } 1220 1221 1222 1223 //////////////////////////////////////////////////////////////////////////// 1224 // 1225 // Some BER definitions convenient for LDAP 1226 // 1227 //////////////////////////////////////////////////////////////////////////// 1228 1229 static final int LDAP_VERSION3_VERSION2 = 32; 1230 static final int LDAP_VERSION2 = 0x02; 1231 static final int LDAP_VERSION3 = 0x03; // LDAPv3 1232 static final int LDAP_VERSION = LDAP_VERSION3; 1233 1234 static final int LDAP_REF_FOLLOW = 0x01; // follow referrals 1235 static final int LDAP_REF_THROW = 0x02; // throw referral ex. 1236 static final int LDAP_REF_IGNORE = 0x03; // ignore referrals 1237 static final int LDAP_REF_FOLLOW_SCHEME = 0x04; // follow referrals of the same scheme 1238 1239 static final String LDAP_URL = "ldap://"; // LDAPv3 1240 static final String LDAPS_URL = "ldaps://"; // LDAPv3 1241 1242 static final int LBER_BOOLEAN = 0x01; 1243 static final int LBER_INTEGER = 0x02; 1244 static final int LBER_BITSTRING = 0x03; 1245 static final int LBER_OCTETSTRING = 0x04; 1246 static final int LBER_NULL = 0x05; 1247 static final int LBER_ENUMERATED = 0x0a; 1248 static final int LBER_SEQUENCE = 0x30; 1249 static final int LBER_SET = 0x31; 1250 1251 static final int LDAP_SUPERIOR_DN = 0x80; 1252 1253 static final int LDAP_REQ_BIND = 0x60; // app + constructed 1254 static final int LDAP_REQ_UNBIND = 0x42; // app + primitive 1255 static final int LDAP_REQ_SEARCH = 0x63; // app + constructed 1256 static final int LDAP_REQ_MODIFY = 0x66; // app + constructed 1257 static final int LDAP_REQ_ADD = 0x68; // app + constructed 1258 static final int LDAP_REQ_DELETE = 0x4a; // app + primitive 1259 static final int LDAP_REQ_MODRDN = 0x6c; // app + constructed 1260 static final int LDAP_REQ_COMPARE = 0x6e; // app + constructed 1261 static final int LDAP_REQ_ABANDON = 0x50; // app + primitive 1262 static final int LDAP_REQ_EXTENSION = 0x77; // app + constructed (LDAPv3) 1263 1264 static final int LDAP_REP_BIND = 0x61; // app + constructed | 1 1265 static final int LDAP_REP_SEARCH = 0x64; // app + constructed | 4 1266 static final int LDAP_REP_SEARCH_REF = 0x73;// app + constructed (LDAPv3) 1267 static final int LDAP_REP_RESULT = 0x65; // app + constructed | 5 1268 static final int LDAP_REP_MODIFY = 0x67; // app + constructed | 7 1269 static final int LDAP_REP_ADD = 0x69; // app + constructed | 9 1270 static final int LDAP_REP_DELETE = 0x6b; // app + primitive | b 1271 static final int LDAP_REP_MODRDN = 0x6d; // app + primitive | d 1272 static final int LDAP_REP_COMPARE = 0x6f; // app + primitive | f 1273 static final int LDAP_REP_EXTENSION = 0x78; // app + constructed (LDAPv3) 1274 1275 static final int LDAP_REP_REFERRAL = 0xa3; // ctx + constructed (LDAPv3) 1276 static final int LDAP_REP_EXT_OID = 0x8a; // ctx + primitive (LDAPv3) 1277 static final int LDAP_REP_EXT_VAL = 0x8b; // ctx + primitive (LDAPv3) 1278 1279 // LDAPv3 Controls 1280 1281 static final int LDAP_CONTROLS = 0xa0; // ctx + constructed (LDAPv3) 1282 static final String LDAP_CONTROL_MANAGE_DSA_IT = "2.16.840.1.113730.3.4.2"; 1283 static final String LDAP_CONTROL_PREFERRED_LANG = "1.3.6.1.4.1.1466.20035"; 1284 static final String LDAP_CONTROL_PAGED_RESULTS = "1.2.840.113556.1.4.319"; 1285 static final String LDAP_CONTROL_SERVER_SORT_REQ = "1.2.840.113556.1.4.473"; 1286 static final String LDAP_CONTROL_SERVER_SORT_RES = "1.2.840.113556.1.4.474"; 1287 1288 //////////////////////////////////////////////////////////////////////////// 1289 // 1290 // return codes 1291 // 1292 //////////////////////////////////////////////////////////////////////////// 1293 1294 static final int LDAP_SUCCESS = 0; 1295 static final int LDAP_OPERATIONS_ERROR = 1; 1296 static final int LDAP_PROTOCOL_ERROR = 2; 1297 static final int LDAP_TIME_LIMIT_EXCEEDED = 3; 1298 static final int LDAP_SIZE_LIMIT_EXCEEDED = 4; 1299 static final int LDAP_COMPARE_FALSE = 5; 1300 static final int LDAP_COMPARE_TRUE = 6; 1301 static final int LDAP_AUTH_METHOD_NOT_SUPPORTED = 7; 1302 static final int LDAP_STRONG_AUTH_REQUIRED = 8; 1303 static final int LDAP_PARTIAL_RESULTS = 9; // Slapd 1304 static final int LDAP_REFERRAL = 10; // LDAPv3 1305 static final int LDAP_ADMIN_LIMIT_EXCEEDED = 11; // LDAPv3 1306 static final int LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 12; // LDAPv3 1307 static final int LDAP_CONFIDENTIALITY_REQUIRED = 13; // LDAPv3 1308 static final int LDAP_SASL_BIND_IN_PROGRESS = 14; // LDAPv3 1309 static final int LDAP_NO_SUCH_ATTRIBUTE = 16; 1310 static final int LDAP_UNDEFINED_ATTRIBUTE_TYPE = 17; 1311 static final int LDAP_INAPPROPRIATE_MATCHING = 18; 1312 static final int LDAP_CONSTRAINT_VIOLATION = 19; 1313 static final int LDAP_ATTRIBUTE_OR_VALUE_EXISTS = 20; 1314 static final int LDAP_INVALID_ATTRIBUTE_SYNTAX = 21; 1315 static final int LDAP_NO_SUCH_OBJECT = 32; 1316 static final int LDAP_ALIAS_PROBLEM = 33; 1317 static final int LDAP_INVALID_DN_SYNTAX = 34; 1318 static final int LDAP_IS_LEAF = 35; 1319 static final int LDAP_ALIAS_DEREFERENCING_PROBLEM = 36; 1320 static final int LDAP_INAPPROPRIATE_AUTHENTICATION = 48; 1321 static final int LDAP_INVALID_CREDENTIALS = 49; 1322 static final int LDAP_INSUFFICIENT_ACCESS_RIGHTS = 50; 1323 static final int LDAP_BUSY = 51; 1324 static final int LDAP_UNAVAILABLE = 52; 1325 static final int LDAP_UNWILLING_TO_PERFORM = 53; 1326 static final int LDAP_LOOP_DETECT = 54; 1327 static final int LDAP_NAMING_VIOLATION = 64; 1328 static final int LDAP_OBJECT_CLASS_VIOLATION = 65; 1329 static final int LDAP_NOT_ALLOWED_ON_NON_LEAF = 66; 1330 static final int LDAP_NOT_ALLOWED_ON_RDN = 67; 1331 static final int LDAP_ENTRY_ALREADY_EXISTS = 68; 1332 static final int LDAP_OBJECT_CLASS_MODS_PROHIBITED = 69; 1333 static final int LDAP_AFFECTS_MULTIPLE_DSAS = 71; // LDAPv3 1334 static final int LDAP_OTHER = 80; 1335 1336 static final String[] ldap_error_message = { 1337 "Success", // 0 1338 "Operations Error", // 1 1339 "Protocol Error", // 2 1340 "Timelimit Exceeded", // 3 1341 "Sizelimit Exceeded", // 4 1342 "Compare False", // 5 1343 "Compare True", // 6 1344 "Authentication Method Not Supported", // 7 1345 "Strong Authentication Required", // 8 1346 null, 1347 "Referral", // 10 1348 "Administrative Limit Exceeded", // 11 1349 "Unavailable Critical Extension", // 12 1350 "Confidentiality Required", // 13 1351 "SASL Bind In Progress", // 14 1352 null, 1353 "No Such Attribute", // 16 1354 "Undefined Attribute Type", // 17 1355 "Inappropriate Matching", // 18 1356 "Constraint Violation", // 19 1357 "Attribute Or Value Exists", // 20 1358 "Invalid Attribute Syntax", // 21 1359 null, 1360 null, 1361 null, 1362 null, 1363 null, 1364 null, 1365 null, 1366 null, 1367 null, 1368 null, 1369 "No Such Object", // 32 1370 "Alias Problem", // 33 1371 "Invalid DN Syntax", // 34 1372 null, 1373 "Alias Dereferencing Problem", // 36 1374 null, 1375 null, 1376 null, 1377 null, 1378 null, 1379 null, 1380 null, 1381 null, 1382 null, 1383 null, 1384 null, 1385 "Inappropriate Authentication", // 48 1386 "Invalid Credentials", // 49 1387 "Insufficient Access Rights", // 50 1388 "Busy", // 51 1389 "Unavailable", // 52 1390 "Unwilling To Perform", // 53 1391 "Loop Detect", // 54 1392 null, 1393 null, 1394 null, 1395 null, 1396 null, 1397 null, 1398 null, 1399 null, 1400 null, 1401 "Naming Violation", // 64 1402 "Object Class Violation", // 65 1403 "Not Allowed On Non-leaf", // 66 1404 "Not Allowed On RDN", // 67 1405 "Entry Already Exists", // 68 1406 "Object Class Modifications Prohibited", // 69 1407 null, 1408 "Affects Multiple DSAs", // 71 1409 null, 1410 null, 1411 null, 1412 null, 1413 null, 1414 null, 1415 null, 1416 null, 1417 "Other", // 80 1418 null, 1419 null, 1420 null, 1421 null, 1422 null, 1423 null, 1424 null, 1425 null, 1426 null, 1427 null 1428 }; 1429 1430 1431 /* 1432 * Generate an error message from the LDAP error code and error diagnostic. 1433 * The message format is: 1434 * 1435 * "[LDAP: error code <errorCode> - <errorMessage>]" 1436 * 1437 * where <errorCode> is a numeric error code 1438 * and <errorMessage> is a textual description of the error (if available) 1439 * 1440 */ getErrorMessage(int errorCode, String errorMessage)1441 static String getErrorMessage(int errorCode, String errorMessage) { 1442 1443 String message = "[LDAP: error code " + errorCode; 1444 1445 if ((errorMessage != null) && (errorMessage.length() != 0)) { 1446 1447 // append error message from the server 1448 message = message + " - " + errorMessage + "]"; 1449 1450 } else { 1451 1452 // append built-in error message 1453 try { 1454 if (ldap_error_message[errorCode] != null) { 1455 message = message + " - " + ldap_error_message[errorCode] + 1456 "]"; 1457 } 1458 } catch (ArrayIndexOutOfBoundsException ex) { 1459 message = message + "]"; 1460 } 1461 } 1462 return message; 1463 } 1464 1465 1466 //////////////////////////////////////////////////////////////////////////// 1467 // 1468 // Unsolicited notification support. 1469 // 1470 // An LdapClient maintains a list of LdapCtx that have registered 1471 // for UnsolicitedNotifications. This is a list because a single 1472 // LdapClient might be shared among multiple contexts. 1473 // 1474 // When addUnsolicited() is invoked, the LdapCtx is added to the list. 1475 // 1476 // When Connection receives an unsolicited notification (msgid == 0), 1477 // it invokes LdapClient.processUnsolicited(). processUnsolicited() 1478 // parses the Extended Response. If there are registered listeners, 1479 // LdapClient creates an UnsolicitedNotification from the response 1480 // and informs each LdapCtx to fire an event for the notification. 1481 // If it is a DISCONNECT notification, the connection is closed and a 1482 // NamingExceptionEvent is fired to the listeners. 1483 // 1484 // When the connection is closed out-of-band like this, the next 1485 // time a method is invoked on LdapClient, an IOException is thrown. 1486 // 1487 // removeUnsolicited() is invoked to remove an LdapCtx from this client. 1488 // 1489 //////////////////////////////////////////////////////////////////////////// 1490 private Vector<LdapCtx> unsolicited = new Vector<>(3); addUnsolicited(LdapCtx ctx)1491 void addUnsolicited(LdapCtx ctx) { 1492 if (debug > 0) { 1493 System.err.println("LdapClient.addUnsolicited" + ctx); 1494 } 1495 unsolicited.addElement(ctx); 1496 } 1497 removeUnsolicited(LdapCtx ctx)1498 void removeUnsolicited(LdapCtx ctx) { 1499 if (debug > 0) { 1500 System.err.println("LdapClient.removeUnsolicited" + ctx); 1501 } 1502 unsolicited.removeElement(ctx); 1503 } 1504 1505 // NOTE: Cannot be synchronized because this is called asynchronously 1506 // by the reader thread in Connection. Instead, sync on 'unsolicited' Vector. processUnsolicited(BerDecoder ber)1507 void processUnsolicited(BerDecoder ber) { 1508 if (debug > 0) { 1509 System.err.println("LdapClient.processUnsolicited"); 1510 } 1511 try { 1512 // Parse the response 1513 LdapResult res = new LdapResult(); 1514 1515 ber.parseSeq(null); // init seq 1516 ber.parseInt(); // msg id; should be 0; ignored 1517 if (ber.parseByte() != LDAP_REP_EXTENSION) { 1518 throw new IOException( 1519 "Unsolicited Notification must be an Extended Response"); 1520 } 1521 ber.parseLength(); 1522 parseExtResponse(ber, res); 1523 1524 if (DISCONNECT_OID.equals(res.extensionId)) { 1525 // force closing of connection 1526 forceClose(pooled); 1527 } 1528 1529 LdapCtx first = null; 1530 UnsolicitedNotification notice = null; 1531 1532 synchronized (unsolicited) { 1533 if (unsolicited.size() > 0) { 1534 first = unsolicited.elementAt(0); 1535 1536 // Create an UnsolicitedNotification using the parsed data 1537 // Need a 'ctx' object because we want to use the context's 1538 // list of provider control factories. 1539 notice = new UnsolicitedResponseImpl( 1540 res.extensionId, 1541 res.extensionValue, 1542 res.referrals, 1543 res.status, 1544 res.errorMessage, 1545 res.matchedDN, 1546 (res.resControls != null) ? 1547 first.convertControls(res.resControls) : 1548 null); 1549 } 1550 } 1551 1552 if (notice != null) { 1553 // Fire UnsolicitedNotification events to listeners 1554 notifyUnsolicited(notice); 1555 1556 // If "disconnect" notification, 1557 // notify unsolicited listeners via NamingException 1558 if (DISCONNECT_OID.equals(res.extensionId)) { 1559 notifyUnsolicited( 1560 new CommunicationException("Connection closed")); 1561 } 1562 } 1563 } catch (IOException e) { 1564 NamingException ne = new CommunicationException( 1565 "Problem parsing unsolicited notification"); 1566 ne.setRootCause(e); 1567 1568 notifyUnsolicited(ne); 1569 1570 } catch (NamingException e) { 1571 notifyUnsolicited(e); 1572 } 1573 } 1574 1575 notifyUnsolicited(Object e)1576 private void notifyUnsolicited(Object e) { 1577 Vector<LdapCtx> unsolicitedCopy; 1578 synchronized (unsolicited) { 1579 unsolicitedCopy = new Vector<>(unsolicited); 1580 if (e instanceof NamingException) { 1581 unsolicited.setSize(0); // no more listeners after exception 1582 } 1583 } 1584 for (int i = 0; i < unsolicitedCopy.size(); i++) { 1585 unsolicitedCopy.elementAt(i).fireUnsolicited(e); 1586 } 1587 } 1588 ensureOpen()1589 private void ensureOpen() throws IOException { 1590 if (conn == null || !conn.useable) { 1591 if (conn != null && conn.closureReason != null) { 1592 throw conn.closureReason; 1593 } else { 1594 throw new IOException("connection closed"); 1595 } 1596 } 1597 } 1598 1599 // package private (used by LdapCtx) getInstance(boolean usePool, String hostname, int port, String factory, int connectTimeout, int readTimeout, OutputStream trace, int version, String authMechanism, Control[] ctls, String protocol, String user, Object passwd, Hashtable<?,?> env)1600 static LdapClient getInstance(boolean usePool, String hostname, int port, 1601 String factory, int connectTimeout, int readTimeout, OutputStream trace, 1602 int version, String authMechanism, Control[] ctls, String protocol, 1603 String user, Object passwd, Hashtable<?,?> env) throws NamingException { 1604 1605 if (usePool) { 1606 if (LdapPoolManager.isPoolingAllowed(factory, trace, 1607 authMechanism, protocol, env)) { 1608 LdapClient answer = LdapPoolManager.getLdapClient( 1609 hostname, port, factory, connectTimeout, readTimeout, 1610 trace, version, authMechanism, ctls, protocol, user, 1611 passwd, env); 1612 answer.referenceCount = 1; // always one when starting out 1613 return answer; 1614 } 1615 } 1616 return new LdapClient(hostname, port, factory, connectTimeout, 1617 readTimeout, trace, null); 1618 } 1619 } 1620