docs: fix a typo in history file
[bbaumbach/samba-autobuild/.git] / source3 / libads / cldap.c
1 /* 
2    Samba Unix/Linux SMB client library 
3    net ads cldap functions 
4    Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
5    Copyright (C) 2003 Jim McDonough (jmcd@us.ibm.com)
6    Copyright (C) 2008 Guenther Deschner (gd@samba.org)
7    Copyright (C) 2009 Stefan Metzmacher (metze@samba.org)
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 #include "includes.h"
24 #include "../libcli/cldap/cldap.h"
25 #include "../librpc/gen_ndr/ndr_netlogon.h"
26 #include "../lib/tsocket/tsocket.h"
27 #include "../lib/util/tevent_ntstatus.h"
28 #include "libads/cldap.h"
29
30 struct cldap_multi_netlogon_state {
31         struct tevent_context *ev;
32         const struct tsocket_address * const *servers;
33         int num_servers;
34         const char *domain;
35         const char *hostname;
36         unsigned ntversion;
37         int min_servers;
38
39         struct cldap_socket **cldap;
40         struct tevent_req **subreqs;
41         int num_sent;
42         int num_received;
43         int num_good_received;
44         struct cldap_netlogon *ios;
45         struct netlogon_samlogon_response **responses;
46 };
47
48 static void cldap_multi_netlogon_done(struct tevent_req *subreq);
49 static void cldap_multi_netlogon_next(struct tevent_req *subreq);
50
51 /****************************************************************
52 ****************************************************************/
53
54 #define RETURN_ON_FALSE(x) if (!(x)) return false;
55
56 bool check_cldap_reply_required_flags(uint32_t ret_flags,
57                                       uint32_t req_flags)
58 {
59         if (req_flags == 0) {
60                 return true;
61         }
62
63         if (req_flags & DS_PDC_REQUIRED)
64                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_PDC);
65
66         if (req_flags & DS_GC_SERVER_REQUIRED)
67                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_GC);
68
69         if (req_flags & DS_ONLY_LDAP_NEEDED)
70                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_LDAP);
71
72         if ((req_flags & DS_DIRECTORY_SERVICE_REQUIRED) ||
73             (req_flags & DS_DIRECTORY_SERVICE_PREFERRED))
74                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS);
75
76         if (req_flags & DS_KDC_REQUIRED)
77                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_KDC);
78
79         if (req_flags & DS_TIMESERV_REQUIRED)
80                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_TIMESERV);
81
82         if (req_flags & DS_WEB_SERVICE_REQUIRED)
83                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_ADS_WEB_SERVICE);
84
85         if (req_flags & DS_WRITABLE_REQUIRED)
86                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_WRITABLE);
87
88         if (req_flags & DS_DIRECTORY_SERVICE_6_REQUIRED)
89                 RETURN_ON_FALSE(ret_flags & (NBT_SERVER_SELECT_SECRET_DOMAIN_6
90                                              |NBT_SERVER_FULL_SECRET_DOMAIN_6));
91
92         if (req_flags & DS_DIRECTORY_SERVICE_8_REQUIRED)
93                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS_8);
94
95         if (req_flags & DS_DIRECTORY_SERVICE_9_REQUIRED)
96                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS_9);
97
98         if (req_flags & DS_DIRECTORY_SERVICE_10_REQUIRED)
99                 RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS_10);
100
101         return true;
102 }
103
104 /*
105  * Do a parallel cldap ping to the servers. The first "min_servers"
106  * are fired directly, the remaining ones in 100msec intervals. If
107  * "min_servers" responses came in successfully, we immediately reply,
108  * not waiting for the remaining ones.
109  */
110
111 struct tevent_req *cldap_multi_netlogon_send(
112         TALLOC_CTX *mem_ctx, struct tevent_context *ev,
113         const struct tsocket_address * const *servers, int num_servers,
114         const char *domain, const char *hostname, unsigned ntversion,
115         int min_servers)
116 {
117         struct tevent_req *req, *subreq;
118         struct cldap_multi_netlogon_state *state;
119         int i;
120
121         req = tevent_req_create(mem_ctx, &state,
122                                 struct cldap_multi_netlogon_state);
123         if (req == NULL) {
124                 return NULL;
125         }
126         state->ev = ev;
127         state->servers = servers;
128         state->num_servers = num_servers;
129         state->domain = domain;
130         state->hostname = hostname;
131         state->ntversion = ntversion;
132         state->min_servers = min_servers;
133
134         if (min_servers > num_servers) {
135                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
136                 return tevent_req_post(req, ev);
137         }
138
139         state->subreqs = talloc_zero_array(state,
140                                            struct tevent_req *,
141                                            num_servers);
142         if (tevent_req_nomem(state->subreqs, req)) {
143                 return tevent_req_post(req, ev);
144         }
145
146         state->cldap = talloc_zero_array(state,
147                                          struct cldap_socket *,
148                                          num_servers);
149         if (tevent_req_nomem(state->cldap, req)) {
150                 return tevent_req_post(req, ev);
151         }
152
153         state->responses = talloc_zero_array(state,
154                                 struct netlogon_samlogon_response *,
155                                 num_servers);
156         if (tevent_req_nomem(state->responses, req)) {
157                 return tevent_req_post(req, ev);
158         }
159
160         state->ios = talloc_zero_array(state->responses,
161                                        struct cldap_netlogon,
162                                        num_servers);
163         if (tevent_req_nomem(state->ios, req)) {
164                 return tevent_req_post(req, ev);
165         }
166
167         for (i=0; i<num_servers; i++) {
168                 NTSTATUS status;
169
170                 status = cldap_socket_init(state->cldap,
171                                            NULL, /* local_addr */
172                                            state->servers[i],
173                                            &state->cldap[i]);
174                 if (!NT_STATUS_IS_OK(status)) {
175                         /*
176                          * Don't error out all sends just
177                          * because one cldap_socket_init() failed.
178                          * Log it here, and the cldap_netlogon_send()
179                          * will catch it (with in.dest_address == NULL)
180                          * and correctly error out in
181                          * cldap_multi_netlogon_done(). This still allows
182                          * the other requests to be concurrently sent.
183                          */
184                         DBG_NOTICE("cldap_socket_init failed for %s "
185                                 " error %s\n",
186                                 tsocket_address_string(state->servers[i],
187                                         req),
188                                 nt_errstr(status));
189                 }
190
191                 state->ios[i].in.dest_address   = NULL;
192                 state->ios[i].in.dest_port      = 0;
193                 state->ios[i].in.realm          = domain;
194                 state->ios[i].in.host           = NULL;
195                 state->ios[i].in.user           = NULL;
196                 state->ios[i].in.domain_guid    = NULL;
197                 state->ios[i].in.domain_sid     = NULL;
198                 state->ios[i].in.acct_control   = 0;
199                 state->ios[i].in.version        = ntversion;
200                 state->ios[i].in.map_response   = false;
201         }
202
203         for (i=0; i<min_servers; i++) {
204                 state->subreqs[i] = cldap_netlogon_send(state->subreqs,
205                                                         state->ev,
206                                                         state->cldap[i],
207                                                         &state->ios[i]);
208                 if (tevent_req_nomem(state->subreqs[i], req)) {
209                         return tevent_req_post(req, ev);
210                 }
211                 tevent_req_set_callback(
212                         state->subreqs[i], cldap_multi_netlogon_done, req);
213         }
214         state->num_sent = min_servers;
215
216         if (state->num_sent < state->num_servers) {
217                 /*
218                  * After 100 milliseconds fire the next one
219                  */
220                 subreq = tevent_wakeup_send(state, state->ev,
221                                             timeval_current_ofs(0, 100000));
222                 if (tevent_req_nomem(subreq, req)) {
223                         return tevent_req_post(req, ev);
224                 }
225                 tevent_req_set_callback(subreq, cldap_multi_netlogon_next,
226                                         req);
227         }
228
229         return req;
230 }
231
232 static void cldap_multi_netlogon_done(struct tevent_req *subreq)
233 {
234         struct tevent_req *req = tevent_req_callback_data(
235                 subreq, struct tevent_req);
236         struct cldap_multi_netlogon_state *state = tevent_req_data(
237                 req, struct cldap_multi_netlogon_state);
238         NTSTATUS status;
239         struct netlogon_samlogon_response *response;
240         int i;
241
242         for (i=0; i<state->num_sent; i++) {
243                 if (state->subreqs[i] == subreq) {
244                         break;
245                 }
246         }
247         if (i == state->num_sent) {
248                 /*
249                  * Got a response we did not fire...
250                  */
251                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
252                 return;
253         }
254         state->subreqs[i] = NULL;
255
256         response = talloc_zero(state, struct netlogon_samlogon_response);
257         if (tevent_req_nomem(response, req)) {
258                 return;
259         }
260
261         status = cldap_netlogon_recv(subreq, response,
262                                      &state->ios[i]);
263         TALLOC_FREE(subreq);
264         state->num_received += 1;
265
266         if (NT_STATUS_IS_OK(status)) {
267                 *response = state->ios[i].out.netlogon;
268                 state->responses[i] = talloc_move(state->responses,
269                                                   &response);
270                 state->num_good_received += 1;
271         }
272
273         if ((state->num_received == state->num_servers) ||
274             (state->num_good_received >= state->min_servers)) {
275                 tevent_req_done(req);
276                 return;
277         }
278 }
279
280 static void cldap_multi_netlogon_next(struct tevent_req *subreq)
281 {
282         struct tevent_req *req = tevent_req_callback_data(
283                 subreq, struct tevent_req);
284         struct cldap_multi_netlogon_state *state = tevent_req_data(
285                 req, struct cldap_multi_netlogon_state);
286         bool ret;
287
288         ret = tevent_wakeup_recv(subreq);
289         TALLOC_FREE(subreq);
290         if (!ret) {
291                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
292                 return;
293         }
294
295         subreq = cldap_netlogon_send(state->subreqs,
296                                      state->ev,
297                                      state->cldap[state->num_sent],
298                                      &state->ios[state->num_sent]);
299         if (tevent_req_nomem(subreq, req)) {
300                 return;
301         }
302         tevent_req_set_callback(subreq, cldap_multi_netlogon_done, req);
303         state->subreqs[state->num_sent] = subreq;
304         state->num_sent += 1;
305
306         if (state->num_sent < state->num_servers) {
307                 /*
308                  * After 100 milliseconds fire the next one
309                  */
310                 subreq = tevent_wakeup_send(state, state->ev,
311                                             timeval_current_ofs(0, 100000));
312                 if (tevent_req_nomem(subreq, req)) {
313                         return;
314                 }
315                 tevent_req_set_callback(subreq, cldap_multi_netlogon_next,
316                                         req);
317         }
318 }
319
320 NTSTATUS cldap_multi_netlogon_recv(
321         struct tevent_req *req, TALLOC_CTX *mem_ctx,
322         struct netlogon_samlogon_response ***responses)
323 {
324         struct cldap_multi_netlogon_state *state = tevent_req_data(
325                 req, struct cldap_multi_netlogon_state);
326         NTSTATUS status;
327
328         if (tevent_req_is_nterror(req, &status) &&
329             !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
330                 return status;
331         }
332         /*
333          * If we timeout, give back what we have so far
334          */
335         *responses = talloc_move(mem_ctx, &state->responses);
336         return NT_STATUS_OK;
337 }
338
339 NTSTATUS cldap_multi_netlogon(
340         TALLOC_CTX *mem_ctx,
341         const struct tsocket_address * const *servers,
342         int num_servers,
343         const char *domain, const char *hostname, unsigned ntversion,
344         int min_servers, struct timeval timeout,
345         struct netlogon_samlogon_response ***responses)
346 {
347         struct tevent_context *ev;
348         struct tevent_req *req;
349         NTSTATUS status = NT_STATUS_NO_MEMORY;
350
351         ev = samba_tevent_context_init(talloc_tos());
352         if (ev == NULL) {
353                 goto fail;
354         }
355         req = cldap_multi_netlogon_send(
356                 ev, ev, servers, num_servers, domain, hostname, ntversion,
357                 min_servers);
358         if (req == NULL) {
359                 goto fail;
360         }
361         if (!tevent_req_set_endtime(req, ev, timeout)) {
362                 goto fail;
363         }
364         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
365                 goto fail;
366         }
367         status = cldap_multi_netlogon_recv(req, mem_ctx, responses);
368 fail:
369         TALLOC_FREE(ev);
370         return status;
371 }
372
373 /*******************************************************************
374   do a cldap netlogon query.  Always 389/udp
375 *******************************************************************/
376
377 bool ads_cldap_netlogon(TALLOC_CTX *mem_ctx,
378                         struct sockaddr_storage *ss,
379                         const char *realm,
380                         uint32_t nt_version,
381                         struct netlogon_samlogon_response **_reply)
382 {
383         NTSTATUS status;
384         char addrstr[INET6_ADDRSTRLEN];
385         const char *dest_str;
386         struct tsocket_address *dest_addr;
387         const struct tsocket_address * const *dest_addrs;
388         struct netlogon_samlogon_response **responses = NULL;
389         int ret;
390
391         dest_str = print_sockaddr(addrstr, sizeof(addrstr), ss);
392
393         ret = tsocket_address_inet_from_strings(mem_ctx, "ip",
394                                                 dest_str, LDAP_PORT,
395                                                 &dest_addr);
396         if (ret != 0) {
397                 status = map_nt_error_from_unix(errno);
398                 DEBUG(2,("Failed to create cldap tsocket_address for %s - %s\n",
399                          dest_str, nt_errstr(status)));
400                 return false;
401         }
402
403         dest_addrs = (const struct tsocket_address * const *)&dest_addr;
404
405         status = cldap_multi_netlogon(talloc_tos(),
406                                 dest_addrs, 1,
407                                 realm, NULL,
408                                 nt_version, 1,
409                                 timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0),
410                                 &responses);
411         if (!NT_STATUS_IS_OK(status)) {
412                 DEBUG(2, ("ads_cldap_netlogon: cldap_multi_netlogon "
413                           "failed: %s\n", nt_errstr(status)));
414                 return false;
415         }
416         if (responses == NULL || responses[0] == NULL) {
417                 DEBUG(2, ("ads_cldap_netlogon: did not get a reply\n"));
418                 TALLOC_FREE(responses);
419                 return false;
420         }
421         *_reply = talloc_move(mem_ctx, &responses[0]);
422
423         return true;
424 }
425
426 /*******************************************************************
427   do a cldap netlogon query.  Always 389/udp
428 *******************************************************************/
429
430 bool ads_cldap_netlogon_5(TALLOC_CTX *mem_ctx,
431                           struct sockaddr_storage *ss,
432                           const char *realm,
433                           struct NETLOGON_SAM_LOGON_RESPONSE_EX *reply5)
434 {
435         uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
436         struct netlogon_samlogon_response *reply = NULL;
437         bool ret;
438
439         ret = ads_cldap_netlogon(mem_ctx, ss, realm, nt_version, &reply);
440         if (!ret) {
441                 return false;
442         }
443
444         if (reply->ntver != NETLOGON_NT_VERSION_5EX) {
445                 DEBUG(0,("ads_cldap_netlogon_5: nt_version mismatch: 0x%08x\n",
446                         reply->ntver));
447                 return false;
448         }
449
450         *reply5 = reply->data.nt5_ex;
451
452         return true;
453 }