mirror of
https://github.com/samba-team/samba.git
synced 2025-01-19 10:03:58 +03:00
f804b590f9
to handle domain trusts. Jeremy and I talked about this and it's going in as working code. It keeps winbind clean and solves the trust problem with minimal changes. To summarize, there are 2 basic cases where the deadlock would occur. (1) lookuping up secondary groups for a user, and (2) get[gr|pw]nam() calls that fall through the NSS layer because they don't exist anywhere. o To handle case #1, we bypass winbindd in sys_getgrouplist() unless the username includes the 'winbind separator'. o Case #2 is handled by adding checks in winbindd to return failure if we are a DC and the domain matches our own. This code has been tested using basic share connections, domain logons, and with pam_winbind (both with and without 'winbind use default domain'). The 'trustdomain' auth module should work as well if an admin wants to manually create UNIX users for acounts in the trusted domains. Other misc fixes: * we need to fix check_ntlm_password() to be able to determine if an auth module is authoritative over a user (NT_STATUS_WRONG_PASSWORD, etc...). I worked around my specific situation, but this needs to be fixed. the winbindd auth module was causing delays. * fix named server mutex deadlock between trust domain auth module and winbindd looking up a uid * make sure SAM_ACCOUNT gets stored in the server_info struct for the _net_sam_logon() reply. Configuration details: The recommended method for supporting trusts is to use winbind. The gets us around some of the server mutex issues as well. * set 'files winbind' for passwd: and group: in /etc/nsswitch.conf * create domain trusts like normal * join winbind on the pdc to the Samba domain using 'net rpc join' * add normal parameters to smb.conf for winbind * set 'auth method = guest sam winbind' * start smbd, nmbd, & winbindd Problems that remain: * join a Windows 2k/XP box to a Samba domain. * create a 2-way trust between the Samba domain and an NT domain * logon to the windows client as a user from theh trusted domain * try to browse server in the trusted domain (or other workstations). an NT client seems to work ok, but 2k and XP either prompt for passwords or fail with errors. apparanently this never got tested since no one has ever been able to logon as a trusted user to a Samba domain from a Windows client.
-
994 lines
25 KiB
C
994 lines
25 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
|
|
Winbind daemon for ntdom nss module
|
|
|
|
Copyright (C) Tim Potter 2000
|
|
Copyright (C) Jeremy Allison 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"
|
|
|
|
#undef DBGC_CLASS
|
|
#define DBGC_CLASS DBGC_WINBIND
|
|
|
|
/***************************************************************
|
|
Empty static struct for negative caching.
|
|
****************************************************************/
|
|
|
|
/* Fill a grent structure from various other information */
|
|
|
|
static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
|
|
const char *gr_name, gid_t unix_gid)
|
|
{
|
|
fstring full_group_name;
|
|
/* Fill in uid/gid */
|
|
fill_domain_username(full_group_name, dom_name, gr_name);
|
|
|
|
gr->gr_gid = unix_gid;
|
|
|
|
/* Group name and password */
|
|
|
|
safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
|
|
safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
|
|
|
|
return True;
|
|
}
|
|
|
|
/* Fill in the group membership field of a NT group given by group_sid */
|
|
|
|
static BOOL fill_grent_mem(struct winbindd_domain *domain,
|
|
DOM_SID *group_sid,
|
|
enum SID_NAME_USE group_name_type,
|
|
int *num_gr_mem, char **gr_mem, int *gr_mem_len)
|
|
{
|
|
DOM_SID **sid_mem = NULL;
|
|
uint32 num_names = 0;
|
|
uint32 *name_types = NULL;
|
|
unsigned int buf_len, buf_ndx, i;
|
|
char **names = NULL, *buf;
|
|
BOOL result = False;
|
|
TALLOC_CTX *mem_ctx;
|
|
NTSTATUS status;
|
|
fstring sid_string;
|
|
|
|
if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
|
|
return False;
|
|
|
|
/* Initialise group membership information */
|
|
|
|
DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
|
|
|
|
*num_gr_mem = 0;
|
|
|
|
if ( !((group_name_type==SID_NAME_DOM_GRP) ||
|
|
((group_name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
|
|
{
|
|
DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
|
|
sid_to_string(sid_string, group_sid), domain->name,
|
|
group_name_type));
|
|
goto done;
|
|
}
|
|
|
|
/* Lookup group members */
|
|
status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
|
|
&sid_mem, &names, &name_types);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
|
|
sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
|
|
|
|
goto done;
|
|
}
|
|
|
|
DEBUG(10, ("looked up %d names\n", num_names));
|
|
|
|
if (DEBUGLEVEL >= 10) {
|
|
for (i = 0; i < num_names; i++)
|
|
DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
|
|
name_types[i]));
|
|
}
|
|
|
|
/* Add members to list */
|
|
|
|
buf = NULL;
|
|
buf_len = buf_ndx = 0;
|
|
|
|
again:
|
|
|
|
for (i = 0; i < num_names; i++) {
|
|
char *the_name;
|
|
fstring name;
|
|
int len;
|
|
|
|
the_name = names[i];
|
|
|
|
DEBUG(10, ("processing name %s\n", the_name));
|
|
|
|
/* FIXME: need to cope with groups within groups. These
|
|
occur in Universal groups on a Windows 2000 native mode
|
|
server. */
|
|
|
|
if (name_types[i] != SID_NAME_USER) {
|
|
DEBUG(3, ("name %s isn't a domain user\n", the_name));
|
|
continue;
|
|
}
|
|
|
|
/* Don't bother with machine accounts */
|
|
|
|
if (the_name[strlen(the_name) - 1] == '$') {
|
|
DEBUG(10, ("%s is machine account\n", the_name));
|
|
continue;
|
|
}
|
|
|
|
/* Append domain name */
|
|
|
|
fill_domain_username(name, domain->name, the_name);
|
|
|
|
len = strlen(name);
|
|
|
|
/* Add to list or calculate buffer length */
|
|
|
|
if (!buf) {
|
|
buf_len += len + 1; /* List is comma separated */
|
|
(*num_gr_mem)++;
|
|
DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
|
|
} else {
|
|
DEBUG(10, ("appending %s at ndx %d\n", name, len));
|
|
safe_strcpy(&buf[buf_ndx], name, len);
|
|
buf_ndx += len;
|
|
buf[buf_ndx] = ',';
|
|
buf_ndx++;
|
|
}
|
|
}
|
|
|
|
/* Allocate buffer */
|
|
|
|
if (!buf && buf_len != 0) {
|
|
if (!(buf = malloc(buf_len))) {
|
|
DEBUG(1, ("out of memory\n"));
|
|
result = False;
|
|
goto done;
|
|
}
|
|
memset(buf, 0, buf_len);
|
|
goto again;
|
|
}
|
|
|
|
if (buf && buf_ndx > 0) {
|
|
buf[buf_ndx - 1] = '\0';
|
|
}
|
|
|
|
*gr_mem = buf;
|
|
*gr_mem_len = buf_len;
|
|
|
|
DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
|
|
buf_len, *num_gr_mem ? buf : "NULL"));
|
|
result = True;
|
|
|
|
done:
|
|
|
|
talloc_destroy(mem_ctx);
|
|
|
|
DEBUG(10, ("fill_grent_mem returning %d\n", result));
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Return a group structure from a group name */
|
|
|
|
enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
|
|
{
|
|
DOM_SID group_sid;
|
|
struct winbindd_domain *domain;
|
|
enum SID_NAME_USE name_type;
|
|
fstring name_domain, name_group;
|
|
char *tmp, *gr_mem;
|
|
int gr_mem_len;
|
|
gid_t gid;
|
|
|
|
/* Ensure null termination */
|
|
state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
|
|
|
|
DEBUG(3, ("[%5d]: getgrnam %s\n", state->pid,
|
|
state->request.data.groupname));
|
|
|
|
/* Parse domain and groupname */
|
|
|
|
memset(name_group, 0, sizeof(fstring));
|
|
|
|
tmp = state->request.data.groupname;
|
|
if (!parse_domain_user(tmp, name_domain, name_group))
|
|
return WINBINDD_ERROR;
|
|
|
|
/* don't handle our own domain if we are a DC. This code handles cases where
|
|
the account doesn't exist anywhere and gets passed on down the NSS layer */
|
|
|
|
if ( ((lp_server_role() == ROLE_DOMAIN_PDC) || (lp_server_role()==ROLE_DOMAIN_BDC)) &&
|
|
strequal(name_domain, lp_workgroup()) )
|
|
{
|
|
DEBUG(7,("winbindd_getgrnam: rejecting getpwnam() for %s\\%s since I am on the PDC for this domain\n",
|
|
name_domain, name_group));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Get info for the domain */
|
|
|
|
if ((domain = find_domain_from_name(name_domain)) == NULL) {
|
|
DEBUG(0, ("could not get domain sid for domain %s\n",
|
|
name_domain));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Get rid and name type from name */
|
|
|
|
if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
|
|
&name_type)) {
|
|
DEBUG(1, ("group %s in domain %s does not exist\n",
|
|
name_group, name_domain));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
if ( !((name_type==SID_NAME_DOM_GRP) ||
|
|
((name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
|
|
{
|
|
DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
|
|
name_group, name_type));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
if (!NT_STATUS_IS_OK(sid_to_gid(&group_sid, &gid))) {
|
|
DEBUG(1, ("error converting unix gid to sid\n"));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
if (!fill_grent(&state->response.data.gr, name_domain,
|
|
name_group, gid) ||
|
|
!fill_grent_mem(domain, &group_sid, name_type,
|
|
&state->response.data.gr.num_gr_mem,
|
|
&gr_mem, &gr_mem_len)) {
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Group membership lives at start of extra data */
|
|
|
|
state->response.data.gr.gr_mem_ofs = 0;
|
|
|
|
state->response.length += gr_mem_len;
|
|
state->response.extra_data = gr_mem;
|
|
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
/* Return a group structure from a gid number */
|
|
|
|
enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
|
|
{
|
|
struct winbindd_domain *domain;
|
|
DOM_SID group_sid;
|
|
enum SID_NAME_USE name_type;
|
|
fstring dom_name;
|
|
fstring group_name;
|
|
int gr_mem_len;
|
|
char *gr_mem;
|
|
|
|
DEBUG(3, ("[%5d]: getgrgid %d\n", state->pid,
|
|
state->request.data.gid));
|
|
|
|
/* Bug out if the gid isn't in the winbind range */
|
|
|
|
if ((state->request.data.gid < server_state.gid_low) ||
|
|
(state->request.data.gid > server_state.gid_high))
|
|
return WINBINDD_ERROR;
|
|
|
|
/* Get rid from gid */
|
|
if (!NT_STATUS_IS_OK(gid_to_sid(&group_sid, state->request.data.gid))) {
|
|
DEBUG(1, ("could not convert gid %d to rid\n",
|
|
state->request.data.gid));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Get name from sid */
|
|
|
|
if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
|
|
DEBUG(1, ("could not lookup sid\n"));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Fill in group structure */
|
|
|
|
domain = find_domain_from_sid(&group_sid);
|
|
|
|
if (!domain) {
|
|
DEBUG(1,("Can't find domain from sid\n"));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
if ( !((name_type==SID_NAME_DOM_GRP) ||
|
|
((name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
|
|
{
|
|
DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
|
|
group_name, name_type));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
if (!fill_grent(&state->response.data.gr, dom_name, group_name,
|
|
state->request.data.gid) ||
|
|
!fill_grent_mem(domain, &group_sid, name_type,
|
|
&state->response.data.gr.num_gr_mem,
|
|
&gr_mem, &gr_mem_len))
|
|
return WINBINDD_ERROR;
|
|
|
|
/* Group membership lives at start of extra data */
|
|
|
|
state->response.data.gr.gr_mem_ofs = 0;
|
|
|
|
state->response.length += gr_mem_len;
|
|
state->response.extra_data = gr_mem;
|
|
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
/*
|
|
* set/get/endgrent functions
|
|
*/
|
|
|
|
/* "Rewind" file pointer for group database enumeration */
|
|
|
|
enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
|
|
{
|
|
struct winbindd_domain *domain;
|
|
|
|
DEBUG(3, ("[%5d]: setgrent\n", state->pid));
|
|
|
|
/* Check user has enabled this */
|
|
|
|
if (!lp_winbind_enum_groups())
|
|
return WINBINDD_ERROR;
|
|
|
|
/* Free old static data if it exists */
|
|
|
|
if (state->getgrent_state != NULL) {
|
|
free_getent_state(state->getgrent_state);
|
|
state->getgrent_state = NULL;
|
|
}
|
|
|
|
/* Create sam pipes for each domain we know about */
|
|
|
|
for (domain = domain_list(); domain != NULL; domain = domain->next) {
|
|
struct getent_state *domain_state;
|
|
|
|
/* Create a state record for this domain */
|
|
|
|
if ((domain_state = (struct getent_state *)
|
|
malloc(sizeof(struct getent_state))) == NULL) {
|
|
DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
ZERO_STRUCTP(domain_state);
|
|
|
|
fstrcpy(domain_state->domain_name, domain->name);
|
|
|
|
/* Add to list of open domains */
|
|
|
|
DLIST_ADD(state->getgrent_state, domain_state);
|
|
}
|
|
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
/* Close file pointer to ntdom group database */
|
|
|
|
enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
|
|
{
|
|
DEBUG(3, ("[%5d]: endgrent\n", state->pid));
|
|
|
|
free_getent_state(state->getgrent_state);
|
|
state->getgrent_state = NULL;
|
|
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
/* Get the list of domain groups and domain aliases for a domain. We fill in
|
|
the sam_entries and num_sam_entries fields with domain group information.
|
|
The dispinfo_ndx field is incremented to the index of the next group to
|
|
fetch. Return True if some groups were returned, False otherwise. */
|
|
|
|
#define MAX_FETCH_SAM_ENTRIES 100
|
|
|
|
static BOOL get_sam_group_entries(struct getent_state *ent)
|
|
{
|
|
NTSTATUS status;
|
|
uint32 num_entries;
|
|
struct acct_info *name_list = NULL, *tmp_name_list = NULL;
|
|
TALLOC_CTX *mem_ctx;
|
|
BOOL result = False;
|
|
struct acct_info *sam_grp_entries = NULL;
|
|
struct winbindd_domain *domain;
|
|
|
|
if (ent->got_sam_entries)
|
|
return False;
|
|
|
|
if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
|
|
ent->domain_name))) {
|
|
DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
|
|
return False;
|
|
}
|
|
|
|
/* Free any existing group info */
|
|
|
|
SAFE_FREE(ent->sam_entries);
|
|
ent->num_sam_entries = 0;
|
|
ent->got_sam_entries = True;
|
|
|
|
/* Enumerate domain groups */
|
|
|
|
num_entries = 0;
|
|
|
|
if (!(domain = find_domain_from_name(ent->domain_name))) {
|
|
DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
|
|
goto done;
|
|
}
|
|
|
|
/* always get the domain global groups */
|
|
|
|
status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
|
|
result = False;
|
|
goto done;
|
|
}
|
|
|
|
/* Copy entries into return buffer */
|
|
|
|
if (num_entries) {
|
|
if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
|
|
DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
|
|
num_entries));
|
|
result = False;
|
|
goto done;
|
|
}
|
|
memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
|
|
}
|
|
|
|
ent->num_sam_entries = num_entries;
|
|
|
|
/* get the domain local groups if we are a member of a native win2k domain
|
|
and are not using LDAP to get the groups */
|
|
|
|
if ( lp_security() != SEC_ADS && domain->native_mode
|
|
&& strequal(lp_workgroup(), domain->name) )
|
|
{
|
|
DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
|
|
|
|
status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
|
|
|
|
if ( !NT_STATUS_IS_OK(status) ) {
|
|
DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
|
|
num_entries = 0;
|
|
}
|
|
else
|
|
DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
|
|
|
|
/* Copy entries into return buffer */
|
|
|
|
if ( num_entries ) {
|
|
if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
|
|
{
|
|
DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
|
|
num_entries));
|
|
result = False;
|
|
SAFE_FREE( name_list );
|
|
goto done;
|
|
}
|
|
|
|
name_list = tmp_name_list;
|
|
|
|
memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
|
|
num_entries * sizeof(struct acct_info) );
|
|
}
|
|
|
|
ent->num_sam_entries += num_entries;
|
|
}
|
|
|
|
|
|
/* Fill in remaining fields */
|
|
|
|
ent->sam_entries = name_list;
|
|
ent->sam_entry_index = 0;
|
|
|
|
result = (ent->num_sam_entries > 0);
|
|
|
|
done:
|
|
talloc_destroy(mem_ctx);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Fetch next group entry from ntdom database */
|
|
|
|
#define MAX_GETGRENT_GROUPS 500
|
|
|
|
enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
|
|
{
|
|
struct getent_state *ent;
|
|
struct winbindd_gr *group_list = NULL;
|
|
int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
|
|
char *new_extra_data, *gr_mem_list = NULL;
|
|
|
|
DEBUG(3, ("[%5d]: getgrent\n", state->pid));
|
|
|
|
/* Check user has enabled this */
|
|
|
|
if (!lp_winbind_enum_groups())
|
|
return WINBINDD_ERROR;
|
|
|
|
num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
|
|
|
|
if ((state->response.extra_data =
|
|
malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
|
|
return WINBINDD_ERROR;
|
|
|
|
state->response.data.num_entries = 0;
|
|
|
|
group_list = (struct winbindd_gr *)state->response.extra_data;
|
|
|
|
if (!(ent = state->getgrent_state))
|
|
return WINBINDD_ERROR;
|
|
|
|
/* Start sending back groups */
|
|
|
|
for (i = 0; i < num_groups; i++) {
|
|
struct acct_info *name_list = NULL;
|
|
fstring domain_group_name;
|
|
uint32 result;
|
|
gid_t group_gid;
|
|
int gr_mem_len;
|
|
char *gr_mem, *new_gr_mem_list;
|
|
DOM_SID group_sid;
|
|
struct winbindd_domain *domain;
|
|
|
|
/* Do we need to fetch another chunk of groups? */
|
|
|
|
tryagain:
|
|
|
|
DEBUG(10, ("entry_index = %d, num_entries = %d\n",
|
|
ent->sam_entry_index, ent->num_sam_entries));
|
|
|
|
if (ent->num_sam_entries == ent->sam_entry_index) {
|
|
|
|
while(ent && !get_sam_group_entries(ent)) {
|
|
struct getent_state *next_ent;
|
|
|
|
DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
|
|
|
|
/* Free state information for this domain */
|
|
|
|
SAFE_FREE(ent->sam_entries);
|
|
|
|
next_ent = ent->next;
|
|
DLIST_REMOVE(state->getgrent_state, ent);
|
|
|
|
SAFE_FREE(ent);
|
|
ent = next_ent;
|
|
}
|
|
|
|
/* No more domains */
|
|
|
|
if (!ent)
|
|
break;
|
|
}
|
|
|
|
name_list = ent->sam_entries;
|
|
|
|
if (!(domain =
|
|
find_domain_from_name(ent->domain_name))) {
|
|
DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
|
|
result = False;
|
|
goto done;
|
|
}
|
|
|
|
/* Lookup group info */
|
|
|
|
sid_copy(&group_sid, &domain->sid);
|
|
sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
|
|
|
|
if (!NT_STATUS_IS_OK(sid_to_gid(&group_sid, &group_gid))) {
|
|
|
|
DEBUG(1, ("could not look up gid for group %s\n",
|
|
name_list[ent->sam_entry_index].acct_name));
|
|
|
|
ent->sam_entry_index++;
|
|
goto tryagain;
|
|
}
|
|
|
|
DEBUG(10, ("got gid %d for group %x\n", group_gid,
|
|
name_list[ent->sam_entry_index].rid));
|
|
|
|
/* Fill in group entry */
|
|
|
|
fill_domain_username(domain_group_name, ent->domain_name,
|
|
name_list[ent->sam_entry_index].acct_name);
|
|
|
|
result = fill_grent(&group_list[group_list_ndx],
|
|
ent->domain_name,
|
|
name_list[ent->sam_entry_index].acct_name,
|
|
group_gid);
|
|
|
|
/* Fill in group membership entry */
|
|
|
|
if (result) {
|
|
DOM_SID member_sid;
|
|
group_list[group_list_ndx].num_gr_mem = 0;
|
|
gr_mem = NULL;
|
|
gr_mem_len = 0;
|
|
|
|
/* Get group membership */
|
|
if (state->request.cmd == WINBINDD_GETGRLST) {
|
|
result = True;
|
|
} else {
|
|
sid_copy(&member_sid, &domain->sid);
|
|
sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
|
|
result = fill_grent_mem(
|
|
domain,
|
|
&member_sid,
|
|
SID_NAME_DOM_GRP,
|
|
&group_list[group_list_ndx].num_gr_mem,
|
|
&gr_mem, &gr_mem_len);
|
|
}
|
|
}
|
|
|
|
if (result) {
|
|
/* Append to group membership list */
|
|
new_gr_mem_list = Realloc(
|
|
gr_mem_list,
|
|
gr_mem_list_len + gr_mem_len);
|
|
|
|
if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
|
|
DEBUG(0, ("out of memory\n"));
|
|
SAFE_FREE(gr_mem_list);
|
|
gr_mem_list_len = 0;
|
|
break;
|
|
}
|
|
|
|
DEBUG(10, ("list_len = %d, mem_len = %d\n",
|
|
gr_mem_list_len, gr_mem_len));
|
|
|
|
gr_mem_list = new_gr_mem_list;
|
|
|
|
memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
|
|
gr_mem_len);
|
|
|
|
SAFE_FREE(gr_mem);
|
|
|
|
group_list[group_list_ndx].gr_mem_ofs =
|
|
gr_mem_list_len;
|
|
|
|
gr_mem_list_len += gr_mem_len;
|
|
}
|
|
|
|
ent->sam_entry_index++;
|
|
|
|
/* Add group to return list */
|
|
|
|
if (result) {
|
|
|
|
DEBUG(10, ("adding group num_entries = %d\n",
|
|
state->response.data.num_entries));
|
|
|
|
group_list_ndx++;
|
|
state->response.data.num_entries++;
|
|
|
|
state->response.length +=
|
|
sizeof(struct winbindd_gr);
|
|
|
|
} else {
|
|
DEBUG(0, ("could not lookup domain group %s\n",
|
|
domain_group_name));
|
|
}
|
|
}
|
|
|
|
/* Copy the list of group memberships to the end of the extra data */
|
|
|
|
if (group_list_ndx == 0)
|
|
goto done;
|
|
|
|
new_extra_data = Realloc(
|
|
state->response.extra_data,
|
|
group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
|
|
|
|
if (!new_extra_data) {
|
|
DEBUG(0, ("out of memory\n"));
|
|
group_list_ndx = 0;
|
|
SAFE_FREE(state->response.extra_data);
|
|
SAFE_FREE(gr_mem_list);
|
|
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
state->response.extra_data = new_extra_data;
|
|
|
|
memcpy(&((char *)state->response.extra_data)
|
|
[group_list_ndx * sizeof(struct winbindd_gr)],
|
|
gr_mem_list, gr_mem_list_len);
|
|
|
|
SAFE_FREE(gr_mem_list);
|
|
|
|
state->response.length += gr_mem_list_len;
|
|
|
|
DEBUG(10, ("returning %d groups, length = %d\n",
|
|
group_list_ndx, gr_mem_list_len));
|
|
|
|
/* Out of domains */
|
|
|
|
done:
|
|
|
|
return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
|
|
}
|
|
|
|
/* List domain groups without mapping to unix ids */
|
|
|
|
enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
|
|
{
|
|
uint32 total_entries = 0;
|
|
struct winbindd_domain *domain;
|
|
char *extra_data = NULL;
|
|
char *ted = NULL;
|
|
unsigned int extra_data_len = 0, i;
|
|
|
|
DEBUG(3, ("[%5d]: list groups\n", state->pid));
|
|
|
|
/* Enumerate over trusted domains */
|
|
|
|
for (domain = domain_list(); domain; domain = domain->next) {
|
|
struct getent_state groups;
|
|
|
|
ZERO_STRUCT(groups);
|
|
|
|
/* Get list of sam groups */
|
|
|
|
fstrcpy(groups.domain_name, domain->name);
|
|
|
|
get_sam_group_entries(&groups);
|
|
|
|
if (groups.num_sam_entries == 0) {
|
|
/* this domain is empty or in an error state */
|
|
continue;
|
|
}
|
|
|
|
/* keep track the of the total number of groups seen so
|
|
far over all domains */
|
|
total_entries += groups.num_sam_entries;
|
|
|
|
/* Allocate some memory for extra data. Note that we limit
|
|
account names to sizeof(fstring) = 128 characters. */
|
|
ted = Realloc(extra_data, sizeof(fstring) * total_entries);
|
|
|
|
if (!ted) {
|
|
DEBUG(0,("failed to enlarge buffer!\n"));
|
|
SAFE_FREE(extra_data);
|
|
return WINBINDD_ERROR;
|
|
} else
|
|
extra_data = ted;
|
|
|
|
/* Pack group list into extra data fields */
|
|
for (i = 0; i < groups.num_sam_entries; i++) {
|
|
char *group_name = ((struct acct_info *)
|
|
groups.sam_entries)[i].acct_name;
|
|
fstring name;
|
|
|
|
fill_domain_username(name, domain->name, group_name);
|
|
/* Append to extra data */
|
|
memcpy(&extra_data[extra_data_len], name,
|
|
strlen(name));
|
|
extra_data_len += strlen(name);
|
|
extra_data[extra_data_len++] = ',';
|
|
}
|
|
|
|
free(groups.sam_entries);
|
|
}
|
|
|
|
/* Assign extra_data fields in response structure */
|
|
if (extra_data) {
|
|
extra_data[extra_data_len - 1] = '\0';
|
|
state->response.extra_data = extra_data;
|
|
state->response.length += extra_data_len;
|
|
}
|
|
|
|
/* No domains may have responded but that's still OK so don't
|
|
return an error. */
|
|
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
/* Get user supplementary groups. This is much quicker than trying to
|
|
invert the groups database. We merge the groups from the gids and
|
|
other_sids info3 fields as trusted domain, universal group
|
|
memberships, and nested groups (win2k native mode only) are not
|
|
returned by the getgroups RPC call but are present in the info3. */
|
|
|
|
enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
|
|
{
|
|
fstring name_domain, name_user;
|
|
DOM_SID user_sid, group_sid;
|
|
enum SID_NAME_USE name_type;
|
|
uint32 num_groups = 0;
|
|
uint32 num_gids = 0;
|
|
NTSTATUS status;
|
|
DOM_SID **user_grpsids;
|
|
struct winbindd_domain *domain;
|
|
enum winbindd_result result = WINBINDD_ERROR;
|
|
gid_t *gid_list;
|
|
unsigned int i;
|
|
TALLOC_CTX *mem_ctx;
|
|
NET_USER_INFO_3 *info3 = NULL;
|
|
|
|
/* Ensure null termination */
|
|
state->request.data.username[sizeof(state->request.data.username)-1]='\0';
|
|
|
|
DEBUG(3, ("[%5d]: getgroups %s\n", state->pid,
|
|
state->request.data.username));
|
|
|
|
if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
|
|
state->request.data.username)))
|
|
return WINBINDD_ERROR;
|
|
|
|
/* Parse domain and username */
|
|
|
|
if (!parse_domain_user(state->request.data.username, name_domain,
|
|
name_user))
|
|
goto done;
|
|
|
|
/* Get info for the domain */
|
|
|
|
if ((domain = find_domain_from_name(name_domain)) == NULL) {
|
|
DEBUG(0, ("could not find domain entry for domain %s\n",
|
|
name_domain));
|
|
goto done;
|
|
}
|
|
|
|
/* Get rid and name type from name. The following costs 1 packet */
|
|
|
|
if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid,
|
|
&name_type)) {
|
|
DEBUG(1, ("user '%s' does not exist\n", name_user));
|
|
goto done;
|
|
}
|
|
|
|
if (name_type != SID_NAME_USER) {
|
|
DEBUG(1, ("name '%s' is not a user name: %d\n",
|
|
name_user, name_type));
|
|
goto done;
|
|
}
|
|
|
|
/* Treat the info3 cache as authoritative as the
|
|
lookup_usergroups() function may return cached data. */
|
|
|
|
if ((info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
|
|
|
|
DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
|
|
info3->num_groups2, info3->num_other_sids));
|
|
|
|
num_groups = info3->num_other_sids + info3->num_groups2;
|
|
gid_list = calloc(sizeof(gid_t), num_groups);
|
|
|
|
/* Go through each other sid and convert it to a gid */
|
|
|
|
for (i = 0; i < info3->num_other_sids; i++) {
|
|
fstring name;
|
|
fstring dom_name;
|
|
enum SID_NAME_USE sid_type;
|
|
|
|
/* Is this sid known to us? It can either be
|
|
a trusted domain sid or a foreign sid. */
|
|
|
|
if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
|
|
dom_name, name, &sid_type))
|
|
{
|
|
DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
|
|
sid_string_static(&info3->other_sids[i].sid)));
|
|
continue;
|
|
}
|
|
|
|
/* Check it is a domain group or an alias (domain local group)
|
|
in a win2k native mode domain. */
|
|
|
|
if ( !((sid_type==SID_NAME_DOM_GRP) ||
|
|
((sid_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
|
|
{
|
|
DEBUG(10, ("winbindd_getgroups: sid type %d "
|
|
"for %s is not a domain group\n",
|
|
sid_type,
|
|
sid_string_static(
|
|
&info3->other_sids[i].sid)));
|
|
continue;
|
|
}
|
|
|
|
/* Map to a gid */
|
|
|
|
if (!NT_STATUS_IS_OK(sid_to_gid(&info3->other_sids[i].sid,
|
|
&gid_list[num_gids])) )
|
|
{
|
|
DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
|
|
sid_string_static(&info3->other_sids[i].sid)));
|
|
continue;
|
|
}
|
|
|
|
/* We've jumped through a lot of hoops to get here */
|
|
|
|
DEBUG(10, ("winbindd_getgroups: mapped other sid %s to "
|
|
"gid %d\n", sid_string_static(
|
|
&info3->other_sids[i].sid),
|
|
gid_list[num_gids]));
|
|
|
|
num_gids++;
|
|
}
|
|
|
|
for (i = 0; i < info3->num_groups2; i++) {
|
|
|
|
/* create the group SID */
|
|
|
|
sid_copy( &group_sid, &domain->sid );
|
|
sid_append_rid( &group_sid, info3->gids[i].g_rid );
|
|
|
|
if (!NT_STATUS_IS_OK(sid_to_gid(&group_sid, &gid_list[num_gids])) ) {
|
|
DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
|
|
sid_string_static(&group_sid)));
|
|
}
|
|
|
|
num_gids++;
|
|
}
|
|
|
|
SAFE_FREE(info3);
|
|
|
|
} else {
|
|
status = domain->methods->lookup_usergroups(domain, mem_ctx,
|
|
&user_sid, &num_groups,
|
|
&user_grpsids);
|
|
if (!NT_STATUS_IS_OK(status))
|
|
goto done;
|
|
|
|
gid_list = malloc(sizeof(gid_t) * num_groups);
|
|
|
|
if (state->response.extra_data)
|
|
goto done;
|
|
|
|
for (i = 0; i < num_groups; i++) {
|
|
if (!NT_STATUS_IS_OK(sid_to_gid(user_grpsids[i], &gid_list[num_gids]))) {
|
|
DEBUG(1, ("unable to convert group sid %s to gid\n",
|
|
sid_string_static(user_grpsids[i])));
|
|
continue;
|
|
}
|
|
num_gids++;
|
|
}
|
|
}
|
|
|
|
/* Send data back to client */
|
|
|
|
state->response.data.num_entries = num_gids;
|
|
state->response.extra_data = gid_list;
|
|
state->response.length += num_gids * sizeof(gid_t);
|
|
|
|
result = WINBINDD_OK;
|
|
|
|
done:
|
|
|
|
talloc_destroy(mem_ctx);
|
|
|
|
return result;
|
|
}
|