Use tevent_req_oom
[ira/wip.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(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(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         bool fallback = false;
432
433         NTSTATUS status, result;
434
435         status = dcerpc_wbint_LookupSids_recv(subreq, state, &result);
436         TALLOC_FREE(subreq);
437         if (tevent_req_nterror(req, status)) {
438                 return;
439         }
440
441         d = &state->domains[state->domains_done];
442
443         if (NT_STATUS_IS_ERR(result)) {
444                 fallback = true;
445         } else if (state->tmp_names.count != d->sids.num_sids) {
446                 fallback = true;
447         }
448
449         if (fallback) {
450                 for (i=0; i < d->sids.num_sids; i++) {
451                         uint32_t res_sid_index = d->sid_indexes[i];
452
453                         state->single_sids[state->num_single_sids] =
454                                 res_sid_index;
455                         state->num_single_sids += 1;
456                 }
457                 state->domains_done += 1;
458                 wb_lookupsids_next(req, state);
459                 return;
460         }
461
462         /*
463          * Look at the individual states in the translated names.
464          */
465
466         for (i=0; i<state->tmp_names.count; i++) {
467
468                 uint32_t res_sid_index = d->sid_indexes[i];
469
470                 if (state->tmp_names.names[i].sid_type == SID_NAME_UNKNOWN) {
471                         /*
472                          * Make unknown SIDs go through
473                          * wb_lookupsid. This retries the forest root.
474                          */
475                         state->single_sids[state->num_single_sids] =
476                                 res_sid_index;
477                         state->num_single_sids += 1;
478                         continue;
479                 }
480                 if (!wb_lookupsids_move_name(
481                             &state->tmp_domains, &state->tmp_names.names[i],
482                             state->res_domains, state->res_names,
483                             res_sid_index)) {
484                         tevent_req_oom(req);
485                         return;
486                 }
487         }
488         state->domains_done += 1;
489         wb_lookupsids_next(req, state);
490 }
491
492 static void wb_lookupsids_single_done(struct tevent_req *subreq)
493 {
494         struct tevent_req *req = tevent_req_callback_data(
495                 subreq, struct tevent_req);
496         struct wb_lookupsids_state *state = tevent_req_data(
497                 req, struct wb_lookupsids_state);
498         const char *domain_name, *name;
499         enum lsa_SidType type;
500         uint32_t res_sid_index;
501         uint32_t src_rid;
502
503         struct dom_sid src_domain_sid;
504         struct lsa_DomainInfo src_domain;
505         struct lsa_RefDomainList src_domains;
506         struct lsa_TranslatedName src_name;
507
508         NTSTATUS status;
509
510         status = wb_lookupsid_recv(subreq, talloc_tos(), &type,
511                                    &domain_name, &name);
512         TALLOC_FREE(subreq);
513         if (!NT_STATUS_IS_OK(status)) {
514                 type = SID_NAME_UNKNOWN;
515
516                 domain_name = talloc_strdup(talloc_tos(), "");
517                 if (tevent_req_nomem(domain_name, req)) {
518                         return;
519                 }
520                 name = talloc_strdup(talloc_tos(), "");
521                 if (tevent_req_nomem(name, req)) {
522                         return;
523                 }
524         }
525
526         /*
527          * Fake up structs for wb_lookupsids_move_name
528          */
529         res_sid_index = state->single_sids[state->single_sids_done];
530
531         sid_copy(&src_domain_sid, &state->sids[res_sid_index]);
532         sid_split_rid(&src_domain_sid, &src_rid);
533         src_domain.name.string = domain_name;
534         src_domain.sid = &src_domain_sid;
535
536         src_domains.count = 1;
537         src_domains.domains = &src_domain;
538
539         src_name.sid_type = type;
540         src_name.name.string = name;
541         src_name.sid_index = 0;
542
543         if (!wb_lookupsids_move_name(
544                     &src_domains, &src_name,
545                     state->res_domains, state->res_names,
546                     res_sid_index)) {
547                 tevent_req_oom(req);
548                 return;
549         }
550         state->single_sids_done += 1;
551         wb_lookupsids_next(req, state);
552 }
553
554 static void wb_lookupsids_lookuprids_done(struct tevent_req *subreq)
555 {
556         struct tevent_req *req = tevent_req_callback_data(
557                 subreq, struct tevent_req);
558         struct wb_lookupsids_state *state = tevent_req_data(
559                 req, struct wb_lookupsids_state);
560         struct dom_sid src_domain_sid;
561         struct lsa_DomainInfo src_domain;
562         struct lsa_RefDomainList src_domains;
563         NTSTATUS status, result;
564         struct wb_lookupsids_domain *d;
565         uint32_t i;
566         bool fallback = false;
567
568         status = dcerpc_wbint_LookupRids_recv(subreq, state, &result);
569         TALLOC_FREE(subreq);
570         if (tevent_req_nterror(req, status)) {
571                 return;
572         }
573
574         d = &state->domains[state->domains_done];
575
576         if (NT_STATUS_IS_ERR(result)) {
577                 fallback = true;
578         } else if (state->rid_names.num_principals != d->sids.num_sids) {
579                 fallback = true;
580         }
581
582         if (fallback) {
583                 for (i=0; i < d->sids.num_sids; i++) {
584                         uint32_t res_sid_index = d->sid_indexes[i];
585
586                         state->single_sids[state->num_single_sids] =
587                                 res_sid_index;
588                         state->num_single_sids += 1;
589                 }
590                 state->domains_done += 1;
591                 wb_lookupsids_next(req, state);
592                 return;
593         }
594
595         /*
596          * Look at the individual states in the translated names.
597          */
598
599         sid_copy(&src_domain_sid, get_global_sam_sid());
600         src_domain.name.string = get_global_sam_name();
601         src_domain.sid = &src_domain_sid;
602         src_domains.count = 1;
603         src_domains.domains = &src_domain;
604
605         for (i=0; i<state->rid_names.num_principals; i++) {
606                 struct lsa_TranslatedName src_name;
607                 uint32_t res_sid_index;
608
609                 /*
610                  * Fake up structs for wb_lookupsids_move_name
611                  */
612                 res_sid_index = d->sid_indexes[i];
613
614                 src_name.sid_type = state->rid_names.principals[i].type;
615                 src_name.name.string = state->rid_names.principals[i].name;
616                 src_name.sid_index = 0;
617
618                 if (!wb_lookupsids_move_name(
619                             &src_domains, &src_name,
620                             state->res_domains, state->res_names,
621                             res_sid_index)) {
622                         tevent_req_oom(req);
623                         return;
624                 }
625         }
626
627         state->domains_done += 1;
628         wb_lookupsids_next(req, state);
629 }
630
631 NTSTATUS wb_lookupsids_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
632                             struct lsa_RefDomainList **domains,
633                             struct lsa_TransNameArray **names)
634 {
635         struct wb_lookupsids_state *state = tevent_req_data(
636                 req, struct wb_lookupsids_state);
637         NTSTATUS status;
638
639         if (tevent_req_is_nterror(req, &status)) {
640                 return status;
641         }
642
643         /*
644          * The returned names need to match the given sids,
645          * if not we have a bug in the code!
646          *
647          */
648         SMB_ASSERT(state->res_names->count == state->num_sids);
649
650         /*
651          * Not strictly needed, but it might make debugging in the callers
652          * easier in future, if the talloc_array_length() returns the
653          * expected result...
654          */
655         state->res_domains->domains = talloc_realloc(state->res_domains,
656                                                      state->res_domains->domains,
657                                                      struct lsa_DomainInfo,
658                                                      state->res_domains->count);
659
660         *domains = talloc_move(mem_ctx, &state->res_domains);
661         *names = talloc_move(mem_ctx, &state->res_names);
662         return NT_STATUS_OK;
663 }