diff options
-rw-r--r-- | source3/configure.in | 2 | ||||
-rw-r--r-- | source3/lib/replace.c | 66 | ||||
-rw-r--r-- | source3/lib/util_getent.c | 56 | ||||
-rw-r--r-- | source3/rpc_server/srv_util.c | 62 |
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++) |