LCOV - code coverage report
Current view: top level - source4/rpc_server/drsuapi - updaterefs.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 105 141 74.5 %
Date: 2024-06-13 04:01:37 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        2563 : 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             :         struct repsTo reps;
      46             :         WERROR werr;
      47             :         unsigned int i;
      48        2563 :         bool found = false;
      49             : 
      50        2563 :         werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
      51        2563 :         if (!W_ERROR_IS_OK(werr)) {
      52           0 :                 return werr;
      53             :         }
      54             : 
      55        3956 :         for (i=0; i<reps.count; i++) {
      56        3731 :                 if (GUID_equal(dest_guid,
      57        3731 :                                &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
      58        2338 :                         found = true;
      59        2338 :                         break;
      60             :                 }
      61             :         }
      62             : 
      63        2563 :         if (options & DRSUAPI_DRS_ADD_REF) {
      64        2560 :                 if (found && !(options & DRSUAPI_DRS_DEL_REF)) {
      65         908 :                         return WERR_DS_DRA_REF_ALREADY_EXISTS;
      66             :                 }
      67             :         }
      68             : 
      69        1655 :         if (options & DRSUAPI_DRS_DEL_REF) {
      70        1633 :                 if (!found && !(options & DRSUAPI_DRS_ADD_REF)) {
      71           1 :                         return WERR_DS_DRA_REF_NOT_FOUND;
      72             :                 }
      73             :         }
      74             : 
      75        1654 :         return WERR_OK;
      76             : }
      77             : 
      78             : /*
      79             :   add a replication destination for a given partition GUID
      80             :  */
      81        1652 : 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             :         struct repsTo reps;
      86             :         WERROR werr;
      87             :         unsigned int i;
      88             : 
      89        1652 :         werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
      90        1652 :         if (!W_ERROR_IS_OK(werr)) {
      91           0 :                 return werr;
      92             :         }
      93             : 
      94        2961 :         for (i=0; i<reps.count; i++) {
      95        1309 :                 if (GUID_equal(&dest->source_dsa_obj_guid,
      96        1309 :                                &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        1652 :         reps.r = talloc_realloc(mem_ctx, reps.r, struct repsFromToBlob, reps.count+1);
     106        1652 :         if (reps.r == NULL) {
     107           0 :                 return WERR_DS_DRA_INTERNAL_ERROR;
     108             :         }
     109        1652 :         ZERO_STRUCT(reps.r[reps.count]);
     110        1652 :         reps.r[reps.count].version = 1;
     111        1652 :         reps.r[reps.count].ctr.ctr1 = *dest;
     112             :         /* add the GCSPN flag if the client asked for it */
     113        1652 :         reps.r[reps.count].ctr.ctr1.replica_flags |= (options & DRSUAPI_DRS_REF_GCSPN);
     114        1652 :         reps.count++;
     115             : 
     116        1652 :         werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
     117        1652 :         if (!W_ERROR_IS_OK(werr)) {
     118           0 :                 return werr;
     119             :         }
     120             : 
     121        1652 :         return WERR_OK; 
     122             : }
     123             : 
     124             : /*
     125             :   delete a replication destination for a given partition GUID
     126             :  */
     127        1632 : 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             :         struct repsTo reps;
     132             :         WERROR werr;
     133             :         unsigned int i;
     134        1632 :         bool found = false;
     135             : 
     136        1632 :         werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
     137        1632 :         if (!W_ERROR_IS_OK(werr)) {
     138           0 :                 return werr;
     139             :         }
     140             : 
     141        4328 :         for (i=0; i<reps.count; i++) {
     142        2696 :                 if (GUID_equal(dest_guid,
     143        2696 :                                &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
     144        1430 :                         if (i+1 < reps.count) {
     145          40 :                                 memmove(&reps.r[i], &reps.r[i+1], sizeof(reps.r[i])*(reps.count-(i+1)));
     146             :                         }
     147        1430 :                         reps.count--;
     148        1430 :                         found = true;
     149             :                 }
     150             :         }
     151             : 
     152        1632 :         werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
     153        1632 :         if (!W_ERROR_IS_OK(werr)) {
     154           0 :                 return werr;
     155             :         }
     156             : 
     157        1828 :         if (!found &&
     158         398 :             !(options & DRSUAPI_DRS_GETCHG_CHECK) &&
     159         202 :             !(options & DRSUAPI_DRS_ADD_REF)) {
     160           0 :                 return WERR_DS_DRA_REF_NOT_FOUND;
     161             :         }
     162             : 
     163        1632 :         return WERR_OK; 
     164             : }
     165             : 
     166             : struct drepl_refresh_state {
     167             :         struct dreplsrv_refresh r;
     168             : };
     169             : 
     170             : /**
     171             :  * @brief Update the references for the given NC and the destination DSA object
     172             :  *
     173             :  * This function is callable from non RPC functions (ie. getncchanges), it
     174             :  * will validate the request to update reference and then will add/del a repsTo
     175             :  * to the specified server referenced by its DSA GUID in the request.
     176             :  *
     177             :  * @param[in]       msg_ctx          Messaging context for sending partition
     178             :  *                                   refresh in dreplsrv
     179             :  *
     180             :  * @param[in]       b_state          A bind_state object
     181             :  *
     182             :  * @param[in]       mem_ctx          A talloc context for memory allocation
     183             :  *
     184             :  * @param[in]       req              A drsuapi_DsReplicaUpdateRefsRequest1
     185             :  *                                   object which NC, which server and which
     186             :  *                                   action (add/delete) should be performed
     187             :  *
     188             :  * @return                           WERR_OK is success, different error
     189             :  *                                   otherwise.
     190             :  */
     191        2563 : WERROR drsuapi_UpdateRefs(struct imessaging_context *msg_ctx,
     192             :                           struct tevent_context *event_ctx,
     193             :                           struct drsuapi_bind_state *b_state, TALLOC_CTX *mem_ctx,
     194             :                           struct drsuapi_DsReplicaUpdateRefsRequest1 *req)
     195             : {
     196             :         WERROR werr;
     197             :         int ret;
     198             :         struct ldb_dn *dn_normalised;
     199             :         struct ldb_dn *nc_root;
     200        2563 :         struct ldb_context *sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
     201             :         struct dcerpc_binding_handle *irpc_handle;
     202             :         struct tevent_req *subreq;
     203             :         struct drepl_refresh_state *state;
     204             : 
     205             : 
     206        2563 :         DEBUG(4,("DsReplicaUpdateRefs for host '%s' with GUID %s options 0x%08x nc=%s\n",
     207             :                  req->dest_dsa_dns_name, GUID_string(mem_ctx, &req->dest_dsa_guid),
     208             :                  req->options,
     209             :                  drs_ObjectIdentifier_to_debug_string(mem_ctx, req->naming_context)));
     210             : 
     211             :         /*
     212             :          * 4.1.26.2 Server Behavior of the IDL_DRSUpdateRefs Method
     213             :          * Implements the input validation checks
     214             :          */
     215        2563 :         if (GUID_all_zero(&req->dest_dsa_guid)) {
     216           0 :                 return WERR_DS_DRA_INVALID_PARAMETER;
     217             :         }
     218             : 
     219             :         /* FIXME it seems that we should check the length of the stuff too*/
     220        2563 :         if (req->dest_dsa_dns_name == NULL) {
     221           0 :                 return WERR_DS_DRA_INVALID_PARAMETER;
     222             :         }
     223             : 
     224        2563 :         if (!(req->options & (DRSUAPI_DRS_DEL_REF|DRSUAPI_DRS_ADD_REF))) {
     225           0 :                 return WERR_DS_DRA_INVALID_PARAMETER;
     226             :         }
     227             : 
     228        2563 :         ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx, sam_ctx, req->naming_context,
     229             :                                                      &dn_normalised, &nc_root);
     230        2563 :         if (ret != LDB_SUCCESS) {
     231           0 :                 DBG_WARNING("Didn't find a nc for %s: %s\n",
     232             :                             drs_ObjectIdentifier_to_debug_string(mem_ctx,
     233             :                                                                  req->naming_context),
     234             :                             ldb_errstring(sam_ctx));
     235           0 :                 return WERR_DS_DRA_BAD_NC;
     236             :         }
     237        2563 :         if (ldb_dn_compare(dn_normalised, nc_root) != 0) {
     238           0 :                 DBG_NOTICE("dn %s is not equal to %s (from %s)\n",
     239             :                            ldb_dn_get_linearized(dn_normalised),
     240             :                            ldb_dn_get_linearized(nc_root),
     241             :                            drs_ObjectIdentifier_to_debug_string(mem_ctx,
     242             :                                                                 req->naming_context));
     243           0 :                 return WERR_DS_DRA_BAD_NC;
     244             :         }
     245             : 
     246             :         /*
     247             :          * First check without a transaction open.
     248             :          *
     249             :          * This means that in the usual case, it will never open it and never
     250             :          * bother to refresh the dreplsrv.
     251             :          */
     252        2563 :         werr = uref_check_dest(sam_ctx,
     253             :                                mem_ctx,
     254             :                                dn_normalised,
     255             :                                &req->dest_dsa_guid,
     256             :                                req->options);
     257        4212 :         if (W_ERROR_EQUAL(werr, WERR_DS_DRA_REF_ALREADY_EXISTS) ||
     258        1655 :             W_ERROR_EQUAL(werr, WERR_DS_DRA_REF_NOT_FOUND)) {
     259         909 :                 if (req->options & DRSUAPI_DRS_GETCHG_CHECK) {
     260         907 :                         return WERR_OK;
     261             :                 }
     262           2 :                 return werr;
     263             :         }
     264             : 
     265        1654 :         if (ldb_transaction_start(sam_ctx) != LDB_SUCCESS) {
     266           0 :                 DEBUG(0,(__location__ ": Failed to start transaction on samdb: %s\n",
     267             :                          ldb_errstring(sam_ctx)));
     268           0 :                 return WERR_DS_DRA_INTERNAL_ERROR;
     269             :         }
     270             : 
     271        1654 :         if (req->options & DRSUAPI_DRS_DEL_REF) {
     272        1632 :                 werr = uref_del_dest(sam_ctx,
     273             :                                      mem_ctx,
     274             :                                      dn_normalised,
     275             :                                      &req->dest_dsa_guid,
     276             :                                      req->options);
     277        1632 :                 if (!W_ERROR_IS_OK(werr)) {
     278           0 :                         DEBUG(0,("Failed to delete repsTo for %s: %s\n",
     279             :                                  GUID_string(mem_ctx, &req->dest_dsa_guid),
     280             :                                  win_errstr(werr)));
     281           0 :                         goto failed;
     282             :                 }
     283             :         }
     284             : 
     285        1654 :         if (req->options & DRSUAPI_DRS_ADD_REF) {
     286             :                 struct repsFromTo1 dest;
     287             :                 struct repsFromTo1OtherInfo oi;
     288             : 
     289        1652 :                 ZERO_STRUCT(dest);
     290        1652 :                 ZERO_STRUCT(oi);
     291             : 
     292        1652 :                 oi.dns_name = req->dest_dsa_dns_name;
     293        1652 :                 dest.other_info          = &oi;
     294        1652 :                 dest.source_dsa_obj_guid = req->dest_dsa_guid;
     295        1652 :                 dest.replica_flags       = req->options;
     296             : 
     297        1652 :                 werr = uref_add_dest(sam_ctx,
     298             :                                      mem_ctx,
     299             :                                      dn_normalised,
     300             :                                      &dest,
     301             :                                      req->options);
     302        1652 :                 if (!W_ERROR_IS_OK(werr)) {
     303           0 :                         DEBUG(0,("Failed to add repsTo for %s: %s\n",
     304             :                                  GUID_string(mem_ctx, &dest.source_dsa_obj_guid),
     305             :                                  win_errstr(werr)));
     306           0 :                         goto failed;
     307             :                 }
     308             :         }
     309             : 
     310        1654 :         if (ldb_transaction_commit(sam_ctx) != LDB_SUCCESS) {
     311           0 :                 DEBUG(0,(__location__ ": Failed to commit transaction on samdb: %s\n",
     312             :                          ldb_errstring(sam_ctx)));
     313           0 :                 return WERR_DS_DRA_INTERNAL_ERROR;
     314             :         }
     315             : 
     316        1654 :         state = talloc_zero(mem_ctx, struct drepl_refresh_state);
     317        1654 :         if (state == NULL) {
     318           0 :                 return WERR_OK;
     319             :         }
     320             : 
     321        1654 :         irpc_handle = irpc_binding_handle_by_name(mem_ctx, msg_ctx,
     322             :                                                   "dreplsrv", &ndr_table_irpc);
     323        1654 :         if (irpc_handle == NULL) {
     324             :                 /* dreplsrv is not running yet */
     325           0 :                 TALLOC_FREE(state);
     326           0 :                 return WERR_OK;
     327             :         }
     328             : 
     329             :         /*
     330             :          * [Taken from auth_sam_trigger_repl_secret in auth_sam.c]
     331             :          *
     332             :          * This seem to rely on the current IRPC implementation,
     333             :          * which delivers the message in the _send function.
     334             :          *
     335             :          * TODO: we need a ONE_WAY IRPC handle and register
     336             :          * a callback and wait for it to be triggered!
     337             :          */
     338        1654 :         subreq = dcerpc_dreplsrv_refresh_r_send(state, event_ctx,
     339             :                                                 irpc_handle, &state->r);
     340        1654 :         TALLOC_FREE(subreq);
     341        1654 :         TALLOC_FREE(state);
     342             : 
     343        1654 :         return WERR_OK;
     344             : 
     345           0 : failed:
     346           0 :         ldb_transaction_cancel(sam_ctx);
     347           0 :         return werr;
     348             : }
     349             : 
     350             : /* 
     351             :   drsuapi_DsReplicaUpdateRefs
     352             : */
     353        1635 : WERROR dcesrv_drsuapi_DsReplicaUpdateRefs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
     354             :                                           struct drsuapi_DsReplicaUpdateRefs *r)
     355             : {
     356        1629 :         struct auth_session_info *session_info =
     357           6 :                 dcesrv_call_session_info(dce_call);
     358        1629 :         struct imessaging_context *imsg_ctx =
     359        1635 :                 dcesrv_imessaging_context(dce_call->conn);
     360             :         struct dcesrv_handle *h;
     361             :         struct drsuapi_bind_state *b_state;
     362             :         struct drsuapi_DsReplicaUpdateRefsRequest1 *req;
     363             :         WERROR werr;
     364             :         int ret;
     365             :         enum security_user_level security_level;
     366             : 
     367        1635 :         DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
     368        1635 :         b_state = h->data;
     369             : 
     370        1635 :         if (r->in.level != 1) {
     371           0 :                 DEBUG(0,("DrReplicUpdateRefs - unsupported level %u\n", r->in.level));
     372           0 :                 return WERR_DS_DRA_INVALID_PARAMETER;
     373             :         }
     374        1635 :         req = &r->in.req.req1;
     375        1635 :         werr = drs_security_access_check(b_state->sam_ctx,
     376             :                                          mem_ctx,
     377             :                                          session_info->security_token,
     378             :                                          req->naming_context,
     379             :                                          GUID_DRS_MANAGE_TOPOLOGY);
     380             : 
     381        1635 :         if (!W_ERROR_IS_OK(werr)) {
     382           0 :                 return werr;
     383             :         }
     384             : 
     385        1635 :         security_level = security_session_user_level(session_info, NULL);
     386        1635 :         if (security_level < SECURITY_ADMINISTRATOR) {
     387             :                 /* check that they are using an DSA objectGUID that they own */
     388        1451 :                 ret = dsdb_validate_dsa_guid(b_state->sam_ctx,
     389        1451 :                                              &req->dest_dsa_guid,
     390        1451 :                                              &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]);
     391        1451 :                 if (ret != LDB_SUCCESS) {
     392           0 :                         DEBUG(0,(__location__ ": Refusing DsReplicaUpdateRefs for sid %s with GUID %s\n",
     393             :                                  dom_sid_string(mem_ctx,
     394             :                                                 &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]),
     395             :                                  GUID_string(mem_ctx, &req->dest_dsa_guid)));
     396           0 :                         return WERR_DS_DRA_ACCESS_DENIED;
     397             :                 }
     398             :         }
     399             : 
     400        1635 :         werr = drsuapi_UpdateRefs(imsg_ctx,
     401             :                                   dce_call->event_ctx,
     402             :                                   b_state,
     403             :                                   mem_ctx,
     404             :                                   req);
     405             : 
     406             : #if 0
     407             :         NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsReplicaUpdateRefs, NDR_BOTH, r);
     408             : #endif
     409             : 
     410        1635 :         return werr;
     411             : }

Generated by: LCOV version 1.13