1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00

idmap_adex: Add new idmap plugin for support RFC2307 enabled AD forests.

The adex idmap/nss_info plugin is an adapation of the Likewise
Enterprise plugin with support for OU based cells removed
(since the Windows pieces to manage the cells are not available).

This plugin supports

  * The RFC2307 schema for users and groups.
  * Connections to trusted domains
  * Global catalog searches
  * Cross forest trusts
  * User and group aliases

Prerequiste: Add the following attributes to the Partial Attribute
Set in global catalog:

  * uidNumber
  * uid
  * gidNumber

A basic config using the current trunk code would look like

 [global]
      idmap backend = adex
      idmap uid = 10000 - 19999
      idmap gid = 20000 - 29999
      idmap config US:backend = adex
      idmap config US:range = 20000 - 29999
      winbind nss info = adex

       winbind normalize names = yes
       winbind refresh tickets = yes
       template homedir = /home/%D/%U
       template shell = /bin/bash
This commit is contained in:
Gerald (Jerry) Carter 2008-09-19 12:27:15 -05:00 committed by Gerald W. Carter
parent b18449dbd5
commit 7d5fb989ac
9 changed files with 3753 additions and 0 deletions

View File

@ -985,6 +985,14 @@ IDMAP_HASH_OBJ = \
winbindd/idmap_hash/idmap_hash.o \
winbindd/idmap_hash/mapfile.o
IDMAP_ADEX_OBJ = \
winbindd/idmap_adex/idmap_adex.o \
winbindd/idmap_adex/cell_util.o \
winbindd/idmap_adex/likewise_cell.o \
winbindd/idmap_adex/provider_unified.o \
winbindd/idmap_adex/gc_util.o \
winbindd/idmap_adex/domain_util.o
WINBINDD_OBJ1 = \
winbindd/winbindd.o \
winbindd/winbindd_user.o \
@ -2218,6 +2226,10 @@ bin/hash.@SHLIBEXT@: $(BINARY_PREREQS) $(IDMAP_HASH_OBJ)
@echo "Building plugin $@"
@$(SHLD_MODULE) $(IDMAP_HASH_OBJ)
bin/adex.@SHLIBEXT@: $(BINARY_PREREQS) $(IDMAP_ADEX_OBJ)
@echo "Building plugin $@"
@$(SHLD_MODULE) $(IDMAP_ADEX_OBJ)
bin/tdb2.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_tdb2.o
@echo "Building plugin $@"
@$(SHLD_MODULE) winbindd/idmap_tdb2.o

View File

@ -6058,6 +6058,7 @@ SMB_MODULE(idmap_nss, winbindd/idmap_nss.o, "bin/nss.$SHLIBEXT", IDMAP)
SMB_MODULE(idmap_rid, winbindd/idmap_rid.o, "bin/rid.$SHLIBEXT", IDMAP)
SMB_MODULE(idmap_ad, winbindd/idmap_ad.o, "bin/ad.$SHLIBEXT", IDMAP)
SMB_MODULE(idmap_hash, \$(IDMAP_HASH_OBJ), "bin/hash.$SHLIBEXT", IDMAP)
SMB_MODULE(idmap_adex, \$(IDMAP_ADEX_OBJ), "bin/adex.$SHLIBEXT", IDMAP)
SMB_SUBSYSTEM(IDMAP, winbindd/idmap.o)
SMB_MODULE(nss_info_template, winbindd/nss_info_template.o, "bin/template.$SHLIBEXT", NSS_INFO)

View File

@ -0,0 +1,292 @@
/*
* idmap_adex: Support for AD Forests
*
* Copyright (C) Gerald (Jerry) Carter 2006-2008
*
* 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 "idmap_adex.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_IDMAP
/**********************************************************************
**********************************************************************/
char *find_attr_string(char **list, size_t num_lines, const char *substr)
{
int i;
int cmplen = strlen(substr);
for (i = 0; i < num_lines; i++) {
/* make sure to avoid substring matches like uid
and uidNumber */
if ((StrnCaseCmp(list[i], substr, cmplen) == 0) &&
(list[i][cmplen] == '=')) {
/* Don't return an empty string */
if (list[i][cmplen + 1] != '\0')
return &(list[i][cmplen + 1]);
return NULL;
}
}
return NULL;
}
/**********************************************************************
**********************************************************************/
bool is_object_class(char **list, size_t num_lines, const char *substr)
{
int i;
for (i = 0; i < num_lines; i++) {
if (strequal(list[i], substr)) {
return true;
}
}
return false;
}
/**********************************************************************
Find out about the cell (e.g. use2307Attrs, etc...)
**********************************************************************/
NTSTATUS cell_lookup_settings(struct likewise_cell * cell)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
/* Parameter check */
if (!cell) {
nt_status = NT_STATUS_INVALID_PARAMETER;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* Only supporting Forest-wide, schema based searches */
cell_set_flags(cell, LWCELL_FLAG_USE_RFC2307_ATTRS);
cell_set_flags(cell, LWCELL_FLAG_SEARCH_FOREST);
cell->provider = &ccp_unified;
nt_status = NT_STATUS_OK;
done:
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(1,("LWI: Failed to obtain cell settings (%s)\n",
nt_errstr(nt_status)));
}
return nt_status;
}
static NTSTATUS cell_lookup_forest(struct likewise_cell *c)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct gc_info *gc = NULL;
if (!c) {
return NT_STATUS_INVALID_PARAMETER;
}
if ((gc = TALLOC_ZERO_P(NULL, struct gc_info)) == NULL) {
nt_status = NT_STATUS_NO_MEMORY;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* Query the rootDSE for the forest root naming conect first.
Check that the a GC server for the forest has not already
been added */
nt_status = gc_find_forest_root(gc, cell_dns_domain(c));
BAIL_ON_NTSTATUS_ERROR(nt_status);
c->forest_name = talloc_strdup(c, gc->forest_name);
BAIL_ON_PTR_ERROR(c->forest_name, nt_status);
done:
if (gc) {
talloc_free(gc);
}
return nt_status;
}
/**********************************************************************
**********************************************************************/
NTSTATUS cell_locate_membership(ADS_STRUCT * ads)
{
ADS_STATUS status;
char *domain_dn = ads_build_dn(lp_realm());
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
DOM_SID sid;
struct likewise_cell *cell = NULL;
/* In the Likewise plugin, I had to support the concept of cells
based on the machine's membership in an OU. However, now I'll
just assume our membership in the forest cell */
DEBUG(2, ("locate_cell_membership: Located membership "
"in cell \"%s\"\n", domain_dn));
if ((cell = cell_new()) == NULL) {
nt_status = NT_STATUS_NO_MEMORY;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
status = ads_domain_sid(ads, &sid);
if (!ADS_ERR_OK(status)) {
DEBUG(3,("locate_cell_membership: Failed to find "
"domain SID for %s\n", domain_dn));
}
/* save the SID and search base for our domain */
cell_set_dns_domain(cell, lp_realm());
cell_set_connection(cell, ads);
cell_set_dn(cell, domain_dn);
cell_set_domain_sid(cell, &sid);
/* Now save our forest root */
cell_lookup_forest(cell);
/* Add the cell to the list */
if (!cell_list_add(cell)) {
nt_status = NT_STATUS_INSUFFICIENT_RESOURCES;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* Done! */
nt_status = NT_STATUS_OK;
done:
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(0,("LWI: Failed to locate cell membership (%s)\n",
nt_errstr(nt_status)));
}
SAFE_FREE(domain_dn);
return nt_status;
}
/*********************************************************************
********************************************************************/
int min_id_value(void)
{
int id_val;
id_val = lp_parm_int(-1, "lwidentity", "min_id_value", MIN_ID_VALUE);
/* Still don't let it go below 50 */
return MAX(50, id_val);
}
/********************************************************************
*******************************************************************/
char *cell_dn_to_dns(const char *dn)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
char *domain = NULL;
char *dns_name = NULL;
const char *tmp_dn;
char *buffer = NULL;
TALLOC_CTX *frame = talloc_stackframe();
if (!dn || !*dn) {
goto done;
}
tmp_dn = talloc_strdup(frame, dn);
BAIL_ON_PTR_ERROR(tmp_dn, nt_status);
while (next_token_talloc(frame, &tmp_dn, &buffer, ",")) {
/* skip everything up the where DC=... begins */
if (StrnCaseCmp(buffer, "DC=", 3) != 0)
continue;
if (!domain) {
domain = talloc_strdup(frame, &buffer[3]);
} else {
domain = talloc_asprintf_append(domain, ".%s",
&buffer[3]);
}
BAIL_ON_PTR_ERROR(domain, nt_status);
}
dns_name = SMB_STRDUP(domain);
BAIL_ON_PTR_ERROR(dns_name, nt_status);
nt_status = NT_STATUS_OK;
done:
PRINT_NTSTATUS_ERROR(nt_status, "cell_dn_to_dns", 1);
talloc_destroy(frame);
return dns_name;
}
/*********************************************************************
********************************************************************/
NTSTATUS get_sid_type(ADS_STRUCT *ads,
LDAPMessage *msg,
enum lsa_SidType *type)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
uint32_t atype;
if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
nt_status = NT_STATUS_INVALID_USER_BUFFER;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
switch (atype &0xF0000000) {
case ATYPE_SECURITY_GLOBAL_GROUP:
*type = SID_NAME_DOM_GRP;
break;
case ATYPE_SECURITY_LOCAL_GROUP:
*type = SID_NAME_ALIAS;
break;
case ATYPE_NORMAL_ACCOUNT:
case ATYPE_WORKSTATION_TRUST:
case ATYPE_INTERDOMAIN_TRUST:
*type = SID_NAME_USER;
break;
default:
*type = SID_NAME_USE_NONE;
nt_status = NT_STATUS_INVALID_ACCOUNT_NAME;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
nt_status = NT_STATUS_OK;
done:
return nt_status;
}

