LCOV - code coverage report
Current view: top level - lib/ldb-samba - ldb_matching_rules.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 206 287 71.8 %
Date: 2024-04-21 15:09:00 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    ldb database library - Extended match rules
       5             : 
       6             :    Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me>
       7             :    Copyright (C) Andrew Bartlett <abartlet@samba.org>
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : #include "includes.h"
      24             : #include <ldb_module.h>
      25             : #include "dsdb/samdb/samdb.h"
      26             : #include "ldb_matching_rules.h"
      27             : #include "libcli/security/security.h"
      28             : #include "dsdb/common/util.h"
      29             : #include "librpc/gen_ndr/ndr_dnsp.h"
      30             : #include "lib/util/smb_strtox.h"
      31             : 
      32             : #undef strcasecmp
      33             : 
      34        5625 : static int ldb_eval_transitive_filter_helper(TALLOC_CTX *mem_ctx,
      35             :                                              struct ldb_context *ldb,
      36             :                                              const char *attr,
      37             :                                              const struct dsdb_dn *dn_to_match,
      38             :                                              const char *dn_oid,
      39             :                                              struct dsdb_dn *to_visit,
      40             :                                              struct dsdb_dn ***visited,
      41             :                                              unsigned int *visited_count,
      42             :                                              bool *matched)
      43             : {
      44           0 :         TALLOC_CTX *tmp_ctx;
      45           0 :         int ret, i, j;
      46           0 :         struct ldb_result *res;
      47           0 :         struct ldb_message *msg;
      48           0 :         struct ldb_message_element *el;
      49        5625 :         const char *attrs[] = { attr, NULL };
      50             : 
      51        5625 :         tmp_ctx = talloc_new(mem_ctx);
      52        5625 :         if (tmp_ctx == NULL) {
      53           0 :                 return LDB_ERR_OPERATIONS_ERROR;
      54             :         }
      55             : 
      56             :         /*
      57             :          * Fetch the entry to_visit
      58             :          *
      59             :          * NOTE: This is a new LDB search from the TOP of the module
      60             :          * stack.  This means that this search runs the whole stack
      61             :          * from top to bottom.
      62             :          *
      63             :          * This may seem to be in-efficient, but it is also the only
      64             :          * way to ensure that the ACLs for this search are applied
      65             :          * correctly.
      66             :          *
      67             :          * Note also that we don't have the original request
      68             :          * here, so we can not apply controls or timeouts here.
      69             :          */
      70        5625 :         ret = dsdb_search_dn(ldb,
      71             :                              tmp_ctx,
      72             :                              &res,
      73             :                              to_visit->dn,
      74             :                              attrs,
      75             :                              DSDB_MARK_REQ_UNTRUSTED);
      76        5625 :         if (ret != LDB_SUCCESS) {
      77           0 :                 DBG_NOTICE("search failure (%d: %s) looking for '%s' on '%s'\n",
      78             :                            ret,
      79             :                            ldb_strerror(ret),
      80             :                            attr,
      81             :                            ldb_dn_get_linearized(to_visit->dn));
      82           0 :                 talloc_free(tmp_ctx);
      83           0 :                 return ret;
      84             :         }
      85        5625 :         if (res->count != 1) {
      86           0 :                 talloc_free(tmp_ctx);
      87           0 :                 return LDB_ERR_OPERATIONS_ERROR;
      88             :         }
      89        5625 :         msg = res->msgs[0];
      90             : 
      91             :         /* Fetch the attribute to match from the entry being visited */
      92        5625 :         el = ldb_msg_find_element(msg, attr);
      93        5625 :         if (el == NULL) {
      94             :                 /* This entry does not have the attribute to match */
      95        4381 :                 talloc_free(tmp_ctx);
      96        4381 :                 *matched = false;
      97        4381 :                 return LDB_SUCCESS;
      98             :         }
      99             : 
     100             :         /*
     101             :          * If the value to match is present in the attribute values of the
     102             :          * current entry being visited, set matched to true and return OK
     103             :          */
     104        2934 :         for (i=0; i<el->num_values; i++) {
     105           0 :                 struct dsdb_dn *dn;
     106        1955 :                 dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
     107        1955 :                 if (dn == NULL) {
     108           0 :                         talloc_free(tmp_ctx);
     109           0 :                         *matched = false;
     110           0 :                         return LDB_ERR_INVALID_DN_SYNTAX;
     111             :                 }
     112             : 
     113        1955 :                 if (ldb_dn_compare(dn_to_match->dn, dn->dn) == 0) {
     114         265 :                         talloc_free(tmp_ctx);
     115         265 :                         *matched = true;
     116         265 :                         return LDB_SUCCESS;
     117             :                 }
     118             :         }
     119             : 
     120             :         /*
     121             :          * If arrived here, the value to match is not in the values of the
     122             :          * entry being visited. Add the entry being visited (to_visit)
     123             :          * to the visited array. The array is (re)allocated in the parent
     124             :          * memory context.
     125             :          */
     126         979 :         if (visited == NULL) {
     127           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     128         979 :         } else if (*visited == NULL) {
     129         543 :                 *visited = talloc_array(mem_ctx, struct dsdb_dn *, 1);
     130         543 :                 if (*visited == NULL) {
     131           0 :                         talloc_free(tmp_ctx);
     132           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     133             :                 }
     134         543 :                 (*visited)[0] = to_visit;
     135         543 :                 (*visited_count) = 1;
     136             :         } else {
     137         436 :                 *visited = talloc_realloc(mem_ctx, *visited, struct dsdb_dn *,
     138             :                                          (*visited_count) + 1);
     139         436 :                 if (*visited == NULL) {
     140           0 :                         talloc_free(tmp_ctx);
     141           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     142             :                 }
     143         436 :                 (*visited)[(*visited_count)] = to_visit;
     144         436 :                 (*visited_count)++;
     145             :         }
     146             : 
     147             :         /*
     148             :          * steal to_visit into visited array context, as it has to live until
     149             :          * the array is freed.
     150             :          */
     151         979 :         talloc_steal(*visited, to_visit);
     152             : 
     153             :         /*
     154             :          * Iterate over the values of the attribute of the entry being
     155             :          * visited (to_visit) and follow them, calling this function
     156             :          * recursively.
     157             :          * If the value is in the visited array, skip it.
     158             :          * Otherwise, follow the link and visit it.
     159             :          */
     160        2230 :         for (i=0; i<el->num_values; i++) {
     161           0 :                 struct dsdb_dn *next_to_visit;
     162        1437 :                 bool skip = false;
     163             : 
     164        1437 :                 next_to_visit = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
     165        1437 :                 if (next_to_visit == NULL) {
     166           0 :                         talloc_free(tmp_ctx);
     167           0 :                         *matched = false;
     168           0 :                         return LDB_ERR_INVALID_DN_SYNTAX;
     169             :                 }
     170             : 
     171             :                 /*
     172             :                  * If the value is already in the visited array, skip it.
     173             :                  * Note the last element of the array is ignored because it is
     174             :                  * the current entry DN.
     175             :                  */
     176        2683 :                 for (j=0; j < (*visited_count) - 1; j++) {
     177        1272 :                         struct dsdb_dn *visited_dn = (*visited)[j];
     178        1272 :                         if (ldb_dn_compare(visited_dn->dn,
     179             :                                            next_to_visit->dn) == 0) {
     180          26 :                                 skip = true;
     181          26 :                                 break;
     182             :                         }
     183             :                 }
     184        1437 :                 if (skip) {
     185          26 :                         talloc_free(next_to_visit);
     186          26 :                         continue;
     187             :                 }
     188             : 
     189             :                 /* If the value is not in the visited array, evaluate it */
     190        1411 :                 ret = ldb_eval_transitive_filter_helper(tmp_ctx, ldb, attr,
     191             :                                                         dn_to_match, dn_oid,
     192             :                                                         next_to_visit,
     193             :                                                         visited, visited_count,
     194             :                                                         matched);
     195        1411 :                 if (ret != LDB_SUCCESS) {
     196           0 :                         talloc_free(tmp_ctx);
     197           0 :                         return ret;
     198             :                 }
     199        1411 :                 if (*matched) {
     200         186 :                         talloc_free(tmp_ctx);
     201         186 :                         return LDB_SUCCESS;
     202             :                 }
     203             :         }
     204             : 
     205         793 :         talloc_free(tmp_ctx);
     206         793 :         *matched = false;
     207         793 :         return LDB_SUCCESS;
     208             : }
     209             : 
     210             : /*
     211             :  * This function parses the linked attribute value to match, whose syntax
     212             :  * will be one of the different DN syntaxes, into a ldb_dn struct.
     213             :  */
     214        4216 : static int ldb_eval_transitive_filter(TALLOC_CTX *mem_ctx,
     215             :                                       struct ldb_context *ldb,
     216             :                                       const char *attr,
     217             :                                       const struct ldb_val *value_to_match,
     218             :                                       struct dsdb_dn *current_object_dn,
     219             :                                       bool *matched)
     220             : {
     221           0 :         const struct dsdb_schema *schema;
     222           0 :         const struct dsdb_attribute *schema_attr;
     223           0 :         struct dsdb_dn *dn_to_match;
     224           0 :         const char *dn_oid;
     225           0 :         unsigned int count;
     226        4216 :         struct dsdb_dn **visited = NULL;
     227             : 
     228        4216 :         schema = dsdb_get_schema(ldb, mem_ctx);
     229        4216 :         if (schema == NULL) {
     230           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     231             :         }
     232             : 
     233        4216 :         schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attr);
     234        4216 :         if (schema_attr == NULL) {
     235           0 :                 return LDB_ERR_NO_SUCH_ATTRIBUTE;
     236             :         }
     237             : 
     238             :         /* This is the DN syntax of the attribute being matched */
     239        4216 :         dn_oid = schema_attr->syntax->ldap_oid;
     240             : 
     241             :         /*
     242             :          * Build a ldb_dn struct holding the value to match, which is the
     243             :          * value entered in the search filter
     244             :          */
     245        4216 :         dn_to_match = dsdb_dn_parse(mem_ctx, ldb, value_to_match, dn_oid);
     246        4216 :         if (dn_to_match == NULL) {
     247           2 :                 *matched = false;
     248           2 :                 return LDB_SUCCESS;
     249             :         }
     250             : 
     251        4214 :         return ldb_eval_transitive_filter_helper(mem_ctx, ldb, attr,
     252             :                                                  dn_to_match, dn_oid,
     253             :                                                  current_object_dn,
     254             :                                                  &visited, &count, matched);
     255             : }
     256             : 
     257             : /*
     258             :  * This rule provides recursive search of a link attribute
     259             :  *
     260             :  * Documented in [MS-ADTS] section 3.1.1.3.4.4.3 LDAP_MATCHING_RULE_TRANSITIVE_EVAL
     261             :  * This allows a search filter such as:
     262             :  *
     263             :  * member:1.2.840.113556.1.4.1941:=cn=user,cn=users,dc=samba,dc=example,dc=com
     264             :  *
     265             :  * This searches not only the member attribute, but also any member
     266             :  * attributes that point at an object with this member in them.  All the
     267             :  * various DN syntax types are supported, not just plain DNs.
     268             :  *
     269             :  */
     270        4243 : static int ldb_comparator_trans(struct ldb_context *ldb,
     271             :                                 const char *oid,
     272             :                                 const struct ldb_message *msg,
     273             :                                 const char *attribute_to_match,
     274             :                                 const struct ldb_val *value_to_match,
     275             :                                 bool *matched)
     276             : {
     277           0 :         const struct dsdb_schema *schema;
     278           0 :         const struct dsdb_attribute *schema_attr;
     279           0 :         struct ldb_dn *msg_dn;
     280           0 :         struct dsdb_dn *dsdb_msg_dn;
     281           0 :         TALLOC_CTX *tmp_ctx;
     282           0 :         int ret;
     283             : 
     284        4243 :         tmp_ctx = talloc_new(ldb);
     285        4243 :         if (tmp_ctx == NULL) {
     286           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     287             :         }
     288             : 
     289             :         /*
     290             :          * If the target attribute to match is not a linked attribute, then
     291             :          * the filter evaluates to undefined
     292             :          */
     293        4243 :         schema = dsdb_get_schema(ldb, tmp_ctx);
     294        4243 :         if (schema == NULL) {
     295           0 :                 talloc_free(tmp_ctx);
     296           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     297             :         }
     298             : 
     299        4243 :         schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
     300        4243 :         if (schema_attr == NULL) {
     301           0 :                 talloc_free(tmp_ctx);
     302           0 :                 return LDB_ERR_NO_SUCH_ATTRIBUTE;
     303             :         }
     304             : 
     305             :         /*
     306             :          * This extended match filter is only valid for linked attributes,
     307             :          * following the MS definition (the schema attribute has a linkID
     308             :          * defined). See dochelp request 114111212024789 on cifs-protocols
     309             :          * mailing list.
     310             :          */
     311        4243 :         if (schema_attr->linkID == 0) {
     312          27 :                 *matched = false;
     313          27 :                 talloc_free(tmp_ctx);
     314          27 :                 return LDB_SUCCESS;
     315             :         }
     316             : 
     317             :         /* Duplicate original msg dn as the msg must not be modified */
     318        4216 :         msg_dn = ldb_dn_copy(tmp_ctx, msg->dn);
     319        4216 :         if (msg_dn == NULL) {
     320           0 :                 talloc_free(tmp_ctx);
     321           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     322             :         }
     323             : 
     324             :         /*
     325             :          * Build a dsdb dn from the message copied DN, which should be a plain
     326             :          * DN syntax.
     327             :          */
     328        4216 :         dsdb_msg_dn = dsdb_dn_construct(tmp_ctx, msg_dn, data_blob_null,
     329             :                                         LDB_SYNTAX_DN);
     330        4216 :         if (dsdb_msg_dn == NULL) {
     331           0 :                 *matched = false;
     332           0 :                 return LDB_ERR_INVALID_DN_SYNTAX;
     333             :         }
     334             : 
     335        4216 :         ret = ldb_eval_transitive_filter(tmp_ctx, ldb,
     336             :                                          attribute_to_match,
     337             :                                          value_to_match,
     338             :                                          dsdb_msg_dn, matched);
     339        4216 :         talloc_free(tmp_ctx);
     340        4216 :         return ret;
     341             : }
     342             : 
     343             : 
     344             : /*
     345             :  * This rule provides match of a dns object with expired records.
     346             :  *
     347             :  * This allows a search filter such as:
     348             :  *
     349             :  * dnsRecord:1.3.6.1.4.1.7165.4.5.3:=3694869
     350             :  *
     351             :  * where the value is a number of hours since the start of 1601.
     352             :  *
     353             :  * This allows the caller to find records that should become a DNS
     354             :  * tomestone, despite that information being deep within an NDR packed
     355             :  * object
     356             :  */
     357         337 : static int dsdb_match_for_dns_to_tombstone_time(struct ldb_context *ldb,
     358             :                                                 const char *oid,
     359             :                                                 const struct ldb_message *msg,
     360             :                                                 const char *attribute_to_match,
     361             :                                                 const struct ldb_val *value_to_match,
     362             :                                                 bool *matched)
     363             : {
     364           0 :         TALLOC_CTX *tmp_ctx;
     365           0 :         unsigned int i;
     366         337 :         struct ldb_message_element *el = NULL;
     367         337 :         struct auth_session_info *session_info = NULL;
     368           0 :         uint64_t tombstone_time;
     369         337 :         struct dnsp_DnssrvRpcRecord *rec = NULL;
     370           0 :         enum ndr_err_code err;
     371         337 :         *matched = false;
     372             : 
     373             :         /* Needs to be dnsRecord, no match otherwise */
     374         337 :         if (ldb_attr_cmp(attribute_to_match, "dnsRecord") != 0) {
     375           6 :                 return LDB_SUCCESS;
     376             :         }
     377             : 
     378         331 :         el = ldb_msg_find_element(msg, attribute_to_match);
     379         331 :         if (el == NULL) {
     380          15 :                 return LDB_SUCCESS;
     381             :         }
     382             : 
     383         316 :         if (ldb_msg_element_is_inaccessible(el)) {
     384           0 :                 *matched = false;
     385           0 :                 return LDB_SUCCESS;
     386             :         }
     387             : 
     388         316 :         session_info = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"),
     389             :                                        struct auth_session_info);
     390         316 :         if (session_info == NULL) {
     391           0 :                 return ldb_oom(ldb);
     392             :         }
     393         316 :         if (security_session_user_level(session_info, NULL)
     394             :                 != SECURITY_SYSTEM) {
     395             : 
     396           3 :                 DBG_ERR("unauthorised access\n");
     397           3 :                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
     398             :         }
     399             : 
     400             :         /* We only expect uint32_t <= 10 digits */
     401         313 :         if (value_to_match->length >= 12) {
     402           3 :                 DBG_ERR("Invalid timestamp passed\n");
     403           3 :                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
     404             :         } else {
     405         310 :                 int error = 0;
     406           0 :                 char s[12];
     407             : 
     408         310 :                 memcpy(s, value_to_match->data, value_to_match->length);
     409         310 :                 s[value_to_match->length] = 0;
     410         310 :                 if (s[0] == '\0') {
     411           3 :                         DBG_ERR("Empty timestamp passed\n");
     412           9 :                         return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
     413             :                 }
     414         307 :                 tombstone_time = smb_strtoull(s,
     415             :                                               NULL,
     416             :                                               10,
     417             :                                               &error,
     418             :                                               SMB_STR_FULL_STR_CONV);
     419         307 :                 if (error != 0) {
     420           6 :                         DBG_ERR("Invalid timestamp string passed\n");
     421           6 :                         return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
     422             :                 }
     423             :         }
     424             : 
     425         301 :         tmp_ctx = talloc_new(ldb);
     426         301 :         if (tmp_ctx == NULL) {
     427           0 :                 return ldb_oom(ldb);
     428             :         }
     429             : 
     430        1209 :         for (i = 0; i < el->num_values; i++) {
     431         924 :                 rec = talloc_zero(tmp_ctx, struct dnsp_DnssrvRpcRecord);
     432         924 :                 if (rec == NULL) {
     433           0 :                         TALLOC_FREE(tmp_ctx);
     434           0 :                         return ldb_oom(ldb);
     435             :                 }
     436         924 :                 err = ndr_pull_struct_blob(
     437         924 :                         &(el->values[i]),
     438             :                         tmp_ctx,
     439             :                         rec,
     440             :                         (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
     441         924 :                 if (!NDR_ERR_CODE_IS_SUCCESS(err)){
     442           0 :                         DBG_ERR("Failed to pull dns rec blob.\n");
     443           0 :                         TALLOC_FREE(tmp_ctx);
     444           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     445             :                 }
     446             : 
     447         924 :                 if (rec->wType == DNS_TYPE_SOA || rec->wType == DNS_TYPE_NS) {
     448          40 :                         TALLOC_FREE(rec);
     449          40 :                         continue;
     450             :                 }
     451             : 
     452         884 :                 if (rec->wType == DNS_TYPE_TOMBSTONE) {
     453           2 :                         TALLOC_FREE(rec);
     454           2 :                         continue;
     455             :                 }
     456         882 :                 if (rec->dwTimeStamp == 0) {
     457         134 :                         TALLOC_FREE(rec);
     458         134 :                         continue;
     459             :                 }
     460         748 :                 if (rec->dwTimeStamp > tombstone_time) {
     461         732 :                         TALLOC_FREE(rec);
     462         732 :                         continue;
     463             :                 }
     464             : 
     465          16 :                 *matched = true;
     466          16 :                 break;
     467             :         }
     468             : 
     469         301 :         TALLOC_FREE(tmp_ctx);
     470         301 :         return LDB_SUCCESS;
     471             : }
     472             : 
     473             : 
     474             : /*
     475             :  * This rule provides match of a link attribute against a 'should be expunged' criteria
     476             :  *
     477             :  * This allows a search filter such as:
     478             :  *
     479             :  * member:1.3.6.1.4.1.7165.4.5.2:=131139216000000000
     480             :  *
     481             :  * This searches the member attribute, but also any member attributes
     482             :  * that are deleted and should be expunged after the specified NTTIME
     483             :  * time.
     484             :  *
     485             :  */
     486     8192808 : static int dsdb_match_for_expunge(struct ldb_context *ldb,
     487             :                                   const char *oid,
     488             :                                   const struct ldb_message *msg,
     489             :                                   const char *attribute_to_match,
     490             :                                   const struct ldb_val *value_to_match,
     491             :                                   bool *matched)
     492             : {
     493      407989 :         const struct dsdb_schema *schema;
     494      407989 :         const struct dsdb_attribute *schema_attr;
     495      407989 :         TALLOC_CTX *tmp_ctx;
     496      407989 :         unsigned int i;
     497      407989 :         struct ldb_message_element *el;
     498      407989 :         struct auth_session_info *session_info;
     499      407989 :         uint64_t tombstone_time;
     500     8192808 :         *matched = false;
     501             : 
     502     8192808 :         el = ldb_msg_find_element(msg, attribute_to_match);
     503     8192808 :         if (el == NULL) {
     504     7782896 :                 return LDB_SUCCESS;
     505             :         }
     506             : 
     507        2060 :         if (ldb_msg_element_is_inaccessible(el)) {
     508           0 :                 *matched = false;
     509           0 :                 return LDB_SUCCESS;
     510             :         }
     511             : 
     512         137 :         session_info
     513        2060 :                 = talloc_get_type(ldb_get_opaque(ldb, DSDB_SESSION_INFO),
     514             :                                   struct auth_session_info);
     515        2060 :         if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
     516           0 :                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
     517             :         }
     518             : 
     519             :         /*
     520             :          * If the target attribute to match is not a linked attribute, then
     521             :          * the filter evaluates to undefined
     522             :          */
     523        2060 :         schema = dsdb_get_schema(ldb, NULL);
     524        2060 :         if (schema == NULL) {
     525           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     526             :         }
     527             : 
     528             :         /* TODO this is O(log n) per attribute */
     529        2060 :         schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
     530        2060 :         if (schema_attr == NULL) {
     531           0 :                 return LDB_ERR_NO_SUCH_ATTRIBUTE;
     532             :         }
     533             : 
     534             :         /*
     535             :          * This extended match filter is only valid for forward linked attributes.
     536             :          */
     537        2060 :         if (schema_attr->linkID == 0 || (schema_attr->linkID & 1) == 1) {
     538           0 :                 return LDB_ERR_NO_SUCH_ATTRIBUTE;
     539             :         }
     540             : 
     541             :         /* Just check we don't allow the caller to fill our stack */
     542        2058 :         if (value_to_match->length >=64) {
     543           0 :                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
     544        2058 :         } else {
     545        2058 :                 int error = 0;
     546        2058 :                 char s[value_to_match->length+1];
     547             : 
     548        2058 :                 memcpy(s, value_to_match->data, value_to_match->length);
     549        2058 :                 s[value_to_match->length] = 0;
     550        2058 :                 if (s[0] == '\0' || s[0] == '-') {
     551           6 :                         return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
     552             :                 }
     553        2057 :                 tombstone_time = smb_strtoull(s,
     554             :                                               NULL,
     555             :                                               10,
     556             :                                               &error,
     557             :                                               SMB_STR_FULL_STR_CONV);
     558        2057 :                 if (error != 0) {
     559           0 :                         return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
     560             :                 }
     561             :         }
     562             : 
     563        2052 :         tmp_ctx = talloc_new(ldb);
     564        2052 :         if (tmp_ctx == NULL) {
     565           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     566             :         }
     567             : 
     568        6163 :         for (i = 0; i < el->num_values; i++) {
     569         247 :                 NTSTATUS status;
     570         247 :                 struct dsdb_dn *dn;
     571         247 :                 uint64_t rmd_changetime;
     572        4130 :                 if (dsdb_dn_is_deleted_val(&el->values[i]) == false) {
     573        4111 :                         continue;
     574             :                 }
     575             : 
     576          91 :                 dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
     577          70 :                                    schema_attr->syntax->ldap_oid);
     578          70 :                 if (dn == NULL) {
     579           0 :                         DEBUG(1, ("Error: Failed to parse linked attribute blob of %s.\n", el->name));
     580           0 :                         continue;
     581             :                 }
     582             : 
     583          70 :                 status = dsdb_get_extended_dn_uint64(dn->dn, &rmd_changetime,
     584             :                                                      "RMD_CHANGETIME");
     585          70 :                 if (!NT_STATUS_IS_OK(status)) {
     586           0 :                         DEBUG(1, ("Error: RMD_CHANGETIME is missing on a forward link.\n"));
     587           0 :                         continue;
     588             :                 }
     589             : 
     590          70 :                 if (rmd_changetime > tombstone_time) {
     591          51 :                         continue;
     592             :                 }
     593             : 
     594          19 :                 *matched = true;
     595          19 :                 break;
     596             :         }
     597        2052 :         talloc_free(tmp_ctx);
     598        2052 :         return LDB_SUCCESS;
     599             : }
     600             : 
     601             : 
     602      341523 : int ldb_register_samba_matching_rules(struct ldb_context *ldb)
     603             : {
     604      341523 :         struct ldb_extended_match_rule *transitive_eval = NULL,
     605      341523 :                 *match_for_expunge = NULL,
     606      341523 :                 *match_for_dns_to_tombstone_time = NULL;
     607       11351 :         int ret;
     608             : 
     609      341523 :         transitive_eval = talloc_zero(ldb, struct ldb_extended_match_rule);
     610      341523 :         transitive_eval->oid = SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL;
     611      341523 :         transitive_eval->callback = ldb_comparator_trans;
     612      341523 :         ret = ldb_register_extended_match_rule(ldb, transitive_eval);
     613      341523 :         if (ret != LDB_SUCCESS) {
     614           0 :                 talloc_free(transitive_eval);
     615           0 :                 return ret;
     616             :         }
     617             : 
     618      341523 :         match_for_expunge = talloc_zero(ldb, struct ldb_extended_match_rule);
     619      341523 :         match_for_expunge->oid = DSDB_MATCH_FOR_EXPUNGE;
     620      341523 :         match_for_expunge->callback = dsdb_match_for_expunge;
     621      341523 :         ret = ldb_register_extended_match_rule(ldb, match_for_expunge);
     622      341523 :         if (ret != LDB_SUCCESS) {
     623           0 :                 talloc_free(match_for_expunge);
     624           0 :                 return ret;
     625             :         }
     626             : 
     627      341523 :         match_for_dns_to_tombstone_time = talloc_zero(
     628             :                 ldb,
     629             :                 struct ldb_extended_match_rule);
     630      341523 :         match_for_dns_to_tombstone_time->oid = DSDB_MATCH_FOR_DNS_TO_TOMBSTONE_TIME;
     631       11351 :         match_for_dns_to_tombstone_time->callback
     632      341523 :                 = dsdb_match_for_dns_to_tombstone_time;
     633      341523 :         ret = ldb_register_extended_match_rule(ldb,
     634             :                                                match_for_dns_to_tombstone_time);
     635      341523 :         if (ret != LDB_SUCCESS) {
     636           0 :                 TALLOC_FREE(match_for_dns_to_tombstone_time);
     637           0 :                 return ret;
     638             :         }
     639             : 
     640      330172 :         return LDB_SUCCESS;
     641             : }

Generated by: LCOV version 1.14