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