krb5 commit: Fix quoting issues in LDAP KDB module

Greg Hudson ghudson at MIT.EDU
Sat Nov 17 15:44:15 EST 2012


https://github.com/krb5/krb5/commit/85898e8f1c9e4f5bff70e1ff810519363b262eb4
commit 85898e8f1c9e4f5bff70e1ff810519363b262eb4
Author: Greg Hudson <ghudson at mit.edu>
Date:   Sat Nov 17 15:30:32 2012 -0500

    Fix quoting issues in LDAP KDB module
    
    Modify ldap_filter_correct() to quote special characters for DN
    strings as well as filters, since it is already used to quote a DN
    string in krb5_ldap_name_to_policydn() and there's no harm in
    over-quoting.  In krb5_ldap_put_principal(), quote the unparsed
    principal name for use in DNs we choose.  In
    krb5_ldap_create_password_policy(), use the policy name for the CN of
    the policy entry instead of the (possibly quoted) first element of the
    DN.
    
    Adapted from a patch by Jim Shi <hanmao_shi at apple.com>.
    
    ticket: 7296

 src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c |   18 ++++--
 src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c |   16 +----
 src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.c      |   77 ++++----------------
 src/tests/kdbtest.c                                |   34 +++++----
 src/tests/t_kdb.py                                 |    2 -
 5 files changed, 48 insertions(+), 99 deletions(-)

diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
index a7101a7..c386a9e 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
@@ -496,6 +496,7 @@ krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
     LDAPMessage                 *result=NULL, *ent=NULL;
     char                        *user=NULL, *subtree=NULL, *principal_dn=NULL;
     char                        **values=NULL, *strval[10]={NULL}, errbuf[1024];
+    char                        *filtuser=NULL;
     struct berval               **bersecretkey=NULL;
     LDAPMod                     **mods=NULL;
     krb5_boolean                create_standalone_prinicipal=FALSE;
@@ -534,6 +535,11 @@ krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
         if (((st=krb5_unparse_name(context, entry->princ, &user)) != 0) ||
             ((st=krb5_ldap_unparse_principal_name(user)) != 0))
             goto cleanup;
+        filtuser = ldap_filter_correct(user);
+        if (filtuser == NULL) {
+            st = ENOMEM;
+            goto cleanup;
+        }
     }
 
     /* Identity the type of operation, it can be
@@ -554,7 +560,7 @@ krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
         goto cleanup;
 
     if (entry->mask & KADM5_LOAD) {
-        int              tree = 0, ntrees = 0, princlen = 0, numlentries = 0;
+        int              tree = 0, ntrees = 0, numlentries = 0;
         char             **subtreelist = NULL, *filter = NULL;
 
         /*  A load operation is special, will do a mix-in (add krbprinc
@@ -572,12 +578,11 @@ krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
                                      "name not found"));
             goto cleanup;
         }
-        princlen = strlen(FILTER) + strlen(user) + 2 + 1;      /* 2 for closing brackets */
-        if ((filter = malloc(princlen)) == NULL) {
+        if (asprintf(&filter, FILTER"%s))", filtuser) < 0) {
+            filter = NULL;
             st = ENOMEM;
             goto cleanup;
         }
-        snprintf(filter, princlen, FILTER"%s))", user);
 
         /* get the current subtree list */
         if ((st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees)) != 0)
@@ -684,7 +689,7 @@ krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
         CHECK_NULL(subtree);
 
         if (asprintf(&standalone_principal_dn, "krbprincipalname=%s,%s",
-                     user, subtree) < 0)
+                     filtuser, subtree) < 0)
             standalone_principal_dn = NULL;
         CHECK_NULL(standalone_principal_dn);
         /*
@@ -1262,6 +1267,9 @@ cleanup:
     if (user)
         free(user);
 
+    if (filtuser)
+        free(filtuser);
+
     free_xargs(xargs);
 
     if (standalone_principal_dn)
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c
index 09cfb8c..e955f8e 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.c
@@ -140,7 +140,7 @@ krb5_ldap_create_password_policy(krb5_context context, osa_policy_ent_t policy)
     kdb5_dal_handle             *dal_handle=NULL;
     krb5_ldap_context           *ldap_context=NULL;
     krb5_ldap_server_handle     *ldap_server_handle=NULL;
-    char                        **rdns=NULL, *strval[2]={NULL}, *policy_dn;
+    char                        *strval[2]={NULL}, *policy_dn;
 
     /* Clear the global error string */
     krb5_clear_error_message(context);
@@ -156,16 +156,7 @@ krb5_ldap_create_password_policy(krb5_context context, osa_policy_ent_t policy)
     if (st != 0)
         goto cleanup;
 
-    /* get the first component of the dn to set the cn attribute */
-    rdns = ldap_explode_dn(policy_dn, 1);
-    if (rdns == NULL) {
-        st = EINVAL;
-        krb5_set_error_message(context, st,
-                               _("Invalid password policy DN syntax"));
-        goto cleanup;
-    }
-
-    strval[0] = rdns[0];
+    strval[0] = policy->name;
     if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0)
         goto cleanup;
 
