From dc19f161698dab5b71d61fa2bacc7e7b8da5fbba Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sat, 9 Oct 2004 01:44:05 +0000 Subject: [PATCH] r2868: Well, I'm not quite sure what I'm doing back in Samba 3.0, but anyway... I've been grumbling about under-efficient calls in SAMR, and finally got around to fixing some of them. We now call sys_getgroups() (which in turn calls initgroups(), until glibc 3.4 is released) to figure out a user's group membership. This is far, far more efficient than scanning all the groups looking for a match, and is still the 'posix way', just using an effiecient call. The seperate issue of 'who is in this group' remains, but this one has been biting some people. I need to talk to VL about how best to exersise nasty corner cases, but my initial tests hold strong. (The code is also much simpiler than before, which has to count for something :-) Andrew Bartlett --- source/lib/util.c | 22 ++++ source/lib/util_smbd.c | 53 +++++++--- source/nsswitch/winbindd_group.c | 18 ---- source/rpc_server/srv_util.c | 167 +++++++++---------------------- 4 files changed, 111 insertions(+), 149 deletions(-) diff --git a/source/lib/util.c b/source/lib/util.c index 5e88bd896ff..89cf1bfa021 100644 --- a/source/lib/util.c +++ b/source/lib/util.c @@ -288,6 +288,28 @@ BOOL in_group(gid_t group, gid_t current_gid, int ngroups, const gid_t *groups) return(False); } +/**************************************************************************** + Add a gid to an array of gids if it's not already there. +****************************************************************************/ + +void add_gid_to_array_unique(gid_t gid, gid_t **gids, int *num) +{ + int i; + + for (i=0; i<*num; i++) { + if ((*gids)[i] == gid) + return; + } + + *gids = Realloc(*gids, (*num+1) * sizeof(gid_t)); + + if (*gids == NULL) + return; + + (*gids)[*num] = gid; + *num += 1; +} + /**************************************************************************** Like atoi but gets the value up to the separator character. ****************************************************************************/ diff --git a/source/lib/util_smbd.c b/source/lib/util_smbd.c index 071f20b4162..36c3104e55a 100644 --- a/source/lib/util_smbd.c +++ b/source/lib/util_smbd.c @@ -37,29 +37,58 @@ NOTE! uses become_root() to gain correct priviages on systems that lack a native getgroups() call (uses initgroups and getgroups) */ -int getgroups_user(const char *user, gid_t **groups) +BOOL getgroups_user(const char *user, gid_t **ret_groups, int *ngroups) { struct passwd *pwd; int ngrp, max_grp; + gid_t *temp_groups; + gid_t *groups; + int i; pwd = getpwnam_alloc(user); - if (!pwd) return -1; + if (!pwd) return False; max_grp = groups_max(); - (*groups) = (gid_t *)malloc(sizeof(gid_t) * max_grp); - if (! *groups) { + temp_groups = (gid_t *)malloc(sizeof(gid_t) * max_grp); + if (! temp_groups) { passwd_free(&pwd); - errno = ENOMEM; - return -1; + return False; } - ngrp = sys_getgrouplist(user, pwd->pw_gid, *groups, &max_grp); - if (ngrp <= 0) { - passwd_free(&pwd); - free(*groups); - return ngrp; + if (sys_getgrouplist(user, pwd->pw_gid, temp_groups, &max_grp) == -1) { + + gid_t *groups_tmp; + + groups_tmp = Realloc(temp_groups, sizeof(gid_t) * max_grp); + + if (!groups_tmp) { + SAFE_FREE(temp_groups); + return False; + } + temp_groups = groups_tmp; + + if (sys_getgrouplist(user, pwd->pw_gid, temp_groups, &max_grp) == -1) { + DEBUG(0, ("get_user_groups: failed to get the unix group list\n")); + passwd_free(&pwd); + SAFE_FREE(temp_groups); + return False; + } } + + ngrp = 0; + groups = NULL; + + /* Add in primary group first */ + add_gid_to_array_unique(pwd->pw_gid, &groups, &ngrp); passwd_free(&pwd); - return ngrp; + + for (i=0; i 0) { + + *pgids = talloc(ctx, sizeof(DOM_GID) * n_unix_groups); + + if (!*pgids) { + DEBUG(0, ("get_user_group: malloc() failed for DOM_GID list!\n")); + SAFE_FREE(unix_groups); + return False; + } + } + become_root(); - /* first get the list of the domain groups */ - ret = pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, &num_entries, ENUM_ONLY_MAPPED); - + j = 0; + for (i = 0; i < n_unix_groups; i++) { + GROUP_MAP map; + uint32 rid; + + if (!pdb_getgrgid(&map, unix_groups[i])) { + DEBUG(3, ("get_user_groups: failed to convert gid %ld to a domain group!\n", + (long int)unix_groups[i+1])); + if (i == 0) { + DEBUG(1,("get_domain_user_groups: primary gid of user [%s] is not a Domain group !\n", username)); + DEBUGADD(1,("get_domain_user_groups: You should fix it, NT doesn't like that\n")); + } + } else if ((map.sid_name_use == SID_NAME_DOM_GRP) + && sid_peek_check_rid(get_global_sam_sid(), &map.sid, &rid)) { + (*pgids)[j].attr=7; + (*pgids)[j].g_rid=rid; + j++; + } + } unbecome_root(); - /* end wrapper for group enumeration */ + *numgroups = j; - - if ( !ret ) - return False; - - DEBUG(10,("get_domain_user_groups: there are %d mapped groups\n", num_entries)); - - - /* - * alloc memory. In the worse case, we alloc memory for nothing. - * but I prefer to alloc for nothing - * than reallocing everytime. - */ - gids = (DOM_GID *)talloc(ctx, sizeof(DOM_GID) * num_entries); - - /* for each group, check if the user is a member of. Only include groups - from this domain */ - - for(i=0; igr_mem[num]!=NULL; num++) { - if(strcmp(grp->gr_mem[num], user_name)==0) { - /* we found the user, add the group to the list */ - sid_peek_rid(&map[i].sid, &(gids[cur_gid].g_rid)); - gids[cur_gid].attr=7; - DEBUG(10,("get_domain_user_groups: user found in group %s\n", map[i].nt_name)); - cur_gid++; - break; - } - } - } - - /* we have checked the groups */ - /* we must now check the gid of the user or the primary group rid, that's the same */ - for (i=0; i