0001: /*
0002: * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package com.sun.jndi.ldap;
0027:
0028: import java.net.*;
0029: import java.io.*;
0030: import java.util.Vector;
0031: import java.util.Hashtable;
0032:
0033: import javax.naming.*;
0034: import javax.naming.directory.*;
0035: import javax.naming.ldap.*;
0036:
0037: import com.sun.jndi.ldap.pool.PooledConnection;
0038: import com.sun.jndi.ldap.pool.PoolCallback;
0039: import com.sun.jndi.ldap.sasl.LdapSasl;
0040: import com.sun.jndi.ldap.sasl.SaslInputStream;
0041:
0042: /**
0043: * LDAP (RFC-1777) and LDAPv3 (RFC-2251) compliant client
0044: *
0045: * This class represents a connection to an LDAP client.
0046: * Callers interact with this class at an LDAP operation level.
0047: * That is, the caller invokes a method to do a SEARCH or MODRDN
0048: * operation and gets back the result.
0049: * The caller uses the constructor to create a connection to the server.
0050: * It then needs to use authenticate() to perform an LDAP BIND.
0051: * Note that for v3, BIND is optional so authenticate() might not
0052: * actually send a BIND. authenticate() can be used later on to issue
0053: * a BIND, for example, for a v3 client that wants to change the connection's
0054: * credentials.
0055: *<p>
0056: * Multiple LdapCtx might share the same LdapClient. For example, contexts
0057: * derived from the same initial context would share the same LdapClient
0058: * until changes to a context's properties necessitates its own LdapClient.
0059: * LdapClient methods that access shared data are thread-safe (i.e., caller
0060: * does not have to sync).
0061: *<p>
0062: * Fields:
0063: * isLdapv3 - no sync; initialized and updated within sync authenticate();
0064: * always updated when connection is "quiet" and not shared;
0065: * read access from outside LdapClient not sync
0066: * referenceCount - sync within LdapClient; exception is forceClose() which
0067: * is used by Connection thread to close connection upon receiving
0068: * an Unsolicited Notification.
0069: * access from outside LdapClient must sync;
0070: * conn - no sync; Connection takes care of its own sync
0071: * unsolicited - sync Vector; multiple operations sync'ed
0072: *
0073: * @author Vincent Ryan
0074: * @author Jagane Sundar
0075: * @author Rosanna Lee
0076: */
0077:
0078: public final class LdapClient implements PooledConnection {
0079: // ---------------------- Constants ----------------------------------
0080: private static final int debug = 0;
0081: static final boolean caseIgnore = true;
0082:
0083: // Default list of binary attributes
0084: private static final Hashtable defaultBinaryAttrs = new Hashtable(
0085: 23, 0.75f);
0086: static {
0087: defaultBinaryAttrs.put("userpassword", Boolean.TRUE); //2.5.4.35
0088: defaultBinaryAttrs.put("javaserializeddata", Boolean.TRUE);
0089: //1.3.6.1.4.1.42.2.27.4.1.8
0090: defaultBinaryAttrs.put("javaserializedobject", Boolean.TRUE);
0091: // 1.3.6.1.4.1.42.2.27.4.1.2
0092: defaultBinaryAttrs.put("jpegphoto", Boolean.TRUE);
0093: //0.9.2342.19200300.100.1.60
0094: defaultBinaryAttrs.put("audio", Boolean.TRUE); //0.9.2342.19200300.100.1.55
0095: defaultBinaryAttrs.put("thumbnailphoto", Boolean.TRUE);
0096: //1.3.6.1.4.1.1466.101.120.35
0097: defaultBinaryAttrs.put("thumbnaillogo", Boolean.TRUE);
0098: //1.3.6.1.4.1.1466.101.120.36
0099: defaultBinaryAttrs.put("usercertificate", Boolean.TRUE); //2.5.4.36
0100: defaultBinaryAttrs.put("cacertificate", Boolean.TRUE); //2.5.4.37
0101: defaultBinaryAttrs.put("certificaterevocationlist",
0102: Boolean.TRUE);
0103: //2.5.4.39
0104: defaultBinaryAttrs.put("authorityrevocationlist", Boolean.TRUE); //2.5.4.38
0105: defaultBinaryAttrs.put("crosscertificatepair", Boolean.TRUE); //2.5.4.40
0106: defaultBinaryAttrs.put("photo", Boolean.TRUE); //0.9.2342.19200300.100.1.7
0107: defaultBinaryAttrs.put("personalsignature", Boolean.TRUE);
0108: //0.9.2342.19200300.100.1.53
0109: defaultBinaryAttrs.put("x500uniqueidentifier", Boolean.TRUE); //2.5.4.45
0110: }
0111:
0112: private static final String DISCONNECT_OID = "1.3.6.1.4.1.1466.20036";
0113:
0114: // ----------------------- instance fields ------------------------
0115: boolean isLdapv3; // Used by LdapCtx
0116: int referenceCount = 1; // Used by LdapCtx for check for sharing
0117:
0118: Connection conn; // Connection to server; has reader thread
0119: // used by LdapCtx for StartTLS
0120:
0121: final private PoolCallback pcb;
0122: final private boolean pooled;
0123: private boolean authenticateCalled = false;
0124:
0125: ////////////////////////////////////////////////////////////////////////////
0126: //
0127: // constructor: Create an authenticated connection to server
0128: //
0129: ////////////////////////////////////////////////////////////////////////////
0130:
0131: LdapClient(String host, int port, String socketFactory,
0132: int connectTimeout, int readTimeout, OutputStream trace,
0133: PoolCallback pcb) throws NamingException {
0134:
0135: if (debug > 0)
0136: System.err.println("LdapClient: constructor called " + host
0137: + ":" + port);
0138: conn = new Connection(this , host, port, socketFactory,
0139: connectTimeout, readTimeout, trace);
0140:
0141: this .pcb = pcb;
0142: pooled = (pcb != null);
0143: }
0144:
0145: synchronized boolean authenticateCalled() {
0146: return authenticateCalled;
0147: }
0148:
0149: synchronized LdapResult authenticate(boolean initial, String name,
0150: Object pw, int version, String authMechanism,
0151: Control[] ctls, Hashtable env) throws NamingException {
0152:
0153: authenticateCalled = true;
0154:
0155: try {
0156: ensureOpen();
0157: } catch (IOException e) {
0158: NamingException ne = new CommunicationException();
0159: ne.setRootCause(e);
0160: throw ne;
0161: }
0162:
0163: switch (version) {
0164: case LDAP_VERSION3_VERSION2:
0165: case LDAP_VERSION3:
0166: isLdapv3 = true;
0167: break;
0168: case LDAP_VERSION2:
0169: isLdapv3 = false;
0170: break;
0171: default:
0172: throw new CommunicationException("Protocol version "
0173: + version + " not supported");
0174: }
0175:
0176: LdapResult res = null;
0177:
0178: if (authMechanism.equalsIgnoreCase("none")
0179: || authMechanism.equalsIgnoreCase("anonymous")) {
0180:
0181: // Perform LDAP bind if we are reauthenticating, using LDAPv2,
0182: // supporting failover to LDAPv2, or controls have been supplied.
0183: if (!initial || (version == LDAP_VERSION2)
0184: || (version == LDAP_VERSION3_VERSION2)
0185: || ((ctls != null) && (ctls.length > 0))) {
0186: try {
0187: // anonymous bind; update name/pw for LDAPv2 retry
0188: res = ldapBind(name = null, (byte[]) (pw = null),
0189: ctls, null, false);
0190: if (res.status == LdapClient.LDAP_SUCCESS) {
0191: conn.setBound();
0192: }
0193: } catch (IOException e) {
0194: NamingException ne = new CommunicationException(
0195: "anonymous bind failed: " + conn.host + ":"
0196: + conn.port);
0197: ne.setRootCause(e);
0198: throw ne;
0199: }
0200: } else {
0201: // Skip LDAP bind for LDAPv3 anonymous bind
0202: res = new LdapResult();
0203: res.status = LdapClient.LDAP_SUCCESS;
0204: }
0205: } else if (authMechanism.equalsIgnoreCase("simple")) {
0206: // simple authentication
0207: byte[] encodedPw = null;
0208: try {
0209: encodedPw = encodePassword(pw, isLdapv3);
0210: res = ldapBind(name, encodedPw, ctls, null, false);
0211: if (res.status == LdapClient.LDAP_SUCCESS) {
0212: conn.setBound();
0213: }
0214: } catch (IOException e) {
0215: NamingException ne = new CommunicationException(
0216: "simple bind failed: " + conn.host + ":"
0217: + conn.port);
0218: ne.setRootCause(e);
0219: throw ne;
0220: } finally {
0221: // If pw was copied to a new array, clear that array as
0222: // a security precaution.
0223: if (encodedPw != pw && encodedPw != null) {
0224: for (int i = 0; i < encodedPw.length; i++) {
0225: encodedPw[i] = 0;
0226: }
0227: }
0228: }
0229: } else if (isLdapv3) {
0230: // SASL authentication
0231: try {
0232: res = LdapSasl.saslBind(this , conn, conn.host, name,
0233: pw, authMechanism, env, ctls);
0234: if (res.status == LdapClient.LDAP_SUCCESS) {
0235: conn.setBound();
0236: }
0237: } catch (IOException e) {
0238: NamingException ne = new CommunicationException(
0239: "SASL bind failed: " + conn.host + ":"
0240: + conn.port);
0241: ne.setRootCause(e);
0242: throw ne;
0243: }
0244: } else {
0245: throw new AuthenticationNotSupportedException(authMechanism);
0246: }
0247:
0248: //
0249: // re-try login using v2 if failing over
0250: //
0251: if (initial
0252: && (res.status == LdapClient.LDAP_PROTOCOL_ERROR)
0253: && (version == LdapClient.LDAP_VERSION3_VERSION2)
0254: && (authMechanism.equalsIgnoreCase("none")
0255: || authMechanism.equalsIgnoreCase("anonymous") || authMechanism
0256: .equalsIgnoreCase("simple"))) {
0257:
0258: byte[] encodedPw = null;
0259: try {
0260: isLdapv3 = false;
0261: encodedPw = encodePassword(pw, false);
0262: res = ldapBind(name, encodedPw, ctls, null, false);
0263: if (res.status == LdapClient.LDAP_SUCCESS) {
0264: conn.setBound();
0265: }
0266: } catch (IOException e) {
0267: NamingException ne = new CommunicationException(
0268: authMechanism + ":" + conn.host + ":"
0269: + conn.port);
0270: ne.setRootCause(e);
0271: throw ne;
0272: } finally {
0273: // If pw was copied to a new array, clear that array as
0274: // a security precaution.
0275: if (encodedPw != pw && encodedPw != null) {
0276: for (int i = 0; i < encodedPw.length; i++) {
0277: encodedPw[i] = 0;
0278: }
0279: }
0280: }
0281: }
0282:
0283: // principal name not found
0284: // (map NameNotFoundException to AuthenticationException)
0285: // %%% This is a workaround for Netscape servers returning
0286: // %%% no such object when the principal name is not found
0287: // %%% Note that when this workaround is applied, it does not allow
0288: // %%% response controls to be recorded by the calling context
0289: if (res.status == LdapClient.LDAP_NO_SUCH_OBJECT) {
0290: throw new AuthenticationException(getErrorMessage(
0291: res.status, res.errorMessage));
0292: }
0293: conn.setV3(isLdapv3);
0294: return res;
0295: }
0296:
0297: /**
0298: * Sends an LDAP Bind request.
0299: * Cannot be private; called by LdapSasl
0300: * @param dn The possibly null DN to use in the BIND request. null if anonymous.
0301: * @param toServer The possibly null array of bytes to send to the server.
0302: * @param auth The authentication mechanism
0303: *
0304: */
0305: synchronized public LdapResult ldapBind(String dn, byte[] toServer,
0306: Control[] bindCtls, String auth, boolean pauseAfterReceipt)
0307: throws java.io.IOException, NamingException {
0308:
0309: ensureOpen();
0310:
0311: // flush outstanding requests
0312: conn.abandonOutstandingReqs(null);
0313:
0314: BerEncoder ber = new BerEncoder();
0315: int curMsgId = conn.getMsgId();
0316: LdapResult res = new LdapResult();
0317: res.status = LDAP_OPERATIONS_ERROR;
0318:
0319: //
0320: // build the bind request.
0321: //
0322: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
0323: ber.encodeInt(curMsgId);
0324: ber.beginSeq(LdapClient.LDAP_REQ_BIND);
0325: ber.encodeInt(isLdapv3 ? LDAP_VERSION3 : LDAP_VERSION2);
0326: ber.encodeString(dn, isLdapv3);
0327:
0328: // if authentication mechanism specified, it is SASL
0329: if (auth != null) {
0330: ber.beginSeq(Ber.ASN_CONTEXT | Ber.ASN_CONSTRUCTOR | 3);
0331: ber.encodeString(auth, isLdapv3); // SASL mechanism
0332: if (toServer != null) {
0333: ber.encodeOctetString(toServer, Ber.ASN_OCTET_STR);
0334: }
0335: ber.endSeq();
0336: } else {
0337: if (toServer != null) {
0338: ber.encodeOctetString(toServer, Ber.ASN_CONTEXT);
0339: } else {
0340: ber.encodeOctetString(null, Ber.ASN_CONTEXT, 0, 0);
0341: }
0342: }
0343: ber.endSeq();
0344:
0345: // Encode controls
0346: if (isLdapv3) {
0347: encodeControls(ber, bindCtls);
0348: }
0349: ber.endSeq();
0350:
0351: LdapRequest req = conn.writeRequest(ber, curMsgId,
0352: pauseAfterReceipt);
0353: if (toServer != null) {
0354: ber.reset(); // clear internally-stored password
0355: }
0356:
0357: // Read reply
0358: BerDecoder rber = conn.readReply(req);
0359:
0360: rber.parseSeq(null); // init seq
0361: rber.parseInt(); // msg id
0362: if (rber.parseByte() != LDAP_REP_BIND) {
0363: return res;
0364: }
0365:
0366: rber.parseLength();
0367: parseResult(rber, res, isLdapv3);
0368:
0369: // handle server's credentials (if present)
0370: if (isLdapv3 && (rber.bytesLeft() > 0)
0371: && (rber.peekByte() == (Ber.ASN_CONTEXT | 7))) {
0372: res.serverCreds = rber.parseOctetString(
0373: (Ber.ASN_CONTEXT | 7), null);
0374: }
0375:
0376: res.resControls = isLdapv3 ? parseControls(rber) : null;
0377:
0378: conn.removeRequest(req);
0379: return res;
0380: }
0381:
0382: /**
0383: * Determines whether SASL encryption/integrity is in progress.
0384: * This check is made prior to reauthentication. You cannot reauthenticate
0385: * over an encrypted/integrity-protected SASL channel. You must
0386: * close the channel and open a new one.
0387: */
0388: boolean usingSaslStreams() {
0389: return (conn.inStream instanceof SaslInputStream);
0390: }
0391:
0392: synchronized void incRefCount() {
0393: ++referenceCount;
0394: if (debug > 1) {
0395: System.err.println("LdapClient.incRefCount: "
0396: + referenceCount + " " + this );
0397: }
0398:
0399: }
0400:
0401: /**
0402: * Returns the encoded password.
0403: */
0404: private static byte[] encodePassword(Object pw, boolean v3)
0405: throws IOException {
0406:
0407: if (pw instanceof char[]) {
0408: pw = new String((char[]) pw);
0409: }
0410:
0411: if (pw instanceof String) {
0412: if (v3) {
0413: return ((String) pw).getBytes("UTF8");
0414: } else {
0415: return ((String) pw).getBytes("8859_1");
0416: }
0417: } else {
0418: return (byte[]) pw;
0419: }
0420: }
0421:
0422: synchronized void close(Control[] reqCtls, boolean hardClose) {
0423: --referenceCount;
0424:
0425: if (debug > 1) {
0426: System.err.println("LdapClient: " + this );
0427: System.err.println("LdapClient: close() called: "
0428: + referenceCount);
0429: (new Throwable()).printStackTrace();
0430: }
0431:
0432: if (referenceCount <= 0 && conn != null) {
0433: if (debug > 0)
0434: System.err.println("LdapClient: closed connection "
0435: + this );
0436: if (!pooled) {
0437: // Not being pooled; continue with closing
0438: conn.cleanup(reqCtls, false);
0439: conn = null;
0440: } else {
0441: // Pooled
0442:
0443: // Is this a real close or a request to return conn to pool
0444: if (hardClose) {
0445: conn.cleanup(reqCtls, false);
0446: conn = null;
0447: pcb.removePooledConnection(this );
0448: } else {
0449: pcb.releasePooledConnection(this );
0450: }
0451: }
0452: }
0453: }
0454:
0455: // NOTE: Should NOT be synchronized otherwise won't be able to close
0456: private void forceClose(boolean cleanPool) {
0457: referenceCount = 0; // force closing of connection
0458:
0459: if (debug > 1) {
0460: System.err.println("LdapClient: forceClose() of " + this );
0461: }
0462:
0463: if (conn != null) {
0464: if (debug > 0)
0465: System.err
0466: .println("LdapClient: forced close of connection "
0467: + this );
0468: conn.cleanup(null, false);
0469: conn = null;
0470:
0471: if (cleanPool) {
0472: pcb.removePooledConnection(this );
0473: }
0474: }
0475: }
0476:
0477: protected void finalize() {
0478: if (debug > 0)
0479: System.err.println("LdapClient: finalize " + this );
0480: forceClose(pooled);
0481: }
0482:
0483: /*
0484: * Used by connection pooling to close physical connection.
0485: */
0486: synchronized public void closeConnection() {
0487: forceClose(false); // this is a pool callback so no need to clean pool
0488: }
0489:
0490: /**
0491: * Called by Connection.cleanup(). LdapClient should
0492: * notify any unsolicited listeners and removing itself from any pool.
0493: * This is almost like forceClose(), except it doesn't call
0494: * Connection.cleanup() (because this is called from cleanup()).
0495: */
0496: void processConnectionClosure() {
0497: // Notify listeners
0498: if (unsolicited.size() > 0) {
0499: String msg;
0500: if (conn != null) {
0501: msg = conn.host + ":" + conn.port
0502: + " connection closed";
0503: } else {
0504: msg = "Connection closed";
0505: }
0506: notifyUnsolicited(new CommunicationException(msg));
0507: }
0508:
0509: // Remove from pool
0510: if (pooled) {
0511: pcb.removePooledConnection(this );
0512: }
0513: }
0514:
0515: ////////////////////////////////////////////////////////////////////////////
0516: //
0517: // LDAP search. also includes methods to encode rfc 1558 compliant filters
0518: //
0519: ////////////////////////////////////////////////////////////////////////////
0520:
0521: static final int SCOPE_BASE_OBJECT = 0;
0522: static final int SCOPE_ONE_LEVEL = 1;
0523: static final int SCOPE_SUBTREE = 2;
0524:
0525: LdapResult search(String dn, int scope, int deref, int sizeLimit,
0526: int timeLimit, boolean attrsOnly, String attrs[],
0527: String filter, int batchSize, Control[] reqCtls,
0528: Hashtable binaryAttrs, boolean waitFirstReply)
0529: throws IOException, NamingException {
0530:
0531: ensureOpen();
0532:
0533: LdapResult res = new LdapResult();
0534:
0535: BerEncoder ber = new BerEncoder();
0536: int curMsgId = conn.getMsgId();
0537:
0538: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
0539: ber.encodeInt(curMsgId);
0540: ber.beginSeq(LDAP_REQ_SEARCH);
0541: ber.encodeString(dn == null ? "" : dn, isLdapv3);
0542: ber.encodeInt(scope, LBER_ENUMERATED);
0543: ber.encodeInt(deref, LBER_ENUMERATED);
0544: ber.encodeInt(sizeLimit);
0545: ber.encodeInt(timeLimit);
0546: ber.encodeBoolean(attrsOnly);
0547: Filter.encodeFilterString(ber, filter, isLdapv3);
0548: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
0549: ber.encodeStringArray(attrs, isLdapv3);
0550: ber.endSeq();
0551: ber.endSeq();
0552: if (isLdapv3)
0553: encodeControls(ber, reqCtls);
0554: ber.endSeq();
0555:
0556: LdapRequest req = conn.writeRequest(ber, curMsgId);
0557:
0558: res.msgId = curMsgId;
0559: res.status = LdapClient.LDAP_SUCCESS; //optimistic
0560: if (waitFirstReply) {
0561: // get first reply
0562: res = getSearchReply(req, batchSize, res, binaryAttrs);
0563: }
0564: return res;
0565: }
0566:
0567: /*
0568: * Abandon the search operation and remove it from the message queue.
0569: */
0570: void clearSearchReply(LdapResult res, Control[] ctls) {
0571: if (res != null && conn != null) {
0572:
0573: // Only send an LDAP abandon operation when clearing the search
0574: // reply from a one-level or subtree search.
0575: LdapRequest req = conn.findRequest(res.msgId);
0576: if (req == null) {
0577: return;
0578: }
0579:
0580: // OK if req got removed after check; double removal attempt
0581: // but otherwise no harm done
0582:
0583: // Send an LDAP abandon only if the search operation has not yet
0584: // completed.
0585: if (req.hasSearchCompleted()) {
0586: conn.removeRequest(req);
0587: } else {
0588: conn.abandonRequest(req, ctls);
0589: }
0590: }
0591: }
0592:
0593: /*
0594: * Retrieve the next batch of entries and/or referrals.
0595: */
0596: LdapResult getSearchReply(int batchSize, LdapResult res,
0597: Hashtable binaryAttrs) throws IOException, NamingException {
0598:
0599: ensureOpen();
0600:
0601: LdapRequest req;
0602:
0603: if ((req = conn.findRequest(res.msgId)) == null) {
0604: return null;
0605: }
0606:
0607: return getSearchReply(req, batchSize, res, binaryAttrs);
0608: }
0609:
0610: private LdapResult getSearchReply(LdapRequest req, int batchSize,
0611: LdapResult res, Hashtable binaryAttrs) throws IOException,
0612: NamingException {
0613:
0614: if (batchSize == 0)
0615: batchSize = Integer.MAX_VALUE;
0616:
0617: if (res.entries != null) {
0618: res.entries.setSize(0); // clear the (previous) set of entries
0619: } else {
0620: res.entries = new Vector(
0621: batchSize == Integer.MAX_VALUE ? 32 : batchSize);
0622: }
0623:
0624: if (res.referrals != null) {
0625: res.referrals.setSize(0); // clear the (previous) set of referrals
0626: }
0627:
0628: BerDecoder replyBer; // Decoder for response
0629: int seq; // Request id
0630:
0631: Attributes lattrs; // Attribute set read from response
0632: Attribute la; // Attribute read from response
0633: String DN; // DN read from response
0634: LdapEntry le; // LDAP entry representing response
0635: int[] seqlen; // Holder for response length
0636: int endseq; // Position of end of response
0637:
0638: for (int i = 0; i < batchSize;) {
0639: replyBer = conn.readReply(req);
0640:
0641: //
0642: // process search reply
0643: //
0644: replyBer.parseSeq(null); // init seq
0645: replyBer.parseInt(); // req id
0646: seq = replyBer.parseSeq(null);
0647:
0648: if (seq == LDAP_REP_SEARCH) {
0649:
0650: // handle LDAPv3 search entries
0651: lattrs = new BasicAttributes(caseIgnore);
0652: DN = replyBer.parseString(isLdapv3);
0653: le = new LdapEntry(DN, lattrs);
0654: seqlen = new int[1];
0655:
0656: replyBer.parseSeq(seqlen);
0657: endseq = replyBer.getParsePosition() + seqlen[0];
0658: while ((replyBer.getParsePosition() < endseq)
0659: && (replyBer.bytesLeft() > 0)) {
0660: la = parseAttribute(replyBer, binaryAttrs);
0661: lattrs.put(la);
0662: }
0663: le.respCtls = isLdapv3 ? parseControls(replyBer) : null;
0664:
0665: res.entries.addElement(le);
0666: i++;
0667:
0668: } else if ((seq == LDAP_REP_SEARCH_REF) && isLdapv3) {
0669:
0670: // handle LDAPv3 search reference
0671: Vector URLs = new Vector(4);
0672:
0673: // %%% Although not strictly correct, some LDAP servers
0674: // encode the SEQUENCE OF tag in the SearchResultRef
0675: if (replyBer.peekByte() == (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR)) {
0676: replyBer.parseSeq(null);
0677: }
0678:
0679: while ((replyBer.bytesLeft() > 0)
0680: && (replyBer.peekByte() == Ber.ASN_OCTET_STR)) {
0681:
0682: URLs.addElement(replyBer.parseString(isLdapv3));
0683: }
0684:
0685: if (res.referrals == null) {
0686: res.referrals = new Vector(4);
0687: }
0688: res.referrals.addElement(URLs);
0689: res.resControls = isLdapv3 ? parseControls(replyBer)
0690: : null;
0691:
0692: // Save referral and continue to get next search result
0693:
0694: } else if (seq == LDAP_REP_EXTENSION) {
0695:
0696: parseExtResponse(replyBer, res); //%%% ignore for now
0697:
0698: } else if (seq == LDAP_REP_RESULT) {
0699:
0700: parseResult(replyBer, res, isLdapv3);
0701: res.resControls = isLdapv3 ? parseControls(replyBer)
0702: : null;
0703:
0704: conn.removeRequest(req);
0705: return res; // Done with search
0706: }
0707: }
0708:
0709: return res;
0710: }
0711:
0712: private Attribute parseAttribute(BerDecoder ber,
0713: Hashtable binaryAttrs) throws IOException {
0714:
0715: int len[] = new int[1];
0716: int seq = ber.parseSeq(null);
0717: String attrid = ber.parseString(isLdapv3);
0718: boolean hasBinaryValues = isBinaryValued(attrid, binaryAttrs);
0719: Attribute la = new LdapAttribute(attrid);
0720:
0721: if ((seq = ber.parseSeq(len)) == LBER_SET) {
0722: int attrlen = len[0];
0723: while (ber.bytesLeft() > 0 && attrlen > 0) {
0724: try {
0725: attrlen -= parseAttributeValue(ber, la,
0726: hasBinaryValues);
0727: } catch (IOException ex) {
0728: ber.seek(attrlen);
0729: break;
0730: }
0731: }
0732: } else {
0733: // Skip the rest of the sequence because it is not what we want
0734: ber.seek(len[0]);
0735: }
0736: return la;
0737: }
0738:
0739: //
0740: // returns number of bytes that were parsed. Adds the values to attr
0741: //
0742: private int parseAttributeValue(BerDecoder ber, Attribute la,
0743: boolean hasBinaryValues) throws IOException {
0744:
0745: int len[] = new int[1];
0746:
0747: if (hasBinaryValues) {
0748: la.add(ber.parseOctetString(ber.peekByte(), len));
0749: } else {
0750: la.add(ber.parseStringWithTag(Ber.ASN_SIMPLE_STRING,
0751: isLdapv3, len));
0752: }
0753: return len[0];
0754: }
0755:
0756: private boolean isBinaryValued(String attrid, Hashtable binaryAttrs) {
0757: String id = attrid.toLowerCase();
0758:
0759: return ((id.indexOf(";binary") != -1)
0760: || defaultBinaryAttrs.containsKey(id) || ((binaryAttrs != null) && (binaryAttrs
0761: .containsKey(id))));
0762: }
0763:
0764: // package entry point; used by Connection
0765: static void parseResult(BerDecoder replyBer, LdapResult res,
0766: boolean isLdapv3) throws IOException {
0767:
0768: res.status = replyBer.parseEnumeration();
0769: res.matchedDN = replyBer.parseString(isLdapv3);
0770: res.errorMessage = replyBer.parseString(isLdapv3);
0771:
0772: // handle LDAPv3 referrals (if present)
0773: if (isLdapv3 && (replyBer.bytesLeft() > 0)
0774: && (replyBer.peekByte() == LDAP_REP_REFERRAL)) {
0775:
0776: Vector URLs = new Vector(4);
0777: int[] seqlen = new int[1];
0778:
0779: replyBer.parseSeq(seqlen);
0780: int endseq = replyBer.getParsePosition() + seqlen[0];
0781: while ((replyBer.getParsePosition() < endseq)
0782: && (replyBer.bytesLeft() > 0)) {
0783:
0784: URLs.addElement(replyBer.parseString(isLdapv3));
0785: }
0786:
0787: if (res.referrals == null) {
0788: res.referrals = new Vector(4);
0789: }
0790: res.referrals.addElement(URLs);
0791: }
0792: }
0793:
0794: // package entry point; used by Connection
0795: static Vector parseControls(BerDecoder replyBer) throws IOException {
0796:
0797: // handle LDAPv3 controls (if present)
0798: if ((replyBer.bytesLeft() > 0)
0799: && (replyBer.peekByte() == LDAP_CONTROLS)) {
0800: Vector ctls = new Vector(4);
0801: String controlOID;
0802: boolean criticality = false; // default
0803: byte[] controlValue = null; // optional
0804: int[] seqlen = new int[1];
0805:
0806: replyBer.parseSeq(seqlen);
0807: int endseq = replyBer.getParsePosition() + seqlen[0];
0808: while ((replyBer.getParsePosition() < endseq)
0809: && (replyBer.bytesLeft() > 0)) {
0810:
0811: replyBer.parseSeq(null);
0812: controlOID = replyBer.parseString(true);
0813:
0814: if ((replyBer.bytesLeft() > 0)
0815: && (replyBer.peekByte() == Ber.ASN_BOOLEAN)) {
0816: criticality = replyBer.parseBoolean();
0817: }
0818: if ((replyBer.bytesLeft() > 0)
0819: && (replyBer.peekByte() == Ber.ASN_OCTET_STR)) {
0820: controlValue = replyBer.parseOctetString(
0821: Ber.ASN_OCTET_STR, null);
0822: }
0823: if (controlOID != null) {
0824: ctls.addElement(new BasicControl(controlOID,
0825: criticality, controlValue));
0826: }
0827: }
0828: return ctls;
0829: } else {
0830: return null;
0831: }
0832: }
0833:
0834: private void parseExtResponse(BerDecoder replyBer, LdapResult res)
0835: throws IOException {
0836:
0837: parseResult(replyBer, res, isLdapv3);
0838:
0839: if ((replyBer.bytesLeft() > 0)
0840: && (replyBer.peekByte() == LDAP_REP_EXT_OID)) {
0841: res.extensionId = replyBer.parseStringWithTag(
0842: LDAP_REP_EXT_OID, isLdapv3, null);
0843: }
0844: if ((replyBer.bytesLeft() > 0)
0845: && (replyBer.peekByte() == LDAP_REP_EXT_VAL)) {
0846: res.extensionValue = replyBer.parseOctetString(
0847: LDAP_REP_EXT_VAL, null);
0848: }
0849:
0850: res.resControls = parseControls(replyBer);
0851: }
0852:
0853: //
0854: // Encode LDAPv3 controls
0855: //
0856: static void encodeControls(BerEncoder ber, Control[] reqCtls)
0857: throws IOException {
0858:
0859: if ((reqCtls == null) || (reqCtls.length == 0)) {
0860: return;
0861: }
0862:
0863: byte[] controlVal;
0864:
0865: ber.beginSeq(LdapClient.LDAP_CONTROLS);
0866:
0867: for (int i = 0; i < reqCtls.length; i++) {
0868: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
0869: ber.encodeString(reqCtls[i].getID(), true); // control OID
0870: if (reqCtls[i].isCritical()) {
0871: ber.encodeBoolean(true); // critical control
0872: }
0873: if ((controlVal = reqCtls[i].getEncodedValue()) != null) {
0874: ber.encodeOctetString(controlVal, Ber.ASN_OCTET_STR);
0875: }
0876: ber.endSeq();
0877: }
0878: ber.endSeq();
0879: }
0880:
0881: /**
0882: * Reads the next reply corresponding to msgId, outstanding on requestBer.
0883: * Processes the result and any controls.
0884: */
0885: private LdapResult processReply(LdapRequest req, LdapResult res,
0886: int responseType) throws IOException, NamingException {
0887:
0888: BerDecoder rber = conn.readReply(req);
0889:
0890: rber.parseSeq(null); // init seq
0891: rber.parseInt(); // msg id
0892: if (rber.parseByte() != responseType) {
0893: return res;
0894: }
0895:
0896: rber.parseLength();
0897: parseResult(rber, res, isLdapv3);
0898: res.resControls = isLdapv3 ? parseControls(rber) : null;
0899:
0900: conn.removeRequest(req);
0901:
0902: return res; // Done with operation
0903: }
0904:
0905: ////////////////////////////////////////////////////////////////////////////
0906: //
0907: // LDAP modify:
0908: // Modify the DN dn with the operations on attributes attrs.
0909: // ie, operations[0] is the operation to be performed on
0910: // attrs[0];
0911: // dn - DN to modify
0912: // operations - add, delete or replace
0913: // attrs - array of Attribute
0914: // reqCtls - array of request controls
0915: //
0916: ////////////////////////////////////////////////////////////////////////////
0917:
0918: static final int ADD = 0;
0919: static final int DELETE = 1;
0920: static final int REPLACE = 2;
0921:
0922: LdapResult modify(String dn, int operations[], Attribute attrs[],
0923: Control[] reqCtls) throws IOException, NamingException {
0924:
0925: ensureOpen();
0926:
0927: LdapResult res = new LdapResult();
0928: res.status = LDAP_OPERATIONS_ERROR;
0929:
0930: if (dn == null || operations.length != attrs.length)
0931: return res;
0932:
0933: BerEncoder ber = new BerEncoder();
0934: int curMsgId = conn.getMsgId();
0935:
0936: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
0937: ber.encodeInt(curMsgId);
0938: ber.beginSeq(LDAP_REQ_MODIFY);
0939: ber.encodeString(dn, isLdapv3);
0940: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
0941: for (int i = 0; i < operations.length; i++) {
0942: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
0943: ber.encodeInt(operations[i], LBER_ENUMERATED);
0944:
0945: // zero values is not permitted for the add op.
0946: if ((operations[i] == ADD) && hasNoValue(attrs[i])) {
0947: throw new InvalidAttributeValueException("'"
0948: + attrs[i].getID() + "' has no values.");
0949: } else {
0950: encodeAttribute(ber, attrs[i]);
0951: }
0952: ber.endSeq();
0953: }
0954: ber.endSeq();
0955: ber.endSeq();
0956: if (isLdapv3)
0957: encodeControls(ber, reqCtls);
0958: ber.endSeq();
0959:
0960: LdapRequest req = conn.writeRequest(ber, curMsgId);
0961:
0962: return processReply(req, res, LDAP_REP_MODIFY);
0963: }
0964:
0965: private void encodeAttribute(BerEncoder ber, Attribute attr)
0966: throws IOException, NamingException {
0967:
0968: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
0969: ber.encodeString(attr.getID(), isLdapv3);
0970: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR | 1);
0971: NamingEnumeration enum_ = attr.getAll();
0972: Object val;
0973: while (enum_.hasMore()) {
0974: val = enum_.next();
0975: if (val instanceof String) {
0976: ber.encodeString((String) val, isLdapv3);
0977: } else if (val instanceof byte[]) {
0978: ber.encodeOctetString((byte[]) val, Ber.ASN_OCTET_STR);
0979: } else if (val == null) {
0980: // no attribute value
0981: } else {
0982: throw new InvalidAttributeValueException("Malformed '"
0983: + attr.getID() + "' attribute value");
0984: }
0985: }
0986: ber.endSeq();
0987: ber.endSeq();
0988: }
0989:
0990: private static boolean hasNoValue(Attribute attr)
0991: throws NamingException {
0992: return attr.size() == 0
0993: || (attr.size() == 1 && attr.get() == null);
0994: }
0995:
0996: ////////////////////////////////////////////////////////////////////////////
0997: //
0998: // LDAP add
0999: // Adds entry to the Directory
1000: //
1001: ////////////////////////////////////////////////////////////////////////////
1002:
1003: LdapResult add(LdapEntry entry, Control[] reqCtls)
1004: throws IOException, NamingException {
1005:
1006: ensureOpen();
1007:
1008: LdapResult res = new LdapResult();
1009: res.status = LDAP_OPERATIONS_ERROR;
1010:
1011: if (entry == null || entry.DN == null)
1012: return res;
1013:
1014: BerEncoder ber = new BerEncoder();
1015: int curMsgId = conn.getMsgId();
1016: Attribute attr;
1017:
1018: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
1019: ber.encodeInt(curMsgId);
1020: ber.beginSeq(LDAP_REQ_ADD);
1021: ber.encodeString(entry.DN, isLdapv3);
1022: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
1023: NamingEnumeration enum_ = entry.attributes.getAll();
1024: while (enum_.hasMore()) {
1025: attr = (Attribute) 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)
1038: encodeControls(ber, reqCtls);
1039: ber.endSeq();
1040:
1041: LdapRequest req = conn.writeRequest(ber, curMsgId);
1042: return processReply(req, res, LDAP_REP_ADD);
1043: }
1044:
1045: ////////////////////////////////////////////////////////////////////////////
1046: //
1047: // LDAP delete
1048: // deletes entry from the Directory
1049: //
1050: ////////////////////////////////////////////////////////////////////////////
1051:
1052: LdapResult delete(String DN, Control[] reqCtls) throws IOException,
1053: NamingException {
1054:
1055: ensureOpen();
1056:
1057: LdapResult res = new LdapResult();
1058: res.status = LDAP_OPERATIONS_ERROR;
1059:
1060: if (DN == null)
1061: return res;
1062:
1063: BerEncoder ber = new BerEncoder();
1064: int curMsgId = conn.getMsgId();
1065:
1066: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
1067: ber.encodeInt(curMsgId);
1068: ber.encodeString(DN, LDAP_REQ_DELETE, isLdapv3);
1069: if (isLdapv3)
1070: encodeControls(ber, reqCtls);
1071: ber.endSeq();
1072:
1073: LdapRequest req = conn.writeRequest(ber, curMsgId);
1074:
1075: return processReply(req, res, LDAP_REP_DELETE);
1076: }
1077:
1078: ////////////////////////////////////////////////////////////////////////////
1079: //
1080: // LDAP modrdn
1081: // Changes the last element of DN to newrdn
1082: // dn - DN to change
1083: // newrdn - new RDN to rename to
1084: // deleteoldrdn - boolean whether to delete old attrs or not
1085: // newSuperior - new place to put the entry in the tree
1086: // (ignored if server is LDAPv2)
1087: // reqCtls - array of request controls
1088: //
1089: ////////////////////////////////////////////////////////////////////////////
1090:
1091: LdapResult moddn(String DN, String newrdn, boolean deleteOldRdn,
1092: String newSuperior, Control[] reqCtls) throws IOException,
1093: NamingException {
1094:
1095: ensureOpen();
1096:
1097: boolean changeSuperior = (newSuperior != null && newSuperior
1098: .length() > 0);
1099:
1100: LdapResult res = new LdapResult();
1101: res.status = LDAP_OPERATIONS_ERROR;
1102:
1103: if (DN == null || newrdn == null)
1104: return res;
1105:
1106: BerEncoder ber = new BerEncoder();
1107: int curMsgId = conn.getMsgId();
1108:
1109: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
1110: ber.encodeInt(curMsgId);
1111: ber.beginSeq(LDAP_REQ_MODRDN);
1112: ber.encodeString(DN, isLdapv3);
1113: ber.encodeString(newrdn, isLdapv3);
1114: ber.encodeBoolean(deleteOldRdn);
1115: if (isLdapv3 && changeSuperior) {
1116: //System.err.println("changin superior");
1117: ber.encodeString(newSuperior, LDAP_SUPERIOR_DN, isLdapv3);
1118: }
1119: ber.endSeq();
1120: if (isLdapv3)
1121: encodeControls(ber, reqCtls);
1122: ber.endSeq();
1123:
1124: LdapRequest req = conn.writeRequest(ber, curMsgId);
1125:
1126: return processReply(req, res, LDAP_REP_MODRDN);
1127: }
1128:
1129: ////////////////////////////////////////////////////////////////////////////
1130: //
1131: // LDAP compare
1132: // Compare attribute->value pairs in dn
1133: //
1134: ////////////////////////////////////////////////////////////////////////////
1135:
1136: LdapResult compare(String DN, String type, String value,
1137: Control[] reqCtls) throws IOException, NamingException {
1138:
1139: ensureOpen();
1140:
1141: LdapResult res = new LdapResult();
1142: res.status = LDAP_OPERATIONS_ERROR;
1143:
1144: if (DN == null || type == null || value == null)
1145: return res;
1146:
1147: BerEncoder ber = new BerEncoder();
1148: int curMsgId = conn.getMsgId();
1149:
1150: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
1151: ber.encodeInt(curMsgId);
1152: ber.beginSeq(LDAP_REQ_COMPARE);
1153: ber.encodeString(DN, isLdapv3);
1154: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
1155: ber.encodeString(type, isLdapv3);
1156:
1157: // replace any escaped characters in the value
1158: byte[] val = isLdapv3 ? value.getBytes("UTF8") : value
1159: .getBytes("8859_1");
1160: ber.encodeOctetString(Filter.unescapeFilterValue(val, 0,
1161: val.length), Ber.ASN_OCTET_STR);
1162:
1163: ber.endSeq();
1164: ber.endSeq();
1165: if (isLdapv3)
1166: encodeControls(ber, reqCtls);
1167: ber.endSeq();
1168:
1169: LdapRequest req = conn.writeRequest(ber, curMsgId);
1170:
1171: return processReply(req, res, LDAP_REP_COMPARE);
1172: }
1173:
1174: ////////////////////////////////////////////////////////////////////////////
1175: //
1176: // LDAP extended operation
1177: //
1178: ////////////////////////////////////////////////////////////////////////////
1179:
1180: LdapResult extendedOp(String id, byte[] request, Control[] reqCtls,
1181: boolean pauseAfterReceipt) throws IOException,
1182: NamingException {
1183:
1184: ensureOpen();
1185:
1186: LdapResult res = new LdapResult();
1187: res.status = LDAP_OPERATIONS_ERROR;
1188:
1189: if (id == null)
1190: return res;
1191:
1192: BerEncoder ber = new BerEncoder();
1193: int curMsgId = conn.getMsgId();
1194:
1195: ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
1196: ber.encodeInt(curMsgId);
1197: ber.beginSeq(LDAP_REQ_EXTENSION);
1198: ber.encodeString(id, Ber.ASN_CONTEXT | 0, isLdapv3);//[0]
1199: if (request != null) {
1200: ber.encodeOctetString(request, Ber.ASN_CONTEXT | 1);//[1]
1201: }
1202: ber.endSeq();
1203: encodeControls(ber, reqCtls); // always v3
1204: ber.endSeq();
1205:
1206: LdapRequest req = conn.writeRequest(ber, curMsgId,
1207: pauseAfterReceipt);
1208:
1209: BerDecoder rber = conn.readReply(req);
1210:
1211: rber.parseSeq(null); // init seq
1212: rber.parseInt(); // msg id
1213: if (rber.parseByte() != LDAP_REP_EXTENSION) {
1214: return res;
1215: }
1216:
1217: rber.parseLength();
1218: parseExtResponse(rber, res);
1219: conn.removeRequest(req);
1220:
1221: return res; // Done with operation
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, null, null, null, null, null,
1360: null,
1361: null,
1362: null,
1363: null,
1364: "No Such Object", // 32
1365: "Alias Problem", // 33
1366: "Invalid DN Syntax", // 34
1367: null,
1368: "Alias Dereferencing Problem", // 36
1369: null, null, null, null, null, null, null, null, null, null,
1370: null,
1371: "Inappropriate Authentication", // 48
1372: "Invalid Credentials", // 49
1373: "Insufficient Access Rights", // 50
1374: "Busy", // 51
1375: "Unavailable", // 52
1376: "Unwilling To Perform", // 53
1377: "Loop Detect", // 54
1378: null, null, null, null, null, null, null, null, null,
1379: "Naming Violation", // 64
1380: "Object Class Violation", // 65
1381: "Not Allowed On Non-leaf", // 66
1382: "Not Allowed On RDN", // 67
1383: "Entry Already Exists", // 68
1384: "Object Class Modifications Prohibited", // 69
1385: null, "Affects Multiple DSAs", // 71
1386: null, null, null, null, null, null, null, null, "Other", // 80
1387: null, null, null, null, null, null, null, null, null, null };
1388:
1389: /*
1390: * Generate an error message from the LDAP error code and error diagnostic.
1391: * The message format is:
1392: *
1393: * "[LDAP: error code <errorCode> - <errorMessage>]"
1394: *
1395: * where <errorCode> is a numeric error code
1396: * and <errorMessage> is a textual description of the error (if available)
1397: *
1398: */
1399: static String getErrorMessage(int errorCode, String errorMessage) {
1400:
1401: String message = "[LDAP: error code " + errorCode;
1402:
1403: if ((errorMessage != null) && (errorMessage.length() != 0)) {
1404:
1405: // append error message from the server
1406: message = message + " - " + errorMessage + "]";
1407:
1408: } else {
1409:
1410: // append built-in error message
1411: try {
1412: if (ldap_error_message[errorCode] != null) {
1413: message = message + " - "
1414: + ldap_error_message[errorCode] + "]";
1415: }
1416: } catch (ArrayIndexOutOfBoundsException ex) {
1417: message = message + "]";
1418: }
1419: }
1420: return message;
1421: }
1422:
1423: ////////////////////////////////////////////////////////////////////////////
1424: //
1425: // Unsolicited notification support.
1426: //
1427: // An LdapClient maintains a list of LdapCtx that have registered
1428: // for UnsolicitedNotifications. This is a list because a single
1429: // LdapClient might be shared among multiple contexts.
1430: //
1431: // When addUnsolicited() is invoked, the LdapCtx is added to the list.
1432: //
1433: // When Connection receives an unsolicited notification (msgid == 0),
1434: // it invokes LdapClient.processUnsolicited(). processUnsolicited()
1435: // parses the Extended Response. If there are registered listeners,
1436: // LdapClient creates an UnsolicitedNotification from the response
1437: // and informs each LdapCtx to fire an event for the notification.
1438: // If it is a DISCONNECT notification, the connection is closed and a
1439: // NamingExceptionEvent is fired to the listeners.
1440: //
1441: // When the connection is closed out-of-band like this, the next
1442: // time a method is invoked on LdapClient, an IOException is thrown.
1443: //
1444: // removeUnsolicited() is invoked to remove an LdapCtx from this client.
1445: //
1446: ////////////////////////////////////////////////////////////////////////////
1447: private Vector unsolicited = new Vector(3);
1448:
1449: void addUnsolicited(LdapCtx ctx) {
1450: if (debug > 0) {
1451: System.err.println("LdapClient.addUnsolicited" + ctx);
1452: }
1453: unsolicited.addElement(ctx);
1454: }
1455:
1456: void removeUnsolicited(LdapCtx ctx) {
1457: if (debug > 0) {
1458: System.err.println("LdapClient.removeUnsolicited" + ctx);
1459: }
1460: synchronized (unsolicited) {
1461: if (unsolicited.size() == 0) {
1462: return;
1463: }
1464: unsolicited.removeElement(ctx);
1465: }
1466: }
1467:
1468: // NOTE: Cannot be synchronized because this is called asynchronously
1469: // by the reader thread in Connection. Instead, sync on 'unsolicited' Vector.
1470: void processUnsolicited(BerDecoder ber) {
1471: if (debug > 0) {
1472: System.err.println("LdapClient.processUnsolicited");
1473: }
1474: synchronized (unsolicited) {
1475: try {
1476: // Parse the response
1477: LdapResult res = new LdapResult();
1478:
1479: ber.parseSeq(null); // init seq
1480: ber.parseInt(); // msg id; should be 0; ignored
1481: if (ber.parseByte() != LDAP_REP_EXTENSION) {
1482: throw new IOException(
1483: "Unsolicited Notification must be an Extended Response");
1484: }
1485: ber.parseLength();
1486: parseExtResponse(ber, res);
1487:
1488: if (DISCONNECT_OID.equals(res.extensionId)) {
1489: // force closing of connection
1490: forceClose(pooled);
1491: }
1492:
1493: if (unsolicited.size() > 0) {
1494: // Create an UnsolicitedNotification using the parsed data
1495: // Need a 'ctx' object because we want to use the context's
1496: // list of provider control factories.
1497: UnsolicitedNotification notice = new UnsolicitedResponseImpl(
1498: res.extensionId,
1499: res.extensionValue,
1500: res.referrals,
1501: res.status,
1502: res.errorMessage,
1503: res.matchedDN,
1504: (res.resControls != null) ? ((LdapCtx) unsolicited
1505: .elementAt(0))
1506: .convertControls(res.resControls)
1507: : null);
1508:
1509: // Fire UnsolicitedNotification events to listeners
1510: notifyUnsolicited(notice);
1511:
1512: // If "disconnect" notification,
1513: // notify unsolicited listeners via NamingException
1514: if (DISCONNECT_OID.equals(res.extensionId)) {
1515: notifyUnsolicited(new CommunicationException(
1516: "Connection closed"));
1517: }
1518: }
1519: } catch (IOException e) {
1520: if (unsolicited.size() == 0)
1521: return; // no one registered; ignore
1522:
1523: NamingException ne = new CommunicationException(
1524: "Problem parsing unsolicited notification");
1525: ne.setRootCause(e);
1526:
1527: notifyUnsolicited(ne);
1528:
1529: } catch (NamingException e) {
1530: notifyUnsolicited(e);
1531: }
1532: }
1533: }
1534:
1535: private void notifyUnsolicited(Object e) {
1536: for (int i = 0; i < unsolicited.size(); i++) {
1537: ((LdapCtx) unsolicited.elementAt(i)).fireUnsolicited(e);
1538: }
1539: if (e instanceof NamingException) {
1540: unsolicited.setSize(0); // no more listeners after exception
1541: }
1542: }
1543:
1544: private void ensureOpen() throws IOException {
1545: if (conn == null || !conn.useable) {
1546: if (conn != null && conn.closureReason != null) {
1547: throw conn.closureReason;
1548: } else {
1549: throw new IOException("connection closed");
1550: }
1551: }
1552: }
1553:
1554: // package private (used by LdapCtx)
1555: static LdapClient getInstance(boolean usePool, String hostname,
1556: int port, String factory, int connectTimeout,
1557: int readTimeout, OutputStream trace, int version,
1558: String authMechanism, Control[] ctls, String protocol,
1559: String user, Object passwd, Hashtable env)
1560: throws NamingException {
1561:
1562: if (usePool) {
1563: if (LdapPoolManager.isPoolingAllowed(factory, trace,
1564: authMechanism, protocol, env)) {
1565: LdapClient answer = LdapPoolManager.getLdapClient(
1566: hostname, port, factory, connectTimeout,
1567: readTimeout, trace, version, authMechanism,
1568: ctls, protocol, user, passwd, env);
1569: answer.referenceCount = 1; // always one when starting out
1570: return answer;
1571: }
1572: }
1573: return new LdapClient(hostname, port, factory, connectTimeout,
1574: readTimeout, trace, null);
1575: }
1576: }
|