2011-10-04 15:08:50 +04:00
/*
Unix SMB / CIFS implementation .
Copyright Matthieu Patou < mat @ matws . net > 2010 - 2011
Copyright Stefan Metzmacher 2011
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 "librpc/gen_ndr/dfsblobs.h"
# include "librpc/gen_ndr/ndr_dfsblobs.h"
# include "dsdb/samdb/samdb.h"
# include "auth/session.h"
# include "param/param.h"
# include "lib/tsocket/tsocket.h"
# include "dfs_server/dfs_server_ad.h"
2012-07-30 02:49:13 +04:00
# include "lib/util/util_net.h"
2015-12-28 22:01:54 +03:00
# include "libds/common/roles.h"
2011-10-04 15:08:50 +04:00
# define MAX_DFS_RESPONSE 56*1024 /* 56 Kb */
/* A DC set is a group of DC, they might have been grouped together
because they belong to the same site , or to site with same cost . . .
*/
struct dc_set {
const char * * names ;
uint32_t count ;
} ;
2014-03-21 01:49:08 +04:00
static void shuffle_dc_set ( struct dc_set * list )
{
2015-07-09 17:28:05 +03:00
uint32_t i ;
2014-03-21 01:49:08 +04:00
2015-07-09 17:28:05 +03:00
for ( i = list - > count ; i > 1 ; i - - ) {
uint32_t r ;
const char * tmp ;
2014-03-21 01:49:08 +04:00
2015-07-09 17:28:05 +03:00
r = generate_random ( ) % i ;
2014-03-21 01:49:08 +04:00
2015-07-09 17:28:05 +03:00
tmp = list - > names [ i - 1 ] ;
list - > names [ i - 1 ] = list - > names [ r ] ;
list - > names [ r ] = tmp ;
}
2014-03-21 01:49:08 +04:00
}
2011-10-04 15:08:50 +04:00
/*
fill a referral type structure
*/
static NTSTATUS fill_normal_dfs_referraltype ( TALLOC_CTX * mem_ctx ,
struct dfs_referral_type * ref ,
uint16_t version ,
const char * dfs_path ,
const char * server_path , int isfirstoffset )
{
ZERO_STRUCTP ( ref ) ;
switch ( version ) {
case 4 :
2012-04-24 11:24:39 +04:00
ref - > version = version ;
2011-10-04 15:08:50 +04:00
/* For the moment there is a bug with XP that don't seems to appriciate much
* level4 so we return just level 3 for everyone
*/
ref - > referral . v4 . server_type = DFS_SERVER_NON_ROOT ;
/* "normal" referral seems to always include the GUID */
ref - > referral . v4 . size = 34 ;
if ( isfirstoffset ) {
ref - > referral . v4 . entry_flags = DFS_HEADER_FLAG_TARGET_BCK ;
}
ref - > referral . v4 . ttl = 900 ; /* As w2k8r2 */
ref - > referral . v4 . referrals . r1 . DFS_path = talloc_strdup ( mem_ctx , dfs_path ) ;
if ( ref - > referral . v4 . referrals . r1 . DFS_path = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
ref - > referral . v4 . referrals . r1 . DFS_alt_path = talloc_strdup ( mem_ctx , dfs_path ) ;
if ( ref - > referral . v4 . referrals . r1 . DFS_alt_path = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
ref - > referral . v4 . referrals . r1 . netw_address = talloc_strdup ( mem_ctx , server_path ) ;
if ( ref - > referral . v4 . referrals . r1 . netw_address = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
return NT_STATUS_OK ;
case 3 :
ref - > version = version ;
ref - > referral . v3 . server_type = DFS_SERVER_NON_ROOT ;
/* "normal" referral seems to always include the GUID */
ref - > referral . v3 . size = 34 ;
ref - > referral . v3 . entry_flags = 0 ;
ref - > referral . v3 . ttl = 600 ; /* As w2k3 */
ref - > referral . v3 . referrals . r1 . DFS_path = talloc_strdup ( mem_ctx , dfs_path ) ;
if ( ref - > referral . v3 . referrals . r1 . DFS_path = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
ref - > referral . v3 . referrals . r1 . DFS_alt_path = talloc_strdup ( mem_ctx , dfs_path ) ;
if ( ref - > referral . v3 . referrals . r1 . DFS_alt_path = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
ref - > referral . v3 . referrals . r1 . netw_address = talloc_strdup ( mem_ctx , server_path ) ;
if ( ref - > referral . v3 . referrals . r1 . netw_address = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
return NT_STATUS_OK ;
}
return NT_STATUS_INVALID_LEVEL ;
}
/*
fill a domain refererral
*/
static NTSTATUS fill_domain_dfs_referraltype ( TALLOC_CTX * mem_ctx ,
struct dfs_referral_type * ref ,
uint16_t version ,
const char * domain ,
const char * * names ,
uint16_t numnames )
{
switch ( version ) {
case 3 :
ZERO_STRUCTP ( ref ) ;
DEBUG ( 8 , ( " Called fill_domain_dfs_referraltype \n " ) ) ;
ref - > version = version ;
ref - > referral . v3 . server_type = DFS_SERVER_NON_ROOT ;
2012-04-24 11:24:39 +04:00
#if 0
/* We use to have variable size, on Windows 2008R2 it's the same
* and it seems that it gives better results so . . . let ' s use the same
* size .
*
* Additional note : XP SP2 will ask for version 3 and SP3 for version 4.
*/
2011-10-04 15:08:50 +04:00
/*
* It ' s hard coded . . . don ' t think it ' s a good way but the
* sizeof return not the correct values
*
* We have 18 if the GUID is not included 34 otherwise
*/
if ( numnames = = 0 ) {
/* Windows return without the guid when returning domain list
*/
ref - > referral . v3 . size = 18 ;
} else {
ref - > referral . v3 . size = 34 ;
}
2012-04-24 11:24:39 +04:00
# endif
/* As seen in w2k8r2 it always return the null GUID */
ref - > referral . v3 . size = 34 ;
2011-10-04 15:08:50 +04:00
ref - > referral . v3 . entry_flags = DFS_FLAG_REFERRAL_DOMAIN_RESP ;
2012-04-24 11:24:39 +04:00
ref - > referral . v3 . ttl = 600 ; /* As w2k3 and w2k8r2*/
2011-10-04 15:08:50 +04:00
ref - > referral . v3 . referrals . r2 . special_name = talloc_strdup ( mem_ctx ,
domain ) ;
if ( ref - > referral . v3 . referrals . r2 . special_name = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
ref - > referral . v3 . referrals . r2 . nb_expanded_names = numnames ;
/* Put the final terminator */
if ( names ) {
int i ;
const char * * names2 = talloc_array ( mem_ctx , const char * ,
numnames + 1 ) ;
NT_STATUS_HAVE_NO_MEMORY ( names2 ) ;
for ( i = 0 ; i < numnames ; i + + ) {
names2 [ i ] = talloc_asprintf ( names2 , " \\ %s " , names [ i ] ) ;
NT_STATUS_HAVE_NO_MEMORY ( names2 [ i ] ) ;
}
names2 [ numnames ] = NULL ;
ref - > referral . v3 . referrals . r2 . expanded_names = names2 ;
}
return NT_STATUS_OK ;
}
return NT_STATUS_INVALID_LEVEL ;
}
/*
get the DCs list within a site
*/
static NTSTATUS get_dcs_insite ( TALLOC_CTX * ctx , struct ldb_context * ldb ,
struct ldb_dn * sitedn , struct dc_set * list ,
bool dofqdn )
{
static const char * attrs [ ] = { " serverReference " , NULL } ;
static const char * attrs2 [ ] = { " dNSHostName " , " sAMAccountName " , NULL } ;
struct ldb_result * r ;
unsigned int i ;
int ret ;
const char * * dc_list ;
ret = ldb_search ( ldb , ctx , & r , sitedn , LDB_SCOPE_SUBTREE , attrs ,
" (&(objectClass=server)(serverReference=*)) " ) ;
if ( ret ! = LDB_SUCCESS ) {
DEBUG ( 2 , ( __location__ " : Failed to get list of servers - %s \n " ,
ldb_errstring ( ldb ) ) ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
if ( r - > count = = 0 ) {
/* none in this site */
talloc_free ( r ) ;
return NT_STATUS_OK ;
}
/*
* need to search for all server object to know the size of the array .
* Search all the object of class server in this site
*/
dc_list = talloc_array ( r , const char * , r - > count ) ;
2014-02-13 08:51:11 +04:00
if ( dc_list = = NULL ) {
TALLOC_FREE ( r ) ;
return NT_STATUS_NO_MEMORY ;
}
2011-10-04 15:08:50 +04:00
/* TODO put some random here in the order */
list - > names = talloc_realloc ( list , list - > names , const char * , list - > count + r - > count ) ;
2014-02-13 08:51:11 +04:00
if ( list - > names = = NULL ) {
TALLOC_FREE ( r ) ;
return NT_STATUS_NO_MEMORY ;
}
2011-10-04 15:08:50 +04:00
for ( i = 0 ; i < r - > count ; i + + ) {
struct ldb_dn * dn ;
2013-07-30 02:40:39 +04:00
struct ldb_message * msg ;
2011-10-04 15:08:50 +04:00
dn = ldb_msg_find_attr_as_dn ( ldb , ctx , r - > msgs [ i ] , " serverReference " ) ;
if ( ! dn ) {
return NT_STATUS_INTERNAL_ERROR ;
}
2013-07-30 02:40:39 +04:00
ret = dsdb_search_one ( ldb , r , & msg , dn , LDB_SCOPE_BASE , attrs2 , 0 , " (objectClass=computer) " ) ;
2011-10-04 15:08:50 +04:00
if ( ret ! = LDB_SUCCESS ) {
DEBUG ( 2 , ( __location__ " : Search for computer on %s failed - %s \n " ,
ldb_dn_get_linearized ( dn ) , ldb_errstring ( ldb ) ) ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
if ( dofqdn ) {
2013-07-30 02:40:39 +04:00
const char * dns = ldb_msg_find_attr_as_string ( msg , " dNSHostName " , NULL ) ;
2011-10-04 15:08:50 +04:00
if ( dns = = NULL ) {
DEBUG ( 2 , ( __location__ " : dNSHostName missing on %s \n " ,
ldb_dn_get_linearized ( dn ) ) ) ;
talloc_free ( r ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
list - > names [ list - > count ] = talloc_strdup ( list - > names , dns ) ;
2014-02-13 08:51:11 +04:00
if ( list - > names [ list - > count ] = = NULL ) {
TALLOC_FREE ( r ) ;
return NT_STATUS_NO_MEMORY ;
}
2011-10-04 15:08:50 +04:00
} else {
char * tmp ;
2013-07-30 02:40:39 +04:00
const char * aname = ldb_msg_find_attr_as_string ( msg , " sAMAccountName " , NULL ) ;
2012-07-30 03:21:16 +04:00
if ( aname = = NULL ) {
2011-10-04 15:08:50 +04:00
DEBUG ( 2 , ( __location__ " : sAMAccountName missing on %s \n " ,
ldb_dn_get_linearized ( dn ) ) ) ;
talloc_free ( r ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
2012-07-30 03:21:16 +04:00
tmp = talloc_strdup ( list - > names , aname ) ;
2014-02-13 08:51:11 +04:00
if ( tmp = = NULL ) {
TALLOC_FREE ( r ) ;
return NT_STATUS_NO_MEMORY ;
}
2011-10-04 15:08:50 +04:00
/* Netbios name is also the sAMAccountName for
computer but without the final $ */
tmp [ strlen ( tmp ) - 1 ] = ' \0 ' ;
list - > names [ list - > count ] = tmp ;
}
list - > count + + ;
2013-07-30 02:40:39 +04:00
talloc_free ( msg ) ;
2011-10-04 15:08:50 +04:00
}
2014-03-21 01:49:08 +04:00
shuffle_dc_set ( list ) ;
2011-10-04 15:08:50 +04:00
talloc_free ( r ) ;
return NT_STATUS_OK ;
}
/*
get all DCs
*/
static NTSTATUS get_dcs ( TALLOC_CTX * ctx , struct ldb_context * ldb ,
const char * searched_site , bool need_fqdn ,
struct dc_set * * * pset_list , uint32_t flags )
{
/*
* Flags will be used later to indicate things like least - expensive
* or same - site options
*/
const char * attrs_none [ ] = { NULL } ;
const char * attrs3 [ ] = { " name " , NULL } ;
struct ldb_dn * configdn , * sitedn , * dn , * sitescontainerdn ;
struct ldb_result * r ;
struct dc_set * * set_list = NULL ;
uint32_t i ;
int ret ;
uint32_t current_pos = 0 ;
NTSTATUS status ;
2012-12-06 17:37:21 +04:00
TALLOC_CTX * subctx ;
2011-10-04 15:08:50 +04:00
* pset_list = set_list = NULL ;
subctx = talloc_new ( ctx ) ;
NT_STATUS_HAVE_NO_MEMORY ( subctx ) ;
configdn = ldb_get_config_basedn ( ldb ) ;
/* Let's search for the Site container */
ret = ldb_search ( ldb , subctx , & r , configdn , LDB_SCOPE_SUBTREE , attrs_none ,
" (objectClass=sitesContainer) " ) ;
if ( ret ! = LDB_SUCCESS ) {
DEBUG ( 2 , ( __location__ " : Failed to find sitesContainer within %s - %s \n " ,
ldb_dn_get_linearized ( configdn ) , ldb_errstring ( ldb ) ) ) ;
talloc_free ( subctx ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
if ( r - > count > 1 ) {
DEBUG ( 2 , ( __location__ " : Expected 1 sitesContainer - found %u within %s \n " ,
r - > count , ldb_dn_get_linearized ( configdn ) ) ) ;
talloc_free ( subctx ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
sitescontainerdn = talloc_steal ( subctx , r - > msgs [ 0 ] - > dn ) ;
talloc_free ( r ) ;
/*
* TODO : Here we should have a more subtle handling
* for the case " same-site "
*/
ret = ldb_search ( ldb , subctx , & r , sitescontainerdn , LDB_SCOPE_SUBTREE ,
attrs_none , " (objectClass=server) " ) ;
if ( ret ! = LDB_SUCCESS ) {
DEBUG ( 2 , ( __location__ " : Failed to find servers within %s - %s \n " ,
ldb_dn_get_linearized ( sitescontainerdn ) , ldb_errstring ( ldb ) ) ) ;
talloc_free ( subctx ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
talloc_free ( r ) ;
if ( searched_site ! = NULL & & searched_site [ 0 ] ! = ' \0 ' ) {
ret = ldb_search ( ldb , subctx , & r , configdn , LDB_SCOPE_SUBTREE ,
attrs_none , " (&(name=%s)(objectClass=site)) " , searched_site ) ;
if ( ret ! = LDB_SUCCESS ) {
talloc_free ( subctx ) ;
return NT_STATUS_FOOBAR ;
} else if ( r - > count ! = 1 ) {
talloc_free ( subctx ) ;
return NT_STATUS_FOOBAR ;
}
/* All of this was to get the DN of the searched_site */
sitedn = r - > msgs [ 0 ] - > dn ;
2014-03-31 20:45:07 +04:00
/*
* We will realloc + 2 because we will need one additional place
* for element at current_pos + 1 for the NULL element
*/
set_list = talloc_realloc ( subctx , set_list , struct dc_set * , current_pos + 2 ) ;
2014-02-13 08:51:11 +04:00
if ( set_list = = NULL ) {
TALLOC_FREE ( subctx ) ;
return NT_STATUS_NO_MEMORY ;
}
2011-10-04 15:08:50 +04:00
set_list [ current_pos ] = talloc ( set_list , struct dc_set ) ;
2014-02-13 08:51:11 +04:00
if ( set_list [ current_pos ] = = NULL ) {
TALLOC_FREE ( subctx ) ;
return NT_STATUS_NO_MEMORY ;
}
2011-10-04 15:08:50 +04:00
set_list [ current_pos ] - > names = NULL ;
set_list [ current_pos ] - > count = 0 ;
2014-03-31 20:45:07 +04:00
set_list [ current_pos + 1 ] = NULL ;
2011-10-04 15:08:50 +04:00
status = get_dcs_insite ( subctx , ldb , sitedn ,
set_list [ current_pos ] , need_fqdn ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 2 , ( __location__ " : Failed to get DC from site %s - %s \n " ,
ldb_dn_get_linearized ( sitedn ) , nt_errstr ( status ) ) ) ;
talloc_free ( subctx ) ;
return status ;
}
talloc_free ( r ) ;
current_pos + + ;
}
/* Let's find all the sites */
ret = ldb_search ( ldb , subctx , & r , configdn , LDB_SCOPE_SUBTREE , attrs3 , " (objectClass=site) " ) ;
if ( ret ! = LDB_SUCCESS ) {
DEBUG ( 2 , ( __location__ " : Failed to find any site containers in %s \n " ,
ldb_dn_get_linearized ( configdn ) ) ) ;
talloc_free ( subctx ) ;
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
/*
* TODO :
* We should randomize the order in the main site ,
* it ' s mostly needed for sysvol / netlogon referral .
* Depending of flag we either randomize order of the
* not " in the same site DCs "
* or we randomize by group of site that have the same cost
* In the long run we want to manipulate an array of site_set
* All the site in one set have the same cost ( if least - expansive options is selected )
* and we will put all the dc related to 1 site set into 1 DCs set .
* Within a site set , site order has to be randomized
*
* But for the moment we just return the list of sites
*/
if ( r - > count ) {
/*
* We will realloc + 2 because we will need one additional place
* for element at current_pos + 1 for the NULL element
*/
set_list = talloc_realloc ( subctx , set_list , struct dc_set * ,
current_pos + 2 ) ;
2014-02-13 08:51:11 +04:00
if ( set_list = = NULL ) {
TALLOC_FREE ( subctx ) ;
return NT_STATUS_NO_MEMORY ;
}
2011-10-04 15:08:50 +04:00
set_list [ current_pos ] = talloc ( ctx , struct dc_set ) ;
2014-02-13 08:51:11 +04:00
if ( set_list [ current_pos ] = = NULL ) {
TALLOC_FREE ( subctx ) ;
return NT_STATUS_NO_MEMORY ;
}
2011-10-04 15:08:50 +04:00
set_list [ current_pos ] - > names = NULL ;
set_list [ current_pos ] - > count = 0 ;
set_list [ current_pos + 1 ] = NULL ;
}
for ( i = 0 ; i < r - > count ; i + + ) {
const char * site_name = ldb_msg_find_attr_as_string ( r - > msgs [ i ] , " name " , NULL ) ;
if ( site_name = = NULL ) {
DEBUG ( 2 , ( __location__ " : Failed to find name attribute in %s \n " ,
ldb_dn_get_linearized ( r - > msgs [ i ] - > dn ) ) ) ;
talloc_free ( subctx ) ;
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
if ( searched_site = = NULL | |
strcmp ( searched_site , site_name ) ! = 0 ) {
DEBUG ( 2 , ( __location__ " : Site: %s %s \n " ,
searched_site , site_name ) ) ;
/*
* Do all the site but the one of the client
* ( because it has already been done . . . )
*/
dn = r - > msgs [ i ] - > dn ;
status = get_dcs_insite ( subctx , ldb , dn ,
set_list [ current_pos ] ,
need_fqdn ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( subctx ) ;
return status ;
}
}
}
* pset_list = talloc_move ( ctx , & set_list ) ;
talloc_free ( subctx ) ;
return NT_STATUS_OK ;
}
static NTSTATUS dodomain_referral ( struct loadparm_context * lp_ctx ,
struct ldb_context * sam_ctx ,
const struct tsocket_address * client ,
struct dfs_GetDFSReferral * r )
{
/*
* TODO for the moment we just return the local domain
*/
NTSTATUS status ;
const char * dns_domain = lpcfg_dnsdomain ( lp_ctx ) ;
const char * netbios_domain = lpcfg_workgroup ( lp_ctx ) ;
struct dfs_referral_type * referrals ;
const char * referral_str ;
/* In the future this needs to be fetched from the ldb */
uint32_t found_domain = 2 ;
2012-06-10 16:08:20 +04:00
if ( lpcfg_server_role ( lp_ctx ) ! = ROLE_ACTIVE_DIRECTORY_DC ) {
2011-10-04 15:08:50 +04:00
DEBUG ( 10 , ( " Received a domain referral request on a non DC \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
if ( r - > in . req . max_referral_level < 3 ) {
DEBUG ( 2 , ( " invalid max_referral_level %u \n " ,
r - > in . req . max_referral_level ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
r - > out . resp = talloc_zero ( r , struct dfs_referral_resp ) ;
if ( r - > out . resp = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
r - > out . resp - > path_consumed = 0 ;
r - > out . resp - > header_flags = 0 ; /* Do like w2k3 */
r - > out . resp - > nb_referrals = found_domain ; /* the fqdn one + the NT domain */
referrals = talloc_zero_array ( r - > out . resp ,
struct dfs_referral_type ,
r - > out . resp - > nb_referrals ) ;
if ( referrals = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
r - > out . resp - > referral_entries = referrals ;
referral_str = talloc_asprintf ( r , " \\ %s " , netbios_domain ) ;
if ( referral_str = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
status = fill_domain_dfs_referraltype ( referrals ,
& referrals [ 0 ] , 3 ,
referral_str ,
NULL , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 2 , ( " %s: Unable to fill domain referral structure - %s \n " ,
__location__ , nt_errstr ( status ) ) ) ;
return status ;
}
referral_str = talloc_asprintf ( r , " \\ %s " , dns_domain ) ;
if ( referral_str = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
status = fill_domain_dfs_referraltype ( referrals ,
& referrals [ 1 ] , 3 ,
referral_str ,
NULL , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 2 , ( " %s: Unable to fill domain referral structure - %s \n " ,
__location__ , nt_errstr ( status ) ) ) ;
return status ;
}
return NT_STATUS_OK ;
}
/*
* Handle the logic for dfs referral request like
* \ \ dns_domain or \ \ netbios_domain .
*/
static NTSTATUS dodc_referral ( struct loadparm_context * lp_ctx ,
struct ldb_context * sam_ctx ,
const struct tsocket_address * client ,
struct dfs_GetDFSReferral * r ,
const char * domain_name )
{
NTSTATUS status ;
const char * site_name = NULL ; /* Name of the site where the client is */
bool need_fqdn = false ;
unsigned int i ;
const char * * dc_list = NULL ;
uint32_t num_dcs = 0 ;
struct dc_set * * set ;
char * client_str = NULL ;
struct dfs_referral_type * referrals ;
const char * referral_str ;
2012-06-10 16:08:20 +04:00
if ( lpcfg_server_role ( lp_ctx ) ! = ROLE_ACTIVE_DIRECTORY_DC ) {
2011-10-04 15:08:50 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
if ( r - > in . req . max_referral_level < 3 ) {
DEBUG ( 2 , ( " invalid max_referral_level %u \n " ,
r - > in . req . max_referral_level ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
DEBUG ( 10 , ( " in this we have request for %s requested is %s \n " ,
domain_name , r - > in . req . servername ) ) ;
if ( strchr ( domain_name , ' . ' ) ) {
need_fqdn = 1 ;
}
if ( tsocket_address_is_inet ( client , " ip " ) ) {
client_str = tsocket_address_inet_addr_string ( client , r ) ;
if ( client_str = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
}
2018-03-26 04:25:45 +03:00
site_name = samdb_client_site_name ( sam_ctx , r , client_str , NULL , true ) ;
2011-10-04 15:08:50 +04:00
status = get_dcs ( r , sam_ctx , site_name , need_fqdn , & set , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 3 , ( " Unable to get list of DCs - %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
for ( i = 0 ; set [ i ] ; i + + ) {
uint32_t j ;
dc_list = talloc_realloc ( r , dc_list , const char * ,
num_dcs + set [ i ] - > count + 1 ) ;
if ( dc_list = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
for ( j = 0 ; j < set [ i ] - > count ; j + + ) {
dc_list [ num_dcs + j ] = talloc_move ( dc_list ,
& set [ i ] - > names [ j ] ) ;
}
num_dcs = num_dcs + set [ i ] - > count ;
TALLOC_FREE ( set [ i ] ) ;
dc_list [ num_dcs ] = NULL ;
}
r - > out . resp = talloc_zero ( r , struct dfs_referral_resp ) ;
if ( r - > out . resp = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
r - > out . resp - > path_consumed = 0 ;
r - > out . resp - > header_flags = 0 ; /* Do like w2k3 */
r - > out . resp - > nb_referrals = 1 ;
referrals = talloc_zero_array ( r - > out . resp ,
struct dfs_referral_type ,
r - > out . resp - > nb_referrals ) ;
if ( referrals = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
r - > out . resp - > referral_entries = referrals ;
if ( r - > in . req . servername [ 0 ] = = ' \\ ' ) {
referral_str = talloc_asprintf ( referrals , " %s " ,
domain_name ) ;
} else {
referral_str = talloc_asprintf ( referrals , " \\ %s " ,
domain_name ) ;
}
if ( referral_str = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
status = fill_domain_dfs_referraltype ( referrals ,
& referrals [ 0 ] , 3 ,
referral_str ,
dc_list , num_dcs ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 2 , ( " %s: Unable to fill domain referral structure - %s \n " ,
__location__ , nt_errstr ( status ) ) ) ;
return status ;
}
return NT_STATUS_OK ;
}
/*
* Handle the logic for dfs referral request like
* \ \ domain \ sysvol or \ \ domain \ netlogon
*/
static NTSTATUS dosysvol_referral ( struct loadparm_context * lp_ctx ,
struct ldb_context * sam_ctx ,
const struct tsocket_address * client ,
struct dfs_GetDFSReferral * r ,
const char * domain_name ,
const char * dfs_name )
{
const char * site_name = NULL ; /* Name of the site where the client is */
bool need_fqdn = false ;
unsigned int i , c = 0 , nb_entries = 0 ;
struct dc_set * * set ;
char * client_str = NULL ;
NTSTATUS status ;
struct dfs_referral_type * referrals ;
2012-06-10 16:08:20 +04:00
if ( lpcfg_server_role ( lp_ctx ) ! = ROLE_ACTIVE_DIRECTORY_DC ) {
2011-10-04 15:08:50 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
if ( r - > in . req . max_referral_level < 3 ) {
DEBUG ( 2 , ( " invalid max_referral_level %u \n " ,
r - > in . req . max_referral_level ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
DEBUG ( 10 , ( " in this we have request for %s and share %s requested is %s \n " ,
domain_name , dfs_name , r - > in . req . servername ) ) ;
if ( strchr ( domain_name , ' . ' ) ) {
need_fqdn = 1 ;
}
if ( tsocket_address_is_inet ( client , " ip " ) ) {
client_str = tsocket_address_inet_addr_string ( client , r ) ;
if ( client_str = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
}
2018-03-26 04:25:45 +03:00
site_name = samdb_client_site_name ( sam_ctx , r , client_str , NULL , true ) ;
2011-10-04 15:08:50 +04:00
status = get_dcs ( r , sam_ctx , site_name , need_fqdn , & set , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 3 , ( " Unable to get list of DCs - %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
for ( i = 0 ; set [ i ] ; i + + ) {
nb_entries = nb_entries + set [ i ] - > count ;
}
r - > out . resp = talloc_zero ( r , struct dfs_referral_resp ) ;
if ( r - > out . resp = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
/* The length is expected in bytes */
r - > out . resp - > path_consumed = strlen_m ( r - > in . req . servername ) * 2 ;
/* Do like w2k3 and like in 3.3.5.3 of MS-DFSC*/
r - > out . resp - > header_flags = DFS_HEADER_FLAG_STORAGE_SVR ;
r - > out . resp - > nb_referrals = nb_entries ;
referrals = talloc_zero_array ( r - > out . resp ,
struct dfs_referral_type ,
r - > out . resp - > nb_referrals ) ;
if ( referrals = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
r - > out . resp - > referral_entries = referrals ;
c = 0 ;
for ( i = 0 ; set [ i ] ; i + + ) {
uint32_t j ;
for ( j = 0 ; j < set [ i ] - > count ; j + + ) {
struct dfs_referral_type * ref = & referrals [ c ] ;
const char * referral_str ;
referral_str = talloc_asprintf ( referrals , " \\ %s \\ %s " ,
set [ i ] - > names [ j ] , dfs_name ) ;
if ( referral_str = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
DEBUG ( 8 , ( " Doing a dfs referral for %s with this value "
" %s requested %s \n " ,
set [ i ] - > names [ j ] , referral_str ,
r - > in . req . servername ) ) ;
status = fill_normal_dfs_referraltype ( referrals , ref ,
r - > in . req . max_referral_level ,
r - > in . req . servername ,
referral_str , c = = 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 2 , ( " %s: Unable to fill domain referral "
" structure - %s \n " ,
__location__ , nt_errstr ( status ) ) ) ;
return status ;
}
c + + ;
}
}
return NT_STATUS_OK ;
}
/*
trans2 getdfsreferral implementation
*/
NTSTATUS dfs_server_ad_get_referrals ( struct loadparm_context * lp_ctx ,
struct ldb_context * sam_ctx ,
const struct tsocket_address * client ,
struct dfs_GetDFSReferral * r )
{
char * server_name = NULL ;
char * dfs_name = NULL ;
char * link_path = NULL ;
const char * netbios_domain ;
const char * dns_domain ;
const char * netbios_name ;
const char * dns_name ;
2013-06-05 17:01:14 +04:00
const char * * netbios_aliases ;
2015-07-09 23:20:12 +03:00
char path_separator ;
2011-10-04 15:08:50 +04:00
if ( ! lpcfg_host_msdfs ( lp_ctx ) ) {
return NT_STATUS_FS_DRIVER_REQUIRED ;
}
if ( r - > in . req . servername = = NULL ) {
return NT_STATUS_INVALID_PARAMETER ;
}
DEBUG ( 8 , ( " Requested DFS name: %s length: %u \n " ,
r - > in . req . servername ,
( unsigned int ) strlen_m ( r - > in . req . servername ) * 2 ) ) ;
/*
* If the servername is " " then we are in a case of domain dfs
* and the client just searches for the list of local domain
* it is attached and also trusted ones .
*/
if ( strlen ( r - > in . req . servername ) = = 0 ) {
return dodomain_referral ( lp_ctx , sam_ctx , client , r ) ;
}
server_name = talloc_strdup ( r , r - > in . req . servername ) ;
if ( server_name = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2015-07-09 23:20:12 +03:00
path_separator = ( * server_name = = ' / ' ) ? ' / ' : ' \\ ' ;
while ( * server_name & & * server_name = = path_separator ) {
2011-10-04 15:08:50 +04:00
server_name + + ;
}
2015-07-09 23:20:13 +03:00
dfs_name = strchr_m ( server_name , path_separator ) ;
2011-10-04 15:08:50 +04:00
if ( dfs_name ! = NULL ) {
dfs_name [ 0 ] = ' \0 ' ;
dfs_name + + ;
2015-07-09 23:20:13 +03:00
link_path = strchr_m ( dfs_name , path_separator ) ;
2011-10-04 15:08:50 +04:00
if ( link_path ! = NULL ) {
link_path [ 0 ] = ' \0 ' ;
link_path + + ;
}
}
if ( link_path ! = NULL ) {
/*
* If it is a DFS Link we do not
* handle it here .
*/
return NT_STATUS_NOT_FOUND ;
}
netbios_domain = lpcfg_workgroup ( lp_ctx ) ;
dns_domain = lpcfg_dnsdomain ( lp_ctx ) ;
netbios_name = lpcfg_netbios_name ( lp_ctx ) ;
dns_name = talloc_asprintf ( r , " %s.%s " , netbios_name , dns_domain ) ;
if ( dns_name = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
if ( ( strcasecmp_m ( server_name , netbios_name ) = = 0 ) | |
( strcasecmp_m ( server_name , dns_name ) = = 0 ) ) {
/*
* If it is not domain related do not
* handle it here .
*/
return NT_STATUS_NOT_FOUND ;
2012-07-30 02:49:13 +04:00
}
2011-10-04 15:08:50 +04:00
2012-07-30 02:49:13 +04:00
if ( is_ipaddress ( server_name ) ) {
/*
* If it is not domain related do not
* handle it here .
*/
return NT_STATUS_NOT_FOUND ;
2011-10-04 15:08:50 +04:00
}
2013-06-05 17:01:14 +04:00
netbios_aliases = lpcfg_netbios_aliases ( lp_ctx ) ;
while ( netbios_aliases & & * netbios_aliases ) {
const char * netbios_alias = * netbios_aliases ;
char * dns_alias ;
int cmp ;
cmp = strcasecmp_m ( server_name , netbios_alias ) ;
if ( cmp = = 0 ) {
/*
* If it is not domain related do not
* handle it here .
*/
return NT_STATUS_NOT_FOUND ;
}
dns_alias = talloc_asprintf ( r , " %s.%s " ,
netbios_alias ,
dns_domain ) ;
if ( dns_alias = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
cmp = strcasecmp_m ( server_name , dns_alias ) ;
talloc_free ( dns_alias ) ;
if ( cmp = = 0 ) {
/*
* If it is not domain related do not
* handle it here .
*/
return NT_STATUS_NOT_FOUND ;
}
netbios_aliases + + ;
}
2011-10-04 15:08:50 +04:00
if ( ( strcasecmp_m ( server_name , netbios_domain ) ! = 0 ) & &
( strcasecmp_m ( server_name , dns_domain ) ! = 0 ) ) {
/*
* Not a domain we handle .
*/
return NT_STATUS_INVALID_PARAMETER ;
}
/*
* Here we have filtered the thing the requested name don ' t contain our DNS name .
* So if the share = = NULL or if share in ( " sysvol " , " netlogon " )
* then we proceed . In the first case it will be a dc refereal in the second it will
* be just a sysvol / netlogon referral .
*/
if ( dfs_name = = NULL ) {
return dodc_referral ( lp_ctx , sam_ctx ,
client , r , server_name ) ;
}
/*
* Here we have filtered the thing the requested name don ' t contain our DNS name .
* So if the share = = NULL or if share in ( " sysvol " , " netlogon " )
* then we proceed . In the first case it will be a dc refereal in the second it will
* be just a sysvol / netlogon referral .
*/
if ( strcasecmp ( dfs_name , " sysvol " ) = = 0 | |
strcasecmp ( dfs_name , " netlogon " ) = = 0 ) {
return dosysvol_referral ( lp_ctx , sam_ctx , client , r ,
server_name , dfs_name ) ;
}
/* By default until all the case are handled */
return NT_STATUS_NOT_FOUND ;
}