@@ -184,9 +175,6 @@ krb5_ldap_create_password_policy(krb5_context context, osa_policy_ent_t policy)
     }
 
 cleanup:
-    if (rdns)
-        ldap_value_free(rdns);
-
     if (policy_dn != NULL)
         free (policy_dn);
     ldap_mods_free(mods, 1);
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.c
index 45649da..7e0d456 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.c
@@ -70,72 +70,25 @@ char  *krbContainerRefclass[] = { "krbContainerRefAux", NULL};
  * list realms from eDirectory
  */
 
-/*
- * Function to remove all special characters from a string (rfc2254).
- * Use whenever exact matching is to be done ...
- */
+/* Return a copy of in, quoting all characters which are special in an LDAP
+ * filter (RFC 4515) or DN string (RFC 4514).  Return NULL on failure. */
 char *
 ldap_filter_correct (char *in)
 {
-    size_t i, count;
-    char *out, *ptr;
-    size_t len = strlen(in);
-
-    for (i = 0, count = 0; i < len; i++)
-        switch (in[i]) {
-        case '*':
-        case '(':
-        case ')':
-        case '\\':
-        case '\0':
-            count ++;
-        }
-
-    out = (char *)malloc((len + (count * 2) + 1) * sizeof (char));
-    assert (out != NULL);
-    memset(out, 0, len + (count * 2) + 1);
-
-    for (i = 0, ptr = out; i < len; i++)
-        switch (in[i]) {
-        case '*':
-            ptr[0] = '\\';
-            ptr[1] = '2';
-            ptr[2] = 'a';
-            ptr += 3;
-            break;
-        case '(':
-            ptr[0] = '\\';
-            ptr[1] = '2';
-            ptr[2] = '8';
-            ptr += 3;
-            break;
-        case ')':
-            ptr[0] = '\\';
-            ptr[1] = '2';
-            ptr[2] = '9';
-            ptr += 3;
-            break;
-        case '\\':
-            ptr[0] = '\\';
-            ptr[1] = '5';
-            ptr[2] = 'c';
-            ptr += 3;
+    size_t count;
+    const char special[] = "*()\\ #\"+,;<>";
+    struct k5buf buf;
+
+    krb5int_buf_init_dynamic(&buf);
+    while (TRUE) {
+        count = strcspn(in, special);
+        krb5int_buf_add_len(&buf, in, count);
+        in += count;
+        if (*in == '\0')
             break;
-        case '\0':
-            ptr[0] = '\\';
-            ptr[1] = '0';
-            ptr[2] = '0';
-            ptr += 3;
-            break;
-        default:
-            ptr[0] = in[i];
-            ptr += 1;
-            break;
-        }
-
-    /* ptr[count - 1] = '\0'; */
-
-    return out;
+        krb5int_buf_add_fmt(&buf, "\\%2x", (unsigned char)*in++);
+    }
+    return krb5int_buf_data(&buf);
 }
 
 static int
