1/* 2 Unix SMB/CIFS implementation. 3 async implementation of WINBINDD_GETGRENT 4 Copyright (C) Volker Lendecke 2009 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18*/ 19 20#include "includes.h" 21#include "winbindd.h" 22 23struct winbindd_getgrent_state { 24 struct tevent_context *ev; 25 struct winbindd_cli_state *cli; 26 int max_groups; 27 int num_groups; 28 struct winbindd_gr *groups; 29 struct talloc_dict **members; 30}; 31 32static void winbindd_getgrent_done(struct tevent_req *subreq); 33 34struct tevent_req *winbindd_getgrent_send(TALLOC_CTX *mem_ctx, 35 struct tevent_context *ev, 36 struct winbindd_cli_state *cli, 37 struct winbindd_request *request) 38{ 39 struct tevent_req *req, *subreq; 40 struct winbindd_getgrent_state *state; 41 42 req = tevent_req_create(mem_ctx, &state, 43 struct winbindd_getgrent_state); 44 if (req == NULL) { 45 return NULL; 46 } 47 state->ev = ev; 48 state->num_groups = 0; 49 state->cli = cli; 50 51 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)cli->pid)); 52 53 if (!lp_winbind_enum_groups()) { 54 tevent_req_nterror(req, NT_STATUS_NO_MORE_ENTRIES); 55 return tevent_req_post(req, ev); 56 } 57 58 if (cli->grent_state == NULL) { 59 tevent_req_nterror(req, NT_STATUS_NO_MORE_ENTRIES); 60 return tevent_req_post(req, ev); 61 } 62 63 state->max_groups = MIN(500, request->data.num_entries); 64 if (state->max_groups == 0) { 65 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); 66 return tevent_req_post(req, ev); 67 } 68 69 state->groups = talloc_zero_array(state, struct winbindd_gr, 70 state->max_groups); 71 if (tevent_req_nomem(state->groups, req)) { 72 return tevent_req_post(req, ev); 73 } 74 75 state->members = talloc_array(state, struct talloc_dict *, 76 state->max_groups); 77 if (tevent_req_nomem(state->members, req)) { 78 TALLOC_FREE(state->groups); 79 return tevent_req_post(req, ev); 80 } 81 82 subreq = wb_next_grent_send(state, ev, lp_winbind_expand_groups(), 83 cli->grent_state, 84 &state->groups[state->num_groups]); 85 if (tevent_req_nomem(subreq, req)) { 86 return tevent_req_post(req, ev); 87 } 88 tevent_req_set_callback(subreq, winbindd_getgrent_done, req); 89 return req; 90} 91 92static void winbindd_getgrent_done(struct tevent_req *subreq) 93{ 94 struct tevent_req *req = tevent_req_callback_data( 95 subreq, struct tevent_req); 96 struct winbindd_getgrent_state *state = tevent_req_data( 97 req, struct winbindd_getgrent_state); 98 NTSTATUS status; 99 100 status = wb_next_grent_recv(subreq, state, 101 &state->members[state->num_groups]); 102 TALLOC_FREE(subreq); 103 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) { 104 DEBUG(10, ("winbindd_getgrent_done: done with %d groups\n", 105 (int)state->num_groups)); 106 TALLOC_FREE(state->cli->grent_state); 107 tevent_req_done(req); 108 return; 109 } 110 if (!NT_STATUS_IS_OK(status)) { 111 tevent_req_nterror(req, status); 112 return; 113 } 114 state->num_groups += 1; 115 if (state->num_groups >= state->max_groups) { 116 DEBUG(10, ("winbindd_getgrent_done: Got enough groups: %d\n", 117 (int)state->num_groups)); 118 tevent_req_done(req); 119 return; 120 } 121 if (state->cli->grent_state == NULL) { 122 DEBUG(10, ("winbindd_getgrent_done: endgrent called in " 123 "between\n")); 124 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); 125 return; 126 } 127 subreq = wb_next_grent_send(state, state->ev, 128 lp_winbind_expand_groups(), 129 state->cli->grent_state, 130 &state->groups[state->num_groups]); 131 if (tevent_req_nomem(subreq, req)) { 132 return; 133 } 134 tevent_req_set_callback(subreq, winbindd_getgrent_done, req); 135} 136 137NTSTATUS winbindd_getgrent_recv(struct tevent_req *req, 138 struct winbindd_response *response) 139{ 140 struct winbindd_getgrent_state *state = tevent_req_data( 141 req, struct winbindd_getgrent_state); 142 NTSTATUS status; 143 char **memberstrings; 144 char *result; 145 size_t base_memberofs, total_memberlen; 146 int i; 147 148 if (tevent_req_is_nterror(req, &status)) { 149 DEBUG(5, ("getgrent failed: %s\n", nt_errstr(status))); 150 return status; 151 } 152 153 if (state->num_groups == 0) { 154 return NT_STATUS_NO_MORE_ENTRIES; 155 } 156 157 memberstrings = talloc_array(talloc_tos(), char *, state->num_groups); 158 if (memberstrings == NULL) { 159 return NT_STATUS_NO_MEMORY; 160 } 161 162 total_memberlen = 0; 163 164 for (i=0; i<state->num_groups; i++) { 165 int num_members; 166 167 status = winbindd_print_groupmembers( 168 state->members[i], memberstrings, &num_members, 169 &memberstrings[i]); 170 171 if (!NT_STATUS_IS_OK(status)) { 172 TALLOC_FREE(memberstrings); 173 return status; 174 } 175 TALLOC_FREE(state->members[i]); 176 177 state->groups[i].num_gr_mem = num_members; 178 state->groups[i].gr_mem_ofs = total_memberlen; 179 180 total_memberlen += talloc_get_size(memberstrings[i]); 181 } 182 183 base_memberofs = state->num_groups * sizeof(struct winbindd_gr); 184 185 result = talloc_realloc(state, state->groups, char, 186 base_memberofs + total_memberlen); 187 if (result == NULL) { 188 return NT_STATUS_NO_MEMORY; 189 } 190 state->groups = (struct winbindd_gr *)result; 191 192 for (i=0; i<state->num_groups; i++) { 193 memcpy(result + base_memberofs + state->groups[i].gr_mem_ofs, 194 memberstrings[i], talloc_get_size(memberstrings[i])); 195 TALLOC_FREE(memberstrings[i]); 196 } 197 198 TALLOC_FREE(memberstrings); 199 200 response->data.num_entries = state->num_groups; 201 response->length += talloc_get_size(result); 202 response->extra_data.data = talloc_move(response, &result); 203 return NT_STATUS_OK; 204} 205