s3-talloc Change TALLOC_ARRAY() to talloc_array()
[kai/samba.git] / source3 / winbindd / wb_lookupsids.c
1 /*
2    Unix SMB/CIFS implementation.
3    async lookupsids
4    Copyright (C) Volker Lendecke 2011
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 #include "librpc/gen_ndr/ndr_wbint_c.h"
23 #include "../libcli/security/security.h"
24 #include "passdb/machine_sid.h"
25
26 struct wb_lookupsids_domain {
27         struct dom_sid sid;
28         struct winbindd_domain *domain;
29
30         /*
31          * Array of sids to be passed into wbint_LookupSids. Preallocated with
32          * num_sids.
33          */
34         struct lsa_SidArray sids;
35
36         /*
37          * Indexes into wb_lookupsids_state->sids and thus
38          * wb_lookupsids_state->res_names. Preallocated with num_sids.
39          */
40         uint32_t *sid_indexes;
41 };
42
43 struct wb_translated_name {
44         const char *domain_name;
45         const char *name;
46         enum lsa_SidType type;
47 };
48
49 static struct wb_lookupsids_domain *wb_lookupsids_get_domain(
50         const struct dom_sid *sid, TALLOC_CTX *mem_ctx,
51         struct wb_lookupsids_domain **domains, uint32_t num_sids);
52
53 struct wb_lookupsids_state {
54         struct tevent_context *ev;
55
56         /*
57          * SIDs passed in
58          */
59         struct dom_sid *sids;
60         uint32_t num_sids;
61
62         /*
63          * The domains we're using for bulk lookup via wbint_LookupRids or
64          * wbint_LookupSids. We expect very few domains, so we do a
65          * talloc_realloc and rely on talloc_array_length.
66          */
67         struct wb_lookupsids_domain *domains;
68         uint32_t domains_done;
69
70         /*
71          * These SIDs are looked up individually via
72          * wbint_LookupSid. Preallocated with num_sids.
73          */
74         uint32_t *single_sids;
75         uint32_t num_single_sids;
76         uint32_t single_sids_done;
77
78         /*
79          * Intermediate store for wbint_LookupRids to passdb. These are
80          * spliced into res_domains/res_names in wb_lookupsids_move_name.
81          */
82         struct wbint_RidArray rids;
83         const char *domain_name;
84         struct wbint_Principals rid_names;
85
86         /*
87          * Intermediate results for wbint_LookupSids. These results are
88          * spliced into res_domains/res_names in wb_lookupsids_move_name.
89          */
90         struct lsa_RefDomainList tmp_domains;
91         struct lsa_TransNameArray tmp_names;
92
93         /*
94          * Results
95          */
96         struct lsa_RefDomainList *res_domains;
97         /*
98          * Indexed as "sids" in this structure
99          */
100         struct lsa_TransNameArray *res_names;
101 };
102
103 static bool wb_lookupsids_next(struct tevent_req *req,
104                                struct wb_lookupsids_state *state);
105 static void wb_lookupsids_single_done(struct tevent_req *subreq);
106 static void wb_lookupsids_lookuprids_done(struct tevent_req *subreq);
107 static void wb_lookupsids_done(struct tevent_req *subreq);
108
109 struct tevent_req *wb_lookupsids_send(TALLOC_CTX *mem_ctx,
110                                       struct tevent_context *ev,
111                                       struct dom_sid *sids,
112                                       uint32_t num_sids)
113 {
114         struct tevent_req *req;
115         struct wb_lookupsids_state *state;
116         uint32_t i;
117
118         req = tevent_req_create(mem_ctx, &state, struct wb_lookupsids_state);
119         if (req == NULL) {
120                 return NULL;
121         }
122         state->ev = ev;
123         state->sids = sids;
124         state->num_sids = num_sids;
125
126         if (num_sids == 0) {
127                 tevent_req_done(req);
128                 return tevent_req_post(req, ev);
129         }
130
131         state->single_sids = talloc_array(state, uint32_t, num_sids);
132         if (tevent_req_nomem(state->single_sids, req)) {
133                 return tevent_req_post(req, ev);
134         }
135
136         state->res_domains = TALLOC_ZERO_P(state, struct lsa_RefDomainList);
137         if (tevent_req_nomem(state->res_domains, req)) {
138                 return tevent_req_post(req, ev);
139         }
140         state->res_domains->domains = talloc_array(
141                 state->res_domains, struct lsa_DomainInfo, num_sids);
142         if (tevent_req_nomem(state->res_domains->domains, req)) {
143                 return tevent_req_post(req, ev);
144         }
145
146         state->res_names = TALLOC_ZERO_P(state, struct lsa_TransNameArray);
147         if (tevent_req_nomem(state->res_names, req)) {
148                 return tevent_req_post(req, ev);
149         }
150         state->res_names->names = talloc_array(
151                 state->res_names, struct lsa_TranslatedName, num_sids);
152         if (tevent_req_nomem(state->res_names->names, req)) {
153                 return tevent_req_post(req, ev);
154         }
155
156         for (i=0; i<num_sids; i++) {
157                 struct wb_lookupsids_domain *d;
158
159                 d = wb_lookupsids_get_domain(&sids[i], state, &state->domains,
160                                              num_sids);
161                 if (d != NULL) {
162                         d->sids.sids[d->sids.num_sids].sid = &sids[i];
163                         d->sid_indexes[d->sids.num_sids] = i;
164                         d->sids.num_sids += 1;
165                 } else {
166                         state->single_sids[state->num_single_sids] = i;
167                         state->num_single_sids += 1;
168                 }
169         }
170
171         if (!wb_lookupsids_next(req, state)) {
172                 return tevent_req_post(req, ev);
173         }
174         return req;
175 }
176
177 static bool wb_lookupsids_next(struct tevent_req *req,
178                                struct wb_lookupsids_state *state)
179 {
180         struct tevent_req *subreq;
181
182         if (state->domains_done < talloc_array_length(state->domains)) {
183                 struct wb_lookupsids_domain *d;
184                 uint32_t i;
185
186                 d = &state->domains[state->domains_done];
187
188                 if (sid_check_is_domain(&d->sid)) {
189                         state->rids.num_rids = d->sids.num_sids;
190                         state->rids.rids = talloc_array(state, uint32_t,
191                                                         state->rids.num_rids);
192                         if (tevent_req_nomem(state->rids.rids, req)) {
193                                 return false;
194                         }
195                         for (i=0; i<state->rids.num_rids; i++) {
196                                 sid_peek_rid(d->sids.sids[i].sid,
197                                              &state->rids.rids[i]);
198                         }
199                         subreq = dcerpc_wbint_LookupRids_send(
200                                 state, state->ev, dom_child_handle(d->domain),
201                                 &state->rids, &state->domain_name,
202                                 &state->rid_names);
203                         if (tevent_req_nomem(subreq, req)) {
204                                 return false;
205                         }
206                         tevent_req_set_callback(
207                                 subreq, wb_lookupsids_lookuprids_done, req);
208                         return true;
209                 }
210
211                 subreq = dcerpc_wbint_LookupSids_send(
212                         state, state->ev, dom_child_handle(d->domain),
213                         &d->sids, &state->tmp_domains,  &state->tmp_names);
214                 if (tevent_req_nomem(subreq, req)) {
215                         return false;
216                 }
217                 tevent_req_set_callback(subreq, wb_lookupsids_done, req);
218                 return true;
219         }
220
221         if (state->single_sids_done < state->num_single_sids) {
222                 uint32_t sid_idx;
223                 const struct dom_sid *sid;
224
225                 sid_idx = state->single_sids[state->single_sids_done];
226                 sid = &state->sids[sid_idx];
227
228                 subreq = wb_lookupsid_send(state, state->ev, sid);
229                 if (tevent_req_nomem(subreq, req)) {
230                         return false;
231                 }
232                 tevent_req_set_callback(subreq, wb_lookupsids_single_done,
233                                         req);
234                 return true;
235         }
236
237         tevent_req_done(req);
238         return false;
239 }
240
241 /*
242  * Decide whether to do bulk lookupsids. We have optimizations for
243  * passdb via lookuprids and to remote DCs via lookupsids.
244  */
245
246 static bool wb_lookupsids_bulk(const struct dom_sid *sid)
247 {
248         if (sid->num_auths != 5) {
249                 /*
250                  * Only do "S-1-5-21-x-y-z-rid" domains via bulk
251                  * lookup
252                  */
253                 DEBUG(10, ("No bulk setup for SID %s with %d subauths\n",
254                            sid_string_dbg(sid), sid->num_auths));
255                 return false;
256         }
257
258         if (sid_check_is_in_our_domain(sid)) {
259                 /*
260                  * Passdb lookup via lookuprids
261                  */
262                 DEBUG(10, ("%s is in our domain\n", sid_string_tos(sid)));
263                 return true;
264         }
265
266         if ((lp_server_role() == ROLE_DOMAIN_PDC) ||
267             (lp_server_role() == ROLE_DOMAIN_BDC)) {
268                 /*
269                  * Bulk lookups to trusted DCs
270                  */
271                 return (find_domain_from_sid_noinit(sid) != NULL);
272         }
273
274         if (lp_server_role() != ROLE_DOMAIN_MEMBER) {
275                 /*
276                  * Don't do bulk lookups as standalone, the only bulk
277                  * lookup left is for domain members.
278                  */
279                 return false;
280         }
281
282         if (sid_check_is_in_unix_groups(sid) ||
283             sid_check_is_unix_groups(sid) ||
284             sid_check_is_in_unix_users(sid) ||
285             sid_check_is_unix_users(sid) ||
286             sid_check_is_in_builtin(sid) ||
287             sid_check_is_builtin(sid)) {
288                 /*
289                  * These are locally done piece by piece anyway, no
290                  * need for bulk optimizations.
291                  */
292                 return false;
293         }
294
295         /*
296          * All other SIDs are sent to the DC we're connected to as
297          * member via a single lsa_lookupsids call.
298          */
299         return true;
300 }
301
302 static struct wb_lookupsids_domain *wb_lookupsids_get_domain(
303         const struct dom_sid *sid, TALLOC_CTX *mem_ctx,
304         struct wb_lookupsids_domain **pdomains, uint32_t num_sids)
305 {
306         struct wb_lookupsids_domain *domains, *domain;
307         struct winbindd_domain *wb_domain;
308         uint32_t i, num_domains;
309
310         if (!wb_lookupsids_bulk(sid)) {
311                 return NULL;
312         }
313
314         domains = *pdomains;
315         num_domains = talloc_array_length(domains);
316
317         for (i=0; i<num_domains; i++) {
318                 if (dom_sid_compare_domain(sid, &domains[i].sid) == 0) {
319                         return &domains[i];
320                 }
321         }
322
323         wb_domain = find_domain_from_sid_noinit(sid);
324         if (wb_domain == NULL) {
325                 return NULL;
326         }
327
328         domains = talloc_realloc(
329                 mem_ctx, domains, struct wb_lookupsids_domain, num_domains+1);
330         if (domains == NULL) {
331                 return NULL;
332         }
333         *pdomains = domains;
334
335         domain = &domains[num_domains];
336         sid_copy(&domain->sid, sid);
337         sid_split_rid(&domain->sid, NULL);
338         domain->domain = wb_domain;
339
340         domain->sids.sids = talloc_array(domains, struct lsa_SidPtr, num_sids);
341         if (domains->sids.sids == NULL) {
342                 goto fail;
343         }
344         domain->sids.num_sids = 0;
345
346         domain->sid_indexes = talloc_array(domains, uint32_t, num_sids);
347         if (domain->sid_indexes == NULL) {
348                 TALLOC_FREE(domain->sids.sids);
349                 goto fail;
350         }
351         return domain;
352
353 fail:
354         /*
355          * Realloc to the state it was in before
356          */
357         *pdomains = talloc_realloc(
358                 mem_ctx, domains, struct wb_lookupsids_domain, num_domains);
359         return NULL;
360 }
361
362 static bool wb_lookupsids_find_dom_idx(struct lsa_DomainInfo *domain,
363                                        struct lsa_RefDomainList *list,
364                                        uint32_t *idx)
365 {
366         uint32_t i;
367         struct lsa_DomainInfo *new_domain;
368
369         for (i=0; i<list->count; i++) {
370                 if (sid_equal(domain->sid, list->domains[i].sid)) {
371                         *idx = i;
372                         return true;
373                 }
374         }
375
376         new_domain = &list->domains[list->count];
377
378         new_domain->name.string = talloc_strdup(
379                 list->domains, domain->name.string);
380         if (new_domain->name.string == NULL) {
381                 return false;
382         }
383
384         new_domain->sid = dom_sid_dup(list->domains, domain->sid);
385         if (new_domain->sid == NULL) {
386                 return false;
387         }
388
389         *idx = list->count;
390         list->count += 1;
391         return true;
392 }
393
394 static bool wb_lookupsids_move_name(struct lsa_RefDomainList *src_domains,
395                                     struct lsa_TranslatedName *src_name,
396                                     struct lsa_RefDomainList *dst_domains,
397                                     struct lsa_TransNameArray *dst_names,
398                                     uint32_t dst_name_index)
399 {
400         struct lsa_TranslatedName *dst_name;
401         struct lsa_DomainInfo *src_domain;
402         uint32_t src_domain_index, dst_domain_index;
403
404         src_domain_index = src_name->sid_index;
405         src_domain = &src_domains->domains[src_domain_index];
406
407         if (!wb_lookupsids_find_dom_idx(
408                     src_domain, dst_domains, &dst_domain_index)) {
409                 return false;
410         }
411
412         dst_name = &dst_names->names[dst_name_index];
413
414         dst_name->sid_type = src_name->sid_type;
415         dst_name->name.string = talloc_move(dst_names->names,
416                                             &src_name->name.string);
417         dst_name->sid_index = dst_domain_index;
418         dst_names->count += 1;
419
420         return true;
421 }
422
423 static void wb_lookupsids_done(struct tevent_req *subreq)
424 {
425         struct tevent_req *req = tevent_req_callback_data(
426                 subreq, struct tevent_req);
427         struct wb_lookupsids_state *state = tevent_req_data(
428                 req, struct wb_lookupsids_state);
429         struct wb_lookupsids_domain *d;
430         uint32_t i;
431
432         NTSTATUS status, result;
433
434         status = dcerpc_wbint_LookupSids_recv(subreq, state, &result);
435         TALLOC_FREE(subreq);
436         if (tevent_req_nterror(req, status)) {
437                 return;
438         }
439
440         /*
441          * Ignore "result" here. We depend on the individual states in
442          * the translated names.
443          */
444
445         d = &state->domains[state->domains_done];
446
447         for (i=0; i<state->tmp_names.count; i++) {
448
449                 uint32_t res_sid_index = d->sid_indexes[i];
450
451                 if (state->tmp_names.names[i].sid_type == SID_NAME_UNKNOWN) {
452                         /*
453                          * Make unknown SIDs go through
454                          * wb_lookupsid. This retries the forest root.
455                          */
456                         state->single_sids[state->num_single_sids] =
457                                 res_sid_index;
458                         state->num_single_sids += 1;
459                         continue;
460                 }
461                 if (!wb_lookupsids_move_name(
462                             &state->tmp_domains, &state->tmp_names.names[i],
463                             state->res_domains, state->res_names,
464                             res_sid_index)) {
465                         tevent_req_nomem(NULL, req);
466                         return;
467                 }
468         }
469         state->domains_done += 1;
470         wb_lookupsids_next(req, state);
471 }
472
473 static void wb_lookupsids_single_done(struct tevent_req *subreq)
474 {
475         struct tevent_req *req = tevent_req_callback_data(
476                 subreq, struct tevent_req);
477         struct wb_lookupsids_state *state = tevent_req_data(
478                 req, struct wb_lookupsids_state);
479         const char *domain_name, *name;
480         enum lsa_SidType type;
481         uint32_t res_sid_index;
482         uint32_t src_rid;
483
484         struct dom_sid src_domain_sid;
485         struct lsa_DomainInfo src_domain;
486         struct lsa_RefDomainList src_domains;
487         struct lsa_TranslatedName src_name;
488
489         NTSTATUS status;
490
491         status = wb_lookupsid_recv(subreq, talloc_tos(), &type,
492                                    &domain_name, &name);
493         TALLOC_FREE(subreq);
494         if (!NT_STATUS_IS_OK(status)) {
495                 type = SID_NAME_UNKNOWN;
496
497                 domain_name = talloc_strdup(talloc_tos(), "");
498                 if (tevent_req_nomem(domain_name, req)) {
499                         return;
500                 }
501                 name = talloc_strdup(talloc_tos(), "");
502                 if (tevent_req_nomem(name, req)) {
503                         return;
504                 }
505         }
506
507         /*
508          * Fake up structs for wb_lookupsids_move_name
509          */
510         res_sid_index = state->single_sids[state->single_sids_done];
511
512         sid_copy(&src_domain_sid, &state->sids[res_sid_index]);
513         sid_split_rid(&src_domain_sid, &src_rid);
514         src_domain.name.string = domain_name;
515         src_domain.sid = &src_domain_sid;
516
517         src_domains.count = 1;
518         src_domains.domains = &src_domain;
519
520         src_name.sid_type = type;
521         src_name.name.string = name;
522         src_name.sid_index = 0;
523
524         if (!wb_lookupsids_move_name(
525                     &src_domains, &src_name,
526                     state->res_domains, state->res_names,
527                     res_sid_index)) {
528                 tevent_req_nomem(NULL, req);
529                 return;
530         }
531         state->single_sids_done += 1;
532         wb_lookupsids_next(req, state);
533 }
534
535 static void wb_lookupsids_lookuprids_done(struct tevent_req *subreq)
536 {
537         struct tevent_req *req = tevent_req_callback_data(
538                 subreq, struct tevent_req);
539         struct wb_lookupsids_state *state = tevent_req_data(
540                 req, struct wb_lookupsids_state);
541         struct dom_sid src_domain_sid;
542         struct lsa_DomainInfo src_domain;
543         struct lsa_RefDomainList src_domains;
544         NTSTATUS status, result;
545         struct wb_lookupsids_domain *d;
546         uint32_t i;
547
548         status = dcerpc_wbint_LookupRids_recv(subreq, state, &result);
549         TALLOC_FREE(subreq);
550         if (tevent_req_nterror(req, status)) {
551                 return;
552         }
553
554         d = &state->domains[state->domains_done];
555         sid_copy(&src_domain_sid, get_global_sam_sid());
556         src_domain.name.string = get_global_sam_name();
557         src_domain.sid = &src_domain_sid;
558         src_domains.count = 1;
559         src_domains.domains = &src_domain;
560
561         for (i=0; i<state->rid_names.num_principals; i++) {
562                 struct lsa_TranslatedName src_name;
563                 uint32_t res_sid_index;
564
565                 /*
566                  * Fake up structs for wb_lookupsids_move_name
567                  */
568                 res_sid_index = d->sid_indexes[i];
569
570                 src_name.sid_type = state->rid_names.principals[i].type;
571                 src_name.name.string = state->rid_names.principals[i].name;
572                 src_name.sid_index = 0;
573
574                 if (!wb_lookupsids_move_name(
575                             &src_domains, &src_name,
576                             state->res_domains, state->res_names,
577                             res_sid_index)) {
578                         tevent_req_nomem(NULL, req);
579                         return;
580                 }
581         }
582
583         state->domains_done += 1;
584         wb_lookupsids_next(req, state);
585 }
586
587 NTSTATUS wb_lookupsids_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
588                             struct lsa_RefDomainList **domains,
589                             struct lsa_TransNameArray **names)
590 {
591         struct wb_lookupsids_state *state = tevent_req_data(
592                 req, struct wb_lookupsids_state);
593         NTSTATUS status;
594
595         if (tevent_req_is_nterror(req, &status)) {
596                 return status;
597         }
598         *domains = talloc_move(mem_ctx, &state->res_domains);
599         *names = talloc_move(mem_ctx, &state->res_names);
600         return NT_STATUS_OK;
601 }