1 /* 2 * Copyright (c) 1999, 2017, 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 Connection conn; // Connection to server; has reader thread 119 // used by LdapCtx for StartTLS 120 121 final private PoolCallback pcb; 122 final private boolean pooled; 123 private boolean authenticateCalled = false; 124 125 //////////////////////////////////////////////////////////////////////////// 126 // 127 // constructor: Create an authenticated connection to server 128 // 129 //////////////////////////////////////////////////////////////////////////// 130 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 144 synchronized boolean authenticateCalled() { 145 return authenticateCalled; 146 } 147 148 synchronized LdapResult 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 */ 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 */ 395 boolean usingSaslStreams() { 396 return (conn.inStream instanceof SaslInputStream); 397 } 398 399 synchronized void incRefCount() { 400 ++referenceCount; 401 if (debug > 1) { 402 System.err.println("LdapClient.incRefCount: " + referenceCount + " " + this); 403 } 404 405 } 406 407 /** 408 * Returns the encoded password. 409 */ 410 private static byte[] encodePassword(Object pw, boolean v3) throws IOException { 411 412 if (pw instanceof char[]) { 413 pw = new String((char[])pw); 414 } 415 416 if (pw instanceof String) { 417 if (v3) { 418 return ((String)pw).getBytes("UTF8"); 419 } else { 420 return ((String)pw).getBytes("8859_1"); 421 } 422 } else { 423 return (byte[])pw; 424 } 425 } 426 427 synchronized void close(Control[] reqCtls, boolean hardClose) { 428 --referenceCount; 429 430 if (debug > 1) { 431 System.err.println("LdapClient: " + this); 432 System.err.println("LdapClient: close() called: " + referenceCount); 433 (new Throwable()).printStackTrace(); 434 } 435 436 if (referenceCount <= 0 && conn != null) { 437 if (debug > 0) System.err.println("LdapClient: closed connection " + this); 438 if (!pooled) { 439 // Not being pooled; continue with closing 440 conn.cleanup(reqCtls, false); 441 conn = null; 442 } else { 443 // Pooled 444 445 // Is this a real close or a request to return conn to pool 446 if (hardClose) { 447 conn.cleanup(reqCtls, false); 448 conn = null; 449 pcb.removePooledConnection(this); 450 } else { 451 pcb.releasePooledConnection(this); 452 } 453 } 454 } 455 } 456 457 // NOTE: Should NOT be synchronized otherwise won't be able to close 458 private void forceClose(boolean cleanPool) { 459 referenceCount = 0; // force closing of connection 460 461 if (debug > 1) { 462 System.err.println("LdapClient: forceClose() of " + this); 463 } 464 465 if (conn != null) { 466 if (debug > 0) System.err.println( 467 "LdapClient: forced close of connection " + this); 468 conn.cleanup(null, false); 469 conn = null; 470 471 if (cleanPool) { 472 pcb.removePooledConnection(this); 473 } 474 } 475 } 476 477 @SuppressWarnings("deprecation") 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 */ 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 */ 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 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 */ 570 void clearSearchReply(LdapResult res, Control[] ctls) { 571 if (res != null && conn != 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 */ 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 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 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 // 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 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 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 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 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 // 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 */ 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 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 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 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 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 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 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 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 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 */ 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); 1491 void addUnsolicited(LdapCtx ctx) { 1492 if (debug > 0) { 1493 System.err.println("LdapClient.addUnsolicited" + ctx); 1494 } 1495 unsolicited.addElement(ctx); 1496 } 1497 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. 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 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 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) 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 }