View File

@ -0,0 +1,278 @@
/*
* idmap_adex: Domain search interface
*
* Copyright (C) Gerald (Jerry) Carter 2007-2008
*
* 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 "idmap_adex.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_IDMAP
struct dc_info {
struct dc_info *prev, *next;
char *dns_name;
struct likewise_cell *domain_cell;
};
static struct dc_info *_dc_server_list = NULL;
/**********************************************************************
*********************************************************************/
static struct dc_info *dc_list_head(void)
{
return _dc_server_list;
}
/**********************************************************************
*********************************************************************/
static NTSTATUS dc_add_domain(const char *domain)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct dc_info *dc = NULL;
/* Check for duplicates */
dc = dc_list_head();
while (dc) {
if (strequal (dc->dns_name, domain))
break;
dc = dc->next;
}
if (dc) {
DEBUG(10,("dc_add_domain: %s already in list\n", domain));
return NT_STATUS_OK;
}
dc = TALLOC_ZERO_P(NULL, struct dc_info);
BAIL_ON_PTR_ERROR(dc, nt_status);
dc->dns_name = talloc_strdup(dc, domain);
BAIL_ON_PTR_ERROR(dc->dns_name, nt_status);
DLIST_ADD_END(_dc_server_list, dc, struct dc_info*);
nt_status = NT_STATUS_OK;
done:
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_destroy(dc);
DEBUG(0,("LWI: Failed to add new DC connection for %s (%s)\n",
domain, nt_errstr(nt_status)));
}
return nt_status;
}
/**********************************************************************
*********************************************************************/
static void dc_server_list_destroy(void)
{
struct dc_info *dc = dc_list_head();
while (dc) {
struct dc_info *p = dc->next;
cell_destroy(dc->domain_cell);
talloc_destroy(dc);
dc = p;
}
return;
}
/**********************************************************************
*********************************************************************/
NTSTATUS domain_init_list(void)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct winbindd_tdc_domain *domains = NULL;
size_t num_domains = 0;
int i;
if (_dc_server_list != NULL) {
dc_server_list_destroy();
}
/* Add our domain */
nt_status = dc_add_domain(lp_realm());
BAIL_ON_NTSTATUS_ERROR(nt_status);
if (!wcache_tdc_fetch_list(&domains, &num_domains)) {
nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* Add all domains with an incoming trust path */
for (i=0; i<num_domains; i++) {
uint32_t flags = (NETR_TRUST_FLAG_INBOUND|NETR_TRUST_FLAG_IN_FOREST);
/* We just require one of the flags to be set here */
if (domains[i].trust_flags & flags) {
nt_status = dc_add_domain(domains[i].dns_name);
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
}
nt_status = NT_STATUS_OK;
done:
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(2,("LWI: Failed to initialize DC list (%s)\n",
nt_errstr(nt_status)));
}
TALLOC_FREE(domains);
return nt_status;
}
/********************************************************************
*******************************************************************/
static NTSTATUS dc_do_search(struct dc_info *dc,
const char *search_base,
int scope,
const char *expr,
const char **attrs,
LDAPMessage ** msg)
{
ADS_STATUS status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
status = cell_do_search(dc->domain_cell, search_base,
scope, expr, attrs, msg);
nt_status = ads_ntstatus(status);
return nt_status;
}
/**********************************************************************
*********************************************************************/
static struct dc_info *dc_find_domain(const char *dns_domain)
{
struct dc_info *dc = dc_list_head();
if (!dc)
return NULL;
while (dc) {
if (strequal(dc->dns_name, dns_domain)) {
return dc;
}
dc = dc->next;
}
return NULL;
}
/**********************************************************************
*********************************************************************/
NTSTATUS dc_search_domains(struct likewise_cell **cell,
LDAPMessage **msg,
const char *dn,
const DOM_SID *sid)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
TALLOC_CTX *frame = talloc_stackframe();
char *dns_domain;
const char *attrs[] = { "*", NULL };
struct dc_info *dc = NULL;
const char *base = NULL;
if (!dn || !*dn) {
nt_status = NT_STATUS_INVALID_PARAMETER;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
dns_domain = cell_dn_to_dns(dn);
BAIL_ON_PTR_ERROR(dns_domain, nt_status);
if ((dc = dc_find_domain(dns_domain)) == NULL) {
nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* Reparse the cell settings for the domain if necessary */
if (!dc->domain_cell) {
char *base_dn;
base_dn = ads_build_dn(dc->dns_name);
BAIL_ON_PTR_ERROR(base_dn, nt_status);
nt_status = cell_connect_dn(&dc->domain_cell, base_dn);
SAFE_FREE(base_dn);
BAIL_ON_NTSTATUS_ERROR(nt_status);
nt_status = cell_lookup_settings(dc->domain_cell);
BAIL_ON_NTSTATUS_ERROR(nt_status);
/* By definition this is already part of a larger
forest-wide search scope */
cell_set_flags(dc->domain_cell, LWCELL_FLAG_SEARCH_FOREST);
}
/* Check whether we are operating in non-schema or RFC2307
mode */
if (cell_flags(dc->domain_cell) & LWCELL_FLAG_USE_RFC2307_ATTRS) {
nt_status = dc_do_search(dc, dn, LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, msg);
} else {
const char *sid_str = NULL;
char *filter = NULL;
sid_str = sid_string_talloc(frame, sid);
BAIL_ON_PTR_ERROR(sid_str, nt_status);
filter = talloc_asprintf(frame, "(keywords=backLink=%s)",
sid_str);
BAIL_ON_PTR_ERROR(filter, nt_status);
base = cell_search_base(dc->domain_cell);
BAIL_ON_PTR_ERROR(base, nt_status);
nt_status = dc_do_search(dc, base, LDAP_SCOPE_SUBTREE,
filter, attrs, msg);
}
BAIL_ON_NTSTATUS_ERROR(nt_status);
*cell = dc->domain_cell;
done:
talloc_destroy(CONST_DISCARD(char*, base));
talloc_destroy(frame);
return nt_status;
}

View File

@ -0,0 +1,848 @@
/*
* idmap_adex: Global Catalog search interface
*
* Copyright (C) Gerald (Jerry) Carter 2007-2008
*
* 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 "idmap_adex.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_IDMAP
static struct gc_info *_gc_server_list = NULL;
/**********************************************************************
*********************************************************************/
static struct gc_info *gc_list_head(void)
{
return _gc_server_list;
}
/**********************************************************************
Checks if either of the domains is a subdomain of the other
*********************************************************************/
static bool is_subdomain(const char* a, const char *b)
{
char *s;
TALLOC_CTX *frame = talloc_stackframe();
char *x, *y;
bool ret = false;
/* Trivial cases */
if (!a && !b)
return true;
if (!a || !b)
return false;
/* Normalize the case */
x = talloc_strdup(frame, a);
y = talloc_strdup(frame, b);
if (!x || !y) {
ret = false;
goto done;
}
strupper_m(x);
strupper_m(y);
/* Exact match */
if (strcmp(x, y) == 0) {
ret = true;
goto done;
}
/* Check for trailing substrings */
s = strstr_m(x, y);
if (s && (strlen(s) == strlen(y))) {
ret = true;
goto done;
}
s = strstr_m(y, x);
if (s && (strlen(s) == strlen(x))) {
ret = true;
goto done;
}
done:
talloc_destroy(frame);
return ret;
}
/**********************************************************************
*********************************************************************/
NTSTATUS gc_find_forest_root(struct gc_info *gc, const char *domain)
{
ADS_STRUCT *ads = NULL;
ADS_STATUS ads_status;
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct nbt_cldap_netlogon_5 cldap_reply;
TALLOC_CTX *frame = talloc_stackframe();
if (!gc || !domain) {
return NT_STATUS_INVALID_PARAMETER;
}
ZERO_STRUCT(cldap_reply);
ads = ads_init(domain, NULL, NULL);
BAIL_ON_PTR_ERROR(ads, nt_status);
ads->auth.flags = ADS_AUTH_NO_BIND;
ads_status = ads_connect(ads);
if (!ADS_ERR_OK(ads_status)) {
DEBUG(4, ("find_forest_root: ads_connect(%s) failed! (%s)\n",
domain, ads_errstr(ads_status)));
}
nt_status = ads_ntstatus(ads_status);
BAIL_ON_NTSTATUS_ERROR(nt_status);
if (!ads_cldap_netlogon_5(frame,
ads->config.ldap_server_name,
ads->config.realm,
&cldap_reply))
{
DEBUG(4,("find_forest_root: Failed to get a CLDAP reply from %s!\n",
ads->server.ldap_server));
nt_status = NT_STATUS_IO_TIMEOUT;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
gc->forest_name = talloc_strdup(gc, cldap_reply.forest);
BAIL_ON_PTR_ERROR(gc->forest_name, nt_status);
done:
if (ads) {
ads_destroy(&ads);
}
return nt_status;
}
/**********************************************************************
*********************************************************************/
static NTSTATUS gc_add_forest(const char *domain)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct gc_info *gc = NULL;
struct gc_info *find_gc = NULL;
char *dn;
ADS_STRUCT *ads = NULL;
struct likewise_cell *primary_cell = NULL;
primary_cell = cell_list_head();
if (!primary_cell) {
nt_status = NT_STATUS_INVALID_SERVER_STATE;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* Check for duplicates based on domain name first as this
requires no connection */
find_gc = gc_list_head();
while (find_gc) {
if (strequal (find_gc->forest_name, domain))
break;
find_gc = find_gc->next;
}
if (find_gc) {
DEBUG(10,("gc_add_forest: %s already in list\n", find_gc->forest_name));
return NT_STATUS_OK;
}
if ((gc = TALLOC_ZERO_P(NULL, struct gc_info)) == NULL) {
nt_status = NT_STATUS_NO_MEMORY;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* Query the rootDSE for the forest root naming conect first.
Check that the a GC server for the forest has not already
been added */
nt_status = gc_find_forest_root(gc, domain);
BAIL_ON_NTSTATUS_ERROR(nt_status);
find_gc = gc_list_head();
while (find_gc) {
if (strequal (find_gc->forest_name, gc->forest_name))
break;
find_gc = find_gc->next;
}
if (find_gc) {
DEBUG(10,("gc_add_forest: Forest %s already in list\n",
find_gc->forest_name));
return NT_STATUS_OK;
}
/* Not found, so add it here. Make sure we connect to
a DC in _this_ domain and not the forest root. */
dn = ads_build_dn(gc->forest_name);
BAIL_ON_PTR_ERROR(dn, nt_status);
gc->search_base = talloc_strdup(gc, dn);
SAFE_FREE(dn);
BAIL_ON_PTR_ERROR(gc->search_base, nt_status);
#if 0
/* Can't use cell_connect_dn() here as there is no way to
specifiy the LWCELL_FLAG_GC_CELL flag setting for cell_connect() */
nt_status = cell_connect_dn(&gc->forest_cell, gc->search_base);
BAIL_ON_NTSTATUS_ERROR(nt_status);
#else
gc->forest_cell = cell_new();
BAIL_ON_PTR_ERROR(gc->forest_cell, nt_status);
/* Set the DNS domain, dn, etc ... and add it to the list */
cell_set_dns_domain(gc->forest_cell, gc->forest_name);
cell_set_dn(gc->forest_cell, gc->search_base);
cell_set_flags(gc->forest_cell, LWCELL_FLAG_GC_CELL);
#endif
/* It is possible to belong to a non-forest cell and a
non-provisioned forest (at our domain levele). In that
case, we should just inherit the flags from our primary
cell since the GC searches will match our own schema
model. */
if (strequal(primary_cell->forest_name, gc->forest_name)
|| is_subdomain(primary_cell->dns_domain, gc->forest_name))
{
cell_set_flags(gc->forest_cell, cell_flags(primary_cell));
} else {
/* outside of our domain */
nt_status = cell_connect(gc->forest_cell);
BAIL_ON_NTSTATUS_ERROR(nt_status);
nt_status = cell_lookup_settings(gc->forest_cell);
BAIL_ON_NTSTATUS_ERROR(nt_status);
/* Drop the connection now that we have the settings */
ads = cell_connection(gc->forest_cell);
ads_destroy(&ads);
cell_set_connection(gc->forest_cell, NULL);
}
DLIST_ADD_END(_gc_server_list, gc, struct gc_info*);
DEBUG(10,("gc_add_forest: Added %s to Global Catalog list of servers\n",
gc->forest_name));
nt_status = NT_STATUS_OK;
done:
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_destroy(gc);
DEBUG(3,("LWI: Failed to add new GC connection for %s (%s)\n",
domain, nt_errstr(nt_status)));
}
return nt_status;
}
/**********************************************************************
*********************************************************************/
static void gc_server_list_destroy(void)
{
struct gc_info *gc = gc_list_head();
while (gc) {
struct gc_info *p = gc->next;
cell_destroy(gc->forest_cell);
talloc_destroy(gc);
gc = p;
}
_gc_server_list = NULL;
return;
}
/**********************************************************************
Setup the initial list of forests and initial the forest cell
settings for each. FIXME!!!
*********************************************************************/
NTSTATUS gc_init_list(void)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct winbindd_tdc_domain *domains = NULL;
size_t num_domains = 0;
int i;
if (_gc_server_list != NULL) {
gc_server_list_destroy();
}
if (!wcache_tdc_fetch_list(&domains, &num_domains)) {
nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* Find our forest first. Have to try all domains here starting
with our own. gc_add_forest() filters duplicates */
nt_status = gc_add_forest(lp_realm());
WARN_ON_NTSTATUS_ERROR(nt_status);
for (i=0; i<num_domains; i++) {
uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST);
/* I think we should be able to break out of loop once
we add a GC for our forest and not have to test every one.
In fact, this entire loop is probably irrelevant since
the GC location code should always find a GC given lp_realm().
Will have to spend time testing before making the change.
--jerry */
if ((domains[i].trust_flags & flags) == flags) {
nt_status = gc_add_forest(domains[i].dns_name);
WARN_ON_NTSTATUS_ERROR(nt_status);
/* Don't BAIL here since not every domain may
have a GC server */
}
}
/* Now add trusted forests. gc_add_forest() will filter out
duplicates. Check everything with an incoming trust path
that is not in our own forest. */
for (i=0; i<num_domains; i++) {
uint32_t flags = domains[i].trust_flags;
uint32_t attribs = domains[i].trust_attribs;
/* Skip non_AD domains */
if (strlen(domains[i].dns_name) == 0) {
continue;
}
/* Only add a GC for a forest outside of our own.
Ignore QUARANTINED/EXTERNAL trusts */
if ((flags & NETR_TRUST_FLAG_INBOUND)
&& !(flags & NETR_TRUST_FLAG_IN_FOREST)
&& (attribs & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
{
nt_status = gc_add_forest(domains[i].dns_name);
WARN_ON_NTSTATUS_ERROR(nt_status);
}
}
nt_status = NT_STATUS_OK;
done:
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(2,("LWI: Failed to initialized GC list (%s)\n",
nt_errstr(nt_status)));
}
TALLOC_FREE(domains);
return nt_status;
}
/**********************************************************************
*********************************************************************/
struct gc_info *gc_search_start(void)
{
NTSTATUS nt_status = NT_STATUS_OK;
struct gc_info *gc = gc_list_head();
if (!gc) {
nt_status = gc_init_list();
BAIL_ON_NTSTATUS_ERROR(nt_status);
gc = gc_list_head();
}
done:
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(2,("LWI: Failed to initialize GC list (%s)\n",
nt_errstr(nt_status)));
}
return gc;
}
/**********************************************************************
Search Global Catalog. Always search our own forest. The flags set
controls whether or not we search cross forest. Assume that the
resulting set is always returned from one GC so that we don't have to
both combining the LDAPMessage * results
*********************************************************************/
NTSTATUS gc_search_forest(struct gc_info *gc,
LDAPMessage **msg,
const char *filter)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
const char *attrs[] = {"*", NULL};
LDAPMessage *m = NULL;
if (!gc || !msg || !filter) {
nt_status = NT_STATUS_INVALID_PARAMETER;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* When you have multiple domain trees in a forest, the
GC will search all naming contexts when you send it
and empty ("") base search suffix. Tested against
Windows 2003. */
ads_status = cell_do_search(gc->forest_cell, "",
LDAP_SCOPE_SUBTREE, filter, attrs, &m);
nt_status = ads_ntstatus(ads_status);
BAIL_ON_NTSTATUS_ERROR(nt_status);
*msg = m;
done:
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(2,("LWI: Forest wide search %s failed (%s)\n",
filter, nt_errstr(nt_status)));
}
return nt_status;
}
/**********************************************************************
Search all forests via GC and return the results in an array of
ADS_STRUCT/LDAPMessage pairs.
*********************************************************************/
NTSTATUS gc_search_all_forests(const char *filter,
ADS_STRUCT ***ads_list,
LDAPMessage ***msg_list,
int *num_resp, uint32_t flags)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct gc_info *gc = NULL;
uint32_t test_flags = ADEX_GC_SEARCH_CHECK_UNIQUE;
*ads_list = NULL;
*msg_list = NULL;
*num_resp = 0;
if ((gc = gc_search_start()) == NULL) {
nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
while (gc) {
LDAPMessage *m = NULL;
nt_status = gc_search_forest(gc, &m, filter);
if (!NT_STATUS_IS_OK(nt_status)) {
gc = gc->next;
continue;
}
nt_status = add_ads_result_to_array(cell_connection(gc->forest_cell),
m, ads_list, msg_list,
num_resp);
BAIL_ON_NTSTATUS_ERROR(nt_status);
/* If there can only be one match, then we are done */
if ((*num_resp > 0) && ((flags & test_flags) == test_flags)) {
break;
}
gc = gc->next;
}
if (*num_resp == 0) {
nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
nt_status = NT_STATUS_OK;
done:
return nt_status;
}
/**********************************************************************
Search all forests via GC and return the results in an array of
ADS_STRUCT/LDAPMessage pairs.
*********************************************************************/
NTSTATUS gc_search_all_forests_unique(const char *filter,
ADS_STRUCT **ads,
LDAPMessage **msg)
{
ADS_STRUCT **ads_list = NULL;
LDAPMessage **msg_list = NULL;
int num_resp;
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
nt_status = gc_search_all_forests(filter, &ads_list,
&msg_list, &num_resp,
ADEX_GC_SEARCH_CHECK_UNIQUE);
BAIL_ON_NTSTATUS_ERROR(nt_status);
nt_status = check_result_unique(ads_list[0], msg_list[0]);
BAIL_ON_NTSTATUS_ERROR(nt_status);
*ads = ads_list[0];
*msg = msg_list[0];
done:
/* Be care that we don't free the msg result being returned */
if (!NT_STATUS_IS_OK(nt_status)) {
free_result_array(ads_list, msg_list, num_resp);
} else {
talloc_destroy(ads_list);
talloc_destroy(msg_list);
}
return nt_status;
}
/*********************************************************************
********************************************************************/
NTSTATUS gc_name_to_sid(const char *domain,
const char *name,
DOM_SID *sid,
enum lsa_SidType *sid_type)
{
TALLOC_CTX *frame = talloc_stackframe();
char *p, *name_user;
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
char *name_filter;
ADS_STRUCT *ads = NULL;
LDAPMessage *msg = NULL;
LDAPMessage *e = NULL;
char *dn = NULL;
char *dns_domain = NULL;
ADS_STRUCT **ads_list = NULL;
LDAPMessage **msg_list = NULL;
int num_resp = 0;
int i;
/* Strip the "DOMAIN\" prefix if necessary and search for
a matching sAMAccountName in the forest */
if ((p = strchr_m( name, '\\' )) == NULL)
name_user = talloc_strdup( frame, name );
else
name_user = talloc_strdup( frame, p+1 );
BAIL_ON_PTR_ERROR(name_user, nt_status);
name_filter = talloc_asprintf(frame, "(sAMAccountName=%s)", name_user);
BAIL_ON_PTR_ERROR(name_filter, nt_status);
nt_status = gc_search_all_forests(name_filter, &ads_list,
&msg_list, &num_resp, 0);
BAIL_ON_NTSTATUS_ERROR(nt_status);
/* Assume failure until we know otherwise*/
nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
/* Match the domain name from the DN */
for (i=0; i<num_resp; i++) {
ads = ads_list[i];
msg = msg_list[i];
e = ads_first_entry(ads, msg);
while (e) {
struct winbindd_tdc_domain *domain_rec;
dn = ads_get_dn(ads, e);
BAIL_ON_PTR_ERROR(dn, nt_status);
dns_domain = cell_dn_to_dns(dn);
SAFE_FREE(dn);
BAIL_ON_PTR_ERROR(dns_domain, nt_status);
domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
SAFE_FREE(dns_domain);
/* Ignore failures and continue the search */
if (!domain_rec) {
e = ads_next_entry(ads, e);
continue;
}
/* Check for a match on the domain name */
if (strequal(domain, domain_rec->domain_name)) {
if (!ads_pull_sid(ads, e, "objectSid", sid)) {
nt_status = NT_STATUS_INVALID_SID;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
talloc_destroy(domain_rec);
nt_status = get_sid_type(ads, msg, sid_type);
BAIL_ON_NTSTATUS_ERROR(nt_status);
/* We're done! */
nt_status = NT_STATUS_OK;
break;
}
/* once more around thew merry-go-round */
talloc_destroy(domain_rec);
e = ads_next_entry(ads, e);
}
}
done:
free_result_array(ads_list, msg_list, num_resp);
talloc_destroy(frame);
return nt_status;
}
/********************************************************************
Pull an attribute string value
*******************************************************************/
static NTSTATUS get_object_account_name(ADS_STRUCT *ads,
LDAPMessage *msg,
char **name)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
char *sam_name = NULL;
struct winbindd_tdc_domain *domain_rec = NULL;
char *dns_domain = NULL;
char *dn = NULL;
TALLOC_CTX *frame = talloc_stackframe();
int len;
/* Check parameters */
if (!ads || !msg || !name) {
nt_status = NT_STATUS_INVALID_PARAMETER;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* get the name and domain */
dn = ads_get_dn(ads, msg);
BAIL_ON_PTR_ERROR(dn, nt_status);
DEBUG(10,("get_object_account_name: dn = \"%s\"\n", dn));
dns_domain = cell_dn_to_dns(dn);
SAFE_FREE(dn);
BAIL_ON_PTR_ERROR(dns_domain, nt_status);
domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
SAFE_FREE(dns_domain);
if (!domain_rec) {
nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
sam_name = ads_pull_string(ads, frame, msg, "sAMAccountName");
BAIL_ON_PTR_ERROR(sam_name, nt_status);
len = asprintf(name, "%s\\%s", domain_rec->domain_name, sam_name);
if (len == -1) {
*name = NULL;
BAIL_ON_PTR_ERROR((*name), nt_status);
}
nt_status = NT_STATUS_OK;
done:
talloc_destroy(frame);
return nt_status;
}
/*********************************************************************
********************************************************************/
NTSTATUS gc_sid_to_name(const DOM_SID *sid,
char **name,
enum lsa_SidType *sid_type)
{
TALLOC_CTX *frame = talloc_stackframe();
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
char *filter;
ADS_STRUCT *ads = NULL;
LDAPMessage *msg = NULL;
char *sid_string;
*name = NULL;
sid_string = sid_binstring(sid);
BAIL_ON_PTR_ERROR(sid_string, nt_status);
filter = talloc_asprintf(frame, "(objectSid=%s)", sid_string);
SAFE_FREE(sid_string);
BAIL_ON_PTR_ERROR(filter, nt_status);
nt_status = gc_search_all_forests_unique(filter, &ads, &msg);
BAIL_ON_NTSTATUS_ERROR(nt_status);
nt_status = get_object_account_name(ads, msg, name);
BAIL_ON_NTSTATUS_ERROR(nt_status);
nt_status = get_sid_type(ads, msg, sid_type);
BAIL_ON_NTSTATUS_ERROR(nt_status);
done:
ads_msgfree(ads, msg);
talloc_destroy(frame);
return nt_status;
}
/**********************************************************************
*********************************************************************/
NTSTATUS add_ads_result_to_array(ADS_STRUCT *ads,
LDAPMessage *msg,
ADS_STRUCT ***ads_list,
LDAPMessage ***msg_list,
int *size)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
ADS_STRUCT **ads_tmp = NULL;
LDAPMessage **msg_tmp = NULL;
int count = *size;
if (!ads || !msg) {
nt_status = NT_STATUS_INVALID_PARAMETER;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
#if 0
/* Don't add a response with no entries */
if (ads_count_replies(ads, msg) == 0) {
return NT_STATUS_OK;
}
#endif
if (count == 0) {
ads_tmp = TALLOC_ARRAY(NULL, ADS_STRUCT*, 1);
BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
msg_tmp = TALLOC_ARRAY(NULL, LDAPMessage*, 1);
BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
} else {
ads_tmp = TALLOC_REALLOC_ARRAY(*ads_list, *ads_list, ADS_STRUCT*,
count+1);
BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
msg_tmp = TALLOC_REALLOC_ARRAY(*msg_list, *msg_list, LDAPMessage*,
count+1);
BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
}
ads_tmp[count] = ads;
msg_tmp[count] = msg;
count++;
*ads_list = ads_tmp;
*msg_list = msg_tmp;
*size = count;
nt_status = NT_STATUS_OK;
done:
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_destroy(ads_tmp);
talloc_destroy(msg_tmp);
}
return nt_status;
}
/**********************************************************************
Frees search results. Do not free the ads_list as these are
references back to the GC search structures.
*********************************************************************/
void free_result_array(ADS_STRUCT **ads_list,
LDAPMessage **msg_list,
int num_resp)
{
int i;
for (i=0; i<num_resp; i++) {
ads_msgfree(ads_list[i], msg_list[i]);
}
talloc_destroy(ads_list);
talloc_destroy(msg_list);
}
/**********************************************************************
Check that we have exactly one entry from the search
*********************************************************************/
NTSTATUS check_result_unique(ADS_STRUCT *ads, LDAPMessage *msg)
{
NTSTATUS nt_status;
int count;
count = ads_count_replies(ads, msg);
if (count <= 0) {
nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
if (count > 1) {
nt_status = NT_STATUS_DUPLICATE_NAME;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
nt_status = NT_STATUS_OK;
done:
return nt_status;
}

View File

@ -0,0 +1,460 @@
/*
* idmap_adex: Support for D Forests
*
* Copyright (C) Gerald (Jerry) Carter 2006-2008
*
* 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 "idmap_adex.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_IDMAP
#define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
NTSTATUS init_module(void);
/*
* IdMap backend
*/
/********************************************************************
Basic init function responsible for determining our current mode
(standalone or using Centeris Cells). This must return success or
it will be dropped from the idmap backend list.
*******************************************************************/
static NTSTATUS _idmap_adex_init(struct idmap_domain *dom,
const char *params)
{
ADS_STRUCT *ads = NULL;
ADS_STATUS status;
static NTSTATUS init_status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
DOM_SID domain_sid;
fstring dcname;
struct sockaddr_storage ip;
struct likewise_cell *lwcell;
if (NT_STATUS_IS_OK(init_status))
return NT_STATUS_OK;
/* Silently fail if we are not a member server in security = ads */
if ((lp_server_role() != ROLE_DOMAIN_MEMBER) ||
(lp_security() != SEC_ADS)) {
init_status = NT_STATUS_INVALID_SERVER_STATE;
BAIL_ON_NTSTATUS_ERROR(init_status);
}
/* fetch our domain SID first */
if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
init_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
BAIL_ON_NTSTATUS_ERROR(init_status);
}
/* reuse the same ticket cache as winbindd */
setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
/* Establish a connection to a DC */
if ((ads = ads_init(lp_realm(), lp_workgroup(), NULL)) == NULL) {
init_status = NT_STATUS_NO_MEMORY;
BAIL_ON_NTSTATUS_ERROR(init_status);
}
ads->auth.password =
secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
ads->auth.realm = SMB_STRDUP(lp_realm());
/* get the DC name here to setup the server affinity cache and
local krb5.conf */
get_dc_name(lp_workgroup(), lp_realm(), dcname, &ip);
status = ads_connect(ads);
if (!ADS_ERR_OK(status)) {
DEBUG(0, ("_idmap_adex_init: ads_connect() failed! (%s)\n",
ads_errstr(status)));
}
init_status = ads_ntstatus(status);
BAIL_ON_NTSTATUS_ERROR(init_status);
/* Find out cell membership */
init_status = cell_locate_membership(ads);
if (!NT_STATUS_IS_OK(init_status)) {
DEBUG(0,("LWI: Fail to locate cell membership (%s).",
nt_errstr(init_status)));
goto done;
}
/* Fill in the cell information */
lwcell = cell_list_head();
init_status = cell_lookup_settings(lwcell);
BAIL_ON_NTSTATUS_ERROR(init_status);
/* Miscellaneous setup. E.g. set up the list of GC
servers and domain list for our forest (does not actually
connect). */
init_status = gc_init_list();
BAIL_ON_NTSTATUS_ERROR(init_status);
init_status = domain_init_list();
BAIL_ON_NTSTATUS_ERROR(init_status);
done:
if (!NT_STATUS_IS_OK(init_status)) {
DEBUG(1,("Likewise initialization failed (%s)\n",
nt_errstr(init_status)));
}
/* cleanup */
if (!NT_STATUS_IS_OK(init_status)) {
cell_list_destroy();
/* init_status stores the failure reason but we need to
return success or else idmap_init() will drop us from the
backend list */
return NT_STATUS_OK;
}
init_status = NT_STATUS_OK;
return init_status;
}
/**********************************************************************
*********************************************************************/
static NTSTATUS _idmap_adex_get_sid_from_id(struct
idmap_domain
*dom, struct
id_map
**ids)
{
int i;
bool one_mapped = false;
bool all_mapped = true;
NTSTATUS nt_status;
struct likewise_cell *cell;
nt_status = _idmap_adex_init(dom, NULL);
if (!NT_STATUS_IS_OK(nt_status))
return nt_status;
if ((cell = cell_list_head()) == NULL) {
return NT_STATUS_INVALID_SERVER_STATE;
}
/* have to work through these one by one */
for (i = 0; ids[i]; i++) {
NTSTATUS status;
status = cell->provider->get_sid_from_id(ids[i]->sid,
ids[i]->xid.id,
ids[i]->xid.type);
/* Fail if we cannot find any DC */
if (NT_STATUS_EQUAL
(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
return status;
}
if (!NT_STATUS_IS_OK(status)) {
ids[i]->status = ID_UNMAPPED;
all_mapped = false;
continue;
}
ids[i]->status = ID_MAPPED;
one_mapped = true;
}
return NT_STATUS_OK;
}
/**********************************************************************
*********************************************************************/
static NTSTATUS _idmap_adex_get_id_from_sid(struct
idmap_domain
*dom, struct
id_map
**ids)
{
int i;
bool one_mapped = false;
bool all_mapped = true;
NTSTATUS nt_status;
struct likewise_cell *cell;
nt_status = _idmap_adex_init(dom, NULL);
if (!NT_STATUS_IS_OK(nt_status))
return nt_status;
if ((cell = cell_list_head()) == NULL) {
return NT_STATUS_INVALID_SERVER_STATE;
}
/* have to work through these one by one */
for (i = 0; ids[i]; i++) {
NTSTATUS status;
status = cell->provider->get_id_from_sid(&ids[i]->xid.id,
&ids[i]->xid.
type, ids[i]->sid);
/* Fail if we cannot find any DC */
if (NT_STATUS_EQUAL
(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
return status;
}
if (!NT_STATUS_IS_OK(status)) {
ids[i]->status = ID_UNMAPPED;
all_mapped = false;
continue;
}
ids[i]->status = ID_MAPPED;
one_mapped = true;
}
return NT_STATUS_OK;
}
/**********************************************************************
*********************************************************************/
static NTSTATUS _idmap_adex_set_mapping(struct
idmap_domain
*dom, const struct
id_map *map)
{
DEBUG(0, ("_idmap_adex_set_mapping: not implemented\n"));
return NT_STATUS_NOT_IMPLEMENTED;
}
/**********************************************************************
*********************************************************************/
static NTSTATUS _idmap_adex_remove_mapping(struct
idmap_domain
*dom, const
struct
id_map
*map)
{
DEBUG(0, ("_idmap_adex_remove_mapping: not implemented\n"));
return NT_STATUS_NOT_IMPLEMENTED;
}
/**********************************************************************
*********************************************************************/
static NTSTATUS _idmap_adex_dump(struct idmap_domain
*dom, struct id_map **maps, int *num_map)
{
return NT_STATUS_NOT_IMPLEMENTED;
}
/**********************************************************************
*********************************************************************/
static NTSTATUS _idmap_adex_close(struct idmap_domain
*dom)
{
/* FIXME! need to do cleanup here */
return NT_STATUS_OK;
}
/*
* IdMap NSS plugin
*/
/**********************************************************************
*********************************************************************/
static NTSTATUS _nss_adex_init(struct nss_domain_entry
*e)
{
return _idmap_adex_init(NULL, NULL);
}
/**********************************************************************
*********************************************************************/
static NTSTATUS _nss_adex_get_info(struct
nss_domain_entry *e,
const DOM_SID * sid,
TALLOC_CTX * ctx,
ADS_STRUCT * ads,
LDAPMessage * msg,
char **homedir,
char **shell, char **gecos, gid_t * p_gid)
{
NTSTATUS nt_status;
struct likewise_cell *cell;
nt_status = _idmap_adex_init(NULL, NULL);
if (!NT_STATUS_IS_OK(nt_status))
return nt_status;
if ((cell = cell_list_head()) == NULL) {
return NT_STATUS_INVALID_SERVER_STATE;
}
return cell->provider->get_nss_info(sid, ctx, homedir,
shell, gecos, p_gid);
}
/**********************************************************************
*********************************************************************/
static NTSTATUS _nss_adex_map_to_alias(TALLOC_CTX * mem_ctx, const char
*domain, const char
*name, char **alias)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct likewise_cell *cell = NULL;
nt_status = _idmap_adex_init(NULL, NULL);
BAIL_ON_NTSTATUS_ERROR(nt_status);
if ((cell = cell_list_head()) == NULL) {
nt_status = NT_STATUS_INVALID_SERVER_STATE;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
nt_status = cell->provider->map_to_alias(mem_ctx, domain,
name, alias);
/* go ahead and allow the cache mgr to mark this in
negative cache */
if (!NT_STATUS_IS_OK(nt_status))
nt_status = NT_STATUS_NONE_MAPPED;
done:
return nt_status;
}
/**********************************************************************
*********************************************************************/
static NTSTATUS _nss_adex_map_from_alias(TALLOC_CTX * mem_ctx, const char
*domain, const char
*alias, char **name)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct likewise_cell *cell = NULL;
nt_status = _idmap_adex_init(NULL, NULL);
BAIL_ON_NTSTATUS_ERROR(nt_status);
if ((cell = cell_list_head()) == NULL) {
nt_status = NT_STATUS_INVALID_SERVER_STATE;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
nt_status = cell->provider->map_from_alias(mem_ctx, domain,
alias, name);
/* go ahead and allow the cache mgr to mark this in
negative cache */
if (!NT_STATUS_IS_OK(nt_status))
nt_status = NT_STATUS_NONE_MAPPED;
done:
return nt_status;
}
/**********************************************************************
*********************************************************************/
static NTSTATUS _nss_adex_close(void)
{
return NT_STATUS_NOT_IMPLEMENTED;
}
/**********************************************************************
*********************************************************************/
static struct idmap_methods adex_idmap_methods = {
.init = _idmap_adex_init,
.unixids_to_sids = _idmap_adex_get_sid_from_id,
.sids_to_unixids = _idmap_adex_get_id_from_sid,
.set_mapping = _idmap_adex_set_mapping,
.remove_mapping = _idmap_adex_remove_mapping,
.dump_data = _idmap_adex_dump,
.close_fn = _idmap_adex_close
};
static struct nss_info_methods adex_nss_methods = {
.init = _nss_adex_init,
.get_nss_info = _nss_adex_get_info,
.map_to_alias = _nss_adex_map_to_alias,
.map_from_alias = _nss_adex_map_from_alias,
.close_fn = _nss_adex_close
};
/**********************************************************************
Register with the idmap and idmap_nss subsystems. We have to protect
against the idmap and nss_info interfaces being in a half-registered
state.
**********************************************************************/
NTSTATUS idmap_adex_init(void)
{
static NTSTATUS idmap_status = NT_STATUS_UNSUCCESSFUL;
static NTSTATUS nss_status = NT_STATUS_UNSUCCESSFUL;
if (!NT_STATUS_IS_OK(idmap_status)) {
idmap_status =
smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
"adex", &adex_idmap_methods);
if (!NT_STATUS_IS_OK(idmap_status)) {
DEBUG(0,
("idmap_centeris_init: Failed to register the adex"
"idmap plugin.\n"));
return idmap_status;
}
}
if (!NT_STATUS_IS_OK(nss_status)) {
nss_status =
smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
"adex", &adex_nss_methods);
if (!NT_STATUS_IS_OK(nss_status)) {
DEBUG(0,
("idmap_adex_init: Failed to register the adex"
"nss plugin.\n"));
return nss_status;
}
}
return NT_STATUS_OK;
}
NTSTATUS nss_info_adex_init(void)
{
return idmap_adex_init();
}

View File

@ -0,0 +1,257 @@
/*
* idmap_centeris: Support for Local IDs and Centeris Cell Structure
*
* Copyright (C) Gerald (Jerry) Carter 2006-2008
*
* 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.
*/
#ifndef _IDMAP_ADEX_H
#define _IDMAP_ADEX_H
#include "winbindd/winbindd.h"
#define ADEX_CELL_RDN "$LikewiseIdentityCell"
#define ADEX_OC_USER "centerisLikewiseUser"
#define ADEX_OC_GROUP "centerisLikewiseGroup"
#define AD_USER "User"
#define AD_GROUP "Group"
#define ADEX_OC_POSIX_USER "posixAccount"
#define ADEX_OC_POSIX_GROUP "posixGroup"
#define ADEX_ATTR_UIDNUM "uidNumber"
#define ADEX_ATTR_GIDNUM "gidNUmber"
#define ADEX_ATTR_HOMEDIR "unixHomeDirectory"
#define ADEX_ATTR_USERPW "unixUserPassword"
#define ADEX_ATTR_GROUPALIAS "groupAlias" /* Not part of RFC2307 */
#define ADEX_ATTR_SHELL "loginShell"
#define ADEX_ATTR_GECOS "gecos"
#define ADEX_ATTR_UID "uid"
#define ADEX_ATTR_DISPLAYNAME "displayName"
#define MIN_ID_VALUE 100
#define BAIL_ON_NTSTATUS_ERROR(x) \
do { \
if (!NT_STATUS_IS_OK(x)) { \
DEBUG(10,("Failed! (%s)\n", nt_errstr(x))); \
goto done; \
} \
} \
while (0); \
#define WARN_ON_NTSTATUS_ERROR(x) \
do { \
if (!NT_STATUS_IS_OK(x)) { \
DEBUG(10,("Failure ignored! (%s)\n", nt_errstr(x))); \
} \
} \
while (0); \
#define BAIL_ON_ADS_ERROR(x) \
do { \
if (!ADS_ERR_OK(x)) { \
goto done; \
} \
} \
while (0);
#define BAIL_ON_PTR_ERROR(p, x) \
do { \
if ((p) == NULL ) { \
DEBUG(10,("NULL pointer!\n")); \
x = NT_STATUS_NO_MEMORY; \
goto done; \
} \
} while (0);
#define PRINT_NTSTATUS_ERROR(x, hdr, level) \
do { \
if (!NT_STATUS_IS_OK(x)) { \
DEBUG(level,("LWI ("hdr"): %s\n", nt_errstr(x))); \
} \
} while(0);
/*
* Cell Provider API
*/
struct cell_provider_api {
NTSTATUS(*get_sid_from_id) (DOM_SID * sid,
uint32_t id, enum id_type type);
NTSTATUS(*get_id_from_sid) (uint32_t * id,
enum id_type * type, const DOM_SID * sid);
NTSTATUS(*get_nss_info) (const DOM_SID * sid,
TALLOC_CTX * ctx,
char **homedir,
char **shell, char **gecos, gid_t * p_gid);
NTSTATUS(*map_to_alias) (TALLOC_CTX * mem_ctx,
const char *domain,
const char *name, char **alias);
NTSTATUS(*map_from_alias) (TALLOC_CTX * mem_ctx,
const char *domain,
const char *alias, char **name);
};
/* registered providers */
extern struct cell_provider_api ccp_unified;
extern struct cell_provider_api ccp_local;
#define LWCELL_FLAG_USE_RFC2307_ATTRS 0x00000001
#define LWCELL_FLAG_SEARCH_FOREST 0x00000002
#define LWCELL_FLAG_GC_CELL 0x00000004
#define LWCELL_FLAG_LOCAL_MODE 0x00000008
struct likewise_cell {
struct likewise_cell *prev, *next;
ADS_STRUCT *conn;
struct likewise_cell *gc_search_cell;
DOM_SID domain_sid;
char *dns_domain;
char *forest_name;
char *dn;
struct GUID *links; /* only held by owning cell */
size_t num_links;
uint32_t flags;
struct cell_provider_api *provider;
};
/* Search flags used for Global Catalog API */
#define ADEX_GC_SEARCH_CHECK_UNIQUE 0x00000001
struct gc_info {
struct gc_info *prev, *next;
char *forest_name;
char *search_base;
struct likewise_cell *forest_cell;
};
/* Available functions outside of idmap_lwidentity.c */
/* cell_util.c */
char *find_attr_string(char **list, size_t num_lines, const char *substr);
bool is_object_class(char **list, size_t num_lines, const char *substr);
int min_id_value(void);
char *cell_dn_to_dns(const char *dn);
NTSTATUS get_sid_type(ADS_STRUCT *ads,
LDAPMessage *msg,
enum lsa_SidType *type);
NTSTATUS cell_locate_membership(ADS_STRUCT * ads);
NTSTATUS cell_lookup_settings(struct likewise_cell * cell);
NTSTATUS cell_follow_links(struct likewise_cell *cell);
NTSTATUS cell_set_local_provider(void);
/* likewise_cell.c */
struct likewise_cell *cell_new(void);
struct likewise_cell *cell_list_head(void);
bool cell_list_add(struct likewise_cell *cell);
bool cell_list_remove(struct likewise_cell * cell);
void cell_list_destroy();
void cell_destroy(struct likewise_cell *c);
void cell_set_forest_searches(struct likewise_cell *c,
bool search);
void cell_set_dns_domain(struct likewise_cell *c,
const char *dns_domain);
void cell_set_connection(struct likewise_cell *c,
ADS_STRUCT *ads);
void cell_set_dn(struct likewise_cell *c,
const char *dn);
void cell_set_domain_sid(struct likewise_cell *c,
DOM_SID *sid);
void cell_set_flags(struct likewise_cell *c, uint32_t flags);
void cell_clear_flags(struct likewise_cell *c, uint32_t flags);
const char* cell_search_base(struct likewise_cell *c);
const char *cell_dns_domain(struct likewise_cell *c);
ADS_STRUCT *cell_connection(struct likewise_cell *c);
bool cell_search_forest(struct likewise_cell *c);
ADS_STATUS cell_do_search(struct likewise_cell *c,
const char *search_base,
int scope,
const char *expr,
const char **attrs,
LDAPMessage ** msg);
uint32_t cell_flags(struct likewise_cell *c);
NTSTATUS cell_connect_dn(struct likewise_cell **c,
const char *dn);
NTSTATUS cell_connect(struct likewise_cell *c);
/* gc_util.c */
NTSTATUS gc_init_list(void);
NTSTATUS gc_find_forest_root(struct gc_info *gc,
const char *domain);
struct gc_info *gc_search_start(void);
NTSTATUS gc_search_forest(struct gc_info *gc,
LDAPMessage **msg,
const char *filter);
NTSTATUS gc_search_all_forests(const char *filter,
ADS_STRUCT ***ads_list,
LDAPMessage ***msg_list,
int *num_resp, uint32_t flags);
NTSTATUS gc_search_all_forests_unique(const char *filter,
ADS_STRUCT **ads,
LDAPMessage **msg);
NTSTATUS gc_name_to_sid(const char *domain,
const char *name,
DOM_SID *sid,
enum lsa_SidType *sid_type);
NTSTATUS gc_sid_to_name(const DOM_SID *sid,
char **name,
enum lsa_SidType *sid_type);
NTSTATUS add_ads_result_to_array(ADS_STRUCT *ads,
LDAPMessage *msg,
ADS_STRUCT ***ads_list,
LDAPMessage ***msg_list,
int *size);
void free_result_array(ADS_STRUCT **ads_list,
LDAPMessage **msg_list,
int num_resp);
NTSTATUS check_result_unique(ADS_STRUCT *ads,
LDAPMessage *msg);
/* domain_util.c */
NTSTATUS domain_init_list(void);
NTSTATUS dc_search_domains(struct likewise_cell **cell,
LDAPMessage **msg,
const char *dn,
const DOM_SID *user_sid);
#endif /* _IDMAP_ADEX_H */

View File

@ -0,0 +1,425 @@
/*
* idmap_adex: Support for AD Forests
*
* Copyright (C) Gerald (Jerry) Carter 2006-2008
*
* 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 "idmap_adex.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_IDMAP
static struct likewise_cell *_lw_cell_list = NULL;
/**********************************************************************
Return the current HEAD of the list
*********************************************************************/
struct likewise_cell *cell_list_head(void)
{
return _lw_cell_list;
}
/**********************************************************************
*********************************************************************/
void cell_destroy(struct likewise_cell *c)
{
if (!c)
return;
if (c->conn)
ads_destroy(&c->conn);
talloc_destroy(c);
}
/**********************************************************************
Free all cell entries and reset the list head to NULL
*********************************************************************/
void cell_list_destroy(void)
{
struct likewise_cell *p = _lw_cell_list;
while (p) {
struct likewise_cell *q = p->next;
cell_destroy(p);
p = q;
}
_lw_cell_list = NULL;
return;
}
/**********************************************************************
Add a new cell structure to the list
*********************************************************************/
struct likewise_cell* cell_new(void)
{
struct likewise_cell *c;
/* Each cell struct is a TALLOC_CTX* */
c = TALLOC_ZERO_P(NULL, struct likewise_cell);
if (!c) {
DEBUG(0,("cell_new: memory allocation failure!\n"));
return NULL;
}
return c;
}
/**********************************************************************
Add a new cell structure to the list
*********************************************************************/
bool cell_list_add(struct likewise_cell * cell)
{
if (!cell) {
return false;
}
/* Always add to the end */
DLIST_ADD_END(_lw_cell_list, cell, struct likewise_cell *);
return true;
}
/**********************************************************************
Add a new cell structure to the list
*********************************************************************/
bool cell_list_remove(struct likewise_cell * cell)
{
if (!cell) {
return false;
}
/* Remove and drop the cell structure */
DLIST_REMOVE(_lw_cell_list, cell);
talloc_destroy(cell);
return true;
}
/**********************************************************************
Set the containing DNS domain for a cell
*********************************************************************/
void cell_set_dns_domain(struct likewise_cell *c, const char *dns_domain)
{
c->dns_domain = talloc_strdup(c, dns_domain);
}
/**********************************************************************
Set ADS connection for a cell
*********************************************************************/
void cell_set_connection(struct likewise_cell *c, ADS_STRUCT *ads)
{
c->conn = ads;
}
/**********************************************************************
*********************************************************************/
void cell_set_flags(struct likewise_cell *c, uint32_t flags)
{
c->flags |= flags;
}
/**********************************************************************
*********************************************************************/
void cell_clear_flags(struct likewise_cell *c, uint32_t flags)
{
c->flags &= ~flags;
}
/**********************************************************************
Set the Cell's DN
*********************************************************************/
void cell_set_dn(struct likewise_cell *c, const char *dn)
{
if ( c->dn) {
talloc_free(c->dn);
c->dn = NULL;
}
c->dn = talloc_strdup(c, dn);
}
/**********************************************************************
*********************************************************************/
void cell_set_domain_sid(struct likewise_cell *c, DOM_SID *sid)
{
sid_copy(&c->domain_sid, sid);
}
/*
* Query Routines
*/
/**********************************************************************
*********************************************************************/
const char* cell_search_base(struct likewise_cell *c)
{
if (!c)
return NULL;
return talloc_asprintf(c, "cn=%s,%s", ADEX_CELL_RDN, c->dn);
}
/**********************************************************************
*********************************************************************/
bool cell_search_forest(struct likewise_cell *c)
{
uint32_t test_flags = LWCELL_FLAG_SEARCH_FOREST;
return ((c->flags & test_flags) == test_flags);
}
/**********************************************************************
*********************************************************************/
uint32_t cell_flags(struct likewise_cell *c)
{
if (!c)
return 0;
return c->flags;
}
/**********************************************************************
*********************************************************************/
const char *cell_dns_domain(struct likewise_cell *c)
{
if (!c)
return NULL;
return c->dns_domain;
}
/**********************************************************************
*********************************************************************/
ADS_STRUCT *cell_connection(struct likewise_cell *c)
{
if (!c)
return NULL;
return c->conn;
}
/*
* Connection functions
*/
/********************************************************************
*******************************************************************/
NTSTATUS cell_connect(struct likewise_cell *c)
{
ADS_STRUCT *ads = NULL;
ADS_STATUS ads_status;
fstring dc_name;
struct sockaddr_storage dcip;
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
/* have to at least have the AD domain name */
if (!c->dns_domain) {
nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* clear out any old information */
if (c->conn) {
ads_destroy(&c->conn);
c->conn = NULL;
}
/* now setup the new connection */
ads = ads_init(c->dns_domain, NULL, NULL);
BAIL_ON_PTR_ERROR(ads, nt_status);
ads->auth.password =
secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
ads->auth.realm = SMB_STRDUP(lp_realm());
/* Make the connection. We should already have an initial
TGT using the machine creds */
if (cell_flags(c) & LWCELL_FLAG_GC_CELL) {
ads_status = ads_connect_gc(ads);
} else {
/* Set up server affinity for normal cells and the client
site name cache */
if (!get_dc_name("", c->dns_domain, dc_name, &dcip)) {
nt_status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
ads_status = ads_connect(ads);
}
c->conn = ads;
nt_status = ads_ntstatus(ads_status);
done:
if (!NT_STATUS_IS_OK(nt_status)) {
ads_destroy(&ads);
c->conn = NULL;
}
return nt_status;
}
/********************************************************************
*******************************************************************/
NTSTATUS cell_connect_dn(struct likewise_cell **c, const char *dn)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct likewise_cell *new_cell = NULL;
char *dns_domain = NULL;
if (*c || !dn) {
nt_status = NT_STATUS_INVALID_PARAMETER;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
if ((new_cell = cell_new()) == NULL) {
nt_status = NT_STATUS_NO_MEMORY;
BAIL_ON_NTSTATUS_ERROR(nt_status);
}
/* Set the DNS domain, dn, etc ... and add it to the list */
dns_domain = cell_dn_to_dns(dn);
cell_set_dns_domain(new_cell, dns_domain);
SAFE_FREE(dns_domain);
cell_set_dn(new_cell, dn);
nt_status = cell_connect(new_cell);
BAIL_ON_NTSTATUS_ERROR(nt_status);
*c = new_cell;
done:
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(1,("LWI: Failled to connect to cell \"%s\" (%s)\n",
dn ? dn : "NULL", nt_errstr(nt_status)));
talloc_destroy(new_cell);
}
return nt_status;
}
/********************************************************************
*******************************************************************/
#define MAX_SEARCH_COUNT 2
ADS_STATUS cell_do_search(struct likewise_cell *c,
const char *search_base,
int scope,
const char *expr,
const char **attrs,
LDAPMessage ** msg)
{
int search_count = 0;
ADS_STATUS status;
NTSTATUS nt_status;
/* check for a NULL connection */
if (!c->conn) {
nt_status = cell_connect(c);
if (!NT_STATUS_IS_OK(nt_status)) {
status = ADS_ERROR_NT(nt_status);
return status;
}
}
DEBUG(10, ("cell_do_search: Base = %s, Filter = %s, Scope = %d, GC = %s\n",
search_base, expr, scope,
c->conn->server.gc ? "yes" : "no"));
/* we try multiple times in case the ADS_STRUCT is bad
and we need to reconnect */
while (search_count < MAX_SEARCH_COUNT) {
*msg = NULL;
status = ads_do_search(c->conn, search_base,
scope, expr, attrs, msg);
if (ADS_ERR_OK(status)) {
return status;
}
DEBUG(5, ("cell_do_search: search[%d] failed (%s)\n",
search_count, ads_errstr(status)));
search_count++;
/* Houston, we have a problem */
if (status.error_type == ENUM_ADS_ERROR_LDAP) {
switch (status.err.rc) {
case LDAP_TIMELIMIT_EXCEEDED:
case LDAP_TIMEOUT:
case -1: /* we get this error if we cannot contact
the LDAP server */
nt_status = cell_connect(c);
if (!NT_STATUS_IS_OK(nt_status)) {
status = ADS_ERROR_NT(nt_status);
return status;
}
break;
default:
/* we're all done here */
return status;
}
}
}
DEBUG(5, ("cell_do_search: exceeded maximum search count!\n"));
return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
}

File diff suppressed because it is too large Load Diff