LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - dsdb_notification.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 76 86 88.4 %
Date: 2021-09-23 10:06:22 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :    notification control module
       3             : 
       4             :    Copyright (C) Stefan Metzmacher 2015
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : 
      21             : #include "includes.h"
      22             : #include "ldb/include/ldb.h"
      23             : #include "ldb/include/ldb_errors.h"
      24             : #include "ldb/include/ldb_module.h"
      25             : #include "dsdb/samdb/samdb.h"
      26             : #include "dsdb/samdb/ldb_modules/util.h"
      27             : 
      28             : struct dsdb_notification_cookie {
      29             :         uint64_t known_usn;
      30             : };
      31             : 
      32        1570 : static int dsdb_notification_verify_tree(struct ldb_parse_tree *tree)
      33             : {
      34             :         unsigned int i;
      35             :         int ret;
      36        1570 :         unsigned int num_ok = 0;
      37             :         /*
      38             :          * these attributes are present on every object
      39             :          * and windows accepts them.
      40             :          *
      41             :          * While [MS-ADTS] says only '(objectClass=*)'
      42             :          * would be allowed.
      43             :          */
      44             :         static const char * const attrs_ok[] = {
      45             :                 "objectClass",
      46             :                 "objectGUID",
      47             :                 "distinguishedName",
      48             :                 "name",
      49             :                 NULL,
      50             :         };
      51             : 
      52        1570 :         switch (tree->operation) {
      53           4 :         case LDB_OP_AND:
      54           8 :                 for (i = 0; i < tree->u.list.num_elements; i++) {
      55             :                         /*
      56             :                          * all elements need to be valid
      57             :                          */
      58           8 :                         ret = dsdb_notification_verify_tree(tree->u.list.elements[i]);
      59           8 :                         if (ret != LDB_SUCCESS) {
      60           4 :                                 return ret;
      61             :                         }
      62           4 :                         num_ok++;
      63             :                 }
      64           0 :                 break;
      65           5 :         case LDB_OP_OR:
      66           5 :                 for (i = 0; i < tree->u.list.num_elements; i++) {
      67             :                         /*
      68             :                          * at least one element needs to be valid
      69             :                          */
      70           5 :                         ret = dsdb_notification_verify_tree(tree->u.list.elements[i]);
      71           5 :                         if (ret == LDB_SUCCESS) {
      72           5 :                                 num_ok++;
      73           5 :                                 break;
      74             :                         }
      75             :                 }
      76           5 :                 break;
      77          24 :         case LDB_OP_NOT:
      78             :         case LDB_OP_EQUALITY:
      79             :         case LDB_OP_GREATER:
      80             :         case LDB_OP_LESS:
      81             :         case LDB_OP_APPROX:
      82             :         case LDB_OP_SUBSTRING:
      83             :         case LDB_OP_EXTENDED:
      84          24 :                 break;
      85             : 
      86        1537 :         case LDB_OP_PRESENT:
      87        1537 :                 ret = ldb_attr_in_list(attrs_ok, tree->u.present.attr);
      88        1537 :                 if (ret == 1) {
      89          60 :                         num_ok++;
      90             :                 }
      91        1537 :                 break;
      92             :         }
      93             : 
      94        1566 :         if (num_ok != 0) {
      95          65 :                 return LDB_SUCCESS;
      96             :         }
      97             : 
      98        1501 :         return LDB_ERR_UNWILLING_TO_PERFORM;
      99             : }
     100             : 
     101        1557 : static int dsdb_notification_filter_search(struct ldb_module *module,
     102             :                                           struct ldb_request *req,
     103             :                                           struct ldb_control *control)
     104             : {
     105        1557 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     106        1557 :         char *filter_usn = NULL;
     107        1557 :         struct ldb_parse_tree *down_tree = NULL;
     108        1557 :         struct ldb_request *down_req = NULL;
     109        1557 :         struct dsdb_notification_cookie *cookie = NULL;
     110             :         int ret;
     111             : 
     112        1557 :         if (req->op.search.tree == NULL) {
     113           0 :                 return dsdb_module_werror(module, LDB_ERR_OTHER,
     114             :                                           WERR_DS_NOTIFY_FILTER_TOO_COMPLEX,
     115             :                                           "Search filter missing.");
     116             :         }
     117             : 
     118        1557 :         ret = dsdb_notification_verify_tree(req->op.search.tree);
     119        1557 :         if (ret != LDB_SUCCESS) {
     120        1501 :                 return dsdb_module_werror(module, ret,
     121             :                                           WERR_DS_NOTIFY_FILTER_TOO_COMPLEX,
     122             :                                           "Search filter too complex.");
     123             :         }
     124             : 
     125             :         /*
     126             :          * For now we use a very simple design:
     127             :          *
     128             :          * - We don't do fully async ldb_requests,
     129             :          *   the caller needs to retry periodically!
     130             :          * - The only useful caller is the LDAP server, which is a long
     131             :          *   running task that can do periodic retries.
     132             :          * - We use a cookie in order to transfer state between the
     133             :          *   retries.
     134             :          * - We just search the available new objects each time we're
     135             :          *   called.
     136             :          *
     137             :          * As the only valid search filter is '(objectClass=*)' or
     138             :          * something similar that matches every object, we simply
     139             :          * replace it with (uSNChanged >= ) filter.
     140             :          * We could improve this later if required...
     141             :          */
     142             : 
     143             :         /*
     144             :          * The ldap_control_handler() decode_flag_request for
     145             :          * LDB_CONTROL_NOTIFICATION_OID. This makes sure
     146             :          * notification_control->data is NULL when comming from
     147             :          * the client.
     148             :          */
     149          56 :         if (control->data == NULL) {
     150          53 :                 cookie = talloc_zero(control, struct dsdb_notification_cookie);
     151          53 :                 if (cookie == NULL) {
     152           0 :                         return ldb_module_oom(module);
     153             :                 }
     154          53 :                 control->data = (uint8_t *)cookie;
     155             : 
     156             :                 /* mark the control as done */
     157          53 :                 control->critical = 0;
     158             :         }
     159             : 
     160          56 :         cookie = talloc_get_type_abort(control->data,
     161             :                                        struct dsdb_notification_cookie);
     162             : 
     163          56 :         if (cookie->known_usn != 0) {
     164           3 :                 filter_usn = talloc_asprintf(req, "%llu",
     165           3 :                                 (unsigned long long)(cookie->known_usn)+1);
     166           3 :                 if (filter_usn == NULL) {
     167           0 :                         return ldb_module_oom(module);
     168             :                 }
     169             :         }
     170             : 
     171          56 :         ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ,
     172             :                                   &cookie->known_usn);
     173          56 :         if (ret != LDB_SUCCESS) {
     174           0 :                 return ret;
     175             :         }
     176             : 
     177          56 :         if (filter_usn == NULL) {
     178             :                 /*
     179             :                  * It's the first time, let the caller comeback later
     180             :                  * as we won't find any new objects.
     181             :                  */
     182          53 :                 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
     183             :         }
     184             : 
     185           3 :         down_tree = talloc_zero(req, struct ldb_parse_tree);
     186           3 :         if (down_tree == NULL) {
     187           0 :                 return ldb_module_oom(module);
     188             :         }
     189           3 :         down_tree->operation = LDB_OP_GREATER;
     190           3 :         down_tree->u.equality.attr = "uSNChanged";
     191           3 :         down_tree->u.equality.value = data_blob_string_const(filter_usn);
     192           3 :         (void)talloc_move(down_req, &filter_usn);
     193             : 
     194           3 :         ret = ldb_build_search_req_ex(&down_req, ldb, req,
     195             :                                       req->op.search.base,
     196             :                                       req->op.search.scope,
     197             :                                       down_tree,
     198             :                                       req->op.search.attrs,
     199             :                                       req->controls,
     200             :                                       req, dsdb_next_callback,
     201             :                                       req);
     202           3 :         LDB_REQ_SET_LOCATION(down_req);
     203           3 :         if (ret != LDB_SUCCESS) {
     204           0 :                 return ret;
     205             :         }
     206             : 
     207             :         /* perform the search */
     208           3 :         return ldb_next_request(module, down_req);
     209             : }
     210             : 
     211    14364486 : static int dsdb_notification_search(struct ldb_module *module, struct ldb_request *req)
     212             : {
     213    14364486 :         struct ldb_control *control = NULL;
     214             : 
     215    14364486 :         if (ldb_dn_is_special(req->op.search.base)) {
     216     5005754 :                 return ldb_next_request(module, req);
     217             :         }
     218             : 
     219             :         /*
     220             :          * check if there's an extended dn control
     221             :          */
     222     9358732 :         control = ldb_request_get_control(req, LDB_CONTROL_NOTIFICATION_OID);
     223     9358732 :         if (control == NULL) {
     224             :                 /* not found go on */
     225     9357175 :                 return ldb_next_request(module, req);
     226             :         }
     227             : 
     228        1557 :         return dsdb_notification_filter_search(module, req, control);
     229             : }
     230             : 
     231      132453 : static int dsdb_notification_init(struct ldb_module *module)
     232             : {
     233             :         int ret;
     234             : 
     235      132453 :         ret = ldb_mod_register_control(module, LDB_CONTROL_NOTIFICATION_OID);
     236      132453 :         if (ret != LDB_SUCCESS) {
     237           0 :                 struct ldb_context *ldb = ldb_module_get_ctx(module);
     238             : 
     239           0 :                 ldb_debug(ldb, LDB_DEBUG_ERROR,
     240             :                         "notification: Unable to register control with rootdse!\n");
     241           0 :                 return ldb_module_operr(module);
     242             :         }
     243             : 
     244      132453 :         return ldb_next_init(module);
     245             : }
     246             : 
     247             : static const struct ldb_module_ops ldb_dsdb_notification_module_ops = {
     248             :         .name              = "dsdb_notification",
     249             :         .search            = dsdb_notification_search,
     250             :         .init_context      = dsdb_notification_init,
     251             : };
     252             : 
     253             : /*
     254             :   initialise the module
     255             :  */
     256        5536 : _PUBLIC_ int ldb_dsdb_notification_module_init(const char *version)
     257             : {
     258             :         int ret;
     259        5536 :         LDB_MODULE_CHECK_VERSION(version);
     260        5536 :         ret = ldb_register_module(&ldb_dsdb_notification_module_ops);
     261        5536 :         return ret;
     262             : }

Generated by: LCOV version 1.13