libcli/cldap: fix a crash bug in cldap_socket_recv_dgram() (bug #8593)
[sfrench/samba-autobuild/.git] / libcli / cldap / cldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    cldap client library
5
6    Copyright (C) Andrew Tridgell 2005
7    Copyright (C) Stefan Metzmacher 2009
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /*
24   see RFC1798 for details of CLDAP
25
26   basic properties
27     - carried over UDP on port 389
28     - request and response matched by message ID
29     - request consists of only a single searchRequest element
30     - response can be in one of two forms
31        - a single searchResponse, followed by a searchResult
32        - a single searchResult
33 */
34
35 #include "includes.h"
36 #include <tevent.h>
37 #include "../lib/util/dlinklist.h"
38 #include "../libcli/ldap/ldap_message.h"
39 #include "../libcli/ldap/ldap_ndr.h"
40 #include "../libcli/cldap/cldap.h"
41 #include "../lib/tsocket/tsocket.h"
42 #include "../libcli/security/dom_sid.h"
43 #include "../librpc/gen_ndr/ndr_nbt.h"
44 #include "../lib/util/asn1.h"
45 #include "../lib/util/tevent_ntstatus.h"
46
47 #undef strcasecmp
48
49 /*
50   context structure for operations on cldap packets
51 */
52 struct cldap_socket {
53         /* the low level socket */
54         struct tdgram_context *sock;
55
56         /*
57          * Are we in connected mode, which means
58          * we get ICMP errors back instead of timing
59          * out requests. And we can only send requests
60          * to the connected peer.
61          */
62         bool connected;
63
64         /* the queue for outgoing dgrams */
65         struct tevent_queue *send_queue;
66
67         /* do we have an async tsocket_recvfrom request pending */
68         struct tevent_req *recv_subreq;
69
70         struct {
71                 /* a queue of pending search requests */
72                 struct cldap_search_state *list;
73
74                 /* mapping from message_id to pending request */
75                 struct idr_context *idr;
76         } searches;
77
78         /* what to do with incoming request packets */
79         struct {
80                 struct tevent_context *ev;
81                 void (*handler)(struct cldap_socket *,
82                                 void *private_data,
83                                 struct cldap_incoming *);
84                 void *private_data;
85         } incoming;
86 };
87
88 struct cldap_search_state {
89         struct cldap_search_state *prev, *next;
90
91         struct {
92                 struct tevent_context *ev;
93                 struct cldap_socket *cldap;
94         } caller;
95
96         int message_id;
97
98         struct {
99                 uint32_t idx;
100                 uint32_t delay;
101                 uint32_t count;
102                 struct tsocket_address *dest;
103                 DATA_BLOB blob;
104         } request;
105
106         struct {
107                 struct cldap_incoming *in;
108                 struct asn1_data *asn1;
109         } response;
110
111         struct tevent_req *req;
112 };
113
114 static int cldap_socket_destructor(struct cldap_socket *c)
115 {
116         while (c->searches.list) {
117                 struct cldap_search_state *s = c->searches.list;
118                 DLIST_REMOVE(c->searches.list, s);
119                 ZERO_STRUCT(s->caller);
120         }
121
122         talloc_free(c->recv_subreq);
123         talloc_free(c->send_queue);
124         talloc_free(c->sock);
125         return 0;
126 }
127
128 static void cldap_recvfrom_done(struct tevent_req *subreq);
129
130 static bool cldap_recvfrom_setup(struct cldap_socket *c)
131 {
132         struct tevent_context *ev;
133
134         if (c->recv_subreq) {
135                 return true;
136         }
137
138         if (!c->searches.list && !c->incoming.handler) {
139                 return true;
140         }
141
142         ev = c->incoming.ev;
143         if (ev == NULL) {
144                 ev = c->searches.list->caller.ev;
145         }
146
147         c->recv_subreq = tdgram_recvfrom_send(c, ev, c->sock);
148         if (!c->recv_subreq) {
149                 return false;
150         }
151         tevent_req_set_callback(c->recv_subreq, cldap_recvfrom_done, c);
152
153         return true;
154 }
155
156 static void cldap_recvfrom_stop(struct cldap_socket *c)
157 {
158         if (!c->recv_subreq) {
159                 return;
160         }
161
162         if (c->searches.list || c->incoming.handler) {
163                 return;
164         }
165
166         talloc_free(c->recv_subreq);
167         c->recv_subreq = NULL;
168 }
169
170 static bool cldap_socket_recv_dgram(struct cldap_socket *c,
171                                     struct cldap_incoming *in);
172
173 static void cldap_recvfrom_done(struct tevent_req *subreq)
174 {
175         struct cldap_socket *c = tevent_req_callback_data(subreq,
176                                  struct cldap_socket);
177         struct cldap_incoming *in = NULL;
178         ssize_t ret;
179         bool setup_done;
180
181         c->recv_subreq = NULL;
182
183         in = talloc_zero(c, struct cldap_incoming);
184         if (!in) {
185                 goto nomem;
186         }
187
188         ret = tdgram_recvfrom_recv(subreq,
189                                    &in->recv_errno,
190                                    in,
191                                    &in->buf,
192                                    &in->src);
193         talloc_free(subreq);
194         subreq = NULL;
195         if (ret >= 0) {
196                 in->len = ret;
197         }
198         if (ret == -1 && in->recv_errno == 0) {
199                 in->recv_errno = EIO;
200         }
201
202         /* this function should free or steal 'in' */
203         setup_done = cldap_socket_recv_dgram(c, in);
204         in = NULL;
205
206         if (!setup_done && !cldap_recvfrom_setup(c)) {
207                 goto nomem;
208         }
209
210         return;
211
212 nomem:
213         talloc_free(subreq);
214         talloc_free(in);
215         /*TODO: call a dead socket handler */
216         return;
217 }
218
219 /*
220   handle recv events on a cldap socket
221 */
222 static bool cldap_socket_recv_dgram(struct cldap_socket *c,
223                                     struct cldap_incoming *in)
224 {
225         DATA_BLOB blob;
226         struct asn1_data *asn1;
227         void *p;
228         struct cldap_search_state *search;
229         NTSTATUS status;
230
231         if (in->recv_errno != 0) {
232                 goto error;
233         }
234
235         blob = data_blob_const(in->buf, in->len);
236
237         asn1 = asn1_init(in);
238         if (!asn1) {
239                 goto nomem;
240         }
241
242         if (!asn1_load(asn1, blob)) {
243                 goto nomem;
244         }
245
246         in->ldap_msg = talloc(in, struct ldap_message);
247         if (in->ldap_msg == NULL) {
248                 goto nomem;
249         }
250
251         /* this initial decode is used to find the message id */
252         status = ldap_decode(asn1, NULL, in->ldap_msg);
253         if (!NT_STATUS_IS_OK(status)) {
254                 goto nterror;
255         }
256
257         /* find the pending request */
258         p = idr_find(c->searches.idr, in->ldap_msg->messageid);
259         if (p == NULL) {
260                 if (!c->incoming.handler) {
261                         TALLOC_FREE(in);
262                         return true;
263                 }
264
265                 /* this function should free or steal 'in' */
266                 c->incoming.handler(c, c->incoming.private_data, in);
267                 return false;
268         }
269
270         search = talloc_get_type_abort(p, struct cldap_search_state);
271         search->response.in = talloc_move(search, &in);
272         search->response.asn1 = asn1;
273         search->response.asn1->ofs = 0;
274
275         DLIST_REMOVE(c->searches.list, search);
276
277         if (cldap_recvfrom_setup(c)) {
278                 tevent_req_done(search->req);
279                 return true;
280         }
281
282         /*
283          * This request was ok, just defer the notify of the caller
284          * and then just fail the next request if needed
285          */
286         tevent_req_defer_callback(search->req, search->caller.ev);
287         tevent_req_done(search->req);
288
289         status = NT_STATUS_NO_MEMORY;
290         /* in is NULL it this point */
291         goto nterror;
292 nomem:
293         in->recv_errno = ENOMEM;
294 error:
295         status = map_nt_error_from_unix_common(in->recv_errno);
296 nterror:
297         TALLOC_FREE(in);
298         /* in connected mode the first pending search gets the error */
299         if (!c->connected) {
300                 /* otherwise we just ignore the error */
301                 return false;
302         }
303         if (!c->searches.list) {
304                 return false;
305         }
306         /*
307          * We might called tevent_req_done() for a successful
308          * search before, so we better deliver the failure
309          * after the success, that is why we better also
310          * use tevent_req_defer_callback() here.
311          */
312         tevent_req_defer_callback(c->searches.list->req,
313                                   c->searches.list->caller.ev);
314         tevent_req_nterror(c->searches.list->req, status);
315         return false;
316 }
317
318 /*
319   initialise a cldap_sock
320 */
321 NTSTATUS cldap_socket_init(TALLOC_CTX *mem_ctx,
322                            const struct tsocket_address *local_addr,
323                            const struct tsocket_address *remote_addr,
324                            struct cldap_socket **_cldap)
325 {
326         struct cldap_socket *c = NULL;
327         struct tsocket_address *any = NULL;
328         NTSTATUS status;
329         int ret;
330         const char *fam = NULL;
331
332         if (local_addr == NULL && remote_addr == NULL) {
333                 return NT_STATUS_INVALID_PARAMETER_MIX;
334         }
335
336         if (remote_addr) {
337                 bool is_ipv4;
338                 bool is_ipv6;
339
340                 is_ipv4 = tsocket_address_is_inet(remote_addr, "ipv4");
341                 is_ipv6 = tsocket_address_is_inet(remote_addr, "ipv6");
342
343                 if (is_ipv4) {
344                         fam = "ipv4";
345                 } else if (is_ipv6) {
346                         fam = "ipv6";
347                 } else {
348                         return NT_STATUS_INVALID_ADDRESS;
349                 }
350         }
351
352         c = talloc_zero(mem_ctx, struct cldap_socket);
353         if (!c) {
354                 goto nomem;
355         }
356
357         if (!local_addr) {
358                 /*
359                  * Here we know the address family of the remote address.
360                  */
361                 if (fam == NULL) {
362                         return NT_STATUS_INVALID_PARAMETER_MIX;
363                 }
364
365                 ret = tsocket_address_inet_from_strings(c, fam,
366                                                         NULL, 0,
367                                                         &any);
368                 if (ret != 0) {
369                         status = map_nt_error_from_unix_common(errno);
370                         goto nterror;
371                 }
372                 local_addr = any;
373         }
374
375         c->searches.idr = idr_init(c);
376         if (!c->searches.idr) {
377                 goto nomem;
378         }
379
380         ret = tdgram_inet_udp_socket(local_addr, remote_addr,
381                                      c, &c->sock);
382         if (ret != 0) {
383                 status = map_nt_error_from_unix_common(errno);
384                 goto nterror;
385         }
386         talloc_free(any);
387
388         if (remote_addr) {
389                 c->connected = true;
390         }
391
392         c->send_queue = tevent_queue_create(c, "cldap_send_queue");
393         if (!c->send_queue) {
394                 goto nomem;
395         }
396
397         talloc_set_destructor(c, cldap_socket_destructor);
398
399         *_cldap = c;
400         return NT_STATUS_OK;
401
402 nomem:
403         status = NT_STATUS_NO_MEMORY;
404 nterror:
405         talloc_free(c);
406         return status;
407 }
408
409 /*
410   setup a handler for incoming requests
411 */
412 NTSTATUS cldap_set_incoming_handler(struct cldap_socket *c,
413                                     struct tevent_context *ev,
414                                     void (*handler)(struct cldap_socket *,
415                                                     void *private_data,
416                                                     struct cldap_incoming *),
417                                     void *private_data)
418 {
419         if (c->connected) {
420                 return NT_STATUS_PIPE_CONNECTED;
421         }
422
423         c->incoming.ev = ev;
424         c->incoming.handler = handler;
425         c->incoming.private_data = private_data;
426
427         if (!cldap_recvfrom_setup(c)) {
428                 ZERO_STRUCT(c->incoming);
429                 return NT_STATUS_NO_MEMORY;
430         }
431
432         return NT_STATUS_OK;
433 }
434
435 struct cldap_reply_state {
436         struct tsocket_address *dest;
437         DATA_BLOB blob;
438 };
439
440 static void cldap_reply_state_destroy(struct tevent_req *subreq);
441
442 /*
443   queue a cldap reply for send
444 */
445 NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io)
446 {
447         struct cldap_reply_state *state = NULL;
448         struct ldap_message *msg;
449         DATA_BLOB blob1, blob2;
450         NTSTATUS status;
451         struct tevent_req *subreq;
452
453         if (cldap->connected) {
454                 return NT_STATUS_PIPE_CONNECTED;
455         }
456
457         if (cldap->incoming.ev == NULL) {
458                 return NT_STATUS_INVALID_PIPE_STATE;
459         }
460
461         if (!io->dest) {
462                 return NT_STATUS_INVALID_ADDRESS;
463         }
464
465         state = talloc(cldap, struct cldap_reply_state);
466         NT_STATUS_HAVE_NO_MEMORY(state);
467
468         state->dest = tsocket_address_copy(io->dest, state);
469         if (!state->dest) {
470                 goto nomem;
471         }
472
473         msg = talloc(state, struct ldap_message);
474         if (!msg) {
475                 goto nomem;
476         }
477
478         msg->messageid       = io->messageid;
479         msg->controls        = NULL;
480
481         if (io->response) {
482                 msg->type = LDAP_TAG_SearchResultEntry;
483                 msg->r.SearchResultEntry = *io->response;
484
485                 if (!ldap_encode(msg, NULL, &blob1, state)) {
486                         status = NT_STATUS_INVALID_PARAMETER;
487                         goto failed;
488                 }
489         } else {
490                 blob1 = data_blob(NULL, 0);
491         }
492
493         msg->type = LDAP_TAG_SearchResultDone;
494         msg->r.SearchResultDone = *io->result;
495
496         if (!ldap_encode(msg, NULL, &blob2, state)) {
497                 status = NT_STATUS_INVALID_PARAMETER;
498                 goto failed;
499         }
500         talloc_free(msg);
501
502         state->blob = data_blob_talloc(state, NULL, blob1.length + blob2.length);
503         if (!state->blob.data) {
504                 goto nomem;
505         }
506
507         memcpy(state->blob.data, blob1.data, blob1.length);
508         memcpy(state->blob.data+blob1.length, blob2.data, blob2.length);
509         data_blob_free(&blob1);
510         data_blob_free(&blob2);
511
512         subreq = tdgram_sendto_queue_send(state,
513                                           cldap->incoming.ev,
514                                           cldap->sock,
515                                           cldap->send_queue,
516                                           state->blob.data,
517                                           state->blob.length,
518                                           state->dest);
519         if (!subreq) {
520                 goto nomem;
521         }
522         /* the callback will just free the state, as we don't need a result */
523         tevent_req_set_callback(subreq, cldap_reply_state_destroy, state);
524
525         return NT_STATUS_OK;
526
527 nomem:
528         status = NT_STATUS_NO_MEMORY;
529 failed:
530         talloc_free(state);
531         return status;
532 }
533
534 static void cldap_reply_state_destroy(struct tevent_req *subreq)
535 {
536         struct cldap_reply_state *state = tevent_req_callback_data(subreq,
537                                           struct cldap_reply_state);
538
539         /* we don't want to know the result here, we just free the state */
540         talloc_free(subreq);
541         talloc_free(state);
542 }
543
544 static int cldap_search_state_destructor(struct cldap_search_state *s)
545 {
546         if (s->caller.cldap) {
547                 if (s->message_id != -1) {
548                         idr_remove(s->caller.cldap->searches.idr, s->message_id);
549                         s->message_id = -1;
550                 }
551                 DLIST_REMOVE(s->caller.cldap->searches.list, s);
552                 cldap_recvfrom_stop(s->caller.cldap);
553                 ZERO_STRUCT(s->caller);
554         }
555
556         return 0;
557 }
558
559 static void cldap_search_state_queue_done(struct tevent_req *subreq);
560 static void cldap_search_state_wakeup_done(struct tevent_req *subreq);
561
562 /*
563   queue a cldap reply for send
564 */
565 struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
566                                      struct tevent_context *ev,
567                                      struct cldap_socket *cldap,
568                                      const struct cldap_search *io)
569 {
570         struct tevent_req *req, *subreq;
571         struct cldap_search_state *state = NULL;
572         struct ldap_message *msg;
573         struct ldap_SearchRequest *search;
574         struct timeval now;
575         struct timeval end;
576         uint32_t i;
577         int ret;
578
579         req = tevent_req_create(mem_ctx, &state,
580                                 struct cldap_search_state);
581         if (!req) {
582                 return NULL;
583         }
584         ZERO_STRUCTP(state);
585         state->caller.ev = ev;
586         state->req = req;
587         state->caller.cldap = cldap;
588         state->message_id = -1;
589
590         talloc_set_destructor(state, cldap_search_state_destructor);
591
592         if (io->in.dest_address) {
593                 if (cldap->connected) {
594                         tevent_req_nterror(req, NT_STATUS_PIPE_CONNECTED);
595                         goto post;
596                 }
597                 ret = tsocket_address_inet_from_strings(state,
598                                                         "ip",
599                                                         io->in.dest_address,
600                                                         io->in.dest_port,
601                                                         &state->request.dest);
602                 if (ret != 0) {
603                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
604                         goto post;
605                 }
606         } else {
607                 if (!cldap->connected) {
608                         tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
609                         goto post;
610                 }
611                 state->request.dest = NULL;
612         }
613
614         state->message_id = idr_get_new_random(cldap->searches.idr,
615                                                state, UINT16_MAX);
616         if (state->message_id == -1) {
617                 tevent_req_nterror(req, NT_STATUS_INSUFFICIENT_RESOURCES);
618                 goto post;
619         }
620
621         msg = talloc(state, struct ldap_message);
622         if (tevent_req_nomem(msg, req)) {
623                 goto post;
624         }
625
626         msg->messageid  = state->message_id;
627         msg->type       = LDAP_TAG_SearchRequest;
628         msg->controls   = NULL;
629         search = &msg->r.SearchRequest;
630
631         search->basedn          = "";
632         search->scope           = LDAP_SEARCH_SCOPE_BASE;
633         search->deref           = LDAP_DEREFERENCE_NEVER;
634         search->timelimit       = 0;
635         search->sizelimit       = 0;
636         search->attributesonly  = false;
637         search->num_attributes  = str_list_length(io->in.attributes);
638         search->attributes      = io->in.attributes;
639         search->tree            = ldb_parse_tree(msg, io->in.filter);
640         if (tevent_req_nomem(search->tree, req)) {
641                 goto post;
642         }
643
644         if (!ldap_encode(msg, NULL, &state->request.blob, state)) {
645                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
646                 goto post;
647         }
648         talloc_free(msg);
649
650         state->request.idx = 0;
651         state->request.delay = 10*1000*1000;
652         state->request.count = 3;
653         if (io->in.timeout > 0) {
654                 state->request.delay = io->in.timeout * 1000 * 1000;
655                 state->request.count = io->in.retries + 1;
656         }
657
658         now = tevent_timeval_current();
659         end = now;
660         for (i = 0; i < state->request.count; i++) {
661                 end = tevent_timeval_add(&end, state->request.delay / 1000000,
662                                          state->request.delay % 1000000);
663         }
664
665         if (!tevent_req_set_endtime(req, state->caller.ev, end)) {
666                 tevent_req_oom(req);
667                 goto post;
668         }
669
670         subreq = tdgram_sendto_queue_send(state,
671                                           state->caller.ev,
672                                           state->caller.cldap->sock,
673                                           state->caller.cldap->send_queue,
674                                           state->request.blob.data,
675                                           state->request.blob.length,
676                                           state->request.dest);
677         if (tevent_req_nomem(subreq, req)) {
678                 goto post;
679         }
680         tevent_req_set_callback(subreq, cldap_search_state_queue_done, req);
681
682         DLIST_ADD_END(cldap->searches.list, state, struct cldap_search_state *);
683
684         return req;
685
686  post:
687         return tevent_req_post(req, state->caller.ev);
688 }
689
690 static void cldap_search_state_queue_done(struct tevent_req *subreq)
691 {
692         struct tevent_req *req = tevent_req_callback_data(subreq,
693                                  struct tevent_req);
694         struct cldap_search_state *state = tevent_req_data(req,
695                                            struct cldap_search_state);
696         ssize_t ret;
697         int sys_errno = 0;
698         struct timeval next;
699
700         ret = tdgram_sendto_queue_recv(subreq, &sys_errno);
701         talloc_free(subreq);
702         if (ret == -1) {
703                 NTSTATUS status;
704                 status = map_nt_error_from_unix_common(sys_errno);
705                 DLIST_REMOVE(state->caller.cldap->searches.list, state);
706                 ZERO_STRUCT(state->caller.cldap);
707                 tevent_req_nterror(req, status);
708                 return;
709         }
710
711         state->request.idx++;
712
713         /* wait for incoming traffic */
714         if (!cldap_recvfrom_setup(state->caller.cldap)) {
715                 tevent_req_oom(req);
716                 return;
717         }
718
719         if (state->request.idx > state->request.count) {
720                 /* we just wait for the response or a timeout */
721                 return;
722         }
723
724         next = tevent_timeval_current_ofs(state->request.delay / 1000000,
725                                           state->request.delay % 1000000);
726         subreq = tevent_wakeup_send(state,
727                                     state->caller.ev,
728                                     next);
729         if (tevent_req_nomem(subreq, req)) {
730                 return;
731         }
732         tevent_req_set_callback(subreq, cldap_search_state_wakeup_done, req);
733 }
734
735 static void cldap_search_state_wakeup_done(struct tevent_req *subreq)
736 {
737         struct tevent_req *req = tevent_req_callback_data(subreq,
738                                  struct tevent_req);
739         struct cldap_search_state *state = tevent_req_data(req,
740                                            struct cldap_search_state);
741         bool ok;
742
743         ok = tevent_wakeup_recv(subreq);
744         talloc_free(subreq);
745         if (!ok) {
746                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
747                 return;
748         }
749
750         subreq = tdgram_sendto_queue_send(state,
751                                           state->caller.ev,
752                                           state->caller.cldap->sock,
753                                           state->caller.cldap->send_queue,
754                                           state->request.blob.data,
755                                           state->request.blob.length,
756                                           state->request.dest);
757         if (tevent_req_nomem(subreq, req)) {
758                 return;
759         }
760         tevent_req_set_callback(subreq, cldap_search_state_queue_done, req);
761 }
762
763 /*
764   receive a cldap reply
765 */
766 NTSTATUS cldap_search_recv(struct tevent_req *req,
767                            TALLOC_CTX *mem_ctx,
768                            struct cldap_search *io)
769 {
770         struct cldap_search_state *state = tevent_req_data(req,
771                                            struct cldap_search_state);
772         struct ldap_message *ldap_msg;
773         NTSTATUS status;
774
775         if (tevent_req_is_nterror(req, &status)) {
776                 goto failed;
777         }
778
779         ldap_msg = talloc(mem_ctx, struct ldap_message);
780         if (!ldap_msg) {
781                 goto nomem;
782         }
783
784         status = ldap_decode(state->response.asn1, NULL, ldap_msg);
785         if (!NT_STATUS_IS_OK(status)) {
786                 goto failed;
787         }
788
789         ZERO_STRUCT(io->out);
790
791         /* the first possible form has a search result in first place */
792         if (ldap_msg->type == LDAP_TAG_SearchResultEntry) {
793                 io->out.response = talloc(mem_ctx, struct ldap_SearchResEntry);
794                 if (!io->out.response) {
795                         goto nomem;
796                 }
797                 *io->out.response = ldap_msg->r.SearchResultEntry;
798
799                 /* decode the 2nd part */
800                 status = ldap_decode(state->response.asn1, NULL, ldap_msg);
801                 if (!NT_STATUS_IS_OK(status)) {
802                         goto failed;
803                 }
804         }
805
806         if (ldap_msg->type != LDAP_TAG_SearchResultDone) {
807                 status = NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
808                 goto failed;
809         }
810
811         io->out.result = talloc(mem_ctx, struct ldap_Result);
812         if (!io->out.result) {
813                 goto nomem;
814         }
815         *io->out.result = ldap_msg->r.SearchResultDone;
816
817         if (io->out.result->resultcode != LDAP_SUCCESS) {
818                 status = NT_STATUS_LDAP(io->out.result->resultcode);
819                 goto failed;
820         }
821
822         tevent_req_received(req);
823         return NT_STATUS_OK;
824
825 nomem:
826         status = NT_STATUS_NO_MEMORY;
827 failed:
828         tevent_req_received(req);
829         return status;
830 }
831
832
833 /*
834   synchronous cldap search
835 */
836 NTSTATUS cldap_search(struct cldap_socket *cldap,
837                       TALLOC_CTX *mem_ctx,
838                       struct cldap_search *io)
839 {
840         TALLOC_CTX *frame;
841         struct tevent_req *req;
842         struct tevent_context *ev;
843         NTSTATUS status;
844
845         if (cldap->searches.list) {
846                 return NT_STATUS_PIPE_BUSY;
847         }
848
849         if (cldap->incoming.handler) {
850                 return NT_STATUS_INVALID_PIPE_STATE;
851         }
852
853         frame = talloc_stackframe();
854
855         ev = tevent_context_init(frame);
856         if (ev == NULL) {
857                 TALLOC_FREE(frame);
858                 return NT_STATUS_NO_MEMORY;
859         }
860
861         req = cldap_search_send(mem_ctx, ev, cldap, io);
862         if (req == NULL) {
863                 TALLOC_FREE(frame);
864                 return NT_STATUS_NO_MEMORY;
865         }
866
867         if (!tevent_req_poll(req, ev)) {
868                 status = map_nt_error_from_unix_common(errno);
869                 TALLOC_FREE(frame);
870                 return status;
871         }
872
873         status = cldap_search_recv(req, mem_ctx, io);
874         if (!NT_STATUS_IS_OK(status)) {
875                 TALLOC_FREE(frame);
876                 return status;
877         }
878
879         TALLOC_FREE(frame);
880         return NT_STATUS_OK;
881 }
882
883 struct cldap_netlogon_state {
884         struct cldap_search search;
885 };
886
887 static void cldap_netlogon_state_done(struct tevent_req *subreq);
888 /*
889   queue a cldap netlogon for send
890 */
891 struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
892                                        struct tevent_context *ev,
893                                        struct cldap_socket *cldap,
894                                        const struct cldap_netlogon *io)
895 {
896         struct tevent_req *req, *subreq;
897         struct cldap_netlogon_state *state;
898         char *filter;
899         static const char * const attr[] = { "NetLogon", NULL };
900
901         req = tevent_req_create(mem_ctx, &state,
902                                 struct cldap_netlogon_state);
903         if (!req) {
904                 return NULL;
905         }
906
907         filter = talloc_asprintf(state, "(&(NtVer=%s)", 
908                                  ldap_encode_ndr_uint32(state, io->in.version));
909         if (tevent_req_nomem(filter, req)) {
910                 goto post;
911         }
912         if (io->in.user) {
913                 filter = talloc_asprintf_append_buffer(filter, "(User=%s)", io->in.user);
914                 if (tevent_req_nomem(filter, req)) {
915                         goto post;
916                 }
917         }
918         if (io->in.host) {
919                 filter = talloc_asprintf_append_buffer(filter, "(Host=%s)", io->in.host);
920                 if (tevent_req_nomem(filter, req)) {
921                         goto post;
922                 }
923         }
924         if (io->in.realm) {
925                 filter = talloc_asprintf_append_buffer(filter, "(DnsDomain=%s)", io->in.realm);
926                 if (tevent_req_nomem(filter, req)) {
927                         goto post;
928                 }
929         }
930         if (io->in.acct_control != -1) {
931                 filter = talloc_asprintf_append_buffer(filter, "(AAC=%s)", 
932                                                 ldap_encode_ndr_uint32(state, io->in.acct_control));
933                 if (tevent_req_nomem(filter, req)) {
934                         goto post;
935                 }
936         }
937         if (io->in.domain_sid) {
938                 struct dom_sid *sid = dom_sid_parse_talloc(state, io->in.domain_sid);
939                 if (tevent_req_nomem(sid, req)) {
940                         goto post;
941                 }
942                 filter = talloc_asprintf_append_buffer(filter, "(domainSid=%s)",
943                                                 ldap_encode_ndr_dom_sid(state, sid));
944                 if (tevent_req_nomem(filter, req)) {
945                         goto post;
946                 }
947         }
948         if (io->in.domain_guid) {
949                 struct GUID guid;
950                 NTSTATUS status;
951                 status = GUID_from_string(io->in.domain_guid, &guid);
952                 if (tevent_req_nterror(req, status)) {
953                         goto post;
954                 }
955                 filter = talloc_asprintf_append_buffer(filter, "(DomainGuid=%s)",
956                                                 ldap_encode_ndr_GUID(state, &guid));
957                 if (tevent_req_nomem(filter, req)) {
958                         goto post;
959                 }
960         }
961         filter = talloc_asprintf_append_buffer(filter, ")");
962         if (tevent_req_nomem(filter, req)) {
963                 goto post;
964         }
965
966         if (io->in.dest_address) {
967                 state->search.in.dest_address = talloc_strdup(state,
968                                                 io->in.dest_address);
969                 if (tevent_req_nomem(state->search.in.dest_address, req)) {
970                         goto post;
971                 }
972                 state->search.in.dest_port = io->in.dest_port;
973         } else {
974                 state->search.in.dest_address   = NULL;
975                 state->search.in.dest_port      = 0;
976         }
977         state->search.in.filter         = filter;
978         state->search.in.attributes     = attr;
979         state->search.in.timeout        = 2;
980         state->search.in.retries        = 2;
981
982         subreq = cldap_search_send(state, ev, cldap, &state->search);
983         if (tevent_req_nomem(subreq, req)) {
984                 goto post;
985         }
986         tevent_req_set_callback(subreq, cldap_netlogon_state_done, req);
987
988         return req;
989 post:
990         return tevent_req_post(req, ev);
991 }
992
993 static void cldap_netlogon_state_done(struct tevent_req *subreq)
994 {
995         struct tevent_req *req = tevent_req_callback_data(subreq,
996                                  struct tevent_req);
997         struct cldap_netlogon_state *state = tevent_req_data(req,
998                                              struct cldap_netlogon_state);
999         NTSTATUS status;
1000
1001         status = cldap_search_recv(subreq, state, &state->search);
1002         talloc_free(subreq);
1003
1004         if (tevent_req_nterror(req, status)) {
1005                 return;
1006         }
1007
1008         tevent_req_done(req);
1009 }
1010
1011 /*
1012   receive a cldap netlogon reply
1013 */
1014 NTSTATUS cldap_netlogon_recv(struct tevent_req *req,
1015                              TALLOC_CTX *mem_ctx,
1016                              struct cldap_netlogon *io)
1017 {
1018         struct cldap_netlogon_state *state = tevent_req_data(req,
1019                                              struct cldap_netlogon_state);
1020         NTSTATUS status;
1021         DATA_BLOB *data;
1022
1023         if (tevent_req_is_nterror(req, &status)) {
1024                 goto failed;
1025         }
1026
1027         if (state->search.out.response == NULL) {
1028                 status = NT_STATUS_NOT_FOUND;
1029                 goto failed;
1030         }
1031
1032         if (state->search.out.response->num_attributes != 1 ||
1033             strcasecmp(state->search.out.response->attributes[0].name, "netlogon") != 0 ||
1034             state->search.out.response->attributes[0].num_values != 1 ||
1035             state->search.out.response->attributes[0].values->length < 2) {
1036                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
1037                 goto failed;
1038         }
1039         data = state->search.out.response->attributes[0].values;
1040
1041         status = pull_netlogon_samlogon_response(data, mem_ctx,
1042                                                  &io->out.netlogon);
1043         if (!NT_STATUS_IS_OK(status)) {
1044                 goto failed;
1045         }
1046
1047         if (io->in.map_response) {
1048                 map_netlogon_samlogon_response(&io->out.netlogon);
1049         }
1050
1051         status =  NT_STATUS_OK;
1052 failed:
1053         tevent_req_received(req);
1054         return status;
1055 }
1056
1057 /*
1058   sync cldap netlogon search
1059 */
1060 NTSTATUS cldap_netlogon(struct cldap_socket *cldap,
1061                         TALLOC_CTX *mem_ctx,
1062                         struct cldap_netlogon *io)
1063 {
1064         TALLOC_CTX *frame;
1065         struct tevent_req *req;
1066         struct tevent_context *ev;
1067         NTSTATUS status;
1068
1069         if (cldap->searches.list) {
1070                 return NT_STATUS_PIPE_BUSY;
1071         }
1072
1073         if (cldap->incoming.handler) {
1074                 return NT_STATUS_INVALID_PIPE_STATE;
1075         }
1076
1077         frame = talloc_stackframe();
1078
1079         ev = tevent_context_init(frame);
1080         if (ev == NULL) {
1081                 TALLOC_FREE(frame);
1082                 return NT_STATUS_NO_MEMORY;
1083         }
1084
1085         req = cldap_netlogon_send(mem_ctx, ev, cldap, io);
1086         if (req == NULL) {
1087                 TALLOC_FREE(frame);
1088                 return NT_STATUS_NO_MEMORY;
1089         }
1090
1091         if (!tevent_req_poll(req, ev)) {
1092                 status = map_nt_error_from_unix_common(errno);
1093                 TALLOC_FREE(frame);
1094                 return status;
1095         }
1096
1097         status = cldap_netlogon_recv(req, mem_ctx, io);
1098         if (!NT_STATUS_IS_OK(status)) {
1099                 TALLOC_FREE(frame);
1100                 return status;
1101         }
1102
1103         TALLOC_FREE(frame);
1104         return NT_STATUS_OK;
1105 }
1106
1107
1108 /*
1109   send an empty reply (used on any error, so the client doesn't keep waiting
1110   or send the bad request again)
1111 */
1112 NTSTATUS cldap_empty_reply(struct cldap_socket *cldap,
1113                            uint32_t message_id,
1114                            struct tsocket_address *dest)
1115 {
1116         NTSTATUS status;
1117         struct cldap_reply reply;
1118         struct ldap_Result result;
1119
1120         reply.messageid    = message_id;
1121         reply.dest         = dest;
1122         reply.response     = NULL;
1123         reply.result       = &result;
1124
1125         ZERO_STRUCT(result);
1126
1127         status = cldap_reply_send(cldap, &reply);
1128
1129         return status;
1130 }
1131
1132 /*
1133   send an error reply (used on any error, so the client doesn't keep waiting
1134   or send the bad request again)
1135 */
1136 NTSTATUS cldap_error_reply(struct cldap_socket *cldap,
1137                            uint32_t message_id,
1138                            struct tsocket_address *dest,
1139                            int resultcode,
1140                            const char *errormessage)
1141 {
1142         NTSTATUS status;
1143         struct cldap_reply reply;
1144         struct ldap_Result result;
1145
1146         reply.messageid    = message_id;
1147         reply.dest         = dest;
1148         reply.response     = NULL;
1149         reply.result       = &result;
1150
1151         ZERO_STRUCT(result);
1152         result.resultcode       = resultcode;
1153         result.errormessage     = errormessage;
1154
1155         status = cldap_reply_send(cldap, &reply);
1156
1157         return status;
1158 }
1159
1160
1161 /*
1162   send a netlogon reply 
1163 */
1164 NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap,
1165                               uint32_t message_id,
1166                               struct tsocket_address *dest,
1167                               uint32_t version,
1168                               struct netlogon_samlogon_response *netlogon)
1169 {
1170         NTSTATUS status;
1171         struct cldap_reply reply;
1172         struct ldap_SearchResEntry response;
1173         struct ldap_Result result;
1174         TALLOC_CTX *tmp_ctx = talloc_new(cldap);
1175         DATA_BLOB blob;
1176
1177         status = push_netlogon_samlogon_response(&blob, tmp_ctx,
1178                                                  netlogon);
1179         if (!NT_STATUS_IS_OK(status)) {
1180                 talloc_free(tmp_ctx);
1181                 return status;
1182         }
1183         reply.messageid    = message_id;
1184         reply.dest         = dest;
1185         reply.response     = &response;
1186         reply.result       = &result;
1187
1188         ZERO_STRUCT(result);
1189
1190         response.dn = "";
1191         response.num_attributes = 1;
1192         response.attributes = talloc(tmp_ctx, struct ldb_message_element);
1193         NT_STATUS_HAVE_NO_MEMORY(response.attributes);
1194         response.attributes->name = "netlogon";
1195         response.attributes->num_values = 1;
1196         response.attributes->values = &blob;
1197
1198         status = cldap_reply_send(cldap, &reply);
1199
1200         talloc_free(tmp_ctx);
1201
1202         return status;
1203 }
1204