Re: [Freeipa-devel] [PATCH 0019] Add proper DN escaping before LDAP library calls

2012-05-09 Thread Adam Tkac

On 05/03/2012 03:46 PM, Petr Spacek wrote:

On 05/03/2012 11:25 AM, Petr Spacek wrote:

Hello,

this patch adds missing DNS-LDAP escaping conversion. It's necessary to
prevent (potential) LDAP injection attacks in future.

Code isn't very nice, because DNS users decimal escaping \123, LDAP uses
hexadecimal escaping \ab and set of escaped characters is smaller in 
DNS than

in LDAP.

Any improvements are welcome.

Petr^2 Spacek


Here is second version of the patch.

Changes:
- comments
- order of [._-] in if()
- function was renamed to dns_to_ldap_dn_escape()

Escaping logic itself wasn't changed.


Hello Peter,

please check my comments inside the patch.

Regards, Adam



Petr^2 Spacek

bind-dyndb-ldap-pspacek-0019-2-Add-proper-DN-escaping-before-LDAP-library-calls.patch


 From 0291e1b63e077af06b84374c7aa0b15bd71aeb7d Mon Sep 17 00:00:00 2001
From: Petr Spacekpspa...@redhat.com
Date: Mon, 23 Apr 2012 11:38:43 +0200
Subject: [PATCH] Add proper DN escaping before LDAP library calls.
  Signed-off-by: Petr Spacekpspa...@redhat.com

---
  src/ldap_convert.c  |  105 --
  src/zone_register.c |7 +++
  src/zone_register.h |3 +
  3 files changed, 110 insertions(+), 5 deletions(-)

diff --git a/src/ldap_convert.c b/src/ldap_convert.c
index 
6405a98fda942cbba10b4c862351c4e3695aba85..1e7c31097b532a8641a34de589ef0926df6bf45f
 100644
--- a/src/ldap_convert.c
+++ b/src/ldap_convert.c
@@ -21,6 +21,7 @@
  #includeisc/buffer.h
  #includeisc/mem.h
  #includeisc/util.h
+#includeisc/string.h

  #includedns/name.h
  #includedns/rdatatype.h
@@ -32,6 +33,7 @@

  #includeerrno.h
  #includestrings.h
+#includectype.h

  #include str.h
  #include ldap_convert.h
@@ -189,6 +191,94 @@ cleanup:
return result;
  }

