libcli/cldap: fix a crash bug in cldap_socket_recv_dgram() (bug #8593)
[sfrench/samba-autobuild/.git] / libcli / cldap / cldap.c
index d3fb0bb1e92d1aaf72c0a34fd153a76abd9da905..29c3b7bb51f82a01d3427cad39437317f6cb264a 100644 (file)
@@ -51,7 +51,7 @@
 */
 struct cldap_socket {
        /* the low level socket */
-       struct tsocket_context *sock;
+       struct tdgram_context *sock;
 
        /*
         * Are we in connected mode, which means
@@ -61,15 +61,6 @@ struct cldap_socket {
         */
        bool connected;
 
-       /*
-        * we allow sync requests only, if the caller
-        * did not pass an event context to cldap_socket_init()
-        */
-       struct {
-               bool allow_poll;
-               struct tevent_context *ctx;
-       } event;
-
        /* the queue for outgoing dgrams */
        struct tevent_queue *send_queue;
 
@@ -86,6 +77,7 @@ struct cldap_socket {
 
        /* what to do with incoming request packets */
        struct {
+               struct tevent_context *ev;
                void (*handler)(struct cldap_socket *,
                                void *private_data,
                                struct cldap_incoming *);
@@ -97,6 +89,7 @@ struct cldap_search_state {
        struct cldap_search_state *prev, *next;
 
        struct {
+               struct tevent_context *ev;
                struct cldap_socket *cldap;
        } caller;
 
@@ -120,8 +113,6 @@ struct cldap_search_state {
 
 static int cldap_socket_destructor(struct cldap_socket *c)
 {
-       tsocket_disconnect(c->sock);
-
        while (c->searches.list) {
                struct cldap_search_state *s = c->searches.list;
                DLIST_REMOVE(c->searches.list, s);
@@ -138,6 +129,8 @@ static void cldap_recvfrom_done(struct tevent_req *subreq);
 
 static bool cldap_recvfrom_setup(struct cldap_socket *c)
 {
+       struct tevent_context *ev;
+
        if (c->recv_subreq) {
                return true;
        }
@@ -146,7 +139,12 @@ static bool cldap_recvfrom_setup(struct cldap_socket *c)
                return true;
        }
 
-       c->recv_subreq = tsocket_recvfrom_send(c->sock, c);
+       ev = c->incoming.ev;
+       if (ev == NULL) {
+               ev = c->searches.list->caller.ev;
+       }
+
+       c->recv_subreq = tdgram_recvfrom_send(c, ev, c->sock);
        if (!c->recv_subreq) {
                return false;
        }
@@ -169,7 +167,7 @@ static void cldap_recvfrom_stop(struct cldap_socket *c)
        c->recv_subreq = NULL;
 }
 
-static void cldap_socket_recv_dgram(struct cldap_socket *c,
+static bool cldap_socket_recv_dgram(struct cldap_socket *c,
                                    struct cldap_incoming *in);
 
 static void cldap_recvfrom_done(struct tevent_req *subreq)
@@ -178,6 +176,7 @@ static void cldap_recvfrom_done(struct tevent_req *subreq)
                                 struct cldap_socket);
        struct cldap_incoming *in = NULL;
        ssize_t ret;
+       bool setup_done;
 
        c->recv_subreq = NULL;
 
@@ -186,11 +185,11 @@ static void cldap_recvfrom_done(struct tevent_req *subreq)
                goto nomem;
        }
 
-       ret = tsocket_recvfrom_recv(subreq,
-                                   &in->recv_errno,
-                                   in,
-                                   &in->buf,
-                                   &in->src);
+       ret = tdgram_recvfrom_recv(subreq,
+                                  &in->recv_errno,
+                                  in,
+                                  &in->buf,
+                                  &in->src);
        talloc_free(subreq);
        subreq = NULL;
        if (ret >= 0) {
@@ -201,10 +200,10 @@ static void cldap_recvfrom_done(struct tevent_req *subreq)
        }
 
        /* this function should free or steal 'in' */
-       cldap_socket_recv_dgram(c, in);
+       setup_done = cldap_socket_recv_dgram(c, in);
        in = NULL;
 
-       if (!cldap_recvfrom_setup(c)) {
+       if (!setup_done && !cldap_recvfrom_setup(c)) {
                goto nomem;
        }
 
@@ -220,7 +219,7 @@ nomem:
 /*
   handle recv events on a cldap socket
 */
-static void cldap_socket_recv_dgram(struct cldap_socket *c,
+static bool cldap_socket_recv_dgram(struct cldap_socket *c,
                                    struct cldap_incoming *in)
 {
        DATA_BLOB blob;
@@ -259,45 +258,67 @@ static void cldap_socket_recv_dgram(struct cldap_socket *c,
        p = idr_find(c->searches.idr, in->ldap_msg->messageid);
        if (p == NULL) {
                if (!c->incoming.handler) {
-                       goto done;
+                       TALLOC_FREE(in);
+                       return true;
                }
 
                /* this function should free or steal 'in' */
                c->incoming.handler(c, c->incoming.private_data, in);
-               return;
+               return false;
        }
 
-       search = talloc_get_type(p, struct cldap_search_state);
+       search = talloc_get_type_abort(p, struct cldap_search_state);
        search->response.in = talloc_move(search, &in);
        search->response.asn1 = asn1;
        search->response.asn1->ofs = 0;
 
+       DLIST_REMOVE(c->searches.list, search);
+
+       if (cldap_recvfrom_setup(c)) {
+               tevent_req_done(search->req);
+               return true;
+       }
+
+       /*
+        * This request was ok, just defer the notify of the caller
+        * and then just fail the next request if needed
+        */
+       tevent_req_defer_callback(search->req, search->caller.ev);
        tevent_req_done(search->req);
-       goto done;
 
+       status = NT_STATUS_NO_MEMORY;
+       /* in is NULL it this point */
+       goto nterror;
 nomem:
        in->recv_errno = ENOMEM;
 error:
-       status = map_nt_error_from_unix(in->recv_errno);
+       status = map_nt_error_from_unix_common(in->recv_errno);
 nterror:
+       TALLOC_FREE(in);
        /* in connected mode the first pending search gets the error */
        if (!c->connected) {
                /* otherwise we just ignore the error */
-               goto done;
+               return false;
        }
        if (!c->searches.list) {
-               goto done;
+               return false;
        }
+       /*
+        * We might called tevent_req_done() for a successful
+        * search before, so we better deliver the failure
+        * after the success, that is why we better also
+        * use tevent_req_defer_callback() here.
+        */
+       tevent_req_defer_callback(c->searches.list->req,
+                                 c->searches.list->caller.ev);
        tevent_req_nterror(c->searches.list->req, status);
-done:
-       talloc_free(in);
+       return false;
 }
 
 /*
   initialise a cldap_sock
 */
 NTSTATUS cldap_socket_init(TALLOC_CTX *mem_ctx,
-                          struct tevent_context *ev,
                           const struct tsocket_address *local_addr,
                           const struct tsocket_address *remote_addr,
                           struct cldap_socket **_cldap)
@@ -306,27 +327,46 @@ NTSTATUS cldap_socket_init(TALLOC_CTX *mem_ctx,
        struct tsocket_address *any = NULL;
        NTSTATUS status;
        int ret;
+       const char *fam = NULL;
+
+       if (local_addr == NULL && remote_addr == NULL) {
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
+       if (remote_addr) {
+               bool is_ipv4;
+               bool is_ipv6;
+
+               is_ipv4 = tsocket_address_is_inet(remote_addr, "ipv4");
+               is_ipv6 = tsocket_address_is_inet(remote_addr, "ipv6");
+
+               if (is_ipv4) {
+                       fam = "ipv4";
+               } else if (is_ipv6) {
+                       fam = "ipv6";
+               } else {
+                       return NT_STATUS_INVALID_ADDRESS;
+               }
+       }
 
        c = talloc_zero(mem_ctx, struct cldap_socket);
        if (!c) {
                goto nomem;
        }
 
-       if (!ev) {
-               ev = tevent_context_init(c);
-               if (!ev) {
-                       goto nomem;
+       if (!local_addr) {
+               /*
+                * Here we know the address family of the remote address.
+                */
+               if (fam == NULL) {
+                       return NT_STATUS_INVALID_PARAMETER_MIX;
                }
-               c->event.allow_poll = true;
-       }
-       c->event.ctx = ev;
 
-       if (!local_addr) {
-               ret = tsocket_address_inet_from_strings(c, "ipv4",
+               ret = tsocket_address_inet_from_strings(c, fam,
                                                        NULL, 0,
                                                        &any);
                if (ret != 0) {
-                       status = map_nt_error_from_unix(errno);
+                       status = map_nt_error_from_unix_common(errno);
                        goto nterror;
                }
                local_addr = any;
@@ -337,23 +377,15 @@ NTSTATUS cldap_socket_init(TALLOC_CTX *mem_ctx,
                goto nomem;
        }
 
-       ret = tsocket_address_create_socket(local_addr,
-                                           TSOCKET_TYPE_DGRAM,
-                                           c, &c->sock);
+       ret = tdgram_inet_udp_socket(local_addr, remote_addr,
+                                    c, &c->sock);
        if (ret != 0) {
-               status = map_nt_error_from_unix(errno);
+               status = map_nt_error_from_unix_common(errno);
                goto nterror;
        }
        talloc_free(any);
 
-       tsocket_set_event_context(c->sock, c->event.ctx);
-
        if (remote_addr) {
-               ret = tsocket_connect(c->sock, remote_addr);
-               if (ret != 0) {
-                       status = map_nt_error_from_unix(errno);
-                       goto nterror;
-               }
                c->connected = true;
        }
 
@@ -378,6 +410,7 @@ nterror:
   setup a handler for incoming requests
 */
 NTSTATUS cldap_set_incoming_handler(struct cldap_socket *c,
+                                   struct tevent_context *ev,
                                    void (*handler)(struct cldap_socket *,
                                                    void *private_data,
                                                    struct cldap_incoming *),
@@ -387,11 +420,7 @@ NTSTATUS cldap_set_incoming_handler(struct cldap_socket *c,
                return NT_STATUS_PIPE_CONNECTED;
        }
 
-       /* if sync requests are allowed, we don't allow an incoming handler */
-       if (c->event.allow_poll) {
-               return NT_STATUS_INVALID_PIPE_STATE;
-       }
-
+       c->incoming.ev = ev;
        c->incoming.handler = handler;
        c->incoming.private_data = private_data;
 
@@ -408,7 +437,7 @@ struct cldap_reply_state {
        DATA_BLOB blob;
 };
 
-static void cldap_reply_state_destroy(struct tevent_req *req);
+static void cldap_reply_state_destroy(struct tevent_req *subreq);
 
 /*
   queue a cldap reply for send
@@ -419,12 +448,16 @@ NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io)
        struct ldap_message *msg;
        DATA_BLOB blob1, blob2;
        NTSTATUS status;
-       struct tevent_req *req;
+       struct tevent_req *subreq;
 
        if (cldap->connected) {
                return NT_STATUS_PIPE_CONNECTED;
        }
 
+       if (cldap->incoming.ev == NULL) {
+               return NT_STATUS_INVALID_PIPE_STATE;
+       }
+
        if (!io->dest) {
                return NT_STATUS_INVALID_ADDRESS;
        }
@@ -476,17 +509,18 @@ NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io)
        data_blob_free(&blob1);
        data_blob_free(&blob2);
 
-       req = tsocket_sendto_queue_send(state,
-                                       cldap->sock,
-                                       cldap->send_queue,
-                                       state->blob.data,
-                                       state->blob.length,
-                                       state->dest);
-       if (!req) {
+       subreq = tdgram_sendto_queue_send(state,
+                                         cldap->incoming.ev,
+                                         cldap->sock,
+                                         cldap->send_queue,
+                                         state->blob.data,
+                                         state->blob.length,
+                                         state->dest);
+       if (!subreq) {
                goto nomem;
        }
        /* the callback will just free the state, as we don't need a result */
-       tevent_req_set_callback(req, cldap_reply_state_destroy, state);
+       tevent_req_set_callback(subreq, cldap_reply_state_destroy, state);
 
        return NT_STATUS_OK;
 
@@ -497,13 +531,13 @@ failed:
        return status;
 }
 
-static void cldap_reply_state_destroy(struct tevent_req *req)
+static void cldap_reply_state_destroy(struct tevent_req *subreq)
 {
-       struct cldap_reply_state *state = tevent_req_callback_data(req,
+       struct cldap_reply_state *state = tevent_req_callback_data(subreq,
                                          struct cldap_reply_state);
 
        /* we don't want to know the result here, we just free the state */
-       talloc_free(req);
+       talloc_free(subreq);
        talloc_free(state);
 }
 
@@ -529,8 +563,9 @@ static void cldap_search_state_wakeup_done(struct tevent_req *subreq);
   queue a cldap reply for send
 */
 struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
-                                   struct cldap_socket *cldap,
-                                   const struct cldap_search *io)
+                                    struct tevent_context *ev,
+                                    struct cldap_socket *cldap,
+                                    const struct cldap_search *io)
 {
        struct tevent_req *req, *subreq;
        struct cldap_search_state *state = NULL;
@@ -547,6 +582,7 @@ struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
                return NULL;
        }
        ZERO_STRUCTP(state);
+       state->caller.ev = ev;
        state->req = req;
        state->caller.cldap = cldap;
        state->message_id = -1;
@@ -559,7 +595,7 @@ struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
                        goto post;
                }
                ret = tsocket_address_inet_from_strings(state,
-                                                       "ipv4",
+                                                       "ip",
                                                        io->in.dest_address,
                                                        io->in.dest_port,
                                                        &state->request.dest);
@@ -622,20 +658,22 @@ struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
        now = tevent_timeval_current();
        end = now;
        for (i = 0; i < state->request.count; i++) {
-               end = tevent_timeval_add(&end, 0, state->request.delay);
+               end = tevent_timeval_add(&end, state->request.delay / 1000000,
+                                        state->request.delay % 1000000);
        }
 
-       if (!tevent_req_set_endtime(req, state->caller.cldap->event.ctx, end)) {
-               tevent_req_nomem(NULL, req);
+       if (!tevent_req_set_endtime(req, state->caller.ev, end)) {
+               tevent_req_oom(req);
                goto post;
        }
 
-       subreq = tsocket_sendto_queue_send(state,
-                                          state->caller.cldap->sock,
-                                          state->caller.cldap->send_queue,
-                                          state->request.blob.data,
-                                          state->request.blob.length,
-                                          state->request.dest);
+       subreq = tdgram_sendto_queue_send(state,
+                                         state->caller.ev,
+                                         state->caller.cldap->sock,
+                                         state->caller.cldap->send_queue,
+                                         state->request.blob.data,
+                                         state->request.blob.length,
+                                         state->request.dest);
        if (tevent_req_nomem(subreq, req)) {
                goto post;
        }
@@ -646,7 +684,7 @@ struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
        return req;
 
  post:
-       return tevent_req_post(req, cldap->event.ctx);
+       return tevent_req_post(req, state->caller.ev);
 }
 
 static void cldap_search_state_queue_done(struct tevent_req *subreq)
@@ -659,11 +697,11 @@ static void cldap_search_state_queue_done(struct tevent_req *subreq)
        int sys_errno = 0;
        struct timeval next;
 
-       ret = tsocket_sendto_queue_recv(subreq, &sys_errno);
+       ret = tdgram_sendto_queue_recv(subreq, &sys_errno);
        talloc_free(subreq);
        if (ret == -1) {
                NTSTATUS status;
-               status = map_nt_error_from_unix(sys_errno);
+               status = map_nt_error_from_unix_common(sys_errno);
                DLIST_REMOVE(state->caller.cldap->searches.list, state);
                ZERO_STRUCT(state->caller.cldap);
                tevent_req_nterror(req, status);
@@ -674,7 +712,7 @@ static void cldap_search_state_queue_done(struct tevent_req *subreq)
 
        /* wait for incoming traffic */
        if (!cldap_recvfrom_setup(state->caller.cldap)) {
-               tevent_req_nomem(NULL, req);
+               tevent_req_oom(req);
                return;
        }
 
@@ -683,9 +721,10 @@ static void cldap_search_state_queue_done(struct tevent_req *subreq)
                return;
        }
 
-       next = tevent_timeval_current_ofs(0, state->request.delay);
+       next = tevent_timeval_current_ofs(state->request.delay / 1000000,
+                                         state->request.delay % 1000000);
        subreq = tevent_wakeup_send(state,
-                                   state->caller.cldap->event.ctx,
+                                   state->caller.ev,
                                    next);
        if (tevent_req_nomem(subreq, req)) {
                return;
@@ -708,12 +747,13 @@ static void cldap_search_state_wakeup_done(struct tevent_req *subreq)
                return;
        }
 
-       subreq = tsocket_sendto_queue_send(state,
-                                          state->caller.cldap->sock,
-                                          state->caller.cldap->send_queue,
-                                          state->request.blob.data,
-                                          state->request.blob.length,
-                                          state->request.dest);
+       subreq = tdgram_sendto_queue_send(state,
+                                         state->caller.ev,
+                                         state->caller.cldap->sock,
+                                         state->caller.cldap->send_queue,
+                                         state->request.blob.data,
+                                         state->request.blob.length,
+                                         state->request.dest);
        if (tevent_req_nomem(subreq, req)) {
                return;
        }
@@ -797,29 +837,47 @@ NTSTATUS cldap_search(struct cldap_socket *cldap,
                      TALLOC_CTX *mem_ctx,
                      struct cldap_search *io)
 {
+       TALLOC_CTX *frame;
        struct tevent_req *req;
+       struct tevent_context *ev;
        NTSTATUS status;
 
-       if (!cldap->event.allow_poll) {
+       if (cldap->searches.list) {
+               return NT_STATUS_PIPE_BUSY;
+       }
+
+       if (cldap->incoming.handler) {
                return NT_STATUS_INVALID_PIPE_STATE;
        }
 
-       if (cldap->searches.list) {
-               return NT_STATUS_PIPE_BUSY;
+       frame = talloc_stackframe();
+
+       ev = tevent_context_init(frame);
+       if (ev == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       req = cldap_search_send(mem_ctx, cldap, io);
-       NT_STATUS_HAVE_NO_MEMORY(req);
+       req = cldap_search_send(mem_ctx, ev, cldap, io);
+       if (req == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       if (!tevent_req_poll(req, cldap->event.ctx)) {
-               talloc_free(req);
-               return NT_STATUS_INTERNAL_ERROR;
+       if (!tevent_req_poll(req, ev)) {
+               status = map_nt_error_from_unix_common(errno);
+               TALLOC_FREE(frame);
+               return status;
        }
 
        status = cldap_search_recv(req, mem_ctx, io);
-       talloc_free(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
 
-       return status;
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
 }
 
 struct cldap_netlogon_state {
@@ -831,8 +889,9 @@ static void cldap_netlogon_state_done(struct tevent_req *subreq);
   queue a cldap netlogon for send
 */
 struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
-                                     struct cldap_socket *cldap,
-                                     const struct cldap_netlogon *io)
+                                      struct tevent_context *ev,
+                                      struct cldap_socket *cldap,
+                                      const struct cldap_netlogon *io)
 {
        struct tevent_req *req, *subreq;
        struct cldap_netlogon_state *state;
@@ -920,7 +979,7 @@ struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
        state->search.in.timeout        = 2;
        state->search.in.retries        = 2;
 
-       subreq = cldap_search_send(state, cldap, &state->search);
+       subreq = cldap_search_send(state, ev, cldap, &state->search);
        if (tevent_req_nomem(subreq, req)) {
                goto post;
        }
@@ -928,7 +987,7 @@ struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
 
        return req;
 post:
-       return tevent_req_post(req, cldap->event.ctx);
+       return tevent_req_post(req, ev);
 }
 
 static void cldap_netlogon_state_done(struct tevent_req *subreq)
@@ -953,7 +1012,6 @@ static void cldap_netlogon_state_done(struct tevent_req *subreq)
   receive a cldap netlogon reply
 */
 NTSTATUS cldap_netlogon_recv(struct tevent_req *req,
-                            struct smb_iconv_convenience *iconv_convenience,
                             TALLOC_CTX *mem_ctx,
                             struct cldap_netlogon *io)
 {
@@ -981,7 +1039,6 @@ NTSTATUS cldap_netlogon_recv(struct tevent_req *req,
        data = state->search.out.response->attributes[0].values;
 
        status = pull_netlogon_samlogon_response(data, mem_ctx,
-                                                iconv_convenience,
                                                 &io->out.netlogon);
        if (!NT_STATUS_IS_OK(status)) {
                goto failed;
@@ -1001,33 +1058,50 @@ failed:
   sync cldap netlogon search
 */
 NTSTATUS cldap_netlogon(struct cldap_socket *cldap,
-                       struct smb_iconv_convenience *iconv_convenience,
                        TALLOC_CTX *mem_ctx,
                        struct cldap_netlogon *io)
 {
+       TALLOC_CTX *frame;
        struct tevent_req *req;
+       struct tevent_context *ev;
        NTSTATUS status;
 
-       if (!cldap->event.allow_poll) {
+       if (cldap->searches.list) {
+               return NT_STATUS_PIPE_BUSY;
+       }
+
+       if (cldap->incoming.handler) {
                return NT_STATUS_INVALID_PIPE_STATE;
        }
 
-       if (cldap->searches.list) {
-               return NT_STATUS_PIPE_BUSY;
+       frame = talloc_stackframe();
+
+       ev = tevent_context_init(frame);
+       if (ev == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       req = cldap_netlogon_send(mem_ctx, cldap, io);
-       NT_STATUS_HAVE_NO_MEMORY(req);
+       req = cldap_netlogon_send(mem_ctx, ev, cldap, io);
+       if (req == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       if (!tevent_req_poll(req, cldap->event.ctx)) {
-               talloc_free(req);
-               return NT_STATUS_INTERNAL_ERROR;
+       if (!tevent_req_poll(req, ev)) {
+               status = map_nt_error_from_unix_common(errno);
+               TALLOC_FREE(frame);
+               return status;
        }
 
-       status = cldap_netlogon_recv(req, iconv_convenience, mem_ctx, io);
-       talloc_free(req);
+       status = cldap_netlogon_recv(req, mem_ctx, io);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
 
-       return status;
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
 }
 
 
@@ -1088,7 +1162,6 @@ NTSTATUS cldap_error_reply(struct cldap_socket *cldap,
   send a netlogon reply 
 */
 NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap,
-                             struct smb_iconv_convenience *iconv_convenience,
                              uint32_t message_id,
                              struct tsocket_address *dest,
                              uint32_t version,
@@ -1102,7 +1175,6 @@ NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap,
        DATA_BLOB blob;
 
        status = push_netlogon_samlogon_response(&blob, tmp_ctx,
-                                                iconv_convenience,
                                                 netlogon);
        if (!NT_STATUS_IS_OK(status)) {
                talloc_free(tmp_ctx);