ldb: Add ldb_dn_compare_base_one for checking if a dn is just bellow the base
authorMatthieu Patou <mat@matws.net>
Sun, 30 Dec 2012 05:47:29 +0000 (21:47 -0800)
committerMatthieu Patou <mat@matws.net>
Wed, 27 May 2015 05:01:37 +0000 (22:01 -0700)
Change-Id: I5626ef10f7c9ef28a1427ab309555c300fb696cd

lib/ldb/common/ldb_dn.c
lib/ldb/include/ldb.h

index ed7e5549eb406450a72a22a985eb4b6efa68fe95..85401d308b637d36c716e97745da45ce4ef133d3 100644 (file)
@@ -1054,12 +1054,164 @@ char *ldb_dn_alloc_casefold(TALLOC_CTX *mem_ctx, struct ldb_dn *dn)
        return talloc_strdup(mem_ctx, ldb_dn_get_casefold(dn));
 }
 
-/* Determine if dn is below base, in the ldap tree.  Used for
- * evaluating a subtree search.
- * 0 if they match, otherwise non-zero
+/**
+ * @brief Compare the linerarized part of DNs and see if the dn match the base one
+ *
+ * This function check if dn DN match the base DN, it assumes that both DN have
+ * a linearized version. There is no good reason to use this function directly
+ * it has been abstracted to a function in order to make the
+ * function ldb_dn_compare_base_internal more readable, you should use this one
+ * instead.
+ *
+ * @param[in]  base            The base DN that is checked against
+ *
+ * @param[in]  dn              The DN to test
+ *
+ * @param[in]  onelevel        A boolean to indicate if we validate only
+ *                             DN that are just 1 level bellow the DN
+ *
+ * @return                     0 if the DN is bellow (as influenced by the
+ *                             onelevel parameter), > 0 if it's not, and < 0
+ *                             if a check on each componenent is needed
  */
