LCOV - code coverage report
Current view: top level - source4/rpc_server/drsuapi - updaterefs.c (source / functions) Hit Total Coverage
Test: coverage report for fix-15632 9995c5c2 Lines: 104 166 62.7 %
Date: 2024-04-13 12:30:31 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    implement the DRSUpdateRefs call
       5             : 
       6             :    Copyright (C) Andrew Tridgell 2009
       7             :    
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             :    
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             :    
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "includes.h"
      23             : #include "rpc_server/dcerpc_server.h"
      24             : #include "dsdb/samdb/samdb.h"
      25             : #include "libcli/security/security.h"
      26             : #include "libcli/security/session.h"
      27             : #include "rpc_server/drsuapi/dcesrv_drsuapi.h"
      28             : #include "auth/session.h"
      29             : #include "librpc/gen_ndr/ndr_drsuapi.h"
      30             : #include "librpc/gen_ndr/ndr_irpc_c.h"
      31             : #include "lib/messaging/irpc.h"
      32             : 
      33             : #undef DBGC_CLASS
      34             : #define DBGC_CLASS            DBGC_DRS_REPL
      35             : 
      36             : struct repsTo {
      37             :         uint32_t count;
      38             :         struct repsFromToBlob *r;
      39             : };
      40             : 
      41        2579 : static WERROR uref_check_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
      42             :                               struct ldb_dn *dn, struct GUID *dest_guid,
      43             :                               uint32_t options)
      44             : {
      45           0 :         struct repsTo reps;
      46           0 :         WERROR werr;
      47           0 :         unsigned int i;
      48        2579 :         bool found = false;
      49             : 
      50        2579 :         werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
      51        2579 :         if (!W_ERROR_IS_OK(werr)) {
      52           0 :                 return werr;
      53             :         }
      54             : 
      55        4272 :         for (i=0; i<reps.count; i++) {
      56        4014 :                 if (GUID_equal(dest_guid,
      57        4014 :                                &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
      58        2321 :                         found = true;
      59        2321 :                         break;
      60             :                 }
      61             :         }
      62             : 
      63        2579 :         if (options & DRSUAPI_DRS_ADD_REF) {
      64        2576 :                 if (found && !(options & DRSUAPI_DRS_DEL_REF)) {
      65         928 :                         return WERR_DS_DRA_REF_ALREADY_EXISTS;
      66             :                 }
      67             :         }
      68             : 
      69        1651 :         if (options & DRSUAPI_DRS_DEL_REF) {
      70        1636 :                 if (!found && !(options & DRSUAPI_DRS_ADD_REF)) {
      71           1 :                         return WERR_DS_DRA_REF_NOT_FOUND;
      72             :                 }
      73             :         }
      74             : 
      75        1650 :         return WERR_OK;
      76             : }
      77             : 
      78             : /*
      79             :   add a replication destination for a given partition GUID
      80             :  */
      81        1648 : static WERROR uref_add_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
      82             :                             struct ldb_dn *dn, struct repsFromTo1 *dest, 
      83             :                             uint32_t options)
      84             : {
      85           0 :         struct repsTo reps;
      86           0 :         WERROR werr;
      87           0 :         unsigned int i;
      88             : 
      89        1648 :         werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
      90        1648 :         if (!W_ERROR_IS_OK(werr)) {
      91           0 :                 return werr;
      92             :         }
      93             : 
      94        2906 :         for (i=0; i<reps.count; i++) {
      95        1258 :                 if (GUID_equal(&dest->source_dsa_obj_guid,
      96        1258 :                                &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
      97           0 :                         if (options & DRSUAPI_DRS_GETCHG_CHECK) {
      98           0 :                                 return WERR_OK;
      99             :                         } else {
     100           0 :                                 return WERR_DS_DRA_REF_ALREADY_EXISTS;
     101             :                         }
     102             :                 }
     103             :         }
     104             : 
     105        1648 :         reps.r = talloc_realloc(mem_ctx, reps.r, struct repsFromToBlob, reps.count+1);
     106        1648 :         if (reps.r == NULL) {
     107           0 :                 return WERR_DS_DRA_INTERNAL_ERROR;
     108             :         }
     109        1648 :         ZERO_STRUCT(reps.r[reps.count]);
     110        1648 :         reps.r[reps.count].version = 1;
     111        1648 :         reps.r[reps.count].ctr.ctr1 = *dest;
     112             :         /* add the GCSPN flag if the client asked for it */
     113        1648 :         reps.r[reps.count].ctr.ctr1.replica_flags |= (options & DRSUAPI_DRS_REF_GCSPN);
     114        1648 :         reps.count++;
     115             : 
     116        1648 :         werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
     117        1648 :         if (!W_ERROR_IS_OK(werr)) {
     118           0 :                 return werr;
     119             :         }
     120             : 
     121        1648 :         return WERR_OK; 
     122             : }
     123             : 
     124             : /*
     125             :   delete a replication destination for a given partition GUID
     126             :  */
     127        1635 : static WERROR uref_del_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
     128             :                             struct ldb_dn *dn, struct GUID *dest_guid, 
     129             :                             uint32_t options)
     130             : {
     131           0 :         struct repsTo reps;
     132           0 :         WERROR werr;
     133           0 :         unsigned int i;
     134        1635 :         bool found = false;
     135             : 
     136        1635 :         werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
     137        1635 :         if (!W_ERROR_IS_OK(werr)) {
     138           0 :                 return werr;
     139             :         }
     140             : 
     141        4286 :         for (i=0; i<reps.count; i++) {
     142        2651 :                 if (GUID_equal(dest_guid,
     143        2651 :                                &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
     144        1393 :                         if (i+1 < reps.count) {
     145          73 :                                 memmove(&reps.r[i], &reps.r[i+1], sizeof(reps.r[i])*(reps.count-(i+1)));
     146             :                         }
     147             :                         /*
     148             :                          * If we remove an element, decrement i so that we don't
     149             :                          * skip over the element following.
     150             :                          */
     151        1393 :                         i--;
     152        1393 :                         reps.count--;
     153        1393 :                         found = true;
     154             :                 }
     155             :         }
     156             : 
     157        1635 :         werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
     158        1635 :         if (!W_ERROR_IS_OK(werr)) {
     159           0 :                 return werr;
     160             :         }
     161             : 
     162        1635 :         if (!found &&
     163         242 :             !(options & DRSUAPI_DRS_GETCHG_CHECK) &&
     164         242 :             !(options & DRSUAPI_DRS_ADD_REF)) {
     165           0 :                 return WERR_DS_DRA_REF_NOT_FOUND;
     166             :         }
     167             : 
     168        1635 :         return WERR_OK; 
     169             : }
     170             : 
     171             : struct drepl_refresh_state {
     172             :         struct dreplsrv_refresh r;
     173             : };
     174             : 
     175             : /**
     176             :  * @brief Update the references for the given NC and the destination DSA object
     177             :  *
     178             :  * This function is callable from non RPC functions (ie. getncchanges), it
     179             :  * will validate the request to update reference and then will add/del a repsTo
     180             :  * to the specified server referenced by its DSA GUID in the request.
     181             :  *
     182             :  * @param[in]       msg_ctx          Messaging context for sending partition
     183             :  *                                   refresh in dreplsrv
     184             :  *
     185             :  * @param[in]       b_state          A bind_state object
     186             :  *
     187             :  * @param[in]       mem_ctx          A talloc context for memory allocation
     188             :  *
     189             :  * @param[in]       req              A drsuapi_DsReplicaUpdateRefsRequest1
     190             :  *                                   object which NC, which server and which
     191             :  *                                   action (add/delete) should be performed
     192             :  *
     193             :  * @return                           WERR_OK is success, different error
     194             :  *                                   otherwise.
     195             :  */
     196        2579 : WERROR drsuapi_UpdateRefs(struct imessaging_context *msg_ctx,
     197             :                           struct tevent_context *event_ctx,
     198             :                           struct drsuapi_bind_state *b_state, TALLOC_CTX *mem_ctx,
     199             :                           struct drsuapi_DsReplicaUpdateRefsRequest1 *req)
     200             : {
     201           0 :         WERROR werr;
     202           0 :         int ret;
     203           0 :         struct ldb_dn *dn_normalised;
     204           0 :         struct ldb_dn *nc_root;
     205        2579 :         struct ldb_context *sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
     206           0 :         struct dcerpc_binding_handle *irpc_handle;
     207           0 :         struct tevent_req *subreq;
     208           0 :         struct drepl_refresh_state *state;
     209             : 
     210             : 
     211        2579 :         DEBUG(4,("DsReplicaUpdateRefs for host '%s' with GUID %s options 0x%08x nc=%s\n",
     212             :                  req->dest_dsa_dns_name, GUID_string(mem_ctx, &req->dest_dsa_guid),
     213             :                  req->options,
     214             :                  drs_ObjectIdentifier_to_debug_string(mem_ctx, req->naming_context)));
     215             : 
     216             :         /*
     217             :          * 4.1.26.2 Server Behavior of the IDL_DRSUpdateRefs Method
     218             :          * Implements the input validation checks
     219             :          */
     220        2579 :         if (GUID_all_zero(&req->dest_dsa_guid)) {
     221           0 :                 return WERR_DS_DRA_INVALID_PARAMETER;
     222             :         }
     223             : 
     224             :         /* FIXME it seems that we should check the length of the stuff too*/
     225        2579 :         if (req->dest_dsa_dns_name == NULL) {
     226           0 :                 return WERR_DS_DRA_INVALID_PARAMETER;
     227             :         }
     228             : 
     229        2579 :         if (!(req->options & (DRSUAPI_DRS_DEL_REF|DRSUAPI_DRS_ADD_REF))) {
     230           0 :                 return WERR_DS_DRA_INVALID_PARAMETER;
     231             :         }
     232             : 
     233        2579 :         ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx, sam_ctx, req->naming_context,
     234             :                                                      &dn_normalised, &nc_root);
     235        2579 :         if (ret != LDB_SUCCESS) {
     236           0 :                 DBG_WARNING("Didn't find a nc for %s: %s\n",
     237             :                             drs_ObjectIdentifier_to_debug_string(mem_ctx,
     238             :                                                                  req->naming_context),
     239             :                             ldb_errstring(sam_ctx));
     240           0 :                 return WERR_DS_DRA_BAD_NC;
     241             :         }
     242        2579 :         if (ldb_dn_compare(dn_normalised, nc_root) != 0) {
     243           0 :                 DBG_NOTICE("dn %s is not equal to %s (from %s)\n",
     244             :                            ldb_dn_get_linearized(dn_normalised),
     245             :                            ldb_dn_get_linearized(nc_root),
     246             :                            drs_ObjectIdentifier_to_debug_string(mem_ctx,
     247             :                                                                 req->naming_context));
     248           0 :                 return WERR_DS_DRA_BAD_NC;
     249             :         }
     250             : 
     251             :         /*
     252             :          * First check without a transaction open.
     253             :          *
     254             :          * This means that in the usual case, it will never open it and never
     255             :          * bother to refresh the dreplsrv.
     256             :          */
     257        2579 :         werr = uref_check_dest(sam_ctx,
     258             :                                mem_ctx,
     259             :                                dn_normalised,
     260             :                                &req->dest_dsa_guid,
     261             :                                req->options);
     262        2579 :         if (W_ERROR_EQUAL(werr, WERR_DS_DRA_REF_ALREADY_EXISTS) ||
     263        1651 :             W_ERROR_EQUAL(werr, WERR_DS_DRA_REF_NOT_FOUND)) {
     264         929 :                 if (req->options & DRSUAPI_DRS_GETCHG_CHECK) {
     265         927 :                         return WERR_OK;
     266             :                 }
     267           2 :                 return werr;
     268             :         }
     269             : 
     270        1650 :         if (ldb_transaction_start(sam_ctx) != LDB_SUCCESS) {
     271           0 :                 DEBUG(0,(__location__ ": Failed to start transaction on samdb: %s\n",
     272             :                          ldb_errstring(sam_ctx)));
     273           0 :                 return WERR_DS_DRA_INTERNAL_ERROR;
     274             :         }
     275             : 
     276        1650 :         if (req->options & DRSUAPI_DRS_DEL_REF) {
     277        1635 :                 werr = uref_del_dest(sam_ctx,
     278             :                                      mem_ctx,
     279             :                                      dn_normalised,
     280             :                                      &req->dest_dsa_guid,
     281             :                                      req->options);
     282        1635 :                 if (!W_ERROR_IS_OK(werr)) {
     283           0 :                         DEBUG(0,("Failed to delete repsTo for %s: %s\n",
     284             :                                  GUID_string(mem_ctx, &req->dest_dsa_guid),
     285             :                                  win_errstr(werr)));
     286           0 :                         goto failed;
     287             :                 }
     288             :         }
     289             : 
     290        1650 :         if (req->options & DRSUAPI_DRS_ADD_REF) {
     291           0 :                 struct repsFromTo1 dest;
     292           0 :                 struct repsFromTo1OtherInfo oi;
     293             : 
     294        1648 :                 ZERO_STRUCT(dest);
     295        1648 :                 ZERO_STRUCT(oi);
     296             : 
     297        1648 :                 oi.dns_name = req->dest_dsa_dns_name;
     298        1648 :                 dest.other_info          = &oi;
     299        1648 :                 dest.source_dsa_obj_guid = req->dest_dsa_guid;
     300        1648 :                 dest.replica_flags       = req->options;
     301             : 
     302        1648 :                 werr = uref_add_dest(sam_ctx,
     303             :                                      mem_ctx,
     304             :                                      dn_normalised,
     305             :                                      &dest,
     306             :                                      req->options);
     307        1648 :                 if (!W_ERROR_IS_OK(werr)) {
     308           0 :                         DEBUG(0,("Failed to add repsTo for %s: %s\n",
     309             :                                  GUID_string(mem_ctx, &dest.source_dsa_obj_guid),
     310             :                                  win_errstr(werr)));
     311           0 :                         goto failed;
     312             :                 }
     313             :         }
     314             : 
     315        1650 :         if (ldb_transaction_commit(sam_ctx) != LDB_SUCCESS) {
     316           0 :                 DEBUG(0,(__location__ ": Failed to commit transaction on samdb: %s\n",
     317             :                          ldb_errstring(sam_ctx)));
     318           0 :                 return WERR_DS_DRA_INTERNAL_ERROR;
     319             :         }
     320             : 
     321        1650 :         state = talloc_zero(mem_ctx, struct drepl_refresh_state);
     322        1650 :         if (state == NULL) {
     323           0 :                 return WERR_OK;
     324             :         }
     325             : 
     326        1650 :         irpc_handle = irpc_binding_handle_by_name(mem_ctx, msg_ctx,
     327             :                                                   "dreplsrv", &ndr_table_irpc);
     328        1650 :         if (irpc_handle == NULL) {
     329             :                 /* dreplsrv is not running yet */
     330           0 :                 TALLOC_FREE(state);
     331           0 :                 return WERR_OK;
     332             :         }
     333             : 
     334             :         /*
     335             :          * [Taken from auth_sam_trigger_repl_secret in auth_sam.c]
     336             :          *
     337             :          * This seem to rely on the current IRPC implementation,
     338             :          * which delivers the message in the _send function.
     339             :          *
     340             :          * TODO: we need a ONE_WAY IRPC handle and register
     341             :          * a callback and wait for it to be triggered!
     342             :          */
     343        1650 :         subreq = dcerpc_dreplsrv_refresh_r_send(state, event_ctx,
     344             :                                                 irpc_handle, &state->r);
     345        1650 :         TALLOC_FREE(subreq);
     346        1650 :         TALLOC_FREE(state);
     347             : 
     348        1650 :         return WERR_OK;
     349             : 
     350           0 : failed:
     351           0 :         ldb_transaction_cancel(sam_ctx);
     352           0 :         return werr;
     353             : }
     354             : 
     355             : /* 
     356             :   drsuapi_DsReplicaUpdateRefs
     357             : */
     358        1638 : WERROR dcesrv_drsuapi_DsReplicaUpdateRefs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
     359             :                                           struct drsuapi_DsReplicaUpdateRefs *r)
     360             : {
     361           0 :         struct auth_session_info *session_info =
     362        1638 :                 dcesrv_call_session_info(dce_call);
     363           0 :         struct imessaging_context *imsg_ctx =
     364        1638 :                 dcesrv_imessaging_context(dce_call->conn);
     365           0 :         struct dcesrv_handle *h;
     366           0 :         struct drsuapi_bind_state *b_state;
     367           0 :         struct drsuapi_DsReplicaUpdateRefsRequest1 *req;
     368           0 :         WERROR werr;
     369           0 :         int ret;
     370           0 :         enum security_user_level security_level;
     371             : 
     372        1638 :         DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
     373        1638 :         b_state = h->data;
     374             : 
     375        1638 :         if (r->in.level != 1) {
     376           0 :                 DEBUG(0,("DrReplicUpdateRefs - unsupported level %u\n", r->in.level));
     377           0 :                 return WERR_DS_DRA_INVALID_PARAMETER;
     378             :         }
     379        1638 :         req = &r->in.req.req1;
     380        1638 :         werr = drs_security_access_check(b_state->sam_ctx,
     381             :                                          mem_ctx,
     382             :                                          session_info->security_token,
     383             :                                          req->naming_context,
     384             :                                          GUID_DRS_MANAGE_TOPOLOGY);
     385             : 
     386        1638 :         if (!W_ERROR_IS_OK(werr)) {
     387           0 :                 return werr;
     388             :         }
     389             : 
     390        1638 :         security_level = security_session_user_level(session_info, NULL);
     391        1638 :         if (security_level < SECURITY_ADMINISTRATOR) {
     392             :                 /* check that they are using an DSA objectGUID that they own */
     393        1424 :                 ret = dsdb_validate_dsa_guid(b_state->sam_ctx,
     394        1424 :                                              &req->dest_dsa_guid,
     395        1424 :                                              &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]);
     396        1424 :                 if (ret != LDB_SUCCESS) {
     397           0 :                         DEBUG(0,(__location__ ": Refusing DsReplicaUpdateRefs for sid %s with GUID %s\n",
     398             :                                  dom_sid_string(mem_ctx,
     399             :                                                 &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]),
     400             :                                  GUID_string(mem_ctx, &req->dest_dsa_guid)));
     401           0 :                         return WERR_DS_DRA_ACCESS_DENIED;
     402             :                 }
     403             :         }
     404             : 
     405        1638 :         werr = drsuapi_UpdateRefs(imsg_ctx,
     406             :                                   dce_call->event_ctx,
     407             :                                   b_state,
     408             :                                   mem_ctx,
     409             :                                   req);
     410             : 
     411             : #if 0
     412             :         NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsReplicaUpdateRefs, NDR_BOTH, r);
     413             : #endif
     414             : 
     415        1638 :         return werr;
     416             : }

Generated by: LCOV version 1.14