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