Skip to content

Commit

Permalink
Fix an issue with percent-decoding of LDAP URLs
Browse files Browse the repository at this point in the history
Fixed an issue in which LDAP URLs with consecutive percent-encoded
bytes were not decoded correctly.
  • Loading branch information
dirmgr committed Dec 12, 2019
1 parent 59b23b6 commit c0fb784
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 145 deletions.
12 changes: 9 additions & 3 deletions docs/release-notes.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ <h3>Version 4.0.14</h3>
</p>

<ul>
<li>
Fixed an issue in which LDAP URLs with consecutive percent-encoded bytes were
not decoded correctly.
<br><br>
</li>

<li>
Fixed an issue that could cause the LDAP SDK to incorrectly handle data read
from a server when the communication is protected with SASL integrity or
Expand Down Expand Up @@ -46,8 +52,8 @@ <h3>Version 4.0.14</h3>
</li>

<li>
Added a new a new non-final <tt>MockableLDAPConnection</tt> class that makes it
easier to mock an <tt>LDAPConnection</tt> instance. It implements
Added a new non-final <tt>MockableLDAPConnection</tt> class that makes it easier
to mock an <tt>LDAPConnection</tt> instance. It implements
<tt>FullLDAPInterface</tt> and wraps a provided <tt>LDAPConnection</tt> instance.
If you create a <tt>MockableLDAPConnection</tt> subclass, then you may override
any of the <tt>FullLDAPInterface</tt> methods to implement your own behavior.
Expand All @@ -57,7 +63,7 @@ <h3>Version 4.0.14</h3>
</li>

<li>
Fixed a minor typo in the <tt>ldapsearch</tt> usage information
Fixed a minor typo in the <tt>ldapsearch</tt> usage information.
<br><br>
</li>
</ul>
Expand Down
268 changes: 126 additions & 142 deletions src/com/unboundid/ldap/sdk/LDAPURL.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@


import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.ArrayList;

import com.unboundid.util.ByteStringBuffer;
import com.unboundid.util.Debug;
import com.unboundid.util.NotMutable;
import com.unboundid.util.StaticUtils;
Expand Down Expand Up @@ -909,7 +909,7 @@ public static String percentDecode(final String s)
}

int pos = firstPercentPos;
final StringBuilder buffer = new StringBuilder(2 * length);
final ByteStringBuffer buffer = new ByteStringBuffer(2 * length);
buffer.append(s.substring(0, firstPercentPos));

while (pos < length)
Expand All @@ -923,152 +923,136 @@ public static String percentDecode(final String s)
ERR_LDAPURL_HEX_STRING_TOO_SHORT.get(s));
}


final ByteBuffer byteBuffer = ByteBuffer.allocate(length - pos);
while (pos < length)
final byte b;
switch (s.charAt(pos++))
{
final byte b;
switch (s.charAt(pos++))
{
case '0':
b = 0x00;
break;
case '1':
b = 0x10;
break;
case '2':
b = 0x20;
break;
case '3':
b = 0x30;
break;
case '4':
b = 0x40;
break;
case '5':
b = 0x50;
break;
case '6':
b = 0x60;
break;
case '7':
b = 0x70;
break;
case '8':
b = (byte) 0x80;
break;
case '9':
b = (byte) 0x90;
break;
case 'a':
case 'A':
b = (byte) 0xA0;
break;
case 'b':
case 'B':
b = (byte) 0xB0;
break;
case 'c':
case 'C':
b = (byte) 0xC0;
break;
case 'd':
case 'D':
b = (byte) 0xD0;
break;
case 'e':
case 'E':
b = (byte) 0xE0;
break;
case 'f':
case 'F':
b = (byte) 0xF0;
break;
default:
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_LDAPURL_INVALID_HEX_CHAR.get(
s.charAt(pos-1)));
}

if (pos >= length)
{
case '0':
b = 0x00;
break;
case '1':
b = 0x10;
break;
case '2':
b = 0x20;
break;
case '3':
b = 0x30;
break;
case '4':
b = 0x40;
break;
case '5':
b = 0x50;
break;
case '6':
b = 0x60;
break;
case '7':
b = 0x70;
break;
case '8':
b = (byte) 0x80;
break;
case '9':
b = (byte) 0x90;
break;
case 'a':
case 'A':
b = (byte) 0xA0;
break;
case 'b':
case 'B':
b = (byte) 0xB0;
break;
case 'c':
case 'C':
b = (byte) 0xC0;
break;
case 'd':
case 'D':
b = (byte) 0xD0;
break;
case 'e':
case 'E':
b = (byte) 0xE0;
break;
case 'f':
case 'F':
b = (byte) 0xF0;
break;
default:
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_LDAPURL_HEX_STRING_TOO_SHORT.get(s));
}
ERR_LDAPURL_INVALID_HEX_CHAR.get(
s.charAt(pos-1)));
}