+static int ldb_dn_linearized_compare_base(struct ldb_dn *base, struct ldb_dn *dn,
+                                         bool onelevel)
+{
+       int dif;
+       int i;
+       bool comma_found = false;
+       dif = strlen(dn->linearized) - strlen(base->linearized);
+       if (dif < 0) {
+               return 1;
+       }
+       if (!base->linearized[0]) {
+               if (onelevel) {
+                       return 1;
+               } else {
+                       return 0;
+               }
+       }
 
-int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
+       if (onelevel && dif == 0) {
+               /*
+                * dn has the same length as base, and we were
+                * asked for comparing only onelevel so this
+                * can't be ok whatever the comparison of the string returns
+                */
+               return 1;
+       }
+
+       if (strcmp(base->linearized, &dn->linearized[dif]) == 0) {
+               if (dif == 0) {
+                       /*
+                        * Same length, same content, not onelevel search
+                        * that's ok, matched.
+                        */
+                       return 0;
+               }
+
+               /*
+                * So far we know that len(dn) > len(base) and that
+                * (char*)(dn->linearized+dif) match base->linearized
+                * We can have 0 or more comma ',' if it's 0 it's not a match
+                * (it's for instance dn: adc=sambacorp and base dc=sambacorp)
+                *
+                * As we already matched the string in base->linearized, we
+                * know that dn is a child of base only if chars between the
+                * last comma in the non matched part (from 0 to dif) and the
+                * end of the matched part are spaces.
+                * If there is comma it's also not a match.
+                *
+                * If there is more than 1 comma and if onelevel is true then
+                * it might be ok but in order to be sure and avoid costly
+                * checks for the vast majority of onelevel, we return -1,
+                * leaving it for more complete checks.
+                */
+               i = dif - 1;
+               /*
+                * Loop starting from then of the difference string
+                * we break if we found something else than a space or a comma
+                * ie. with basedn: dc=sambacorp
+                * if we have
+                * cn="foobar"dc=sambacorp
+                * or
+                * bdc=sambacorp
+                * or
+                * a=truc dc=sambacorp
+                */
+               while ((i > 0) && (!comma_found)) {
+                       /* ',' or ' ' => ok, else fail */
+                       if (dn->linearized[i] == ',') {
+                               comma_found = true;
+                               break;
+                       }
+                       if (dn->linearized[i] == ' ') {
+                               i--;
+                               continue;
+                       }
+                       break;
+               }
+               /*
+                * No comma, we already took care of dn1 == dn2 before
+                * so this can't be good
+                */
+               if (!comma_found) {
+                       return 1;
+               }
+
+               /*
+                * Ok at least one separating comma and we are not doing a
+                * one level match so it's ok
+                */
+               if (!onelevel) {
+                       return 0;
+               }
+               comma_found = false;
+               while ((i > 0) && (!comma_found)) {
+                       if (dn->linearized[i] == ',' &&
+                           dn->linearized[i-1] != '\\') {
+                               comma_found = true;
+                               break;
+                       }
+                       i--;
+               }
+               if (comma_found) {
+                       /*
+                        * Ok we are not sure, it looks like we have more than
+                        * one ',' but it might be in between quote
+                        * so we return -1, so the caller know that it
+                        * has to use a more complex way of comparing
+                        */
+                       return -1;
+               } else {
+                       return 0;
+               }
+       }
+
+       /*
+        * dn can differ in the case and still be the same so let's leave
+        * it to the caller to do a more complete check
+        */
+       return -1;
+}
+/**
+ * @brief compare if a DN is bellow the base
+ *
+ * A DN is said to be bellow another if the last n components of it are the same
+ * as the n component of the base DN
+ *
+ * @param[in]  base            The base DN that is checked against
+ *
+ * @param[in]  dn              The DN to test
+ *
+ * @param[in]  onelevel        A boolean to indicate if we validate only
+ *                             DN that are just 1 level bellow the DN
+ *
+ * @return                     0 if the DN is bellow (as influenced by the
+ *                             onelevel parameter), non zero otherwise
+ */
+static int ldb_dn_compare_base_internal(struct ldb_dn *base, struct ldb_dn *dn,
+                                       bool onelevel)
 {
        int ret;
        unsigned int n_base, n_dn;
@@ -1069,24 +1221,21 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
 
        if (( ! base->valid_case) || ( ! dn->valid_case)) {
                if (base->linearized && dn->linearized && dn->special == base->special) {
-                       /* try with a normal compare first, if we are lucky
-                        * we will avoid exploding and casfolding */
-                       int dif;
-                       dif = strlen(dn->linearized) - strlen(base->linearized);
-                       if (dif < 0) {
-                               return dif;
-                       }
-                       if (strcmp(base->linearized,
-                                  &dn->linearized[dif]) == 0) {
-                               return 0;
+                       /*
+                        * try with a normal compare first, if we are lucky
+                        * we will avoid exploding and casefolding
+                        */
+                       ret = ldb_dn_linearized_compare_base(base, dn, onelevel);
+                       if (ret >=0 ) {
+                               return ret;
                        }
                }
 
-               if ( ! ldb_dn_casefold_internal(base)) {
+               if (!ldb_dn_casefold_internal(base)) {
                        return 1;
                }
 
-               if ( ! ldb_dn_casefold_internal(dn)) {
+               if (!ldb_dn_casefold_internal(dn)) {
                        return -1;
                }
 
@@ -1098,6 +1247,12 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
                return (dn->comp_num - base->comp_num);
        }
 
+       if (onelevel && (dn->comp_num != (base->comp_num + 1))) {
+               int v = dn->comp_num - base->comp_num;
+
+               return v == 0 ? 1: v;
+       }
+
        if ((dn->comp_num == 0) || (base->comp_num == 0)) {
                if (dn->special && base->special) {
                        return strcmp(base->linearized, dn->linearized);
@@ -1141,6 +1296,36 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
        return 0;
 }
 
+/**
+ * @brief Determine if DN is bellow base DN
+ *
+ * @param[in]  base        The base DN of the comparison
+ *
+ * @param[in]  dn          The DN to test
+ *
+ * @return                 0 if the DN is bellow base in the ldap tree,
+ *                         otherwise non-zero
+ */
+int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
+{
+       return ldb_dn_compare_base_internal(base, dn, false);
+}
+
+/**
+ * @brief Determine if DN is one level bellow base DN
+ *
+ * @param[in]  base        The base DN of the comparison
+ *
+ * @param[in]  dn          The DN to test
+ *
+ * @return                 0 if the DN is one level bellow base in the ldap tree,
+ *                         otherwise non-zero
+ */
+int ldb_dn_compare_base_one(struct ldb_dn *base, struct ldb_dn *dn)
+{
+       return ldb_dn_compare_base_internal(base, dn, true);
+}
+
 /* compare DNs using casefolding compare functions.
 
    If they match, then return 0
index f48f7537d27b1e36c9986f1f40ce7aca7111313a..8205db8a7a1f8232d96fb8eb71fb24f4b58af5c4 100644 (file)
@@ -1807,6 +1807,7 @@ const char *ldb_dn_get_casefold(struct ldb_dn *dn);
 char *ldb_dn_alloc_casefold(TALLOC_CTX *mem_ctx, struct ldb_dn *dn);
 
 int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn);
+int ldb_dn_compare_base_one(struct ldb_dn *base, struct ldb_dn *dn);
 int ldb_dn_compare(struct ldb_dn *edn0, struct ldb_dn *edn1);
 
 bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base);