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