1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-26 10:04:02 +03:00
samba-mirror/source3/nsswitch/winbindd_ads.c
Andrew Tridgell ab9ff0fa73 This fixes a number of ADS problems, particularly with netbiosless
setups.

- split up the ads structure into logical pieces. This makes it much
  easier to keep things like the authentication realm and the server
  realm separate (they can be different).

- allow ads callers to specify that no sasl bind should be performed
(used by "net ads info" for example)

- fix an error with handing ADS_ERROR_SYSTEM() when errno is 0

- completely rewrote the code for finding the LDAP server. Now try DNS
  methods first, and try all DNS servers returned from the SRV DNS
  query, sorted by closeness to our interfaces (using the same sort code
  as we use in replies from WINS servers). This allows us to cope with
  ADS DCs that are down, and ensures we don't pick one that is on the
  other side of the country unless absolutely necessary.

- recognise dnsRecords as binary when displaying them

- cope with the realm not being configured in smb.conf (work it out
  from the LDAP server)

- look at the trustDirection when looking up trusted domains and don't
  include trusts that trust our domains but we don't trust
  theirs.

- use LDAP to query the alternate (netbios) name for a realm, and make
  sure that both and long and short forms of the name are accepted by
  winbindd. Use the short form by default for listing users/groups.

- rescan the list of trusted domains every 5 minutes in case new trust
  relationships are added while winbindd is running

- include transient trust relationships (ie. C trusts B, B trusts A,
  so C trusts A) in winbindd.

- don't do a gratuituous node status lookup when finding an ADS DC (we
  don't need it and it could fail)

- remove unused sid_to_distinguished_name function

- make sure we find the allternate name of our primary domain when
  operating with a netbiosless ADS DC (using LDAP to do the lookup)

- fixed the rpc trusted domain enumeration to support up to approx
  2000 trusted domains (the old limit was 3)

- use the IP for the remote_machine (%m) macro when the client doesn't
  supply us with a name via a netbios session request (eg. port 445)

- if the client uses SPNEGO then use the machine name from the SPNEGO
  auth packet for remote_machine (%m) macro

- add new 'net ads workgroup' command to find the netbios workgroup
  name for a realm
(This used to be commit e358d7b24c86a46d8c361b9e32a25d4f71a6dc00)
2002-08-05 02:47:46 +00:00

865 lines
20 KiB
C

