CVE-2013-4408:s3:Ensure LookupSids replies arrays are range checked.
[amitay/samba.git] / source4 / winbind / wb_async_helpers.c
index 17efd06c8193224309590d4f5891f278717b2104..e3de2eb38ea923c60a4e744e7de9b0d4193da1ba 100644 (file)
@@ -5,7 +5,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 /*
   a composite API for finding a DC and its name
 */
 
 #include "includes.h"
-#include "libcli/raw/libcliraw.h"
+#include <tevent.h>
 #include "libcli/composite/composite.h"
 #include "winbind/wb_async_helpers.h"
 
-#include "librpc/gen_ndr/nbt.h"
-#include "librpc/gen_ndr/samr.h"
-#include "lib/messaging/irpc.h"
-#include "librpc/gen_ndr/irpc.h"
-#include "librpc/gen_ndr/ndr_irpc.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
 
-struct finddcs_state {
-       struct wb_finddcs *io;
-       struct composite_context *creq;
+#include "winbind/wb_helper.h"
 
-       struct nbtd_getdcname *r;
-       struct irpc_request *ireq;
+
+struct lsa_lookupsids_state {
+       struct composite_context *ctx;
+       uint32_t num_sids;
+       struct lsa_LookupSids r;
+       struct lsa_SidArray sids;
+       struct lsa_TransNameArray names;
+       struct lsa_RefDomainList *domains;
+       uint32_t count;
+       struct wb_sid_object **result;
 };
 
-static void finddcs_getdc(struct irpc_request *req)
+static void lsa_lookupsids_recv_names(struct tevent_req *subreq);
+
+struct composite_context *wb_lsa_lookupsids_send(TALLOC_CTX *mem_ctx,
+                                                struct dcerpc_pipe *lsa_pipe,
+                                                struct policy_handle *handle,
+                                                uint32_t num_sids,
+                                                const struct dom_sid **sids)
 {
-       struct composite_context *c = talloc_get_type(req->async.private,
-                                                     struct composite_context);
-       struct finddcs_state *state =
-               talloc_get_type(c->private, struct finddcs_state);
+       struct composite_context *result;
+       struct lsa_lookupsids_state *state;
+       uint32_t i;
+       struct tevent_req *subreq;
 
-       c->status = irpc_call_recv(req);
-       if (!NT_STATUS_IS_OK(c->status)) {
-               goto done;
-       }
+       result = composite_create(mem_ctx, lsa_pipe->conn->event_ctx);
+       if (result == NULL) goto failed;
 
-       state->io->out.dcs[0].name = talloc_steal(state->io->out.dcs,
-                                                 state->r->out.dcname);
+       state = talloc(result, struct lsa_lookupsids_state);
+       if (state == NULL) goto failed;
+       result->private_data = state;
+       state->ctx = result;
 
-       c->status = NT_STATUS_OK;
-       c->state = SMBCLI_REQUEST_DONE;
+       state->sids.num_sids = num_sids;
+       state->sids.sids = talloc_array(state, struct lsa_SidPtr, num_sids);
+       if (state->sids.sids == NULL) goto failed;
 
- done:
-       if (!NT_STATUS_IS_OK(c->status)) {
-               c->state = SMBCLI_REQUEST_ERROR;
-       }
-               
-       if (c->state >= SMBCLI_REQUEST_DONE &&
-           c->async.fn) {
-               c->async.fn(c);
+       for (i=0; i<num_sids; i++) {
+               state->sids.sids[i].sid = dom_sid_dup(state->sids.sids,
+                                                     sids[i]);
+               if (state->sids.sids[i].sid == NULL) goto failed;
        }
+
+       state->domains = talloc(state, struct lsa_RefDomainList);
+       if (state->domains == NULL) goto failed;
+
+       state->count = 0;
+       state->num_sids = num_sids;
+       state->names.count = 0;
+       state->names.names = NULL;
+
+       state->r.in.handle = handle;
+       state->r.in.sids = &state->sids;
+       state->r.in.names = &state->names;
+       state->r.in.level = 1;
+       state->r.in.count = &state->count;
+       state->r.out.names = &state->names;
+       state->r.out.count = &state->count;
+       state->r.out.domains = &state->domains;
+
+       subreq = dcerpc_lsa_LookupSids_r_send(state,
+                                             result->event_ctx,
+                                             lsa_pipe->binding_handle,
+                                             &state->r);
+       if (subreq == NULL) goto failed;
+       tevent_req_set_callback(subreq, lsa_lookupsids_recv_names, state);
+
+       return result;
+
+ failed:
+       talloc_free(result);
+       return NULL;
 }
 
-/*
-  called when name resolution is finished
-*/
-static void finddcs_resolve(struct composite_context *res_ctx)
+static void lsa_lookupsids_recv_names(struct tevent_req *subreq)
 {
-       struct composite_context *c = talloc_get_type(res_ctx->async.private,
-                                                     struct composite_context);
-       struct finddcs_state *state =
-               talloc_get_type(c->private, struct finddcs_state);
-       uint32_t *nbt_servers;
-
-       state->io->out.num_dcs = 1;
-       state->io->out.dcs = talloc_array(state, struct nbt_dc_name,
-                                         state->io->out.num_dcs);
-       if (state->io->out.dcs == NULL) {
-               c->status = NT_STATUS_NO_MEMORY;
-               goto done;
+       struct lsa_lookupsids_state *state =
+               tevent_req_callback_data(subreq,
+               struct lsa_lookupsids_state);
+       uint32_t i;
+
+       state->ctx->status = dcerpc_lsa_LookupSids_r_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       if (!composite_is_ok(state->ctx)) return;
+       state->ctx->status = state->r.out.result;
+       if (!NT_STATUS_IS_OK(state->ctx->status) &&
+           !NT_STATUS_EQUAL(state->ctx->status, STATUS_SOME_UNMAPPED)) {
+               composite_error(state->ctx, state->ctx->status);
+               return;
        }
 
-       c->status = resolve_name_recv(res_ctx, state->io->out.dcs,
-                                     &state->io->out.dcs[0].address);
-       if (!NT_STATUS_IS_OK(c->status)) {
-               goto done;
+       if (state->names.count != state->num_sids) {
+               composite_error(state->ctx,
+                               NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
        }
 
-       nbt_servers = irpc_servers_byname(state->io->in.msg_ctx, "nbt_server");
-       if ((nbt_servers == NULL) || (nbt_servers[0] == 0)) {
-               c->status = NT_STATUS_NO_LOGON_SERVERS;
-               goto done;
+       state->result = talloc_array(state, struct wb_sid_object *,
+                                    state->num_sids);
+       if (composite_nomem(state->result, state->ctx)) return;
+
+       for (i=0; i<state->num_sids; i++) {
+               struct lsa_TranslatedName *name =
+                       &state->r.out.names->names[i];
+               struct lsa_DomainInfo *dom;
+               struct lsa_RefDomainList *domains =
+                       state->domains;
+
+               state->result[i] = talloc_zero(state->result,
+                                              struct wb_sid_object);
+               if (composite_nomem(state->result[i], state->ctx)) return;
+
+               state->result[i]->type = name->sid_type;
+               if (state->result[i]->type == SID_NAME_UNKNOWN) {
+                       continue;
+               }
+
+               if (domains == NULL) {
+                       composite_error(state->ctx,
+                                       NT_STATUS_INVALID_NETWORK_RESPONSE);
+                       return;
+               }
+               if (name->sid_index >= domains->count) {
+                       composite_error(state->ctx,
+                                       NT_STATUS_INVALID_NETWORK_RESPONSE);
+                       return;
+               }
+
+               dom = &domains->domains[name->sid_index];
+               state->result[i]->domain = talloc_reference(state->result[i],
+                                                           dom->name.string);
+               if ((name->sid_type == SID_NAME_DOMAIN) ||
+                   (name->name.string == NULL)) {
+                       state->result[i]->name =
+                               talloc_strdup(state->result[i], "");
+               } else {
+                       state->result[i]->name =
+                               talloc_steal(state->result[i],
+                                            name->name.string);
+               }
+
+               if (composite_nomem(state->result[i]->name, state->ctx)) {
+                       return;
+               }
        }
 
-       state->r = talloc(state, struct nbtd_getdcname);
-       if (state->r == NULL) {
-               c->status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
+       composite_done(state->ctx);
+}
 
-       state->r->in.domainname = talloc_strdup(state->r, lp_workgroup());
-       state->r->in.ip_address = state->io->out.dcs[0].address;
-       state->r->in.my_computername = lp_netbios_name();
-       state->r->in.my_accountname = talloc_asprintf(state->r, "%s$",
-                                                     lp_netbios_name());
-       state->r->in.account_control = ACB_WSTRUST;
-       state->r->in.domain_sid = secrets_get_domain_sid(state->r,
-                                                        lp_workgroup());
-
-       if ((state->r->in.domainname == NULL) ||
-           (state->r->in.my_accountname == NULL)) {
-               DEBUG(0, ("talloc failed\n"));
-               c->status = NT_STATUS_NO_MEMORY;
-               goto done;
+NTSTATUS wb_lsa_lookupsids_recv(struct composite_context *c,
+                               TALLOC_CTX *mem_ctx,
+                               struct wb_sid_object ***names)
+{
+       NTSTATUS status = composite_wait(c);
+       if (NT_STATUS_IS_OK(status)) {
+               struct lsa_lookupsids_state *state =
+                       talloc_get_type(c->private_data,
+                                       struct lsa_lookupsids_state);
+               *names = talloc_steal(mem_ctx, state->result);
        }
-       if (state->r->in.domain_sid == NULL) {
-               c->status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
-               goto done;
+       talloc_free(c);
+       return status;
+}
+
+
+struct lsa_lookupnames_state {
+       struct composite_context *ctx;
+       uint32_t num_names;
+       struct lsa_LookupNames r;
+       struct lsa_TransSidArray sids;
+       struct lsa_RefDomainList *domains;
+       uint32_t count;
+       struct wb_sid_object **result;
+};
+
+static void lsa_lookupnames_recv_sids(struct tevent_req *subreq);
+
+struct composite_context *wb_lsa_lookupnames_send(TALLOC_CTX *mem_ctx,
+                                                 struct dcerpc_pipe *lsa_pipe,
+                                                 struct policy_handle *handle,
+                                                 uint32_t num_names,
+                                                 const char **names)
+{
+       struct composite_context *result;
+       struct lsa_lookupnames_state *state;
+       struct tevent_req *subreq;
+
+       struct lsa_String *lsa_names;
+       uint32_t i;
+
+       result = composite_create(mem_ctx, lsa_pipe->conn->event_ctx);
+       if (result == NULL) goto failed;
+
+       state = talloc(result, struct lsa_lookupnames_state);
+       if (state == NULL) goto failed;
+       result->private_data = state;
+       state->ctx = result;
+
+       state->sids.count = 0;
+       state->sids.sids = NULL;
+       state->num_names = num_names;
+       state->count = 0;
+
+       lsa_names = talloc_array(state, struct lsa_String, num_names);
+       if (lsa_names == NULL) goto failed;
+
+       for (i=0; i<num_names; i++) {
+               lsa_names[i].string = names[i];
        }
 
-       state->ireq = irpc_call_send(state->io->in.msg_ctx, nbt_servers[0],
-                                    &dcerpc_table_irpc, DCERPC_NBTD_GETDCNAME,
-                                    state->r, state);
-       
-       if (state->ireq == NULL) {
-               c->status = NT_STATUS_NO_MEMORY;
-               goto done;
+       state->domains = talloc(state, struct lsa_RefDomainList);
+       if (state->domains == NULL) goto failed;
+
+       state->r.in.handle = handle;
+       state->r.in.num_names = num_names;
+       state->r.in.names = lsa_names;
+       state->r.in.sids = &state->sids;
+       state->r.in.level = 1;
+       state->r.in.count = &state->count;
+       state->r.out.count = &state->count;
+       state->r.out.sids = &state->sids;
+       state->r.out.domains = &state->domains;
+
+       subreq = dcerpc_lsa_LookupNames_r_send(state,
+                                              result->event_ctx,
+                                              lsa_pipe->binding_handle,
+                                              &state->r);
+       if (subreq == NULL) goto failed;
+       tevent_req_set_callback(subreq, lsa_lookupnames_recv_sids, state);
+
+       return result;
+
+ failed:
+       talloc_free(result);
+       return NULL;
+}
+
+static void lsa_lookupnames_recv_sids(struct tevent_req *subreq)
+{
+       struct lsa_lookupnames_state *state =
+               tevent_req_callback_data(subreq,
+               struct lsa_lookupnames_state);
+       uint32_t i;
+
+       state->ctx->status = dcerpc_lsa_LookupNames_r_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       if (!composite_is_ok(state->ctx)) return;
+       state->ctx->status = state->r.out.result;
+       if (!NT_STATUS_IS_OK(state->ctx->status) &&
+           !NT_STATUS_EQUAL(state->ctx->status, STATUS_SOME_UNMAPPED)) {
+               composite_error(state->ctx, state->ctx->status);
+               return;
        }
 
-       c->status = NT_STATUS_OK;
-       state->ireq->async.fn = finddcs_getdc;
-       state->ireq->async.private = c;
+       state->result = talloc_array(state, struct wb_sid_object *,
+                                    state->num_names);
+       if (composite_nomem(state->result, state->ctx)) return;
 
- done:
-       if (!NT_STATUS_IS_OK(c->status)) {
-               c->state = SMBCLI_REQUEST_ERROR;
+       for (i=0; i<state->num_names; i++) {
+               struct lsa_TranslatedSid *sid = &state->r.out.sids->sids[i];
+               struct lsa_RefDomainList *domains = state->domains;
+               struct lsa_DomainInfo *dom;
+
+               state->result[i] = talloc_zero(state->result,
+                                              struct wb_sid_object);
+               if (composite_nomem(state->result[i], state->ctx)) return;
+
+               state->result[i]->type = sid->sid_type;
+               if (state->result[i]->type == SID_NAME_UNKNOWN) {
+                       continue;
+               }
+
+               if (sid->sid_index >= domains->count) {
+                       composite_error(state->ctx,
+                                       NT_STATUS_INVALID_PARAMETER);
+                       return;
+               }
+
+               dom = &domains->domains[sid->sid_index];
+
+               state->result[i]->sid = dom_sid_add_rid(state->result[i],
+                                                       dom->sid, sid->rid);
        }
-               
-       if (c->state >= SMBCLI_REQUEST_DONE &&
-           c->async.fn) {
-               c->async.fn(c);
+
+       composite_done(state->ctx);
+}
+
+NTSTATUS wb_lsa_lookupnames_recv(struct composite_context *c,
+                                TALLOC_CTX *mem_ctx,
+                                struct wb_sid_object ***sids)
+{
+       NTSTATUS status = composite_wait(c);
+       if (NT_STATUS_IS_OK(status)) {
+               struct lsa_lookupnames_state *state =
+                       talloc_get_type(c->private_data,
+                                       struct lsa_lookupnames_state);
+               *sids = talloc_steal(mem_ctx, state->result);
        }
+       talloc_free(c);
+       return status;
 }
+struct samr_getuserdomgroups_state {
+       struct composite_context *ctx;
+       struct dcerpc_pipe *samr_pipe;
+
+       uint32_t num_rids;
+       uint32_t *rids;
+
+       struct samr_RidWithAttributeArray *rid_array;
+
+       struct policy_handle *user_handle;
+       struct samr_OpenUser o;
+       struct samr_GetGroupsForUser g;
+       struct samr_Close c;
+};
 
-struct composite_context *wb_finddcs_send(struct wb_finddcs *io,
-                                         struct event_context *event_ctx)
+static void samr_usergroups_recv_open(struct tevent_req *subreq);
+static void samr_usergroups_recv_groups(struct tevent_req *subreq);
+static void samr_usergroups_recv_close(struct tevent_req *subreq);
+
+struct composite_context *wb_samr_userdomgroups_send(TALLOC_CTX *mem_ctx,
+                                                    struct dcerpc_pipe *samr_pipe,
+                                                    struct policy_handle *domain_handle,
+                                                    uint32_t rid)
 {
-       struct composite_context *c;
-       struct finddcs_state *state;
-       struct nbt_name name;
+       struct composite_context *result;
+       struct samr_getuserdomgroups_state *state;
+       struct tevent_req *subreq;
 
-       c = talloc_zero(NULL, struct composite_context);
-       if (c == NULL) goto failed;
-       c->state = SMBCLI_REQUEST_SEND;
-       c->event_ctx = event_ctx;
+       result = composite_create(mem_ctx, samr_pipe->conn->event_ctx);
+       if (result == NULL) goto failed;
 
-       state = talloc(c, struct finddcs_state);
+       state = talloc(result, struct samr_getuserdomgroups_state);
        if (state == NULL) goto failed;
+       result->private_data = state;
+       state->ctx = result;
 
-       state->io = io;
+       state->samr_pipe = samr_pipe;
 
-       make_nbt_name(&name, io->in.domain, 0x1c);
-       state->creq = resolve_name_send(&name, c->event_ctx,
-                                       lp_name_resolve_order());
+       state->user_handle = talloc(state, struct policy_handle);
+       if (state->user_handle == NULL) goto failed;
 
-       if (state->creq == NULL) goto failed;
-       state->creq->async.private = c;
-       state->creq->async.fn = finddcs_resolve;
-       c->private = state;
+       state->o.in.domain_handle = domain_handle;
+       state->o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+       state->o.in.rid = rid;
+       state->o.out.user_handle = state->user_handle;
 
-       return c;
-failed:
-       talloc_free(c);
+       subreq = dcerpc_samr_OpenUser_r_send(state,
+                                            result->event_ctx,
+                                            state->samr_pipe->binding_handle,
+                                            &state->o);
+       if (subreq == NULL) goto failed;
+       tevent_req_set_callback(subreq, samr_usergroups_recv_open, state);
+
+       return result;
+
+ failed:
+       talloc_free(result);
        return NULL;
 }
+                                             
+static void samr_usergroups_recv_open(struct tevent_req *subreq)
+{
+       struct samr_getuserdomgroups_state *state =
+               tevent_req_callback_data(subreq,
+               struct samr_getuserdomgroups_state);
+
+       state->ctx->status = dcerpc_samr_OpenUser_r_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       if (!composite_is_ok(state->ctx)) return;
+       state->ctx->status = state->o.out.result;
+       if (!composite_is_ok(state->ctx)) return;
+
+       state->g.in.user_handle = state->user_handle;
+       state->g.out.rids = &state->rid_array;
+
+       subreq = dcerpc_samr_GetGroupsForUser_r_send(state,
+                                                    state->ctx->event_ctx,
+                                                    state->samr_pipe->binding_handle,
+                                                    &state->g);
+       if (composite_nomem(subreq, state->ctx)) return;
+       tevent_req_set_callback(subreq, samr_usergroups_recv_groups, state);
+}
 
-NTSTATUS wb_finddcs_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
+static void samr_usergroups_recv_groups(struct tevent_req *subreq)
 {
-       NTSTATUS status;
+       struct samr_getuserdomgroups_state *state =
+               tevent_req_callback_data(subreq,
+               struct samr_getuserdomgroups_state);
+
+       state->ctx->status = dcerpc_samr_GetGroupsForUser_r_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       if (!composite_is_ok(state->ctx)) return;
+       state->ctx->status = state->g.out.result;
+       if (!composite_is_ok(state->ctx)) return;
+
+       state->c.in.handle = state->user_handle;
+       state->c.out.handle = state->user_handle;
+
+       subreq = dcerpc_samr_Close_r_send(state,
+                                         state->ctx->event_ctx,
+                                         state->samr_pipe->binding_handle,
+                                         &state->c);
+       if (composite_nomem(subreq, state->ctx)) return;
+       tevent_req_set_callback(subreq, samr_usergroups_recv_close, state);
+}
 
-       status = composite_wait(c);
+static void samr_usergroups_recv_close(struct tevent_req *subreq)
+{
+        struct samr_getuserdomgroups_state *state =
+               tevent_req_callback_data(subreq,
+               struct samr_getuserdomgroups_state);
 
-       if (NT_STATUS_IS_OK(status)) {
-               struct finddcs_state *state =
-                       talloc_get_type(c->private, struct finddcs_state);
-               talloc_steal(mem_ctx, state->io->out.dcs);
-       }
+       state->ctx->status = dcerpc_samr_Close_r_recv(subreq, state);
+       TALLOC_FREE(subreq);
+        if (!composite_is_ok(state->ctx)) return;
+        state->ctx->status = state->c.out.result;
+        if (!composite_is_ok(state->ctx)) return;
 
-       talloc_free(c);
-       return status;
+       composite_done(state->ctx);
 }
 
-NTSTATUS wb_finddcs(struct wb_finddcs *io, TALLOC_CTX *mem_ctx,
-                   struct event_context *ev)
+NTSTATUS wb_samr_userdomgroups_recv(struct composite_context *ctx,
+                                   TALLOC_CTX *mem_ctx,
+                                   uint32_t *num_rids, uint32_t **rids)
 {
-       struct composite_context *c = wb_finddcs_send(io, ev);
-       return wb_finddcs_recv(c, mem_ctx);
+        struct samr_getuserdomgroups_state *state =
+                talloc_get_type(ctx->private_data,
+                                struct samr_getuserdomgroups_state);
+
+       uint32_t i;
+       NTSTATUS status = composite_wait(ctx);
+       if (!NT_STATUS_IS_OK(status)) goto done;
+
+       *num_rids = state->rid_array->count;
+       *rids = talloc_array(mem_ctx, uint32_t, *num_rids);
+       if (*rids == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       for (i=0; i<*num_rids; i++) {
+               (*rids)[i] = state->rid_array->rids[i].rid;
+       }
+
+ done:
+       talloc_free(ctx);
+       return status;
 }