1 /* 2 * Copyright (c) 1999, 2012, 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 protected void finalize() { 478 if (debug > 0) System.err.println("LdapClient: finalize " + this); 479 forceClose(pooled); 480 } 481 482 /* 483 * Used by connection pooling to close physical connection. 484 */ 485 synchronized public void closeConnection() { 486 forceClose(false); // this is a pool callback so no need to clean pool 487 } 488 489 /** 490 * Called by Connection.cleanup(). LdapClient should 491 * notify any unsolicited listeners and removing itself from any pool. 492 * This is almost like forceClose(), except it doesn't call 493 * Connection.cleanup() (because this is called from cleanup()). 494 */ 495 void processConnectionClosure() { 496 // Notify listeners 497 if (unsolicited.size() > 0) { 498 String msg; 499 if (conn != null) { 500 msg = conn.host + ":" + conn.port + " connection closed"; 501 } else { 502 msg = "Connection closed"; 503 } 504 notifyUnsolicited(new CommunicationException(msg)); 505 } 506 507 // Remove from pool 508 if (pooled) { 509 pcb.removePooledConnection(this); 510 } 511 } 512 513 //////////////////////////////////////////////////////////////////////////// 514 // 515 // LDAP search. also includes methods to encode rfc 1558 compliant filters 516 // 517 //////////////////////////////////////////////////////////////////////////// 518 519 static final int SCOPE_BASE_OBJECT = 0; 520 static final int SCOPE_ONE_LEVEL = 1; 521 static final int SCOPE_SUBTREE = 2; 522 523 LdapResult search(String dn, int scope, int deref, int sizeLimit, 524 int timeLimit, boolean attrsOnly, String attrs[], 525 String filter, int batchSize, Control[] reqCtls, 526 Hashtable<String, Boolean> binaryAttrs, 527 boolean waitFirstReply, int replyQueueCapacity) 528 throws IOException, NamingException { 529 530 ensureOpen(); 531 532 LdapResult res = new LdapResult(); 533 534 BerEncoder ber = new BerEncoder(); 535 int curMsgId = conn.getMsgId(); 536 537 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 538 ber.encodeInt(curMsgId); 539 ber.beginSeq(LDAP_REQ_SEARCH); 540 ber.encodeString(dn == null ? "" : dn, isLdapv3); 541 ber.encodeInt(scope, LBER_ENUMERATED); 542 ber.encodeInt(deref, LBER_ENUMERATED); 543 ber.encodeInt(sizeLimit); 544 ber.encodeInt(timeLimit); 545 ber.encodeBoolean(attrsOnly); 546 Filter.encodeFilterString(ber, filter, isLdapv3); 547 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 548 ber.encodeStringArray(attrs, isLdapv3); 549 ber.endSeq(); 550 ber.endSeq(); 551 if (isLdapv3) encodeControls(ber, reqCtls); 552 ber.endSeq(); 553 554 LdapRequest req = 555 conn.writeRequest(ber, curMsgId, false, replyQueueCapacity); 556 557 res.msgId = curMsgId; 558 res.status = LdapClient.LDAP_SUCCESS; //optimistic 559 if (waitFirstReply) { 560 // get first reply 561 res = getSearchReply(req, batchSize, res, binaryAttrs); 562 } 563 return res; 564 } 565 566 /* 567 * Abandon the search operation and remove it from the message queue. 568 */ 569 void clearSearchReply(LdapResult res, Control[] ctls) { 570 if (res != null && conn != null) { 571 572 // Only send an LDAP abandon operation when clearing the search 573 // reply from a one-level or subtree search. 574 LdapRequest req = conn.findRequest(res.msgId); 575 if (req == null) { 576 return; 577 } 578 579 // OK if req got removed after check; double removal attempt 580 // but otherwise no harm done 581 582 // Send an LDAP abandon only if the search operation has not yet 583 // completed. 584 if (req.hasSearchCompleted()) { 585 conn.removeRequest(req); 586 } else { 587 conn.abandonRequest(req, ctls); 588 } 589 } 590 } 591 592 /* 593 * Retrieve the next batch of entries and/or referrals. 594 */ 595 LdapResult getSearchReply(int batchSize, LdapResult res, 596 Hashtable<String, Boolean> binaryAttrs) throws IOException, NamingException { 597 598 ensureOpen(); 599 600 LdapRequest req; 601 602 if ((req = conn.findRequest(res.msgId)) == null) { 603 return null; 604 } 605 606 return getSearchReply(req, batchSize, res, binaryAttrs); 607 } 608 609 private LdapResult getSearchReply(LdapRequest req, 610 int batchSize, LdapResult res, Hashtable<String, Boolean> binaryAttrs) 611 throws IOException, NamingException { 612 613 if (batchSize == 0) 614 batchSize = Integer.MAX_VALUE; 615 616 if (res.entries != null) { 617 res.entries.setSize(0); // clear the (previous) set of entries 618 } else { 619 res.entries = 620 new Vector<>(batchSize == Integer.MAX_VALUE ? 32 : batchSize); 621 } 622 623 if (res.referrals != null) { 624 res.referrals.setSize(0); // clear the (previous) set of referrals 625 } 626 627 BerDecoder replyBer; // Decoder for response 628 int seq; // Request id 629 630 Attributes lattrs; // Attribute set read from response 631 Attribute la; // Attribute read from response 632 String DN; // DN read from response 633 LdapEntry le; // LDAP entry representing response 634 int[] seqlen; // Holder for response length 635 int endseq; // Position of end of response 636 637 for (int i = 0; i < batchSize;) { 638 replyBer = conn.readReply(req); 639 640 // 641 // process search reply 642 // 643 replyBer.parseSeq(null); // init seq 644 replyBer.parseInt(); // req id 645 seq = replyBer.parseSeq(null); 646 647 if (seq == LDAP_REP_SEARCH) { 648 649 // handle LDAPv3 search entries 650 lattrs = new BasicAttributes(caseIgnore); 651 DN = replyBer.parseString(isLdapv3); 652 le = new LdapEntry(DN, lattrs); 653 seqlen = new int[1]; 654 655 replyBer.parseSeq(seqlen); 656 endseq = replyBer.getParsePosition() + seqlen[0]; 657 while ((replyBer.getParsePosition() < endseq) && 658 (replyBer.bytesLeft() > 0)) { 659 la = parseAttribute(replyBer, binaryAttrs); 660 lattrs.put(la); 661 } 662 le.respCtls = isLdapv3 ? parseControls(replyBer) : null; 663 664 res.entries.addElement(le); 665 i++; 666 667 } else if ((seq == LDAP_REP_SEARCH_REF) && isLdapv3) { 668 669 // handle LDAPv3 search reference 670 Vector<String> URLs = new Vector<>(4); 671 672 // %%% Although not strictly correct, some LDAP servers 673 // encode the SEQUENCE OF tag in the SearchResultRef 674 if (replyBer.peekByte() == 675 (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR)) { 676 replyBer.parseSeq(null); 677 } 678 679 while ((replyBer.bytesLeft() > 0) && 680 (replyBer.peekByte() == Ber.ASN_OCTET_STR)) { 681 682 URLs.addElement(replyBer.parseString(isLdapv3)); 683 } 684 685 if (res.referrals == null) { 686 res.referrals = new Vector<>(4); 687 } 688 res.referrals.addElement(URLs); 689 res.resControls = isLdapv3 ? parseControls(replyBer) : null; 690 691 // Save referral and continue to get next search result 692 693 } else if (seq == LDAP_REP_EXTENSION) { 694 695 parseExtResponse(replyBer, res); //%%% ignore for now 696 697 } else if (seq == LDAP_REP_RESULT) { 698 699 parseResult(replyBer, res, isLdapv3); 700 res.resControls = isLdapv3 ? parseControls(replyBer) : null; 701 702 conn.removeRequest(req); 703 return res; // Done with search 704 } 705 } 706 707 return res; 708 } 709 710 private Attribute parseAttribute(BerDecoder ber, 711 Hashtable<String, Boolean> binaryAttrs) 712 throws IOException { 713 714 int len[] = new int[1]; 715 int seq = ber.parseSeq(null); 716 String attrid = ber.parseString(isLdapv3); 717 boolean hasBinaryValues = isBinaryValued(attrid, binaryAttrs); 718 Attribute la = new LdapAttribute(attrid); 719 720 if ((seq = ber.parseSeq(len)) == LBER_SET) { 721 int attrlen = len[0]; 722 while (ber.bytesLeft() > 0 && attrlen > 0) { 723 try { 724 attrlen -= parseAttributeValue(ber, la, hasBinaryValues); 725 } catch (IOException ex) { 726 ber.seek(attrlen); 727 break; 728 } 729 } 730 } else { 731 // Skip the rest of the sequence because it is not what we want 732 ber.seek(len[0]); 733 } 734 return la; 735 } 736 737 // 738 // returns number of bytes that were parsed. Adds the values to attr 739 // 740 private int parseAttributeValue(BerDecoder ber, Attribute la, 741 boolean hasBinaryValues) throws IOException { 742 743 int len[] = new int[1]; 744 745 if (hasBinaryValues) { 746 la.add(ber.parseOctetString(ber.peekByte(), len)); 747 } else { 748 la.add(ber.parseStringWithTag( 749 Ber.ASN_SIMPLE_STRING, isLdapv3, len)); 750 } 751 return len[0]; 752 } 753 754 private boolean isBinaryValued(String attrid, 755 Hashtable<String, Boolean> binaryAttrs) { 756 String id = attrid.toLowerCase(Locale.ENGLISH); 757 758 return ((id.indexOf(";binary") != -1) || 759 defaultBinaryAttrs.containsKey(id) || 760 ((binaryAttrs != null) && (binaryAttrs.containsKey(id)))); 761 } 762 763 // package entry point; used by Connection 764 static void parseResult(BerDecoder replyBer, LdapResult res, 765 boolean isLdapv3) throws IOException { 766 767 res.status = replyBer.parseEnumeration(); 768 res.matchedDN = replyBer.parseString(isLdapv3); 769 res.errorMessage = replyBer.parseString(isLdapv3); 770 771 // handle LDAPv3 referrals (if present) 772 if (isLdapv3 && 773 (replyBer.bytesLeft() > 0) && 774 (replyBer.peekByte() == LDAP_REP_REFERRAL)) { 775 776 Vector<String> URLs = new Vector<>(4); 777 int[] seqlen = new int[1]; 778 779 replyBer.parseSeq(seqlen); 780 int endseq = replyBer.getParsePosition() + seqlen[0]; 781 while ((replyBer.getParsePosition() < endseq) && 782 (replyBer.bytesLeft() > 0)) { 783 784 URLs.addElement(replyBer.parseString(isLdapv3)); 785 } 786 787 if (res.referrals == null) { 788 res.referrals = new Vector<>(4); 789 } 790 res.referrals.addElement(URLs); 791 } 792 } 793 794 // package entry point; used by Connection 795 static Vector<Control> parseControls(BerDecoder replyBer) throws IOException { 796 797 // handle LDAPv3 controls (if present) 798 if ((replyBer.bytesLeft() > 0) && (replyBer.peekByte() == LDAP_CONTROLS)) { 799 Vector<Control> ctls = new Vector<>(4); 800 String controlOID; 801 boolean criticality = false; // default 802 byte[] controlValue = null; // optional 803 int[] seqlen = new int[1]; 804 805 replyBer.parseSeq(seqlen); 806 int endseq = replyBer.getParsePosition() + seqlen[0]; 807 while ((replyBer.getParsePosition() < endseq) && 808 (replyBer.bytesLeft() > 0)) { 809 810 replyBer.parseSeq(null); 811 controlOID = replyBer.parseString(true); 812 813 if ((replyBer.bytesLeft() > 0) && 814 (replyBer.peekByte() == Ber.ASN_BOOLEAN)) { 815 criticality = replyBer.parseBoolean(); 816 } 817 if ((replyBer.bytesLeft() > 0) && 818 (replyBer.peekByte() == Ber.ASN_OCTET_STR)) { 819 controlValue = 820 replyBer.parseOctetString(Ber.ASN_OCTET_STR, null); 821 } 822 if (controlOID != null) { 823 ctls.addElement( 824 new BasicControl(controlOID, criticality, controlValue)); 825 } 826 } 827 return ctls; 828 } else { 829 return null; 830 } 831 } 832 833 private void parseExtResponse(BerDecoder replyBer, LdapResult res) 834 throws IOException { 835 836 parseResult(replyBer, res, isLdapv3); 837 838 if ((replyBer.bytesLeft() > 0) && 839 (replyBer.peekByte() == LDAP_REP_EXT_OID)) { 840 res.extensionId = 841 replyBer.parseStringWithTag(LDAP_REP_EXT_OID, isLdapv3, null); 842 } 843 if ((replyBer.bytesLeft() > 0) && 844 (replyBer.peekByte() == LDAP_REP_EXT_VAL)) { 845 res.extensionValue = 846 replyBer.parseOctetString(LDAP_REP_EXT_VAL, null); 847 } 848 849 res.resControls = parseControls(replyBer); 850 } 851 852 // 853 // Encode LDAPv3 controls 854 // 855 static void encodeControls(BerEncoder ber, Control[] reqCtls) 856 throws IOException { 857 858 if ((reqCtls == null) || (reqCtls.length == 0)) { 859 return; 860 } 861 862 byte[] controlVal; 863 864 ber.beginSeq(LdapClient.LDAP_CONTROLS); 865 866 for (int i = 0; i < reqCtls.length; i++) { 867 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 868 ber.encodeString(reqCtls[i].getID(), true); // control OID 869 if (reqCtls[i].isCritical()) { 870 ber.encodeBoolean(true); // critical control 871 } 872 if ((controlVal = reqCtls[i].getEncodedValue()) != null) { 873 ber.encodeOctetString(controlVal, Ber.ASN_OCTET_STR); 874 } 875 ber.endSeq(); 876 } 877 ber.endSeq(); 878 } 879 880 /** 881 * Reads the next reply corresponding to msgId, outstanding on requestBer. 882 * Processes the result and any controls. 883 */ 884 private LdapResult processReply(LdapRequest req, 885 LdapResult res, int responseType) throws IOException, NamingException { 886 887 BerDecoder rber = conn.readReply(req); 888 889 rber.parseSeq(null); // init seq 890 rber.parseInt(); // msg id 891 if (rber.parseByte() != responseType) { 892 return res; 893 } 894 895 rber.parseLength(); 896 parseResult(rber, res, isLdapv3); 897 res.resControls = isLdapv3 ? parseControls(rber) : null; 898 899 conn.removeRequest(req); 900 901 return res; // Done with operation 902 } 903 904 //////////////////////////////////////////////////////////////////////////// 905 // 906 // LDAP modify: 907 // Modify the DN dn with the operations on attributes attrs. 908 // ie, operations[0] is the operation to be performed on 909 // attrs[0]; 910 // dn - DN to modify 911 // operations - add, delete or replace 912 // attrs - array of Attribute 913 // reqCtls - array of request controls 914 // 915 //////////////////////////////////////////////////////////////////////////// 916 917 static final int ADD = 0; 918 static final int DELETE = 1; 919 static final int REPLACE = 2; 920 921 LdapResult modify(String dn, int operations[], Attribute attrs[], 922 Control[] reqCtls) 923 throws IOException, NamingException { 924 925 ensureOpen(); 926 927 LdapResult res = new LdapResult(); 928 res.status = LDAP_OPERATIONS_ERROR; 929 930 if (dn == null || operations.length != attrs.length) 931 return res; 932 933 BerEncoder ber = new BerEncoder(); 934 int curMsgId = conn.getMsgId(); 935 936 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 937 ber.encodeInt(curMsgId); 938 ber.beginSeq(LDAP_REQ_MODIFY); 939 ber.encodeString(dn, isLdapv3); 940 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 941 for (int i = 0; i < operations.length; i++) { 942 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 943 ber.encodeInt(operations[i], LBER_ENUMERATED); 944 945 // zero values is not permitted for the add op. 946 if ((operations[i] == ADD) && hasNoValue(attrs[i])) { 947 throw new InvalidAttributeValueException( 948 "'" + attrs[i].getID() + "' has no values."); 949 } else { 950 encodeAttribute(ber, attrs[i]); 951 } 952 ber.endSeq(); 953 } 954 ber.endSeq(); 955 ber.endSeq(); 956 if (isLdapv3) encodeControls(ber, reqCtls); 957 ber.endSeq(); 958 959 LdapRequest req = conn.writeRequest(ber, curMsgId); 960 961 return processReply(req, res, LDAP_REP_MODIFY); 962 } 963 964 private void encodeAttribute(BerEncoder ber, Attribute attr) 965 throws IOException, NamingException { 966 967 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 968 ber.encodeString(attr.getID(), isLdapv3); 969 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR | 1); 970 NamingEnumeration<?> enum_ = attr.getAll(); 971 Object val; 972 while (enum_.hasMore()) { 973 val = enum_.next(); 974 if (val instanceof String) { 975 ber.encodeString((String)val, isLdapv3); 976 } else if (val instanceof byte[]) { 977 ber.encodeOctetString((byte[])val, Ber.ASN_OCTET_STR); 978 } else if (val == null) { 979 // no attribute value 980 } else { 981 throw new InvalidAttributeValueException( 982 "Malformed '" + attr.getID() + "' attribute value"); 983 } 984 } 985 ber.endSeq(); 986 ber.endSeq(); 987 } 988 989 private static boolean hasNoValue(Attribute attr) throws NamingException { 990 return attr.size() == 0 || (attr.size() == 1 && attr.get() == null); 991 } 992 993 //////////////////////////////////////////////////////////////////////////// 994 // 995 // LDAP add 996 // Adds entry to the Directory 997 // 998 //////////////////////////////////////////////////////////////////////////// 999 1000 LdapResult add(LdapEntry entry, Control[] reqCtls) 1001 throws IOException, NamingException { 1002 1003 ensureOpen(); 1004 1005 LdapResult res = new LdapResult(); 1006 res.status = LDAP_OPERATIONS_ERROR; 1007 1008 if (entry == null || entry.DN == null) 1009 return res; 1010 1011 BerEncoder ber = new BerEncoder(); 1012 int curMsgId = conn.getMsgId(); 1013 Attribute attr; 1014 1015 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1016 ber.encodeInt(curMsgId); 1017 ber.beginSeq(LDAP_REQ_ADD); 1018 ber.encodeString(entry.DN, isLdapv3); 1019 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1020 NamingEnumeration<? extends Attribute> enum_ = 1021 entry.attributes.getAll(); 1022 while (enum_.hasMore()) { 1023 attr = enum_.next(); 1024 1025 // zero values is not permitted 1026 if (hasNoValue(attr)) { 1027 throw new InvalidAttributeValueException( 1028 "'" + attr.getID() + "' has no values."); 1029 } else { 1030 encodeAttribute(ber, attr); 1031 } 1032 } 1033 ber.endSeq(); 1034 ber.endSeq(); 1035 if (isLdapv3) encodeControls(ber, reqCtls); 1036 ber.endSeq(); 1037 1038 LdapRequest req = conn.writeRequest(ber, curMsgId); 1039 return processReply(req, res, LDAP_REP_ADD); 1040 } 1041 1042 //////////////////////////////////////////////////////////////////////////// 1043 // 1044 // LDAP delete 1045 // deletes entry from the Directory 1046 // 1047 //////////////////////////////////////////////////////////////////////////// 1048 1049 LdapResult delete(String DN, Control[] reqCtls) 1050 throws IOException, NamingException { 1051 1052 ensureOpen(); 1053 1054 LdapResult res = new LdapResult(); 1055 res.status = LDAP_OPERATIONS_ERROR; 1056 1057 if (DN == null) 1058 return res; 1059 1060 BerEncoder ber = new BerEncoder(); 1061 int curMsgId = conn.getMsgId(); 1062 1063 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1064 ber.encodeInt(curMsgId); 1065 ber.encodeString(DN, LDAP_REQ_DELETE, isLdapv3); 1066 if (isLdapv3) encodeControls(ber, reqCtls); 1067 ber.endSeq(); 1068 1069 LdapRequest req = conn.writeRequest(ber, curMsgId); 1070 1071 return processReply(req, res, LDAP_REP_DELETE); 1072 } 1073 1074 //////////////////////////////////////////////////////////////////////////// 1075 // 1076 // LDAP modrdn 1077 // Changes the last element of DN to newrdn 1078 // dn - DN to change 1079 // newrdn - new RDN to rename to 1080 // deleteoldrdn - boolean whether to delete old attrs or not 1081 // newSuperior - new place to put the entry in the tree 1082 // (ignored if server is LDAPv2) 1083 // reqCtls - array of request controls 1084 // 1085 //////////////////////////////////////////////////////////////////////////// 1086 1087 LdapResult moddn(String DN, String newrdn, boolean deleteOldRdn, 1088 String newSuperior, Control[] reqCtls) 1089 throws IOException, NamingException { 1090 1091 ensureOpen(); 1092 1093 boolean changeSuperior = (newSuperior != null && 1094 newSuperior.length() > 0); 1095 1096 LdapResult res = new LdapResult(); 1097 res.status = LDAP_OPERATIONS_ERROR; 1098 1099 if (DN == null || newrdn == null) 1100 return res; 1101 1102 BerEncoder ber = new BerEncoder(); 1103 int curMsgId = conn.getMsgId(); 1104 1105 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1106 ber.encodeInt(curMsgId); 1107 ber.beginSeq(LDAP_REQ_MODRDN); 1108 ber.encodeString(DN, isLdapv3); 1109 ber.encodeString(newrdn, isLdapv3); 1110 ber.encodeBoolean(deleteOldRdn); 1111 if(isLdapv3 && changeSuperior) { 1112 //System.err.println("changin superior"); 1113 ber.encodeString(newSuperior, LDAP_SUPERIOR_DN, isLdapv3); 1114 } 1115 ber.endSeq(); 1116 if (isLdapv3) encodeControls(ber, reqCtls); 1117 ber.endSeq(); 1118 1119 1120 LdapRequest req = conn.writeRequest(ber, curMsgId); 1121 1122 return processReply(req, res, LDAP_REP_MODRDN); 1123 } 1124 1125 //////////////////////////////////////////////////////////////////////////// 1126 // 1127 // LDAP compare 1128 // Compare attribute->value pairs in dn 1129 // 1130 //////////////////////////////////////////////////////////////////////////// 1131 1132 LdapResult compare(String DN, String type, String value, Control[] reqCtls) 1133 throws IOException, NamingException { 1134 1135 ensureOpen(); 1136 1137 LdapResult res = new LdapResult(); 1138 res.status = LDAP_OPERATIONS_ERROR; 1139 1140 if (DN == null || type == null || value == null) 1141 return res; 1142 1143 BerEncoder ber = new BerEncoder(); 1144 int curMsgId = conn.getMsgId(); 1145 1146 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1147 ber.encodeInt(curMsgId); 1148 ber.beginSeq(LDAP_REQ_COMPARE); 1149 ber.encodeString(DN, isLdapv3); 1150 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1151 ber.encodeString(type, isLdapv3); 1152 1153 // replace any escaped characters in the value 1154 byte[] val = isLdapv3 ? 1155 value.getBytes("UTF8") : value.getBytes("8859_1"); 1156 ber.encodeOctetString( 1157 Filter.unescapeFilterValue(val, 0, val.length), 1158 Ber.ASN_OCTET_STR); 1159 1160 ber.endSeq(); 1161 ber.endSeq(); 1162 if (isLdapv3) encodeControls(ber, reqCtls); 1163 ber.endSeq(); 1164 1165 LdapRequest req = conn.writeRequest(ber, curMsgId); 1166 1167 return processReply(req, res, LDAP_REP_COMPARE); 1168 } 1169 1170 //////////////////////////////////////////////////////////////////////////// 1171 // 1172 // LDAP extended operation 1173 // 1174 //////////////////////////////////////////////////////////////////////////// 1175 1176 LdapResult extendedOp(String id, byte[] request, Control[] reqCtls, 1177 boolean pauseAfterReceipt) throws IOException, NamingException { 1178 1179 ensureOpen(); 1180 1181 LdapResult res = new LdapResult(); 1182 res.status = LDAP_OPERATIONS_ERROR; 1183 1184 if (id == null) 1185 return res; 1186 1187 BerEncoder ber = new BerEncoder(); 1188 int curMsgId = conn.getMsgId(); 1189 1190 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1191 ber.encodeInt(curMsgId); 1192 ber.beginSeq(LDAP_REQ_EXTENSION); 1193 ber.encodeString(id, 1194 Ber.ASN_CONTEXT | 0, isLdapv3);//[0] 1195 if (request != null) { 1196 ber.encodeOctetString(request, 1197 Ber.ASN_CONTEXT | 1);//[1] 1198 } 1199 ber.endSeq(); 1200 encodeControls(ber, reqCtls); // always v3 1201 ber.endSeq(); 1202 1203 LdapRequest req = conn.writeRequest(ber, curMsgId, pauseAfterReceipt); 1204 1205 BerDecoder rber = conn.readReply(req); 1206 1207 rber.parseSeq(null); // init seq 1208 rber.parseInt(); // msg id 1209 if (rber.parseByte() != LDAP_REP_EXTENSION) { 1210 return res; 1211 } 1212 1213 rber.parseLength(); 1214 parseExtResponse(rber, res); 1215 conn.removeRequest(req); 1216 1217 return res; // Done with operation 1218 } 1219 1220 1221 1222 //////////////////////////////////////////////////////////////////////////// 1223 // 1224 // Some BER definitions convenient for LDAP 1225 // 1226 //////////////////////////////////////////////////////////////////////////// 1227 1228 static final int LDAP_VERSION3_VERSION2 = 32; 1229 static final int LDAP_VERSION2 = 0x02; 1230 static final int LDAP_VERSION3 = 0x03; // LDAPv3 1231 static final int LDAP_VERSION = LDAP_VERSION3; 1232 1233 static final int LDAP_REF_FOLLOW = 0x01; // follow referrals 1234 static final int LDAP_REF_THROW = 0x02; // throw referral ex. 1235 static final int LDAP_REF_IGNORE = 0x03; // ignore referrals 1236 1237 static final String LDAP_URL = "ldap://"; // LDAPv3 1238 static final String LDAPS_URL = "ldaps://"; // LDAPv3 1239 1240 static final int LBER_BOOLEAN = 0x01; 1241 static final int LBER_INTEGER = 0x02; 1242 static final int LBER_BITSTRING = 0x03; 1243 static final int LBER_OCTETSTRING = 0x04; 1244 static final int LBER_NULL = 0x05; 1245 static final int LBER_ENUMERATED = 0x0a; 1246 static final int LBER_SEQUENCE = 0x30; 1247 static final int LBER_SET = 0x31; 1248 1249 static final int LDAP_SUPERIOR_DN = 0x80; 1250 1251 static final int LDAP_REQ_BIND = 0x60; // app + constructed 1252 static final int LDAP_REQ_UNBIND = 0x42; // app + primitive 1253 static final int LDAP_REQ_SEARCH = 0x63; // app + constructed 1254 static final int LDAP_REQ_MODIFY = 0x66; // app + constructed 1255 static final int LDAP_REQ_ADD = 0x68; // app + constructed 1256 static final int LDAP_REQ_DELETE = 0x4a; // app + primitive 1257 static final int LDAP_REQ_MODRDN = 0x6c; // app + constructed 1258 static final int LDAP_REQ_COMPARE = 0x6e; // app + constructed 1259 static final int LDAP_REQ_ABANDON = 0x50; // app + primitive 1260 static final int LDAP_REQ_EXTENSION = 0x77; // app + constructed (LDAPv3) 1261 1262 static final int LDAP_REP_BIND = 0x61; // app + constructed | 1 1263 static final int LDAP_REP_SEARCH = 0x64; // app + constructed | 4 1264 static final int LDAP_REP_SEARCH_REF = 0x73;// app + constructed (LDAPv3) 1265 static final int LDAP_REP_RESULT = 0x65; // app + constructed | 5 1266 static final int LDAP_REP_MODIFY = 0x67; // app + constructed | 7 1267 static final int LDAP_REP_ADD = 0x69; // app + constructed | 9 1268 static final int LDAP_REP_DELETE = 0x6b; // app + primitive | b 1269 static final int LDAP_REP_MODRDN = 0x6d; // app + primitive | d 1270 static final int LDAP_REP_COMPARE = 0x6f; // app + primitive | f 1271 static final int LDAP_REP_EXTENSION = 0x78; // app + constructed (LDAPv3) 1272 1273 static final int LDAP_REP_REFERRAL = 0xa3; // ctx + constructed (LDAPv3) 1274 static final int LDAP_REP_EXT_OID = 0x8a; // ctx + primitive (LDAPv3) 1275 static final int LDAP_REP_EXT_VAL = 0x8b; // ctx + primitive (LDAPv3) 1276 1277 // LDAPv3 Controls 1278 1279 static final int LDAP_CONTROLS = 0xa0; // ctx + constructed (LDAPv3) 1280 static final String LDAP_CONTROL_MANAGE_DSA_IT = "2.16.840.1.113730.3.4.2"; 1281 static final String LDAP_CONTROL_PREFERRED_LANG = "1.3.6.1.4.1.1466.20035"; 1282 static final String LDAP_CONTROL_PAGED_RESULTS = "1.2.840.113556.1.4.319"; 1283 static final String LDAP_CONTROL_SERVER_SORT_REQ = "1.2.840.113556.1.4.473"; 1284 static final String LDAP_CONTROL_SERVER_SORT_RES = "1.2.840.113556.1.4.474"; 1285 1286 //////////////////////////////////////////////////////////////////////////// 1287 // 1288 // return codes 1289 // 1290 //////////////////////////////////////////////////////////////////////////// 1291 1292 static final int LDAP_SUCCESS = 0; 1293 static final int LDAP_OPERATIONS_ERROR = 1; 1294 static final int LDAP_PROTOCOL_ERROR = 2; 1295 static final int LDAP_TIME_LIMIT_EXCEEDED = 3; 1296 static final int LDAP_SIZE_LIMIT_EXCEEDED = 4; 1297 static final int LDAP_COMPARE_FALSE = 5; 1298 static final int LDAP_COMPARE_TRUE = 6; 1299 static final int LDAP_AUTH_METHOD_NOT_SUPPORTED = 7; 1300 static final int LDAP_STRONG_AUTH_REQUIRED = 8; 1301 static final int LDAP_PARTIAL_RESULTS = 9; // Slapd 1302 static final int LDAP_REFERRAL = 10; // LDAPv3 1303 static final int LDAP_ADMIN_LIMIT_EXCEEDED = 11; // LDAPv3 1304 static final int LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 12; // LDAPv3 1305 static final int LDAP_CONFIDENTIALITY_REQUIRED = 13; // LDAPv3 1306 static final int LDAP_SASL_BIND_IN_PROGRESS = 14; // LDAPv3 1307 static final int LDAP_NO_SUCH_ATTRIBUTE = 16; 1308 static final int LDAP_UNDEFINED_ATTRIBUTE_TYPE = 17; 1309 static final int LDAP_INAPPROPRIATE_MATCHING = 18; 1310 static final int LDAP_CONSTRAINT_VIOLATION = 19; 1311 static final int LDAP_ATTRIBUTE_OR_VALUE_EXISTS = 20; 1312 static final int LDAP_INVALID_ATTRIBUTE_SYNTAX = 21; 1313 static final int LDAP_NO_SUCH_OBJECT = 32; 1314 static final int LDAP_ALIAS_PROBLEM = 33; 1315 static final int LDAP_INVALID_DN_SYNTAX = 34; 1316 static final int LDAP_IS_LEAF = 35; 1317 static final int LDAP_ALIAS_DEREFERENCING_PROBLEM = 36; 1318 static final int LDAP_INAPPROPRIATE_AUTHENTICATION = 48; 1319 static final int LDAP_INVALID_CREDENTIALS = 49; 1320 static final int LDAP_INSUFFICIENT_ACCESS_RIGHTS = 50; 1321 static final int LDAP_BUSY = 51; 1322 static final int LDAP_UNAVAILABLE = 52; 1323 static final int LDAP_UNWILLING_TO_PERFORM = 53; 1324 static final int LDAP_LOOP_DETECT = 54; 1325 static final int LDAP_NAMING_VIOLATION = 64; 1326 static final int LDAP_OBJECT_CLASS_VIOLATION = 65; 1327 static final int LDAP_NOT_ALLOWED_ON_NON_LEAF = 66; 1328 static final int LDAP_NOT_ALLOWED_ON_RDN = 67; 1329 static final int LDAP_ENTRY_ALREADY_EXISTS = 68; 1330 static final int LDAP_OBJECT_CLASS_MODS_PROHIBITED = 69; 1331 static final int LDAP_AFFECTS_MULTIPLE_DSAS = 71; // LDAPv3 1332 static final int LDAP_OTHER = 80; 1333 1334 static final String[] ldap_error_message = { 1335 "Success", // 0 1336 "Operations Error", // 1 1337 "Protocol Error", // 2 1338 "Timelimit Exceeded", // 3 1339 "Sizelimit Exceeded", // 4 1340 "Compare False", // 5 1341 "Compare True", // 6 1342 "Authentication Method Not Supported", // 7 1343 "Strong Authentication Required", // 8 1344 null, 1345 "Referral", // 10 1346 "Administrative Limit Exceeded", // 11 1347 "Unavailable Critical Extension", // 12 1348 "Confidentiality Required", // 13 1349 "SASL Bind In Progress", // 14 1350 null, 1351 "No Such Attribute", // 16 1352 "Undefined Attribute Type", // 17 1353 "Inappropriate Matching", // 18 1354 "Constraint Violation", // 19 1355 "Attribute Or Value Exists", // 20 1356 "Invalid Attribute Syntax", // 21 1357 null, 1358 null, 1359 null, 1360 null, 1361 null, 1362 null, 1363 null, 1364 null, 1365 null, 1366 null, 1367 "No Such Object", // 32 1368 "Alias Problem", // 33 1369 "Invalid DN Syntax", // 34 1370 null, 1371 "Alias Dereferencing Problem", // 36 1372 null, 1373 null, 1374 null, 1375 null, 1376 null, 1377 null, 1378 null, 1379 null, 1380 null, 1381 null, 1382 null, 1383 "Inappropriate Authentication", // 48 1384 "Invalid Credentials", // 49 1385 "Insufficient Access Rights", // 50 1386 "Busy", // 51 1387 "Unavailable", // 52 1388 "Unwilling To Perform", // 53 1389 "Loop Detect", // 54 1390 null, 1391 null, 1392 null, 1393 null, 1394 null, 1395 null, 1396 null, 1397 null, 1398 null, 1399 "Naming Violation", // 64 1400 "Object Class Violation", // 65 1401 "Not Allowed On Non-leaf", // 66 1402 "Not Allowed On RDN", // 67 1403 "Entry Already Exists", // 68 1404 "Object Class Modifications Prohibited", // 69 1405 null, 1406 "Affects Multiple DSAs", // 71 1407 null, 1408 null, 1409 null, 1410 null, 1411 null, 1412 null, 1413 null, 1414 null, 1415 "Other", // 80 1416 null, 1417 null, 1418 null, 1419 null, 1420 null, 1421 null, 1422 null, 1423 null, 1424 null, 1425 null 1426 }; 1427 1428 1429 /* 1430 * Generate an error message from the LDAP error code and error diagnostic. 1431 * The message format is: 1432 * 1433 * "[LDAP: error code <errorCode> - <errorMessage>]" 1434 * 1435 * where <errorCode> is a numeric error code 1436 * and <errorMessage> is a textual description of the error (if available) 1437 * 1438 */ 1439 static String getErrorMessage(int errorCode, String errorMessage) { 1440 1441 String message = "[LDAP: error code " + errorCode; 1442 1443 if ((errorMessage != null) && (errorMessage.length() != 0)) { 1444 1445 // append error message from the server 1446 message = message + " - " + errorMessage + "]"; 1447 1448 } else { 1449 1450 // append built-in error message 1451 try { 1452 if (ldap_error_message[errorCode] != null) { 1453 message = message + " - " + ldap_error_message[errorCode] + 1454 "]"; 1455 } 1456 } catch (ArrayIndexOutOfBoundsException ex) { 1457 message = message + "]"; 1458 } 1459 } 1460 return message; 1461 } 1462 1463 1464 //////////////////////////////////////////////////////////////////////////// 1465 // 1466 // Unsolicited notification support. 1467 // 1468 // An LdapClient maintains a list of LdapCtx that have registered 1469 // for UnsolicitedNotifications. This is a list because a single 1470 // LdapClient might be shared among multiple contexts. 1471 // 1472 // When addUnsolicited() is invoked, the LdapCtx is added to the list. 1473 // 1474 // When Connection receives an unsolicited notification (msgid == 0), 1475 // it invokes LdapClient.processUnsolicited(). processUnsolicited() 1476 // parses the Extended Response. If there are registered listeners, 1477 // LdapClient creates an UnsolicitedNotification from the response 1478 // and informs each LdapCtx to fire an event for the notification. 1479 // If it is a DISCONNECT notification, the connection is closed and a 1480 // NamingExceptionEvent is fired to the listeners. 1481 // 1482 // When the connection is closed out-of-band like this, the next 1483 // time a method is invoked on LdapClient, an IOException is thrown. 1484 // 1485 // removeUnsolicited() is invoked to remove an LdapCtx from this client. 1486 // 1487 //////////////////////////////////////////////////////////////////////////// 1488 private Vector<LdapCtx> unsolicited = new Vector<>(3); 1489 void addUnsolicited(LdapCtx ctx) { 1490 if (debug > 0) { 1491 System.err.println("LdapClient.addUnsolicited" + ctx); 1492 } 1493 unsolicited.addElement(ctx); 1494 } 1495 1496 void removeUnsolicited(LdapCtx ctx) { 1497 if (debug > 0) { 1498 System.err.println("LdapClient.removeUnsolicited" + ctx); 1499 } 1500 unsolicited.removeElement(ctx); 1501 } 1502 1503 // NOTE: Cannot be synchronized because this is called asynchronously 1504 // by the reader thread in Connection. Instead, sync on 'unsolicited' Vector. 1505 void processUnsolicited(BerDecoder ber) { 1506 if (debug > 0) { 1507 System.err.println("LdapClient.processUnsolicited"); 1508 } 1509 try { 1510 // Parse the response 1511 LdapResult res = new LdapResult(); 1512 1513 ber.parseSeq(null); // init seq 1514 ber.parseInt(); // msg id; should be 0; ignored 1515 if (ber.parseByte() != LDAP_REP_EXTENSION) { 1516 throw new IOException( 1517 "Unsolicited Notification must be an Extended Response"); 1518 } 1519 ber.parseLength(); 1520 parseExtResponse(ber, res); 1521 1522 if (DISCONNECT_OID.equals(res.extensionId)) { 1523 // force closing of connection 1524 forceClose(pooled); 1525 } 1526 1527 LdapCtx first = null; 1528 UnsolicitedNotification notice = null; 1529 1530 synchronized (unsolicited) { 1531 if (unsolicited.size() > 0) { 1532 first = unsolicited.elementAt(0); 1533 1534 // Create an UnsolicitedNotification using the parsed data 1535 // Need a 'ctx' object because we want to use the context's 1536 // list of provider control factories. 1537 notice = new UnsolicitedResponseImpl( 1538 res.extensionId, 1539 res.extensionValue, 1540 res.referrals, 1541 res.status, 1542 res.errorMessage, 1543 res.matchedDN, 1544 (res.resControls != null) ? 1545 first.convertControls(res.resControls) : 1546 null); 1547 } 1548 } 1549 1550 if (notice != null) { 1551 // Fire UnsolicitedNotification events to listeners 1552 notifyUnsolicited(notice); 1553 1554 // If "disconnect" notification, 1555 // notify unsolicited listeners via NamingException 1556 if (DISCONNECT_OID.equals(res.extensionId)) { 1557 notifyUnsolicited( 1558 new CommunicationException("Connection closed")); 1559 } 1560 } 1561 } catch (IOException e) { 1562 NamingException ne = new CommunicationException( 1563 "Problem parsing unsolicited notification"); 1564 ne.setRootCause(e); 1565 1566 notifyUnsolicited(ne); 1567 1568 } catch (NamingException e) { 1569 notifyUnsolicited(e); 1570 } 1571 } 1572 1573 1574 private void notifyUnsolicited(Object e) { 1575 Vector<LdapCtx> unsolicitedCopy; 1576 synchronized (unsolicited) { 1577 unsolicitedCopy = new Vector<>(unsolicited); 1578 if (e instanceof NamingException) { 1579 unsolicited.setSize(0); // no more listeners after exception 1580 } 1581 } 1582 for (int i = 0; i < unsolicitedCopy.size(); i++) { 1583 unsolicitedCopy.elementAt(i).fireUnsolicited(e); 1584 } 1585 } 1586 1587 private void ensureOpen() throws IOException { 1588 if (conn == null || !conn.useable) { 1589 if (conn != null && conn.closureReason != null) { 1590 throw conn.closureReason; 1591 } else { 1592 throw new IOException("connection closed"); 1593 } 1594 } 1595 } 1596 1597 // package private (used by LdapCtx) 1598 static LdapClient getInstance(boolean usePool, String hostname, int port, 1599 String factory, int connectTimeout, int readTimeout, OutputStream trace, 1600 int version, String authMechanism, Control[] ctls, String protocol, 1601 String user, Object passwd, Hashtable<?,?> env) throws NamingException { 1602 1603 if (usePool) { 1604 if (LdapPoolManager.isPoolingAllowed(factory, trace, 1605 authMechanism, protocol, env)) { 1606 LdapClient answer = LdapPoolManager.getLdapClient( 1607 hostname, port, factory, connectTimeout, readTimeout, 1608 trace, version, authMechanism, ctls, protocol, user, 1609 passwd, env); 1610 answer.referenceCount = 1; // always one when starting out 1611 return answer; 1612 } 1613 } 1614 return new LdapClient(hostname, port, factory, connectTimeout, 1615 readTimeout, trace, null); 1616 } 1617 }