LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - group_audit.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 444 505 87.9 %
Date: 2021-09-23 10:06:22 Functions: 27 30 90.0 %

          Line data    Source code
       1             : /*
       2             :    ldb database library
       3             : 
       4             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
       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             :  * Provide an audit log of changes made to group memberships
      22             :  *
      23             :  */
      24             : 
      25             : #include "includes.h"
      26             : #include "ldb_module.h"
      27             : #include "lib/audit_logging/audit_logging.h"
      28             : #include "librpc/gen_ndr/windows_event_ids.h"
      29             : 
      30             : #include "dsdb/samdb/samdb.h"
      31             : #include "dsdb/samdb/ldb_modules/util.h"
      32             : #include "dsdb/samdb/ldb_modules/audit_util_proto.h"
      33             : #include "libcli/security/dom_sid.h"
      34             : #include "auth/common_auth.h"
      35             : #include "param/param.h"
      36             : 
      37             : #define AUDIT_JSON_TYPE "groupChange"
      38             : #define AUDIT_HR_TAG "Group Change"
      39             : #define AUDIT_MAJOR 1
      40             : #define AUDIT_MINOR 1
      41             : #define GROUP_LOG_LVL 5
      42             : 
      43             : static const char *const group_attrs[] = {"member", "groupType", NULL};
      44             : static const char *const group_type_attr[] = {"groupType", NULL};
      45             : static const char * const primary_group_attr[] = {
      46             :         "primaryGroupID",
      47             :         "objectSID",
      48             :         NULL};
      49             : 
      50             : struct audit_context {
      51             :         bool send_events;
      52             :         struct imessaging_context *msg_ctx;
      53             : };
      54             : 
      55             : struct audit_callback_context {
      56             :         struct ldb_request *request;
      57             :         struct ldb_module *module;
      58             :         struct ldb_message_element *members;
      59             :         uint32_t primary_group;
      60             :         void (*log_changes)(
      61             :                 struct audit_callback_context *acc,
      62             :                 const int status);
      63             : };
      64             : 
      65             : /*
      66             :  * @brief get the transaction id.
      67             :  *
      68             :  * Get the id of the transaction that the current request is contained in.
      69             :  *
      70             :  * @param req the request.
      71             :  *
      72             :  * @return the transaction id GUID, or NULL if it is not there.
      73             :  */
      74       33684 : static struct GUID *get_transaction_id(
      75             :         const struct ldb_request *request)
      76             : {
      77             :         struct ldb_control *control;
      78             :         struct dsdb_control_transaction_identifier *transaction_id;
      79             : 
      80       33684 :         control = ldb_request_get_control(
      81             :                 discard_const(request),
      82             :                 DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID);
      83       33684 :         if (control == NULL) {
      84        3611 :                 return NULL;
      85             :         }
      86       30068 :         transaction_id = talloc_get_type(
      87             :                 control->data,
      88             :                 struct dsdb_control_transaction_identifier);
      89       30068 :         if (transaction_id == NULL) {
      90           0 :                 return NULL;
      91             :         }
      92       30068 :         return &transaction_id->transaction_guid;
      93             : }
      94             : 
      95             : /*
      96             :  * @brief generate a JSON log entry for a group change.
      97             :  *
      98             :  * Generate a JSON object containing details of a users group change.
      99             :  *
     100             :  * @param module the ldb module
     101             :  * @param request the ldb_request
     102             :  * @param action the change action being performed
     103             :  * @param user the user name
     104             :  * @param group the group name
     105             :  * @param status the ldb status code for the ldb operation.
     106             :  *
     107             :  * @return A json object containing the details.
     108             :  *         NULL if an error was detected
     109             :  */
     110       33682 : static struct json_object audit_group_json(const struct ldb_module *module,
     111             :                                            const struct ldb_request *request,
     112             :                                            const char *action,
     113             :                                            const char *user,
     114             :                                            const char *group,
     115             :                                            const enum event_id_type event_id,
     116             :                                            const int status)
     117             : {
     118       33682 :         struct ldb_context *ldb = NULL;
     119       33682 :         const struct dom_sid *sid = NULL;
     120       33682 :         struct json_object wrapper = json_empty_object;
     121       33682 :         struct json_object audit = json_empty_object;
     122       33682 :         const struct tsocket_address *remote = NULL;
     123       33682 :         const struct GUID *unique_session_token = NULL;
     124       33682 :         struct GUID *transaction_id = NULL;
     125       33682 :         int rc = 0;
     126             : 
     127       33682 :         ldb = ldb_module_get_ctx(discard_const(module));
     128             : 
     129       33682 :         remote = dsdb_audit_get_remote_address(ldb);
     130       33682 :         sid = dsdb_audit_get_user_sid(module);
     131       33682 :         unique_session_token = dsdb_audit_get_unique_session_token(module);
     132       33682 :         transaction_id = get_transaction_id(request);
     133             : 
     134       33682 :         audit = json_new_object();
     135       33682 :         if (json_is_invalid(&audit)) {
     136           0 :                 goto failure;
     137             :         }
     138       33681 :         rc = json_add_version(&audit, AUDIT_MAJOR, AUDIT_MINOR);
     139       33681 :         if (rc != 0) {
     140           0 :                 goto failure;
     141             :         }
     142       33680 :         if (event_id != EVT_ID_NONE) {
     143       21061 :                 rc = json_add_int(&audit, "eventId", event_id);
     144       21061 :                 if (rc != 0) {
     145           0 :                         goto failure;
     146             :                 }
     147             :         }
     148       33680 :         rc = json_add_int(&audit, "statusCode", status);
     149       33680 :         if (rc != 0) {
     150           0 :                 goto failure;
     151             :         }
     152       33680 :         rc = json_add_string(&audit, "status", ldb_strerror(status));
     153       33680 :         if (rc != 0) {
     154           0 :                 goto failure;
     155             :         }
     156       33680 :         rc = json_add_string(&audit, "action", action);
     157       33680 :         if (rc != 0) {
     158           0 :                 goto failure;
     159             :         }
     160       33680 :         rc = json_add_address(&audit, "remoteAddress", remote);
     161       33680 :         if (rc != 0) {
     162           0 :                 goto failure;
     163             :         }
     164       33680 :         rc = json_add_sid(&audit, "userSid", sid);
     165       33680 :         if (rc != 0) {
     166           0 :                 goto failure;
     167             :         }
     168       33680 :         rc = json_add_string(&audit, "group", group);
     169       33680 :         if (rc != 0) {
     170           0 :                 goto failure;
     171             :         }
     172       33680 :         rc = json_add_guid(&audit, "transactionId", transaction_id);
     173       33680 :         if (rc != 0) {
     174           0 :                 goto failure;
     175             :         }
     176       33680 :         rc = json_add_guid(&audit, "sessionId", unique_session_token);
     177       33680 :         if (rc != 0) {
     178           0 :                 goto failure;
     179             :         }
     180       33680 :         rc = json_add_string(&audit, "user", user);
     181       33680 :         if (rc != 0) {
     182           0 :                 goto failure;
     183             :         }
     184             : 
     185       33680 :         wrapper = json_new_object();
     186       33680 :         if (json_is_invalid(&wrapper)) {
     187           0 :                 goto failure;
     188             :         }
     189       33679 :         rc = json_add_timestamp(&wrapper);
     190       33679 :         if (rc != 0) {
     191           0 :                 goto failure;
     192             :         }
     193       33678 :         rc = json_add_string(&wrapper, "type", AUDIT_JSON_TYPE);
     194       33678 :         if (rc != 0) {
     195           0 :                 goto failure;
     196             :         }
     197       33678 :         rc = json_add_object(&wrapper, AUDIT_JSON_TYPE, &audit);
     198       33678 :         if (rc != 0) {
     199           0 :                 goto failure;
     200             :         }
     201             : 
     202       33678 :         return wrapper;
     203           4 : failure:
     204             :         /*
     205             :          * On a failure audit will not have been added to wrapper so it
     206             :          * needs to free it to avoid a leak.
     207             :          *
     208             :          * wrapper is freed to invalidate it as it will have only been
     209             :          * partially constructed and may be inconsistent.
     210             :          *
     211             :          * All the json manipulation routines handle a freed object correctly
     212             :          */
     213           4 :         json_free(&audit);
     214           4 :         json_free(&wrapper);
     215           4 :         DBG_ERR("Failed to create group change JSON log message\n");
     216           4 :         return wrapper;
     217             : }
     218             : 
     219             : /*
     220             :  * @brief generate a human readable log entry for a group change.
     221             :  *
     222             :  * Generate a human readable log entry containing details of a users group
     223             :  * change.
     224             :  *
     225             :  * @param ctx the talloc context owning the returned log entry
     226             :  * @param module the ldb module
     227             :  * @param request the ldb_request
     228             :  * @param action the change action being performed
     229             :  * @param user the user name
     230             :  * @param group the group name
     231             :  * @param status the ldb status code for the ldb operation.
     232             :  *
     233             :  * @return A human readable log line.
     234             :  */
     235           1 : static char *audit_group_human_readable(
     236             :         TALLOC_CTX *mem_ctx,
     237             :         const struct ldb_module *module,
     238             :         const struct ldb_request *request,
     239             :         const char *action,
     240             :         const char *user,
     241             :         const char *group,
     242             :         const int status)
     243             : {
     244           1 :         struct ldb_context *ldb = NULL;
     245           1 :         const char *remote_host = NULL;
     246           1 :         const struct dom_sid *sid = NULL;
     247           1 :         const char *user_sid = NULL;
     248           1 :         const char *timestamp = NULL;
     249           1 :         char *log_entry = NULL;
     250             : 
     251           1 :         TALLOC_CTX *ctx = talloc_new(NULL);
     252             : 
     253           1 :         ldb = ldb_module_get_ctx(discard_const(module));
     254             : 
     255           1 :         remote_host = dsdb_audit_get_remote_host(ldb, ctx);
     256           1 :         sid = dsdb_audit_get_user_sid(module);
     257           1 :         user_sid = dom_sid_string(ctx, sid);
     258           1 :         timestamp = audit_get_timestamp(ctx);
     259             : 
     260           1 :         log_entry = talloc_asprintf(
     261             :                 mem_ctx,
     262             :                 "[%s] at [%s] status [%s] "
     263             :                 "Remote host [%s] SID [%s] Group [%s] User [%s]",
     264             :                 action,
     265             :                 timestamp,
     266             :                 ldb_strerror(status),
     267             :                 remote_host,
     268             :                 user_sid,
     269             :                 group,
     270             :                 user);
     271           1 :         TALLOC_FREE(ctx);
     272           1 :         return log_entry;
     273             : }
     274             : 
     275             : /*
     276             :  * @brief generate an array of parsed_dns, deferring the actual parsing.
     277             :  *
     278             :  * Get an array of 'struct parsed_dns' without the parsing.
     279             :  * The parsed_dns are parsed only when needed to avoid the expense of parsing.
     280             :  *
     281             :  * This procedure assumes that the dn's are sorted in GUID order and contains
     282             :  * no duplicates.  This should be valid as the module sits below repl_meta_data
     283             :  * which ensures this.
     284             :  *
     285             :  * @param mem_ctx The memory context that will own the generated array
     286             :  * @param el The message element used to generate the array.
     287             :  *
     288             :  * @return an array of struct parsed_dns, or NULL in the event of an error
     289             :  */
     290       10643 : static struct parsed_dn *get_parsed_dns(
     291             :         TALLOC_CTX *mem_ctx,
     292             :         struct ldb_message_element *el)
     293             : {
     294             :         int ret;
     295       10643 :         struct parsed_dn *pdn = NULL;
     296             : 
     297       10643 :         if (el == NULL || el->num_values == 0) {
     298        1264 :                 return NULL;
     299             :         }
     300             : 
     301        9348 :         ret = get_parsed_dns_trusted(mem_ctx, el, &pdn);
     302        9348 :         if (ret == LDB_ERR_OPERATIONS_ERROR) {
     303           0 :                 DBG_ERR("Out of memory\n");
     304           0 :                 return NULL;
     305             :         }
     306        9348 :         return pdn;
     307             : 
     308             : }
     309             : 
     310             : enum dn_compare_result {
     311             :         LESS_THAN,
     312             :         BINARY_EQUAL,
     313             :         EQUAL,
     314             :         GREATER_THAN
     315             : };
     316             : /*
     317             :  * @brief compare parsed_dn, using GUID ordering
     318             :  *
     319             :  * Compare two parsed_dn structures, using GUID ordering.
     320             :  * To avoid the overhead of parsing the DN's this function does a binary
     321             :  * compare first. The DN's tre only parsed if they are not equal at a binary
     322             :  * level.
     323             :  *
     324             :  * @param ctx talloc context that will own the parsed dsdb_dn
     325             :  * @param ldb ldb_context
     326             :  * @param dn1 The first dn
     327             :  * @param dn2 The second dn
     328             :  *
     329             :  * @return BINARY_EQUAL values are equal at a binary level
     330             :  *         EQUAL        DN's are equal but the meta data is different
     331             :  *         LESS_THAN    dn1's GUID is less than dn2's GUID
     332             :  *         GREATER_THAN dn1's GUID is greater than  dn2's GUID
     333             :  *
     334             :  */
     335       26079 : static enum dn_compare_result dn_compare(
     336             :         TALLOC_CTX *mem_ctx,
     337             :         struct ldb_context *ldb,
     338             :         struct parsed_dn *dn1,
     339             :         struct parsed_dn *dn2) {
     340             : 
     341       26079 :         int res = 0;
     342             : 
     343             :         /*
     344             :          * Do a binary compare first to avoid unnecessary parsing
     345             :          */
     346       26079 :         if (data_blob_cmp(dn1->v, dn2->v) == 0) {
     347             :                 /*
     348             :                  * Values are equal at a binary level so no need
     349             :                  * for further processing
     350             :                  */
     351       22034 :                 return BINARY_EQUAL;
     352             :         }
     353             :         /*
     354             :          * Values not equal at the binary level, so lets
     355             :          * do a GUID ordering compare. To do this we will need to ensure
     356             :          * that the dn's have been parsed.
     357             :          */
     358        4039 :         if (dn1->dsdb_dn == NULL) {
     359        3554 :                 really_parse_trusted_dn(
     360             :                         mem_ctx,
     361             :                         ldb,
     362             :                         dn1,
     363             :                         LDB_SYNTAX_DN);
     364             :         }
     365        4039 :         if (dn2->dsdb_dn == NULL) {
     366        4039 :                 really_parse_trusted_dn(
     367             :                         mem_ctx,
     368             :                         ldb,
     369             :                         dn2,
     370             :                         LDB_SYNTAX_DN);
     371             :         }
     372             : 
     373        4039 :         res = ndr_guid_compare(&dn1->guid, &dn2->guid);
     374        4039 :         if (res < 0) {
     375        2281 :                 return LESS_THAN;
     376        1756 :         } else if (res == 0) {
     377         640 :                 return EQUAL;
     378             :         } else {
     379        1111 :                 return GREATER_THAN;
     380             :         }
     381             : }
     382             : 
     383             : /*
     384             :  * @brief Get the DN of a users primary group as a printable string.
     385             :  *
     386             :  * Get the DN of a users primary group as a printable string.
     387             :  *
     388             :  * @param mem_ctx Talloc context the the returned string will be allocated on.
     389             :  * @param module The ldb module
     390             :  * @param account_sid The SID for the uses account.
     391             :  * @param primary_group_rid The RID for the users primary group.
     392             :  *
     393             :  * @return a formatted DN, or null if there is an error.
     394             :  */
     395       12174 : static const char *get_primary_group_dn(
     396             :         TALLOC_CTX *mem_ctx,
     397             :         struct ldb_module *module,
     398             :         struct dom_sid *account_sid,
     399             :         uint32_t primary_group_rid)
     400             : {
     401             :         NTSTATUS status;
     402             : 
     403       12174 :         struct ldb_context *ldb = NULL;
     404       12174 :         struct dom_sid *domain_sid = NULL;
     405       12174 :         struct dom_sid *primary_group_sid = NULL;
     406       12174 :         char *sid = NULL;
     407       12174 :         struct ldb_dn *dn = NULL;
     408       12174 :         struct ldb_message *msg = NULL;
     409             :         int rc;
     410             : 
     411       12174 :         ldb = ldb_module_get_ctx(module);
     412             : 
     413       12174 :         status = dom_sid_split_rid(mem_ctx, account_sid, &domain_sid, NULL);
     414       12174 :         if (!NT_STATUS_IS_OK(status)) {
     415           0 :                 return NULL;
     416             :         }
     417             : 
     418       12173 :         primary_group_sid = dom_sid_add_rid(
     419             :                 mem_ctx,
     420             :                 domain_sid,
     421             :                 primary_group_rid);
     422       12173 :         if (!primary_group_sid) {
     423           0 :                 return NULL;
     424             :         }
     425             : 
     426       12173 :         sid = dom_sid_string(mem_ctx, primary_group_sid);
     427       12173 :         if (sid == NULL) {
     428           0 :                 return NULL;
     429             :         }
     430             : 
     431       12173 :         dn = ldb_dn_new_fmt(mem_ctx, ldb, "<SID=%s>", sid);
     432       12173 :         if(dn == NULL) {
     433           0 :                 return sid;
     434             :         }
     435       12173 :         rc = dsdb_search_one(
     436             :                 ldb,
     437             :                 mem_ctx,
     438             :                 &msg,
     439             :                 dn,
     440             :                 LDB_SCOPE_BASE,
     441             :                 NULL,
     442             :                 0,
     443             :                 NULL);
     444       12173 :         if (rc != LDB_SUCCESS) {
     445           0 :                 return NULL;
     446             :         }
     447             : 
     448       12172 :         return ldb_dn_get_linearized(msg->dn);
     449             : }
     450             : 
     451             : /*
     452             :  * @brief Log details of a change to a users primary group.
     453             :  *
     454             :  * Log details of a change to a users primary group.
     455             :  * There is no windows event id associated with a Primary Group change.
     456             :  * However for a new user we generate an added to group event.
     457             :  *
     458             :  * @param module The ldb module.
     459             :  * @param request The request being logged.
     460             :  * @param action Description of the action being performed.
     461             :  * @param group The linearized for of the group DN
     462             :  * @param status the LDB status code for the processing of the request.
     463             :  *
     464             :  */
     465       12171 : static void log_primary_group_change(
     466             :         struct ldb_module *module,
     467             :         const struct ldb_request *request,
     468             :         const char *action,
     469             :         const char *group,
     470             :         const int  status)
     471             : {
     472       12171 :         const char *user = NULL;
     473             : 
     474       10035 :         struct audit_context *ac =
     475       12171 :                 talloc_get_type(
     476             :                         ldb_module_get_private(module),
     477             :                         struct audit_context);
     478             : 
     479       12171 :         TALLOC_CTX *ctx = talloc_new(NULL);
     480             : 
     481       12171 :         user = dsdb_audit_get_primary_dn(request);
     482       12171 :         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL)) {
     483           0 :                 char *message = NULL;
     484           0 :                 message = audit_group_human_readable(
     485             :                         ctx,
     486             :                         module,
     487             :                         request,
     488             :                         action,
     489             :                         user,
     490             :                         group,
     491             :                         status);
     492           0 :                 audit_log_human_text(
     493             :                         AUDIT_HR_TAG,
     494             :                         message,
     495             :                         DBGC_DSDB_GROUP_AUDIT,
     496             :                         GROUP_LOG_LVL);
     497           0 :                 TALLOC_FREE(message);
     498             :         }
     499             : 
     500       22206 :         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
     501       22206 :                 (ac->msg_ctx && ac->send_events)) {
     502             : 
     503             :                 struct json_object json;
     504       12171 :                 json = audit_group_json(
     505             :                     module, request, action, user, group, EVT_ID_NONE, status);
     506       12171 :                 audit_log_json(
     507             :                         &json,
     508             :                         DBGC_DSDB_GROUP_AUDIT_JSON,
     509             :                         GROUP_LOG_LVL);
     510       12171 :                 if (ac->send_events) {
     511       12171 :                         audit_message_send(
     512             :                                 ac->msg_ctx,
     513             :                                 DSDB_GROUP_EVENT_NAME,
     514             :                                 MSG_GROUP_LOG,
     515             :                                 &json);
     516             :                 }
     517       12171 :                 json_free(&json);
     518       12067 :                 if (request->operation == LDB_ADD) {
     519             :                         /*
     520             :                          * Have just added a user, generate a groupChange
     521             :                          * message indicating the user has been added to thier
     522             :                          * new PrimaryGroup.
     523             :                          */
     524             :                 }
     525             :         }
     526       12171 :         TALLOC_FREE(ctx);
     527       12171 : }
     528             : 
     529             : /*
     530             :  * @brief Log details of a single change to a users group membership.
     531             :  *
     532             :  * Log details of a change to a users group membership, except for changes
     533             :  * to their primary group which is handled by log_primary_group_change.
     534             :  *
     535             :  * @param module The ldb module.
     536             :  * @param request The request being logged.
     537             :  * @param action Description of the action being performed.
     538             :  * @param user The linearized form of the users DN
     539             :  * @param status the LDB status code for the processing of the request.
     540             :  *
     541             :  */
     542       21503 : static void log_membership_change(struct ldb_module *module,
     543             :                                   const struct ldb_request *request,
     544             :                                   const char *action,
     545             :                                   const char *user,
     546             :                                   const enum event_id_type event_id,
     547             :                                   const int status)
     548             : {
     549       21503 :         const char *group = NULL;
     550       17658 :         struct audit_context *ac =
     551       21503 :                 talloc_get_type(
     552             :                         ldb_module_get_private(module),
     553             :                         struct audit_context);
     554             : 
     555       21503 :         TALLOC_CTX *ctx = talloc_new(NULL);
     556       21503 :         group = dsdb_audit_get_primary_dn(request);
     557       21503 :         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL)) {
     558           0 :                 char *message = NULL;
     559           0 :                 message = audit_group_human_readable(
     560             :                         ctx,
     561             :                         module,
     562             :                         request,
     563             :                         action,
     564             :                         user,
     565             :                         group,
     566             :                         status);
     567           0 :                 audit_log_human_text(
     568             :                         AUDIT_HR_TAG,
     569             :                         message,
     570             :                         DBGC_DSDB_GROUP_AUDIT,
     571             :                         GROUP_LOG_LVL);
     572           0 :                 TALLOC_FREE(message);
     573             :         }
     574             : 
     575       39161 :         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
     576       39161 :                 (ac->msg_ctx && ac->send_events)) {
     577             :                 struct json_object json;
     578       21503 :                 json = audit_group_json(
     579             :                     module, request, action, user, group, event_id, status);
     580       21503 :                 audit_log_json(
     581             :                         &json,
     582             :                         DBGC_DSDB_GROUP_AUDIT_JSON,
     583             :                         GROUP_LOG_LVL);
     584       21503 :                 if (ac->send_events) {
     585       21503 :                         audit_message_send(
     586             :                                 ac->msg_ctx,
     587             :                                 DSDB_GROUP_EVENT_NAME,
     588             :                                 MSG_GROUP_LOG,
     589             :                                 &json);
     590             :                 }
     591       21503 :                 json_free(&json);
     592             :         }
     593       21503 :         TALLOC_FREE(ctx);
     594       21503 : }
     595             : 
     596             : /*
     597             :  * @brief Get the windows event type id for removing a user from a group type.
     598             :  *
     599             :  * @param group_type the type of the current group, see libds/common/flags.h
     600             :  *
     601             :  * @return the Windows Event Id
     602             :  *
     603             :  */
     604        3540 : static enum event_id_type get_remove_member_event(uint32_t group_type)
     605             : {
     606             : 
     607        3546 :         switch (group_type) {
     608           6 :         case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
     609           6 :                 return EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP;
     610        2188 :         case GTYPE_SECURITY_GLOBAL_GROUP:
     611        2184 :                 return EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP;
     612          94 :         case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
     613          94 :                 return EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP;
     614        1218 :         case GTYPE_SECURITY_UNIVERSAL_GROUP:
     615        1218 :                 return EVT_ID_USER_REMOVED_FROM_UNIVERSAL_SEC_GROUP;
     616           0 :         case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
     617           0 :                 return EVT_ID_USER_REMOVED_FROM_GLOBAL_GROUP;
     618           3 :         case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
     619           3 :                 return EVT_ID_USER_REMOVED_FROM_LOCAL_GROUP;
     620           0 :         case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
     621           0 :                 return EVT_ID_USER_REMOVED_FROM_UNIVERSAL_GROUP;
     622          35 :         default:
     623          35 :                 return EVT_ID_NONE;
     624             :         }
     625             : }
     626             : 
     627             : /*
     628             :  * @brief Get the windows event type id for adding a user to a group type.
     629             :  *
     630             :  * @param group_type the type of the current group, see libds/common/flags.h
     631             :  *
     632             :  * @return the Windows Event Id
     633             :  *
     634             :  */
     635       17458 : static enum event_id_type get_add_member_event(uint32_t group_type)
     636             : {
     637             : 
     638       17619 :         switch (group_type) {
     639         377 :         case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
     640         377 :                 return EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP;
     641       15422 :         case GTYPE_SECURITY_GLOBAL_GROUP:
     642       15308 :                 return EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP;
     643         404 :         case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
     644         404 :                 return EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP;
     645        1292 :         case GTYPE_SECURITY_UNIVERSAL_GROUP:
     646        1286 :                 return EVT_ID_USER_ADDED_TO_UNIVERSAL_SEC_GROUP;
     647           0 :         case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
     648           0 :                 return EVT_ID_USER_ADDED_TO_GLOBAL_GROUP;
     649           9 :         case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
     650           9 :                 return EVT_ID_USER_ADDED_TO_LOCAL_GROUP;
     651           0 :         case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
     652           0 :                 return EVT_ID_USER_ADDED_TO_UNIVERSAL_GROUP;
     653          74 :         default:
     654          74 :                 return EVT_ID_NONE;
     655             :         }
     656             : }
     657             : 
     658             : /*
     659             :  * @brief Log all the changes to a users group membership.
     660             :  *
     661             :  * Log details of a change to a users group memberships, except for changes
     662             :  * to their primary group which is handled by log_primary_group_change.
     663             :  *
     664             :  * @param module The ldb module.
     665             :  * @param request The request being logged.
     666             :  * @param action Description of the action being performed.
     667             :  * @param user The linearized form of the users DN
     668             :  * @param status the LDB status code for the processing of the request.
     669             :  *
     670             :  */
     671        5320 : static void log_membership_changes(struct ldb_module *module,
     672             :                                    const struct ldb_request *request,
     673             :                                    struct ldb_message_element *el,
     674             :                                    struct ldb_message_element *old_el,
     675             :                                    uint32_t group_type,
     676             :                                    int status)
     677             : {
     678             :         unsigned int i, old_i, new_i;
     679             :         unsigned int old_num_values;
     680             :         unsigned int max_num_values;
     681             :         unsigned int new_num_values;
     682        5320 :         struct parsed_dn *old_val = NULL;
     683        5320 :         struct parsed_dn *new_val = NULL;
     684        5320 :         struct parsed_dn *new_values = NULL;
     685        5320 :         struct parsed_dn *old_values = NULL;
     686        5320 :         struct ldb_context *ldb = NULL;
     687             : 
     688        5320 :         TALLOC_CTX *ctx = talloc_new(NULL);
     689             : 
     690        5320 :         old_num_values = old_el ? old_el->num_values : 0;
     691        5320 :         new_num_values = el ? el->num_values : 0;
     692        5320 :         max_num_values = old_num_values + new_num_values;
     693             : 
     694        5320 :         if (max_num_values == 0) {
     695             :                 /*
     696             :                  * There is nothing to do!
     697             :                  */
     698           0 :                 TALLOC_FREE(ctx);
     699           0 :                 return;
     700             :         }
     701             : 
     702        5320 :         old_values = get_parsed_dns(ctx, old_el);
     703        5320 :         new_values = get_parsed_dns(ctx, el);
     704        5320 :         ldb = ldb_module_get_ctx(module);
     705             : 
     706        5320 :         old_i = 0;
     707        5320 :         new_i = 0;
     708       36569 :         for (i = 0; i < max_num_values; i++) {
     709             :                 enum dn_compare_result cmp;
     710       35275 :                 if (old_i < old_num_values && new_i < new_num_values) {
     711             :                         /*
     712             :                          * Both list have values, so compare the values
     713             :                          */
     714       26075 :                         old_val = &old_values[old_i];
     715       26075 :                         new_val = &new_values[new_i];
     716       26075 :                         cmp = dn_compare(ctx, ldb, old_val, new_val);
     717        9200 :                 } else if (old_i < old_num_values) {
     718             :                         /*
     719             :                          * the new list is empty, read the old list
     720             :                          */
     721         888 :                         old_val = &old_values[old_i];
     722         888 :                         new_val = NULL;
     723         888 :                         cmp = LESS_THAN;
     724        8312 :                 } else if (new_i < new_num_values) {
     725             :                         /*
     726             :                          * the old list is empty, read new list
     727             :                          */
     728        4286 :                         old_val = NULL;
     729        4286 :                         new_val = &new_values[new_i];
     730        4286 :                         cmp = GREATER_THAN;
     731             :                 } else {
     732        4020 :                         break;
     733             :                 }
     734             : 
     735       31249 :                 if (cmp == LESS_THAN) {
     736             :                         /*
     737             :                          * Have an entry in the original record that is not in
     738             :                          * the new record. So it's been deleted
     739             :                          */
     740        3170 :                         const char *user = NULL;
     741             :                         enum event_id_type event_id;
     742        3170 :                         if (old_val->dsdb_dn == NULL) {
     743         888 :                                 really_parse_trusted_dn(
     744             :                                         ctx,
     745             :                                         ldb,
     746             :                                         old_val,
     747             :                                         LDB_SYNTAX_DN);
     748             :                         }
     749        3170 :                         user = ldb_dn_get_linearized(old_val->dsdb_dn->dn);
     750        3170 :                         event_id = get_remove_member_event(group_type);
     751        3170 :                         log_membership_change(
     752             :                             module, request, "Removed", user, event_id, status);
     753        3170 :                         old_i++;
     754       28079 :                 } else if (cmp == BINARY_EQUAL) {
     755             :                         /*
     756             :                          * DN's unchanged at binary level so nothing to do.
     757             :                          */
     758       22039 :                         old_i++;
     759       22039 :                         new_i++;
     760        6040 :                 } else if (cmp == EQUAL) {
     761             :                         /*
     762             :                          * DN is unchanged now need to check the flags to
     763             :                          * determine if a record has been deleted or undeleted
     764             :                          */
     765             :                         uint32_t old_flags;
     766             :                         uint32_t new_flags;
     767         644 :                         if (old_val->dsdb_dn == NULL) {
     768           0 :                                 really_parse_trusted_dn(
     769             :                                         ctx,
     770             :                                         ldb,
     771             :                                         old_val,
     772             :                                         LDB_SYNTAX_DN);
     773             :                         }
     774         644 :                         if (new_val->dsdb_dn == NULL) {
     775           0 :                                 really_parse_trusted_dn(
     776             :                                         ctx,
     777             :                                         ldb,
     778             :                                         new_val,
     779             :                                         LDB_SYNTAX_DN);
     780             :                         }
     781             : 
     782         644 :                         dsdb_get_extended_dn_uint32(
     783         644 :                                 old_val->dsdb_dn->dn,
     784             :                                 &old_flags,
     785             :                                 "RMD_FLAGS");
     786         644 :                         dsdb_get_extended_dn_uint32(
     787         644 :                                 new_val->dsdb_dn->dn,
     788             :                                 &new_flags,
     789             :                                 "RMD_FLAGS");
     790         644 :                         if (new_flags == old_flags) {
     791             :                                 /*
     792             :                                  * No changes to the Repl meta data so can
     793             :                                  * no need to log the change
     794             :                                  */
     795          87 :                                 old_i++;
     796          87 :                                 new_i++;
     797          87 :                                 continue;
     798             :                         }
     799         557 :                         if (new_flags & DSDB_RMD_FLAG_DELETED) {
     800             :                                 /*
     801             :                                  * DN has been deleted.
     802             :                                  */
     803         376 :                                 const char *user = NULL;
     804             :                                 enum event_id_type event_id;
     805         376 :                                 user = ldb_dn_get_linearized(
     806         376 :                                         old_val->dsdb_dn->dn);
     807         376 :                                 event_id = get_remove_member_event(group_type);
     808         376 :                                 log_membership_change(module,
     809             :                                                       request,
     810             :                                                       "Removed",
     811             :                                                       user,
     812             :                                                       event_id,
     813             :                                                       status);
     814             :                         } else {
     815             :                                 /*
     816             :                                  * DN has been re-added
     817             :                                  */
     818         181 :                                 const char *user = NULL;
     819             :                                 enum event_id_type event_id;
     820         181 :                                 user = ldb_dn_get_linearized(
     821         181 :                                         new_val->dsdb_dn->dn);
     822         181 :                                 event_id = get_add_member_event(group_type);
     823         181 :                                 log_membership_change(module,
     824             :                                                       request,
     825             :                                                       "Added",
     826             :                                                       user,
     827             :                                                       event_id,
     828             :                                                       status);
     829             :                         }
     830         557 :                         old_i++;
     831         557 :                         new_i++;
     832             :                 } else {
     833             :                         /*
     834             :                          * Member in the updated record that's not in the
     835             :                          * original, so it must have been added.
     836             :                          */
     837        5396 :                         const char *user = NULL;
     838             :                         enum event_id_type event_id;
     839        5396 :                         if ( new_val->dsdb_dn == NULL) {
     840        4286 :                                 really_parse_trusted_dn(
     841             :                                         ctx,
     842             :                                         ldb,
     843             :                                         new_val,
     844             :                                         LDB_SYNTAX_DN);
     845             :                         }
     846        5396 :                         user = ldb_dn_get_linearized(new_val->dsdb_dn->dn);
     847        5396 :                         event_id = get_add_member_event(group_type);
     848        5396 :                         log_membership_change(
     849             :                             module, request, "Added", user, event_id, status);
     850        5396 :                         new_i++;
     851             :                 }
     852             :         }
     853             : 
     854        5320 :         TALLOC_FREE(ctx);
     855             : }
     856             : 
     857             : /*
     858             :  * @brief log a group change message for a newly added user.
     859             :  *
     860             :  * When a user is added we need to generate a GroupChange Add message to
     861             :  * log that the user has been added to their PrimaryGroup
     862             :  */
     863       12042 : static void log_new_user_added_to_primary_group(
     864             :     TALLOC_CTX *ctx,
     865             :     struct audit_callback_context *acc,
     866             :     const char *group,
     867             :     const int status)
     868             : {
     869             :         uint32_t group_type;
     870       12042 :         enum event_id_type event_id = EVT_ID_NONE;
     871       12042 :         struct ldb_result *res = NULL;
     872       12042 :         struct ldb_dn *group_dn = NULL;
     873       12042 :         struct ldb_context *ldb = NULL;
     874             :         int ret;
     875             : 
     876       12042 :         ldb = ldb_module_get_ctx(acc->module);
     877       12042 :         group_dn = ldb_dn_new(ctx, ldb, group);
     878       12042 :         ret = dsdb_module_search_dn(acc->module,
     879             :                                     ctx,
     880             :                                     &res,
     881             :                                     group_dn,
     882             :                                     group_type_attr,
     883             :                                     DSDB_FLAG_NEXT_MODULE |
     884             :                                         DSDB_SEARCH_REVEAL_INTERNALS |
     885             :                                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
     886             :                                     NULL);
     887       12042 :         if (ret == LDB_SUCCESS) {
     888       12042 :                 const char *user = NULL;
     889        9916 :                 group_type =
     890       12042 :                     ldb_msg_find_attr_as_uint(res->msgs[0], "groupType", 0);
     891       12042 :                 event_id = get_add_member_event(group_type);
     892       12042 :                 user = dsdb_audit_get_primary_dn(acc->request);
     893       12042 :                 log_membership_change(
     894       12042 :                     acc->module, acc->request, "Added", user, event_id, status);
     895             :         }
     896       12042 : }
     897             : 
     898             : /*
     899             :  * @brief Log the details of a primary group change.
     900             :  *
     901             :  * Retrieve the users primary groupo after the operation has completed
     902             :  * and call log_primary_group_change to log the actual changes.
     903             :  *
     904             :  * @param acc details of the primary group before the operation.
     905             :  * @param status The status code returned by the operation.
     906             :  *
     907             :  * @return an LDB status code.
     908             :  */
     909       23497 : static void log_user_primary_group_change(
     910             :         struct audit_callback_context *acc,
     911             :         const int status)
     912             : {
     913       23497 :         TALLOC_CTX *ctx = talloc_new(NULL);
     914       23497 :         uint32_t new_rid = UINT32_MAX;
     915       23497 :         struct dom_sid *account_sid = NULL;
     916             :         int ret;
     917       23497 :         const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
     918             : 
     919       23497 :         if (status == LDB_SUCCESS && msg != NULL) {
     920       23453 :                 struct ldb_result *res = NULL;
     921       23453 :                 ret = dsdb_module_search_dn(
     922             :                         acc->module,
     923             :                         ctx,
     924             :                         &res,
     925        3973 :                         msg->dn,
     926             :                         primary_group_attr,
     927             :                         DSDB_FLAG_NEXT_MODULE |
     928             :                         DSDB_SEARCH_REVEAL_INTERNALS |
     929             :                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
     930             :                         NULL);
     931       23453 :                 if (ret == LDB_SUCCESS) {
     932       12213 :                         new_rid = ldb_msg_find_attr_as_uint(
     933             :                                 msg,
     934             :                                 "primaryGroupID",
     935             :                                 ~0);
     936       12213 :                         account_sid = samdb_result_dom_sid(
     937             :                                 ctx,
     938       12213 :                                 res->msgs[0],
     939             :                                 "objectSid");
     940             :                 }
     941             :         }
     942             :         /*
     943             :          * If we don't have a new value then the user has been deleted
     944             :          * which we currently do not log.
     945             :          * Otherwise only log if the primary group has actually changed.
     946             :          */
     947       23683 :         if (account_sid != NULL &&
     948       12397 :             new_rid != UINT32_MAX &&
     949       12211 :             acc->primary_group != new_rid) {
     950       12171 :                 const char* group = get_primary_group_dn(
     951             :                         ctx,
     952             :                         acc->module,
     953             :                         account_sid,
     954             :                         new_rid);
     955       12171 :                 log_primary_group_change(
     956             :                         acc->module,
     957       12171 :                         acc->request,
     958             :                         "PrimaryGroup",
     959             :                         group,
     960             :                         status);
     961             :                 /*
     962             :                  * Are we adding a new user with the primaryGroupID
     963             :                  * set. If so and we're generating JSON audit logs, will need to
     964             :                  * generate an "Add" message with the appropriate windows
     965             :                  * event id.
     966             :                  */
     967       12171 :                 if (acc->request->operation == LDB_ADD) {
     968       12042 :                         log_new_user_added_to_primary_group(
     969             :                             ctx, acc, group, status);
     970             :                 }
     971             :         }
     972       23497 :         TALLOC_FREE(ctx);
     973       23497 : }
     974             : 
     975             : /*
     976             :  * @brief log the changes to users group membership.
     977             :  *
     978             :  * Retrieve the users group memberships after the operation has completed
     979             :  * and call log_membership_changes to log the actual changes.
     980             :  *
     981             :  * @param acc details of the group memberships before the operation.
     982             :  * @param status The status code returned by the operation.
     983             :  *
     984             :  */
     985        5653 : static void log_group_membership_changes(
     986             :         struct audit_callback_context *acc,
     987             :         const int status)
     988             : {
     989        5653 :         TALLOC_CTX *ctx = talloc_new(NULL);
     990        5653 :         struct ldb_message_element *new_val = NULL;
     991             :         int ret;
     992        5653 :         uint32_t group_type = 0;
     993        5653 :         const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
     994        5653 :         if (status == LDB_SUCCESS && msg != NULL) {
     995        5652 :                 struct ldb_result *res = NULL;
     996        5652 :                 ret = dsdb_module_search_dn(
     997             :                         acc->module,
     998             :                         ctx,
     999             :                         &res,
    1000        1146 :                         msg->dn,
    1001             :                         group_attrs,
    1002             :                         DSDB_FLAG_NEXT_MODULE |
    1003             :                         DSDB_SEARCH_REVEAL_INTERNALS |
    1004             :                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
    1005             :                         NULL);
    1006        5652 :                 if (ret == LDB_SUCCESS) {
    1007        5315 :                         new_val = ldb_msg_find_element(res->msgs[0], "member");
    1008        5315 :                         group_type = ldb_msg_find_attr_as_uint(
    1009        5315 :                             res->msgs[0], "groupType", 0);
    1010        9560 :                         log_membership_changes(acc->module,
    1011        5315 :                                                acc->request,
    1012             :                                                new_val,
    1013             :                                                acc->members,
    1014             :                                                group_type,
    1015             :                                                status);
    1016        5315 :                         TALLOC_FREE(ctx);
    1017        5315 :                         return;
    1018             :                 }
    1019             :         }
    1020             :         /*
    1021             :          * If we get here either
    1022             :          *   one of the lower level modules failed and the group record did
    1023             :          *   not get updated
    1024             :          * or
    1025             :          *   the updated group record could not be read.
    1026             :          *
    1027             :          * In both cases it does not make sense to log individual membership
    1028             :          * changes so we log a group membership change "Failure" message.
    1029             :          *
    1030             :          */
    1031         338 :         log_membership_change(acc->module,
    1032         338 :                               acc->request,
    1033             :                               "Failure",
    1034             :                               "",
    1035             :                               EVT_ID_NONE,
    1036             :                               status);
    1037         338 :         TALLOC_FREE(ctx);
    1038             : }
    1039             : 
    1040             : /*
    1041             :  * @brief call back function to log changes to the group memberships.
    1042             :  *
    1043             :  * Call back function to log changes to the uses broup memberships.
    1044             :  *
    1045             :  * @param req the ldb request.
    1046             :  * @param ares the ldb result
    1047             :  *
    1048             :  * @return am LDB status code.
    1049             :  */
    1050       29147 : static int group_audit_callback(
    1051             :         struct ldb_request *req,
    1052             :         struct ldb_reply *ares)
    1053             : {
    1054       29147 :         struct audit_callback_context *ac = NULL;
    1055             : 
    1056       29147 :         ac = talloc_get_type(
    1057             :                 req->context,
    1058             :                 struct audit_callback_context);
    1059             : 
    1060       29147 :         if (!ares) {
    1061           0 :                 return ldb_module_done(
    1062             :                                 ac->request, NULL, NULL,
    1063             :                                 LDB_ERR_OPERATIONS_ERROR);
    1064             :         }
    1065             : 
    1066             :         /* pass on to the callback */
    1067       29147 :         switch (ares->type) {
    1068           0 :         case LDB_REPLY_ENTRY:
    1069           0 :                 return ldb_module_send_entry(
    1070             :                         ac->request,
    1071             :                         ares->message,
    1072             :                         ares->controls);
    1073             : 
    1074           0 :         case LDB_REPLY_REFERRAL:
    1075           0 :                 return ldb_module_send_referral(
    1076             :                         ac->request,
    1077             :                         ares->referral);
    1078             : 
    1079       29147 :         case LDB_REPLY_DONE:
    1080             :                 /*
    1081             :                  * Log on DONE now we have a result code
    1082             :                  */
    1083       29147 :                 ac->log_changes(ac, ares->error);
    1084       29147 :                 return ldb_module_done(
    1085             :                         ac->request,
    1086             :                         ares->controls,
    1087             :                         ares->response,
    1088             :                         ares->error);
    1089             :                 break;
    1090             : 
    1091           0 :         default:
    1092             :                 /* Can't happen */
    1093           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    1094             :         }
    1095             : }
    1096             : 
    1097             : /*
    1098             :  * @brief Does this request change the primary group.
    1099             :  *
    1100             :  * Does the request change the primary group, i.e. does it contain the
    1101             :  * primaryGroupID attribute.
    1102             :  *
    1103             :  * @param req the request to examine.
    1104             :  *
    1105             :  * @return True if the request modifies the primary group.
    1106             :  */
    1107      460704 : static bool has_primary_group_id(struct ldb_request *req)
    1108             : {
    1109      460704 :         struct ldb_message_element *el = NULL;
    1110      460704 :         const struct ldb_message *msg = NULL;
    1111             : 
    1112      460704 :         msg = dsdb_audit_get_message(req);
    1113      460704 :         el = ldb_msg_find_element(msg, "primaryGroupID");
    1114             : 
    1115      460704 :         return (el != NULL);
    1116             : }
    1117             : 
    1118             : /*
    1119             :  * @brief Does this request change group membership.
    1120             :  *
    1121             :  * Does the request change the ses group memberships, i.e. does it contain the
    1122             :  * member attribute.
    1123             :  *
    1124             :  * @param req the request to examine.
    1125             :  *
    1126             :  * @return True if the request modifies the users group memberships.
    1127             :  */
    1128      466354 : static bool has_group_membership_changes(struct ldb_request *req)
    1129             : {
    1130      466354 :         struct ldb_message_element *el = NULL;
    1131      466354 :         const struct ldb_message *msg = NULL;
    1132             : 
    1133      466354 :         msg = dsdb_audit_get_message(req);
    1134      466354 :         el = ldb_msg_find_element(msg, "member");
    1135             : 
    1136      466354 :         return (el != NULL);
    1137             : }
    1138             : 
    1139             : 
    1140             : 
    1141             : /*
    1142             :  * @brief Install the callback function to log an add request.
    1143             :  *
    1144             :  * Install the callback function to log an add request changing the users
    1145             :  * group memberships. As we want to log the returned status code, we need to
    1146             :  * register a callback function that will be called once the operation has
    1147             :  * completed.
    1148             :  *
    1149             :  * This function reads the current user record so that we can log the before
    1150             :  * and after state.
    1151             :  *
    1152             :  * @param module The ldb module.
    1153             :  * @param req The modify request.
    1154             :  *
    1155             :  * @return and LDB status code.
    1156             :  */
    1157         614 : static int set_group_membership_add_callback(
    1158             :         struct ldb_module *module,
    1159             :         struct ldb_request *req)
    1160             : {
    1161         614 :         struct audit_callback_context *context = NULL;
    1162         614 :         struct ldb_request *new_req = NULL;
    1163         614 :         struct ldb_context *ldb = NULL;
    1164             :         int ret;
    1165             :         /*
    1166             :          * Adding group memberships so will need to log the changes.
    1167             :          */
    1168         614 :         ldb = ldb_module_get_ctx(module);
    1169         614 :         context = talloc_zero(req, struct audit_callback_context);
    1170             : 
    1171         614 :         if (context == NULL) {
    1172           0 :                 return ldb_oom(ldb);
    1173             :         }
    1174         614 :         context->request = req;
    1175         614 :         context->module = module;
    1176         614 :         context->log_changes = log_group_membership_changes;
    1177             :         /*
    1178             :          * We want to log the return code status, so we need to register
    1179             :          * a callback function to get the actual result.
    1180             :          * We need to take a new copy so that we don't alter the callers copy
    1181             :          */
    1182         614 :         ret = ldb_build_add_req(
    1183             :                 &new_req,
    1184             :                 ldb,
    1185             :                 req,
    1186             :                 req->op.add.message,
    1187             :                 req->controls,
    1188             :                 context,
    1189             :                 group_audit_callback,
    1190             :                 req);
    1191         614 :         if (ret != LDB_SUCCESS) {
    1192           0 :                 return ret;
    1193             :         }
    1194         614 :         return ldb_next_request(module, new_req);
    1195             : }
    1196             : 
    1197             : 
    1198             : /*
    1199             :  * @brief Install the callback function to log a modify request.
    1200             :  *
    1201             :  * Install the callback function to log a modify request changing the primary
    1202             :  * group . As we want to log the returned status code, we need to register a
    1203             :  * callback function that will be called once the operation has completed.
    1204             :  *
    1205             :  * This function reads the current user record so that we can log the before
    1206             :  * and after state.
    1207             :  *
    1208             :  * @param module The ldb module.
    1209             :  * @param req The modify request.
    1210             :  *
    1211             :  * @return and LDB status code.
    1212             :  */
    1213       11412 : static int set_primary_group_modify_callback(
    1214             :         struct ldb_module *module,
    1215             :         struct ldb_request *req)
    1216             : {
    1217       11412 :         struct audit_callback_context *context = NULL;
    1218       11412 :         struct ldb_request *new_req = NULL;
    1219       11412 :         struct ldb_context *ldb = NULL;
    1220       11412 :         const struct ldb_message *msg = NULL;
    1221       11412 :         struct ldb_result *res = NULL;
    1222             :         int ret;
    1223             : 
    1224       11412 :         TALLOC_CTX *ctx = talloc_new(NULL);
    1225             : 
    1226       11412 :         ldb = ldb_module_get_ctx(module);
    1227             : 
    1228       11412 :         context = talloc_zero(req, struct audit_callback_context);
    1229       11412 :         if (context == NULL) {
    1230           0 :                 ret = ldb_oom(ldb);
    1231           0 :                 goto exit;
    1232             :         }
    1233       11412 :         context->request = req;
    1234       11412 :         context->module = module;
    1235       11412 :         context->log_changes = log_user_primary_group_change;
    1236             : 
    1237       11412 :         msg = dsdb_audit_get_message(req);
    1238       11412 :         ret = dsdb_module_search_dn(
    1239             :                 module,
    1240             :                 ctx,
    1241             :                 &res,
    1242        1847 :                 msg->dn,
    1243             :                 primary_group_attr,
    1244             :                 DSDB_FLAG_NEXT_MODULE |
    1245             :                 DSDB_SEARCH_REVEAL_INTERNALS |
    1246             :                 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
    1247             :                 NULL);
    1248       11412 :         if (ret == LDB_SUCCESS) {
    1249             :                 uint32_t pg;
    1250       11391 :                 pg = ldb_msg_find_attr_as_uint(
    1251       11391 :                         res->msgs[0],
    1252             :                         "primaryGroupID",
    1253             :                         ~0);
    1254       11391 :                 context->primary_group = pg;
    1255             :         }
    1256             :         /*
    1257             :          * We want to log the return code status, so we need to register
    1258             :          * a callback function to get the actual result.
    1259             :          * We need to take a new copy so that we don't alter the callers copy
    1260             :          */
    1261       11412 :         ret = ldb_build_mod_req(
    1262             :                 &new_req,
    1263             :                 ldb,
    1264             :                 req,
    1265             :                 req->op.add.message,
    1266             :                 req->controls,
    1267             :                 context,
    1268             :                 group_audit_callback,
    1269             :                 req);
    1270       11412 :         if (ret != LDB_SUCCESS) {
    1271           0 :                 goto exit;
    1272             :         }
    1273       11412 :         ret = ldb_next_request(module, new_req);
    1274       11412 : exit:
    1275       11412 :         TALLOC_FREE(ctx);
    1276       11412 :         return ret;
    1277             : }
    1278             : 
    1279             : /*
    1280             :  * @brief Install the callback function to log an add request.
    1281             :  *
    1282             :  * Install the callback function to log an add request changing the primary
    1283             :  * group . As we want to log the returned status code, we need to register a
    1284             :  * callback function that will be called once the operation has completed.
    1285             :  *
    1286             :  * This function reads the current user record so that we can log the before
    1287             :  * and after state.
    1288             :  *
    1289             :  * @param module The ldb module.
    1290             :  * @param req The modify request.
    1291             :  *
    1292             :  * @return and LDB status code.
    1293             :  */
    1294       12085 : static int set_primary_group_add_callback(
    1295             :         struct ldb_module *module,
    1296             :         struct ldb_request *req)
    1297             : {
    1298       12085 :         struct audit_callback_context *context = NULL;
    1299       12085 :         struct ldb_request *new_req = NULL;
    1300       12085 :         struct ldb_context *ldb = NULL;
    1301             :         int ret;
    1302             :         /*
    1303             :          * Adding a user with a primary group.
    1304             :          */
    1305       12085 :         ldb = ldb_module_get_ctx(module);
    1306       12085 :         context = talloc_zero(req, struct audit_callback_context);
    1307             : 
    1308       12085 :         if (context == NULL) {
    1309           0 :                 return ldb_oom(ldb);
    1310             :         }
    1311       12085 :         context->request = req;
    1312       12085 :         context->module = module;
    1313       12085 :         context->log_changes = log_user_primary_group_change;
    1314             :         /*
    1315             :          * We want to log the return code status, so we need to register
    1316             :          * a callback function to get the actual result.
    1317             :          * We need to take a new copy so that we don't alter the callers copy
    1318             :          */
    1319       12085 :         ret = ldb_build_add_req(
    1320             :                 &new_req,
    1321             :                 ldb,
    1322             :                 req,
    1323             :                 req->op.add.message,
    1324             :                 req->controls,
    1325             :                 context,
    1326             :                 group_audit_callback,
    1327             :                 req);
    1328       12085 :         if (ret != LDB_SUCCESS) {
    1329           0 :                 return ret;
    1330             :         }
    1331       12085 :         return ldb_next_request(module, new_req);
    1332             : }
    1333             : 
    1334             : /*
    1335             :  * @brief Module handler for add operations.
    1336             :  *
    1337             :  * Inspect the current add request, and if needed log any group membership
    1338             :  * changes.
    1339             :  *
    1340             :  * @param module The ldb module.
    1341             :  * @param req The modify request.
    1342             :  *
    1343             :  * @return and LDB status code.
    1344             :  */
    1345      822091 : static int group_add(
    1346             :         struct ldb_module *module,
    1347             :         struct ldb_request *req)
    1348             : {
    1349             : 
    1350      695461 :         struct audit_context *ac =
    1351      822091 :                 talloc_get_type(
    1352             :                         ldb_module_get_private(module),
    1353             :                         struct audit_context);
    1354             :         /*
    1355             :          * Currently we don't log replicated group changes
    1356             :          */
    1357      822091 :         if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
    1358      331389 :                 return ldb_next_request(module, req);
    1359             :         }
    1360             : 
    1361      871811 :         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL) ||
    1362      871811 :                 CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
    1363      602081 :                 (ac->msg_ctx && ac->send_events)) {
    1364             :                 /*
    1365             :                  * Avoid the overheads of logging unless it has been
    1366             :                  * enabled
    1367             :                  */
    1368      155293 :                 if (has_group_membership_changes(req)) {
    1369         614 :                         return set_group_membership_add_callback(module, req);
    1370             :                 }
    1371      154679 :                 if (has_primary_group_id(req)) {
    1372       12085 :                         return set_primary_group_add_callback(module, req);
    1373             :                 }
    1374             :         }
    1375      478003 :         return ldb_next_request(module, req);
    1376             : }
    1377             : 
    1378             : /*
    1379             :  * @brief Module handler for delete operations.
    1380             :  *
    1381             :  * Currently there is no logging for delete operations.
    1382             :  *
    1383             :  * @param module The ldb module.
    1384             :  * @param req The modify request.
    1385             :  *
    1386             :  * @return and LDB status code.
    1387             :  */
    1388          17 : static int group_delete(
    1389             :         struct ldb_module *module,
    1390             :         struct ldb_request *req)
    1391             : {
    1392          17 :         return ldb_next_request(module, req);
    1393             : }
    1394             : 
    1395             : /*
    1396             :  * @brief Install the callback function to log a modify request.
    1397             :  *
    1398             :  * Install the callback function to log a modify request. As we want to log the
    1399             :  * returned status code, we need to register a callback function that will be
    1400             :  * called once the operation has completed.
    1401             :  *
    1402             :  * This function reads the current user record so that we can log the before
    1403             :  * and after state.
    1404             :  *
    1405             :  * @param module The ldb module.
    1406             :  * @param req The modify request.
    1407             :  *
    1408             :  * @return and LDB status code.
    1409             :  */
    1410        5036 : static int set_group_modify_callback(
    1411             :         struct ldb_module *module,
    1412             :         struct ldb_request *req)
    1413             : {
    1414        5036 :         struct audit_callback_context *context = NULL;
    1415        5036 :         struct ldb_request *new_req = NULL;
    1416        5036 :         struct ldb_context *ldb = NULL;
    1417        5036 :         struct ldb_result *res = NULL;
    1418             :         int ret;
    1419             : 
    1420        5036 :         ldb = ldb_module_get_ctx(module);
    1421        5036 :         context = talloc_zero(req, struct audit_callback_context);
    1422             : 
    1423        5036 :         if (context == NULL) {
    1424           0 :                 return ldb_oom(ldb);
    1425             :         }
    1426        5036 :         context->request = req;
    1427        5036 :         context->module  = module;
    1428        5036 :         context->log_changes = log_group_membership_changes;
    1429             : 
    1430             :         /*
    1431             :          * About to change the group memberships need to read
    1432             :          * the current state from the database.
    1433             :          */
    1434        5036 :         ret = dsdb_module_search_dn(
    1435             :                 module,
    1436             :                 context,
    1437             :                 &res,
    1438        5036 :                 req->op.add.message->dn,
    1439             :                 group_attrs,
    1440             :                 DSDB_FLAG_NEXT_MODULE |
    1441             :                 DSDB_SEARCH_REVEAL_INTERNALS |
    1442             :                 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
    1443             :                 NULL);
    1444        5036 :         if (ret == LDB_SUCCESS) {
    1445        5032 :                 context->members = ldb_msg_find_element(res->msgs[0], "member");
    1446             :         }
    1447             : 
    1448        5036 :         ret = ldb_build_mod_req(
    1449             :                 &new_req,
    1450             :                 ldb,
    1451             :                 req,
    1452             :                 req->op.mod.message,
    1453             :                 req->controls,
    1454             :                 context,
    1455             :                 group_audit_callback,
    1456             :                 req);
    1457        5036 :         if (ret != LDB_SUCCESS) {
    1458           0 :                 return ret;
    1459             :         }
    1460        5036 :         return ldb_next_request(module, new_req);
    1461             : }
    1462             : 
    1463             : /*
    1464             :  * @brief Module handler for modify operations.
    1465             :  *
    1466             :  * Inspect the current modify request, and if needed log any group membership
    1467             :  * changes.
    1468             :  *
    1469             :  * @param module The ldb module.
    1470             :  * @param req The modify request.
    1471             :  *
    1472             :  * @return and LDB status code.
    1473             :  */
    1474      896292 : static int group_modify(
    1475             :         struct ldb_module *module,
    1476             :         struct ldb_request *req)
    1477             : {
    1478             : 
    1479      732646 :         struct audit_context *ac =
    1480      896292 :                 talloc_get_type(
    1481             :                         ldb_module_get_private(module),
    1482             :                         struct audit_context);
    1483             :         /*
    1484             :          * Currently we don't log replicated group changes
    1485             :          */
    1486      896292 :         if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
    1487       71619 :                 return ldb_next_request(module, req);
    1488             :         }
    1489             : 
    1490     1487709 :         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL) ||
    1491     1487709 :             CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
    1492     1086629 :                 (ac->msg_ctx && ac->send_events)) {
    1493             :                 /*
    1494             :                  * Avoid the overheads of logging unless it has been
    1495             :                  * enabled
    1496             :                  */
    1497      311061 :                 if (has_group_membership_changes(req)) {
    1498        5036 :                         return set_group_modify_callback(module, req);
    1499             :                 }
    1500      306025 :                 if (has_primary_group_id(req)) {
    1501       11412 :                         return set_primary_group_modify_callback(module, req);
    1502             :                 }
    1503             :         }
    1504      808225 :         return ldb_next_request(module, req);
    1505             : }
    1506             : 
    1507             : /*
    1508             :  * @brief ldb module initialisation
    1509             :  *
    1510             :  * Initialise the module, loading the private data etc.
    1511             :  *
    1512             :  * @param module The ldb module to initialise.
    1513             :  *
    1514             :  * @return An LDB status code.
    1515             :  */
    1516      132453 : static int group_init(struct ldb_module *module)
    1517             : {
    1518             : 
    1519      132453 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    1520      132453 :         struct audit_context *context = NULL;
    1521       92424 :         struct loadparm_context *lp_ctx
    1522      132453 :                 = talloc_get_type_abort(
    1523             :                         ldb_get_opaque(ldb, "loadparm"),
    1524             :                         struct loadparm_context);
    1525      132453 :         struct tevent_context *ev = ldb_get_event_context(ldb);
    1526             : 
    1527      132453 :         context = talloc_zero(module, struct audit_context);
    1528      132453 :         if (context == NULL) {
    1529           0 :                 return ldb_module_oom(module);
    1530             :         }
    1531             : 
    1532      132453 :         if (lp_ctx && lpcfg_dsdb_group_change_notification(lp_ctx)) {
    1533       81617 :                 context->send_events = true;
    1534       81617 :                 context->msg_ctx = imessaging_client_init(context,
    1535             :                                                           lp_ctx,
    1536             :                                                           ev);
    1537             :         }
    1538             : 
    1539      132453 :         ldb_module_set_private(module, context);
    1540      132453 :         return ldb_next_init(module);
    1541             : }
    1542             : 
    1543             : static const struct ldb_module_ops ldb_group_audit_log_module_ops = {
    1544             :         .name              = "group_audit_log",
    1545             :         .add               = group_add,
    1546             :         .modify            = group_modify,
    1547             :         .del               = group_delete,
    1548             :         .init_context      = group_init,
    1549             : };
    1550             : 
    1551        5536 : int ldb_group_audit_log_module_init(const char *version)
    1552             : {
    1553        5536 :         LDB_MODULE_CHECK_VERSION(version);
    1554        5536 :         return ldb_register_module(&ldb_group_audit_log_module_ops);
    1555             : }

Generated by: LCOV version 1.13