fix for BUG #267 (problem with supplementary groups).
authorGerald Carter <jerry@samba.org>
Sat, 9 Aug 2003 23:12:35 +0000 (23:12 +0000)
committerGerald Carter <jerry@samba.org>
Sat, 9 Aug 2003 23:12:35 +0000 (23:12 +0000)
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
source3/nsswitch/wb_client.c
source3/smbd/sec_ctx.c

index 061cc7345b39c64930f805183da13b2d1650fffe..d07681ee7daadd44a382b6f35aa08390e91c01f8 100644 (file)
@@ -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);
index 7c5a8dd0546e1637814e1994a860c7d2f628c71c..0c6644e9d0055dc26e326678e1ca0ff9f34c5fa1 100644 (file)
@@ -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
index 411ece52495c5f80b615147ad7fba289c0a1829b..8a85792ead55d4f70e24d3a3ec455eaa5ee90588 100644 (file)
@@ -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) {