summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2002-07-23 09:22:29 +0000
committerAndrew Tridgell <tridge@samba.org>2002-07-23 09:22:29 +0000
commitfef9d6187ece53ae12670cc56b360e913e08f3bb (patch)
treee02543ae6ff6e09e26018abd740566309fea36f5
parent5b513407c21e6e77d495fafa49a7a5d380087667 (diff)
downloadsamba-fef9d6187ece53ae12670cc56b360e913e08f3bb.tar.gz
samba-fef9d6187ece53ae12670cc56b360e913e08f3bb.tar.bz2
samba-fef9d6187ece53ae12670cc56b360e913e08f3bb.zip
implemented getgrouplist() for systems that don't have it and use it
in get_alias_user_groups(). The old method used getgrent() which is extremely slow when the number of groups is large (This used to be commit 44e92b6523ca2c119c2562df22eb71138dca9d9d)
-rw-r--r--source3/configure.in2
-rw-r--r--source3/lib/replace.c66
-rw-r--r--source3/lib/util_getent.c56
-rw-r--r--source3/rpc_server/srv_util.c62
4 files changed, 131 insertions, 55 deletions
diff --git a/source3/configure.in b/source3/configure.in
index 77c14c7191..4397d5846c 100644
--- a/source3/configure.in
+++ b/source3/configure.in
@@ -746,7 +746,7 @@ AC_CHECK_FUNCS(setpriv setgidx setuidx setgroups sysconf mktime rename ftruncate
AC_CHECK_FUNCS(lstat64 fopen64 atexit grantpt dup2 lseek64 ftruncate64 readdir64)
AC_CHECK_FUNCS(fseek64 fseeko64 ftell64 ftello64 setluid getpwanam setlinebuf)
AC_CHECK_FUNCS(srandom random srand rand setenv usleep strcasecmp fcvt fcvtl symlink readlink)
-AC_CHECK_FUNCS(syslog vsyslog)
+AC_CHECK_FUNCS(syslog vsyslog getgrouplist)
# setbuffer is needed for smbtorture
AC_CHECK_FUNCS(setbuffer)
diff --git a/source3/lib/replace.c b/source3/lib/replace.c
index 2cc7d48adb..e2664accfa 100644
--- a/source3/lib/replace.c
+++ b/source3/lib/replace.c
@@ -428,3 +428,69 @@ char *rep_inet_ntoa(struct in_addr ip)
}
#endif /* HAVE_SYSLOG */
#endif /* HAVE_VSYSLOG */
+
+
+#ifndef HAVE_GETGROUPLIST
+/*
+ This is a *much* faster way of getting the list of groups for a user
+ without changing the current supplemenrary group list. The old
+ method used getgrent() which could take 20 minutes on a really big
+ network with hundeds of thousands of groups and users. The new method
+ takes a couple of seconds.
+
+ NOTE!! this function only works if it is called as root!
+ */
+ int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
+{
+ gid_t *gids_saved;
+ int ret, ngrp_saved;
+
+ /* work out how many groups we need to save */
+ ngrp_saved = getgroups(0, NULL);
+ if (ngrp_saved == -1) {
+ /* this shouldn't happen */
+ return -1;
+ }
+
+ gids_saved = (gid_t *)malloc(sizeof(gid_t) * (ngrp_saved+1));
+ if (!gids_saved) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ngrp_saved = getgroups(ngrp_saved, gids_saved);
+ if (ngrp_saved == -1) {
+ free(gids_saved);
+ /* very strange! */
+ return -1;
+ }
+
+ if (initgroups(user, gid) != 0) {
+ free(gids_saved);
+ return -1;
+ }
+
+ /* this must be done to cope with systems that put the current egid in the
+ return from getgroups() */
+ save_re_gid();
+ set_effective_gid(gid);
+ setgid(gid);
+
+ ret = getgroups(*grpcnt, groups);
+ if (ret >= 0) {
+ *grpcnt = ret;
+ }
+
+ restore_re_gid();
+
+ if (setgroups(ngrp_saved, gids_saved) != 0) {
+ /* yikes! */
+ DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n"));
+ free(gids_saved);
+ return -1;
+ }
+
+ free(gids_saved);
+ return ret;
+}
+#endif
diff --git a/source3/lib/util_getent.c b/source3/lib/util_getent.c
index 2e76121aae..5d2fcd7652 100644
--- a/source3/lib/util_getent.c
+++ b/source3/lib/util_getent.c
@@ -21,27 +21,6 @@
#include "includes.h"
-#if 0
-static void print_grent_list(struct sys_grent *glist)
-{
- DEBUG(100, ("print_grent_list: %x\n", glist ));
- while (glist) {
- DEBUG(100,("glist: %x ", glist));
- if (glist->gr_name)
- DEBUG(100,(": gr_name = (%x) %s ", glist->gr_name, glist->gr_name));
- if (glist->gr_passwd)
- DEBUG(100,(": gr_passwd = (%x) %s ", glist->gr_passwd, glist->gr_passwd));
- if (glist->gr_mem) {
- int i;
- for (i = 0; glist->gr_mem[i]; i++)
- DEBUG(100,(" : gr_mem[%d] = (%x) %s ", i, glist->gr_mem[i], glist->gr_mem[i]));
- }
- DEBUG(100,(": gr_next = %x\n", glist->next ));
- glist = glist->next;
- }
- DEBUG(100,("FINISHED !\n\n"));
-}
-#endif
/****************************************************************
Returns a single linked list of group entries.
@@ -320,3 +299,38 @@ void free_userlist(struct sys_userlist *list_head)
SAFE_FREE(old_head);
}
}
+
+
+/*
+ return a full list of groups for a user
+
+ returns the number of groups the user is a member of. The return will include the
+ users primary group.
+
+ remember to free the resulting gid_t array
+
+ NOTE! you must be root to call this function on some systems
+*/
+int getgroups_user(const char *user, gid_t **groups)
+{
+ struct passwd *pwd;
+ int ngrp, max_grp;
+
+ pwd = getpwnam(user);
+ if (!pwd) return -1;
+
+ max_grp = groups_max();
+ (*groups) = (gid_t *)malloc(sizeof(gid_t) * max_grp);
+ if (! *groups) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ngrp = getgrouplist(user, pwd->pw_gid, *groups, &max_grp);
+ if (ngrp <= 0) {
+ free(*groups);
+ return ngrp;
+ }
+
+ return ngrp;
+}
diff --git a/source3/rpc_server/srv_util.c b/source3/rpc_server/srv_util.c
index f896d1d9d8..50bf5db4fd 100644
--- a/source3/rpc_server/srv_util.c
+++ b/source3/rpc_server/srv_util.c
@@ -84,10 +84,10 @@ rid_name domain_group_rids[] =
NTSTATUS get_alias_user_groups(TALLOC_CTX *ctx, DOM_SID *sid, int *numgroups, uint32 **prids, DOM_SID *q_sid)
{
SAM_ACCOUNT *sam_pass=NULL;
- struct sys_grent *glist;
- struct sys_grent *grp;
- int i, num, cur_rid=0;
+ int i, cur_rid=0;
gid_t gid;
+ gid_t *groups = NULL;
+ int num_groups;
GROUP_MAP map;
DOM_SID tmp_sid;
fstring user_name;
@@ -130,16 +130,21 @@ NTSTATUS get_alias_user_groups(TALLOC_CTX *ctx, DOM_SID *sid, int *numgroups, ui
fstrcpy(user_name, pdb_get_username(sam_pass));
grid=pdb_get_group_rid(sam_pass);
gid=pdb_get_gid(sam_pass);
-
- grp = glist = getgrent_list();
- if (grp == NULL) {
+
+ become_root();
+ /* on some systems this must run as root */
+ num_groups = getgroups_user(user_name, &groups);
+ unbecome_root();
+ if (num_groups == -1) {
+ /* this should never happen */
+ DEBUG(2,("get_alias_user_groups: getgroups_user failed\n"));
pdb_free_sam(&sam_pass);
- return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_UNSUCCESSFUL;
}
-
- for (; grp != NULL; grp = grp->next) {
- if(!get_group_from_gid(grp->gr_gid, &map, MAPPING_WITHOUT_PRIV)) {
- DEBUG(10,("get_alias_user_groups: gid %d. not found\n", (int)grp->gr_gid));
+
+ for (i=0;i<num_groups;i++) {
+ if(!get_group_from_gid(groups[i], &map, MAPPING_WITHOUT_PRIV)) {
+ DEBUG(10,("get_alias_user_groups: gid %d. not found\n", (int)groups[i]));
continue;
}
@@ -159,7 +164,7 @@ NTSTATUS get_alias_user_groups(TALLOC_CTX *ctx, DOM_SID *sid, int *numgroups, ui
}
/* Don't return winbind groups as they are not local! */
- if (winbind_groups_exist && (grp->gr_gid >= winbind_gid_low) && (grp->gr_gid <= winbind_gid_high)) {
+ if (winbind_groups_exist && (groups[i] >= winbind_gid_low) && (groups[i] <= winbind_gid_high)) {
DEBUG(10,("get_alias_user_groups: not returing %s, not local.\n", map.nt_name));
continue;
}
@@ -170,30 +175,21 @@ NTSTATUS get_alias_user_groups(TALLOC_CTX *ctx, DOM_SID *sid, int *numgroups, ui
continue;
}
- /* the group is fine, we can check if there is the user we're looking for */
- DEBUG(10,("get_alias_user_groups: checking if the user is a member of %s.\n", map.nt_name));
-
- for(num=0; grp->gr_mem[num]!=NULL; num++) {
- if(strcmp(grp->gr_mem[num], user_name)==0) {
- /* we found the user, add the group to the list */
-
- new_rids=(uint32 *)Realloc(rids, sizeof(uint32)*(cur_rid+1));
- if (new_rids==NULL) {
- DEBUG(10,("get_alias_user_groups: could not realloc memory\n"));
- pdb_free_sam(&sam_pass);
- return NT_STATUS_NO_MEMORY;
- }
- rids=new_rids;
-
- sid_peek_rid(&map.sid, &(rids[cur_rid]));
- DEBUG(10,("get_alias_user_groups: user found in group %s\n", map.nt_name));
- cur_rid++;
- break;
- }
+ new_rids=(uint32 *)Realloc(rids, sizeof(uint32)*(cur_rid+1));
+ if (new_rids==NULL) {
+ DEBUG(10,("get_alias_user_groups: could not realloc memory\n"));
+ pdb_free_sam(&sam_pass);
+ free(groups);
+ return NT_STATUS_NO_MEMORY;
}
+ rids=new_rids;
+
+ sid_peek_rid(&map.sid, &(rids[cur_rid]));
+ cur_rid++;
+ break;
}
- grent_free(glist);
+ free(groups);
/* now check for the user's gid (the primary group rid) */
for (i=0; i<cur_rid && grid!=rids[i]; i++)