[Exim] RFC 1485 compliant LDAP DN quoting

Top Page
Delete this message
Reply to this message
Author: Marian Eichholz
Date:  
To: exim-users
Subject: [Exim] RFC 1485 compliant LDAP DN quoting
Hello,

most probably, ldap_quote_dn in exim 4.14 does *not*, what RFC 1485
recommends as DN string representation.

The ABNF encourages a sedecimal (hex) encoding for whole Attribute keys or
values. This sacrifices flexibility (you always have to quote whole keys or
values) for the sake of robustness (even against URL encoding :-)).

Have a look at the patch below with introduces a "new" eldap_quote_dn() and
eliminates the special DN encoding part in eldap_quote().

Please let me know, if I am right with this approach.

Here are some string tests with this encoding:

./exim -be '${quote_ldap_dn: marian}'
%23206D617269616E

./exim -be '${quote_ldap_dn:marian}'
marian

./exim -be '${quote_ldap_dn:marian010}'
marian010

./exim -be '${quote_ldap_dn:marian010,5}'
%236D617269616E3031302C35

--- src/lookups/ldap-ph.c    Wed Mar 26 17:01:00 2003
+++ src/lookups/ldap.c    Fri Mar 28 14:36:49 2003
@@ -1124,30 +1124,23 @@


       , + " \ < > ;


-    must be quoted by backslashing. Leading and trailing spaces must be
-    escaped, as must a leading #. Then the string must be URL-quoted. This type
+    must be quoted to a hexstring leaded by a hash-sign.
+    See RFC 1485 for details.
+    Then the string must be URL-quoted. This type
     of quoting is implemented by ${quote_ldap_dn:....}.
+    The hash-sign must be hexified and preceded by %.


-For URL quoting, the only characters that need not be quoted are the
-alphamerics and
-
- ! $ ' ( ) * + - . _
-
-All the others must be hexified and preceded by %. This includes the
-backslashes used for LDAP quoting.
-
-For a DN that is given in the USER parameter for authentication, we need the
-same initial quoting as (2) but in this case, the result must NOT be
-URL-escaped, because it isn't a URL. The way this is handled is by
-de-URL-quoting the text when processing the USER parameter in
-control_ldap_search() above. That means that the same quote operator can be
-used. This has the additional advantage that spaces in the DN won't cause
-parsing problems. For example:
+For a DN that is given in the USER parameter for authentication, we
+need the same initial quoting as (2) but in this case, the result (the
+hash sign) must NOT be URL-escaped, because it isn't a URL. The way
+this is handled is by de-URL-quoting the text when processing the USER
+parameter in control_ldap_search() above. That means that the same
+quote operator can be used. This has the additional advantage that
+spaces in the DN won't cause parsing problems. For example:

USER=cn=${quote_ldap_dn:$1},%20dc=example,%20dc=com

-should be safe if there are spaces in $1.
-
+NOTE: RFC 1485 allows for the relative safe hex string encoding only for *whole* strings. There is no characterwise quoting (except raw hexification of each character and manual prefixing with the hash sign).

 Arguments:
   s          the string to be quoted
@@ -1173,9 +1166,41 @@
 quote_ldap_dn, respectively. */


 #define LDAP_QUOTE      "*()\\"
-#define LDAP_DN_QUOTE   ",+\"\\<>;"
-


+uschar *
+eldap_quote_dn(uschar *s)
+{
+  uschar *quoted;
+  uschar *t;
+  uschar len=0;
+  t=s;
+  int keep_unquoted=1;
+  while (*t)
+   {
+     len++;
+     if (keep_unquoted && !isalnum(*t)) keep_unquoted=0;
+     t++;
+   }
+  if (keep_unquoted)
+  {
+    quoted=store_get(len+1);
+    strcpy(quoted,s);
+    return quoted;
+  }
+  t = quoted = store_get(2*len+4); /* 3 for the hash and 1 for NUL */
+  if (!quoted) return NULL;
+  if (!len) {
+    *t='\0';
+    return quoted;
+  }
+  sprintf(t,"%%%02X",'#'); t+=3; /* escape the hash sign for URL encoding */
+  while (*s)
+  {
+    sprintf(t,"%02X",*s++);
+    t+=2;
+  }
+  return quoted;
+}


uschar *
eldap_quote(uschar *s, uschar *opt)
@@ -1183,7 +1208,6 @@
register int c;
int count = 0;
int len;
-BOOL dn = FALSE;
uschar *t = s;
uschar *quoted;

@@ -1192,7 +1216,7 @@
 if (opt != NULL)
   {
   if (Ustrcmp(opt, "dn") != 0) return NULL;    /* No others recognized */
-  dn = TRUE;
+  return eldap_quote_dn(s);
   }


/* Compute how much extra store we need for the string. This doesn't have to be
@@ -1217,8 +1241,6 @@

/* Handle plain quote_ldap */

-if (!dn)
-  {
   while ((c = *s++) != 0)
     {
     if (!isalnum(c))
@@ -1238,57 +1260,6 @@
       }
     *t++ = c;                                /* unquoted character */
     }
-  }
-
-/* Handle quote_ldap_dn */
-
-else
-  {
-  uschar *ss = s + len;
-
-  /* Find the last char before any trailing spaces */
-
-  while (ss > s && ss[-1] == ' ') ss--;
-
-  /* Quote leading spaces and sharps */
-
-  for (; s < ss; s++)
-    {
-    if (*s != ' ' && *s != '#') break;
-    sprintf(CS t, "%%5C%%%02X", *s);
-    t += 6;
-    }
-
-  /* Handle the rest of the string, up to the trailing spaces */
-
-  while (s < ss)
-    {
-    c = *s++;
-    if (!isalnum(c))
-      {
-      if (Ustrchr(LDAP_DN_QUOTE, c) != NULL)
-        {
-        Ustrncpy(t, "%5C", 3);               /* insert \ where needed */
-        t += 3;                              /* fall through to check URL */
-        }
-      if (Ustrchr(URL_NONQUOTE, c) == NULL)  /* e.g. ] => %5D */
-        {
-        sprintf(CS t, "%%%02X", c);
-        t += 3;
-        continue;
-        }
-      }
-    *t++ = c;    /* unquoted character, or non-URL quoted after %5C */
-    }
-
-  /* Handle the trailing spaces */
-
-  while (*ss++ != 0)
-    {
-    Ustrncpy(t, "%5C%20", 6);
-    t += 6;
-    }
-  }


/* Terminate the new string and return */

--
Mit freundlichen Gruessen / Yours sincerely

Marian Eichholz
Postmaster
freenet.de AG          Vorsitzender des Aufsichtsrates: Prof. Dr. Helmut Thoma
Deelbögenkamp 4c       Vorstand: Eckhard Spoerr (Vors.), Axel Krieger
22297 Hamburg          Amtsgericht Hamburg, HRB 74048