+/**
+ * Convert a string from DNS escaping to LDAP escaping.
+ * The Input string dns_str is expected to be the result of 
dns_name_tostring().
+ * The DNS label can contain any binary data as described in
+ * http://tools.ietf.org/html/rfc2181#section-11 .
+ *
+ * DNS escaping uses form   \123 = ASCII value 123 (decimal)
+ * LDAP escaping users form \7b  = ASCII value 7b (hexadecimal)
+ *
+ * Input (DNS escaped) example  : _aaa,bbb\255\000ccc.555.ddd-eee
+ * Output (LDAP escaped) example: _aaa\2cbbb\ff\00ccc.555.ddd-eee
+ *
+ * The DNS to text functions from ISC libraries do not convert certain
+ * characters (e.g. ,). This function converts \123 form to \7b form in all
+ * cases. Other characters (not escaped by ISC libraries) will be additionally
+ * converted to the LDAP escape form.
+ * Input characters [a-zA-Z0-9._-] are left in raw ASCII form.
+ *
+ * If dns_str consists only of the characters in the [a-zA-Z0-9._-] set, it
+ * will be checked  copied to the output buffer, without any additional 
escaping.
+ */
+isc_result_t
+dns_to_ldap_dn_escape(isc_mem_t *mctx, const char const * dns_str, char ** 
ldap_name) {
+   isc_result_t result = ISC_R_FAILURE;
+   char * esc_name = NULL;
+   int idx_symb_first = -1; /* index of first nice printable symbol in 
dns_str */
+   int dns_idx = 0;
+   int esc_idx = 0;
+
+   REQUIRE(dns_str);

Please use REQUIRE(dns_str != NULL);

+   REQUIRE(ldap_name != NULL  *ldap_name == NULL);
+
+   int dns_str_len = strlen(dns_str);
+
+   /**
+* In worst case each symbol from DNS dns_str will be represented
+* as \xy in ldap_name. (xy are hexadecimal digits)
+*/
+   CHECKED_MEM_ALLOCATE(mctx, *ldap_name, 3*dns_str_len+1);
Please put the space between number/variable and an operator (3 * 
dns_str_len + 1 in this case).

+   esc_name = *ldap_name;
+
+   for (dns_idx = 0; dns_idx  dns_str_len; dns_idx++) {
+   if (isalnum(dns_str[dns_idx]) || dns_str[dns_idx] == '.'
+   || dns_str[dns_idx] == '-' || dns_str[dns_idx] 
== '_' ) {
+   if (idx_symb_first == -1)
+   idx_symb_first = dns_idx;
+   continue;
+   } else { /* some not very nice symbols */
+   int ascii_val;
+   if (idx_symb_first != -1) { /* copy previous nice part 
*/
+   int length_ok = dns_idx-idx_symb_first;

ditto

+   memcpy(esc_name + esc_idx, dns_str + 
idx_symb_first, length_ok);
+   esc_idx += length_ok;
+   idx_symb_first = -1;
+   }
+   if (dns_str[dns_idx] != '\\') { /* not nice raw value, 
e.g. ',' */
+   ascii_val = dns_str[dns_idx];
+   } else { /* not nice value in DNS \123 decimal format */
+   /* check if input length= expected size */
+   if (dns_str_len= dns_idx+3) { /* this should 
never happen */

ditto

+   log_bug(dns string too short);
+ 

Re: [Freeipa-devel] [PATCH 0019] Add proper DN escaping before LDAP library calls

2012-05-09 Thread Petr Spacek

On 05/09/2012 01:24 PM, Adam Tkac wrote:

On 05/03/2012 03:46 PM, Petr Spacek wrote:

On 05/03/2012 11:25 AM, Petr Spacek wrote:

Hello,

this patch adds missing DNS-LDAP escaping conversion. It's necessary to
prevent (potential) LDAP injection attacks in future.

Code isn't very nice, because DNS users decimal escaping \123, LDAP uses
hexadecimal escaping \ab and set of escaped characters is smaller in DNS than
in LDAP.

Any improvements are welcome.

Petr^2 Spacek


Here is second version of the patch.

Changes:
- comments
- order of [._-] in if()
- function was renamed to dns_to_ldap_dn_escape()

Escaping logic itself wasn't changed.


Hello Peter,

please check my comments inside the patch.

Oh, I feel so ashamed. All errors were corrected, see attachment.

Petr^2 Spacek



Regards, Adam



Petr^2 Spacek

bind-dyndb-ldap-pspacek-0019-2-Add-proper-DN-escaping-before-LDAP-library-calls.patch
From 1db133bf26a3c8bec7da3b045258bad630378d50 Mon Sep 17 00:00:00 2001
From: Petr Spacek pspa...@redhat.com
Date: Mon, 23 Apr 2012 11:38:43 +0200
Subject: [PATCH] Add proper DN escaping before LDAP library calls.
 Signed-off-by: Petr Spacek pspa...@redhat.com

---
 src/ldap_convert.c  |  109 --
 src/zone_register.c |7 +++
 src/zone_register.h |3 +
 3 files changed, 114 insertions(+), 5 deletions(-)

diff --git a/src/ldap_convert.c b/src/ldap_convert.c
index 6405a98fda942cbba10b4c862351c4e3695aba85..6cb761db97cff12b999263e2fcb4f31603ccd733 100644
--- a/src/ldap_convert.c
+++ b/src/ldap_convert.c
@@ -21,6 +21,7 @@
 #include isc/buffer.h
 #include isc/mem.h
 #include isc/util.h
+#include isc/string.h
 
 #include dns/name.h
 #include dns/rdatatype.h
@@ -32,6 +33,7 @@
 
 #include errno.h
 #include strings.h
+#include ctype.h
 
 #include str.h
 #include ldap_convert.h
@@ -189,6 +191,96 @@ cleanup:
 	return result;
 }
 
+/**
+ * Convert a string from DNS escaping to LDAP escaping.
+ * The Input string dns_str is expected to be the result of dns_name_tostring().
+ * The DNS label can contain any binary data as described in
+ * http://tools.ietf.org/html/rfc2181#section-11 .
+ *
+ * DNS escaping uses form   \123 = ASCII value 123 (decimal)
+ * LDAP escaping users form \7b  = ASCII value 7b (hexadecimal)
+ *
+ * Input (DNS escaped) example  : _aaa,bbb\255\000ccc.555.ddd-eee
+ * Output (LDAP escaped) example: _aaa\2cbbb\ff\00ccc.555.ddd-eee
+ *
+ * The DNS to text functions from ISC libraries do not convert certain
+ * characters (e.g. ,). This function converts \123 form to \7b form in all
+ * cases. Other characters (not escaped by ISC libraries) will be additionally
+ * converted to the LDAP escape form.
+ * Input characters [a-zA-Z0-9._-] are left in raw ASCII form.
+ *
+ * If dns_str consists only of the characters in the [a-zA-Z0-9._-] set, it
+ * will be checked  copied to the output buffer, without any additional escaping.
+ */
+isc_result_t
+dns_to_ldap_dn_escape(isc_mem_t *mctx, const char const * dns_str, char ** ldap_name) {
+	isc_result_t result = ISC_R_FAILURE;
+	char * esc_name = NULL;
+	int idx_symb_first = -1; /* index of first nice printable symbol in dns_str */
+	int dns_idx = 0;
+	int esc_idx = 0;
+
+	REQUIRE(dns_str != NULL);
+	REQUIRE(ldap_name != NULL  *ldap_name == NULL);
+
+	int dns_str_len = strlen(dns_str);
+
+	/**
+	 * In worst case each symbol from DNS dns_str will be represented
+	 * as \xy in ldap_name. (xy are hexadecimal digits)
+	 */
+	CHECKED_MEM_ALLOCATE(mctx, *ldap_name, 3 * dns_str_len + 1);
+	esc_name = *ldap_name;
+
+	for (dns_idx = 0; dns_idx  dns_str_len; dns_idx++) {
+		if (isalnum(dns_str[dns_idx]) || dns_str[dns_idx] == '.'
+|| dns_str[dns_idx] == '-' || dns_str[dns_idx] == '_' ) {
+			if (idx_symb_first == -1)
+idx_symb_first = dns_idx;
+			continue;
+		} else { /* some not very nice symbols */
+			int ascii_val;
+			if (idx_symb_first != -1) { /* copy previous nice part */
+int length_ok = dns_idx - idx_symb_first;
+memcpy(esc_name + esc_idx, dns_str + idx_symb_first, length_ok);
+esc_idx += length_ok;
+idx_symb_first = -1;
+			}
+			if (dns_str[dns_idx] != '\\') { /* not nice raw value, e.g. ',' */
+ascii_val = dns_str[dns_idx];
+			} else { /* not nice value in DNS \123 decimal format */
+/* check if input length = expected size */
+if (dns_str_len = dns_idx + 3) { /* this should never happen */
+	log_bug(dns string too short);
+	result = ISC_R_NOSPACE;
+	goto cleanup;
+}
+ascii_val = 100 * (dns_str[dns_idx + 1] - '0')
+		+ 10 * (dns_str[dns_idx + 2] - '0')
+		+ (dns_str[dns_idx + 3] - '0');
+dns_idx += 3;
+			}
+			/* LDAP uses \xy escaping. xy represent two hexadecimal digits.*/
+			/* TODO: optimize to bit mask  rotate  dec-hex table? */
+			CHECK(isc_string_printf(esc_name + esc_idx, 4, \\%02x, ascii_val));
+			esc_idx += 3; /* isc_string_printf wrote 4 bytes including '\0' */
+		}
+	}
+	if (idx_symb_first != -1) { /* copy last nice part */
+		int 

Re: [Freeipa-devel] [PATCH 0019] Add proper DN escaping before LDAP library calls

2012-05-09 Thread Petr Spacek

On 05/09/2012 02:17 PM, Adam Tkac wrote:

On 05/09/2012 02:11 PM, Petr Spacek wrote:

On 05/09/2012 01:24 PM, Adam Tkac wrote:

On 05/03/2012 03:46 PM, Petr Spacek wrote:

On 05/03/2012 11:25 AM, Petr Spacek wrote:

Hello,

this patch adds missing DNS-LDAP escaping conversion. It's necessary to
prevent (potential) LDAP injection attacks in future.

Code isn't very nice, because DNS users decimal escaping \123, LDAP uses
hexadecimal escaping \ab and set of escaped characters is smaller in DNS
than
in LDAP.

Any improvements are welcome.

Petr^2 Spacek


Here is second version of the patch.

Changes:
- comments
- order of [._-] in if()
- function was renamed to dns_to_ldap_dn_escape()

Escaping logic itself wasn't changed.


Hello Peter,

please check my comments inside the patch.

Oh, I feel so ashamed. All errors were corrected, see attachment.

Petr^2 Spacek

Ack, please push it to master.
Pushed with minor change as discussed on IRC: log_error() was substituted by 
REQUIRE().


https://fedorahosted.org/bind-dyndb-ldap/changeset/3d43fd66aa68ef275855391a94e47e9d2f30309d

Petr^2 Spacek





Regards, Adam



Petr^2 Spacek

bind-dyndb-ldap-pspacek-0019-2-Add-proper-DN-escaping-before-LDAP-library-calls.patch



bind-dyndb-ldap-pspacek-0019-3-Add-proper-DN-escaping-before-LDAP-library-calls.patch



From 3000d2165d3eae332476d223a286f620c5be08bc Mon Sep 17 00:00:00 2001
From: Petr Spacek pspa...@redhat.com
Date: Mon, 23 Apr 2012 11:38:43 +0200
Subject: [PATCH] Add proper DN escaping before LDAP library calls.
 Signed-off-by: Petr Spacek pspa...@redhat.com

---
 src/ldap_convert.c  |  105 --
 src/zone_register.c |7 +++
 src/zone_register.h |3 +
 3 files changed, 110 insertions(+), 5 deletions(-)

diff --git a/src/ldap_convert.c b/src/ldap_convert.c
index 6405a98fda942cbba10b4c862351c4e3695aba85..6b4e321ea6b14128fcfdfef91fb400a222ee2211 100644
--- a/src/ldap_convert.c
+++ b/src/ldap_convert.c
@@ -21,6 +21,7 @@
 #include isc/buffer.h
 #include isc/mem.h
 #include isc/util.h
+#include isc/string.h
 
 #include dns/name.h
 #include dns/rdatatype.h
@@ -32,6 +33,7 @@
 
 #include errno.h
 #include strings.h
+#include ctype.h
 
 #include str.h
 #include ldap_convert.h
@@ -189,6 +191,92 @@ cleanup:
 	return result;
 }
 
+/**
+ * Convert a string from DNS escaping to LDAP escaping.
+ * The Input string dns_str is expected to be the result of dns_name_tostring().
+ * The DNS label can contain any binary data as described in
+ * http://tools.ietf.org/html/rfc2181#section-11 .
+ *
+ * DNS escaping uses form   \123 = ASCII value 123 (decimal)
+ * LDAP escaping users form \7b  = ASCII value 7b (hexadecimal)
+ *
+ * Input (DNS escaped) example  : _aaa,bbb\255\000ccc.555.ddd-eee
+ * Output (LDAP escaped) example: _aaa\2cbbb\ff\00ccc.555.ddd-eee
+ *
+ * The DNS to text functions from ISC libraries do not convert certain
+ * characters (e.g. ,). This function converts \123 form to \7b form in all
+ * cases. Other characters (not escaped by ISC libraries) will be additionally
+ * converted to the LDAP escape form.
+ * Input characters [a-zA-Z0-9._-] are left in raw ASCII form.
+ *
+ * If dns_str consists only of the characters in the [a-zA-Z0-9._-] set, it
+ * will be checked  copied to the output buffer, without any additional escaping.
+ */
+isc_result_t
+dns_to_ldap_dn_escape(isc_mem_t *mctx, const char const * dns_str, char ** ldap_name) {
+	isc_result_t result = ISC_R_FAILURE;
+	char * esc_name = NULL;
+	int idx_symb_first = -1; /* index of first nice printable symbol in dns_str */
+	int dns_idx = 0;
+	int esc_idx = 0;
+
+	REQUIRE(dns_str != NULL);
+	REQUIRE(ldap_name != NULL  *ldap_name == NULL);
+
+	int dns_str_len = strlen(dns_str);
+
+	/**
+	 * In worst case each symbol from DNS dns_str will be represented
+	 * as \xy in ldap_name. (xy are hexadecimal digits)
+	 */
+	CHECKED_MEM_ALLOCATE(mctx, *ldap_name, 3 * dns_str_len + 1);
+	esc_name = *ldap_name;
+
+	for (dns_idx = 0; dns_idx  dns_str_len; dns_idx++) {
+		if (isalnum(dns_str[dns_idx]) || dns_str[dns_idx] == '.'
+|| dns_str[dns_idx] == '-' || dns_str[dns_idx] == '_' ) {
+			if (idx_symb_first == -1)
+idx_symb_first = dns_idx;
+			continue;
+		} else { /* some not very nice symbols */
+			int ascii_val;
+			if (idx_symb_first != -1) { /* copy previous nice part */
+int length_ok = dns_idx - idx_symb_first;
+memcpy(esc_name + esc_idx, dns_str + idx_symb_first, length_ok);
+esc_idx += length_ok;
+idx_symb_first = -1;
+			}
+			if (dns_str[dns_idx] != '\\') { /* not nice raw value, e.g. ',' */
+ascii_val = dns_str[dns_idx];
+			} else { /* not nice value in DNS \123 decimal format */
+/* check if input length = expected size */
+REQUIRE (dns_str_len  dns_idx + 3); /* this problem should never happen */
+ascii_val = 100 * (dns_str[dns_idx + 1] - '0')
+		+ 10 * (dns_str[dns_idx + 2] - '0')
+		+ (dns_str[dns_idx + 3] - '0');
+dns_idx += 3;
+			}
+			/* LDAP uses \xy 

[Freeipa-devel] [PATCH 0019] Add proper DN escaping before LDAP library calls

2012-05-03 Thread Petr Spacek

Hello,

this patch adds missing DNS-LDAP escaping conversion. It's necessary to 
prevent (potential) LDAP injection attacks in future.


Code isn't very nice, because DNS users decimal escaping \123, LDAP uses 
hexadecimal escaping \ab and set of escaped characters is smaller in DNS than 
in LDAP.


Any improvements are welcome.

Petr^2 Spacek
From 10bc76498072e554ccfb1504d81f3166a14b79a5 Mon Sep 17 00:00:00 2001
From: Petr Spacek pspa...@redhat.com
Date: Mon, 23 Apr 2012 11:38:43 +0200
Subject: [PATCH] Add proper DN escaping before LDAP library calls.
 Signed-off-by: Petr Spacek pspa...@redhat.com

---
 src/ldap_convert.c  |   95 ---
 src/zone_register.c |7 
 src/zone_register.h |3 ++
 3 files changed, 100 insertions(+), 5 deletions(-)

diff --git a/src/ldap_convert.c b/src/ldap_convert.c
index 6405a98fda942cbba10b4c862351c4e3695aba85..32c17f92fc2d2f0f9a5237fd8756f8d4e1a2d476 100644
--- a/src/ldap_convert.c
+++ b/src/ldap_convert.c
@@ -21,6 +21,7 @@
 #include isc/buffer.h
 #include isc/mem.h
 #include isc/util.h
+#include isc/string.h
 
 #include dns/name.h
 #include dns/rdatatype.h
@@ -32,6 +33,7 @@
 
 #include errno.h
 #include strings.h
+#include ctype.h
 
 #include str.h
 #include ldap_convert.h
@@ -189,6 +191,84 @@ cleanup:
 	return result;
 }
 
+/**
+ * Convert string from DNS escaping to LDAP escaping.
+ * DNS escaping:  \123 = ASCII value 123 (decimal)
+ * LDAP escaping: \xy  = ASCII value xy (hexadecimal)
+ * DNS to text functions from ISC libraries do not convert certain characters
+ * (e.g. ,). These characters are converted from raw ASCII form to \xy form.
+ * Any character other than [a-zA-Z0-9.-] will be converted to \xy.
+ *
+ * If dns_str consists only from [a-zA-Z0-9.-], it will be checked  copied to
+ * output buffer, without any magic.
+ */
+isc_result_t
+dns_to_ldap_escape(isc_mem_t *mctx, const char const * dns_str, char ** ldap_name) {
+	isc_result_t result = ISC_R_FAILURE;
+	char * esc_name = NULL;
+	int idx_symb_first = -1; /* index of first nice printable symbol in dns_str */
+	int dns_idx = 0;
+	int esc_idx = 0;
+
+	REQUIRE(dns_str);
+	REQUIRE(ldap_name != NULL  *ldap_name == NULL);
+
+	int dns_str_len = strlen(dns_str);
+
+	/**
+	 * In worst case each symbol from DNS dns_str will be represented
+	 * as \xy in ldap_name. (xy are hexadecimal digits)
+	 */
+	CHECKED_MEM_ALLOCATE(mctx, *ldap_name, 3*dns_str_len+1);
+	esc_name = *ldap_name;
+
+	for (dns_idx = 0; dns_idx  dns_str_len; dns_idx++) {
+		if (isalnum(dns_str[dns_idx]) || dns_str[dns_idx] == '-'
+|| dns_str[dns_idx] == '.' || dns_str[dns_idx] == '_' ) {
+			if (idx_symb_first == -1)
+idx_symb_first = dns_idx;
+			continue;
+		} else { /* some not very nice symbols */
+			int ascii_val;
+			if (idx_symb_first != -1) { /* copy previous nice part */
+int length_ok = dns_idx-idx_symb_first;
+memcpy(esc_name + esc_idx, dns_str + idx_symb_first, length_ok);
+esc_idx += length_ok;
+idx_symb_first = -1;
+			}
+			if (dns_str[dns_idx] != '\\') { /* not nice raw value, e.g. ',' */
+ascii_val = dns_str[dns_idx];
+			} else { /* not nice value in DNS \123 decimal format */
+/* check if input length = expected size */
+if (dns_str_len = dns_idx+3) { /* this should never happen */
+	log_bug(dns string too short);
+	result = ISC_R_NOSPACE;
+	goto cleanup;
+}
+ascii_val = 100*(dns_str[dns_idx+1]-'0')
+		+ 10*(dns_str[dns_idx+2]-'0') + (dns_str[dns_idx+3]-'0');
+dns_idx += 3;
+			}
+			/* LDAP uses \xy escaping. xy represent two hexadecimal digits.*/
+			/* TODO: optimize to bit mask  rotate  dec-hex table? */
+			CHECK(isc_string_printf(esc_name+esc_idx, 4, \\%02x, ascii_val));
+			esc_idx += 3; /* isc_string_printf wrote 4 bytes including '\0' */
+		}
+	}
+	if (idx_symb_first != -1) { /* copy last nice part */
+		int length_ok = dns_idx-idx_symb_first;
+		memcpy(esc_name + esc_idx, dns_str + idx_symb_first, dns_idx-idx_symb_first);
+		esc_idx += length_ok;
+		idx_symb_first = -1;
+	}
+	esc_name[esc_idx] = '\0';
+	return ISC_R_SUCCESS;
+
+cleanup:
+	if (*ldap_name) isc_mem_free(mctx, *ldap_name);
+	return result;
+}
+
 static isc_result_t
 explode_dn(const char *dn, char ***explodedp, int notypes)
 {
@@ -243,11 +323,15 @@ dnsname_to_dn(zone_register_t *zr, dns_name_t *name, ld_string_t *target)
 	isc_result_t result;
 	int label_count;
 	const char *zone_dn = NULL;
+	char *dns_str = NULL;
+	char *escaped_name = NULL;
 
 	REQUIRE(zr != NULL);
 	REQUIRE(name != NULL);
 	REQUIRE(target != NULL);
 
+	isc_mem_t * mctx = zr_get_mctx(zr);
+
 	/* Find the DN of the zone we belong to. */
 	{
 		DECLARE_BUFFERED_NAME(zone);
@@ -264,26 +348,26 @@ dnsname_to_dn(zone_register_t *zr, dns_name_t *name, ld_string_t *target)
 
 	str_clear(target);
 	if (label_count  0) {
-		DECLARE_BUFFER(buffer, DNS_NAME_MAXTEXT);
 		dns_name_t labels;
 
-		INIT_BUFFER(buffer);
 		dns_name_init(labels, NULL);
-
 		dns_name_getlabelsequence(name, 0, 

Re: [Freeipa-devel] [PATCH 0019] Add proper DN escaping before LDAP library calls

2012-05-03 Thread Petr Spacek

On 05/03/2012 11:25 AM, Petr Spacek wrote:

Hello,

this patch adds missing DNS-LDAP escaping conversion. It's necessary to
prevent (potential) LDAP injection attacks in future.

Code isn't very nice, because DNS users decimal escaping \123, LDAP uses
hexadecimal escaping \ab and set of escaped characters is smaller in DNS than
in LDAP.

Any improvements are welcome.

Petr^2 Spacek


Here is second version of the patch.

Changes:
- comments
- order of [._-] in if()
- function was renamed to dns_to_ldap_dn_escape()

Escaping logic itself wasn't changed.

Petr^2 Spacek
From 0291e1b63e077af06b84374c7aa0b15bd71aeb7d Mon Sep 17 00:00:00 2001
From: Petr Spacek pspa...@redhat.com
Date: Mon, 23 Apr 2012 11:38:43 +0200
Subject: [PATCH] Add proper DN escaping before LDAP library calls.
 Signed-off-by: Petr Spacek pspa...@redhat.com

---
 src/ldap_convert.c  |  105 --
 src/zone_register.c |7 +++
 src/zone_register.h |3 +
 3 files changed, 110 insertions(+), 5 deletions(-)

diff --git a/src/ldap_convert.c b/src/ldap_convert.c
index 6405a98fda942cbba10b4c862351c4e3695aba85..1e7c31097b532a8641a34de589ef0926df6bf45f 100644
--- a/src/ldap_convert.c
+++ b/src/ldap_convert.c
@@ -21,6 +21,7 @@
 #include isc/buffer.h
 #include isc/mem.h
 #include isc/util.h
+#include isc/string.h
 
 #include dns/name.h
 #include dns/rdatatype.h
@@ -32,6 +33,7 @@
 
 #include errno.h
 #include strings.h
+#include ctype.h
 
 #include str.h
 #include ldap_convert.h
@@ -189,6 +191,94 @@ cleanup:
 	return result;
 }
 
+/**
+ * Convert a string from DNS escaping to LDAP escaping.
+ * The Input string dns_str is expected to be the result of dns_name_tostring().
+ * The DNS label can contain any binary data as described in
+ * http://tools.ietf.org/html/rfc2181#section-11 .
+ *
+ * DNS escaping uses form   \123 = ASCII value 123 (decimal)
+ * LDAP escaping users form \7b  = ASCII value 7b (hexadecimal)
+ *
+ * Input (DNS escaped) example  : _aaa,bbb\255\000ccc.555.ddd-eee
+ * Output (LDAP escaped) example: _aaa\2cbbb\ff\00ccc.555.ddd-eee
+ *
+ * The DNS to text functions from ISC libraries do not convert certain
+ * characters (e.g. ,). This function converts \123 form to \7b form in all
+ * cases. Other characters (not escaped by ISC libraries) will be additionally
+ * converted to the LDAP escape form.
+ * Input characters [a-zA-Z0-9._-] are left in raw ASCII form.
+ *
+ * If dns_str consists only of the characters in the [a-zA-Z0-9._-] set, it
+ * will be checked  copied to the output buffer, without any additional escaping.
+ */
+isc_result_t
+dns_to_ldap_dn_escape(isc_mem_t *mctx, const char const * dns_str, char ** ldap_name) {
+	isc_result_t result = ISC_R_FAILURE;
+	char * esc_name = NULL;
+	int idx_symb_first = -1; /* index of first nice printable symbol in dns_str */
+	int dns_idx = 0;
+	int esc_idx = 0;
+
+	REQUIRE(dns_str);
+	REQUIRE(ldap_name != NULL  *ldap_name == NULL);
+
+	int dns_str_len = strlen(dns_str);
+
+	/**
+	 * In worst case each symbol from DNS dns_str will be represented
+	 * as \xy in ldap_name. (xy are hexadecimal digits)
+	 */
+	CHECKED_MEM_ALLOCATE(mctx, *ldap_name, 3*dns_str_len+1);
+	esc_name = *ldap_name;
+
+	for (dns_idx = 0; dns_idx  dns_str_len; dns_idx++) {
+		if (isalnum(dns_str[dns_idx]) || dns_str[dns_idx] == '.'
+|| dns_str[dns_idx] == '-' || dns_str[dns_idx] == '_' ) {
+			if (idx_symb_first == -1)
+idx_symb_first = dns_idx;
+			continue;
+		} else { /* some not very nice symbols */
+			int ascii_val;
+			if (idx_symb_first != -1) { /* copy previous nice part */
+int length_ok = dns_idx-idx_symb_first;
+memcpy(esc_name + esc_idx, dns_str + idx_symb_first, length_ok);
+esc_idx += length_ok;
+idx_symb_first = -1;
+			}
+			if (dns_str[dns_idx] != '\\') { /* not nice raw value, e.g. ',' */
+ascii_val = dns_str[dns_idx];
+			} else { /* not nice value in DNS \123 decimal format */
+/* check if input length = expected size */
+if (dns_str_len = dns_idx+3) { /* this should never happen */
+	log_bug(dns string too short);
+	result = ISC_R_NOSPACE;
+	goto cleanup;
+}
+ascii_val = 100*(dns_str[dns_idx+1]-'0')
+		+ 10*(dns_str[dns_idx+2]-'0') + (dns_str[dns_idx+3]-'0');
+dns_idx += 3;
+			}
+			/* LDAP uses \xy escaping. xy represent two hexadecimal digits.*/
+			/* TODO: optimize to bit mask  rotate  dec-hex table? */
+			CHECK(isc_string_printf(esc_name+esc_idx, 4, \\%02x, ascii_val));
+			esc_idx += 3; /* isc_string_printf wrote 4 bytes including '\0' */
+		}
+	}
+	if (idx_symb_first != -1) { /* copy last nice part */
+		int length_ok = dns_idx-idx_symb_first;
+		memcpy(esc_name + esc_idx, dns_str + idx_symb_first, dns_idx-idx_symb_first);
+		esc_idx += length_ok;
+		idx_symb_first = -1;
+	}
+	esc_name[esc_idx] = '\0';
+	return ISC_R_SUCCESS;
+
+cleanup:
+	if (*ldap_name) isc_mem_free(mctx, *ldap_name);
+	return result;
+}
+
 static isc_result_t
 explode_dn(const char *dn, char ***explodedp, int 

Re: [Freeipa-devel] [PATCH 0019] Add proper DN escaping before LDAP library calls

2012-05-03 Thread Petr Spacek

On 05/03/2012 11:25 AM, Petr Spacek wrote:

Hello,

this patch adds missing DNS-LDAP escaping conversion. It's necessary to
prevent (potential) LDAP injection attacks in future.

Code isn't very nice, because DNS users decimal escaping \123, LDAP uses
hexadecimal escaping \ab and set of escaped characters is smaller in DNS than
in LDAP.

Any improvements are welcome.

Petr^2 Spacek


Here is second version of the patch.

Changes:
- comments
- order of [._-] in if()
- function was renamed to dns_to_ldap_dn_escape()

Escaping logic itself wasn't changed.

Petr^2 Spacek
From 0291e1b63e077af06b84374c7aa0b15bd71aeb7d Mon Sep 17 00:00:00 2001
From: Petr Spacek pspa...@redhat.com
Date: Mon, 23 Apr 2012 11:38:43 +0200
Subject: [PATCH] Add proper DN escaping before LDAP library calls.
 Signed-off-by: Petr Spacek pspa...@redhat.com

---
 src/ldap_convert.c  |  105 --
 src/zone_register.c |7 +++
 src/zone_register.h |3 +
 3 files changed, 110 insertions(+), 5 deletions(-)

diff --git a/src/ldap_convert.c b/src/ldap_convert.c
index 6405a98fda942cbba10b4c862351c4e3695aba85..1e7c31097b532a8641a34de589ef0926df6bf45f 100644
--- a/src/ldap_convert.c
+++ b/src/ldap_convert.c
@@ -21,6 +21,7 @@
 #include isc/buffer.h
 #include isc/mem.h
 #include isc/util.h
+#include isc/string.h
 
 #include dns/name.h
 #include dns/rdatatype.h
@@ -32,6 +33,7 @@
 
 #include errno.h
 #include strings.h
+#include ctype.h
 
 #include str.h
 #include ldap_convert.h
@@ -189,6 +191,94 @@ cleanup:
 	return result;
 }
 
+/**
+ * Convert a string from DNS escaping to LDAP escaping.
+ * The Input string dns_str is expected to be the result of dns_name_tostring().
+ * The DNS label can contain any binary data as described in
+ * http://tools.ietf.org/html/rfc2181#section-11 .
+ *
+ * DNS escaping uses form   \123 = ASCII value 123 (decimal)
+ * LDAP escaping users form \7b  = ASCII value 7b (hexadecimal)
+ *
+ * Input (DNS escaped) example  : _aaa,bbb\255\000ccc.555.ddd-eee
+ * Output (LDAP escaped) example: _aaa\2cbbb\ff\00ccc.555.ddd-eee
+ *
+ * The DNS to text functions from ISC libraries do not convert certain
+ * characters (e.g. ,). This function converts \123 form to \7b form in all
+ * cases. Other characters (not escaped by ISC libraries) will be additionally
+ * converted to the LDAP escape form.
+ * Input characters [a-zA-Z0-9._-] are left in raw ASCII form.
+ *
+ * If dns_str consists only of the characters in the [a-zA-Z0-9._-] set, it
+ * will be checked  copied to the output buffer, without any additional escaping.
+ */
+isc_result_t
+dns_to_ldap_dn_escape(isc_mem_t *mctx, const char const * dns_str, char ** ldap_name) {
+	isc_result_t result = ISC_R_FAILURE;
+	char * esc_name = NULL;
+	int idx_symb_first = -1; /* index of first nice printable symbol in dns_str */
+	int dns_idx = 0;
+	int esc_idx = 0;
+
+	REQUIRE(dns_str);
+	REQUIRE(ldap_name != NULL  *ldap_name == NULL);
+
+	int dns_str_len = strlen(dns_str);
+
+	/**
+	 * In worst case each symbol from DNS dns_str will be represented
+	 * as \xy in ldap_name. (xy are hexadecimal digits)
+	 */
+	CHECKED_MEM_ALLOCATE(mctx, *ldap_name, 3*dns_str_len+1);
+	esc_name = *ldap_name;
+
+	for (dns_idx = 0; dns_idx  dns_str_len; dns_idx++) {
+		if (isalnum(dns_str[dns_idx]) || dns_str[dns_idx] == '.'
+|| dns_str[dns_idx] == '-' || dns_str[dns_idx] == '_' ) {
+			if (idx_symb_first == -1)
+idx_symb_first = dns_idx;
+			continue;
+		} else { /* some not very nice symbols */
+			int ascii_val;
+			if (idx_symb_first != -1) { /* copy previous nice part */
+int length_ok = dns_idx-idx_symb_first;
+memcpy(esc_name + esc_idx, dns_str + idx_symb_first, length_ok);
+esc_idx += length_ok;
+idx_symb_first = -1;
+			}
+			if (dns_str[dns_idx] != '\\') { /* not nice raw value, e.g. ',' */
+ascii_val = dns_str[dns_idx];
+			} else { /* not nice value in DNS \123 decimal format */
+/* check if input length = expected size */
+if (dns_str_len = dns_idx+3) { /* this should never happen */
+	log_bug(dns string too short);
+	result = ISC_R_NOSPACE;
+	goto cleanup;
+}
+ascii_val = 100*(dns_str[dns_idx+1]-'0')
+		+ 10*(dns_str[dns_idx+2]-'0') + (dns_str[dns_idx+3]-'0');
+dns_idx += 3;
+			}
+			/* LDAP uses \xy escaping. xy represent two hexadecimal digits.*/
+			/* TODO: optimize to bit mask  rotate  dec-hex table? */
+			CHECK(isc_string_printf(esc_name+esc_idx, 4, \\%02x, ascii_val));
+			esc_idx += 3; /* isc_string_printf wrote 4 bytes including '\0' */
+		}
+	}
+	if (idx_symb_first != -1) { /* copy last nice part */
+		int length_ok = dns_idx-idx_symb_first;
+		memcpy(esc_name + esc_idx, dns_str + idx_symb_first, dns_idx-idx_symb_first);
+		esc_idx += length_ok;
+		idx_symb_first = -1;
+	}
+	esc_name[esc_idx] = '\0';
+	return ISC_R_SUCCESS;
+
+cleanup:
+	if (*ldap_name) isc_mem_free(mctx, *ldap_name);
+	return result;
+}
+
 static isc_result_t
 explode_dn(const char *dn, char ***explodedp, int