7caf5a08102c73d3e5035b014738df869f3dfac9
[kai/samba.git] / source4 / libcli / cldap / cldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    cldap client library
5
6    Copyright (C) Andrew Tridgell 2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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 "lib/events/events.h"
37 #include "dlinklist.h"
38 #include "libcli/ldap/ldap.h"
39 #include "libcli/cldap/cldap.h"
40 #include "lib/socket/socket.h"
41 #include "include/asn_1.h"
42
43 #define CLDAP_MAX_PACKET_SIZE 2048
44 const unsigned CLDAP_PORT = 389;
45
46 /*
47   destroy a pending request
48 */
49 static int cldap_request_destructor(void *ptr)
50 {
51         struct cldap_request *req = talloc_get_type(ptr, struct cldap_request);
52         if (req->state == CLDAP_REQUEST_SEND) {
53                 DLIST_REMOVE(req->cldap->send_queue, req);
54         }
55         if (req->message_id != 0) {
56                 idr_remove(req->cldap->idr, req->message_id);
57                 req->message_id = 0;
58         }
59         return 0;
60 }
61
62 /*
63   handle recv events on a cldap socket
64 */
65 static void cldap_socket_recv(struct cldap_socket *cldap)
66 {
67         TALLOC_CTX *tmp_ctx = talloc_new(cldap);
68         NTSTATUS status;
69         const char *src_addr;
70         int src_port;
71         DATA_BLOB blob;
72         size_t nread;
73         struct asn1_data asn1;
74         struct ldap_message ldap_msg;
75         struct cldap_request *req;
76
77         blob = data_blob_talloc(tmp_ctx, NULL, CLDAP_MAX_PACKET_SIZE);
78         if (blob.data == NULL) {
79                 talloc_free(tmp_ctx);
80                 return;
81         }
82
83         status = socket_recvfrom(cldap->sock, blob.data, blob.length, &nread, 0,
84                                  &src_addr, &src_port);
85         if (!NT_STATUS_IS_OK(status)) {
86                 talloc_free(tmp_ctx);
87                 return;
88         }
89         talloc_steal(tmp_ctx, src_addr);
90         blob.length = nread;
91
92         DEBUG(2,("Received cldap packet of length %d from %s:%d\n", 
93                  blob.length, src_addr, src_port));
94
95         if (!asn1_load(&asn1, blob)) {
96                 DEBUG(2,("Failed to setup for asn.1 decode\n"));
97                 talloc_free(tmp_ctx);
98                 return;
99         }
100         talloc_steal(tmp_ctx, asn1.data);
101
102         ZERO_STRUCT(ldap_msg);
103         ldap_msg.mem_ctx = tmp_ctx;
104
105         /* this initial decode is used to find the message id */
106         if (!ldap_decode(&asn1, &ldap_msg)) {
107                 DEBUG(2,("Failed to decode ldap message\n"));
108                 talloc_free(tmp_ctx);
109                 return;
110         }
111
112         /* find the pending request */
113         req = idr_find(cldap->idr, ldap_msg.messageid);
114         if (req == NULL) {
115                 DEBUG(2,("Mismatched cldap reply %u from %s:%d\n",
116                          ldap_msg.messageid, src_addr, src_port));
117                 talloc_free(tmp_ctx);
118                 return;
119         }
120
121         req->asn1 = asn1;
122         talloc_steal(req, asn1.data);
123         req->asn1.ofs = 0;
124
125         req->state = CLDAP_REQUEST_DONE;
126         talloc_free(req->te);
127
128         talloc_free(tmp_ctx);
129
130         if (req->async.fn) {
131                 req->async.fn(req);
132         }
133 }
134
135 /*
136   handle request timeouts
137 */
138 static void cldap_request_timeout(struct event_context *event_ctx, 
139                                   struct timed_event *te, struct timeval t,
140                                   void *private)
141 {
142         struct cldap_request *req = talloc_get_type(private, struct cldap_request);
143
144         /* possibly try again */
145         if (req->num_retries != 0) {
146                 size_t len = req->encoded.length;
147
148                 req->num_retries--;
149
150                 socket_sendto(req->cldap->sock, &req->encoded, &len, 0, 
151                               req->dest_addr, req->dest_port);
152
153                 req->te = event_add_timed(req->cldap->event_ctx, req, 
154                                           timeval_current_ofs(req->timeout, 0),
155                                           cldap_request_timeout, req);
156                 return;
157         }
158
159         req->state = CLDAP_REQUEST_TIMEOUT;
160         if (req->async.fn) {
161                 req->async.fn(req);
162         }
163 }
164
165 /*
166   handle send events on a cldap socket
167 */
168 static void cldap_socket_send(struct cldap_socket *cldap)
169 {
170         struct cldap_request *req;
171         NTSTATUS status;
172
173         while ((req = cldap->send_queue)) {
174                 size_t len;
175                 
176                 len = req->encoded.length;
177                 status = socket_sendto(cldap->sock, &req->encoded, &len, 0, 
178                                        req->dest_addr, req->dest_port);
179                 if (NT_STATUS_IS_ERR(status)) {
180                         DEBUG(3,("Failed to send cldap request of length %u to %s:%d\n",
181                                  req->encoded.length, req->dest_addr, req->dest_port));
182                         DLIST_REMOVE(cldap->send_queue, req);
183                         talloc_free(req);
184                         continue;
185                 }
186
187                 if (!NT_STATUS_IS_OK(status)) return;
188
189                 DLIST_REMOVE(cldap->send_queue, req);
190
191                 req->state = CLDAP_REQUEST_WAIT;
192
193                 req->te = event_add_timed(cldap->event_ctx, req, 
194                                           timeval_current_ofs(req->timeout, 0),
195                                           cldap_request_timeout, req);
196
197                 EVENT_FD_READABLE(cldap->fde);
198         }
199
200         EVENT_FD_NOT_WRITEABLE(cldap->fde);
201         return;
202 }
203
204
205 /*
206   handle fd events on a cldap_socket
207 */
208 static void cldap_socket_handler(struct event_context *ev, struct fd_event *fde,
209                                  uint16_t flags, void *private)
210 {
211         struct cldap_socket *cldap = talloc_get_type(private, struct cldap_socket);
212         if (flags & EVENT_FD_WRITE) {
213                 cldap_socket_send(cldap);
214         } else if (flags & EVENT_FD_READ) {
215                 cldap_socket_recv(cldap);
216         }
217 }
218
219 /*
220   initialise a cldap_socket. The event_ctx is optional, if provided
221   then operations will use that event context
222 */
223 struct cldap_socket *cldap_socket_init(TALLOC_CTX *mem_ctx, 
224                                        struct event_context *event_ctx)
225 {
226         struct cldap_socket *cldap;
227         NTSTATUS status;
228
229         cldap = talloc(mem_ctx, struct cldap_socket);
230         if (cldap == NULL) goto failed;
231
232         if (event_ctx == NULL) {
233                 cldap->event_ctx = event_context_init(cldap);
234         } else {
235                 cldap->event_ctx = talloc_reference(cldap, event_ctx);
236         }
237         if (cldap->event_ctx == NULL) goto failed;
238
239         cldap->idr = idr_init(cldap);
240         if (cldap->idr == NULL) goto failed;
241
242         status = socket_create("ip", SOCKET_TYPE_DGRAM, &cldap->sock, 0);
243         if (!NT_STATUS_IS_OK(status)) goto failed;
244
245         talloc_steal(cldap, cldap->sock);
246
247         cldap->fde = event_add_fd(cldap->event_ctx, cldap, 
248                                       socket_get_fd(cldap->sock), 0,
249                                       cldap_socket_handler, cldap);
250
251         cldap->send_queue = NULL;
252         
253         return cldap;
254
255 failed:
256         talloc_free(cldap);
257         return NULL;
258 }
259
260
261 /*
262   queue a cldap request for send
263 */
264 struct cldap_request *cldap_search_send(struct cldap_socket *cldap, 
265                                         struct cldap_search *io)
266 {
267         struct ldap_message msg;
268         struct cldap_request *req;
269         struct ldap_SearchRequest *search;
270
271         req = talloc_zero(cldap, struct cldap_request);
272         if (req == NULL) goto failed;
273
274         req->cldap       = cldap;
275         req->state       = CLDAP_REQUEST_SEND;
276         req->timeout     = io->in.timeout;
277         req->num_retries = io->in.retries;
278
279         req->dest_addr = talloc_strdup(req, io->in.dest_address);
280         if (req->dest_addr == NULL) goto failed;
281         req->dest_port = CLDAP_PORT;
282
283         req->message_id = idr_get_new_random(cldap->idr, req, UINT16_MAX);
284         if (req->message_id == -1) goto failed;
285
286         talloc_set_destructor(req, cldap_request_destructor);
287
288         msg.mem_ctx         = cldap;
289         msg.messageid       = req->message_id;
290         msg.type            = LDAP_TAG_SearchRequest;
291         msg.num_controls    = 0;
292         msg.controls        = NULL;
293         search = &msg.r.SearchRequest;
294
295         search->basedn         = "";
296         search->scope          = LDAP_SEARCH_SCOPE_BASE;
297         search->deref          = LDAP_DEREFERENCE_NEVER;
298         search->timelimit      = 0;
299         search->sizelimit      = 0;
300         search->attributesonly = False;
301         search->num_attributes = str_list_length(io->in.attributes);
302         search->attributes     = io->in.attributes;
303         search->filter         = io->in.filter;
304
305         if (!ldap_encode(&msg, &req->encoded)) {
306                 DEBUG(0,("Failed to encode cldap message to %s:%d\n",
307                          req->dest_addr, req->dest_port));
308                 goto failed;
309         }
310         talloc_steal(req, req->encoded.data);
311
312         DLIST_ADD_END(cldap->send_queue, req, struct cldap_request *);
313
314         EVENT_FD_WRITEABLE(cldap->fde);
315
316         return req;
317
318 failed:
319         talloc_free(req);
320         return NULL;
321 }
322
323 /*
324   receive a cldap reply
325 */
326 NTSTATUS cldap_search_recv(struct cldap_request *req, 
327                            TALLOC_CTX *mem_ctx, 
328                            struct cldap_search *io)
329 {
330         struct ldap_message ldap_msg;
331
332         if (req == NULL) {
333                 return NT_STATUS_NO_MEMORY;
334         }
335
336         while (req->state < CLDAP_REQUEST_DONE) {
337                 if (event_loop_once(req->cldap->event_ctx) != 0) {
338                         talloc_free(req);
339                         return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
340                 }
341         }
342
343         if (req->state == CLDAP_REQUEST_TIMEOUT) {
344                 talloc_free(req);
345                 return NT_STATUS_IO_TIMEOUT;
346         }
347
348         ZERO_STRUCT(ldap_msg);
349         ldap_msg.mem_ctx = mem_ctx;
350
351         if (!ldap_decode(&req->asn1, &ldap_msg)) {
352                 talloc_free(req);
353                 return NT_STATUS_INVALID_PARAMETER;
354         }
355
356         ZERO_STRUCT(io->out);
357
358         /* the first possible form has a search result in first place */
359         if (ldap_msg.type == LDAP_TAG_SearchResultEntry) {
360                 io->out.response = talloc(mem_ctx, struct ldap_SearchResEntry);
361                 NT_STATUS_HAVE_NO_MEMORY(io->out.response);
362                 *io->out.response = ldap_msg.r.SearchResultEntry;
363
364                 /* decode the 2nd part */
365                 if (!ldap_decode(&req->asn1, &ldap_msg)) {
366                         talloc_free(req);
367                         return NT_STATUS_INVALID_PARAMETER;
368                 }
369         }
370
371         if (ldap_msg.type != LDAP_TAG_SearchResultDone) {
372                 talloc_free(req);
373                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
374         }
375
376         io->out.result = talloc(mem_ctx, struct ldap_Result);
377         NT_STATUS_HAVE_NO_MEMORY(io->out.result);
378         *io->out.result = ldap_msg.r.SearchResultDone;
379
380         talloc_free(req);
381         return NT_STATUS_OK;
382 }
383
384
385 /*
386   synchronous cldap search
387 */
388 NTSTATUS cldap_search(struct cldap_socket *cldap, 
389                       TALLOC_CTX *mem_ctx, 
390                       struct cldap_search *io)
391 {
392         struct cldap_request *req = cldap_search_send(cldap, io);
393         return cldap_search_recv(req, mem_ctx, io);
394 }
395
396
397 /*
398   queue a cldap netlogon for send
399 */
400 struct cldap_request *cldap_netlogon_send(struct cldap_socket *cldap, 
401                                           struct cldap_netlogon *io)
402 {
403         struct cldap_search search;
404         char *filter;
405         struct cldap_request *req;
406         const char *attr[] = { "NetLogon", NULL };
407
408         filter = talloc_asprintf(cldap, 
409                                  "(&(DnsDomain=%s)(Host=%s)(NtVer=\\%02X\\00\\00\\00))", 
410                                  io->in.realm, io->in.host, io->in.version);
411         if (filter == NULL) return NULL;
412
413         search.in.dest_address = io->in.dest_address;
414         search.in.filter       = filter;
415         search.in.attributes   = attr;
416         search.in.timeout      = 2;
417         search.in.retries      = 2;
418
419         req = cldap_search_send(cldap, &search);
420
421         talloc_free(filter);
422
423         return req;
424 }
425
426
427 /*
428   receive a cldap netlogon reply
429 */
430 NTSTATUS cldap_netlogon_recv(struct cldap_request *req, 
431                              TALLOC_CTX *mem_ctx, 
432                              struct cldap_netlogon *io)
433 {
434         NTSTATUS status;
435         struct cldap_search search;
436         DATA_BLOB *data;
437
438         status = cldap_search_recv(req, mem_ctx, &search);
439         if (!NT_STATUS_IS_OK(status)) {
440                 return status;
441         }
442         if (search.out.response == NULL) {
443                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
444         }
445
446         if (search.out.response->num_attributes != 1 ||
447             strcasecmp(search.out.response->attributes[0].name, "netlogon") != 0 ||
448             search.out.response->attributes[0].num_values != 1 ||
449             search.out.response->attributes[0].values->length < 2) {
450                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
451         }
452         data = search.out.response->attributes[0].values;
453
454         status = ndr_pull_struct_blob_all(data, mem_ctx, &io->out.netlogon, 
455                                           (ndr_pull_flags_fn_t)ndr_pull_nbt_cldap_netlogon);
456         if (!NT_STATUS_IS_OK(status)) {
457                 DEBUG(2,("cldap failed to parse netlogon response of type 0x%02x\n",
458                          SVAL(data->data, 0)));
459                 dump_data(10, data->data, data->length);
460         }
461
462         return status;
463 }
464
465 /*
466   sync cldap netlogon search
467 */
468 NTSTATUS cldap_netlogon(struct cldap_socket *cldap, 
469                         TALLOC_CTX *mem_ctx, struct cldap_netlogon *io)
470 {
471         struct cldap_request *req = cldap_netlogon_send(cldap, io);
472         return cldap_netlogon_recv(req, mem_ctx, io);
473 }