mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
r15543: New implementation of 'net ads join' to be more like Windows XP.
The motivating factor is to not require more privileges for the user account than Windows does when joining a domain. The points of interest are * net_ads_join() uses same rpc mechanisms as net_rpc_join() * Enable CLDAP queries for filling in the majority of the ADS_STRUCT->config information * Remove ldap_initialized() from sam/idmap_ad.c and libads/ldap.c * Remove some unnecessary fields from ADS_STRUCT * Manually set the dNSHostName and servicePrincipalName attribute using the machine account after the join Thanks to Guenther and Simo for the review. Still to do: * Fix the userAccountControl for DES only systems * Set the userPrincipalName in order to support things like 'kinit -k' (although we might be able to just use the sAMAccountName instead) * Re-add support for pre-creating the machine account in a specific OU
This commit is contained in:
parent
e4734cb99c
commit
4c4ea7b20f
@ -241,7 +241,7 @@ LIBADS_OBJ = libads/ldap.o libads/ldap_printer.o libads/sasl.o \
|
||||
libads/krb5_setpw.o libads/ldap_user.o \
|
||||
libads/ads_struct.o libads/kerberos_keytab.o \
|
||||
libads/disp_sec.o libads/ads_utils.o libads/ldap_utils.o \
|
||||
libads/authdata.o
|
||||
libads/authdata.o libads/cldap.o
|
||||
|
||||
LIBADS_SERVER_OBJ = libads/util.o libads/kerberos_verify.o
|
||||
|
||||
@ -567,7 +567,7 @@ TOOL_OBJ = client/smbctool.o client/clitar.o $(PARAM_OBJ) $(LIBSMB_OBJ) \
|
||||
$(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) \
|
||||
$(READLINE_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ)
|
||||
|
||||
NET_OBJ1 = utils/net.o utils/net_ads.o utils/net_ads_cldap.o utils/net_help.o \
|
||||
NET_OBJ1 = utils/net.o utils/net_ads.o utils/net_help.o \
|
||||
utils/net_rap.o utils/net_rpc.o utils/net_rpc_samsync.o \
|
||||
utils/net_rpc_join.o utils/net_time.o utils/net_lookup.o \
|
||||
utils/net_cache.o utils/net_groupmap.o utils/net_idmap.o \
|
||||
|
@ -17,7 +17,6 @@ typedef struct {
|
||||
char *realm;
|
||||
char *workgroup;
|
||||
char *ldap_server;
|
||||
char *ldap_uri;
|
||||
int foreign; /* set to 1 if connecting to a foreign realm */
|
||||
} server;
|
||||
|
||||
@ -37,7 +36,6 @@ typedef struct {
|
||||
struct {
|
||||
char *realm;
|
||||
char *bind_path;
|
||||
char *schema_path;
|
||||
char *ldap_server_name;
|
||||
time_t current_time;
|
||||
} config;
|
||||
@ -219,19 +217,6 @@ typedef void **ADS_MODLIST;
|
||||
#define GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP 0x00000004 /* 4 */
|
||||
#define GTYPE_DISTRIBUTION_UNIVERSAL_GROUP 0x00000008 /* 8 */
|
||||
|
||||
/* Mailslot or cldap getdcname response flags */
|
||||
#define ADS_PDC 0x00000001 /* DC is PDC */
|
||||
#define ADS_GC 0x00000004 /* DC is a GC of forest */
|
||||
#define ADS_LDAP 0x00000008 /* DC is an LDAP server */
|
||||
#define ADS_DS 0x00000010 /* DC supports DS */
|
||||
#define ADS_KDC 0x00000020 /* DC is running KDC */
|
||||
#define ADS_TIMESERV 0x00000040 /* DC is running time services */
|
||||
#define ADS_CLOSEST 0x00000080 /* DC is closest to client */
|
||||
#define ADS_WRITABLE 0x00000100 /* DC has writable DS */
|
||||
#define ADS_GOOD_TIMESERV 0x00000200 /* DC has hardware clock
|
||||
(and running time) */
|
||||
#define ADS_NDNC 0x00000400 /* DomainName is non-domain NC serviced
|
||||
by LDAP server */
|
||||
#define ADS_PINGS 0x0000FFFF /* Ping response */
|
||||
#define ADS_DNS_CONTROLLER 0x20000000 /* DomainControllerName is a DNS name*/
|
||||
#define ADS_DNS_DOMAIN 0x40000000 /* DomainName is a DNS name */
|
||||
|
58
source/include/ads_cldap.h
Normal file
58
source/include/ads_cldap.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Samba Unix/Linux SMB client library
|
||||
net ads cldap functions
|
||||
Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
|
||||
Copyright (C) 2003 Jim McDonough (jmcd@us.ibm.com)
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#define MAX_DNS_LABEL 255 + 1
|
||||
|
||||
struct cldap_netlogon_reply {
|
||||
uint32 type;
|
||||
uint32 flags;
|
||||
UUID_FLAT guid;
|
||||
|
||||
char forest[MAX_DNS_LABEL];
|
||||
char domain[MAX_DNS_LABEL];
|
||||
char hostname[MAX_DNS_LABEL];
|
||||
|
||||
char netbios_domain[MAX_DNS_LABEL];
|
||||
char netbios_hostname[MAX_DNS_LABEL];
|
||||
|
||||
char unk[MAX_DNS_LABEL];
|
||||
char user_name[MAX_DNS_LABEL];
|
||||
char site_name[MAX_DNS_LABEL];
|
||||
char site_name_2[MAX_DNS_LABEL];
|
||||
|
||||
uint32 version;
|
||||
uint16 lmnt_token;
|
||||
uint16 lm20_token;
|
||||
};
|
||||
|
||||
/* Mailslot or cldap getdcname response flags */
|
||||
#define ADS_PDC 0x00000001 /* DC is PDC */
|
||||
#define ADS_GC 0x00000004 /* DC is a GC of forest */
|
||||
#define ADS_LDAP 0x00000008 /* DC is an LDAP server */
|
||||
#define ADS_DS 0x00000010 /* DC supports DS */
|
||||
#define ADS_KDC 0x00000020 /* DC is running KDC */
|
||||
#define ADS_TIMESERV 0x00000040 /* DC is running time services */
|
||||
#define ADS_CLOSEST 0x00000080 /* DC is closest to client */
|
||||
#define ADS_WRITABLE 0x00000100 /* DC has writable DS */
|
||||
#define ADS_GOOD_TIMESERV 0x00000200 /* DC has hardware clock (and running time) */
|
||||
#define ADS_NDNC 0x00000400 /* DomainName is non-domain NC serviced by LDAP server */
|
||||
|
||||
|
@ -911,50 +911,29 @@ extern int errno;
|
||||
#include "messages.h"
|
||||
#include "charset.h"
|
||||
#include "dynconfig.h"
|
||||
|
||||
#include "util_getent.h"
|
||||
|
||||
#include "debugparse.h"
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include "privileges.h"
|
||||
|
||||
#include "smb.h"
|
||||
|
||||
#include "ads_cldap.h"
|
||||
#include "nameserv.h"
|
||||
|
||||
#include "secrets.h"
|
||||
|
||||
#include "byteorder.h"
|
||||
|
||||
#include "privileges.h"
|
||||
|
||||
#include "rpc_misc.h"
|
||||
|
||||
#include "rpc_dce.h"
|
||||
|
||||
#include "mapping.h"
|
||||
|
||||
#include "passdb.h"
|
||||
|
||||
#include "rpc_secdes.h"
|
||||
|
||||
#include "authdata.h"
|
||||
|
||||
#include "msdfs.h"
|
||||
|
||||
#include "rap.h"
|
||||
|
||||
#include "md5.h"
|
||||
#include "hmacmd5.h"
|
||||
|
||||
#include "ntlmssp.h"
|
||||
|
||||
#include "auth.h"
|
||||
|
||||
#include "ntdomain.h"
|
||||
|
||||
#include "rpc_svcctl.h"
|
||||
#include "rpc_ntsvcs.h"
|
||||
#include "rpc_lsa.h"
|
||||
@ -972,11 +951,8 @@ extern int errno;
|
||||
#include "rpc_shutdown.h"
|
||||
#include "rpc_perfcount.h"
|
||||
#include "rpc_perfcount_defs.h"
|
||||
|
||||
#include "nt_printing.h"
|
||||
|
||||
#include "idmap.h"
|
||||
|
||||
#include "client.h"
|
||||
|
||||
#ifdef WITH_SMBWRAPPER
|
||||
@ -984,21 +960,13 @@ extern int errno;
|
||||
#endif
|
||||
|
||||
#include "session.h"
|
||||
|
||||
#include "asn_1.h"
|
||||
|
||||
#include "popt.h"
|
||||
|
||||
#include "mangle.h"
|
||||
|
||||
#include "module.h"
|
||||
|
||||
#include "nsswitch/winbind_client.h"
|
||||
|
||||
#include "spnego.h"
|
||||
|
||||
#include "rpc_client.h"
|
||||
|
||||
#include "event.h"
|
||||
|
||||
/*
|
||||
|
@ -118,12 +118,13 @@ void ads_destroy(ADS_STRUCT **ads)
|
||||
|
||||
is_mine = (*ads)->is_mine;
|
||||
#if HAVE_LDAP
|
||||
if ((*ads)->ld) ldap_unbind((*ads)->ld);
|
||||
if ((*ads)->ld) {
|
||||
ldap_unbind((*ads)->ld);
|
||||
}
|
||||
#endif
|
||||
SAFE_FREE((*ads)->server.realm);
|
||||
SAFE_FREE((*ads)->server.workgroup);
|
||||
SAFE_FREE((*ads)->server.ldap_server);
|
||||
SAFE_FREE((*ads)->server.ldap_uri);
|
||||
|
||||
SAFE_FREE((*ads)->auth.realm);
|
||||
SAFE_FREE((*ads)->auth.password);
|
||||
@ -132,7 +133,6 @@ void ads_destroy(ADS_STRUCT **ads)
|
||||
|
||||
SAFE_FREE((*ads)->config.realm);
|
||||
SAFE_FREE((*ads)->config.bind_path);
|
||||
SAFE_FREE((*ads)->config.schema_path);
|
||||
SAFE_FREE((*ads)->config.ldap_server_name);
|
||||
|
||||
SAFE_FREE((*ads)->schema.sfu_uidnumber_attr);
|
||||
|
@ -20,33 +20,6 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "utils/net.h"
|
||||
|
||||
#ifdef HAVE_ADS
|
||||
|
||||
#define MAX_DNS_LABEL 255 + 1
|
||||
|
||||
struct cldap_netlogon_reply {
|
||||
uint32 type;
|
||||
uint32 flags;
|
||||
UUID_FLAT guid;
|
||||
|
||||
char forest[MAX_DNS_LABEL];
|
||||
char domain[MAX_DNS_LABEL];
|
||||
char hostname[MAX_DNS_LABEL];
|
||||
|
||||
char netbios_domain[MAX_DNS_LABEL];
|
||||
char netbios_hostname[MAX_DNS_LABEL];
|
||||
|
||||
char unk[MAX_DNS_LABEL];
|
||||
char user_name[MAX_DNS_LABEL];
|
||||
char site_name[MAX_DNS_LABEL];
|
||||
char site_name_2[MAX_DNS_LABEL];
|
||||
|
||||
uint32 version;
|
||||
uint16 lmnt_token;
|
||||
uint16 lm20_token;
|
||||
};
|
||||
|
||||
/*
|
||||
These seem to be strings as described in RFC1035 4.1.4 and can be:
|
||||
@ -272,94 +245,34 @@ static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
do a cldap netlogon query
|
||||
*/
|
||||
int ads_cldap_netlogon(ADS_STRUCT *ads)
|
||||
/*******************************************************************
|
||||
do a cldap netlogon query. Always 389/udp
|
||||
*******************************************************************/
|
||||
|
||||
BOOL ads_cldap_netlogon(const char *server, const char *realm, struct cldap_netlogon_reply *reply)
|
||||
{
|
||||
int sock;
|
||||
int ret;
|
||||
struct cldap_netlogon_reply reply;
|
||||
const char *target = opt_host ? opt_host : inet_ntoa(ads->ldap_ip);
|
||||
|
||||
sock = open_udp_socket(target, ads->ldap_port);
|
||||
sock = open_udp_socket(server, LDAP_PORT );
|
||||
if (sock == -1) {
|
||||
d_fprintf(stderr, "Failed to open udp socket to %s:%u\n",
|
||||
inet_ntoa(ads->ldap_ip),
|
||||
ads->ldap_port);
|
||||
return -1;
|
||||
|
||||
DEBUG(2,("ads_cldap_netlogon: Failed to open udp socket to %s\n",
|
||||
server));
|
||||
return False;
|
||||
}
|
||||
|
||||
ret = send_cldap_netlogon(sock, ads->config.realm, global_myname(), 6);
|
||||
ret = send_cldap_netlogon(sock, realm, global_myname(), 6);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
return False;
|
||||
}
|
||||
ret = recv_cldap_netlogon(sock, &reply);
|
||||
ret = recv_cldap_netlogon(sock, reply);
|
||||
close(sock);
|
||||
|
||||
if (ret == -1) {
|
||||
return -1;
|
||||
return False;
|
||||
}
|
||||
|
||||
d_printf("Information for Domain Controller: %s\n\n",
|
||||
ads->config.ldap_server_name);
|
||||
|
||||
d_printf("Response Type: ");
|
||||
switch (reply.type) {
|
||||
case SAMLOGON_AD_UNK_R:
|
||||
d_printf("SAMLOGON\n");
|
||||
break;
|
||||
case SAMLOGON_AD_R:
|
||||
d_printf("SAMLOGON_USER\n");
|
||||
break;
|
||||
default:
|
||||
d_printf("0x%x\n", reply.type);
|
||||
break;
|
||||
}
|
||||
d_printf("GUID: %s\n",
|
||||
smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
|
||||
d_printf("Flags:\n"
|
||||
"\tIs a PDC: %s\n"
|
||||
"\tIs a GC of the forest: %s\n"
|
||||
"\tIs an LDAP server: %s\n"
|
||||
"\tSupports DS: %s\n"
|
||||
"\tIs running a KDC: %s\n"
|
||||
"\tIs running time services: %s\n"
|
||||
"\tIs the closest DC: %s\n"
|
||||
"\tIs writable: %s\n"
|
||||
"\tHas a hardware clock: %s\n"
|
||||
"\tIs a non-domain NC serviced by LDAP server: %s\n",
|
||||
(reply.flags & ADS_PDC) ? "yes" : "no",
|
||||
(reply.flags & ADS_GC) ? "yes" : "no",
|
||||
(reply.flags & ADS_LDAP) ? "yes" : "no",
|
||||
(reply.flags & ADS_DS) ? "yes" : "no",
|
||||
(reply.flags & ADS_KDC) ? "yes" : "no",
|
||||
(reply.flags & ADS_TIMESERV) ? "yes" : "no",
|
||||
(reply.flags & ADS_CLOSEST) ? "yes" : "no",
|
||||
(reply.flags & ADS_WRITABLE) ? "yes" : "no",
|
||||
(reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
|
||||
(reply.flags & ADS_NDNC) ? "yes" : "no");
|
||||
|
||||
printf("Forest:\t\t\t%s\n", reply.forest);
|
||||
printf("Domain:\t\t\t%s\n", reply.domain);
|
||||
printf("Domain Controller:\t%s\n", reply.hostname);
|
||||
|
||||
printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
|
||||
printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
|
||||
|
||||
if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
|
||||
if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
|
||||
|
||||
printf("Site Name:\t\t%s\n", reply.site_name);
|
||||
printf("Site Name (2):\t\t%s\n", reply.site_name_2);
|
||||
|
||||
d_printf("NT Version: %d\n", reply.version);
|
||||
d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
|
||||
d_printf("LM20 Token: %.2x\n", reply.lm20_token);
|
||||
|
||||
return ret;
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@ -112,31 +112,52 @@ static int ldap_search_with_timeout(LDAP *ld,
|
||||
/*
|
||||
try a connection to a given ldap server, returning True and setting the servers IP
|
||||
in the ads struct if successful
|
||||
|
||||
TODO : add a negative connection cache in here leveraged off of the one
|
||||
found in the rpc code. --jerry
|
||||
*/
|
||||
BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
|
||||
BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
|
||||
{
|
||||
char *srv;
|
||||
struct cldap_netlogon_reply cldap_reply;
|
||||
|
||||
if (!server || !*server) {
|
||||
return False;
|
||||
}
|
||||
|
||||
DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
|
||||
|
||||
DEBUG(5,("ads_try_connect: sending CLDAP request to %s\n", server));
|
||||
|
||||
/* this copes with inet_ntoa brokenness */
|
||||
|
||||
srv = SMB_STRDUP(server);
|
||||
|
||||
ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout());
|
||||
if (!ads->ld) {
|
||||
free(srv);
|
||||
ZERO_STRUCT( cldap_reply );
|
||||
|
||||
if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
|
||||
DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
|
||||
return False;
|
||||
}
|
||||
ads->ldap_port = port;
|
||||
|
||||
/* Check the CLDAP reply flags */
|
||||
|
||||
if ( !(cldap_reply.flags & ADS_LDAP) ) {
|
||||
DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
|
||||
srv));
|
||||
SAFE_FREE( srv );
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Fill in the ads->config values */
|
||||
|
||||
SAFE_FREE(ads->config.realm);
|
||||
SAFE_FREE(ads->config.bind_path);
|
||||
SAFE_FREE(ads->config.ldap_server_name);
|
||||
|
||||
ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
|
||||
strupper_m(cldap_reply.domain);
|
||||
ads->config.realm = SMB_STRDUP(cldap_reply.domain);
|
||||
ads->config.bind_path = ads_build_dn(ads->config.realm);
|
||||
|
||||
ads->ldap_port = LDAP_PORT;
|
||||
ads->ldap_ip = *interpret_addr2(srv);
|
||||
free(srv);
|
||||
SAFE_FREE(srv);
|
||||
|
||||
/* cache the successful connection */
|
||||
|
||||
@ -145,29 +166,6 @@ BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
try a connection to a given ldap server, based on URL, returning True if successful
|
||||
*/
|
||||
static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
|
||||
{
|
||||
#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
|
||||
DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
|
||||
ads->server.ldap_uri));
|
||||
|
||||
|
||||
if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
|
||||
return True;
|
||||
}
|
||||
DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
|
||||
|
||||
#else
|
||||
|
||||
DEBUG(1, ("no URL support in LDAP libs!\n"));
|
||||
#endif
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Try to find an AD dc using our internal name resolution routines
|
||||
Try the realm first and then then workgroup name if netbios is not
|
||||
@ -233,8 +231,6 @@ again:
|
||||
|
||||
/* if we fail this loop, then giveup since all the IP addresses returned were dead */
|
||||
for ( i=0; i<count; i++ ) {
|
||||
/* since this is an ads conection request, default to LDAP_PORT is not set */
|
||||
int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
|
||||
fstring server;
|
||||
|
||||
fstrcpy( server, inet_ntoa(ip_list[i].ip) );
|
||||
@ -242,7 +238,7 @@ again:
|
||||
if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
|
||||
continue;
|
||||
|
||||
if ( ads_try_connect(ads, server, port) ) {
|
||||
if ( ads_try_connect(ads, server) ) {
|
||||
SAFE_FREE(ip_list);
|
||||
return True;
|
||||
}
|
||||
@ -270,16 +266,10 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads)
|
||||
ads->last_attempt = time(NULL);
|
||||
ads->ld = NULL;
|
||||
|
||||
/* try with a URL based server */
|
||||
|
||||
if (ads->server.ldap_uri &&
|
||||
ads_try_connect_uri(ads)) {
|
||||
goto got_connection;
|
||||
}
|
||||
|
||||
/* try with a user specified server */
|
||||
|
||||
if (ads->server.ldap_server &&
|
||||
ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
|
||||
ads_try_connect(ads, ads->server.ldap_server)) {
|
||||
goto got_connection;
|
||||
}
|
||||
|
||||
@ -292,22 +282,12 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads)
|
||||
got_connection:
|
||||
DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
|
||||
|
||||
status = ads_server_info(ads);
|
||||
if (!ADS_ERR_OK(status)) {
|
||||
DEBUG(1,("Failed to get ldap server info\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
|
||||
|
||||
status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
|
||||
if (!ADS_ERR_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!ads->auth.user_name) {
|
||||
/* have to use the userPrincipalName value here and
|
||||
not servicePrincipalName; found by Guenther Deschner @ Sernet */
|
||||
not servicePrincipalName; found by Guenther Deschner @ Sernet.
|
||||
|
||||
Is this still correct? The comment does not match
|
||||
the code. --jerry */
|
||||
|
||||
asprintf(&ads->auth.user_name, "host/%s", global_myname() );
|
||||
}
|
||||
@ -331,10 +311,35 @@ got_connection:
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the caller() requested no LDAP bind, then we are done */
|
||||
|
||||
if (ads->auth.flags & ADS_AUTH_NO_BIND) {
|
||||
return ADS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Otherwise setup the TCP LDAP session */
|
||||
|
||||
if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
|
||||
LDAP_PORT, lp_ldap_timeout())) == NULL )
|
||||
{
|
||||
return ADS_ERROR(LDAP_OPERATIONS_ERROR);
|
||||
}
|
||||
ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
|
||||
|
||||
status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
|
||||
if (!ADS_ERR_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* fill in the current time and offsets */
|
||||
|
||||
status = ads_current_time( ads );
|
||||
if ( !ADS_ERR_OK(status) ) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Now do the bind */
|
||||
|
||||
if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
|
||||
return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
|
||||
}
|
||||
@ -2464,7 +2469,7 @@ static time_t ads_parse_time(const char *str)
|
||||
}
|
||||
|
||||
|
||||
const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char * OID)
|
||||
const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, const char *schema_path, TALLOC_CTX *mem_ctx, const char * OID)
|
||||
{
|
||||
ADS_STATUS rc;
|
||||
int count = 0;
|
||||
@ -2482,8 +2487,8 @@ const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rc = ads_do_search_retry(ads, ads->config.schema_path,
|
||||
LDAP_SCOPE_SUBTREE, expr, attrs, &res);
|
||||
rc = ads_do_search_retry(ads, schema_path, LDAP_SCOPE_SUBTREE,
|
||||
expr, attrs, &res);
|
||||
if (!ADS_ERR_OK(rc)) {
|
||||
goto failed;
|
||||
}
|
||||
@ -2513,87 +2518,49 @@ failed:
|
||||
* @param ads connection to ads server
|
||||
* @return status of search
|
||||
**/
|
||||
ADS_STATUS ads_server_info(ADS_STRUCT *ads)
|
||||
ADS_STATUS ads_current_time(ADS_STRUCT *ads)
|
||||
{
|
||||
const char *attrs[] = {"ldapServiceName",
|
||||
"currentTime",
|
||||
"schemaNamingContext", NULL};
|
||||
const char *attrs[] = {"currentTime", NULL};
|
||||
ADS_STATUS status;
|
||||
void *res;
|
||||
char *value;
|
||||
char *p;
|
||||
char *timestr;
|
||||
char *schema_path;
|
||||
TALLOC_CTX *ctx;
|
||||
ADS_STRUCT *ads_s = ads;
|
||||
|
||||
if (!(ctx = talloc_init("ads_server_info"))) {
|
||||
return ADS_ERROR(LDAP_NO_MEMORY);
|
||||
}
|
||||
|
||||
status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
|
||||
/* establish a new ldap tcp session if necessary */
|
||||
|
||||
if ( !ads->ld ) {
|
||||
if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
|
||||
ads->server.ldap_server )) == NULL )
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
ads_s->auth.flags = ADS_AUTH_ANON_BIND;
|
||||
status = ads_connect( ads_s );
|
||||
if ( !ADS_ERR_OK(status))
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
|
||||
if (!ADS_ERR_OK(status)) {
|
||||
talloc_destroy(ctx);
|
||||
return status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
value = ads_pull_string(ads, ctx, res, "ldapServiceName");
|
||||
if (!value) {
|
||||
ads_msgfree(ads, res);
|
||||
talloc_destroy(ctx);
|
||||
return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
|
||||
}
|
||||
|
||||
timestr = ads_pull_string(ads, ctx, res, "currentTime");
|
||||
timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
|
||||
if (!timestr) {
|
||||
ads_msgfree(ads, res);
|
||||
talloc_destroy(ctx);
|
||||
return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
|
||||
status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
|
||||
goto done;
|
||||
}
|
||||
|
||||
schema_path = ads_pull_string(ads, ctx, res, "schemaNamingContext");
|
||||
if (!schema_path) {
|
||||
ads_msgfree(ads, res);
|
||||
talloc_destroy(ctx);
|
||||
return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
|
||||
}
|
||||
|
||||
SAFE_FREE(ads->config.schema_path);
|
||||
ads->config.schema_path = SMB_STRDUP(schema_path);
|
||||
|
||||
ads_msgfree(ads, res);
|
||||
|
||||
p = strchr(value, ':');
|
||||
if (!p) {
|
||||
talloc_destroy(ctx);
|
||||
DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
|
||||
"so was deemed invalid\n"));
|
||||
return ADS_ERROR(LDAP_DECODING_ERROR);
|
||||
}
|
||||
|
||||
SAFE_FREE(ads->config.ldap_server_name);
|
||||
|
||||
ads->config.ldap_server_name = SMB_STRDUP(p+1);
|
||||
p = strchr(ads->config.ldap_server_name, '$');
|
||||
if (!p || p[1] != '@') {
|
||||
talloc_destroy(ctx);
|
||||
DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
|
||||
" so was deemed invalid\n", ads->config.ldap_server_name));
|
||||
SAFE_FREE(ads->config.ldap_server_name);
|
||||
return ADS_ERROR(LDAP_DECODING_ERROR);
|
||||
}
|
||||
|
||||
*p = 0;
|
||||
|
||||
SAFE_FREE(ads->config.realm);
|
||||
SAFE_FREE(ads->config.bind_path);
|
||||
|
||||
ads->config.realm = SMB_STRDUP(p+2);
|
||||
ads->config.bind_path = ads_build_dn(ads->config.realm);
|
||||
|
||||
DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
|
||||
ads->config.ldap_server_name, ads->config.realm,
|
||||
ads->config.bind_path));
|
||||
|
||||
/* but save the time and offset in the original ADS_STRUCT */
|
||||
|
||||
ads->config.current_time = ads_parse_time(timestr);
|
||||
|
||||
if (ads->config.current_time != 0) {
|
||||
@ -2601,9 +2568,44 @@ ADS_STATUS ads_server_info(ADS_STRUCT *ads)
|
||||
DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
|
||||
}
|
||||
|
||||
status = ADS_SUCCESS;
|
||||
|
||||
done:
|
||||
/* free any temporary ads connections */
|
||||
if ( ads_s != ads ) {
|
||||
ads_destroy( &ads_s );
|
||||
}
|
||||
talloc_destroy(ctx);
|
||||
|
||||
return ADS_SUCCESS;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
*********************************************************************/
|
||||
|
||||
static ADS_STATUS ads_schema_path(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **schema_path)
|
||||
{
|
||||
ADS_STATUS status;
|
||||
void *res;
|
||||
const char *schema;
|
||||
const char *attrs[] = { "schemaNamingContext", NULL };
|
||||
|
||||
status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
|
||||
if (!ADS_ERR_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if ( (schema = ads_pull_string(ads, mem_ctx, res, "schemaNamingContext")) == NULL ) {
|
||||
return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
|
||||
}
|
||||
|
||||
if ( (*schema_path = talloc_strdup(mem_ctx, schema)) == NULL ) {
|
||||
return ADS_ERROR(LDAP_NO_MEMORY);
|
||||
}
|
||||
|
||||
ads_msgfree(ads, res);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2617,41 +2619,71 @@ BOOL ads_check_sfu_mapping(ADS_STRUCT *ads)
|
||||
BOOL ret = False;
|
||||
TALLOC_CTX *ctx = NULL;
|
||||
const char *gidnumber, *uidnumber, *homedir, *shell, *gecos;
|
||||
char *schema_path;
|
||||
ADS_STRUCT *ads_s = ads;
|
||||
ADS_STATUS status;
|
||||
|
||||
ctx = talloc_init("ads_check_sfu_mapping");
|
||||
if (ctx == NULL)
|
||||
if ( (ctx = talloc_init("ads_check_sfu_mapping")) == NULL ) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
gidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
|
||||
/* establish a new ldap tcp session if necessary */
|
||||
|
||||
if ( !ads->ld ) {
|
||||
if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
|
||||
ads->server.ldap_server )) == NULL )
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
ads_s->auth.flags = ADS_AUTH_ANON_BIND;
|
||||
status = ads_connect( ads_s );
|
||||
if ( !ADS_ERR_OK(status))
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = ads_schema_path( ads, ctx, &schema_path );
|
||||
if ( !ADS_ERR_OK(status) ) {
|
||||
DEBUG(3,("ads_check_sfu_mapping: Unable to retrieve schema DN!\n"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
gidnumber = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
|
||||
if (gidnumber == NULL)
|
||||
goto done;
|
||||
ads->schema.sfu_gidnumber_attr = SMB_STRDUP(gidnumber);
|
||||
|
||||
uidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
|
||||
uidnumber = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
|
||||
if (uidnumber == NULL)
|
||||
goto done;
|
||||
ads->schema.sfu_uidnumber_attr = SMB_STRDUP(uidnumber);
|
||||
|
||||
homedir = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
|
||||
homedir = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
|
||||
if (homedir == NULL)
|
||||
goto done;
|
||||
ads->schema.sfu_homedir_attr = SMB_STRDUP(homedir);
|
||||
|
||||
shell = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_SHELL_OID);
|
||||
shell = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_SHELL_OID);
|
||||
if (shell == NULL)
|
||||
goto done;
|
||||
ads->schema.sfu_shell_attr = SMB_STRDUP(shell);
|
||||
|
||||
gecos = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GECOS_OID);
|
||||
gecos = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_GECOS_OID);
|
||||
if (gecos == NULL)
|
||||
goto done;
|
||||
ads->schema.sfu_gecos_attr = SMB_STRDUP(gecos);
|
||||
|
||||
ret = True;
|
||||
done:
|
||||
if (ctx)
|
||||
/* free any temporary ads connections */
|
||||
if ( ads_s != ads ) {
|
||||
ads_destroy( &ads_s );
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
talloc_destroy(ctx);
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2679,81 +2711,6 @@ ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
|
||||
return ADS_SUCCESS;
|
||||
}
|
||||
|
||||
/* this is rather complex - we need to find the allternate (netbios) name
|
||||
for the domain, but there isn't a simple query to do this. Instead
|
||||
we look for the principle names on the DCs account and find one that has
|
||||
the right form, then extract the netbios name of the domain from that
|
||||
|
||||
NOTE! better method is this:
|
||||
|
||||
bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
|
||||
|
||||
but you need to force the bind path to match the configurationNamingContext from the rootDSE
|
||||
|
||||
*/
|
||||
ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
|
||||
{
|
||||
char *expr;
|
||||
ADS_STATUS rc;
|
||||
char **principles;
|
||||
char *prefix;
|
||||
int prefix_length;
|
||||
int i;
|
||||
void *res;
|
||||
const char *attrs[] = {"servicePrincipalName", NULL};
|
||||
size_t num_principals;
|
||||
|
||||
(*workgroup) = NULL;
|
||||
|
||||
asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
|
||||
ads->config.ldap_server_name, ads->config.realm);
|
||||
if (expr == NULL) {
|
||||
return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
rc = ads_search(ads, &res, expr, attrs);
|
||||
free(expr);
|
||||
|
||||
if (!ADS_ERR_OK(rc)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
principles = ads_pull_strings(ads, mem_ctx, res,
|
||||
"servicePrincipalName", &num_principals);
|
||||
|
||||
ads_msgfree(ads, res);
|
||||
|
||||
if (!principles) {
|
||||
return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
|
||||
}
|
||||
|
||||
asprintf(&prefix, "HOST/%s.%s/",
|
||||
ads->config.ldap_server_name,
|
||||
ads->config.realm);
|
||||
|
||||
prefix_length = strlen(prefix);
|
||||
|
||||
for (i=0;principles[i]; i++) {
|
||||
if (strnequal(principles[i], prefix, prefix_length) &&
|
||||
!strequal(ads->config.realm, principles[i]+prefix_length) &&
|
||||
!strchr(principles[i]+prefix_length, '.')) {
|
||||
/* found an alternate (short) name for the domain. */
|
||||
DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
|
||||
principles[i]+prefix_length,
|
||||
ads->config.realm));
|
||||
(*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(prefix);
|
||||
|
||||
if (!*workgroup) {
|
||||
return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
|
||||
}
|
||||
|
||||
return ADS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* find our site name
|
||||
* @param ads connection to ads server
|
||||
|
@ -1024,70 +1024,62 @@ static BOOL resolve_hosts(const char *name, int name_type,
|
||||
static BOOL resolve_ads(const char *name, int name_type,
|
||||
struct ip_service **return_iplist, int *return_count)
|
||||
{
|
||||
#ifdef HAVE_ADS
|
||||
if ( name_type == 0x1c ) {
|
||||
int count, i = 0;
|
||||
NTSTATUS status;
|
||||
TALLOC_CTX *ctx;
|
||||
struct dns_rr_srv *dcs = NULL;
|
||||
int numdcs = 0;
|
||||
int count, i = 0;
|
||||
NTSTATUS status;
|
||||
TALLOC_CTX *ctx;
|
||||
struct dns_rr_srv *dcs = NULL;
|
||||
int numdcs = 0;
|
||||
|
||||
if ( name_type != 0x1c )
|
||||
return False;
|
||||
|
||||
/* try to lookup the _ldap._tcp.<domain> if we are using ADS */
|
||||
if ( lp_security() != SEC_ADS )
|
||||
return False;
|
||||
DEBUG(5,("resolve_hosts: Attempting to resolve DC's for %s using DNS\n",
|
||||
name));
|
||||
|
||||
DEBUG(5,("resolve_hosts: Attempting to resolve DC's for %s using DNS\n",
|
||||
name));
|
||||
|
||||
if ( (ctx = talloc_init("resolve_ads")) == NULL ) {
|
||||
DEBUG(0,("resolve_ads: talloc_init() failed!\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
status = ads_dns_query_dcs( ctx, name, &dcs, &numdcs );
|
||||
if ( !NT_STATUS_IS_OK( status ) ) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if ( (*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, numdcs)) == NULL ) {
|
||||
DEBUG(0,("resolve_ads: malloc failed for %d entries\n", count ));
|
||||
return False;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while ( i < numdcs ) {
|
||||
|
||||
/* use the IP address from the SRV structure if we have one */
|
||||
if ( is_zero_ip( dcs[i].ip ) )
|
||||
(*return_iplist)[i].ip = *interpret_addr2(dcs[i].hostname);
|
||||
else
|
||||
(*return_iplist)[i].ip = dcs[i].ip;
|
||||
|
||||
(*return_iplist)[i].port = dcs[i].port;
|
||||
|
||||
/* make sure it is a valid IP. I considered checking the negative
|
||||
connection cache, but this is the wrong place for it. Maybe only
|
||||
as a hac. After think about it, if all of the IP addresses retuend
|
||||
from DNS are dead, what hope does a netbios name lookup have?
|
||||
The standard reason for falling back to netbios lookups is that
|
||||
our DNS server doesn't know anything about the DC's -- jerry */
|
||||
|
||||
if ( is_zero_ip((*return_iplist)[i].ip) )
|
||||
continue;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
TALLOC_FREE( dcs );
|
||||
|
||||
*return_count = i;
|
||||
|
||||
return True;
|
||||
} else
|
||||
#endif /* HAVE_ADS */
|
||||
{
|
||||
if ( (ctx = talloc_init("resolve_ads")) == NULL ) {
|
||||
DEBUG(0,("resolve_ads: talloc_init() failed!\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
status = ads_dns_query_dcs( ctx, name, &dcs, &numdcs );
|
||||
if ( !NT_STATUS_IS_OK( status ) ) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if ( (*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, numdcs)) == NULL ) {
|
||||
DEBUG(0,("resolve_ads: malloc failed for %d entries\n", count ));
|
||||
return False;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while ( i < numdcs ) {
|
||||
|
||||
/* use the IP address from the SRV structure if we have one */
|
||||
if ( is_zero_ip( dcs[i].ip ) )
|
||||
(*return_iplist)[i].ip = *interpret_addr2(dcs[i].hostname);
|
||||
else
|
||||
(*return_iplist)[i].ip = dcs[i].ip;
|
||||
|
||||
(*return_iplist)[i].port = dcs[i].port;
|
||||
|
||||
/* make sure it is a valid IP. I considered checking the negative
|
||||
connection cache, but this is the wrong place for it. Maybe only
|
||||
as a hac. After think about it, if all of the IP addresses retuend
|
||||
from DNS are dead, what hope does a netbios name lookup have?
|
||||
The standard reason for falling back to netbios lookups is that
|
||||
our DNS server doesn't know anything about the DC's -- jerry */
|
||||
|
||||
if ( is_zero_ip((*return_iplist)[i].ip) )
|
||||
continue;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
TALLOC_FREE( dcs );
|
||||
|
||||
*return_count = i;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
@ -1178,8 +1170,7 @@ BOOL internal_resolve_name(const char *name, int name_type,
|
||||
}
|
||||
} else if(strequal( tok, "ads")) {
|
||||
/* deal with 0x1c names here. This will result in a
|
||||
SRV record lookup for _ldap._tcp.<domain> if we
|
||||
are using 'security = ads' */
|
||||
SRV record lookup */
|
||||
if (resolve_ads(name, name_type, return_iplist, return_count)) {
|
||||
result = True;
|
||||
goto done;
|
||||
|
@ -618,18 +618,11 @@ static void dcip_to_name( const char *domainname, const char *realm,
|
||||
if ( lp_security() == SEC_ADS )
|
||||
{
|
||||
ADS_STRUCT *ads;
|
||||
ADS_STATUS status;
|
||||
|
||||
ads = ads_init( realm, domainname, NULL );
|
||||
ads->auth.flags |= ADS_AUTH_NO_BIND;
|
||||
|
||||
if ( !ads_try_connect( ads, inet_ntoa(ip), LDAP_PORT ) ) {
|
||||
ads_destroy( &ads );
|
||||
return;
|
||||
}
|
||||
|
||||
status = ads_server_info(ads);
|
||||
if ( !ADS_ERR_OK(status) ) {
|
||||
if ( !ads_try_connect( ads, inet_ntoa(ip) ) ) {
|
||||
ads_destroy( &ads );
|
||||
return;
|
||||
}
|
||||
|
@ -3158,7 +3158,7 @@ WERROR check_published_printers(void)
|
||||
int n_services = lp_numservices();
|
||||
NT_PRINTER_INFO_LEVEL *printer = NULL;
|
||||
|
||||
ads = ads_init(NULL, NULL, NULL);
|
||||
ads = ads_init(lp_realm(), lp_workgroup(), NULL);
|
||||
if (!ads) {
|
||||
DEBUG(3, ("ads_init() failed\n"));
|
||||
return WERR_SERVER_UNAVAILABLE;
|
||||
|
@ -78,10 +78,6 @@ static ADS_STRUCT *ad_idmap_cached_connection(void)
|
||||
ADS_STATUS status;
|
||||
BOOL local = False;
|
||||
|
||||
#ifdef ADS_AUTH_EXTERNAL_BIND
|
||||
local = ((strncmp(ad_idmap_uri, "ldapi://", sizeof("ldapi://") - 1)) == 0);
|
||||
#endif /* ADS_AUTH_EXTERNAL_BIND */
|
||||
|
||||
if (ad_idmap_ads != NULL) {
|
||||
ads = ad_idmap_ads;
|
||||
|
||||
@ -105,40 +101,18 @@ static ADS_STRUCT *ad_idmap_cached_connection(void)
|
||||
setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
|
||||
}
|
||||
|
||||
ads = ads_init(NULL, NULL, NULL);
|
||||
ads = ads_init(lp_realm(), lp_workgroup(), NULL);
|
||||
if (!ads) {
|
||||
DEBUG(1,("ads_init failed\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* if ad_imap_uri is not empty we try to connect to
|
||||
* the given URI in smb.conf. Else try to connect to
|
||||
* one of the DCs
|
||||
*/
|
||||
if (*ad_idmap_uri != '\0') {
|
||||
ads->server.ldap_uri = SMB_STRDUP(ad_idmap_uri);
|
||||
if (ads->server.ldap_uri == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ads->server.ldap_uri = NULL;
|
||||
ads->server.ldap_server = NULL;
|
||||
}
|
||||
/* the machine acct password might have change - fetch it every time */
|
||||
SAFE_FREE(ads->auth.password);
|
||||
ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
|
||||
|
||||
#ifdef ADS_AUTH_EXTERNAL_BIND
|
||||
if (local)
|
||||
ads->auth.flags |= ADS_AUTH_EXTERNAL_BIND;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* the machine acct password might have change - fetch it every time */
|
||||
SAFE_FREE(ads->auth.password);
|
||||
ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
|
||||
|
||||
SAFE_FREE(ads->auth.realm);
|
||||
ads->auth.realm = SMB_STRDUP(lp_realm());
|
||||
}
|
||||
SAFE_FREE(ads->auth.realm);
|
||||
ads->auth.realm = SMB_STRDUP(lp_realm());
|
||||
|
||||
status = ads_connect(ads);
|
||||
if (!ADS_ERR_OK(status)) {
|
||||
|
@ -233,6 +233,29 @@ NTSTATUS connect_to_ipc_anonymous(struct cli_state **c,
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
connect to \\server\ipc$ using KRB5
|
||||
****************************************************************************/
|
||||
NTSTATUS connect_to_ipc_krb5(struct cli_state **c,
|
||||
struct in_addr *server_ip, const char *server_name)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
|
||||
nt_status = cli_full_connection(c, NULL, server_name,
|
||||
server_ip, opt_port,
|
||||
"IPC$", "IPC",
|
||||
opt_user_name, opt_workgroup,
|
||||
opt_password, CLI_FULL_CONNECTION_USE_KERBEROS,
|
||||
Undefined, NULL);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
} else {
|
||||
DEBUG(1,("Cannot connect to server using kerberos. Error was %s\n", nt_errstr(nt_status)));
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect a server and open a given pipe
|
||||
*
|
||||
@ -304,8 +327,9 @@ int net_use_machine_password(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL net_find_server(unsigned flags, struct in_addr *server_ip, char **server_name)
|
||||
BOOL net_find_server(const char *domain, unsigned flags, struct in_addr *server_ip, char **server_name)
|
||||
{
|
||||
const char *d = domain ? domain : opt_target_workgroup;
|
||||
|
||||
if (opt_host) {
|
||||
*server_name = SMB_STRDUP(opt_host);
|
||||
@ -325,23 +349,22 @@ BOOL net_find_server(unsigned flags, struct in_addr *server_ip, char **server_na
|
||||
} else if (flags & NET_FLAGS_PDC) {
|
||||
struct in_addr pdc_ip;
|
||||
|
||||
if (get_pdc_ip(opt_target_workgroup, &pdc_ip)) {
|
||||
if (get_pdc_ip(d, &pdc_ip)) {
|
||||
fstring dc_name;
|
||||
|
||||
if (is_zero_ip(pdc_ip))
|
||||
return False;
|
||||
|
||||
if ( !name_status_find(opt_target_workgroup, 0x1b, 0x20, pdc_ip, dc_name) )
|
||||
if ( !name_status_find(d, 0x1b, 0x20, pdc_ip, dc_name) )
|
||||
return False;
|
||||
|
||||
*server_name = SMB_STRDUP(dc_name);
|
||||
*server_ip = pdc_ip;
|
||||
}
|
||||
|
||||
} else if (flags & NET_FLAGS_DMB) {
|
||||
struct in_addr msbrow_ip;
|
||||
/* if (!resolve_name(MSBROWSE, &msbrow_ip, 1)) */
|
||||
if (!resolve_name(opt_target_workgroup, &msbrow_ip, 0x1B)) {
|
||||
if (!resolve_name(d, &msbrow_ip, 0x1B)) {
|
||||
DEBUG(1,("Unable to resolve domain browser via name lookup\n"));
|
||||
return False;
|
||||
} else {
|
||||
@ -350,7 +373,7 @@ BOOL net_find_server(unsigned flags, struct in_addr *server_ip, char **server_na
|
||||
*server_name = SMB_STRDUP(inet_ntoa(opt_dest_ip));
|
||||
} else if (flags & NET_FLAGS_MASTER) {
|
||||
struct in_addr brow_ips;
|
||||
if (!resolve_name(opt_target_workgroup, &brow_ips, 0x1D)) {
|
||||
if (!resolve_name(d, &brow_ips, 0x1D)) {
|
||||
/* go looking for workgroups */
|
||||
DEBUG(1,("Unable to resolve master browser via name lookup\n"));
|
||||
return False;
|
||||
@ -387,17 +410,27 @@ BOOL net_find_pdc(struct in_addr *server_ip, fstring server_name, const char *do
|
||||
return False;
|
||||
}
|
||||
|
||||
struct cli_state *net_make_ipc_connection( unsigned flags )
|
||||
{
|
||||
return net_make_ipc_connection_ex( NULL, NULL, NULL, flags );
|
||||
}
|
||||
|
||||
struct cli_state *net_make_ipc_connection(unsigned flags)
|
||||
struct cli_state *net_make_ipc_connection_ex( const char *domain, const char *server,
|
||||
struct in_addr *ip, unsigned flags)
|
||||
{
|
||||
char *server_name = NULL;
|
||||
struct in_addr server_ip;
|
||||
struct cli_state *cli = NULL;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
if (!net_find_server(flags, &server_ip, &server_name)) {
|
||||
d_fprintf(stderr, "\nUnable to find a suitable server\n");
|
||||
return NULL;
|
||||
if ( !server || !ip ) {
|
||||
if (!net_find_server(domain, flags, &server_ip, &server_name)) {
|
||||
d_fprintf(stderr, "Unable to find a suitable server\n");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
server_name = SMB_STRDUP( server );
|
||||
server_ip = *ip;
|
||||
}
|
||||
|
||||
if (flags & NET_FLAGS_ANONYMOUS) {
|
||||
|
@ -68,24 +68,15 @@ struct rpc_sh_cmd {
|
||||
|
||||
/* MACROS & DEFINES */
|
||||
|
||||
#define NET_FLAGS_MASTER 1
|
||||
#define NET_FLAGS_DMB 2
|
||||
|
||||
/* Would it be insane to set 'localhost' as the default
|
||||
remote host for this operation?
|
||||
|
||||
For example, localhost is insane for a 'join' operation.
|
||||
*/
|
||||
#define NET_FLAGS_LOCALHOST_DEFAULT_INSANE 4
|
||||
|
||||
/* We want to find the PDC only */
|
||||
#define NET_FLAGS_PDC 8
|
||||
|
||||
/* We want an anonymous connection */
|
||||
#define NET_FLAGS_ANONYMOUS 16
|
||||
|
||||
/* don't open an RPC pipe */
|
||||
#define NET_FLAGS_NO_PIPE 32
|
||||
#define NET_FLAGS_MASTER 0x00000001
|
||||
#define NET_FLAGS_DMB 0x00000002
|
||||
#define NET_FLAGS_LOCALHOST_DEFAULT_INSANE 0x00000004 /* Would it be insane to set 'localhost'
|
||||
as the default remote host for this
|
||||
operation? For example, localhost
|
||||
is insane for a 'join' operation. */
|
||||
#define NET_FLAGS_PDC 0x00000008 /* PDC only */
|
||||
#define NET_FLAGS_ANONYMOUS 0x00000010 /* use an anonymous connection */
|
||||
#define NET_FLAGS_NO_PIPE 0x00000020 /* don't open an RPC pipe */
|
||||
|
||||
/* net share operation modes */
|
||||
#define NET_MODE_SHARE_MIGRATE 1
|
||||
|
@ -23,6 +23,20 @@
|
||||
#include "includes.h"
|
||||
#include "utils/net.h"
|
||||
|
||||
/* Macro for checking RPC error codes to make things more readable */
|
||||
|
||||
#define CHECK_RPC_ERR(rpc, msg) \
|
||||
if (!NT_STATUS_IS_OK(result = rpc)) { \
|
||||
DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
|
||||
goto done; \
|
||||
}
|
||||
|
||||
#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
|
||||
if (!NT_STATUS_IS_OK(result = rpc)) { \
|
||||
DEBUG(0, debug_args); \
|
||||
goto done; \
|
||||
}
|
||||
|
||||
#ifdef HAVE_ADS
|
||||
|
||||
int net_ads_usage(int argc, const char **argv)
|
||||
@ -64,6 +78,79 @@ int net_ads_usage(int argc, const char **argv)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
do a cldap netlogon query
|
||||
*/
|
||||
static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
|
||||
{
|
||||
int ret;
|
||||
struct cldap_netlogon_reply reply;
|
||||
|
||||
if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
|
||||
d_fprintf(stderr, "CLDAP query failed!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
d_printf("Information for Domain Controller: %s\n\n",
|
||||
inet_ntoa(ads->ldap_ip));
|
||||
|
||||
d_printf("Response Type: ");
|
||||
switch (reply.type) {
|
||||
case SAMLOGON_AD_UNK_R:
|
||||
d_printf("SAMLOGON\n");
|
||||
break;
|
||||
case SAMLOGON_AD_R:
|
||||
d_printf("SAMLOGON_USER\n");
|
||||
break;
|
||||
default:
|
||||
d_printf("0x%x\n", reply.type);
|
||||
break;
|
||||
}
|
||||
d_printf("GUID: %s\n",
|
||||
smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
|
||||
d_printf("Flags:\n"
|
||||
"\tIs a PDC: %s\n"
|
||||
"\tIs a GC of the forest: %s\n"
|
||||
"\tIs an LDAP server: %s\n"
|
||||
"\tSupports DS: %s\n"
|
||||
"\tIs running a KDC: %s\n"
|
||||
"\tIs running time services: %s\n"
|
||||
"\tIs the closest DC: %s\n"
|
||||
"\tIs writable: %s\n"
|
||||
"\tHas a hardware clock: %s\n"
|
||||
"\tIs a non-domain NC serviced by LDAP server: %s\n",
|
||||
(reply.flags & ADS_PDC) ? "yes" : "no",
|
||||
(reply.flags & ADS_GC) ? "yes" : "no",
|
||||
(reply.flags & ADS_LDAP) ? "yes" : "no",
|
||||
(reply.flags & ADS_DS) ? "yes" : "no",
|
||||
(reply.flags & ADS_KDC) ? "yes" : "no",
|
||||
(reply.flags & ADS_TIMESERV) ? "yes" : "no",
|
||||
(reply.flags & ADS_CLOSEST) ? "yes" : "no",
|
||||
(reply.flags & ADS_WRITABLE) ? "yes" : "no",
|
||||
(reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
|
||||
(reply.flags & ADS_NDNC) ? "yes" : "no");
|
||||
|
||||
printf("Forest:\t\t\t%s\n", reply.forest);
|
||||
printf("Domain:\t\t\t%s\n", reply.domain);
|
||||
printf("Domain Controller:\t%s\n", reply.hostname);
|
||||
|
||||
printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
|
||||
printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
|
||||
|
||||
if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
|
||||
if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
|
||||
|
||||
printf("Site Name:\t\t%s\n", reply.site_name);
|
||||
printf("Site Name (2):\t\t%s\n", reply.site_name_2);
|
||||
|
||||
d_printf("NT Version: %d\n", reply.version);
|
||||
d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
|
||||
d_printf("LM20 Token: %.2x\n", reply.lm20_token);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
this implements the CLDAP based netlogon lookup requests
|
||||
for finding the domain controller of a ADS domain
|
||||
@ -93,7 +180,7 @@ static int net_ads_lookup(int argc, const char **argv)
|
||||
ads->ldap_port = 389;
|
||||
}
|
||||
|
||||
return ads_cldap_netlogon(ads);
|
||||
return net_ads_cldap_netlogon(ads);
|
||||
}
|
||||
|
||||
|
||||
@ -102,14 +189,7 @@ static int net_ads_info(int argc, const char **argv)
|
||||
{
|
||||
ADS_STRUCT *ads;
|
||||
|
||||
/* if netbios is disabled we have to default to the realm from smb.conf */
|
||||
|
||||
if ( lp_disable_netbios() && *lp_realm() )
|
||||
ads = ads_init(lp_realm(), opt_target_workgroup, opt_host);
|
||||
else
|
||||
ads = ads_init(NULL, opt_target_workgroup, opt_host);
|
||||
|
||||
if (ads) {
|
||||
if ( (ads = ads_init(lp_realm(), opt_target_workgroup, opt_host)) != NULL ) {
|
||||
ads->auth.flags |= ADS_AUTH_NO_BIND;
|
||||
}
|
||||
|
||||
@ -120,6 +200,13 @@ static int net_ads_info(int argc, const char **argv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Try to set the server's current time since we didn't do a full
|
||||
TCP LDAP session initially */
|
||||
|
||||
if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
|
||||
d_fprintf( stderr, "Failed to get server's current time!\n");
|
||||
}
|
||||
|
||||
d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
|
||||
d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
|
||||
d_printf("Realm: %s\n", ads->config.realm);
|
||||
@ -212,10 +299,19 @@ retry:
|
||||
int net_ads_check(void)
|
||||
{
|
||||
ADS_STRUCT *ads;
|
||||
ADS_STATUS status;
|
||||
|
||||
ads = ads_startup();
|
||||
if (!ads)
|
||||
if ( (ads = ads_init( lp_realm(), lp_workgroup(), NULL )) == NULL ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ads->auth.flags |= ADS_AUTH_NO_BIND;
|
||||
|
||||
status = ads_connect(ads);
|
||||
if ( !ADS_ERR_OK(status) ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ads_destroy(&ads);
|
||||
return 0;
|
||||
}
|
||||
@ -226,28 +322,38 @@ int net_ads_check(void)
|
||||
static int net_ads_workgroup(int argc, const char **argv)
|
||||
{
|
||||
ADS_STRUCT *ads;
|
||||
TALLOC_CTX *ctx;
|
||||
const char *workgroup;
|
||||
ADS_STATUS status;
|
||||
const char *realm = NULL;
|
||||
struct cldap_netlogon_reply reply;
|
||||
|
||||
if (!(ads = ads_startup())) return -1;
|
||||
if ( strequal(lp_workgroup(), opt_target_workgroup ) )
|
||||
realm = lp_realm();
|
||||
|
||||
if (!(ctx = talloc_init("net_ads_workgroup"))) {
|
||||
ads_destroy(&ads);
|
||||
ads = ads_init(realm, opt_target_workgroup, opt_host);
|
||||
if (ads) {
|
||||
ads->auth.flags |= ADS_AUTH_NO_BIND;
|
||||
}
|
||||
|
||||
status = ads_connect(ads);
|
||||
if (!ADS_ERR_OK(status) || !ads) {
|
||||
d_fprintf(stderr, "Didn't find the cldap server!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ads->config.realm) {
|
||||
ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
|
||||
ads->ldap_port = 389;
|
||||
}
|
||||
|
||||
if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
|
||||
d_fprintf(stderr, "CLDAP query failed!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
|
||||
d_fprintf(stderr, "Failed to find workgroup for realm '%s'\n",
|
||||
ads->config.realm);
|
||||
talloc_destroy(ctx);
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
d_printf("Workgroup: %s\n", reply.netbios_domain);
|
||||
|
||||
d_printf("Workgroup: %s\n", workgroup);
|
||||
|
||||
talloc_destroy(ctx);
|
||||
ads_destroy(&ads);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -707,28 +813,14 @@ int net_ads_testjoin(int argc, const char **argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
join a domain using ADS
|
||||
*/
|
||||
int net_ads_join(int argc, const char **argv)
|
||||
{
|
||||
ADS_STRUCT *ads;
|
||||
ADS_STATUS rc;
|
||||
char *password;
|
||||
char *machine_account = NULL;
|
||||
char *tmp_password;
|
||||
const char *org_unit = NULL;
|
||||
char *dn;
|
||||
void *res;
|
||||
DOM_SID dom_sid;
|
||||
char *ou_str;
|
||||
uint32 sec_channel_type = SEC_CHAN_WKSTA;
|
||||
uint32 account_type = UF_WORKSTATION_TRUST_ACCOUNT;
|
||||
const char *short_domain_name = NULL;
|
||||
TALLOC_CTX *ctx = NULL;
|
||||
/*******************************************************************
|
||||
Simple configu checks before beginning the join
|
||||
********************************************************************/
|
||||
|
||||
if (lp_server_role() == ROLE_STANDALONE) {
|
||||
d_printf("cannot join as standalone machine\n");
|
||||
static int check_ads_config( void )
|
||||
{
|
||||
if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
|
||||
d_printf("Host is not configured as a member server.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -739,92 +831,397 @@ int net_ads_join(int argc, const char **argv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc > 0) {
|
||||
org_unit = argv[0];
|
||||
if ( lp_security() == SEC_ADS && !*lp_realm()) {
|
||||
d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
|
||||
"join to succeed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!secrets_init()) {
|
||||
DEBUG(1,("Failed to initialise secrets database\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
|
||||
password = SMB_STRDUP(tmp_password);
|
||||
/*******************************************************************
|
||||
Store the machine password and domain SID
|
||||
********************************************************************/
|
||||
|
||||
if (!(ads = ads_startup())) {
|
||||
static int store_domain_account( const char *domain, DOM_SID *sid, const char *pw )
|
||||
{
|
||||
if (!secrets_store_domain_sid(domain, sid)) {
|
||||
DEBUG(1,("Failed to save domain sid\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!*lp_realm()) {
|
||||
d_fprintf(stderr, "realm must be set in in smb.conf for ADS join to succeed.\n");
|
||||
ads_destroy(&ads);
|
||||
if (!secrets_store_machine_password(pw, domain, SEC_CHAN_WKSTA)) {
|
||||
DEBUG(1,("Failed to save machine password\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
********************************************************************/
|
||||
|
||||
static NTSTATUS join_fetch_domain_sid( TALLOC_CTX *mem_ctx, struct cli_state *cli, DOM_SID **sid )
|
||||
{
|
||||
struct rpc_pipe_client *pipe_hnd = NULL;
|
||||
POLICY_HND lsa_pol;
|
||||
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
|
||||
char *domain = NULL;
|
||||
|
||||
if ( (pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_LSARPC, &status)) == NULL ) {
|
||||
DEBUG(0, ("Error connecting to LSA pipe. Error was %s\n",
|
||||
nt_errstr(status) ));
|
||||
return status;
|
||||
}
|
||||
|
||||
status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, True,
|
||||
SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
|
||||
if ( !NT_STATUS_IS_OK(status) )
|
||||
return status;
|
||||
|
||||
status = rpccli_lsa_query_info_policy(pipe_hnd, mem_ctx,
|
||||
&lsa_pol, 5, &domain, sid);
|
||||
if ( !NT_STATUS_IS_OK(status) )
|
||||
return status;
|
||||
|
||||
rpccli_lsa_close(pipe_hnd, mem_ctx, &lsa_pol);
|
||||
cli_rpc_pipe_close(pipe_hnd); /* Done with this pipe */
|
||||
|
||||
/* Bail out if domain didn't get set. */
|
||||
if (!domain) {
|
||||
DEBUG(0, ("Could not get domain name.\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
Do the domain join
|
||||
********************************************************************/
|
||||
|
||||
static NTSTATUS join_create_machine( TALLOC_CTX *mem_ctx, struct cli_state *cli,
|
||||
DOM_SID *dom_sid, const char *clear_pw )
|
||||
{
|
||||
struct rpc_pipe_client *pipe_hnd = NULL;
|
||||
POLICY_HND sam_pol, domain_pol, user_pol;
|
||||
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
|
||||
char *acct_name;
|
||||
const char *const_acct_name;
|
||||
uint32 user_rid;
|
||||
uint32 num_rids, *name_types, *user_rids;
|
||||
uint32 flags = 0x3e8;
|
||||
uint32 acb_info = ACB_WSTRUST;
|
||||
uchar pwbuf[516];
|
||||
SAM_USERINFO_CTR ctr;
|
||||
SAM_USER_INFO_24 p24;
|
||||
SAM_USER_INFO_16 p16;
|
||||
uchar md4_trust_password[16];
|
||||
|
||||
/* Open the domain */
|
||||
|
||||
if ( (pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status)) == NULL ) {
|
||||
DEBUG(0, ("Error connecting to SAM pipe. Error was %s\n",
|
||||
nt_errstr(status) ));
|
||||
return status;
|
||||
}
|
||||
|
||||
status = rpccli_samr_connect(pipe_hnd, mem_ctx,
|
||||
SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
|
||||
if ( !NT_STATUS_IS_OK(status) )
|
||||
return status;
|
||||
|
||||
|
||||
status = rpccli_samr_open_domain(pipe_hnd, mem_ctx, &sam_pol,
|
||||
SEC_RIGHTS_MAXIMUM_ALLOWED, dom_sid, &domain_pol);
|
||||
if ( !NT_STATUS_IS_OK(status) )
|
||||
return status;
|
||||
|
||||
/* Create domain user */
|
||||
|
||||
acct_name = talloc_asprintf(mem_ctx, "%s$", global_myname());
|
||||
strlower_m(acct_name);
|
||||
const_acct_name = acct_name;
|
||||
|
||||
status = rpccli_samr_create_dom_user(pipe_hnd, mem_ctx, &domain_pol,
|
||||
acct_name, acb_info, 0xe005000b, &user_pol, &user_rid);
|
||||
|
||||
if ( !NT_STATUS_IS_OK(status)
|
||||
&& !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS))
|
||||
{
|
||||
d_fprintf(stderr, "Creation of workstation account failed\n");
|
||||
|
||||
/* If NT_STATUS_ACCESS_DENIED then we have a valid
|
||||
username/password combo but the user does not have
|
||||
administrator access. */
|
||||
|
||||
if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED))
|
||||
d_fprintf(stderr, "User specified does not have administrator privileges\n");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* We *must* do this.... don't ask... */
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
|
||||
}
|
||||
|
||||
status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
|
||||
&domain_pol, flags, 1, &const_acct_name,
|
||||
&num_rids, &user_rids, &name_types);
|
||||
if ( !NT_STATUS_IS_OK(status) )
|
||||
return status;
|
||||
|
||||
if ( name_types[0] != SID_NAME_USER) {
|
||||
DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name, name_types[0]));
|
||||
return NT_STATUS_INVALID_WORKSTATION;
|
||||
}
|
||||
|
||||
user_rid = user_rids[0];
|
||||
|
||||
/* Open handle on user */
|
||||
|
||||
status = rpccli_samr_open_user(pipe_hnd, mem_ctx, &domain_pol,
|
||||
SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, &user_pol);
|
||||
|
||||
/* Create a random machine account password */
|
||||
|
||||
E_md4hash( clear_pw, md4_trust_password);
|
||||
encode_pw_buffer(pwbuf, clear_pw, STR_UNICODE);
|
||||
|
||||
/* Set password on machine account */
|
||||
|
||||
ZERO_STRUCT(ctr);
|
||||
ZERO_STRUCT(p24);
|
||||
|
||||
init_sam_user_info24(&p24, (char *)pwbuf,24);
|
||||
|
||||
ctr.switch_value = 24;
|
||||
ctr.info.id24 = &p24;
|
||||
|
||||
status = rpccli_samr_set_userinfo(pipe_hnd, mem_ctx, &user_pol,
|
||||
24, &cli->user_session_key, &ctr);
|
||||
|
||||
/* Why do we have to try to (re-)set the ACB to be the same as what
|
||||
we passed in the samr_create_dom_user() call? When a NT
|
||||
workstation is joined to a domain by an administrator the
|
||||
acb_info is set to 0x80. For a normal user with "Add
|
||||
workstations to the domain" rights the acb_info is 0x84. I'm
|
||||
not sure whether it is supposed to make a difference or not. NT
|
||||
seems to cope with either value so don't bomb out if the set
|
||||
userinfo2 level 0x10 fails. -tpot */
|
||||
|
||||
ZERO_STRUCT(ctr);
|
||||
ctr.switch_value = 16;
|
||||
ctr.info.id16 = &p16;
|
||||
|
||||
init_sam_user_info16(&p16, acb_info);
|
||||
|
||||
/* Ignoring the return value is necessary for joining a domain
|
||||
as a normal user with "Add workstation to domain" privilege. */
|
||||
|
||||
status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol, 16,
|
||||
&cli->user_session_key, &ctr);
|
||||
|
||||
rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
|
||||
cli_rpc_pipe_close(pipe_hnd); /* Done with this pipe */
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
Do the domain join
|
||||
********************************************************************/
|
||||
|
||||
static int net_join_domain( TALLOC_CTX *ctx, const char *servername,
|
||||
struct in_addr *ip, DOM_SID **dom_sid, const char *password )
|
||||
{
|
||||
int ret = -1;
|
||||
struct cli_state *cli = NULL;
|
||||
|
||||
if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, ip, servername)) )
|
||||
goto done;
|
||||
|
||||
saf_store( cli->server_domain, cli->desthost );
|
||||
|
||||
if ( !NT_STATUS_IS_OK(join_fetch_domain_sid( ctx, cli, dom_sid )) )
|
||||
goto done;
|
||||
|
||||
if ( !NT_STATUS_IS_OK(join_create_machine( ctx, cli, *dom_sid, password )) )
|
||||
goto done;
|
||||
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
if ( cli )
|
||||
cli_shutdown(cli);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
Set a machines dNSHostName and servicePrincipalName attributes
|
||||
********************************************************************/
|
||||
|
||||
static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
|
||||
{
|
||||
ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
|
||||
char *host_upn, *new_dn, *controlstr;
|
||||
ADS_MODLIST mods;
|
||||
const char *servicePrincipalName[3] = {NULL, NULL, NULL};
|
||||
char *psp;
|
||||
unsigned acct_control;
|
||||
fstring my_fqdn;
|
||||
LDAPMessage *res = NULL;
|
||||
char *dn_string = NULL;
|
||||
const char *machine_name = global_myname();
|
||||
int count;
|
||||
uint32 account_type;
|
||||
|
||||
if ( !machine_name ) {
|
||||
return ADS_ERROR(LDAP_NO_MEMORY);
|
||||
}
|
||||
|
||||
/* Find our DN */
|
||||
|
||||
status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
|
||||
if (!ADS_ERR_OK(status))
|
||||
return status;
|
||||
|
||||
if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
|
||||
DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
|
||||
return ADS_ERROR(LDAP_NO_MEMORY);
|
||||
}
|
||||
|
||||
if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
|
||||
DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
new_dn = talloc_strdup(ctx, dn_string);
|
||||
ads_memfree(ads_s, dn_string);
|
||||
if (!new_dn) {
|
||||
return ADS_ERROR(LDAP_NO_MEMORY);
|
||||
}
|
||||
|
||||
/* Windows only creates HOST/shortname & HOST/fqdn. We create
|
||||
the UPN as well so that 'kinit -k' will work. You can only
|
||||
request a TGT for entries with a UPN in AD. */
|
||||
|
||||
if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
|
||||
goto done;
|
||||
strupper_m(psp);
|
||||
servicePrincipalName[0] = psp;
|
||||
|
||||
name_to_fqdn(my_fqdn, machine_name);
|
||||
strlower_m(my_fqdn);
|
||||
if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
|
||||
goto done;
|
||||
servicePrincipalName[1] = psp;
|
||||
|
||||
if (!(host_upn = talloc_asprintf(ctx, "%s@%s", servicePrincipalName[0], ads_s->config.realm)))
|
||||
goto done;
|
||||
|
||||
/* set the account control string now */
|
||||
|
||||
acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
|
||||
#ifndef ENCTYPE_ARCFOUR_HMAC
|
||||
acct_control |= UF_USE_DES_KEY_ONLY;
|
||||
#endif
|
||||
if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* now do the mods */
|
||||
|
||||
if (!(mods = ads_init_mods(ctx))) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* fields of primary importance */
|
||||
|
||||
ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
|
||||
ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
|
||||
#if 0
|
||||
ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
|
||||
ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
|
||||
ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
|
||||
ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
|
||||
#endif
|
||||
|
||||
status = ads_gen_mod(ads_s, new_dn, mods);
|
||||
|
||||
done:
|
||||
ads_msgfree(ads_s, res);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
join a domain using ADS (LDAP mods)
|
||||
********************************************************************/
|
||||
|
||||
int net_ads_join(int argc, const char **argv)
|
||||
{
|
||||
ADS_STRUCT *ads, *ads_s;
|
||||
ADS_STATUS status;
|
||||
char *machine_account = NULL;
|
||||
const char *short_domain_name = NULL;
|
||||
char *tmp_password, *password;
|
||||
struct cldap_netlogon_reply cldap_reply;
|
||||
TALLOC_CTX *ctx;
|
||||
DOM_SID *domain_sid = NULL;
|
||||
|
||||
if ( check_ads_config() != 0 ) {
|
||||
d_fprintf(stderr, "Invalid configuration. Exiting....\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(ads = ads_init(lp_realm(), NULL, NULL ))) {
|
||||
return -1;
|
||||
}
|
||||
ads->auth.flags = ADS_AUTH_NO_BIND;
|
||||
status = ads_connect(ads);
|
||||
|
||||
if (strcmp(ads->config.realm, lp_realm()) != 0) {
|
||||
d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf (%s) DO NOT match. Aborting join\n", ads->config.realm, lp_realm());
|
||||
d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
|
||||
"(%s) DO NOT match. Aborting join\n", ads->config.realm,
|
||||
lp_realm());
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ou_str = ads_ou_string(ads,org_unit);
|
||||
asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
|
||||
free(ou_str);
|
||||
|
||||
rc = ads_search_dn(ads, &res, dn, NULL);
|
||||
ads_msgfree(ads, res);
|
||||
|
||||
if (rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) {
|
||||
d_fprintf(stderr, "ads_join_realm: organizational unit %s does not exist (dn:%s)\n",
|
||||
org_unit, dn);
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
free(dn);
|
||||
|
||||
if (!ADS_ERR_OK(rc)) {
|
||||
d_fprintf(stderr, "ads_join_realm: %s\n", ads_errstr(rc));
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ads_join_realm(ads, global_myname(), account_type, org_unit);
|
||||
if (!ADS_ERR_OK(rc)) {
|
||||
d_fprintf(stderr, "ads_join_realm: %s\n", ads_errstr(rc));
|
||||
ads_destroy(&ads);
|
||||
if (!(ctx = talloc_init("net_join_domain"))) {
|
||||
DEBUG(0, ("Could not initialise talloc context\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ads_domain_sid(ads, &dom_sid);
|
||||
if (!ADS_ERR_OK(rc)) {
|
||||
d_fprintf(stderr, "ads_domain_sid: %s\n", ads_errstr(rc));
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
/* Do the domain join here */
|
||||
|
||||
if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
|
||||
d_fprintf(stderr, "asprintf failed\n");
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ads_set_machine_password(ads, machine_account, password);
|
||||
if (!ADS_ERR_OK(rc)) {
|
||||
d_fprintf(stderr, "ads_set_machine_password: %s\n", ads_errstr(rc));
|
||||
ads_destroy(&ads);
|
||||
tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
|
||||
password = talloc_strdup(ctx, tmp_password);
|
||||
|
||||
if ( net_join_domain( ctx, ads->config.ldap_server_name, &ads->ldap_ip, &domain_sid, password ) != 0 ) {
|
||||
d_fprintf(stderr, "Failed to join domain!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* make sure we get the right workgroup */
|
||||
/* Check the short name of the domain */
|
||||
|
||||
if ( !(ctx = talloc_init("net ads join")) ) {
|
||||
d_fprintf(stderr, "talloc_init() failed!\n");
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
ZERO_STRUCT( cldap_reply );
|
||||
|
||||
rc = ads_workgroup_name(ads, ctx, &short_domain_name);
|
||||
if ( ADS_ERR_OK(rc) ) {
|
||||
if ( ads_cldap_netlogon( ads->config.ldap_server_name,
|
||||
ads->server.realm, &cldap_reply ) )
|
||||
{
|
||||
short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
|
||||
if ( !strequal(lp_workgroup(), short_domain_name) ) {
|
||||
d_printf("The workgroup in smb.conf does not match the short\n");
|
||||
d_printf("domain name obtained from the server.\n");
|
||||
@ -836,25 +1233,66 @@ int net_ads_join(int argc, const char **argv)
|
||||
}
|
||||
|
||||
d_printf("Using short domain name -- %s\n", short_domain_name);
|
||||
|
||||
/* HACK ALRET! Store the sid and password under bother the lp_workgroup()
|
||||
|
||||
/* HACK ALERT! Store the sid and password under both the lp_workgroup()
|
||||
value from smb.conf and the string returned from the server. The former is
|
||||
neede to bootstrap winbindd's first connection to the DC to get the real
|
||||
short domain name --jerry */
|
||||
|
||||
if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) {
|
||||
DEBUG(1,("Failed to save domain sid\n"));
|
||||
|
||||
if ( (store_domain_account( lp_workgroup(), domain_sid, password ) == -1)
|
||||
|| (store_domain_account( short_domain_name, domain_sid, password ) == -1) )
|
||||
{
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!secrets_store_machine_password(password, lp_workgroup(), sec_channel_type)) {
|
||||
DEBUG(1,("Failed to save machine password\n"));
|
||||
/* Verify that everything is ok */
|
||||
|
||||
if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
|
||||
d_fprintf(stderr, "Failed to verify membership in domain!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* From here on out, use the machine account. But first delete any
|
||||
existing tickets based on the user's creds. */
|
||||
|
||||
ads_kdestroy( NULL );
|
||||
|
||||
status = ADS_ERROR(LDAP_SERVER_DOWN);
|
||||
ads_s = ads_init( ads->server.realm, ads->server.workgroup, ads->server.ldap_server );
|
||||
|
||||
if ( ads_s ) {
|
||||
asprintf( &ads_s->auth.user_name, "%s$", global_myname() );
|
||||
ads_s->auth.password = secrets_fetch_machine_password( short_domain_name, NULL, NULL );
|
||||
ads_s->auth.realm = SMB_STRDUP( lp_realm() );
|
||||
ads_kinit_password( ads_s );
|
||||
status = ads_connect( ads_s );
|
||||
}
|
||||
if ( !ADS_ERR_OK(status) ) {
|
||||
d_fprintf( stderr, "LDAP bind using machine credentials failed!\n");
|
||||
d_fprintf(stderr, "Only NTLM authentication will be possible.\n");
|
||||
} else {
|
||||
/* create the dNSHostName & servicePrincipalName values */
|
||||
|
||||
status = net_set_machine_spn( ctx, ads_s );
|
||||
if ( !ADS_ERR_OK(status) ) {
|
||||
d_fprintf(stderr, "Failed to set servicePrincipalNames.\n");
|
||||
d_fprintf(stderr, "Only NTLM authentication will be possible.\n");
|
||||
|
||||
/* don't fail */
|
||||
}
|
||||
}
|
||||
|
||||
ads_destroy( &ads_s );
|
||||
|
||||
|
||||
#if defined(HAVE_KRB5)
|
||||
if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
|
||||
d_fprintf(stderr, "asprintf failed\n");
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
if (!kerberos_derive_salting_principal(machine_account)) {
|
||||
DEBUG(1,("Failed to determine salting principal\n"));
|
||||
ads_destroy(&ads);
|
||||
@ -866,36 +1304,25 @@ int net_ads_join(int argc, const char **argv)
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!secrets_store_domain_sid(short_domain_name, &dom_sid)) {
|
||||
DEBUG(1,("Failed to save domain sid\n"));
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!secrets_store_machine_password(password, short_domain_name, sec_channel_type)) {
|
||||
DEBUG(1,("Failed to save machine password\n"));
|
||||
ads_destroy(&ads);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Now build the keytab, using the same ADS connection */
|
||||
if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
|
||||
DEBUG(1,("Error creating host keytab!\n"));
|
||||
}
|
||||
#endif
|
||||
|
||||
d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
|
||||
|
||||
SAFE_FREE(password);
|
||||
SAFE_FREE(machine_account);
|
||||
if ( ctx ) {
|
||||
talloc_destroy(ctx);
|
||||
}
|
||||
TALLOC_FREE( ctx );
|
||||
ads_destroy(&ads);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
********************************************************************/
|
||||
|
||||
int net_ads_printer_usage(int argc, const char **argv)
|
||||
{
|
||||
d_printf(
|
||||
@ -913,6 +1340,9 @@ int net_ads_printer_usage(int argc, const char **argv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
********************************************************************/
|
||||
|
||||
static int net_ads_printer_search(int argc, const char **argv)
|
||||
{
|
||||
ADS_STRUCT *ads;
|
||||
@ -1549,6 +1979,7 @@ int net_ads_help(int argc, const char **argv)
|
||||
#if 0
|
||||
{"INFO", net_ads_info},
|
||||
{"JOIN", net_ads_join},
|
||||
{"JOIN2", net_ads_join2},
|
||||
{"LEAVE", net_ads_leave},
|
||||
{"STATUS", net_ads_status},
|
||||
{"PASSWORD", net_ads_password},
|
||||
|
@ -6197,7 +6197,7 @@ BOOL net_rpc_check(unsigned flags)
|
||||
char *server_name = NULL;
|
||||
|
||||
/* flags (i.e. server type) may depend on command */
|
||||
if (!net_find_server(flags, &server_ip, &server_name))
|
||||
if (!net_find_server(NULL, flags, &server_ip, &server_name))
|
||||
return False;
|
||||
|
||||
ZERO_STRUCT(cli);
|
||||
|
@ -41,7 +41,7 @@
|
||||
* @return A shell status integer (0 for success)
|
||||
*
|
||||
**/
|
||||
static int net_rpc_join_ok(const char *domain)
|
||||
int net_rpc_join_ok(const char *domain, const char *server, struct in_addr *ip )
|
||||
{
|
||||
uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS|NETLOGON_NEG_SCHANNEL;
|
||||
struct cli_state *cli = NULL;
|
||||
@ -50,7 +50,7 @@ static int net_rpc_join_ok(const char *domain)
|
||||
NTSTATUS ntret = NT_STATUS_UNSUCCESSFUL;
|
||||
|
||||
/* Connect to remote machine */
|
||||
if (!(cli = net_make_ipc_connection(NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC))) {
|
||||
if (!(cli = net_make_ipc_connection_ex(domain, server, ip, (NET_FLAGS_ANONYMOUS|NET_FLAGS_PDC)))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -402,7 +402,7 @@ int net_rpc_join_newstyle(int argc, const char **argv)
|
||||
}
|
||||
|
||||
/* double-check, connection from scratch */
|
||||
retval = net_rpc_join_ok(domain);
|
||||
retval = net_rpc_join_ok(domain, cli->desthost, &cli->dest_ip);
|
||||
|
||||
done:
|
||||
|
||||
@ -434,7 +434,7 @@ int net_rpc_testjoin(int argc, const char **argv)
|
||||
char *domain = smb_xstrdup(opt_target_workgroup);
|
||||
|
||||
/* Display success or failure */
|
||||
if (net_rpc_join_ok(domain) != 0) {
|
||||
if (net_rpc_join_ok(domain, NULL, NULL) != 0) {
|
||||
fprintf(stderr,"Join to domain '%s' is not valid\n",domain);
|
||||
free(domain);
|
||||
return -1;
|
||||
|
Loading…
Reference in New Issue
Block a user