mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
7d2c68f2e2
Signed-off-by: Andreas Schneider <asn@samba.org> Reviewed-by: Andrew Bartlett <abartlet@samba.org> Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org> Autobuild-Date(master): Mon Jul 17 12:41:21 UTC 2023 on atb-devel-224
399 lines
14 KiB
C
399 lines
14 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
NBT netbios routines and daemon - version 2
|
|
Copyright (C) Andrew Tridgell 1994-1998
|
|
Copyright (C) Luke Kenneth Casson Leighton 1994-1998
|
|
Copyright (C) Jeremy Allison 1994-2003
|
|
|
|
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/svcctl.h"
|
|
#include "nmbd/nmbd.h"
|
|
|
|
extern uint16_t samba_nb_type; /* Samba's NetBIOS type. */
|
|
|
|
static void become_domain_master_browser_bcast(const char *);
|
|
|
|
/****************************************************************************
|
|
Fail to become a Domain Master Browser on a subnet.
|
|
****************************************************************************/
|
|
|
|
static void become_domain_master_fail(struct subnet_record *subrec,
|
|
struct response_record *rrec,
|
|
struct nmb_name *fail_name)
|
|
{
|
|
unstring failname;
|
|
struct work_record *work;
|
|
struct server_record *servrec;
|
|
|
|
pull_ascii_nstring(failname, sizeof(failname), fail_name->name);
|
|
work = find_workgroup_on_subnet(subrec, failname);
|
|
if(!work) {
|
|
DEBUG(0,("become_domain_master_fail: Error - cannot find \
|
|
workgroup %s on subnet %s\n", failname, subrec->subnet_name));
|
|
return;
|
|
}
|
|
|
|
/* Set the state back to DOMAIN_NONE. */
|
|
work->dom_state = DOMAIN_NONE;
|
|
|
|
if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL) {
|
|
DEBUG(0,("become_domain_master_fail: Error - cannot find server %s \
|
|
in workgroup %s on subnet %s\n",
|
|
lp_netbios_name(), work->work_group, subrec->subnet_name));
|
|
return;
|
|
}
|
|
|
|
/* Update our server status. */
|
|
servrec->serv.type &= ~SV_TYPE_DOMAIN_MASTER;
|
|
|
|
/* Tell the namelist writer to write out a change. */
|
|
subrec->work_changed = True;
|
|
|
|
DEBUG(0,("become_domain_master_fail: Failed to become a domain master browser for \
|
|
workgroup %s on subnet %s. Couldn't register name %s.\n",
|
|
work->work_group, subrec->subnet_name, nmb_namestr(fail_name)));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Become a Domain Master Browser on a subnet.
|
|
****************************************************************************/
|
|
|
|
static void become_domain_master_stage2(struct subnet_record *subrec,
|
|
struct userdata_struct *userdata,
|
|
struct nmb_name *registered_name,
|
|
uint16_t nb_flags,
|
|
int ttl, struct in_addr registered_ip)
|
|
{
|
|
unstring regname;
|
|
struct work_record *work;
|
|
struct server_record *servrec;
|
|
|
|
pull_ascii_nstring(regname, sizeof(regname), registered_name->name);
|
|
work = find_workgroup_on_subnet( subrec, regname);
|
|
|
|
if(!work) {
|
|
DEBUG(0,("become_domain_master_stage2: Error - cannot find \
|
|
workgroup %s on subnet %s\n", regname, subrec->subnet_name));
|
|
return;
|
|
}
|
|
|
|
if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL) {
|
|
DEBUG(0,("become_domain_master_stage2: Error - cannot find server %s \
|
|
in workgroup %s on subnet %s\n",
|
|
lp_netbios_name(), regname, subrec->subnet_name));
|
|
work->dom_state = DOMAIN_NONE;
|
|
return;
|
|
}
|
|
|
|
/* Set the state in the workgroup structure. */
|
|
work->dom_state = DOMAIN_MST; /* Become domain master. */
|
|
|
|
/* Update our server status. */
|
|
servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MASTER);
|
|
|
|
/* Tell the namelist writer to write out a change. */
|
|
subrec->work_changed = True;
|
|
|
|
if( DEBUGLVL( 0 ) ) {
|
|
dbgtext( "*****\n\nSamba server %s ", lp_netbios_name() );
|
|
dbgtext( "is now a domain master browser for " );
|
|
dbgtext( "workgroup %s ", work->work_group );
|
|
dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
|
|
}
|
|
|
|
if( subrec == unicast_subnet ) {
|
|
struct nmb_name nmbname;
|
|
struct in_addr my_first_ip;
|
|
const struct in_addr *nip;
|
|
|
|
/* Put our name and first IP address into the
|
|
workgroup struct as domain master browser. This
|
|
will stop us syncing with ourself if we are also
|
|
a local master browser. */
|
|
|
|
make_nmb_name(&nmbname, lp_netbios_name(), 0x20);
|
|
|
|
work->dmb_name = nmbname;
|
|
|
|
/* Pick the first interface IPv4 address as the domain master
|
|
* browser ip. */
|
|
nip = first_ipv4_iface();
|
|
if (!nip) {
|
|
DEBUG(0,("become_domain_master_stage2: "
|
|
"Error. get_interface returned NULL\n"));
|
|
return;
|
|
}
|
|
my_first_ip = *nip;
|
|
|
|
putip((char *)&work->dmb_addr, &my_first_ip);
|
|
|
|
/* We successfully registered by unicast with the
|
|
WINS server. We now expect to become the domain
|
|
master on the local subnets. If this fails, it's
|
|
probably a 1.9.16p2 to 1.9.16p11 server's fault.
|
|
|
|
This is a configuration issue that should be addressed
|
|
by the network administrator - you shouldn't have
|
|
several machines configured as a domain master browser
|
|
for the same WINS scope (except if they are 1.9.17 or
|
|
greater, and you know what you're doing.
|
|
|
|
see docs/DOMAIN.txt.
|
|
|
|
*/
|
|
become_domain_master_browser_bcast(work->work_group);
|
|
} else {
|
|
/*
|
|
* Now we are a domain master on a broadcast subnet, we need to add
|
|
* the WORKGROUP<1b> name to the unicast subnet so that we can answer
|
|
* unicast requests sent to this name. This bug wasn't found for a while
|
|
* as it is strange to have a DMB without using WINS. JRA.
|
|
*/
|
|
insert_permanent_name_into_unicast(subrec, registered_name, nb_flags);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Start the name registration process when becoming a Domain Master Browser
|
|
on a subnet.
|
|
****************************************************************************/
|
|
|
|
static void become_domain_master_stage1(struct subnet_record *subrec, const char *wg_name)
|
|
{
|
|
struct work_record *work;
|
|
|
|
DEBUG(2,("become_domain_master_stage1: Becoming domain master browser for \
|
|
workgroup %s on subnet %s\n", wg_name, subrec->subnet_name));
|
|
|
|
/* First, find the workgroup on the subnet. */
|
|
if((work = find_workgroup_on_subnet( subrec, wg_name )) == NULL) {
|
|
DEBUG(0,("become_domain_master_stage1: Error - unable to find workgroup %s on subnet %s.\n",
|
|
wg_name, subrec->subnet_name));
|
|
return;
|
|
}
|
|
|
|
DEBUG(3,("become_domain_master_stage1: go to first stage: register <1b> name\n"));
|
|
work->dom_state = DOMAIN_WAIT;
|
|
|
|
/* WORKGROUP<1b> is the domain master browser name. */
|
|
register_name(subrec, work->work_group,0x1b,samba_nb_type,
|
|
become_domain_master_stage2,
|
|
become_domain_master_fail, NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Function called when a query for a WORKGROUP<1b> name succeeds.
|
|
This is normally a fail condition as it means there is already
|
|
a domain master browser for a workgroup and we were trying to
|
|
become one.
|
|
****************************************************************************/
|
|
|
|
static void become_domain_master_query_success(struct subnet_record *subrec,
|
|
struct userdata_struct *userdata,
|
|
struct nmb_name *nmbname, struct in_addr ip,
|
|
struct res_rec *rrec)
|
|
{
|
|
unstring name;
|
|
struct in_addr allones_ip;
|
|
|
|
pull_ascii_nstring(name, sizeof(name), nmbname->name);
|
|
|
|
/* If the given ip is not ours, then we can't become a domain
|
|
controller as the name is already registered.
|
|
*/
|
|
|
|
/* BUG note. Samba 1.9.16p11 servers seem to return the broadcast
|
|
address or zero ip for this query. Pretend this is ok. */
|
|
|
|
allones_ip.s_addr = htonl(INADDR_BROADCAST);
|
|
|
|
if(ismyip_v4(ip) || ip_equal_v4(allones_ip, ip) || is_zero_ip_v4(ip)) {
|
|
if( DEBUGLVL( 3 ) ) {
|
|
dbgtext( "become_domain_master_query_success():\n" );
|
|
dbgtext( "Our address (%s) ", inet_ntoa(ip) );
|
|
dbgtext( "returned in query for name %s ", nmb_namestr(nmbname) );
|
|
dbgtext( "(domain master browser name) " );
|
|
dbgtext( "on subnet %s.\n", subrec->subnet_name );
|
|
dbgtext( "Continuing with domain master code.\n" );
|
|
}
|
|
|
|
become_domain_master_stage1(subrec, name);
|
|
} else {
|
|
if( DEBUGLVL( 0 ) ) {
|
|
dbgtext( "become_domain_master_query_success:\n" );
|
|
dbgtext( "There is already a domain master browser at " );
|
|
dbgtext( "IP %s for workgroup %s ", inet_ntoa(ip), name );
|
|
dbgtext( "registered on subnet %s.\n", subrec->subnet_name );
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Function called when a query for a WORKGROUP<1b> name fails.
|
|
This is normally a success condition as it then allows us to register
|
|
our own Domain Master Browser name.
|
|
****************************************************************************/
|
|
|
|
static void become_domain_master_query_fail(struct subnet_record *subrec,
|
|
struct response_record *rrec,
|
|
struct nmb_name *question_name, int fail_code)
|
|
{
|
|
unstring name;
|
|
|
|
/* If the query was unicast, and the error is not NAM_ERR (name didn't exist),
|
|
then this is a failure. Otherwise, not finding the name is what we want. */
|
|
|
|
if((subrec == unicast_subnet) && (fail_code != NAM_ERR)) {
|
|
DEBUG(0,("become_domain_master_query_fail: Error %d returned when \
|
|
querying WINS server for name %s.\n",
|
|
fail_code, nmb_namestr(question_name)));
|
|
return;
|
|
}
|
|
|
|
/* Otherwise - not having the name allows us to register it. */
|
|
pull_ascii_nstring(name, sizeof(name), question_name->name);
|
|
become_domain_master_stage1(subrec, name);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Attempt to become a domain master browser on all broadcast subnets.
|
|
****************************************************************************/
|
|
|
|
static void become_domain_master_browser_bcast(const char *workgroup_name)
|
|
{
|
|
struct subnet_record *subrec;
|
|
|
|
for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
|
|
struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name);
|
|
|
|
if (work && (work->dom_state == DOMAIN_NONE)) {
|
|
struct nmb_name nmbname;
|
|
make_nmb_name(&nmbname,workgroup_name,0x1b);
|
|
|
|
/*
|
|
* Check for our name on the given broadcast subnet first, only initiate
|
|
* further processing if we cannot find it.
|
|
*/
|
|
|
|
if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL) {
|
|
if( DEBUGLVL( 0 ) ) {
|
|
dbgtext( "become_domain_master_browser_bcast:\n" );
|
|
dbgtext( "Attempting to become domain master browser on " );
|
|
dbgtext( "workgroup %s on subnet %s\n",
|
|
workgroup_name, subrec->subnet_name );
|
|
}
|
|
|
|
/* Send out a query to establish whether there's a
|
|
domain controller on the local subnet. If not,
|
|
we can become a domain controller.
|
|
*/
|
|
|
|
DEBUG(0,("become_domain_master_browser_bcast: querying subnet %s \
|
|
for domain master browser on workgroup %s\n", subrec->subnet_name, workgroup_name));
|
|
|
|
query_name(subrec, workgroup_name, nmbname.name_type,
|
|
become_domain_master_query_success,
|
|
become_domain_master_query_fail,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Attempt to become a domain master browser by registering with WINS.
|
|
****************************************************************************/
|
|
|
|
static void become_domain_master_browser_wins(const char *workgroup_name)
|
|
{
|
|
struct work_record *work;
|
|
|
|
work = find_workgroup_on_subnet(unicast_subnet, workgroup_name);
|
|
|
|
if (work && (work->dom_state == DOMAIN_NONE)) {
|
|
struct nmb_name nmbname;
|
|
|
|
make_nmb_name(&nmbname,workgroup_name,0x1b);
|
|
|
|
/*
|
|
* Check for our name on the unicast subnet first, only initiate
|
|
* further processing if we cannot find it.
|
|
*/
|
|
|
|
if (find_name_on_subnet(unicast_subnet, &nmbname, FIND_SELF_NAME) == NULL) {
|
|
if( DEBUGLVL( 0 ) ) {
|
|
dbgtext( "become_domain_master_browser_wins:\n" );
|
|
dbgtext( "Attempting to become domain master browser " );
|
|
dbgtext( "on workgroup %s, subnet %s.\n",
|
|
workgroup_name, unicast_subnet->subnet_name );
|
|
}
|
|
|
|
/* Send out a query to establish whether there's a
|
|
domain master browser registered with WINS. If not,
|
|
we can become a domain master browser.
|
|
*/
|
|
|
|
DEBUG(0,("become_domain_master_browser_wins: querying WINS server from IP %s \
|
|
for domain master browser name %s on workgroup %s\n",
|
|
inet_ntoa(unicast_subnet->myip), nmb_namestr(&nmbname), workgroup_name));
|
|
|
|
query_name(unicast_subnet, workgroup_name, nmbname.name_type,
|
|
become_domain_master_query_success,
|
|
become_domain_master_query_fail,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Add the domain logon server and domain master browser names
|
|
if we are set up to do so.
|
|
**************************************************************************/
|
|
|
|
void add_domain_names(time_t t)
|
|
{
|
|
static time_t lastrun = 0;
|
|
|
|
if ((lastrun != 0) && (t < lastrun + (CHECK_TIME_ADD_DOM_NAMES * 60)))
|
|
return;
|
|
|
|
lastrun = t;
|
|
|
|
/* Do the "internet group" - <1c> names. */
|
|
if (IS_DC)
|
|
add_logon_names();
|
|
|
|
/* Do the domain master names. */
|
|
if(lp_domain_master()) {
|
|
if(we_are_a_wins_client()) {
|
|
/* We register the WORKGROUP<1b> name with the WINS
|
|
server first, and call add_domain_master_bcast()
|
|
only if this is successful.
|
|
|
|
This results in domain logon services being gracefully provided,
|
|
as opposed to the aggressive nature of 1.9.16p2 to 1.9.16p11.
|
|
1.9.16p2 to 1.9.16p11 - due to a bug in namelogon.c,
|
|
cannot provide domain master / domain logon services.
|
|
*/
|
|
become_domain_master_browser_wins(lp_workgroup());
|
|
} else {
|
|
become_domain_master_browser_bcast(lp_workgroup());
|
|
}
|
|
}
|
|
}
|