switch (s.charAt(pos++))
{
case '0':
byteBuffer.put(b);
break;
case '1':
byteBuffer.put((byte) (b | 0x01));
break;
case '2':
byteBuffer.put((byte) (b | 0x02));
break;
case '3':
byteBuffer.put((byte) (b | 0x03));
break;
case '4':
byteBuffer.put((byte) (b | 0x04));
break;
case '5':
byteBuffer.put((byte) (b | 0x05));
break;
case '6':
byteBuffer.put((byte) (b | 0x06));
break;
case '7':
byteBuffer.put((byte) (b | 0x07));
break;
case '8':
byteBuffer.put((byte) (b | 0x08));
break;
case '9':
byteBuffer.put((byte) (b | 0x09));
break;
case 'a':
case 'A':
byteBuffer.put((byte) (b | 0x0A));
break;
case 'b':
case 'B':
byteBuffer.put((byte) (b | 0x0B));
break;
case 'c':
case 'C':
byteBuffer.put((byte) (b | 0x0C));
break;
case 'd':
case 'D':
byteBuffer.put((byte) (b | 0x0D));
break;
case 'e':
case 'E':
byteBuffer.put((byte) (b | 0x0E));
break;
case 'f':
case 'F':
byteBuffer.put((byte) (b | 0x0F));
break;
default:
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_LDAPURL_INVALID_HEX_CHAR.get(
s.charAt(pos-1)));
}
if (pos >= length)
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_LDAPURL_HEX_STRING_TOO_SHORT.get(s));
}

if ((pos < length) && (s.charAt(pos) != '%'))
{
switch (s.charAt(pos++))
{
case '0':
buffer.append(b);
break;
}
case '1':
buffer.append((byte) (b | 0x01));
break;
case '2':
buffer.append((byte) (b | 0x02));
break;
case '3':
buffer.append((byte) (b | 0x03));
break;
case '4':
buffer.append((byte) (b | 0x04));
break;
case '5':
buffer.append((byte) (b | 0x05));
break;
case '6':
buffer.append((byte) (b | 0x06));
break;
case '7':
buffer.append((byte) (b | 0x07));
break;
case '8':
buffer.append((byte) (b | 0x08));
break;
case '9':
buffer.append((byte) (b | 0x09));
break;
case 'a':
case 'A':
buffer.append((byte) (b | 0x0A));
break;
case 'b':
case 'B':
buffer.append((byte) (b | 0x0B));
break;
case 'c':
case 'C':
buffer.append((byte) (b | 0x0C));
break;
case 'd':
case 'D':
buffer.append((byte) (b | 0x0D));
break;
case 'e':
case 'E':
buffer.append((byte) (b | 0x0E));
break;
case 'f':
case 'F':
buffer.append((byte) (b | 0x0F));
break;
default:
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_LDAPURL_INVALID_HEX_CHAR.get(
s.charAt(pos-1)));
}

byteBuffer.flip();
final byte[] byteArray = new byte[byteBuffer.limit()];
byteBuffer.get(byteArray);

buffer.append(StaticUtils.toUTF8String(byteArray));
}
else
{
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/src/com/unboundid/ldap/sdk/LDAPURLTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,29 @@ public Object[][] getTestValidURLs()
"ldap://server.example.com:389/a=b+c=d,dc=example,dc=com??sub?" +
"(cn=foo%20bar)"
},

new Object[]
{
"ldap://child.root.example.com/OU=%25%5E%25%5E%25%5E*,DC=child," +
"DC=root,DC=example,DC=com",
"ldap",
"child.root.example.com",
true,
389,
false,
new DN(new RDN("OU", "%^%^%^*"), new RDN("DC", "child"),
new RDN("DC", "root"), new RDN("DC", "example"),
new RDN("dc", "com")),
true,
new String[0],
false,
SearchScope.BASE,
false,
Filter.create("(objectClass=*)"),
false,
"ldap://child.root.example.com:389/ou=%25%5e%25%5e%25%5e*,dc=child," +
"dc=root,dc=example,dc=com??base?(objectclass=*)"
}
};
}

Expand Down

0 comments on commit c0fb784

Please sign in to comment.