diff --git a/src/tests/kdbtest.c b/src/tests/kdbtest.c
index 11c433d..b569b56 100644
--- a/src/tests/kdbtest.c
+++ b/src/tests/kdbtest.c
@@ -67,8 +67,8 @@ check_cond(int value, int lineno)
 }
 
 static krb5_data princ_data[2] = {
-    { KV5M_DATA, 3, "xyz" },
-    { KV5M_DATA, 3, "abc" }
+    { KV5M_DATA, 6, "xy*(z)" },
+    { KV5M_DATA, 12, "+<> *()\\#\",;" }
 };
 
 static krb5_principal_data sample_princ = {
@@ -88,14 +88,14 @@ static krb5_principal_data xrealm_princ = {
 /*
  * tl1 through tl4 are normalized to attributes in the LDAP back end.  tl5 is
  * stored as untranslated tl-data.  tl3 contains an encoded osa_princ_ent with
- * a policy reference to "testpol".
+ * a policy reference to "<test*>".
  */
 static krb5_tl_data tl5 = { NULL, KRB5_TL_MKVNO, 2, U("\0\1") };
 static krb5_tl_data tl4 = { &tl5, KRB5_TL_LAST_ADMIN_UNLOCK, 4,
                             U("\6\0\0\0") };
 static krb5_tl_data tl3 = { &tl4, KRB5_TL_KADM_DATA, 32,
                             U("\x12\x34\x5C\x01\x00\x00\x00\x08"
-                              "\x74\x65\x73\x74\x70\x6F\x6C\x00"
+                              "\x3C\x74\x65\x73\x74\x2A\x3E\x00"
                               "\x00\x00\x08\x00\x00\x00\x00\x00"
                               "\x00\x00\x00\x02\x00\x00\x00\x00") };
 static krb5_tl_data tl2 = { &tl3, KRB5_TL_MOD_PRINC, 8, U("\5\6\7\0x at Y\0") };
@@ -131,6 +131,8 @@ static krb5_key_data keys[] = {
 };
 #undef U
 
+static char polname[] = "<test*>";
+
 static krb5_db_entry sample_entry = {
     0,
     KRB5_KDB_V1_BASE_LENGTH,
@@ -159,7 +161,7 @@ static krb5_db_entry sample_entry = {
 
 static osa_policy_ent_rec sample_policy = {
     0,                          /* version */
-    "testpol",                  /* name */
+    polname,                    /* name */
     1357,                       /* pw_min_life */
     100,                        /* pw_max_life */
     6,                          /* pw_min_length */
@@ -294,24 +296,24 @@ main()
     CHECK(krb5_db_open(ctx, NULL, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN));
     CHECK(krb5_db_inited(ctx));
 
-    /* Manipulate a policy, leaving testpol in place at the end. */
+    /* Manipulate a policy, leaving it in place at the end. */
     CHECK_COND(krb5_db_put_policy(ctx, &sample_policy) != 0);
-    CHECK_COND(krb5_db_delete_policy(ctx, "testpol") != 0);
-    CHECK_COND(krb5_db_get_policy(ctx, "testpol", &pol) == KRB5_KDB_NOENTRY);
+    CHECK_COND(krb5_db_delete_policy(ctx, polname) != 0);
+    CHECK_COND(krb5_db_get_policy(ctx, polname, &pol) == KRB5_KDB_NOENTRY);
     CHECK(krb5_db_create_policy(ctx, &sample_policy));
     CHECK_COND(krb5_db_create_policy(ctx, &sample_policy) != 0);
-    CHECK(krb5_db_get_policy(ctx, "testpol", &pol));
+    CHECK(krb5_db_get_policy(ctx, polname, &pol));
     check_policy(pol);
     pol->pw_min_length--;
     CHECK(krb5_db_put_policy(ctx, pol));
     krb5_db_free_policy(ctx, pol);
-    CHECK(krb5_db_get_policy(ctx, "testpol", &pol));
+    CHECK(krb5_db_get_policy(ctx, polname, &pol));
     CHECK_COND(pol->pw_min_length == sample_policy.pw_min_length - 1);
     krb5_db_free_policy(ctx, pol);
-    CHECK(krb5_db_delete_policy(ctx, "testpol"));
+    CHECK(krb5_db_delete_policy(ctx, polname));
     CHECK_COND(krb5_db_put_policy(ctx, &sample_policy) != 0);
-    CHECK_COND(krb5_db_delete_policy(ctx, "testpol") != 0);
-    CHECK_COND(krb5_db_get_policy(ctx, "testpol", &pol) == KRB5_KDB_NOENTRY);
+    CHECK_COND(krb5_db_delete_policy(ctx, polname) != 0);
+    CHECK_COND(krb5_db_get_policy(ctx, polname, &pol) == KRB5_KDB_NOENTRY);
     CHECK(krb5_db_create_policy(ctx, &sample_policy));
     count = 0;
     CHECK(krb5_db_iter_policy(ctx, NULL, iter_pol_handler, &count));
@@ -375,8 +377,8 @@ main()
     CHECK(krb5_dbe_update_tl_data(ctx, ent, &tl_no_policy));
     ent->mask = KADM5_POLICY_CLR | KADM5_KEY_DATA;
     CHECK(krb5_db_put_principal(ctx, ent));
-    /* Deleting testpol should work now that the reference is gone. */
-    CHECK(krb5_db_delete_policy(ctx, "testpol"));
+    /* Deleting polname should work now that the reference is gone. */
+    CHECK(krb5_db_delete_policy(ctx, polname));
 
     /* Put the modified entry again (with KDB_TL_USER_INFO tl-data for LDAP) as
      * from a load operation. */
@@ -391,7 +393,7 @@ main()
 
     /* Exercise principal iteration code. */
     count = 0;
-    CHECK(krb5_db_iterate(ctx, "xyz*", iter_princ_handler, &count));
+    CHECK(krb5_db_iterate(ctx, "xy*", iter_princ_handler, &count));
     CHECK_COND(count == 1);
 
     CHECK(krb5_db_fini(ctx));
diff --git a/src/tests/t_kdb.py b/src/tests/t_kdb.py
index dd79b42..2f0d6fd 100644
--- a/src/tests/t_kdb.py
+++ b/src/tests/t_kdb.py
@@ -264,8 +264,6 @@ if out:
 # We could still use tests to exercise:
 # * DB arg handling in krb5_ldap_create
 # * krbAllowedToDelegateTo attribute processing
-# * Special character handling in ldap_filter_correct (some bugs to
-#   fix first, see #7296 and September 2012 krbdev discussion)
 # * A load operation overwriting a standalone principal entry which
 #   already exists but doesn't have a krbPrincipalName attribute
 #   matching the principal name.


More information about the cvs-krb5 mailing list