From: Gerald W. Carter Date: Fri, 27 Jun 2008 14:22:39 +0000 (-0400) Subject: libads: Add API call to connect to a global catalog server. X-Git-Tag: samba-4.0.0alpha6~801^2~862 X-Git-Url: http://git.samba.org/samba.git/?p=nivanova%2Fsamba-autobuild%2F.git;a=commitdiff_plain;h=9ff1ffcbee46257c3b2b13b84c2a539322493190 libads: Add API call to connect to a global catalog server. Extends ads_connect() to a new call ads_connect_gc() which connects on port 3268 rather than port 389. Also makes ads_try_connect() static and only used internally to ldap.c (This used to be commit f4c37dbe2c986fb7bfe510cdff3b4a9fbc06d079) --- diff --git a/source3/include/ads.h b/source3/include/ads.h index d4551765aa0..0d464b2d810 100644 --- a/source3/include/ads.h +++ b/source3/include/ads.h @@ -53,7 +53,9 @@ typedef struct ads_struct { char *realm; char *workgroup; char *ldap_server; - int foreign; /* set to 1 if connecting to a foreign realm */ + int foreign; /* set to 1 if connecting to a foreign + * realm */ + bool gc; /* Is this a global catalog server? */ } server; /* info needed to authenticate */ diff --git a/source3/include/proto.h b/source3/include/proto.h index 6cef3488939..81cf2dbf4f6 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -2063,7 +2063,6 @@ ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads, bool ads_sitename_match(ADS_STRUCT *ads); bool ads_closest_dc(ADS_STRUCT *ads); -bool ads_try_connect(ADS_STRUCT *ads, const char *server ); ADS_STATUS ads_connect(ADS_STRUCT *ads); ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads); void ads_disconnect(ADS_STRUCT *ads); diff --git a/source3/include/smb.h b/source3/include/smb.h index 7ae66f1cedb..7fcae51aa63 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -1847,6 +1847,7 @@ typedef struct _smb_iconv_t { #ifndef LDAP_PORT #define LDAP_PORT 389 #endif +#define LDAP_GC_PORT 3268 /* used by the IP comparison function */ struct ip_service { diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c index 7c64082ab46..b59dab1f135 100644 --- a/source3/libads/ldap.c +++ b/source3/libads/ldap.c @@ -173,7 +173,7 @@ bool ads_closest_dc(ADS_STRUCT *ads) try a connection to a given ldap server, returning True and setting the servers IP in the ads struct if successful */ -bool ads_try_connect(ADS_STRUCT *ads, const char *server ) +static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc) { char *srv; struct nbt_cldap_netlogon_5 cldap_reply; @@ -238,7 +238,7 @@ bool ads_try_connect(ADS_STRUCT *ads, const char *server ) } ads->server.workgroup = SMB_STRDUP(cldap_reply.domain); - ads->ldap.port = LDAP_PORT; + ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT; if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) { DEBUG(1,("ads_try_connect: unable to convert %s " "to an address\n", @@ -358,7 +358,7 @@ static NTSTATUS ads_find_dc(ADS_STRUCT *ads) } } - if ( ads_try_connect(ads, server) ) { + if ( ads_try_connect(ads, server, false) ) { SAFE_FREE(ip_list); SAFE_FREE(sitename); return NT_STATUS_OK; @@ -385,6 +385,141 @@ static NTSTATUS ads_find_dc(ADS_STRUCT *ads) return NT_STATUS_NO_LOGON_SERVERS; } +/********************************************************************* + *********************************************************************/ + +static NTSTATUS ads_lookup_site(void) +{ + ADS_STRUCT *ads = NULL; + ADS_STATUS ads_status; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + struct nbt_cldap_netlogon_5 cldap_reply; + + ZERO_STRUCT(cldap_reply); + + ads = ads_init(lp_realm(), NULL, NULL); + if (!ads) { + return NT_STATUS_NO_MEMORY; + } + + /* The NO_BIND here will find a DC and set the client site + but not establish the TCP connection */ + + ads->auth.flags = ADS_AUTH_NO_BIND; + ads_status = ads_connect(ads); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n", + ads_errstr(ads_status))); + } + nt_status = ads_ntstatus(ads_status); + + if (ads) { + ads_destroy(&ads); + } + + return nt_status; +} + +/********************************************************************* + *********************************************************************/ + +static const char* host_dns_domain(const char *fqdn) +{ + const char *p = fqdn; + + /* go to next char following '.' */ + + if ((p = strchr_m(fqdn, '.')) != NULL) { + p++; + } + + return p; +} + + +/** + * Connect to the Global Catalog server + * @param ads Pointer to an existing ADS_STRUCT + * @return status of connection + * + * Simple wrapper around ads_connect() that fills in the + * GC ldap server information + **/ + +ADS_STATUS ads_connect_gc(ADS_STRUCT *ads) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct dns_rr_srv *gcs_list; + int num_gcs; + char *realm = ads->server.realm; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); + int i; + bool done = false; + char *sitename = NULL; + + if (!realm) + realm = lp_realm(); + + if ((sitename = sitename_fetch(realm)) == NULL) { + ads_lookup_site(); + sitename = sitename_fetch(realm); + } + + do { + /* We try once with a sitename and once without + (unless we don't have a sitename and then we're + done */ + + if (sitename == NULL) + done = true; + + nt_status = ads_dns_query_gcs(frame, realm, sitename, + &gcs_list, &num_gcs); + + SAFE_FREE(sitename); + + if (!NT_STATUS_IS_OK(nt_status)) { + ads_status = ADS_ERROR_NT(nt_status); + goto done; + } + + /* Loop until we get a successful connection or have gone + through them all. When connecting a GC server, make sure that + the realm is the server's DNS name and not the forest root */ + + for (i=0; iserver.gc = true; + ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname); + ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server)); + ads_status = ads_connect(ads); + if (ADS_ERR_OK(ads_status)) { + /* Reset the bind_dn to "". A Global Catalog server + may host multiple domain trees in a forest. + Windows 2003 GC server will accept "" as the search + path to imply search all domain trees in the forest */ + + SAFE_FREE(ads->config.bind_path); + ads->config.bind_path = SMB_STRDUP(""); + + + goto done; + } + SAFE_FREE(ads->server.ldap_server); + SAFE_FREE(ads->server.realm); + } + + TALLOC_FREE(gcs_list); + num_gcs = 0; + } while (!done); + +done: + SAFE_FREE(sitename); + talloc_destroy(frame); + + return ads_status; +} + /** * Connect to the LDAP server @@ -412,7 +547,7 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads) } if (ads->server.ldap_server && - ads_try_connect(ads, ads->server.ldap_server)) { + ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) { goto got_connection; } @@ -472,7 +607,7 @@ got_connection: /* Otherwise setup the TCP LDAP session */ ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name, - LDAP_PORT, lp_ldap_timeout()); + ads->ldap.port, lp_ldap_timeout()); if (ads->ldap.ld == NULL) { status = ADS_ERROR(LDAP_OPERATIONS_ERROR); goto out; diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c index 1f1544ea0d5..51ef14b708b 100644 --- a/source3/winbindd/winbindd_cm.c +++ b/source3/winbindd/winbindd_cm.c @@ -1043,14 +1043,16 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx, if (lp_security() == SEC_ADS) { ADS_STRUCT *ads; + ADS_STATUS ads_status; char addr[INET6_ADDRSTRLEN]; print_sockaddr(addr, sizeof(addr), pss); - ads = ads_init(domain->alt_name, domain->name, NULL); + ads = ads_init(domain->alt_name, domain->name, addr); ads->auth.flags |= ADS_AUTH_NO_BIND; - if (ads_try_connect(ads, addr)) { + ads_status = ads_connect(ads); + if (ADS_ERR_OK(ads_status)) { /* We got a cldap packet. */ fstrcpy(name, ads->config.ldap_server_name); namecache_store(name, 0x20, 1, &ip_list);