1/* 2 Unix SMB/CIFS implementation. 3 system call wrapper interface. 4 Copyright (C) Andrew Tridgell 2002 5 Copyright (C) Andrew Barteltt 2002 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. 19*/ 20 21/* 22 This file may assume linkage with smbd - for things like become_root() 23 etc. 24*/ 25 26#include "includes.h" 27 28#ifndef HAVE_GETGROUPLIST 29 30/* 31 This is a *much* faster way of getting the list of groups for a user 32 without changing the current supplementary group list. The old 33 method used getgrent() which could take 20 minutes on a really big 34 network with hundeds of thousands of groups and users. The new method 35 takes a couple of seconds. 36 37 NOTE!! this function only works if it is called as root! 38 */ 39 40static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, 41 int *grpcnt) 42{ 43 gid_t *gids_saved; 44 int ret, ngrp_saved, num_gids; 45 46 if (non_root_mode()) { 47 *grpcnt = 0; 48 return 0; 49 } 50 51 /* work out how many groups we need to save */ 52 ngrp_saved = getgroups(0, NULL); 53 if (ngrp_saved == -1) { 54 /* this shouldn't happen */ 55 return -1; 56 } 57 58 gids_saved = SMB_MALLOC_ARRAY(gid_t, ngrp_saved+1); 59 if (!gids_saved) { 60 errno = ENOMEM; 61 return -1; 62 } 63 64 ngrp_saved = getgroups(ngrp_saved, gids_saved); 65 if (ngrp_saved == -1) { 66 SAFE_FREE(gids_saved); 67 /* very strange! */ 68 return -1; 69 } 70 71 if (initgroups(user, gid) != 0) { 72 DEBUG(0, ("getgrouplist_internals: initgroups() failed!\n")); 73 SAFE_FREE(gids_saved); 74 return -1; 75 } 76 77 /* this must be done to cope with systems that put the current egid in the 78 return from getgroups() */ 79 save_re_gid(); 80 set_effective_gid(gid); 81 setgid(gid); 82 83 num_gids = getgroups(0, NULL); 84 if (num_gids == -1) { 85 SAFE_FREE(gids_saved); 86 /* very strange! */ 87 return -1; 88 } 89 90 if (num_gids + 1 > *grpcnt) { 91 *grpcnt = num_gids + 1; 92 ret = -1; 93 } else { 94 ret = getgroups(*grpcnt - 1, &groups[1]); 95 if (ret < 0) { 96 SAFE_FREE(gids_saved); 97 /* very strange! */ 98 return -1; 99 } 100 groups[0] = gid; 101 *grpcnt = ret + 1; 102 } 103 104 restore_re_gid(); 105 106 if (sys_setgroups(gid, ngrp_saved, gids_saved) != 0) { 107 /* yikes! */ 108 DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n")); 109 smb_panic("getgrouplist: failed to reset group list!"); 110 } 111 112 free(gids_saved); 113 return ret; 114} 115#endif 116 117static int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt) 118{ 119 int retval; 120 bool winbind_env; 121 122 DEBUG(10,("sys_getgrouplist: user [%s]\n", user)); 123 124 /* This is only ever called for Unix users, remote memberships are 125 * always determined by the info3 coming back from auth3 or the 126 * PAC. */ 127 winbind_env = winbind_env_set(); 128 (void)winbind_off(); 129 130#ifdef HAVE_GETGROUPLIST 131 retval = getgrouplist(user, gid, groups, grpcnt); 132#else 133 become_root(); 134 retval = getgrouplist_internals(user, gid, groups, grpcnt); 135 unbecome_root(); 136#endif 137 138 /* allow winbindd lookups, but only if they were not already disabled */ 139 if (!winbind_env) { 140 (void)winbind_on(); 141 } 142 143 return retval; 144} 145 146bool getgroups_unix_user(TALLOC_CTX *mem_ctx, const char *user, 147 gid_t primary_gid, 148 gid_t **ret_groups, size_t *p_ngroups) 149{ 150 size_t ngrp; 151 int max_grp; 152 gid_t *temp_groups; 153 gid_t *groups; 154 int i; 155 156 max_grp = MIN(128, groups_max()); 157 temp_groups = SMB_MALLOC_ARRAY(gid_t, max_grp); 158 if (! temp_groups) { 159 return False; 160 } 161 162 if (sys_getgrouplist(user, primary_gid, temp_groups, &max_grp) == -1) { 163 temp_groups = SMB_REALLOC_ARRAY(temp_groups, gid_t, max_grp); 164 if (!temp_groups) { 165 return False; 166 } 167 168 if (sys_getgrouplist(user, primary_gid, 169 temp_groups, &max_grp) == -1) { 170 DEBUG(0, ("get_user_groups: failed to get the unix " 171 "group list\n")); 172 SAFE_FREE(temp_groups); 173 return False; 174 } 175 } 176 177 ngrp = 0; 178 groups = NULL; 179 180 /* Add in primary group first */ 181 if (!add_gid_to_array_unique(mem_ctx, primary_gid, &groups, &ngrp)) { 182 SAFE_FREE(temp_groups); 183 return False; 184 } 185 186 for (i=0; i<max_grp; i++) { 187 if (!add_gid_to_array_unique(mem_ctx, temp_groups[i], 188 &groups, &ngrp)) { 189 SAFE_FREE(temp_groups); 190 return False; 191 } 192 } 193 194 *p_ngroups = ngrp; 195 *ret_groups = groups; 196 SAFE_FREE(temp_groups); 197 return True; 198} 199