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 synchronized (unsolicited) { 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 509 // Remove from pool 510 if (pooled) { 511 pcb.removePooledConnection(this); 512 } 513 } 514 515 //////////////////////////////////////////////////////////////////////////// 516 // 517 // LDAP search. also includes methods to encode rfc 1558 compliant filters 518 // 519 //////////////////////////////////////////////////////////////////////////// 520 521 static final int SCOPE_BASE_OBJECT = 0; 522 static final int SCOPE_ONE_LEVEL = 1; 523 static final int SCOPE_SUBTREE = 2; 524 525 LdapResult search(String dn, int scope, int deref, int sizeLimit, 526 int timeLimit, boolean attrsOnly, String attrs[], 527 String filter, int batchSize, Control[] reqCtls, 528 Hashtable<String, Boolean> binaryAttrs, 529 boolean waitFirstReply, int replyQueueCapacity) 530 throws IOException, NamingException { 531 532 ensureOpen(); 533 534 LdapResult res = new LdapResult(); 535 536 BerEncoder ber = new BerEncoder(); 537 int curMsgId = conn.getMsgId(); 538 539 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 540 ber.encodeInt(curMsgId); 541 ber.beginSeq(LDAP_REQ_SEARCH); 542 ber.encodeString(dn == null ? "" : dn, isLdapv3); 543 ber.encodeInt(scope, LBER_ENUMERATED); 544 ber.encodeInt(deref, LBER_ENUMERATED); 545 ber.encodeInt(sizeLimit); 546 ber.encodeInt(timeLimit); 547 ber.encodeBoolean(attrsOnly); 548 Filter.encodeFilterString(ber, filter, isLdapv3); 549 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 550 ber.encodeStringArray(attrs, isLdapv3); 551 ber.endSeq(); 552 ber.endSeq(); 553 if (isLdapv3) encodeControls(ber, reqCtls); 554 ber.endSeq(); 555 556 LdapRequest req = 557 conn.writeRequest(ber, curMsgId, false, replyQueueCapacity); 558 559 res.msgId = curMsgId; 560 res.status = LdapClient.LDAP_SUCCESS; //optimistic 561 if (waitFirstReply) { 562 // get first reply 563 res = getSearchReply(req, batchSize, res, binaryAttrs); 564 } 565 return res; 566 } 567 568 /* 569 * Abandon the search operation and remove it from the message queue. 570 */ 571 void clearSearchReply(LdapResult res, Control[] ctls) { 572 if (res != null && conn != null) { 573 574 // Only send an LDAP abandon operation when clearing the search 575 // reply from a one-level or subtree search. 576 LdapRequest req = conn.findRequest(res.msgId); 577 if (req == null) { 578 return; 579 } 580 581 // OK if req got removed after check; double removal attempt 582 // but otherwise no harm done 583 584 // Send an LDAP abandon only if the search operation has not yet 585 // completed. 586 if (req.hasSearchCompleted()) { 587 conn.removeRequest(req); 588 } else { 589 conn.abandonRequest(req, ctls); 590 } 591 } 592 } 593 594 /* 595 * Retrieve the next batch of entries and/or referrals. 596 */ 597 LdapResult getSearchReply(int batchSize, LdapResult res, 598 Hashtable<String, Boolean> binaryAttrs) throws IOException, NamingException { 599 600 ensureOpen(); 601 602 LdapRequest req; 603 604 if ((req = conn.findRequest(res.msgId)) == null) { 605 return null; 606 } 607 608 return getSearchReply(req, batchSize, res, binaryAttrs); 609 } 610 611 private LdapResult getSearchReply(LdapRequest req, 612 int batchSize, LdapResult res, Hashtable<String, Boolean> binaryAttrs) 613 throws IOException, NamingException { 614 615 if (batchSize == 0) 616 batchSize = Integer.MAX_VALUE; 617 618 if (res.entries != null) { 619 res.entries.setSize(0); // clear the (previous) set of entries 620 } else { 621 res.entries = 622 new Vector<>(batchSize == Integer.MAX_VALUE ? 32 : batchSize); 623 } 624 625 if (res.referrals != null) { 626 res.referrals.setSize(0); // clear the (previous) set of referrals 627 } 628 629 BerDecoder replyBer; // Decoder for response 630 int seq; // Request id 631 632 Attributes lattrs; // Attribute set read from response 633 Attribute la; // Attribute read from response 634 String DN; // DN read from response 635 LdapEntry le; // LDAP entry representing response 636 int[] seqlen; // Holder for response length 637 int endseq; // Position of end of response 638 639 for (int i = 0; i < batchSize;) { 640 replyBer = conn.readReply(req); 641 642 // 643 // process search reply 644 // 645 replyBer.parseSeq(null); // init seq 646 replyBer.parseInt(); // req id 647 seq = replyBer.parseSeq(null); 648 649 if (seq == LDAP_REP_SEARCH) { 650 651 // handle LDAPv3 search entries 652 lattrs = new BasicAttributes(caseIgnore); 653 DN = replyBer.parseString(isLdapv3); 654 le = new LdapEntry(DN, lattrs); 655 seqlen = new int[1]; 656 657 replyBer.parseSeq(seqlen); 658 endseq = replyBer.getParsePosition() + seqlen[0]; 659 while ((replyBer.getParsePosition() < endseq) && 660 (replyBer.bytesLeft() > 0)) { 661 la = parseAttribute(replyBer, binaryAttrs); 662 lattrs.put(la); 663 } 664 le.respCtls = isLdapv3 ? parseControls(replyBer) : null; 665 666 res.entries.addElement(le); 667 i++; 668 669 } else if ((seq == LDAP_REP_SEARCH_REF) && isLdapv3) { 670 671 // handle LDAPv3 search reference 672 Vector<String> URLs = new Vector<>(4); 673 674 // %%% Although not strictly correct, some LDAP servers 675 // encode the SEQUENCE OF tag in the SearchResultRef 676 if (replyBer.peekByte() == 677 (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR)) { 678 replyBer.parseSeq(null); 679 } 680 681 while ((replyBer.bytesLeft() > 0) && 682 (replyBer.peekByte() == Ber.ASN_OCTET_STR)) { 683 684 URLs.addElement(replyBer.parseString(isLdapv3)); 685 } 686 687 if (res.referrals == null) { 688 res.referrals = new Vector<>(4); 689 } 690 res.referrals.addElement(URLs); 691 res.resControls = isLdapv3 ? parseControls(replyBer) : null; 692 693 // Save referral and continue to get next search result 694 695 } else if (seq == LDAP_REP_EXTENSION) { 696 697 parseExtResponse(replyBer, res); //%%% ignore for now 698 699 } else if (seq == LDAP_REP_RESULT) { 700 701 parseResult(replyBer, res, isLdapv3); 702 res.resControls = isLdapv3 ? parseControls(replyBer) : null; 703 704 conn.removeRequest(req); 705 return res; // Done with search 706 } 707 } 708 709 return res; 710 } 711 712 private Attribute parseAttribute(BerDecoder ber, 713 Hashtable<String, Boolean> binaryAttrs) 714 throws IOException { 715 716 int len[] = new int[1]; 717 int seq = ber.parseSeq(null); 718 String attrid = ber.parseString(isLdapv3); 719 boolean hasBinaryValues = isBinaryValued(attrid, binaryAttrs); 720 Attribute la = new LdapAttribute(attrid); 721 722 if ((seq = ber.parseSeq(len)) == LBER_SET) { 723 int attrlen = len[0]; 724 while (ber.bytesLeft() > 0 && attrlen > 0) { 725 try { 726 attrlen -= parseAttributeValue(ber, la, hasBinaryValues); 727 } catch (IOException ex) { 728 ber.seek(attrlen); 729 break; 730 } 731 } 732 } else { 733 // Skip the rest of the sequence because it is not what we want 734 ber.seek(len[0]); 735 } 736 return la; 737 } 738 739 // 740 // returns number of bytes that were parsed. Adds the values to attr 741 // 742 private int parseAttributeValue(BerDecoder ber, Attribute la, 743 boolean hasBinaryValues) throws IOException { 744 745 int len[] = new int[1]; 746 747 if (hasBinaryValues) { 748 la.add(ber.parseOctetString(ber.peekByte(), len)); 749 } else { 750 la.add(ber.parseStringWithTag( 751 Ber.ASN_SIMPLE_STRING, isLdapv3, len)); 752 } 753 return len[0]; 754 } 755 756 private boolean isBinaryValued(String attrid, 757 Hashtable<String, Boolean> binaryAttrs) { 758 String id = attrid.toLowerCase(Locale.ENGLISH); 759 760 return ((id.indexOf(";binary") != -1) || 761 defaultBinaryAttrs.containsKey(id) || 762 ((binaryAttrs != null) && (binaryAttrs.containsKey(id)))); 763 } 764 765 // package entry point; used by Connection 766 static void parseResult(BerDecoder replyBer, LdapResult res, 767 boolean isLdapv3) throws IOException { 768 769 res.status = replyBer.parseEnumeration(); 770 res.matchedDN = replyBer.parseString(isLdapv3); 771 res.errorMessage = replyBer.parseString(isLdapv3); 772 773 // handle LDAPv3 referrals (if present) 774 if (isLdapv3 && 775 (replyBer.bytesLeft() > 0) && 776 (replyBer.peekByte() == LDAP_REP_REFERRAL)) { 777 778 Vector<String> URLs = new Vector<>(4); 779 int[] seqlen = new int[1]; 780 781 replyBer.parseSeq(seqlen); 782 int endseq = replyBer.getParsePosition() + seqlen[0]; 783 while ((replyBer.getParsePosition() < endseq) && 784 (replyBer.bytesLeft() > 0)) { 785 786 URLs.addElement(replyBer.parseString(isLdapv3)); 787 } 788 789 if (res.referrals == null) { 790 res.referrals = new Vector<>(4); 791 } 792 res.referrals.addElement(URLs); 793 } 794 } 795 796 // package entry point; used by Connection 797 static Vector<Control> parseControls(BerDecoder replyBer) throws IOException { 798 799 // handle LDAPv3 controls (if present) 800 if ((replyBer.bytesLeft() > 0) && (replyBer.peekByte() == LDAP_CONTROLS)) { 801 Vector<Control> ctls = new Vector<>(4); 802 String controlOID; 803 boolean criticality = false; // default 804 byte[] controlValue = null; // optional 805 int[] seqlen = new int[1]; 806 807 replyBer.parseSeq(seqlen); 808 int endseq = replyBer.getParsePosition() + seqlen[0]; 809 while ((replyBer.getParsePosition() < endseq) && 810 (replyBer.bytesLeft() > 0)) { 811 812 replyBer.parseSeq(null); 813 controlOID = replyBer.parseString(true); 814 815 if ((replyBer.bytesLeft() > 0) && 816 (replyBer.peekByte() == Ber.ASN_BOOLEAN)) { 817 criticality = replyBer.parseBoolean(); 818 } 819 if ((replyBer.bytesLeft() > 0) && 820 (replyBer.peekByte() == Ber.ASN_OCTET_STR)) { 821 controlValue = 822 replyBer.parseOctetString(Ber.ASN_OCTET_STR, null); 823 } 824 if (controlOID != null) { 825 ctls.addElement( 826 new BasicControl(controlOID, criticality, controlValue)); 827 } 828 } 829 return ctls; 830 } else { 831 return null; 832 } 833 } 834 835 private void parseExtResponse(BerDecoder replyBer, LdapResult res) 836 throws IOException { 837 838 parseResult(replyBer, res, isLdapv3); 839 840 if ((replyBer.bytesLeft() > 0) && 841 (replyBer.peekByte() == LDAP_REP_EXT_OID)) { 842 res.extensionId = 843 replyBer.parseStringWithTag(LDAP_REP_EXT_OID, isLdapv3, null); 844 } 845 if ((replyBer.bytesLeft() > 0) && 846 (replyBer.peekByte() == LDAP_REP_EXT_VAL)) { 847 res.extensionValue = 848 replyBer.parseOctetString(LDAP_REP_EXT_VAL, null); 849 } 850 851 res.resControls = parseControls(replyBer); 852 } 853 854 // 855 // Encode LDAPv3 controls 856 // 857 static void encodeControls(BerEncoder ber, Control[] reqCtls) 858 throws IOException { 859 860 if ((reqCtls == null) || (reqCtls.length == 0)) { 861 return; 862 } 863 864 byte[] controlVal; 865 866 ber.beginSeq(LdapClient.LDAP_CONTROLS); 867 868 for (int i = 0; i < reqCtls.length; i++) { 869 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 870 ber.encodeString(reqCtls[i].getID(), true); // control OID 871 if (reqCtls[i].isCritical()) { 872 ber.encodeBoolean(true); // critical control 873 } 874 if ((controlVal = reqCtls[i].getEncodedValue()) != null) { 875 ber.encodeOctetString(controlVal, Ber.ASN_OCTET_STR); 876 } 877 ber.endSeq(); 878 } 879 ber.endSeq(); 880 } 881 882 /** 883 * Reads the next reply corresponding to msgId, outstanding on requestBer. 884 * Processes the result and any controls. 885 */ 886 private LdapResult processReply(LdapRequest req, 887 LdapResult res, int responseType) throws IOException, NamingException { 888 889 BerDecoder rber = conn.readReply(req); 890 891 rber.parseSeq(null); // init seq 892 rber.parseInt(); // msg id 893 if (rber.parseByte() != responseType) { 894 return res; 895 } 896 897 rber.parseLength(); 898 parseResult(rber, res, isLdapv3); 899 res.resControls = isLdapv3 ? parseControls(rber) : null; 900 901 conn.removeRequest(req); 902 903 return res; // Done with operation 904 } 905 906 //////////////////////////////////////////////////////////////////////////// 907 // 908 // LDAP modify: 909 // Modify the DN dn with the operations on attributes attrs. 910 // ie, operations[0] is the operation to be performed on 911 // attrs[0]; 912 // dn - DN to modify 913 // operations - add, delete or replace 914 // attrs - array of Attribute 915 // reqCtls - array of request controls 916 // 917 //////////////////////////////////////////////////////////////////////////// 918 919 static final int ADD = 0; 920 static final int DELETE = 1; 921 static final int REPLACE = 2; 922 923 LdapResult modify(String dn, int operations[], Attribute attrs[], 924 Control[] reqCtls) 925 throws IOException, NamingException { 926 927 ensureOpen(); 928 929 LdapResult res = new LdapResult(); 930 res.status = LDAP_OPERATIONS_ERROR; 931 932 if (dn == null || operations.length != attrs.length) 933 return res; 934 935 BerEncoder ber = new BerEncoder(); 936 int curMsgId = conn.getMsgId(); 937 938 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 939 ber.encodeInt(curMsgId); 940 ber.beginSeq(LDAP_REQ_MODIFY); 941 ber.encodeString(dn, isLdapv3); 942 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 943 for (int i = 0; i < operations.length; i++) { 944 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 945 ber.encodeInt(operations[i], LBER_ENUMERATED); 946 947 // zero values is not permitted for the add op. 948 if ((operations[i] == ADD) && hasNoValue(attrs[i])) { 949 throw new InvalidAttributeValueException( 950 "'" + attrs[i].getID() + "' has no values."); 951 } else { 952 encodeAttribute(ber, attrs[i]); 953 } 954 ber.endSeq(); 955 } 956 ber.endSeq(); 957 ber.endSeq(); 958 if (isLdapv3) encodeControls(ber, reqCtls); 959 ber.endSeq(); 960 961 LdapRequest req = conn.writeRequest(ber, curMsgId); 962 963 return processReply(req, res, LDAP_REP_MODIFY); 964 } 965 966 private void encodeAttribute(BerEncoder ber, Attribute attr) 967 throws IOException, NamingException { 968 969 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 970 ber.encodeString(attr.getID(), isLdapv3); 971 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR | 1); 972 NamingEnumeration<?> enum_ = attr.getAll(); 973 Object val; 974 while (enum_.hasMore()) { 975 val = enum_.next(); 976 if (val instanceof String) { 977 ber.encodeString((String)val, isLdapv3); 978 } else if (val instanceof byte[]) { 979 ber.encodeOctetString((byte[])val, Ber.ASN_OCTET_STR); 980 } else if (val == null) { 981 // no attribute value 982 } else { 983 throw new InvalidAttributeValueException( 984 "Malformed '" + attr.getID() + "' attribute value"); 985 } 986 } 987 ber.endSeq(); 988 ber.endSeq(); 989 } 990 991 private static boolean hasNoValue(Attribute attr) throws NamingException { 992 return attr.size() == 0 || (attr.size() == 1 && attr.get() == null); 993 } 994 995 //////////////////////////////////////////////////////////////////////////// 996 // 997 // LDAP add 998 // Adds entry to the Directory 999 // 1000 //////////////////////////////////////////////////////////////////////////// 1001 1002 LdapResult add(LdapEntry entry, Control[] reqCtls) 1003 throws IOException, NamingException { 1004 1005 ensureOpen(); 1006 1007 LdapResult res = new LdapResult(); 1008 res.status = LDAP_OPERATIONS_ERROR; 1009 1010 if (entry == null || entry.DN == null) 1011 return res; 1012 1013 BerEncoder ber = new BerEncoder(); 1014 int curMsgId = conn.getMsgId(); 1015 Attribute attr; 1016 1017 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1018 ber.encodeInt(curMsgId); 1019 ber.beginSeq(LDAP_REQ_ADD); 1020 ber.encodeString(entry.DN, isLdapv3); 1021 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1022 NamingEnumeration<? extends Attribute> enum_ = 1023 entry.attributes.getAll(); 1024 while (enum_.hasMore()) { 1025 attr = enum_.next(); 1026 1027 // zero values is not permitted 1028 if (hasNoValue(attr)) { 1029 throw new InvalidAttributeValueException( 1030 "'" + attr.getID() + "' has no values."); 1031 } else { 1032 encodeAttribute(ber, attr); 1033 } 1034 } 1035 ber.endSeq(); 1036 ber.endSeq(); 1037 if (isLdapv3) encodeControls(ber, reqCtls); 1038 ber.endSeq(); 1039 1040 LdapRequest req = conn.writeRequest(ber, curMsgId); 1041 return processReply(req, res, LDAP_REP_ADD); 1042 } 1043 1044 //////////////////////////////////////////////////////////////////////////// 1045 // 1046 // LDAP delete 1047 // deletes entry from the Directory 1048 // 1049 //////////////////////////////////////////////////////////////////////////// 1050 1051 LdapResult delete(String DN, Control[] reqCtls) 1052 throws IOException, NamingException { 1053 1054 ensureOpen(); 1055 1056 LdapResult res = new LdapResult(); 1057 res.status = LDAP_OPERATIONS_ERROR; 1058 1059 if (DN == null) 1060 return res; 1061 1062 BerEncoder ber = new BerEncoder(); 1063 int curMsgId = conn.getMsgId(); 1064 1065 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1066 ber.encodeInt(curMsgId); 1067 ber.encodeString(DN, LDAP_REQ_DELETE, isLdapv3); 1068 if (isLdapv3) encodeControls(ber, reqCtls); 1069 ber.endSeq(); 1070 1071 LdapRequest req = conn.writeRequest(ber, curMsgId); 1072 1073 return processReply(req, res, LDAP_REP_DELETE); 1074 } 1075 1076 //////////////////////////////////////////////////////////////////////////// 1077 // 1078 // LDAP modrdn 1079 // Changes the last element of DN to newrdn 1080 // dn - DN to change 1081 // newrdn - new RDN to rename to 1082 // deleteoldrdn - boolean whether to delete old attrs or not 1083 // newSuperior - new place to put the entry in the tree 1084 // (ignored if server is LDAPv2) 1085 // reqCtls - array of request controls 1086 // 1087 //////////////////////////////////////////////////////////////////////////// 1088 1089 LdapResult moddn(String DN, String newrdn, boolean deleteOldRdn, 1090 String newSuperior, Control[] reqCtls) 1091 throws IOException, NamingException { 1092 1093 ensureOpen(); 1094 1095 boolean changeSuperior = (newSuperior != null && 1096 newSuperior.length() > 0); 1097 1098 LdapResult res = new LdapResult(); 1099 res.status = LDAP_OPERATIONS_ERROR; 1100 1101 if (DN == null || newrdn == null) 1102 return res; 1103 1104 BerEncoder ber = new BerEncoder(); 1105 int curMsgId = conn.getMsgId(); 1106 1107 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1108 ber.encodeInt(curMsgId); 1109 ber.beginSeq(LDAP_REQ_MODRDN); 1110 ber.encodeString(DN, isLdapv3); 1111 ber.encodeString(newrdn, isLdapv3); 1112 ber.encodeBoolean(deleteOldRdn); 1113 if(isLdapv3 && changeSuperior) { 1114 //System.err.println("changin superior"); 1115 ber.encodeString(newSuperior, LDAP_SUPERIOR_DN, isLdapv3); 1116 } 1117 ber.endSeq(); 1118 if (isLdapv3) encodeControls(ber, reqCtls); 1119 ber.endSeq(); 1120 1121 1122 LdapRequest req = conn.writeRequest(ber, curMsgId); 1123 1124 return processReply(req, res, LDAP_REP_MODRDN); 1125 } 1126 1127 //////////////////////////////////////////////////////////////////////////// 1128 // 1129 // LDAP compare 1130 // Compare attribute->value pairs in dn 1131 // 1132 //////////////////////////////////////////////////////////////////////////// 1133 1134 LdapResult compare(String DN, String type, String value, Control[] reqCtls) 1135 throws IOException, NamingException { 1136 1137 ensureOpen(); 1138 1139 LdapResult res = new LdapResult(); 1140 res.status = LDAP_OPERATIONS_ERROR; 1141 1142 if (DN == null || type == null || value == null) 1143 return res; 1144 1145 BerEncoder ber = new BerEncoder(); 1146 int curMsgId = conn.getMsgId(); 1147 1148 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1149 ber.encodeInt(curMsgId); 1150 ber.beginSeq(LDAP_REQ_COMPARE); 1151 ber.encodeString(DN, isLdapv3); 1152 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1153 ber.encodeString(type, isLdapv3); 1154 1155 // replace any escaped characters in the value 1156 byte[] val = isLdapv3 ? 1157 value.getBytes("UTF8") : value.getBytes("8859_1"); 1158 ber.encodeOctetString( 1159 Filter.unescapeFilterValue(val, 0, val.length), 1160 Ber.ASN_OCTET_STR); 1161 1162 ber.endSeq(); 1163 ber.endSeq(); 1164 if (isLdapv3) encodeControls(ber, reqCtls); 1165 ber.endSeq(); 1166 1167 LdapRequest req = conn.writeRequest(ber, curMsgId); 1168 1169 return processReply(req, res, LDAP_REP_COMPARE); 1170 } 1171 1172 //////////////////////////////////////////////////////////////////////////// 1173 // 1174 // LDAP extended operation 1175 // 1176 //////////////////////////////////////////////////////////////////////////// 1177 1178 LdapResult extendedOp(String id, byte[] request, Control[] reqCtls, 1179 boolean pauseAfterReceipt) throws IOException, NamingException { 1180 1181 ensureOpen(); 1182 1183 LdapResult res = new LdapResult(); 1184 res.status = LDAP_OPERATIONS_ERROR; 1185 1186 if (id == null) 1187 return res; 1188 1189 BerEncoder ber = new BerEncoder(); 1190 int curMsgId = conn.getMsgId(); 1191 1192 ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); 1193 ber.encodeInt(curMsgId); 1194 ber.beginSeq(LDAP_REQ_EXTENSION); 1195 ber.encodeString(id, 1196 Ber.ASN_CONTEXT | 0, isLdapv3);//[0] 1197 if (request != null) { 1198 ber.encodeOctetString(request, 1199 Ber.ASN_CONTEXT | 1);//[1] 1200 } 1201 ber.endSeq(); 1202 encodeControls(ber, reqCtls); // always v3 1203 ber.endSeq(); 1204 1205 LdapRequest req = conn.writeRequest(ber, curMsgId, pauseAfterReceipt); 1206 1207 BerDecoder rber = conn.readReply(req); 1208 1209 rber.parseSeq(null); // init seq 1210 rber.parseInt(); // msg id 1211 if (rber.parseByte() != LDAP_REP_EXTENSION) { 1212 return res; 1213 } 1214 1215 rber.parseLength(); 1216 parseExtResponse(rber, res); 1217 conn.removeRequest(req); 1218 1219 return res; // Done with operation 1220 } 1221 1222 1223 1224 //////////////////////////////////////////////////////////////////////////// 1225 // 1226 // Some BER definitions convenient for LDAP 1227 // 1228 //////////////////////////////////////////////////////////////////////////// 1229 1230 static final int LDAP_VERSION3_VERSION2 = 32; 1231 static final int LDAP_VERSION2 = 0x02; 1232 static final int LDAP_VERSION3 = 0x03; // LDAPv3 1233 static final int LDAP_VERSION = LDAP_VERSION3; 1234 1235 static final int LDAP_REF_FOLLOW = 0x01; // follow referrals 1236 static final int LDAP_REF_THROW = 0x02; // throw referral ex. 1237 static final int LDAP_REF_IGNORE = 0x03; // ignore referrals 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 synchronized (unsolicited) { 1503 if (unsolicited.size() == 0) { 1504 return; 1505 } 1506 unsolicited.removeElement(ctx); 1507 } 1508 } 1509 1510 // NOTE: Cannot be synchronized because this is called asynchronously 1511 // by the reader thread in Connection. Instead, sync on 'unsolicited' Vector. 1512 void processUnsolicited(BerDecoder ber) { 1513 if (debug > 0) { 1514 System.err.println("LdapClient.processUnsolicited"); 1515 } 1516 synchronized (unsolicited) { 1517 try { 1518 // Parse the response 1519 LdapResult res = new LdapResult(); 1520 1521 ber.parseSeq(null); // init seq 1522 ber.parseInt(); // msg id; should be 0; ignored 1523 if (ber.parseByte() != LDAP_REP_EXTENSION) { 1524 throw new IOException( 1525 "Unsolicited Notification must be an Extended Response"); 1526 } 1527 ber.parseLength(); 1528 parseExtResponse(ber, res); 1529 1530 if (DISCONNECT_OID.equals(res.extensionId)) { 1531 // force closing of connection 1532 forceClose(pooled); 1533 } 1534 1535 if (unsolicited.size() > 0) { 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 UnsolicitedNotification 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 unsolicited.elementAt(0).convertControls(res.resControls) : 1548 null); 1549 1550 // Fire UnsolicitedNotification events to listeners 1551 notifyUnsolicited(notice); 1552 1553 // If "disconnect" notification, 1554 // notify unsolicited listeners via NamingException 1555 if (DISCONNECT_OID.equals(res.extensionId)) { 1556 notifyUnsolicited( 1557 new CommunicationException("Connection closed")); 1558 } 1559 } 1560 } catch (IOException e) { 1561 if (unsolicited.size() == 0) 1562 return; // no one registered; ignore 1563 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 1577 private void notifyUnsolicited(Object e) { 1578 for (int i = 0; i < unsolicited.size(); i++) { 1579 unsolicited.elementAt(i).fireUnsolicited(e); 1580 } 1581 if (e instanceof NamingException) { 1582 unsolicited.setSize(0); // no more listeners after exception 1583 } 1584 } 1585 1586 private void ensureOpen() throws IOException { 1587 if (conn == null || !conn.useable) { 1588 if (conn != null && conn.closureReason != null) { 1589 throw conn.closureReason; 1590 } else { 1591 throw new IOException("connection closed"); 1592 } 1593 } 1594 } 1595 1596 // package private (used by LdapCtx) 1597 static LdapClient getInstance(boolean usePool, String hostname, int port, 1598 String factory, int connectTimeout, int readTimeout, OutputStream trace, 1599 int version, String authMechanism, Control[] ctls, String protocol, 1600 String user, Object passwd, Hashtable<?,?> env) throws NamingException { 1601 1602 if (usePool) { 1603 if (LdapPoolManager.isPoolingAllowed(factory, trace, 1604 authMechanism, protocol, env)) { 1605 LdapClient answer = LdapPoolManager.getLdapClient( 1606 hostname, port, factory, connectTimeout, readTimeout, 1607 trace, version, authMechanism, ctls, protocol, user, 1608 passwd, env); 1609 answer.referenceCount = 1; // always one when starting out 1610 return answer; 1611 } 1612 } 1613 return new LdapClient(hostname, port, factory, connectTimeout, 1614 readTimeout, trace, null); 1615 } 1616 }