/*
Unix SMB/CIFS implementation.
Winbind ADS backend functions
Copyright (C) Andrew Tridgell 2001
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 "winbindd.h"
#ifdef HAVE_ADS
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_WINBIND
/* the realm of our primary LDAP server */
static char *primary_realm;
/*
a wrapper around ldap_search_s that retries depending on the error code
this is supposed to catch dropped connections and auto-reconnect
*/
ADS_STATUS ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope,
const char *exp,
const char **attrs, void **res)
{
ADS_STATUS status;
int count = 3;
char *bp;
if (!ads->ld &&
time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) {
return ADS_ERROR(LDAP_SERVER_DOWN);
}
bp = strdup(bind_path);
while (count--) {
status = ads_do_search_all(ads, bp, scope, exp, attrs, res);
if (ADS_ERR_OK(status)) {
DEBUG(5,("Search for %s gave %d replies\n",
exp, ads_count_replies(ads, *res)));
free(bp);
return status;
}
if (*res) ads_msgfree(ads, *res);
*res = NULL;
DEBUG(3,("Reopening ads connection to realm '%s' after error %s\n",
ads->config.realm, ads_errstr(status)));
if (ads->ld) {
ldap_unbind(ads->ld);
}
ads->ld = NULL;
status = ads_connect(ads);
if (!ADS_ERR_OK(status)) {
DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n",
ads_errstr(status)));
ads_destroy(&ads);
free(bp);
return status;
}
}
free(bp);
DEBUG(1,("ads reopen failed after error %s\n", ads_errstr(status)));
return status;
}
ADS_STATUS ads_search_retry(ADS_STRUCT *ads, void **res,
const char *exp,
const char **attrs)
{
return ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
exp, attrs, res);
}
ADS_STATUS ads_search_retry_dn(ADS_STRUCT *ads, void **res,
const char *dn,
const char **attrs)
{
return ads_do_search_retry(ads, dn, LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, res);
}
/*
return our ads connections structure for a domain. We keep the connection
open to make things faster
*/
static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
{
ADS_STRUCT *ads;
ADS_STATUS status;
char *ccache;
if (domain->private) {
return (ADS_STRUCT *)domain->private;
}
/* we don't want this to affect the users ccache */
ccache = lock_path("winbindd_ccache");
SETENV("KRB5CCNAME", ccache, 1);
unlink(ccache);
ads = ads_init(domain->alt_name, domain->name, NULL);
if (!ads) {
DEBUG(1,("ads_init for domain %s failed\n", domain->name));
return NULL;
}
/* the machine acct password might have change - fetch it every time */
SAFE_FREE(ads->auth.password);
ads->auth.password = secrets_fetch_machine_password();
if (primary_realm) {
SAFE_FREE(ads->auth.realm);
ads->auth.realm = strdup(primary_realm);
}
status = ads_connect(ads);
if (!ADS_ERR_OK(status) || !ads->config.realm) {
extern struct winbindd_methods msrpc_methods;
DEBUG(1,("ads_connect for domain %s failed: %s\n",
domain->name, ads_errstr(status)));
ads_destroy(&ads);
/* if we get ECONNREFUSED then it might be a NT4
server, fall back to MSRPC */
if (status.error_type == ADS_ERROR_SYSTEM &&
status.rc == ECONNREFUSED) {
DEBUG(1,("Trying MSRPC methods\n"));
domain->methods = &msrpc_methods;
}
return NULL;
}
/* remember our primary realm for trusted domain support */
if (!primary_realm) {
primary_realm = strdup(ads->config.realm);
}
domain->private = (void *)ads;
return ads;
}
/* useful utility */
static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
{
sid_copy(sid, &domain->sid);
sid_append_rid(sid, rid);
}
/* turn a sAMAccountType into a SID_NAME_USE */
static enum SID_NAME_USE ads_atype_map(uint32 atype)
{
switch (atype & 0xF0000000) {
case ATYPE_GROUP:
return SID_NAME_DOM_GRP;
case ATYPE_USER:
return SID_NAME_USER;
default:
DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
}
return SID_NAME_UNKNOWN;
}
/*
in order to support usernames longer than 21 characters we need to
use both the sAMAccountName and the userPrincipalName attributes
It seems that not all users have the userPrincipalName attribute set
*/
static char *pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
{
char *ret, *p;
ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
if (ret && (p = strchr(ret, '@'))) {
*p = 0;
return ret;
}
return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
}
/* Query display info for a realm. This is the basic user list fn */
static NTSTATUS query_user_list(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 *num_entries,
WINBIND_USERINFO **info)
{
ADS_STRUCT *ads = NULL;
const char *attrs[] = {"userPrincipalName",
"sAMAccountName",
"name", "objectSid", "primaryGroupID",
"sAMAccountType", NULL};
int i, count;
ADS_STATUS rc;
void *res = NULL;
void *msg = NULL;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
*num_entries = 0;
DEBUG(3,("ads: query_user_list\n"));
ads = ads_cached_connection(domain);
if (!ads) goto done;
rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
if (!ADS_ERR_OK(rc)) {
DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
goto done;
}
count = ads_count_replies(ads, res);
if (count == 0) {
DEBUG(1,("query_user_list: No users found\n"));
goto done;
}
(*info) = talloc_zero(mem_ctx, count * sizeof(**info));
if (!*info) {
status = NT_STATUS_NO_MEMORY;
goto done;
}
i = 0;
for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
char *name, *gecos;
DOM_SID sid;
uint32 rid, group;
uint32 atype;
if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
ads_atype_map(atype) != SID_NAME_USER) {
DEBUG(1,("Not a user account? atype=0x%x\n", atype));
continue;
}
name = pull_username(ads, mem_ctx, msg);
gecos = ads_pull_string(ads, mem_ctx, msg, "name");
if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
DEBUG(1,("No sid for %s !?\n", name));
continue;
}
if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
DEBUG(1,("No primary group for %s !?\n", name));
continue;
}
if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
DEBUG(1,("No rid for %s !?\n", name));
continue;
}
(*info)[i].acct_name = name;
(*info)[i].full_name = gecos;
(*info)[i].user_rid = rid;
(*info)[i].group_rid = group;
i++;
}
(*num_entries) = i;
status = NT_STATUS_OK;
DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
done:
if (res) ads_msgfree(ads, res);
return status;
}
/* list all domain groups */
static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 *num_entries,
struct acct_info **info)
{
ADS_STRUCT *ads = NULL;
const char *attrs[] = {"userPrincipalName", "sAMAccountName",
"name", "objectSid",
"sAMAccountType", NULL};
int i, count;
ADS_STATUS rc;
void *res = NULL;
void *msg = NULL;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
*num_entries = 0;
DEBUG(3,("ads: enum_dom_groups\n"));
ads = ads_cached_connection(domain);
if (!ads) goto done;
rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
if (!ADS_ERR_OK(rc)) {
DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
goto done;
}
count = ads_count_replies(ads, res);
if (count == 0) {
DEBUG(1,("enum_dom_groups: No groups found\n"));
goto done;
}
(*info) = talloc_zero(mem_ctx, count * sizeof(**info));
if (!*info) {
status = NT_STATUS_NO_MEMORY;
goto done;
}
i = 0;
for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
char *name, *gecos;
DOM_SID sid;
uint32 rid;
uint32 account_type;
if (!ads_pull_uint32(ads, msg, "sAMAccountType",
&account_type) ||
!(account_type & ATYPE_GROUP)) continue;
name = pull_username(ads, mem_ctx, msg);
gecos = ads_pull_string(ads, mem_ctx, msg, "name");
if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
DEBUG(1,("No sid for %s !?\n", name));
continue;
}
if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
DEBUG(1,("No rid for %s !?\n", name));
continue;
}
fstrcpy((*info)[i].acct_name, name);
fstrcpy((*info)[i].acct_desc, gecos);
(*info)[i].rid = rid;
i++;
}
(*num_entries) = i;
status = NT_STATUS_OK;
DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
done:
if (res) ads_msgfree(ads, res);
return status;
}
/* convert a single name to a sid in a domain */
static NTSTATUS name_to_sid(struct winbindd_domain *domain,
const char *name,
DOM_SID *sid,
enum SID_NAME_USE *type)
{
ADS_STRUCT *ads = NULL;
const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
int count;
ADS_STATUS rc;
void *res = NULL;
char *exp;
uint32 t;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
DEBUG(3,("ads: name_to_sid\n"));
ads = ads_cached_connection(domain);
if (!ads) goto done;
/* accept either the win2000 or the pre-win2000 username */
asprintf(&exp, "(|(sAMAccountName=%s)(userPrincipalName=%s@%s))",
name, name, ads->config.realm);
rc = ads_search_retry(ads, &res, exp, attrs);
free(exp);
if (!ADS_ERR_OK(rc)) {
DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
goto done;
}
count = ads_count_replies(ads, res);
if (count != 1) {
DEBUG(1,("name_to_sid: %s not found\n", name));
goto done;
}
if (!ads_pull_sid(ads, res, "objectSid", sid)) {
DEBUG(1,("No sid for %s !?\n", name));
goto done;
}
if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
DEBUG(1,("No sAMAccountType for %s !?\n", name));
goto done;
}
*type = ads_atype_map(t);
status = NT_STATUS_OK;
DEBUG(3,("ads name_to_sid mapped %s\n", name));
done:
if (res) ads_msgfree(ads, res);
return status;
}
/* convert a sid to a user or group name */
static NTSTATUS sid_to_name(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
DOM_SID *sid,
char **name,
enum SID_NAME_USE *type)
{
ADS_STRUCT *ads = NULL;
const char *attrs[] = {"userPrincipalName",
"sAMAccountName",
"sAMAccountType", NULL};
ADS_STATUS rc;
void *msg = NULL;
char *exp;
char *sidstr;
uint32 atype;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
DEBUG(3,("ads: sid_to_name\n"));
ads = ads_cached_connection(domain);
if (!ads) goto done;
sidstr = sid_binstring(sid);
asprintf(&exp, "(objectSid=%s)", sidstr);
rc = ads_search_retry(ads, &msg, exp, attrs);
free(exp);
free(sidstr);
if (!ADS_ERR_OK(rc)) {
DEBUG(1,("sid_to_name ads_search: %s\n", ads_errstr(rc)));
goto done;
}
if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
goto done;
}
*name = pull_username(ads, mem_ctx, msg);
*type = ads_atype_map(atype);
status = NT_STATUS_OK;
DEBUG(3,("ads sid_to_name mapped %s\n", *name));
done:
if (msg) ads_msgfree(ads, msg);
return status;
}
/* convert a DN to a name, rid and name type
this might become a major speed bottleneck if groups have
lots of users, in which case we could cache the results
*/
static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
const char *dn,
char **name, uint32 *name_type, uint32 *rid)
{
char *exp;
void *res = NULL;
const char *attrs[] = {"userPrincipalName", "sAMAccountName",
"objectSid", "sAMAccountType", NULL};
ADS_STATUS rc;
uint32 atype;
DOM_SID sid;
asprintf(&exp, "(distinguishedName=%s)", dn);
rc = ads_search_retry(ads, &res, exp, attrs);
free(exp);
if (!ADS_ERR_OK(rc)) {
goto failed;
}
(*name) = pull_username(ads, mem_ctx, res);
if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
goto failed;
}
(*name_type) = ads_atype_map(atype);
if (!ads_pull_sid(ads, res, "objectSid", &sid) ||
!sid_peek_rid(&sid, rid)) {
goto failed;
}
if (res) ads_msgfree(ads, res);
return True;
failed:
if (res) ads_msgfree(ads, res);
return False;
}
/* Lookup user information from a rid */
static NTSTATUS query_user(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 user_rid,
WINBIND_USERINFO *info)
{
ADS_STRUCT *ads = NULL;
const char *attrs[] = {"userPrincipalName",
"sAMAccountName",
"name", "objectSid",
"primaryGroupID", NULL};
ADS_STATUS rc;
int count;
void *msg = NULL;
char *exp;
DOM_SID sid;
char *sidstr;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
DEBUG(3,("ads: query_user\n"));
sid_from_rid(domain, user_rid, &sid);
ads = ads_cached_connection(domain);
if (!ads) goto done;
sidstr = sid_binstring(&sid);
asprintf(&exp, "(objectSid=%s)", sidstr);
rc = ads_search_retry(ads, &msg, exp, attrs);
free(exp);
free(sidstr);
if (!ADS_ERR_OK(rc)) {
DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
goto done;
}
count = ads_count_replies(ads, msg);
if (count != 1) {
DEBUG(1,("query_user(rid=%d): Not found\n", user_rid));
goto done;
}
info->acct_name = pull_username(ads, mem_ctx, msg);
info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
DEBUG(1,("No sid for %d !?\n", user_rid));
goto done;
}
if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
DEBUG(1,("No primary group for %d !?\n", user_rid));
goto done;
}
if (!sid_peek_check_rid(&domain->sid,&sid, &info->user_rid)) {
DEBUG(1,("No rid for %d !?\n", user_rid));
goto done;
}
status = NT_STATUS_OK;
DEBUG(3,("ads query_user gave %s\n", info->acct_name));
done:
if (msg) ads_msgfree(ads, msg);
return status;
}
/* Lookup groups a user is a member of. */
static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 user_rid,
uint32 *num_groups, uint32 **user_gids)
{
ADS_STRUCT *ads = NULL;
const char *attrs[] = {"distinguishedName", NULL};
const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
ADS_STATUS rc;
int count;
void *msg = NULL;
char *exp;
char *user_dn;
DOM_SID *sids;
int i;
uint32 primary_group;
DOM_SID sid;
char *sidstr;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
*num_groups = 0;
DEBUG(3,("ads: lookup_usergroups\n"));
(*num_groups) = 0;
sid_from_rid(domain, user_rid, &sid);
ads = ads_cached_connection(domain);
if (!ads) goto done;
sidstr = sid_binstring(&sid);
asprintf(&exp, "(objectSid=%s)", sidstr);
rc = ads_search_retry(ads, &msg, exp, attrs);
free(exp);
free(sidstr);
if (!ADS_ERR_OK(rc)) {
DEBUG(1,("lookup_usergroups(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
goto done;
}
user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
if (msg) ads_msgfree(ads, msg);
rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
if (!ADS_ERR_OK(rc)) {
DEBUG(1,("lookup_usergroups(rid=%d) ads_search tokenGroups: %s\n", user_rid, ads_errstr(rc)));
goto done;
}
if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group)) {
DEBUG(1,("%s: No primary group for rid=%d !?\n", domain->name, user_rid));
goto done;
}
count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids) + 1;
(*user_gids) = (uint32 *)talloc_zero(mem_ctx, sizeof(uint32) * count);
(*user_gids)[(*num_groups)++] = primary_group;
for (i=1;i<count;i++) {
uint32 rid;
if (!sid_peek_check_rid(&domain->sid, &sids[i-1], &rid)) continue;
(*user_gids)[*num_groups] = rid;
(*num_groups)++;
}
status = NT_STATUS_OK;
DEBUG(3,("ads lookup_usergroups for rid=%d\n", user_rid));
done:
if (msg) ads_msgfree(ads, msg);
return status;
}
/*
find the members of a group, given a group rid and domain
*/
static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 group_rid, uint32 *num_names,
uint32 **rid_mem, char ***names,
uint32 **name_types)
{
DOM_SID group_sid;
ADS_STATUS rc;
int count;
void *res=NULL;
ADS_STRUCT *ads = NULL;
char *exp;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
char *sidstr;
const char *attrs[] = {"member", NULL};
char **members;
int i, num_members;
*num_names = 0;
ads = ads_cached_connection(domain);
if (!ads) goto done;
sid_from_rid(domain, group_rid, &group_sid);
sidstr = sid_binstring(&group_sid);
/* search for all members of the group */
asprintf(&exp, "(objectSid=%s)",sidstr);
rc = ads_search_retry(ads, &res, exp, attrs);
free(exp);
free(sidstr);
if (!ADS_ERR_OK(rc)) {
DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
goto done;
}
count = ads_count_replies(ads, res);
if (count == 0) {
status = NT_STATUS_OK;
goto done;
}
members = ads_pull_strings(ads, mem_ctx, res, "member");
if (!members) {
/* no members? ok ... */
status = NT_STATUS_OK;
goto done;
}
/* now we need to turn a list of members into rids, names and name types
the problem is that the members are in the form of distinguised names
*/
for (i=0;members[i];i++) /* noop */ ;
num_members = i;
(*rid_mem) = talloc_zero(mem_ctx, sizeof(uint32) * num_members);
(*name_types) = talloc_zero(mem_ctx, sizeof(uint32) * num_members);
(*names) = talloc_zero(mem_ctx, sizeof(char *) * num_members);
for (i=0;i<num_members;i++) {
uint32 name_type, rid;
char *name;
if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &rid)) {
(*names)[*num_names] = name;
(*name_types)[*num_names] = name_type;
(*rid_mem)[*num_names] = rid;
(*num_names)++;
}
}
status = NT_STATUS_OK;
DEBUG(3,("ads lookup_groupmem for rid=%d\n", group_rid));
done:
if (res) ads_msgfree(ads, res);
return status;
}
/* find the sequence number for a domain */
static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
{
ADS_STRUCT *ads = NULL;
ADS_STATUS rc;
*seq = DOM_SEQUENCE_NONE;
ads = ads_cached_connection(domain);
if (!ads) return NT_STATUS_UNSUCCESSFUL;
rc = ads_USN(ads, seq);
if (!ADS_ERR_OK(rc)) {
/* its a dead connection */
ads_destroy(&ads);
domain->private = NULL;
}
return ads_ntstatus(rc);
}
/* get a list of trusted domains */
static NTSTATUS trusted_domains(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 *num_domains,
char ***names,
char ***alt_names,
DOM_SID **dom_sids)
{
ADS_STRUCT *ads;
ADS_STATUS rc;
*num_domains = 0;
*names = NULL;
ads = ads_cached_connection(domain);
if (!ads) return NT_STATUS_UNSUCCESSFUL;
rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids);
return ads_ntstatus(rc);
}
/* find the domain sid for a domain */
static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
{
ADS_STRUCT *ads;
ADS_STATUS rc;
ads = ads_cached_connection(domain);
if (!ads) return NT_STATUS_UNSUCCESSFUL;
rc = ads_domain_sid(ads, sid);
if (!ADS_ERR_OK(rc)) {
/* its a dead connection */
ads_destroy(&ads);
domain->private = NULL;
}
return ads_ntstatus(rc);
}
/* find alternate names list for the domain - for ADS this is the
netbios name */
static NTSTATUS alternate_name(struct winbindd_domain *domain)
{
ADS_STRUCT *ads;
ADS_STATUS rc;
TALLOC_CTX *ctx;
char *workgroup;
ads = ads_cached_connection(domain);
if (!ads) return NT_STATUS_UNSUCCESSFUL;
if (!(ctx = talloc_init_named("alternate_name"))) {
return NT_STATUS_NO_MEMORY;
}
rc = ads_workgroup_name(ads, ctx, &workgroup);
if (ADS_ERR_OK(rc)) {
fstrcpy(domain->name, workgroup);
fstrcpy(domain->alt_name, ads->config.realm);
strupper(domain->alt_name);
strupper(domain->name);
}
talloc_destroy(ctx);
return ads_ntstatus(rc);
}
/* the ADS backend methods are exposed via this structure */
struct winbindd_methods ads_methods = {
True,
query_user_list,
enum_dom_groups,
name_to_sid,
sid_to_name,
query_user,
lookup_usergroups,
lookup_groupmem,
sequence_number,
trusted_domains,
domain_sid,
alternate_name
};
#endif