Copyright (C) Tim Potter 2000
Copyright (C) Jeremy Allison 2001.
+ Copyright (C) Gerald (Jerry) Carter 2003.
+ Copyright (C) Volker Lendecke 2005
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
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "includes.h"
#include "winbindd.h"
+extern BOOL opt_nocache;
+
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_WINBIND
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);
+
+ fill_domain_username( full_group_name, dom_name, gr_name, False);
gr->gr_gid = unix_gid;
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)
+ size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
{
- DOM_SID **sid_mem = NULL;
+ DOM_SID *sid_mem = NULL;
uint32 num_names = 0;
uint32 *name_types = NULL;
unsigned int buf_len, buf_ndx, i;
DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
*num_gr_mem = 0;
+
+ /* HACK ALERT!! This whole routine does not cope with group members
+ * from more than one domain, ie aliases. Thus we have to work it out
+ * ourselves in a special routine. */
+
+ if (domain->internal)
+ return fill_passdb_alias_grmem(domain, group_sid,
+ num_gr_mem,
+ gr_mem, gr_mem_len);
- if (group_name_type != SID_NAME_DOM_GRP) {
- DEBUG(1, ("SID %s in domain %s isn't a domain group\n",
- sid_to_string(sid_string, group_sid), domain->name));
+ if ( !((group_name_type==SID_NAME_DOM_GRP) ||
+ ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
+ {
+ 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;
}
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]),
+ DEBUG(10, ("\t%20s %s %d\n", names[i],
+ sid_string_static(&sid_mem[i]),
name_types[i]));
}
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;
- }
+ /* make sure to allow machine accounts */
- /* Don't bother with machine accounts */
-
- if (the_name[strlen(the_name) - 1] == '$') {
- DEBUG(10, ("%s is machine account\n", the_name));
+ if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
+ DEBUG(3, ("name %s isn't a domain user (%s)\n", the_name, sid_type_lookup(name_types[i])));
continue;
}
/* Append domain name */
- fill_domain_username(name, domain->name, the_name);
+ fill_domain_username(name, domain->name, the_name, False);
len = strlen(name);
/* Allocate buffer */
if (!buf && buf_len != 0) {
- if (!(buf = malloc(buf_len))) {
+ if (!(buf = SMB_MALLOC(buf_len))) {
DEBUG(1, ("out of memory\n"));
result = False;
goto done;
/* Return a group structure from a group name */
-enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
+void winbindd_getgrnam(struct winbindd_cli_state *state)
{
- DOM_SID group_sid;
+ DOM_SID group_sid, tmp_sid;
+ uint32 grp_rid;
struct winbindd_domain *domain;
enum SID_NAME_USE name_type;
fstring name_domain, name_group;
char *tmp, *gr_mem;
+ size_t gr_mem_len;
gid_t gid;
- int gr_mem_len;
+ union unid_t id;
+ NTSTATUS status;
/* Ensure null termination */
state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
- DEBUG(3, ("[%5d]: getgrnam %s\n", state->pid,
+ DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)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;
-
- /* fail if we are a PDC and this is our domain; should be done by passdb */
- if ( lp_server_role() == ROLE_DOMAIN_PDC && 0==StrCaseCmp( domain->name, lp_workgroup()) )
- return WINBINDD_ERROR;
+ parse_domain_user(tmp, name_domain, name_group);
+
+ /* if no domain or our local domain and no local tdb group, default to
+ * our local domain for aliases */
+
+ if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
+ fstrcpy(name_domain, get_global_sam_name());
+ }
/* 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",
+ DEBUG(3, ("could not get domain sid for domain %s\n",
name_domain));
- return WINBINDD_ERROR;
+ request_error(state);
+ return;
+ }
+ /* should we deal with users for our domain? */
+
+ if ( lp_winbind_trusted_domains_only() && domain->primary) {
+ DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
+ "getgrnam() for %s\\%s.\n", name_domain, name_group));
+ request_error(state);
+ return;
}
/* Get rid and name type from name */
- if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
- &name_type)) {
+ if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, domain->name,
+ name_group, &group_sid, &name_type)) {
DEBUG(1, ("group %s in domain %s does not exist\n",
name_group, name_domain));
- return WINBINDD_ERROR;
+ request_error(state);
+ return;
}
- if ((name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_DOM_GRP)) {
- DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
- name_group, name_type));
- return WINBINDD_ERROR;
+ if ( !((name_type==SID_NAME_DOM_GRP) ||
+ ((name_type==SID_NAME_ALIAS) && domain->primary) ||
+ ((name_type==SID_NAME_ALIAS) && domain->internal) ||
+ ((name_type==SID_NAME_WKN_GRP) && domain->internal)) )
+ {
+ DEBUG(1, ("name '%s' is not a local, domain or builtin "
+ "group: %d\n", name_group, name_type));
+ request_error(state);
+ return;
}
- if (!winbindd_idmap_get_gid_from_sid(&group_sid, &gid)) {
- DEBUG(1, ("error converting unix gid to sid\n"));
- return WINBINDD_ERROR;
+ /* Make sure that the group SID is within the domain of the
+ original domain */
+
+ sid_copy( &tmp_sid, &group_sid );
+ sid_split_rid( &tmp_sid, &grp_rid );
+ if ( !sid_equal( &tmp_sid, &domain->sid ) ) {
+ DEBUG(3,("winbindd_getgrnam: group %s resolves to a SID in the wrong domain [%s]\n",
+ state->request.data.groupname, sid_string_static(&group_sid)));
+ request_error(state);
+ return;
+ }
+
+
+
+ /* Try to get the GID */
+
+ status = idmap_sid_to_gid(&group_sid, &gid, 0);
+
+ if (NT_STATUS_IS_OK(status)) {
+ goto got_gid;
}
+ /* Maybe it's one of our aliases in passdb */
+
+ if (pdb_sid_to_id(&group_sid, &id, &name_type) &&
+ ((name_type == SID_NAME_ALIAS) ||
+ (name_type == SID_NAME_WKN_GRP))) {
+ gid = id.gid;
+ goto got_gid;
+ }
+
+ DEBUG(1, ("error converting unix gid to sid\n"));
+ request_error(state);
+ return;
+
+ got_gid:
+
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;
+ request_error(state);
+ return;
}
/* 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;
+ state->response.extra_data.data = gr_mem;
+ request_ok(state);
}
/* Return a group structure from a gid number */
-enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
+void 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;
+ size_t gr_mem_len;
char *gr_mem;
+ NTSTATUS status;
- DEBUG(3, ("[%5d]: getgrgid %d\n", state->pid,
- state->request.data.gid));
+ DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
+ (unsigned long)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;
+ (state->request.data.gid > server_state.gid_high)) {
+ request_error(state);
+ return;
+ }
- /* Get rid from gid */
+ /* Get sid from gid */
- if (!winbindd_idmap_get_sid_from_gid(state->request.data.gid, &group_sid)) {
- DEBUG(1, ("could not convert gid %d to rid\n",
- state->request.data.gid));
- return WINBINDD_ERROR;
+ status = idmap_gid_to_sid(&group_sid, state->request.data.gid, 0);
+ if (NT_STATUS_IS_OK(status)) {
+ /* This is a remote one */
+ goto got_sid;
}
- /* Get name from sid */
+ /* Ok, this might be "ours", i.e. an alias */
- if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
- DEBUG(1, ("could not lookup sid\n"));
- return WINBINDD_ERROR;
+ if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
+ lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
+ (name_type == SID_NAME_ALIAS)) {
+ /* Hey, got an alias */
+ goto got_sid;
}
- if (!((name_type == SID_NAME_ALIAS) ||
- (name_type == SID_NAME_DOM_GRP))) {
- DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
- group_name, name_type));
- return WINBINDD_ERROR;
+ DEBUG(1, ("could not convert gid %lu to sid\n",
+ (unsigned long)state->request.data.gid));
+ request_error(state);
+ return;
+
+ got_sid:
+ /* Get name from sid */
+
+ if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, dom_name,
+ group_name, &name_type)) {
+ DEBUG(1, ("could not lookup sid\n"));
+ request_error(state);
+ return;
}
/* Fill in group structure */
- domain = find_domain_from_sid(&group_sid);
+ domain = find_domain_from_sid_noinit(&group_sid);
if (!domain) {
DEBUG(1,("Can't find domain from sid\n"));
- return WINBINDD_ERROR;
+ request_error(state);
+ return;
+ }
+
+ if ( !((name_type==SID_NAME_DOM_GRP) ||
+ ((name_type==SID_NAME_ALIAS) && domain->primary) ||
+ ((name_type==SID_NAME_ALIAS) && domain->internal)) )
+ {
+ DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
+ group_name, name_type));
+ request_error(state);
+ return;
}
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;
+ &gr_mem, &gr_mem_len)) {
+ request_error(state);
+ return;
+ }
/* 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;
+ state->response.extra_data.data = gr_mem;
+ request_ok(state);
}
/*
/* "Rewind" file pointer for group database enumeration */
-enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
+static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state)
{
struct winbindd_domain *domain;
- DEBUG(3, ("[%5d]: setgrent\n", state->pid));
+ DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
/* Check user has enabled this */
- if (!lp_winbind_enum_groups())
- return WINBINDD_ERROR;
+ if (!lp_winbind_enum_groups()) {
+ return False;
+ }
/* Free old static data if it exists */
struct getent_state *domain_state;
/* Create a state record for this domain */
+
+ /* don't add our domaina if we are a PDC or if we
+ are a member of a Samba domain */
+
+ if ( lp_winbind_trusted_domains_only() && domain->primary )
+ {
+ continue;
+ }
+
- if ((domain_state = (struct getent_state *)
- malloc(sizeof(struct getent_state))) == NULL) {
+ if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
- return WINBINDD_ERROR;
+ return False;
}
ZERO_STRUCTP(domain_state);
DLIST_ADD(state->getgrent_state, domain_state);
}
- return WINBINDD_OK;
+ state->getgrent_initialized = True;
+ return True;
+}
+
+void winbindd_setgrent(struct winbindd_cli_state *state)
+{
+ if (winbindd_setgrent_internal(state)) {
+ request_ok(state);
+ } else {
+ request_error(state);
+ }
}
/* Close file pointer to ntdom group database */
-enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
+void winbindd_endgrent(struct winbindd_cli_state *state)
{
- DEBUG(3, ("[%5d]: endgrent\n", state->pid));
+ DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
free_getent_state(state->getgrent_state);
+ state->getgrent_initialized = False;
state->getgrent_state = NULL;
-
- return WINBINDD_OK;
+ request_ok(state);
}
/* Get the list of domain groups and domain aliases for a domain. We fill in
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;
+ struct acct_info *name_list = NULL;
TALLOC_CTX *mem_ctx;
BOOL result = False;
struct acct_info *sam_grp_entries = NULL;
if (ent->got_sam_entries)
return False;
-
- if ( lp_server_role() == ROLE_DOMAIN_PDC && 0==StrCaseCmp(lp_workgroup(), ent->domain_name))
- return False;
if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
ent->domain_name))) {
/* Copy entries into return buffer */
if (num_entries) {
- if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
+ if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
num_entries));
result = False;
ent->num_sam_entries = num_entries;
- /* get the domain local groups if we are a member of
- a native win2k domain */
+ /* 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 ( domain->native_mode && domain->methods->enum_local_groups )
+ if ( ( lp_security() != SEC_ADS && domain->native_mode
+ && domain->primary) || domain->internal )
{
- DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
+ DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
+ domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_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))) )
+ if ( !(name_list = SMB_REALLOC_ARRAY( name_list, 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) );
}
#define MAX_GETGRENT_GROUPS 500
-enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
+void 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;
+ char *gr_mem_list = NULL;
- DEBUG(3, ("[%5d]: getgrent\n", state->pid));
+ DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
/* Check user has enabled this */
- if (!lp_winbind_enum_groups())
- return WINBINDD_ERROR;
+ if (!lp_winbind_enum_groups()) {
+ request_error(state);
+ return;
+ }
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;
+ if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
+ request_error(state);
+ return;
+ }
+
+ memset(state->response.extra_data.data, '\0',
+ num_groups * sizeof(struct winbindd_gr) );
state->response.data.num_entries = 0;
- group_list = (struct winbindd_gr *)state->response.extra_data;
+ group_list = (struct winbindd_gr *)state->response.extra_data.data;
- if (!(ent = state->getgrent_state))
- return WINBINDD_ERROR;
+ if (!state->getgrent_initialized)
+ winbindd_setgrent_internal(state);
+
+ if (!(ent = state->getgrent_state)) {
+ request_error(state);
+ return;
+ }
/* Start sending back groups */
fstring domain_group_name;
uint32 result;
gid_t group_gid;
- int gr_mem_len;
- char *gr_mem, *new_gr_mem_list;
+ size_t gr_mem_len;
+ char *gr_mem;
DOM_SID group_sid;
struct winbindd_domain *domain;
sid_copy(&group_sid, &domain->sid);
sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
- if (!winbindd_idmap_get_gid_from_sid(
- &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;
+ if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid,
+ &group_gid, 0))) {
+ union unid_t id;
+ enum SID_NAME_USE type;
+
+ DEBUG(10, ("SID %s not in idmap\n",
+ sid_string_static(&group_sid)));
+
+ if (!pdb_sid_to_id(&group_sid, &id, &type)) {
+ 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;
+ }
+
+ if ((type != SID_NAME_DOM_GRP) &&
+ (type != SID_NAME_ALIAS) &&
+ (type != SID_NAME_WKN_GRP)) {
+ DEBUG(1, ("Group %s is a %s, not a group\n",
+ sid_type_lookup(type),
+ name_list[ent->sam_entry_index].acct_name));
+ ent->sam_entry_index++;
+ goto tryagain;
+ }
+ group_gid = id.gid;
}
- DEBUG(10, ("got gid %d for group %x\n", group_gid,
- name_list[ent->sam_entry_index].rid));
+ DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
+ (unsigned long)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);
+ name_list[ent->sam_entry_index].acct_name, False);
result = fill_grent(&group_list[group_list_ndx],
ent->domain_name,
if (result) {
/* Append to group membership list */
- new_gr_mem_list = Realloc(
- gr_mem_list,
- gr_mem_list_len + gr_mem_len);
+ gr_mem_list = SMB_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)) {
+ if (!gr_mem_list) {
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);
if (group_list_ndx == 0)
goto done;
- new_extra_data = Realloc(
- state->response.extra_data,
+ state->response.extra_data.data = SMB_REALLOC(
+ state->response.extra_data.data,
group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
- if (!new_extra_data) {
+ if (!state->response.extra_data.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;
+ request_error(state);
+ return;
}
- state->response.extra_data = new_extra_data;
-
- memcpy(&((char *)state->response.extra_data)
+ memcpy(&((char *)state->response.extra_data.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",
done:
- return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
+ SAFE_FREE(gr_mem_list);
+
+ if (group_list_ndx > 0)
+ request_ok(state);
+ else
+ request_error(state);
}
/* List domain groups without mapping to unix ids */
-enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
+void winbindd_list_groups(struct winbindd_cli_state *state)
{
uint32 total_entries = 0;
struct winbindd_domain *domain;
+ const char *which_domain;
char *extra_data = NULL;
- char *ted = NULL;
unsigned int extra_data_len = 0, i;
- DEBUG(3, ("[%5d]: list groups\n", state->pid));
+ DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
+ /* Ensure null termination */
+ state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
+ which_domain = state->request.domain_name;
+
/* Enumerate over trusted domains */
for (domain = domain_list(); domain; domain = domain->next) {
struct getent_state groups;
-
- /* fail if we are a PDC and this is our domain; should be done by passdb */
-
- if ( lp_server_role() == ROLE_DOMAIN_PDC && 0==StrCaseCmp( domain->name, lp_workgroup()) )
- continue;
+ /* if we have a domain name restricting the request and this
+ one in the list doesn't match, then just bypass the remainder
+ of the loop */
+
+ if ( *which_domain && !strequal(which_domain, domain->name) )
+ continue;
+
ZERO_STRUCT(groups);
/* Get list of sam groups */
- ZERO_STRUCT(groups);
+
fstrcpy(groups.domain_name, domain->name);
get_sam_group_entries(&groups);
/* 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);
+ extra_data = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
- if (!ted) {
+ if (!extra_data) {
DEBUG(0,("failed to enlarge buffer!\n"));
- SAFE_FREE(extra_data);
- return WINBINDD_ERROR;
- } else
- extra_data = ted;
+ request_error(state);
+ return;
+ }
/* Pack group list into extra data fields */
for (i = 0; i < groups.num_sam_entries; i++) {
groups.sam_entries)[i].acct_name;
fstring name;
- fill_domain_username(name, domain->name, group_name);
+ fill_domain_username(name, domain->name, group_name, False);
/* Append to extra data */
memcpy(&extra_data[extra_data_len], name,
strlen(name));
extra_data[extra_data_len++] = ',';
}
- free(groups.sam_entries);
+ SAFE_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.extra_data.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;
+ request_ok(state);
}
/* Get user supplementary groups. This is much quicker than trying to
- invert the groups database. */
+ 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;
- enum SID_NAME_USE name_type;
- uint32 num_groups, num_gids;
- NTSTATUS status;
- DOM_SID **user_gids;
+struct getgroups_state {
+ struct winbindd_cli_state *state;
struct winbindd_domain *domain;
- enum winbindd_result result = WINBINDD_ERROR;
- gid_t *gid_list;
- unsigned int i;
- TALLOC_CTX *mem_ctx;
-
+ char *domname;
+ char *username;
+ DOM_SID user_sid;
+
+ const DOM_SID *token_sids;
+ size_t i, num_token_sids;
+
+ gid_t *token_gids;
+ size_t num_token_gids;
+};
+
+static void getgroups_usersid_recv(void *private_data, BOOL success,
+ const DOM_SID *sid, enum SID_NAME_USE type);
+static void getgroups_tokensids_recv(void *private_data, BOOL success,
+ DOM_SID *token_sids, size_t num_token_sids);
+static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
+
+void winbindd_getgroups(struct winbindd_cli_state *state)
+{
+ struct getgroups_state *s;
+
/* Ensure null termination */
- state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+ state->request.data.username
+ [sizeof(state->request.data.username)-1]='\0';
- DEBUG(3, ("[%5d]: getgroups %s\n", state->pid,
+ DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)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;
+ s = TALLOC_P(state->mem_ctx, struct getgroups_state);
+ if (s == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ request_error(state);
+ return;
+ }
- /* fail if we are a PDC and this is our domain; should be done by passdb */
+ s->state = state;
+
+ if (!parse_domain_user_talloc(state->mem_ctx,
+ state->request.data.username,
+ &s->domname, &s->username)) {
+ DEBUG(5, ("Could not parse domain user: %s\n",
+ state->request.data.username));
+
+ /* error out if we do not have nested group support */
+
+ if ( !lp_winbind_nested_groups() ) {
+ request_error(state);
+ return;
+ }
+
+ s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
+ s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
+ }
- if ( lp_server_role() == ROLE_DOMAIN_PDC && 0==StrCaseCmp( name_domain, lp_workgroup()) )
- return WINBINDD_ERROR;
-
/* 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;
+
+ s->domain = find_domain_from_name_noinit(s->domname);
+
+ if (s->domain == NULL) {
+ DEBUG(7, ("could not find domain entry for domain %s\n",
+ s->domname));
+ request_error(state);
+ return;
}
+ if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
+ DEBUG(7,("winbindd_getpwnam: My domain -- rejecting "
+ "getgroups() for %s\\%s.\n", s->domname,
+ s->username));
+ request_error(state);
+ return;
+ }
+
/* 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;
+ winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
+ getgroups_usersid_recv, s);
+}
+
+static void getgroups_usersid_recv(void *private_data, BOOL success,
+ const DOM_SID *sid, enum SID_NAME_USE type)
+{
+ struct getgroups_state *s = private_data;
+
+ if ((!success) ||
+ ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
+ request_error(s->state);
+ return;
}
- if (name_type != SID_NAME_USER) {
- DEBUG(1, ("name '%s' is not a user name: %d\n",
- name_user, name_type));
- goto done;
+ sid_copy(&s->user_sid, sid);
+
+ winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
+ getgroups_tokensids_recv, s);
+}
+
+static void getgroups_tokensids_recv(void *private_data, BOOL success,
+ DOM_SID *token_sids, size_t num_token_sids)
+{
+ struct getgroups_state *s = private_data;
+
+ /* We need at least the user sid and the primary group in the token,
+ * otherwise it's an error */
+
+ if ((!success) || (num_token_sids < 2)) {
+ request_error(s->state);
+ return;
}
- status = domain->methods->lookup_usergroups(domain, mem_ctx,
- &user_sid, &num_groups,
- &user_gids);
- if (!NT_STATUS_IS_OK(status)) goto done;
+ s->token_sids = token_sids;
+ s->num_token_sids = num_token_sids;
+ s->i = 0;
+
+ s->token_gids = NULL;
+ s->num_token_gids = 0;
- /* Copy data back to client */
+ getgroups_sid2gid_recv(s, False, 0);
+}
- num_gids = 0;
- gid_list = malloc(sizeof(gid_t) * num_groups);
+static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
+{
+ struct getgroups_state *s = private_data;
- if (state->response.extra_data)
- goto done;
+ if (success)
+ add_gid_to_array_unique(NULL, gid,
+ &s->token_gids,
+ &s->num_token_gids);
- for (i = 0; i < num_groups; i++) {
- if (!winbindd_idmap_get_gid_from_sid(
- user_gids[i],
- &gid_list[num_gids])) {
- fstring sid_string;
+ if (s->i < s->num_token_sids) {
+ const DOM_SID *sid = &s->token_sids[s->i];
+ s->i += 1;
- DEBUG(1, ("unable to convert group sid %s to gid\n",
- sid_to_string(sid_string, user_gids[i])));
- continue;
+ if (sid_equal(sid, &s->user_sid)) {
+ getgroups_sid2gid_recv(s, False, 0);
+ return;
}
-
- num_gids++;
+
+ winbindd_sid2gid_async(s->state->mem_ctx, sid,
+ getgroups_sid2gid_recv, s);
+ return;
}
- state->response.data.num_entries = num_gids;
- state->response.extra_data = gid_list;
- state->response.length += num_gids * sizeof(gid_t);
+ s->state->response.data.num_entries = s->num_token_gids;
+ s->state->response.extra_data.data = s->token_gids;
+ s->state->response.length += s->num_token_gids * sizeof(gid_t);
+ request_ok(s->state);
+}
- result = WINBINDD_OK;
+/* Get user supplementary sids. This is equivalent to the
+ winbindd_getgroups() function but it involves a SID->SIDs mapping
+ rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
+ idmap. This call is designed to be used with applications that need
+ to do ACL evaluation themselves. Note that the cached info3 data is
+ not used
- done:
+ this function assumes that the SID that comes in is a user SID. If
+ you pass in another type of SID then you may get unpredictable
+ results.
+*/
- talloc_destroy(mem_ctx);
+static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
+ size_t num_sids);
- return result;
+void winbindd_getusersids(struct winbindd_cli_state *state)
+{
+ DOM_SID *user_sid;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
+ if (user_sid == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ request_error(state);
+ return;
+ }
+
+ if (!string_to_sid(user_sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not get convert sid %s from string\n",
+ state->request.data.sid));
+ request_error(state);
+ return;
+ }
+
+ winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
+ state);
+}
+
+static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
+ size_t num_sids)
+{
+ struct winbindd_cli_state *state = private_data;
+ char *ret = NULL;
+ unsigned ofs, ret_size = 0;
+ size_t i;
+
+ if (!success) {
+ request_error(state);
+ return;
+ }
+
+ /* work out the response size */
+ for (i = 0; i < num_sids; i++) {
+ const char *s = sid_string_static(&sids[i]);
+ ret_size += strlen(s) + 1;
+ }
+
+ /* build the reply */
+ ret = SMB_MALLOC(ret_size);
+ if (!ret) {
+ DEBUG(0, ("malloc failed\n"));
+ request_error(state);
+ return;
+ }
+ ofs = 0;
+ for (i = 0; i < num_sids; i++) {
+ const char *s = sid_string_static(&sids[i]);
+ safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
+ ofs += strlen(ret+ofs) + 1;
+ }
+
+ /* Send data back to client */
+ state->response.data.num_entries = num_sids;
+ state->response.extra_data.data = ret;
+ state->response.length += ret_size;
+ request_ok(state);
+}
+
+void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
+{
+ DOM_SID user_sid;
+ struct winbindd_domain *domain;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ if (!string_to_sid(&user_sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not get convert sid %s from string\n",
+ state->request.data.sid));
+ request_error(state);
+ return;
+ }
+
+ /* Get info for the domain */
+ if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
+ DEBUG(0,("could not find domain entry for sid %s\n",
+ sid_string_static(&user_sid)));
+ request_error(state);
+ return;
+ }
+
+ sendto_domain(state, domain);
+}
+
+enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state)
+{
+ DOM_SID user_sid;
+ NTSTATUS status;
+
+ char *sidstring;
+ ssize_t len;
+ DOM_SID *groups;
+ uint32 num_groups;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ if (!string_to_sid(&user_sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not get convert sid %s from string\n",
+ state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
+ &user_sid, &num_groups,
+ &groups);
+ if (!NT_STATUS_IS_OK(status))
+ return WINBINDD_ERROR;
+
+ if (num_groups == 0) {
+ state->response.data.num_entries = 0;
+ state->response.extra_data.data = NULL;
+ return WINBINDD_OK;
+ }
+
+ if (!print_sidlist(NULL, groups, num_groups, &sidstring, &len)) {
+ DEBUG(0, ("malloc failed\n"));
+ return WINBINDD_ERROR;
+ }
+
+ state->response.extra_data.data = sidstring;
+ state->response.length += len+1;
+ state->response.data.num_entries = num_groups;
+
+ return WINBINDD_OK;
}