From 2c029a8b96ae476f1d5c2abe14ee25f98a1513d8 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Fri, 12 May 2006 15:17:35 +0000 Subject: 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 used to be commit 4c4ea7b20f44cd200cef8c7b389d51b72eccc39b) --- source3/Makefile.in | 4 +- source3/include/ads.h | 15 - source3/include/ads_cldap.h | 58 ++++ source3/include/includes.h | 34 +-- source3/libads/ads_struct.c | 6 +- source3/libads/cldap.c | 278 +++++++++++++++++ source3/libads/ldap.c | 363 ++++++++++------------ source3/libsmb/namequery.c | 97 +++--- source3/nsswitch/winbindd_cm.c | 9 +- source3/printing/nt_printing.c | 2 +- source3/sam/idmap_ad.c | 38 +-- source3/utils/net.c | 53 +++- source3/utils/net.h | 27 +- source3/utils/net_ads.c | 669 +++++++++++++++++++++++++++++++++-------- source3/utils/net_ads_cldap.c | 365 ---------------------- source3/utils/net_rpc.c | 2 +- source3/utils/net_rpc_join.c | 8 +- 17 files changed, 1161 insertions(+), 867 deletions(-) create mode 100644 source3/include/ads_cldap.h create mode 100644 source3/libads/cldap.c delete mode 100644 source3/utils/net_ads_cldap.c (limited to 'source3') diff --git a/source3/Makefile.in b/source3/Makefile.in index 32c8429cdf..2d0ac9ab7a 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -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 \ diff --git a/source3/include/ads.h b/source3/include/ads.h index f29c94bb90..48b9bbffa0 100644 --- a/source3/include/ads.h +++ b/source3/include/ads.h @@ -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 */ diff --git a/source3/include/ads_cldap.h b/source3/include/ads_cldap.h new file mode 100644 index 0000000000..65feb072e0 --- /dev/null +++ b/source3/include/ads_cldap.h @@ -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 */ + + diff --git a/source3/include/includes.h b/source3/include/includes.h index 998a0715e2..a13cffea14 100644 --- a/source3/include/includes.h +++ b/source3/include/includes.h @@ -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" /* diff --git a/source3/libads/ads_struct.c b/source3/libads/ads_struct.c index 9b2179ad31..48533c7ffb 100644 --- a/source3/libads/ads_struct.c +++ b/source3/libads/ads_struct.c @@ -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); diff --git a/source3/libads/cldap.c b/source3/libads/cldap.c new file mode 100644 index 0000000000..6a62f573c9 --- /dev/null +++ b/source3/libads/cldap.c @@ -0,0 +1,278 @@ +/* + 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. +*/ + +#include "includes.h" + +/* + These seem to be strings as described in RFC1035 4.1.4 and can be: + + - a sequence of labels ending in a zero octet + - a pointer + - a sequence of labels ending with a pointer + + A label is a byte where the first two bits must be zero and the remaining + bits represent the length of the label followed by the label itself. + Therefore, the length of a label is at max 64 bytes. Under RFC1035, a + sequence of labels cannot exceed 255 bytes. + + A pointer consists of a 14 bit offset from the beginning of the data. + + struct ptr { + unsigned ident:2; // must be 11 + unsigned offset:14; // from the beginning of data + }; + + This is used as a method to compress the packet by eliminated duplicate + domain components. Since a UDP packet should probably be < 512 bytes and a + DNS name can be up to 255 bytes, this actually makes a lot of sense. +*/ +static unsigned pull_netlogon_string(char *ret, const char *ptr, + const char *data) +{ + char *pret = ret; + int followed_ptr = 0; + unsigned ret_len = 0; + + memset(pret, 0, MAX_DNS_LABEL); + do { + if ((*ptr & 0xc0) == 0xc0) { + uint16 len; + + if (!followed_ptr) { + ret_len += 2; + followed_ptr = 1; + } + len = ((ptr[0] & 0x3f) << 8) | ptr[1]; + ptr = data + len; + } else if (*ptr) { + uint8 len = (uint8)*(ptr++); + + if ((pret - ret + len + 1) >= MAX_DNS_LABEL) { + d_fprintf(stderr, "DC returning too long DNS name\n"); + return 0; + } + + if (pret != ret) { + *pret = '.'; + pret++; + } + memcpy(pret, ptr, len); + pret += len; + ptr += len; + + if (!followed_ptr) { + ret_len += (len + 1); + } + } + } while (*ptr); + + return followed_ptr ? ret_len : ret_len + 1; +} + +/* + do a cldap netlogon query +*/ +static int send_cldap_netlogon(int sock, const char *domain, + const char *hostname, unsigned ntversion) +{ + ASN1_DATA data; + char ntver[4]; +#ifdef CLDAP_USER_QUERY + char aac[4]; + + SIVAL(aac, 0, 0x00000180); +#endif + SIVAL(ntver, 0, ntversion); + + memset(&data, 0, sizeof(data)); + + asn1_push_tag(&data,ASN1_SEQUENCE(0)); + asn1_write_Integer(&data, 4); + asn1_push_tag(&data, ASN1_APPLICATION(3)); + asn1_write_OctetString(&data, NULL, 0); + asn1_write_enumerated(&data, 0); + asn1_write_enumerated(&data, 0); + asn1_write_Integer(&data, 0); + asn1_write_Integer(&data, 0); + asn1_write_BOOLEAN2(&data, False); + asn1_push_tag(&data, ASN1_CONTEXT(0)); + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "DnsDomain", 9); + asn1_write_OctetString(&data, domain, strlen(domain)); + asn1_pop_tag(&data); + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "Host", 4); + asn1_write_OctetString(&data, hostname, strlen(hostname)); + asn1_pop_tag(&data); + +#ifdef CLDAP_USER_QUERY + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "User", 4); + asn1_write_OctetString(&data, "SAMBA$", 6); + asn1_pop_tag(&data); + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "AAC", 4); + asn1_write_OctetString(&data, aac, 4); + asn1_pop_tag(&data); +#endif + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "NtVer", 5); + asn1_write_OctetString(&data, ntver, 4); + asn1_pop_tag(&data); + + asn1_pop_tag(&data); + + asn1_push_tag(&data,ASN1_SEQUENCE(0)); + asn1_write_OctetString(&data, "NetLogon", 8); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + if (data.has_error) { + d_fprintf(stderr, "Failed to build cldap netlogon at offset %d\n", (int)data.ofs); + asn1_free(&data); + return -1; + } + + if (write(sock, data.data, data.length) != (ssize_t)data.length) { + d_fprintf(stderr, "failed to send cldap query (%s)\n", strerror(errno)); + } + + asn1_free(&data); + + return 0; +} + + +/* + receive a cldap netlogon reply +*/ +static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply) +{ + int ret; + ASN1_DATA data; + DATA_BLOB blob; + DATA_BLOB os1, os2, os3; + int i1; + char *p; + + blob = data_blob(NULL, 8192); + + ret = read(sock, blob.data, blob.length); + + if (ret <= 0) { + d_fprintf(stderr, "no reply received to cldap netlogon\n"); + return -1; + } + blob.length = ret; + + asn1_load(&data, blob); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_read_Integer(&data, &i1); + asn1_start_tag(&data, ASN1_APPLICATION(4)); + asn1_read_OctetString(&data, &os1); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_read_OctetString(&data, &os2); + asn1_start_tag(&data, ASN1_SET); + asn1_read_OctetString(&data, &os3); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + + if (data.has_error) { + d_fprintf(stderr, "Failed to parse cldap reply\n"); + return -1; + } + + p = (char *)os3.data; + + reply->type = IVAL(p, 0); p += 4; + reply->flags = IVAL(p, 0); p += 4; + + memcpy(&reply->guid.info, p, UUID_FLAT_SIZE); + p += UUID_FLAT_SIZE; + + p += pull_netlogon_string(reply->forest, p, (const char *)os3.data); + p += pull_netlogon_string(reply->domain, p, (const char *)os3.data); + p += pull_netlogon_string(reply->hostname, p, (const char *)os3.data); + p += pull_netlogon_string(reply->netbios_domain, p, (const char *)os3.data); + p += pull_netlogon_string(reply->netbios_hostname, p, (const char *)os3.data); + p += pull_netlogon_string(reply->unk, p, (const char *)os3.data); + + if (reply->type == SAMLOGON_AD_R) { + p += pull_netlogon_string(reply->user_name, p, (const char *)os3.data); + } else { + *reply->user_name = 0; + } + + p += pull_netlogon_string(reply->site_name, p, (const char *)os3.data); + p += pull_netlogon_string(reply->site_name_2, p, (const char *)os3.data); + + reply->version = IVAL(p, 0); + reply->lmnt_token = SVAL(p, 4); + reply->lm20_token = SVAL(p, 6); + + data_blob_free(&os1); + data_blob_free(&os2); + data_blob_free(&os3); + data_blob_free(&blob); + + return 0; +} + +/******************************************************************* + 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; + + sock = open_udp_socket(server, LDAP_PORT ); + if (sock == -1) { + DEBUG(2,("ads_cldap_netlogon: Failed to open udp socket to %s\n", + server)); + return False; + } + + ret = send_cldap_netlogon(sock, realm, global_myname(), 6); + if (ret != 0) { + return False; + } + ret = recv_cldap_netlogon(sock, reply); + close(sock); + + if (ret == -1) { + return False; + } + + return True; +} + + diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c index a8877b5697..6f698dc3a9 100644 --- a/source3/libads/ldap.c +++ b/source3/libads/ldap.c @@ -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; ilast_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,97 +2518,94 @@ 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); - if (!ADS_ERR_OK(status)) { - talloc_destroy(ctx); - return status; - } + /* establish a new ldap tcp session if necessary */ - value = ads_pull_string(ads, ctx, res, "ldapServiceName"); - if (!value) { - ads_msgfree(ads, res); - talloc_destroy(ctx); - return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); + 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; } - timestr = ads_pull_string(ads, ctx, res, "currentTime"); - if (!timestr) { - ads_msgfree(ads, res); + status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res); + if (!ADS_ERR_OK(status)) { talloc_destroy(ctx); - return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); + goto done; } - schema_path = ads_pull_string(ads, ctx, res, "schemaNamingContext"); - if (!schema_path) { + 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; } - SAFE_FREE(ads->config.schema_path); - ads->config.schema_path = SMB_STRDUP(schema_path); - - ads_msgfree(ads, res); + /* but save the time and offset in the original ADS_STRUCT */ + + ads->config.current_time = ads_parse_time(timestr); - 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); + if (ads->config.current_time != 0) { + ads->auth.time_offset = ads->config.current_time - time(NULL); + DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset)); } - SAFE_FREE(ads->config.ldap_server_name); + status = ADS_SUCCESS; - 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); +done: + /* free any temporary ads connections */ + if ( ads_s != ads ) { + ads_destroy( &ads_s ); } + talloc_destroy(ctx); - *p = 0; + return status; +} - 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); +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 }; - 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)); + status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res); + if (!ADS_ERR_OK(status)) { + return status; + } - ads->config.current_time = ads_parse_time(timestr); + if ( (schema = ads_pull_string(ads, mem_ctx, res, "schemaNamingContext")) == NULL ) { + return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); + } - if (ads->config.current_time != 0) { - ads->auth.time_offset = ads->config.current_time - time(NULL); - DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset)); + if ( (*schema_path = talloc_strdup(mem_ctx, schema)) == NULL ) { + return ADS_ERROR(LDAP_NO_MEMORY); } - talloc_destroy(ctx); + ads_msgfree(ads, res); - return ADS_SUCCESS; + 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; + + if ( (ctx = talloc_init("ads_check_sfu_mapping")) == NULL ) { + goto done; + } + + /* establish a new ldap tcp session if necessary */ - ctx = talloc_init("ads_check_sfu_mapping"); - if (ctx == NULL) + 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, ctx, ADS_ATTR_SFU_GIDNUMBER_OID); + 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 diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c index 99a2e7ebdb..1033a375c5 100644 --- a/source3/libsmb/namequery.c +++ b/source3/libsmb/namequery.c @@ -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. 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; - } + 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; - } + 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; - } + 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 ) { + 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; + /* 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; + (*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 */ + /* 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; + if ( is_zero_ip((*return_iplist)[i].ip) ) + continue; - i++; - } + i++; + } - TALLOC_FREE( dcs ); + TALLOC_FREE( dcs ); - *return_count = i; + *return_count = i; - return True; - } else -#endif /* HAVE_ADS */ - { - return False; - } + 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. if we - are using 'security = ads' */ + SRV record lookup */ if (resolve_ads(name, name_type, return_iplist, return_count)) { result = True; goto done; diff --git a/source3/nsswitch/winbindd_cm.c b/source3/nsswitch/winbindd_cm.c index 9e450d9de7..6322a33c02 100644 --- a/source3/nsswitch/winbindd_cm.c +++ b/source3/nsswitch/winbindd_cm.c @@ -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; } diff --git a/source3/printing/nt_printing.c b/source3/printing/nt_printing.c index 54d7e8040c..b0bac86dad 100644 --- a/source3/printing/nt_printing.c +++ b/source3/printing/nt_printing.c @@ -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; diff --git a/source3/sam/idmap_ad.c b/source3/sam/idmap_ad.c index f9a959e7ec..0803f2a7ab 100644 --- a/source3/sam/idmap_ad.c +++ b/source3/sam/idmap_ad.c @@ -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)) { diff --git a/source3/utils/net.c b/source3/utils/net.c index 1389885ba1..4c7a6280f4 100644 --- a/source3/utils/net.c +++ b/source3/utils/net.c @@ -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) { diff --git a/source3/utils/net.h b/source3/utils/net.h index fc3167012d..b1af230a65 100644 --- a/source3/utils/net.h +++ b/source3/utils/net.h @@ -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 diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c index f00bf0e527..a514b6c4e6 100644 --- a/source3/utils/net_ads.c +++ b/source3/utils/net_ads.c @@ -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); - return -1; + ads = ads_init(realm, opt_target_workgroup, opt_host); + if (ads) { + ads->auth.flags |= ADS_AUTH_NO_BIND; } - 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); + 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; } - d_printf("Workgroup: %s\n", workgroup); + d_printf("Workgroup: %s\n", reply.netbios_domain); - 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; } - 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()); - ads_destroy(&ads); - 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; } - ou_str = ads_ou_string(ads,org_unit); - asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path); - free(ou_str); + status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol); + if ( !NT_STATUS_IS_OK(status) ) + return status; - rc = ads_search_dn(ads, &res, dn, NULL); - ads_msgfree(ads, res); + status = rpccli_lsa_query_info_policy(pipe_hnd, mem_ctx, + &lsa_pol, 5, &domain, sid); + if ( !NT_STATUS_IS_OK(status) ) + return status; - 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; + 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; } - free(dn); + + return NT_STATUS_OK; +} - if (!ADS_ERR_OK(rc)) { - d_fprintf(stderr, "ads_join_realm: %s\n", ads_errstr(rc)); - ads_destroy(&ads); - return -1; - } +/******************************************************************* + 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; + } - 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); + 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; } - 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); + if (!(ads = ads_init(lp_realm(), NULL, NULL ))) { return -1; } + ads->auth.flags = ADS_AUTH_NO_BIND; + status = ads_connect(ads); - if (asprintf(&machine_account, "%s$", global_myname()) == -1) { - d_fprintf(stderr, "asprintf failed\n"); + 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()); 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); + + if (!(ctx = talloc_init("net_join_domain"))) { + DEBUG(0, ("Could not initialise talloc context\n")); return -1; } + + /* Do the domain join here */ + + tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH); + password = talloc_strdup(ctx, tmp_password); - /* make sure we get the right workgroup */ - - if ( !(ctx = talloc_init("net ads join")) ) { - d_fprintf(stderr, "talloc_init() failed!\n"); - ads_destroy(&ads); + 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; } - rc = ads_workgroup_name(ads, ctx, &short_domain_name); - if ( ADS_ERR_OK(rc) ) { + /* Check the short name of the domain */ + + ZERO_STRUCT( cldap_reply ); + + 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,46 +1233,74 @@ 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")); - ads_destroy(&ads); - return -1; - } + /* Verify that everything is ok */ -#ifdef HAVE_KRB5 - if (!kerberos_derive_salting_principal(machine_account)) { - DEBUG(1,("Failed to determine salting principal\n")); - ads_destroy(&ads); + 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 (!kerberos_derive_cifs_salting_principals()) { - DEBUG(1,("Failed to determine salting principals\n")); +#if defined(HAVE_KRB5) + if (asprintf(&machine_account, "%s$", global_myname()) == -1) { + d_fprintf(stderr, "asprintf failed\n"); ads_destroy(&ads); return -1; } -#endif - if (!secrets_store_domain_sid(short_domain_name, &dom_sid)) { - DEBUG(1,("Failed to save domain sid\n")); + if (!kerberos_derive_salting_principal(machine_account)) { + DEBUG(1,("Failed to determine salting principal\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")); + if (!kerberos_derive_cifs_salting_principals()) { + DEBUG(1,("Failed to determine salting principals\n")); ads_destroy(&ads); return -1; } @@ -884,18 +1309,20 @@ int net_ads_join(int argc, const char **argv) 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}, diff --git a/source3/utils/net_ads_cldap.c b/source3/utils/net_ads_cldap.c deleted file mode 100644 index 2e7a28b322..0000000000 --- a/source3/utils/net_ads_cldap.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - 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. -*/ - -#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: - - - a sequence of labels ending in a zero octet - - a pointer - - a sequence of labels ending with a pointer - - A label is a byte where the first two bits must be zero and the remaining - bits represent the length of the label followed by the label itself. - Therefore, the length of a label is at max 64 bytes. Under RFC1035, a - sequence of labels cannot exceed 255 bytes. - - A pointer consists of a 14 bit offset from the beginning of the data. - - struct ptr { - unsigned ident:2; // must be 11 - unsigned offset:14; // from the beginning of data - }; - - This is used as a method to compress the packet by eliminated duplicate - domain components. Since a UDP packet should probably be < 512 bytes and a - DNS name can be up to 255 bytes, this actually makes a lot of sense. -*/ -static unsigned pull_netlogon_string(char *ret, const char *ptr, - const char *data) -{ - char *pret = ret; - int followed_ptr = 0; - unsigned ret_len = 0; - - memset(pret, 0, MAX_DNS_LABEL); - do { - if ((*ptr & 0xc0) == 0xc0) { - uint16 len; - - if (!followed_ptr) { - ret_len += 2; - followed_ptr = 1; - } - len = ((ptr[0] & 0x3f) << 8) | ptr[1]; - ptr = data + len; - } else if (*ptr) { - uint8 len = (uint8)*(ptr++); - - if ((pret - ret + len + 1) >= MAX_DNS_LABEL) { - d_fprintf(stderr, "DC returning too long DNS name\n"); - return 0; - } - - if (pret != ret) { - *pret = '.'; - pret++; - } - memcpy(pret, ptr, len); - pret += len; - ptr += len; - - if (!followed_ptr) { - ret_len += (len + 1); - } - } - } while (*ptr); - - return followed_ptr ? ret_len : ret_len + 1; -} - -/* - do a cldap netlogon query -*/ -static int send_cldap_netlogon(int sock, const char *domain, - const char *hostname, unsigned ntversion) -{ - ASN1_DATA data; - char ntver[4]; -#ifdef CLDAP_USER_QUERY - char aac[4]; - - SIVAL(aac, 0, 0x00000180); -#endif - SIVAL(ntver, 0, ntversion); - - memset(&data, 0, sizeof(data)); - - asn1_push_tag(&data,ASN1_SEQUENCE(0)); - asn1_write_Integer(&data, 4); - asn1_push_tag(&data, ASN1_APPLICATION(3)); - asn1_write_OctetString(&data, NULL, 0); - asn1_write_enumerated(&data, 0); - asn1_write_enumerated(&data, 0); - asn1_write_Integer(&data, 0); - asn1_write_Integer(&data, 0); - asn1_write_BOOLEAN2(&data, False); - asn1_push_tag(&data, ASN1_CONTEXT(0)); - - asn1_push_tag(&data, ASN1_CONTEXT(3)); - asn1_write_OctetString(&data, "DnsDomain", 9); - asn1_write_OctetString(&data, domain, strlen(domain)); - asn1_pop_tag(&data); - - asn1_push_tag(&data, ASN1_CONTEXT(3)); - asn1_write_OctetString(&data, "Host", 4); - asn1_write_OctetString(&data, hostname, strlen(hostname)); - asn1_pop_tag(&data); - -#ifdef CLDAP_USER_QUERY - asn1_push_tag(&data, ASN1_CONTEXT(3)); - asn1_write_OctetString(&data, "User", 4); - asn1_write_OctetString(&data, "SAMBA$", 6); - asn1_pop_tag(&data); - - asn1_push_tag(&data, ASN1_CONTEXT(3)); - asn1_write_OctetString(&data, "AAC", 4); - asn1_write_OctetString(&data, aac, 4); - asn1_pop_tag(&data); -#endif - - asn1_push_tag(&data, ASN1_CONTEXT(3)); - asn1_write_OctetString(&data, "NtVer", 5); - asn1_write_OctetString(&data, ntver, 4); - asn1_pop_tag(&data); - - asn1_pop_tag(&data); - - asn1_push_tag(&data,ASN1_SEQUENCE(0)); - asn1_write_OctetString(&data, "NetLogon", 8); - asn1_pop_tag(&data); - asn1_pop_tag(&data); - asn1_pop_tag(&data); - - if (data.has_error) { - d_fprintf(stderr, "Failed to build cldap netlogon at offset %d\n", (int)data.ofs); - asn1_free(&data); - return -1; - } - - if (write(sock, data.data, data.length) != (ssize_t)data.length) { - d_fprintf(stderr, "failed to send cldap query (%s)\n", strerror(errno)); - } - - asn1_free(&data); - - return 0; -} - - -/* - receive a cldap netlogon reply -*/ -static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply) -{ - int ret; - ASN1_DATA data; - DATA_BLOB blob; - DATA_BLOB os1, os2, os3; - int i1; - char *p; - - blob = data_blob(NULL, 8192); - - ret = read(sock, blob.data, blob.length); - - if (ret <= 0) { - d_fprintf(stderr, "no reply received to cldap netlogon\n"); - return -1; - } - blob.length = ret; - - asn1_load(&data, blob); - asn1_start_tag(&data, ASN1_SEQUENCE(0)); - asn1_read_Integer(&data, &i1); - asn1_start_tag(&data, ASN1_APPLICATION(4)); - asn1_read_OctetString(&data, &os1); - asn1_start_tag(&data, ASN1_SEQUENCE(0)); - asn1_start_tag(&data, ASN1_SEQUENCE(0)); - asn1_read_OctetString(&data, &os2); - asn1_start_tag(&data, ASN1_SET); - asn1_read_OctetString(&data, &os3); - asn1_end_tag(&data); - asn1_end_tag(&data); - asn1_end_tag(&data); - asn1_end_tag(&data); - asn1_end_tag(&data); - - if (data.has_error) { - d_fprintf(stderr, "Failed to parse cldap reply\n"); - return -1; - } - - p = (char *)os3.data; - - reply->type = IVAL(p, 0); p += 4; - reply->flags = IVAL(p, 0); p += 4; - - memcpy(&reply->guid.info, p, UUID_FLAT_SIZE); - p += UUID_FLAT_SIZE; - - p += pull_netlogon_string(reply->forest, p, (const char *)os3.data); - p += pull_netlogon_string(reply->domain, p, (const char *)os3.data); - p += pull_netlogon_string(reply->hostname, p, (const char *)os3.data); - p += pull_netlogon_string(reply->netbios_domain, p, (const char *)os3.data); - p += pull_netlogon_string(reply->netbios_hostname, p, (const char *)os3.data); - p += pull_netlogon_string(reply->unk, p, (const char *)os3.data); - - if (reply->type == SAMLOGON_AD_R) { - p += pull_netlogon_string(reply->user_name, p, (const char *)os3.data); - } else { - *reply->user_name = 0; - } - - p += pull_netlogon_string(reply->site_name, p, (const char *)os3.data); - p += pull_netlogon_string(reply->site_name_2, p, (const char *)os3.data); - - reply->version = IVAL(p, 0); - reply->lmnt_token = SVAL(p, 4); - reply->lm20_token = SVAL(p, 6); - - data_blob_free(&os1); - data_blob_free(&os2); - data_blob_free(&os3); - data_blob_free(&blob); - - return 0; -} - -/* - do a cldap netlogon query -*/ -int ads_cldap_netlogon(ADS_STRUCT *ads) -{ - 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); - 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; - - } - - ret = send_cldap_netlogon(sock, ads->config.realm, global_myname(), 6); - if (ret != 0) { - return ret; - } - ret = recv_cldap_netlogon(sock, &reply); - close(sock); - - if (ret == -1) { - return -1; - } - - 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; -} - - -#endif diff --git a/source3/utils/net_rpc.c b/source3/utils/net_rpc.c index 0c9f411915..c9e4350692 100644 --- a/source3/utils/net_rpc.c +++ b/source3/utils/net_rpc.c @@ -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); diff --git a/source3/utils/net_rpc_join.c b/source3/utils/net_rpc_join.c index d611940e65..2c55b0e946 100644 --- a/source3/utils/net_rpc_join.c +++ b/source3/utils/net_rpc_join.c @@ -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; -- cgit