2009-09-08 11:49:28 +10:00
/*
Unix SMB / CIFS implementation .
implement the DRSUpdateRefs call
Copyright ( C ) Andrew Tridgell 2009
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "rpc_server/dcerpc_server.h"
# include "dsdb/samdb/samdb.h"
2010-04-22 16:48:01 +10:00
# include "libcli/security/security.h"
2010-11-27 19:42:31 +01:00
# include "libcli/security/session.h"
# include "rpc_server/drsuapi/dcesrv_drsuapi.h"
2010-04-22 16:48:01 +10:00
# include "auth/session.h"
2010-09-16 13:02:43 +10:00
# include "librpc/gen_ndr/ndr_drsuapi.h"
2017-03-29 11:24:50 +13:00
# include "librpc/gen_ndr/ndr_irpc_c.h"
# include "lib/messaging/irpc.h"
2009-09-08 11:49:28 +10:00
2017-09-06 16:37:34 +12:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_DRS_REPL
2009-09-09 12:29:01 +10:00
struct repsTo {
uint32_t count ;
struct repsFromToBlob * r ;
} ;
2017-03-29 15:21:04 +13:00
static WERROR uref_check_dest ( struct ldb_context * sam_ctx , TALLOC_CTX * mem_ctx ,
struct ldb_dn * dn , struct GUID * dest_guid ,
uint32_t options )
{
struct repsTo reps ;
WERROR werr ;
unsigned int i ;
bool found = false ;
werr = dsdb_loadreps ( sam_ctx , mem_ctx , dn , " repsTo " , & reps . r , & reps . count ) ;
if ( ! W_ERROR_IS_OK ( werr ) ) {
return werr ;
}
for ( i = 0 ; i < reps . count ; i + + ) {
if ( GUID_equal ( dest_guid ,
& reps . r [ i ] . ctr . ctr1 . source_dsa_obj_guid ) ) {
found = true ;
break ;
}
}
if ( options & DRSUAPI_DRS_ADD_REF ) {
if ( found & & ! ( options & DRSUAPI_DRS_DEL_REF ) ) {
return WERR_DS_DRA_REF_ALREADY_EXISTS ;
}
}
if ( options & DRSUAPI_DRS_DEL_REF ) {
if ( ! found & & ! ( options & DRSUAPI_DRS_ADD_REF ) ) {
return WERR_DS_DRA_REF_NOT_FOUND ;
}
}
return WERR_OK ;
}
2009-09-08 11:49:28 +10:00
/*
add a replication destination for a given partition GUID
*/
static WERROR uref_add_dest ( struct ldb_context * sam_ctx , TALLOC_CTX * mem_ctx ,
2009-10-14 20:25:48 +11:00
struct ldb_dn * dn , struct repsFromTo1 * dest ,
uint32_t options )
2009-09-08 11:49:28 +10:00
{
struct repsTo reps ;
WERROR werr ;
2009-11-21 18:57:24 +01:00
unsigned int i ;
2009-09-08 11:49:28 +10:00
2009-09-12 11:12:05 +10:00
werr = dsdb_loadreps ( sam_ctx , mem_ctx , dn , " repsTo " , & reps . r , & reps . count ) ;
2009-09-08 11:49:28 +10:00
if ( ! W_ERROR_IS_OK ( werr ) ) {
return werr ;
}
2009-10-14 20:25:48 +11:00
for ( i = 0 ; i < reps . count ; i + + ) {
2014-06-05 10:04:43 +00:00
if ( GUID_equal ( & dest - > source_dsa_obj_guid ,
& reps . r [ i ] . ctr . ctr1 . source_dsa_obj_guid ) ) {
2010-01-14 14:38:00 +11:00
if ( options & DRSUAPI_DRS_GETCHG_CHECK ) {
2009-10-14 20:25:48 +11:00
return WERR_OK ;
} else {
return WERR_DS_DRA_REF_ALREADY_EXISTS ;
}
}
}
2009-09-09 12:29:01 +10:00
reps . r = talloc_realloc ( mem_ctx , reps . r , struct repsFromToBlob , reps . count + 1 ) ;
if ( reps . r = = NULL ) {
2009-09-08 11:49:28 +10:00
return WERR_DS_DRA_INTERNAL_ERROR ;
}
2009-09-09 12:29:01 +10:00
ZERO_STRUCT ( reps . r [ reps . count ] ) ;
reps . r [ reps . count ] . version = 1 ;
reps . r [ reps . count ] . ctr . ctr1 = * dest ;
2010-09-30 12:43:45 -07:00
/* add the GCSPN flag if the client asked for it */
reps . r [ reps . count ] . ctr . ctr1 . replica_flags | = ( options & DRSUAPI_DRS_REF_GCSPN ) ;
2009-09-09 12:29:01 +10:00
reps . count + + ;
2009-09-08 11:49:28 +10:00
2009-09-12 11:12:05 +10:00
werr = dsdb_savereps ( sam_ctx , mem_ctx , dn , " repsTo " , reps . r , reps . count ) ;
2009-09-08 11:49:28 +10:00
if ( ! W_ERROR_IS_OK ( werr ) ) {
return werr ;
}
return WERR_OK ;
}
/*
delete a replication destination for a given partition GUID
*/
static WERROR uref_del_dest ( struct ldb_context * sam_ctx , TALLOC_CTX * mem_ctx ,
2009-10-14 20:25:48 +11:00
struct ldb_dn * dn , struct GUID * dest_guid ,
uint32_t options )
2009-09-08 11:49:28 +10:00
{
struct repsTo reps ;
WERROR werr ;
2009-11-21 18:57:24 +01:00
unsigned int i ;
2009-10-14 20:25:48 +11:00
bool found = false ;
2009-09-08 11:49:28 +10:00
2009-09-12 11:12:05 +10:00
werr = dsdb_loadreps ( sam_ctx , mem_ctx , dn , " repsTo " , & reps . r , & reps . count ) ;
2009-09-08 11:49:28 +10:00
if ( ! W_ERROR_IS_OK ( werr ) ) {
return werr ;
}
2009-09-09 12:29:01 +10:00
for ( i = 0 ; i < reps . count ; i + + ) {
2014-06-05 10:04:43 +00:00
if ( GUID_equal ( dest_guid ,
& reps . r [ i ] . ctr . ctr1 . source_dsa_obj_guid ) ) {
2009-09-09 12:29:01 +10:00
if ( i + 1 < reps . count ) {
memmove ( & reps . r [ i ] , & reps . r [ i + 1 ] , sizeof ( reps . r [ i ] ) * ( reps . count - ( i + 1 ) ) ) ;
2009-09-08 11:49:28 +10:00
}
2023-03-21 13:47:16 +13:00
/*
* If we remove an element , decrement i so that we don ' t
* skip over the element following .
*/
i - - ;
2009-09-09 12:29:01 +10:00
reps . count - - ;
2009-10-14 20:25:48 +11:00
found = true ;
2009-09-08 11:49:28 +10:00
}
}
2009-09-12 11:12:05 +10:00
werr = dsdb_savereps ( sam_ctx , mem_ctx , dn , " repsTo " , reps . r , reps . count ) ;
2009-09-08 11:49:28 +10:00
if ( ! W_ERROR_IS_OK ( werr ) ) {
return werr ;
}
2010-01-02 16:51:30 +11:00
if ( ! found & &
2010-01-14 14:38:00 +11:00
! ( options & DRSUAPI_DRS_GETCHG_CHECK ) & &
! ( options & DRSUAPI_DRS_ADD_REF ) ) {
2009-10-14 20:25:48 +11:00
return WERR_DS_DRA_REF_NOT_FOUND ;
}
2009-09-08 11:49:28 +10:00
return WERR_OK ;
}
2017-03-29 11:24:50 +13:00
struct drepl_refresh_state {
struct dreplsrv_refresh r ;
} ;
2012-10-14 01:04:51 -07:00
/**
* @ brief Update the references for the given NC and the destination DSA object
*
* This function is callable from non RPC functions ( ie . getncchanges ) , it
* will validate the request to update reference and then will add / del a repsTo
* to the specified server referenced by its DSA GUID in the request .
*
2017-03-29 11:24:50 +13:00
* @ param [ in ] msg_ctx Messaging context for sending partition
* refresh in dreplsrv
*
2012-10-14 01:04:51 -07:00
* @ param [ in ] b_state A bind_state object
*
* @ param [ in ] mem_ctx A talloc context for memory allocation
*
* @ param [ in ] req A drsuapi_DsReplicaUpdateRefsRequest1
* object which NC , which server and which
* action ( add / delete ) should be performed
*
* @ return WERR_OK is success , different error
* otherwise .
*/
2017-03-29 11:24:50 +13:00
WERROR drsuapi_UpdateRefs ( struct imessaging_context * msg_ctx ,
struct tevent_context * event_ctx ,
struct drsuapi_bind_state * b_state , TALLOC_CTX * mem_ctx ,
2009-10-14 20:25:48 +11:00
struct drsuapi_DsReplicaUpdateRefsRequest1 * req )
2009-09-08 11:49:28 +10:00
{
WERROR werr ;
2012-09-27 15:02:06 -07:00
int ret ;
2022-12-12 16:15:44 +13:00
struct ldb_dn * dn_normalised ;
2012-09-27 15:02:06 -07:00
struct ldb_dn * nc_root ;
2010-09-25 17:53:14 -07:00
struct ldb_context * sam_ctx = b_state - > sam_ctx_system ? b_state - > sam_ctx_system : b_state - > sam_ctx ;
2017-03-29 11:24:50 +13:00
struct dcerpc_binding_handle * irpc_handle ;
struct tevent_req * subreq ;
struct drepl_refresh_state * state ;
2009-09-15 19:26:33 -07:00
2009-09-10 13:50:46 +10:00
DEBUG ( 4 , ( " DsReplicaUpdateRefs for host '%s' with GUID %s options 0x%08x nc=%s \n " ,
2009-09-08 11:49:28 +10:00
req - > dest_dsa_dns_name , GUID_string ( mem_ctx , & req - > dest_dsa_guid ) ,
req - > options ,
2023-01-31 13:29:05 +13:00
drs_ObjectIdentifier_to_debug_string ( mem_ctx , req - > naming_context ) ) ) ;
2009-09-08 11:49:28 +10:00
2012-09-27 15:22:00 -07:00
/*
* 4.1 .26 .2 Server Behavior of the IDL_DRSUpdateRefs Method
* Implements the input validation checks
*/
if ( GUID_all_zero ( & req - > dest_dsa_guid ) ) {
return WERR_DS_DRA_INVALID_PARAMETER ;
}
2013-06-27 23:58:41 -07:00
/* FIXME it seems that we should check the length of the stuff too*/
2012-09-27 15:22:00 -07:00
if ( req - > dest_dsa_dns_name = = NULL ) {
return WERR_DS_DRA_INVALID_PARAMETER ;
}
if ( ! ( req - > options & ( DRSUAPI_DRS_DEL_REF | DRSUAPI_DRS_ADD_REF ) ) ) {
return WERR_DS_DRA_INVALID_PARAMETER ;
}
2022-12-12 09:47:36 +13:00
ret = drs_ObjectIdentifier_to_dn_and_nc_root ( mem_ctx , sam_ctx , req - > naming_context ,
& dn_normalised , & nc_root ) ;
2012-09-27 15:02:06 -07:00
if ( ret ! = LDB_SUCCESS ) {
2023-01-31 13:29:05 +13:00
DBG_WARNING ( " Didn't find a nc for %s: %s \n " ,
drs_ObjectIdentifier_to_debug_string ( mem_ctx ,
req - > naming_context ) ,
ldb_errstring ( sam_ctx ) ) ;
2012-09-27 15:02:06 -07:00
return WERR_DS_DRA_BAD_NC ;
}
2022-12-12 16:15:44 +13:00
if ( ldb_dn_compare ( dn_normalised , nc_root ) ! = 0 ) {
2023-01-31 13:29:05 +13:00
DBG_NOTICE ( " dn %s is not equal to %s (from %s) \n " ,
2022-12-12 16:15:44 +13:00
ldb_dn_get_linearized ( dn_normalised ) ,
2023-01-31 13:29:05 +13:00
ldb_dn_get_linearized ( nc_root ) ,
drs_ObjectIdentifier_to_debug_string ( mem_ctx ,
req - > naming_context ) ) ;
2012-09-27 15:02:06 -07:00
return WERR_DS_DRA_BAD_NC ;
2009-09-10 14:27:47 +10:00
}
2017-03-29 15:21:04 +13:00
/*
* First check without a transaction open .
*
* This means that in the usual case , it will never open it and never
* bother to refresh the dreplsrv .
*/
2022-12-12 09:47:36 +13:00
werr = uref_check_dest ( sam_ctx ,
mem_ctx ,
dn_normalised ,
& req - > dest_dsa_guid ,
2017-03-29 15:21:04 +13:00
req - > options ) ;
if ( W_ERROR_EQUAL ( werr , WERR_DS_DRA_REF_ALREADY_EXISTS ) | |
W_ERROR_EQUAL ( werr , WERR_DS_DRA_REF_NOT_FOUND ) ) {
if ( req - > options & DRSUAPI_DRS_GETCHG_CHECK ) {
return WERR_OK ;
}
return werr ;
}
2010-09-25 17:53:14 -07:00
if ( ldb_transaction_start ( sam_ctx ) ! = LDB_SUCCESS ) {
2010-09-06 14:33:14 +03:00
DEBUG ( 0 , ( __location__ " : Failed to start transaction on samdb: %s \n " ,
2010-09-25 17:53:14 -07:00
ldb_errstring ( sam_ctx ) ) ) ;
2012-09-27 15:02:06 -07:00
return WERR_DS_DRA_INTERNAL_ERROR ;
2009-09-08 11:49:28 +10:00
}
2010-01-14 14:38:00 +11:00
if ( req - > options & DRSUAPI_DRS_DEL_REF ) {
2022-12-12 09:47:36 +13:00
werr = uref_del_dest ( sam_ctx ,
mem_ctx ,
dn_normalised ,
& req - > dest_dsa_guid ,
req - > options ) ;
2009-09-08 11:49:28 +10:00
if ( ! W_ERROR_IS_OK ( werr ) ) {
2010-09-06 14:33:14 +03:00
DEBUG ( 0 , ( " Failed to delete repsTo for %s: %s \n " ,
GUID_string ( mem_ctx , & req - > dest_dsa_guid ) ,
win_errstr ( werr ) ) ) ;
2009-09-08 11:49:28 +10:00
goto failed ;
}
}
2010-01-14 14:38:00 +11:00
if ( req - > options & DRSUAPI_DRS_ADD_REF ) {
2009-09-09 12:29:01 +10:00
struct repsFromTo1 dest ;
struct repsFromTo1OtherInfo oi ;
2017-03-29 11:24:50 +13:00
2009-09-09 12:29:01 +10:00
ZERO_STRUCT ( dest ) ;
ZERO_STRUCT ( oi ) ;
2009-09-08 11:49:28 +10:00
2009-09-09 12:29:01 +10:00
oi . dns_name = req - > dest_dsa_dns_name ;
dest . other_info = & oi ;
dest . source_dsa_obj_guid = req - > dest_dsa_guid ;
dest . replica_flags = req - > options ;
2009-09-08 11:49:28 +10:00
2022-12-12 09:47:36 +13:00
werr = uref_add_dest ( sam_ctx ,
mem_ctx ,
dn_normalised ,
& dest ,
req - > options ) ;
2009-09-08 11:49:28 +10:00
if ( ! W_ERROR_IS_OK ( werr ) ) {
2010-09-06 14:33:14 +03:00
DEBUG ( 0 , ( " Failed to add repsTo for %s: %s \n " ,
GUID_string ( mem_ctx , & dest . source_dsa_obj_guid ) ,
win_errstr ( werr ) ) ) ;
2009-09-08 11:49:28 +10:00
goto failed ;
}
}
2010-09-25 17:53:14 -07:00
if ( ldb_transaction_commit ( sam_ctx ) ! = LDB_SUCCESS ) {
2010-09-06 14:33:14 +03:00
DEBUG ( 0 , ( __location__ " : Failed to commit transaction on samdb: %s \n " ,
2010-09-25 17:53:14 -07:00
ldb_errstring ( sam_ctx ) ) ) ;
2017-03-29 11:24:50 +13:00
return WERR_DS_DRA_INTERNAL_ERROR ;
2009-09-08 11:49:28 +10:00
}
2017-03-29 11:24:50 +13:00
state = talloc_zero ( mem_ctx , struct drepl_refresh_state ) ;
if ( state = = NULL ) {
return WERR_OK ;
}
irpc_handle = irpc_binding_handle_by_name ( mem_ctx , msg_ctx ,
" dreplsrv " , & ndr_table_irpc ) ;
if ( irpc_handle = = NULL ) {
/* dreplsrv is not running yet */
TALLOC_FREE ( state ) ;
return WERR_OK ;
}
/*
* [ Taken from auth_sam_trigger_repl_secret in auth_sam . c ]
*
* This seem to rely on the current IRPC implementation ,
* which delivers the message in the _send function .
*
* TODO : we need a ONE_WAY IRPC handle and register
* a callback and wait for it to be triggered !
*/
subreq = dcerpc_dreplsrv_refresh_r_send ( state , event_ctx ,
irpc_handle , & state - > r ) ;
TALLOC_FREE ( subreq ) ;
TALLOC_FREE ( state ) ;
2009-09-08 11:49:28 +10:00
return WERR_OK ;
failed :
2010-09-25 17:53:14 -07:00
ldb_transaction_cancel ( sam_ctx ) ;
2009-09-08 11:49:28 +10:00
return werr ;
}
2009-10-14 20:25:48 +11:00
/*
drsuapi_DsReplicaUpdateRefs
*/
WERROR dcesrv_drsuapi_DsReplicaUpdateRefs ( struct dcesrv_call_state * dce_call , TALLOC_CTX * mem_ctx ,
struct drsuapi_DsReplicaUpdateRefs * r )
{
2018-11-03 01:19:51 +01:00
struct auth_session_info * session_info =
dcesrv_call_session_info ( dce_call ) ;
2019-01-23 20:37:21 +01:00
struct imessaging_context * imsg_ctx =
dcesrv_imessaging_context ( dce_call - > conn ) ;
2009-10-14 20:25:48 +11:00
struct dcesrv_handle * h ;
struct drsuapi_bind_state * b_state ;
struct drsuapi_DsReplicaUpdateRefsRequest1 * req ;
WERROR werr ;
2010-04-22 16:48:01 +10:00
int ret ;
enum security_user_level security_level ;
2009-10-14 20:25:48 +11:00
DCESRV_PULL_HANDLE_WERR ( h , r - > in . bind_handle , DRSUAPI_BIND_HANDLE ) ;
b_state = h - > data ;
if ( r - > in . level ! = 1 ) {
DEBUG ( 0 , ( " DrReplicUpdateRefs - unsupported level %u \n " , r - > in . level ) ) ;
return WERR_DS_DRA_INVALID_PARAMETER ;
}
req = & r - > in . req . req1 ;
2010-09-26 21:16:47 -07:00
werr = drs_security_access_check ( b_state - > sam_ctx ,
mem_ctx ,
2018-11-03 01:19:51 +01:00
session_info - > security_token ,
2010-09-26 21:16:47 -07:00
req - > naming_context ,
GUID_DRS_MANAGE_TOPOLOGY ) ;
if ( ! W_ERROR_IS_OK ( werr ) ) {
return werr ;
}
2009-10-14 20:25:48 +11:00
2018-11-03 01:19:51 +01:00
security_level = security_session_user_level ( session_info , NULL ) ;
2010-04-22 16:48:01 +10:00
if ( security_level < SECURITY_ADMINISTRATOR ) {
2010-04-26 00:22:53 +03:00
/* check that they are using an DSA objectGUID that they own */
ret = dsdb_validate_dsa_guid ( b_state - > sam_ctx ,
& req - > dest_dsa_guid ,
2018-11-03 01:19:51 +01:00
& session_info - > security_token - > sids [ PRIMARY_USER_SID_INDEX ] ) ;
2010-04-22 16:48:01 +10:00
if ( ret ! = LDB_SUCCESS ) {
DEBUG ( 0 , ( __location__ " : Refusing DsReplicaUpdateRefs for sid %s with GUID %s \n " ,
dom_sid_string ( mem_ctx ,
2018-11-03 01:19:51 +01:00
& session_info - > security_token - > sids [ PRIMARY_USER_SID_INDEX ] ) ,
2010-04-22 16:48:01 +10:00
GUID_string ( mem_ctx , & req - > dest_dsa_guid ) ) ) ;
return WERR_DS_DRA_ACCESS_DENIED ;
}
}
2019-01-23 20:37:21 +01:00
werr = drsuapi_UpdateRefs ( imsg_ctx ,
dce_call - > event_ctx ,
b_state ,
mem_ctx ,
req ) ;
2010-09-16 13:02:43 +10:00
#if 0
NDR_PRINT_FUNCTION_DEBUG ( drsuapi_DsReplicaUpdateRefs , NDR_BOTH , r ) ;
# endif
return werr ;
2009-10-14 20:25:48 +11:00
}