From c1bc3a7841890d8af470863e018721c035754999 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Sat, 9 Aug 2003 23:12:35 +0000 Subject: fix for BUG #267 (problem with supplementary groups). Use winbindd to get the group list if possible since we already know it from netsamlogon_cache.tdb. More effecient than letting libc call getgrent() to get seconary groups. Tested by Ken Cross. (This used to be commit 3c537c906f29a08e75895c8c8e3ed5c5abaaa940) --- source3/auth/auth_util.c | 118 ++++++++++++++++++++++++++++--------------- source3/nsswitch/wb_client.c | 74 +++++++++++++++++++++++++++ source3/smbd/sec_ctx.c | 2 +- 3 files changed, 151 insertions(+), 43 deletions(-) diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c index 061cc7345b..d07681ee7d 100644 --- a/source3/auth/auth_util.c +++ b/source3/auth/auth_util.c @@ -646,43 +646,66 @@ NT_USER_TOKEN *create_nt_token(uid_t uid, gid_t gid, int ngroups, gid_t *groups, * of groups. ******************************************************************************/ -static NTSTATUS get_user_groups_from_local_sam(const char *username, uid_t uid, gid_t gid, - int *n_groups, DOM_SID **groups, gid_t **unix_groups) +static NTSTATUS get_user_groups(const char *username, uid_t uid, gid_t gid, + int *n_groups, DOM_SID **groups, gid_t **unix_groups) { - int n_unix_groups; - int i; + int n_unix_groups; + int i; *n_groups = 0; *groups = NULL; + + /* Try winbind first */ - n_unix_groups = groups_max(); - if ((*unix_groups = malloc( sizeof(gid_t) * n_unix_groups ) ) == NULL) { - DEBUG(0, ("get_user_groups_from_local_sam: Out of memory allocating unix group list\n")); - return NT_STATUS_NO_MEMORY; + if ( strchr(username, *lp_winbind_separator()) ) { + n_unix_groups = winbind_getgroups( username, unix_groups ); + + DEBUG(10,("get_user_groups: winbind_getgroups(%s): result = %s\n", username, + n_unix_groups == -1 ? "FAIL" : "SUCCESS")); + + if ( n_unix_groups == -1 ) + return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */ } - - if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) { - gid_t *groups_tmp; - groups_tmp = Realloc(*unix_groups, sizeof(gid_t) * n_unix_groups); - if (!groups_tmp) { - SAFE_FREE(*unix_groups); + else { + /* fallback to getgrouplist() */ + + n_unix_groups = groups_max(); + + if ((*unix_groups = malloc( sizeof(gid_t) * n_unix_groups ) ) == NULL) { + DEBUG(0, ("get_user_groups: Out of memory allocating unix group list\n")); return NT_STATUS_NO_MEMORY; } - *unix_groups = groups_tmp; - + if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) { - DEBUG(0, ("get_user_groups_from_local_sam: failed to get the unix group list\n")); - SAFE_FREE(*unix_groups); - return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */ + + gid_t *groups_tmp; + + groups_tmp = Realloc(*unix_groups, sizeof(gid_t) * n_unix_groups); + + if (!groups_tmp) { + SAFE_FREE(*unix_groups); + return NT_STATUS_NO_MEMORY; + } + *unix_groups = groups_tmp; + + if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) { + DEBUG(0, ("get_user_groups: failed to get the unix group list\n")); + SAFE_FREE(*unix_groups); + return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */ + } } } debug_unix_user_token(DBGC_CLASS, 5, uid, gid, n_unix_groups, *unix_groups); + /* now setup the space for storing the SIDS */ + if (n_unix_groups > 0) { + *groups = malloc(sizeof(DOM_SID) * n_unix_groups); + if (!*groups) { - DEBUG(0, ("get_user_group_from_local_sam: malloc() failed for DOM_SID list!\n")); + DEBUG(0, ("get_user_group: malloc() failed for DOM_SID list!\n")); SAFE_FREE(*unix_groups); return NT_STATUS_NO_MEMORY; } @@ -692,7 +715,8 @@ static NTSTATUS get_user_groups_from_local_sam(const char *username, uid_t uid, for (i = 0; i < *n_groups; i++) { if (!NT_STATUS_IS_OK(gid_to_sid(&(*groups)[i], (*unix_groups)[i]))) { - DEBUG(1, ("get_user_groups_from_local_sam: failed to convert gid %ld to a sid!\n", (long int)(*unix_groups)[i+1])); + DEBUG(1, ("get_user_groups: failed to convert gid %ld to a sid!\n", + (long int)(*unix_groups)[i+1])); SAFE_FREE(*groups); SAFE_FREE(*unix_groups); return NT_STATUS_NO_SUCH_USER; @@ -743,10 +767,9 @@ static NTSTATUS add_user_groups(auth_serversupplied_info **server_info, BOOL is_guest; uint32 rid; - nt_status = get_user_groups_from_local_sam(pdb_get_username(sampass), - uid, gid, - &n_groupSIDs, &groupSIDs, - &unix_groups); + nt_status = get_user_groups(pdb_get_username(sampass), uid, gid, + &n_groupSIDs, &groupSIDs, &unix_groups); + if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(4,("get_user_groups_from_local_sam failed\n")); free_server_info(server_info); @@ -1068,11 +1091,11 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, /* Store the user group information in the server_info returned to the caller. */ - nt_status = get_user_groups_from_local_sam((*server_info)->unix_name, + nt_status = get_user_groups((*server_info)->unix_name, uid, gid, &n_lgroupSIDs, &lgroupSIDs, &unix_groups); - if ( !NT_STATUS_IS_OK(nt_status) ) - { - DEBUG(4,("get_user_groups_from_local_sam failed\n")); + + if ( !NT_STATUS_IS_OK(nt_status) ) { + DEBUG(4,("get_user_groups failed\n")); return nt_status; } @@ -1080,9 +1103,9 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, (*server_info)->n_groups = n_lgroupSIDs; /* Create a 'combined' list of all SIDs we might want in the SD */ - all_group_SIDs = malloc(sizeof(DOM_SID) * - (n_lgroupSIDs + info3->num_groups2 + - info3->num_other_sids)); + + all_group_SIDs = malloc(sizeof(DOM_SID) * (info3->num_groups2 +info3->num_other_sids)); + if (!all_group_SIDs) { DEBUG(0, ("malloc() failed for DOM_SID list!\n")); SAFE_FREE(lgroupSIDs); @@ -1090,20 +1113,30 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, return NT_STATUS_NO_MEMORY; } +#if 0 /* JERRY -- no such thing as local groups in current code */ /* Copy the 'local' sids */ memcpy(all_group_SIDs, lgroupSIDs, sizeof(DOM_SID) * n_lgroupSIDs); SAFE_FREE(lgroupSIDs); +#endif /* and create (by appending rids) the 'domain' sids */ + for (i = 0; i < info3->num_groups2; i++) { - sid_copy(&all_group_SIDs[i+n_lgroupSIDs], &(info3->dom_sid.sid)); - if (!sid_append_rid(&all_group_SIDs[i+n_lgroupSIDs], info3->gids[i].g_rid)) { + + sid_copy(&all_group_SIDs[i], &(info3->dom_sid.sid)); + + if (!sid_append_rid(&all_group_SIDs[i], info3->gids[i].g_rid)) { + nt_status = NT_STATUS_INVALID_PARAMETER; + DEBUG(3,("could not append additional group rid 0x%x\n", info3->gids[i].g_rid)); + SAFE_FREE(lgroupSIDs); free_server_info(server_info); + return nt_status; + } } @@ -1113,19 +1146,20 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp */ - for (i = 0; i < info3->num_other_sids; i++) - sid_copy(&all_group_SIDs[ - n_lgroupSIDs + info3->num_groups2 + i], + for (i = 0; i < info3->num_other_sids; i++) { + sid_copy(&all_group_SIDs[info3->num_groups2 + i], &info3->other_sids[i].sid); + } /* Where are the 'global' sids... */ /* can the user be guest? if yes, where is it stored? */ - if (!NT_STATUS_IS_OK( - nt_status = create_nt_user_token( - &user_sid, &group_sid, - n_lgroupSIDs + info3->num_groups2 + info3->num_other_sids, - all_group_SIDs, False, &token))) { + + nt_status = create_nt_user_token(&user_sid, &group_sid, + info3->num_groups2 + info3->num_other_sids, + all_group_SIDs, False, &token); + + if ( !NT_STATUS_IS_OK(nt_status) ) { DEBUG(4,("create_nt_user_token failed\n")); SAFE_FREE(all_group_SIDs); free_server_info(server_info); diff --git a/source3/nsswitch/wb_client.c b/source3/nsswitch/wb_client.c index 7c5a8dd054..0c6644e9d0 100644 --- a/source3/nsswitch/wb_client.c +++ b/source3/nsswitch/wb_client.c @@ -264,6 +264,80 @@ static int wb_getgroups(const char *user, gid_t **groups) return -1; } +/* Call winbindd to initialise group membership. This is necessary for + some systems (i.e RH5.2) that do not have an initgroups function as part + of the nss extension. In RH5.2 this is implemented using getgrent() + which can be amazingly inefficient as well as having problems with + username case. */ + +int winbind_initgroups(char *user, gid_t gid) +{ + gid_t *tgr, *groups = NULL; + int result; + + /* Call normal initgroups if we are a local user */ + + if (!strchr(user, *lp_winbind_separator())) { + return initgroups(user, gid); + } + + result = wb_getgroups(user, &groups); + + DEBUG(10,("winbind_getgroups: %s: result = %s\n", user, + result == -1 ? "FAIL" : "SUCCESS")); + + if (result != -1) { + int ngroups = result, i; + BOOL is_member = False; + + /* Check to see if the passed gid is already in the list */ + + for (i = 0; i < ngroups; i++) { + if (groups[i] == gid) { + is_member = True; + } + } + + /* Add group to list if necessary */ + + if (!is_member) { + tgr = (gid_t *)Realloc(groups, sizeof(gid_t) * ngroups + 1); + + if (!tgr) { + errno = ENOMEM; + result = -1; + goto done; + } + else groups = tgr; + + groups[ngroups] = gid; + ngroups++; + } + + /* Set the groups */ + + if (sys_setgroups(ngroups, groups) == -1) { + errno = EPERM; + result = -1; + goto done; + } + + } else { + + /* The call failed. Set errno to something so we don't get + a bogus value from the last failed system call. */ + + errno = EIO; + } + + /* Free response data if necessary */ + + done: + SAFE_FREE(groups); + + return result; +} + /* Return a list of groups the user is a member of. This function is useful for large systems where inverting the group database would be too time consuming. If size is zero, list is not modified and the total diff --git a/source3/smbd/sec_ctx.c b/source3/smbd/sec_ctx.c index 411ece5249..8a85792ead 100644 --- a/source3/smbd/sec_ctx.c +++ b/source3/smbd/sec_ctx.c @@ -199,7 +199,7 @@ BOOL initialise_groups(char *user, uid_t uid, gid_t gid) /* Call initgroups() to get user groups */ - if (initgroups(user,gid) == -1) { + if (winbind_initgroups(user,gid) == -1) { DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) )); if (getuid() == 0) { if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) { -- cgit