8d7cbb0408b122d49fd5ff972e8ab8b94c6c257f
[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                         goto done;
262                 }
263
264                 /* this function should free or steal 'in' */
265                 c->incoming.handler(c, c->incoming.private_data, in);
266                 return false;
267         }
268
269         search = talloc_get_type(p, struct cldap_search_state);
270         search->response.in = talloc_move(search, &in);
271         search->response.asn1 = asn1;
272         search->response.asn1->ofs = 0;
273
274         DLIST_REMOVE(c->searches.list, search);
275
276         if (!cldap_recvfrom_setup(c)) {
277                 goto nomem;
278         }
279
280         tevent_req_done(search->req);
281         talloc_free(in);
282         return true;
283
284 nomem:
285         in->recv_errno = ENOMEM;
286 error:
287         status = map_nt_error_from_unix_common(in->recv_errno);
288 nterror:
289         /* in connected mode the first pending search gets the error */
290         if (!c->connected) {
291                 /* otherwise we just ignore the error */
292                 goto done;
293         }
294         if (!c->searches.list) {
295                 goto done;
296         }
297         tevent_req_nterror(c->searches.list->req, status);
298 done:
299         talloc_free(in);
300         return false;
301 }
302
303 /*
304   initialise a cldap_sock
305 */
306 NTSTATUS cldap_socket_init(TALLOC_CTX *mem_ctx,
307                            const struct tsocket_address *local_addr,
308                            const struct tsocket_address *remote_addr,
309                            struct cldap_socket **_cldap)
310 {
311         struct cldap_socket *c = NULL;
312         struct tsocket_address *any = NULL;
313         NTSTATUS status;
314         int ret;
315         const char *fam = NULL;
316
317         if (local_addr == NULL && remote_addr == NULL) {
318                 return NT_STATUS_INVALID_PARAMETER_MIX;
319         }
320
321         if (remote_addr) {
322                 bool is_ipv4;
323                 bool is_ipv6;
324
325                 is_ipv4 = tsocket_address_is_inet(remote_addr, "ipv4");
326                 is_ipv6 = tsocket_address_is_inet(remote_addr, "ipv6");
327
328                 if (is_ipv4) {
329                         fam = "ipv4";
330                 } else if (is_ipv6) {
331                         fam = "ipv6";
332                 } else {
333                         return NT_STATUS_INVALID_ADDRESS;
334                 }
335         }
336
337         c = talloc_zero(mem_ctx, struct cldap_socket);
338         if (!c) {
339                 goto nomem;
340         }
341
342         if (!local_addr) {
343                 /*
344                  * Here we the address family of the remote address.
345                  */
346                 ret = tsocket_address_inet_from_strings(c, fam,
347                                                         NULL, 0,
348                                                         &any);
349                 if (ret != 0) {
350                         status = map_nt_error_from_unix_common(errno);
351                         goto nterror;
352                 }
353                 local_addr = any;
354         }
355
356         c->searches.idr = idr_init(c);
357         if (!c->searches.idr) {
358                 goto nomem;
359         }
360
361         ret = tdgram_inet_udp_socket(local_addr, remote_addr,
362                                      c, &c->sock);
363         if (ret != 0) {
364                 status = map_nt_error_from_unix_common(errno);
365                 goto nterror;
366         }
367         talloc_free(any);
368
369         if (remote_addr) {
370                 c->connected = true;
371         }
372
373         c->send_queue = tevent_queue_create(c, "cldap_send_queue");
374         if (!c->send_queue) {
375                 goto nomem;
376         }
377
378         talloc_set_destructor(c, cldap_socket_destructor);
379
380         *_cldap = c;
381         return NT_STATUS_OK;
382
383 nomem:
384         status = NT_STATUS_NO_MEMORY;
385 nterror:
386         talloc_free(c);
387         return status;
388 }
389
390 /*
391   setup a handler for incoming requests
392 */
393 NTSTATUS cldap_set_incoming_handler(struct cldap_socket *c,
394                                     struct tevent_context *ev,
395                                     void (*handler)(struct cldap_socket *,
396                                                     void *private_data,
397                                                     struct cldap_incoming *),
398                                     void *private_data)
399 {
400         if (c->connected) {
401                 return NT_STATUS_PIPE_CONNECTED;
402         }
403
404         c->incoming.ev = ev;
405         c->incoming.handler = handler;
406         c->incoming.private_data = private_data;
407
408         if (!cldap_recvfrom_setup(c)) {
409                 ZERO_STRUCT(c->incoming);
410                 return NT_STATUS_NO_MEMORY;
411         }
412
413         return NT_STATUS_OK;
414 }
415
416 struct cldap_reply_state {
417         struct tsocket_address *dest;
418         DATA_BLOB blob;
419 };
420
421 static void cldap_reply_state_destroy(struct tevent_req *subreq);
422
423 /*
424   queue a cldap reply for send
425 */
426 NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io)
427 {
428         struct cldap_reply_state *state = NULL;
429         struct ldap_message *msg;
430         DATA_BLOB blob1, blob2;
431         NTSTATUS status;
432         struct tevent_req *subreq;
433
434         if (cldap->connected) {
435                 return NT_STATUS_PIPE_CONNECTED;
436         }
437
438         if (cldap->incoming.ev == NULL) {
439                 return NT_STATUS_INVALID_PIPE_STATE;
440         }
441
442         if (!io->dest) {
443                 return NT_STATUS_INVALID_ADDRESS;
444         }
445
446         state = talloc(cldap, struct cldap_reply_state);
447         NT_STATUS_HAVE_NO_MEMORY(state);
448
449         state->dest = tsocket_address_copy(io->dest, state);
450         if (!state->dest) {
451                 goto nomem;
452         }
453
454         msg = talloc(state, struct ldap_message);
455         if (!msg) {
456                 goto nomem;
457         }
458
459         msg->messageid       = io->messageid;
460         msg->controls        = NULL;
461
462         if (io->response) {
463                 msg->type = LDAP_TAG_SearchResultEntry;
464                 msg->r.SearchResultEntry = *io->response;
465
466                 if (!ldap_encode(msg, NULL, &blob1, state)) {
467                         status = NT_STATUS_INVALID_PARAMETER;
468                         goto failed;
469                 }
470         } else {
471                 blob1 = data_blob(NULL, 0);
472         }
473
474         msg->type = LDAP_TAG_SearchResultDone;
475         msg->r.SearchResultDone = *io->result;
476
477         if (!ldap_encode(msg, NULL, &blob2, state)) {
478                 status = NT_STATUS_INVALID_PARAMETER;
479                 goto failed;
480         }
481         talloc_free(msg);
482
483         state->blob = data_blob_talloc(state, NULL, blob1.length + blob2.length);
484         if (!state->blob.data) {
485                 goto nomem;
486         }
487
488         memcpy(state->blob.data, blob1.data, blob1.length);
489         memcpy(state->blob.data+blob1.length, blob2.data, blob2.length);
490         data_blob_free(&blob1);
491         data_blob_free(&blob2);
492
493         subreq = tdgram_sendto_queue_send(state,
494                                           cldap->incoming.ev,
495                                           cldap->sock,
496                                           cldap->send_queue,
497                                           state->blob.data,
498                                           state->blob.length,
499                                           state->dest);
500         if (!subreq) {
501                 goto nomem;
502         }
503         /* the callback will just free the state, as we don't need a result */
504         tevent_req_set_callback(subreq, cldap_reply_state_destroy, state);
505
506         return NT_STATUS_OK;
507
508 nomem:
509         status = NT_STATUS_NO_MEMORY;
510 failed:
511         talloc_free(state);
512         return status;
513 }
514
515 static void cldap_reply_state_destroy(struct tevent_req *subreq)
516 {
517         struct cldap_reply_state *state = tevent_req_callback_data(subreq,
518                                           struct cldap_reply_state);
519
520         /* we don't want to know the result here, we just free the state */
521         talloc_free(subreq);
522         talloc_free(state);
523 }
524
525 static int cldap_search_state_destructor(struct cldap_search_state *s)
526 {
527         if (s->caller.cldap) {
528                 if (s->message_id != -1) {
529                         idr_remove(s->caller.cldap->searches.idr, s->message_id);
530                         s->message_id = -1;
531                 }
532                 DLIST_REMOVE(s->caller.cldap->searches.list, s);
533                 cldap_recvfrom_stop(s->caller.cldap);
534                 ZERO_STRUCT(s->caller);
535         }
536
537         return 0;
538 }
539
540 static void cldap_search_state_queue_done(struct tevent_req *subreq);
541 static void cldap_search_state_wakeup_done(struct tevent_req *subreq);
542
543 /*
544   queue a cldap reply for send
545 */
546 struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
547                                      struct tevent_context *ev,
548                                      struct cldap_socket *cldap,
549                                      const struct cldap_search *io)
550 {
551         struct tevent_req *req, *subreq;
552         struct cldap_search_state *state = NULL;
553         struct ldap_message *msg;
554         struct ldap_SearchRequest *search;
555         struct timeval now;
556         struct timeval end;
557         uint32_t i;
558         int ret;
559
560         req = tevent_req_create(mem_ctx, &state,
561                                 struct cldap_search_state);
562         if (!req) {
563                 return NULL;
564         }
565         ZERO_STRUCTP(state);
566         state->caller.ev = ev;
567         state->req = req;
568         state->caller.cldap = cldap;
569         state->message_id = -1;
570
571         talloc_set_destructor(state, cldap_search_state_destructor);
572
573         if (io->in.dest_address) {
574                 if (cldap->connected) {
575                         tevent_req_nterror(req, NT_STATUS_PIPE_CONNECTED);
576                         goto post;
577                 }
578                 ret = tsocket_address_inet_from_strings(state,
579                                                         "ip",
580                                                         io->in.dest_address,
581                                                         io->in.dest_port,
582                                                         &state->request.dest);
583                 if (ret != 0) {
584                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
585                         goto post;
586                 }
587         } else {
588                 if (!cldap->connected) {
589                         tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
590                         goto post;
591                 }
592                 state->request.dest = NULL;
593         }
594
595         state->message_id = idr_get_new_random(cldap->searches.idr,
596                                                state, UINT16_MAX);
597         if (state->message_id == -1) {
598                 tevent_req_nterror(req, NT_STATUS_INSUFFICIENT_RESOURCES);
599                 goto post;
600         }
601
602         msg = talloc(state, struct ldap_message);
603         if (tevent_req_nomem(msg, req)) {
604                 goto post;
605         }
606
607         msg->messageid  = state->message_id;
608         msg->type       = LDAP_TAG_SearchRequest;
609         msg->controls   = NULL;
610         search = &msg->r.SearchRequest;
611
612         search->basedn          = "";
613         search->scope           = LDAP_SEARCH_SCOPE_BASE;
614         search->deref           = LDAP_DEREFERENCE_NEVER;
615         search->timelimit       = 0;
616         search->sizelimit       = 0;
617         search->attributesonly  = false;
618         search->num_attributes  = str_list_length(io->in.attributes);
619         search->attributes      = io->in.attributes;
620         search->tree            = ldb_parse_tree(msg, io->in.filter);
621         if (tevent_req_nomem(search->tree, req)) {
622                 goto post;
623         }
624
625         if (!ldap_encode(msg, NULL, &state->request.blob, state)) {
626                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
627                 goto post;
628         }
629         talloc_free(msg);
630
631         state->request.idx = 0;
632         state->request.delay = 10*1000*1000;
633         state->request.count = 3;
634         if (io->in.timeout > 0) {
635                 state->request.delay = io->in.timeout * 1000 * 1000;
636                 state->request.count = io->in.retries + 1;
637         }
638
639         now = tevent_timeval_current();
640         end = now;
641         for (i = 0; i < state->request.count; i++) {
642                 end = tevent_timeval_add(&end, state->request.delay / 1000000,
643                                          state->request.delay % 1000000);
644         }
645
646         if (!tevent_req_set_endtime(req, state->caller.ev, end)) {
647                 tevent_req_oom(req);
648                 goto post;
649         }
650
651         subreq = tdgram_sendto_queue_send(state,
652                                           state->caller.ev,
653                                           state->caller.cldap->sock,
654                                           state->caller.cldap->send_queue,
655                                           state->request.blob.data,
656                                           state->request.blob.length,
657                                           state->request.dest);
658         if (tevent_req_nomem(subreq, req)) {
659                 goto post;
660         }
661         tevent_req_set_callback(subreq, cldap_search_state_queue_done, req);
662
663         DLIST_ADD_END(cldap->searches.list, state, struct cldap_search_state *);
664
665         return req;
666
667  post:
668         return tevent_req_post(req, state->caller.ev);
669 }
670
671 static void cldap_search_state_queue_done(struct tevent_req *subreq)
672 {
673         struct tevent_req *req = tevent_req_callback_data(subreq,
674                                  struct tevent_req);
675         struct cldap_search_state *state = tevent_req_data(req,
676                                            struct cldap_search_state);
677         ssize_t ret;
678         int sys_errno = 0;
679         struct timeval next;
680
681         ret = tdgram_sendto_queue_recv(subreq, &sys_errno);
682         talloc_free(subreq);
683         if (ret == -1) {
684                 NTSTATUS status;
685                 status = map_nt_error_from_unix_common(sys_errno);
686                 DLIST_REMOVE(state->caller.cldap->searches.list, state);
687                 ZERO_STRUCT(state->caller.cldap);
688                 tevent_req_nterror(req, status);
689                 return;
690         }
691
692         state->request.idx++;
693
694         /* wait for incoming traffic */
695         if (!cldap_recvfrom_setup(state->caller.cldap)) {
696                 tevent_req_oom(req);
697                 return;
698         }
699
700         if (state->request.idx > state->request.count) {
701                 /* we just wait for the response or a timeout */
702                 return;
703         }
704
705         next = tevent_timeval_current_ofs(state->request.delay / 1000000,
706                                           state->request.delay % 1000000);
707         subreq = tevent_wakeup_send(state,
708                                     state->caller.ev,
709                                     next);
710         if (tevent_req_nomem(subreq, req)) {
711                 return;
712         }
713         tevent_req_set_callback(subreq, cldap_search_state_wakeup_done, req);
714 }
715
716 static void cldap_search_state_wakeup_done(struct tevent_req *subreq)
717 {
718         struct tevent_req *req = tevent_req_callback_data(subreq,
719                                  struct tevent_req);
720         struct cldap_search_state *state = tevent_req_data(req,
721                                            struct cldap_search_state);
722         bool ok;
723
724         ok = tevent_wakeup_recv(subreq);
725         talloc_free(subreq);
726         if (!ok) {
727                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
728                 return;
729         }
730
731         subreq = tdgram_sendto_queue_send(state,
732                                           state->caller.ev,
733                                           state->caller.cldap->sock,
734                                           state->caller.cldap->send_queue,
735                                           state->request.blob.data,
736                                           state->request.blob.length,
737                                           state->request.dest);
738         if (tevent_req_nomem(subreq, req)) {
739                 return;
740         }
741         tevent_req_set_callback(subreq, cldap_search_state_queue_done, req);
742 }
743
744 /*
745   receive a cldap reply
746 */
747 NTSTATUS cldap_search_recv(struct tevent_req *req,
748                            TALLOC_CTX *mem_ctx,
749                            struct cldap_search *io)
750 {
751         struct cldap_search_state *state = tevent_req_data(req,
752                                            struct cldap_search_state);
753         struct ldap_message *ldap_msg;
754         NTSTATUS status;
755
756         if (tevent_req_is_nterror(req, &status)) {
757                 goto failed;
758         }
759
760         ldap_msg = talloc(mem_ctx, struct ldap_message);
761         if (!ldap_msg) {
762                 goto nomem;
763         }
764
765         status = ldap_decode(state->response.asn1, NULL, ldap_msg);
766         if (!NT_STATUS_IS_OK(status)) {
767                 goto failed;
768         }
769
770         ZERO_STRUCT(io->out);
771
772         /* the first possible form has a search result in first place */
773         if (ldap_msg->type == LDAP_TAG_SearchResultEntry) {
774                 io->out.response = talloc(mem_ctx, struct ldap_SearchResEntry);
775                 if (!io->out.response) {
776                         goto nomem;
777                 }
778                 *io->out.response = ldap_msg->r.SearchResultEntry;
779
780                 /* decode the 2nd part */
781                 status = ldap_decode(state->response.asn1, NULL, ldap_msg);
782                 if (!NT_STATUS_IS_OK(status)) {
783                         goto failed;
784                 }
785         }
786
787         if (ldap_msg->type != LDAP_TAG_SearchResultDone) {
788                 status = NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
789                 goto failed;
790         }
791
792         io->out.result = talloc(mem_ctx, struct ldap_Result);
793         if (!io->out.result) {
794                 goto nomem;
795         }
796         *io->out.result = ldap_msg->r.SearchResultDone;
797
798         if (io->out.result->resultcode != LDAP_SUCCESS) {
799                 status = NT_STATUS_LDAP(io->out.result->resultcode);
800                 goto failed;
801         }
802
803         tevent_req_received(req);
804         return NT_STATUS_OK;
805
806 nomem:
807         status = NT_STATUS_NO_MEMORY;
808 failed:
809         tevent_req_received(req);
810         return status;
811 }
812
813
814 /*
815   synchronous cldap search
816 */
817 NTSTATUS cldap_search(struct cldap_socket *cldap,
818                       TALLOC_CTX *mem_ctx,
819                       struct cldap_search *io)
820 {
821         TALLOC_CTX *frame;
822         struct tevent_req *req;
823         struct tevent_context *ev;
824         NTSTATUS status;
825
826         if (cldap->searches.list) {
827                 return NT_STATUS_PIPE_BUSY;
828         }
829
830         if (cldap->incoming.handler) {
831                 return NT_STATUS_INVALID_PIPE_STATE;
832         }
833
834         frame = talloc_stackframe();
835
836         ev = tevent_context_init(frame);
837         if (ev == NULL) {
838                 TALLOC_FREE(frame);
839                 return NT_STATUS_NO_MEMORY;
840         }
841
842         req = cldap_search_send(mem_ctx, ev, cldap, io);
843         if (req == NULL) {
844                 TALLOC_FREE(frame);
845                 return NT_STATUS_NO_MEMORY;
846         }
847
848         if (!tevent_req_poll(req, ev)) {
849                 status = map_nt_error_from_unix_common(errno);
850                 TALLOC_FREE(frame);
851                 return status;
852         }
853
854         status = cldap_search_recv(req, mem_ctx, io);
855         if (!NT_STATUS_IS_OK(status)) {
856                 TALLOC_FREE(frame);
857                 return status;
858         }
859
860         TALLOC_FREE(frame);
861         return NT_STATUS_OK;
862 }
863
864 struct cldap_netlogon_state {
865         struct cldap_search search;
866 };
867
868 static void cldap_netlogon_state_done(struct tevent_req *subreq);
869 /*
870   queue a cldap netlogon for send
871 */
872 struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
873                                        struct tevent_context *ev,
874                                        struct cldap_socket *cldap,
875                                        const struct cldap_netlogon *io)
876 {
877         struct tevent_req *req, *subreq;
878         struct cldap_netlogon_state *state;
879         char *filter;
880         static const char * const attr[] = { "NetLogon", NULL };
881
882         req = tevent_req_create(mem_ctx, &state,
883                                 struct cldap_netlogon_state);
884         if (!req) {
885                 return NULL;
886         }
887
888         filter = talloc_asprintf(state, "(&(NtVer=%s)", 
889                                  ldap_encode_ndr_uint32(state, io->in.version));
890         if (tevent_req_nomem(filter, req)) {
891                 goto post;
892         }
893         if (io->in.user) {
894                 filter = talloc_asprintf_append_buffer(filter, "(User=%s)", io->in.user);
895                 if (tevent_req_nomem(filter, req)) {
896                         goto post;
897                 }
898         }
899         if (io->in.host) {
900                 filter = talloc_asprintf_append_buffer(filter, "(Host=%s)", io->in.host);
901                 if (tevent_req_nomem(filter, req)) {
902                         goto post;
903                 }
904         }
905         if (io->in.realm) {
906                 filter = talloc_asprintf_append_buffer(filter, "(DnsDomain=%s)", io->in.realm);
907                 if (tevent_req_nomem(filter, req)) {
908                         goto post;
909                 }
910         }
911         if (io->in.acct_control != -1) {
912                 filter = talloc_asprintf_append_buffer(filter, "(AAC=%s)", 
913                                                 ldap_encode_ndr_uint32(state, io->in.acct_control));
914                 if (tevent_req_nomem(filter, req)) {
915                         goto post;
916                 }
917         }
918         if (io->in.domain_sid) {
919                 struct dom_sid *sid = dom_sid_parse_talloc(state, io->in.domain_sid);
920                 if (tevent_req_nomem(sid, req)) {
921                         goto post;
922                 }
923                 filter = talloc_asprintf_append_buffer(filter, "(domainSid=%s)",
924                                                 ldap_encode_ndr_dom_sid(state, sid));
925                 if (tevent_req_nomem(filter, req)) {
926                         goto post;
927                 }
928         }
929         if (io->in.domain_guid) {
930                 struct GUID guid;
931                 NTSTATUS status;
932                 status = GUID_from_string(io->in.domain_guid, &guid);
933                 if (tevent_req_nterror(req, status)) {
934                         goto post;
935                 }
936                 filter = talloc_asprintf_append_buffer(filter, "(DomainGuid=%s)",
937                                                 ldap_encode_ndr_GUID(state, &guid));
938                 if (tevent_req_nomem(filter, req)) {
939                         goto post;
940                 }
941         }
942         filter = talloc_asprintf_append_buffer(filter, ")");
943         if (tevent_req_nomem(filter, req)) {
944                 goto post;
945         }
946
947         if (io->in.dest_address) {
948                 state->search.in.dest_address = talloc_strdup(state,
949                                                 io->in.dest_address);
950                 if (tevent_req_nomem(state->search.in.dest_address, req)) {
951                         goto post;
952                 }
953                 state->search.in.dest_port = io->in.dest_port;
954         } else {
955                 state->search.in.dest_address   = NULL;
956                 state->search.in.dest_port      = 0;
957         }
958         state->search.in.filter         = filter;
959         state->search.in.attributes     = attr;
960         state->search.in.timeout        = 2;
961         state->search.in.retries        = 2;
962
963         subreq = cldap_search_send(state, ev, cldap, &state->search);
964         if (tevent_req_nomem(subreq, req)) {
965                 goto post;
966         }
967         tevent_req_set_callback(subreq, cldap_netlogon_state_done, req);
968
969         return req;
970 post:
971         return tevent_req_post(req, ev);
972 }
973
974 static void cldap_netlogon_state_done(struct tevent_req *subreq)
975 {
976         struct tevent_req *req = tevent_req_callback_data(subreq,
977                                  struct tevent_req);
978         struct cldap_netlogon_state *state = tevent_req_data(req,
979                                              struct cldap_netlogon_state);
980         NTSTATUS status;
981
982         status = cldap_search_recv(subreq, state, &state->search);
983         talloc_free(subreq);
984
985         if (tevent_req_nterror(req, status)) {
986                 return;
987         }
988
989         tevent_req_done(req);
990 }
991
992 /*
993   receive a cldap netlogon reply
994 */
995 NTSTATUS cldap_netlogon_recv(struct tevent_req *req,
996                              TALLOC_CTX *mem_ctx,
997                              struct cldap_netlogon *io)
998 {
999         struct cldap_netlogon_state *state = tevent_req_data(req,
1000                                              struct cldap_netlogon_state);
1001         NTSTATUS status;
1002         DATA_BLOB *data;
1003
1004         if (tevent_req_is_nterror(req, &status)) {
1005                 goto failed;
1006         }
1007
1008         if (state->search.out.response == NULL) {
1009                 status = NT_STATUS_NOT_FOUND;
1010                 goto failed;
1011         }
1012
1013         if (state->search.out.response->num_attributes != 1 ||
1014             strcasecmp(state->search.out.response->attributes[0].name, "netlogon") != 0 ||
1015             state->search.out.response->attributes[0].num_values != 1 ||
1016             state->search.out.response->attributes[0].values->length < 2) {
1017                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
1018                 goto failed;
1019         }
1020         data = state->search.out.response->attributes[0].values;
1021
1022         status = pull_netlogon_samlogon_response(data, mem_ctx,
1023                                                  &io->out.netlogon);
1024         if (!NT_STATUS_IS_OK(status)) {
1025                 goto failed;
1026         }
1027
1028         if (io->in.map_response) {
1029                 map_netlogon_samlogon_response(&io->out.netlogon);
1030         }
1031
1032         status =  NT_STATUS_OK;
1033 failed:
1034         tevent_req_received(req);
1035         return status;
1036 }
1037
1038 /*
1039   sync cldap netlogon search
1040 */
1041 NTSTATUS cldap_netlogon(struct cldap_socket *cldap,
1042                         TALLOC_CTX *mem_ctx,
1043                         struct cldap_netlogon *io)
1044 {
1045         TALLOC_CTX *frame;
1046         struct tevent_req *req;
1047         struct tevent_context *ev;
1048         NTSTATUS status;
1049
1050         if (cldap->searches.list) {
1051                 return NT_STATUS_PIPE_BUSY;
1052         }
1053
1054         if (cldap->incoming.handler) {
1055                 return NT_STATUS_INVALID_PIPE_STATE;
1056         }
1057
1058         frame = talloc_stackframe();
1059
1060         ev = tevent_context_init(frame);
1061         if (ev == NULL) {
1062                 TALLOC_FREE(frame);
1063                 return NT_STATUS_NO_MEMORY;
1064         }
1065
1066         req = cldap_netlogon_send(mem_ctx, ev, cldap, io);
1067         if (req == NULL) {
1068                 TALLOC_FREE(frame);
1069                 return NT_STATUS_NO_MEMORY;
1070         }
1071
1072         if (!tevent_req_poll(req, ev)) {
1073                 status = map_nt_error_from_unix_common(errno);
1074                 TALLOC_FREE(frame);
1075                 return status;
1076         }
1077
1078         status = cldap_netlogon_recv(req, mem_ctx, io);
1079         if (!NT_STATUS_IS_OK(status)) {
1080                 TALLOC_FREE(frame);
1081                 return status;
1082         }
1083
1084         TALLOC_FREE(frame);
1085         return NT_STATUS_OK;
1086 }
1087
1088
1089 /*
1090   send an empty reply (used on any error, so the client doesn't keep waiting
1091   or send the bad request again)
1092 */
1093 NTSTATUS cldap_empty_reply(struct cldap_socket *cldap,
1094                            uint32_t message_id,
1095                            struct tsocket_address *dest)
1096 {
1097         NTSTATUS status;
1098         struct cldap_reply reply;
1099         struct ldap_Result result;
1100
1101         reply.messageid    = message_id;
1102         reply.dest         = dest;
1103         reply.response     = NULL;
1104         reply.result       = &result;
1105
1106         ZERO_STRUCT(result);
1107
1108         status = cldap_reply_send(cldap, &reply);
1109
1110         return status;
1111 }
1112
1113 /*
1114   send an error reply (used on any error, so the client doesn't keep waiting
1115   or send the bad request again)
1116 */
1117 NTSTATUS cldap_error_reply(struct cldap_socket *cldap,
1118                            uint32_t message_id,
1119                            struct tsocket_address *dest,
1120                            int resultcode,
1121                            const char *errormessage)
1122 {
1123         NTSTATUS status;
1124         struct cldap_reply reply;
1125         struct ldap_Result result;
1126
1127         reply.messageid    = message_id;
1128         reply.dest         = dest;
1129         reply.response     = NULL;
1130         reply.result       = &result;
1131
1132         ZERO_STRUCT(result);
1133         result.resultcode       = resultcode;
1134         result.errormessage     = errormessage;
1135
1136         status = cldap_reply_send(cldap, &reply);
1137
1138         return status;
1139 }
1140
1141
1142 /*
1143   send a netlogon reply 
1144 */
1145 NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap,
1146                               uint32_t message_id,
1147                               struct tsocket_address *dest,
1148                               uint32_t version,
1149                               struct netlogon_samlogon_response *netlogon)
1150 {
1151         NTSTATUS status;
1152         struct cldap_reply reply;
1153         struct ldap_SearchResEntry response;
1154         struct ldap_Result result;
1155         TALLOC_CTX *tmp_ctx = talloc_new(cldap);
1156         DATA_BLOB blob;
1157
1158         status = push_netlogon_samlogon_response(&blob, tmp_ctx,
1159                                                  netlogon);
1160         if (!NT_STATUS_IS_OK(status)) {
1161                 talloc_free(tmp_ctx);
1162                 return status;
1163         }
1164         reply.messageid    = message_id;
1165         reply.dest         = dest;
1166         reply.response     = &response;
1167         reply.result       = &result;
1168
1169         ZERO_STRUCT(result);
1170
1171         response.dn = "";
1172         response.num_attributes = 1;
1173         response.attributes = talloc(tmp_ctx, struct ldb_message_element);
1174         NT_STATUS_HAVE_NO_MEMORY(response.attributes);
1175         response.attributes->name = "netlogon";
1176         response.attributes->num_values = 1;
1177         response.attributes->values = &blob;
1178
1179         status = cldap_reply_send(cldap, &reply);
1180
1181         talloc_free(tmp_ctx);
1182
1183         return status;
1184 }
1185