/src/samba/source3/libads/netlogon_ping.c
Line | Count | Source |
1 | | /* |
2 | | * Samba Unix/Linux SMB client library |
3 | | * |
4 | | * This program is free software; you can redistribute it and/or modify |
5 | | * it under the terms of the GNU General Public License as published by |
6 | | * the Free Software Foundation; either version 3 of the License, or |
7 | | * (at your option) any later version. |
8 | | * |
9 | | * This program is distributed in the hope that it will be useful, |
10 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | * GNU General Public License for more details. |
13 | | * |
14 | | * You should have received a copy of the GNU General Public License |
15 | | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | |
18 | | #include "replace.h" |
19 | | #include <tevent.h> |
20 | | #include "netlogon_ping.h" |
21 | | #include "libcli/netlogon/netlogon_proto.h" |
22 | | #include "libcli/ldap/ldap_ndr.h" |
23 | | #include "libcli/ldap/ldap_message.h" |
24 | | #include "libcli/cldap/cldap.h" |
25 | | #include "source3/include/tldap.h" |
26 | | #include "source3/include/tldap_util.h" |
27 | | #include "source3/lib/tldap_tls_connect.h" |
28 | | #include "lib/util/tevent_unix.h" |
29 | | #include "lib/util/tevent_ntstatus.h" |
30 | | #include "source4/lib/tls/tls.h" |
31 | | #include "source3/libads/cldap.h" |
32 | | #include "librpc/gen_ndr/netlogon.h" |
33 | | |
34 | | #define RETURN_ON_FALSE(x) \ |
35 | 0 | if (!(x)) \ |
36 | 0 | return false; |
37 | | |
38 | | bool check_cldap_reply_required_flags(uint32_t ret_flags, uint32_t req_flags) |
39 | 0 | { |
40 | 0 | if (req_flags == 0) { |
41 | 0 | return true; |
42 | 0 | } |
43 | | |
44 | 0 | if (req_flags & DS_PDC_REQUIRED) |
45 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_PDC); |
46 | |
|
47 | 0 | if (req_flags & DS_GC_SERVER_REQUIRED) |
48 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_GC); |
49 | |
|
50 | 0 | if (req_flags & DS_ONLY_LDAP_NEEDED) |
51 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_LDAP); |
52 | |
|
53 | 0 | if ((req_flags & DS_DIRECTORY_SERVICE_REQUIRED) || |
54 | 0 | (req_flags & DS_DIRECTORY_SERVICE_PREFERRED)) |
55 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS); |
56 | |
|
57 | 0 | if (req_flags & DS_KDC_REQUIRED) |
58 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_KDC); |
59 | |
|
60 | 0 | if (req_flags & DS_TIMESERV_REQUIRED) |
61 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_TIMESERV); |
62 | |
|
63 | 0 | if (req_flags & DS_WEB_SERVICE_REQUIRED) |
64 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_ADS_WEB_SERVICE); |
65 | |
|
66 | 0 | if (req_flags & DS_WRITABLE_REQUIRED) |
67 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_WRITABLE); |
68 | |
|
69 | 0 | if (req_flags & DS_DIRECTORY_SERVICE_6_REQUIRED) |
70 | 0 | RETURN_ON_FALSE(ret_flags & |
71 | 0 | (NBT_SERVER_SELECT_SECRET_DOMAIN_6 | |
72 | 0 | NBT_SERVER_FULL_SECRET_DOMAIN_6)); |
73 | |
|
74 | 0 | if (req_flags & DS_DIRECTORY_SERVICE_8_REQUIRED) |
75 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS_8); |
76 | |
|
77 | 0 | if (req_flags & DS_DIRECTORY_SERVICE_9_REQUIRED) |
78 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS_9); |
79 | |
|
80 | 0 | if (req_flags & DS_DIRECTORY_SERVICE_10_REQUIRED) |
81 | 0 | RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS_10); |
82 | |
|
83 | 0 | return true; |
84 | 0 | } |
85 | | |
86 | | struct ldap_netlogon_state { |
87 | | struct tevent_context *ev; |
88 | | struct tsocket_address *local; |
89 | | struct tsocket_address *remote; |
90 | | enum client_netlogon_ping_protocol proto; |
91 | | const char *filter; |
92 | | |
93 | | struct tstream_context *plain; |
94 | | struct tldap_context *tldap; |
95 | | struct tstream_tls_params *tls_params; |
96 | | |
97 | | struct netlogon_samlogon_response *response; |
98 | | }; |
99 | | |
100 | | static void ldap_netlogon_connected(struct tevent_req *subreq); |
101 | | static void ldap_netlogon_starttls_done(struct tevent_req *subreq); |
102 | | static void ldap_netlogon_tls_set_up(struct tevent_req *subreq); |
103 | | static void ldap_netlogon_search(struct tevent_req *req); |
104 | | static void ldap_netlogon_searched(struct tevent_req *subreq); |
105 | | |
106 | | static struct tevent_req *ldap_netlogon_send( |
107 | | TALLOC_CTX *mem_ctx, |
108 | | struct tevent_context *ev, |
109 | | const struct tsocket_address *server, |
110 | | enum client_netlogon_ping_protocol proto, |
111 | | const char *filter) |
112 | 0 | { |
113 | 0 | struct tevent_req *req = NULL, *subreq = NULL; |
114 | 0 | struct ldap_netlogon_state *state = NULL; |
115 | 0 | uint16_t port; |
116 | 0 | int ret; |
117 | |
|
118 | 0 | req = tevent_req_create(mem_ctx, &state, struct ldap_netlogon_state); |
119 | 0 | if (req == NULL) { |
120 | 0 | return NULL; |
121 | 0 | } |
122 | 0 | state->ev = ev; |
123 | 0 | state->filter = filter; |
124 | 0 | state->proto = proto; |
125 | |
|
126 | 0 | state->remote = tsocket_address_copy(server, state); |
127 | 0 | if (tevent_req_nomem(state->remote, req)) { |
128 | 0 | return tevent_req_post(req, ev); |
129 | 0 | } |
130 | | |
131 | 0 | port = (proto == CLIENT_NETLOGON_PING_LDAPS) ? 636 : 389; |
132 | |
|
133 | 0 | ret = tsocket_address_inet_set_port(state->remote, port); |
134 | 0 | if (ret != 0) { |
135 | 0 | tevent_req_nterror(req, map_nt_error_from_unix_common(errno)); |
136 | 0 | return tevent_req_post(req, ev); |
137 | 0 | } |
138 | | |
139 | 0 | ret = tsocket_address_inet_from_strings( |
140 | 0 | state, "ip", NULL, 0, &state->local); |
141 | 0 | if (ret != 0) { |
142 | 0 | tevent_req_nterror(req, map_nt_error_from_unix_common(errno)); |
143 | 0 | return tevent_req_post(req, ev); |
144 | 0 | } |
145 | | |
146 | 0 | subreq = tstream_inet_tcp_connect_send(state, |
147 | 0 | state->ev, |
148 | 0 | state->local, |
149 | 0 | state->remote); |
150 | 0 | if (tevent_req_nomem(subreq, req)) { |
151 | 0 | return tevent_req_post(req, ev); |
152 | 0 | } |
153 | 0 | tevent_req_set_callback(subreq, ldap_netlogon_connected, req); |
154 | |
|
155 | 0 | return req; |
156 | 0 | } |
157 | | |
158 | | static void ldap_netlogon_connected(struct tevent_req *subreq) |
159 | 0 | { |
160 | 0 | struct tevent_req *req = tevent_req_callback_data(subreq, |
161 | 0 | struct tevent_req); |
162 | 0 | struct ldap_netlogon_state *state = tevent_req_data( |
163 | 0 | req, struct ldap_netlogon_state); |
164 | 0 | int ret, err; |
165 | 0 | NTSTATUS status; |
166 | |
|
167 | 0 | ret = tstream_inet_tcp_connect_recv( |
168 | 0 | subreq, &err, state, &state->plain, NULL); |
169 | 0 | TALLOC_FREE(subreq); |
170 | 0 | if (ret == -1) { |
171 | 0 | tevent_req_nterror(req, map_nt_error_from_unix_common(err)); |
172 | 0 | return; |
173 | 0 | } |
174 | | |
175 | 0 | state->tldap = tldap_context_create_from_plain_stream( |
176 | 0 | state, &state->plain); |
177 | 0 | if (tevent_req_nomem(state->tldap, req)) { |
178 | 0 | return; |
179 | 0 | } |
180 | | |
181 | 0 | if (state->proto == CLIENT_NETLOGON_PING_LDAP) { |
182 | 0 | ldap_netlogon_search(req); |
183 | 0 | return; |
184 | 0 | } |
185 | | |
186 | 0 | status = tstream_tls_params_client(state, |
187 | 0 | false, |
188 | 0 | NULL, |
189 | 0 | NULL, |
190 | 0 | NULL, |
191 | 0 | "NORMAL", |
192 | 0 | TLS_VERIFY_PEER_NO_CHECK, |
193 | 0 | NULL, |
194 | 0 | &state->tls_params); |
195 | 0 | if (tevent_req_nterror(req, status)) { |
196 | 0 | DBG_ERR("tstream_tls_params_client(NO_CHECK): %s\n", |
197 | 0 | nt_errstr(status)); |
198 | 0 | return; |
199 | 0 | } |
200 | | |
201 | 0 | if (state->proto == CLIENT_NETLOGON_PING_STARTTLS) { |
202 | 0 | subreq = tldap_extended_send(state, |
203 | 0 | state->ev, |
204 | 0 | state->tldap, |
205 | 0 | LDB_EXTENDED_START_TLS_OID, |
206 | 0 | NULL, |
207 | 0 | NULL, |
208 | 0 | 0, |
209 | 0 | NULL, |
210 | 0 | 0); |
211 | 0 | if (tevent_req_nomem(subreq, req)) { |
212 | 0 | return; |
213 | 0 | } |
214 | 0 | tevent_req_set_callback(subreq, |
215 | 0 | ldap_netlogon_starttls_done, |
216 | 0 | req); |
217 | 0 | return; |
218 | 0 | } |
219 | | |
220 | 0 | subreq = tldap_tls_connect_send(state, |
221 | 0 | state->ev, |
222 | 0 | state->tldap, |
223 | 0 | state->tls_params); |
224 | 0 | if (tevent_req_nomem(subreq, req)) { |
225 | 0 | return; |
226 | 0 | } |
227 | 0 | tevent_req_set_callback(subreq, ldap_netlogon_tls_set_up, req); |
228 | 0 | } |
229 | | |
230 | | static void ldap_netlogon_starttls_done(struct tevent_req *subreq) |
231 | 0 | { |
232 | 0 | struct tevent_req *req = tevent_req_callback_data(subreq, |
233 | 0 | struct tevent_req); |
234 | 0 | struct ldap_netlogon_state *state = tevent_req_data( |
235 | 0 | req, struct ldap_netlogon_state); |
236 | 0 | TLDAPRC rc; |
237 | |
|
238 | 0 | rc = tldap_extended_recv(subreq, NULL, NULL, NULL); |
239 | 0 | TALLOC_FREE(subreq); |
240 | 0 | if (!TLDAP_RC_IS_SUCCESS(rc)) { |
241 | 0 | tevent_req_nterror(req, NT_STATUS_LDAP(TLDAP_RC_V(rc))); |
242 | 0 | return; |
243 | 0 | } |
244 | | |
245 | 0 | subreq = tldap_tls_connect_send(state, |
246 | 0 | state->ev, |
247 | 0 | state->tldap, |
248 | 0 | state->tls_params); |
249 | 0 | if (tevent_req_nomem(subreq, req)) { |
250 | 0 | return; |
251 | 0 | } |
252 | 0 | tevent_req_set_callback(subreq, ldap_netlogon_tls_set_up, req); |
253 | 0 | } |
254 | | |
255 | | static void ldap_netlogon_tls_set_up(struct tevent_req *subreq) |
256 | 0 | { |
257 | 0 | struct tevent_req *req = tevent_req_callback_data(subreq, |
258 | 0 | struct tevent_req); |
259 | 0 | TLDAPRC rc; |
260 | |
|
261 | 0 | rc = tldap_tls_connect_recv(subreq); |
262 | 0 | TALLOC_FREE(subreq); |
263 | 0 | if (!TLDAP_RC_IS_SUCCESS(rc)) { |
264 | 0 | tevent_req_nterror(req, NT_STATUS_LDAP(TLDAP_RC_V(rc))); |
265 | 0 | return; |
266 | 0 | } |
267 | | |
268 | 0 | ldap_netlogon_search(req); |
269 | 0 | } |
270 | | |
271 | | static void ldap_netlogon_search(struct tevent_req *req) |
272 | 0 | { |
273 | 0 | struct ldap_netlogon_state *state = tevent_req_data( |
274 | 0 | req, struct ldap_netlogon_state); |
275 | 0 | static const char *attrs[] = {"netlogon"}; |
276 | 0 | struct tevent_req *subreq = NULL; |
277 | |
|
278 | 0 | subreq = tldap_search_all_send(state, |
279 | 0 | state->ev, |
280 | 0 | state->tldap, |
281 | 0 | "", |
282 | 0 | TLDAP_SCOPE_BASE, |
283 | 0 | state->filter, |
284 | 0 | attrs, |
285 | 0 | ARRAY_SIZE(attrs), |
286 | 0 | 0, |
287 | 0 | NULL, |
288 | 0 | 0, |
289 | 0 | NULL, |
290 | 0 | 0, |
291 | 0 | 0, |
292 | 0 | 0, |
293 | 0 | 0); |
294 | 0 | if (tevent_req_nomem(subreq, req)) { |
295 | 0 | return; |
296 | 0 | } |
297 | 0 | tevent_req_set_callback(subreq, ldap_netlogon_searched, req); |
298 | 0 | } |
299 | | |
300 | | static void ldap_netlogon_searched(struct tevent_req *subreq) |
301 | 0 | { |
302 | 0 | struct tevent_req *req = tevent_req_callback_data(subreq, |
303 | 0 | struct tevent_req); |
304 | 0 | struct ldap_netlogon_state *state = tevent_req_data( |
305 | 0 | req, struct ldap_netlogon_state); |
306 | 0 | struct tldap_message **msgs = NULL; |
307 | 0 | DATA_BLOB blob = {.data = NULL}; |
308 | 0 | NTSTATUS status; |
309 | 0 | TLDAPRC rc; |
310 | 0 | bool ok; |
311 | |
|
312 | 0 | rc = tldap_search_all_recv(subreq, state, &msgs, NULL); |
313 | 0 | TALLOC_FREE(subreq); |
314 | 0 | if (!TLDAP_RC_IS_SUCCESS(rc)) { |
315 | 0 | tevent_req_nterror(req, NT_STATUS_LDAP(TLDAP_RC_V(rc))); |
316 | 0 | return; |
317 | 0 | } |
318 | | |
319 | 0 | if (talloc_array_length(msgs) != 1) { |
320 | 0 | tevent_req_nterror(req, |
321 | 0 | NT_STATUS_LDAP(TLDAP_RC_V( |
322 | 0 | TLDAP_NO_RESULTS_RETURNED))); |
323 | 0 | return; |
324 | 0 | } |
325 | | |
326 | 0 | ok = tldap_get_single_valueblob(msgs[0], "netlogon", &blob); |
327 | 0 | if (!ok) { |
328 | 0 | tevent_req_nterror(req, |
329 | 0 | NT_STATUS_LDAP(TLDAP_RC_V( |
330 | 0 | TLDAP_NO_RESULTS_RETURNED))); |
331 | 0 | return; |
332 | 0 | } |
333 | | |
334 | 0 | state->response = talloc(state, struct netlogon_samlogon_response); |
335 | 0 | if (tevent_req_nomem(state->response, req)) { |
336 | 0 | return; |
337 | 0 | } |
338 | | |
339 | 0 | status = pull_netlogon_samlogon_response(&blob, |
340 | 0 | state->response, |
341 | 0 | state->response); |
342 | 0 | if (!NT_STATUS_IS_OK(status)) { |
343 | 0 | tevent_req_nterror(req, status); |
344 | 0 | return; |
345 | 0 | } |
346 | | |
347 | 0 | tevent_req_done(req); |
348 | 0 | } |
349 | | |
350 | | static NTSTATUS ldap_netlogon_recv( |
351 | | struct tevent_req *req, |
352 | | TALLOC_CTX *mem_ctx, |
353 | | struct netlogon_samlogon_response **response) |
354 | 0 | { |
355 | 0 | struct ldap_netlogon_state *state = tevent_req_data( |
356 | 0 | req, struct ldap_netlogon_state); |
357 | 0 | NTSTATUS status; |
358 | |
|
359 | 0 | if (tevent_req_is_nterror(req, &status)) { |
360 | 0 | return status; |
361 | 0 | } |
362 | 0 | *response = talloc_move(mem_ctx, &state->response); |
363 | 0 | tevent_req_received(req); |
364 | 0 | return NT_STATUS_OK; |
365 | 0 | } |
366 | | |
367 | | struct cldap_netlogon_ping_state { |
368 | | struct cldap_socket *sock; |
369 | | struct cldap_search search; |
370 | | struct netlogon_samlogon_response *response; |
371 | | }; |
372 | | |
373 | | static void cldap_netlogon_ping_done(struct tevent_req *subreq); |
374 | | |
375 | | static struct tevent_req *cldap_netlogon_ping_send( |
376 | | TALLOC_CTX *mem_ctx, |
377 | | struct tevent_context *ev, |
378 | | const struct tsocket_address *server, |
379 | | const char *filter) |
380 | 0 | { |
381 | 0 | struct tevent_req *req = NULL, *subreq = NULL; |
382 | 0 | struct cldap_netlogon_ping_state *state = NULL; |
383 | 0 | struct tsocket_address *server_389 = NULL; |
384 | 0 | static const char *const attr[] = {"NetLogon", NULL}; |
385 | 0 | int ret; |
386 | 0 | NTSTATUS status; |
387 | |
|
388 | 0 | req = tevent_req_create(mem_ctx, |
389 | 0 | &state, |
390 | 0 | struct cldap_netlogon_ping_state); |
391 | 0 | if (req == NULL) { |
392 | 0 | return NULL; |
393 | 0 | } |
394 | | |
395 | 0 | server_389 = tsocket_address_copy(server, state); |
396 | 0 | if (tevent_req_nomem(server_389, req)) { |
397 | 0 | return tevent_req_post(req, ev); |
398 | 0 | } |
399 | | |
400 | 0 | ret = tsocket_address_inet_set_port(server_389, 389); |
401 | 0 | if (ret != 0) { |
402 | 0 | tevent_req_nterror(req, map_nt_error_from_unix_common(errno)); |
403 | 0 | return tevent_req_post(req, ev); |
404 | 0 | } |
405 | | |
406 | 0 | status = cldap_socket_init(state, NULL, server_389, &state->sock); |
407 | 0 | if (tevent_req_nterror(req, status)) { |
408 | 0 | return tevent_req_post(req, ev); |
409 | 0 | } |
410 | | |
411 | 0 | state->search = (struct cldap_search){ |
412 | 0 | .in.filter = filter, |
413 | 0 | .in.attributes = attr, |
414 | 0 | .in.timeout = 2, |
415 | 0 | .in.retries = 2, |
416 | 0 | }; |
417 | |
|
418 | 0 | subreq = cldap_search_send(state, ev, state->sock, &state->search); |
419 | 0 | if (tevent_req_nomem(subreq, req)) { |
420 | 0 | return tevent_req_post(req, ev); |
421 | 0 | } |
422 | 0 | tevent_req_set_callback(subreq, cldap_netlogon_ping_done, req); |
423 | 0 | return req; |
424 | 0 | } |
425 | | |
426 | | static void cldap_netlogon_ping_done(struct tevent_req *subreq) |
427 | 0 | { |
428 | 0 | struct tevent_req *req = tevent_req_callback_data(subreq, |
429 | 0 | struct tevent_req); |
430 | 0 | struct cldap_netlogon_ping_state *state = tevent_req_data( |
431 | 0 | req, struct cldap_netlogon_ping_state); |
432 | 0 | struct ldap_SearchResEntry *resp = NULL; |
433 | 0 | NTSTATUS status; |
434 | |
|
435 | 0 | status = cldap_search_recv(subreq, state, &state->search); |
436 | 0 | TALLOC_FREE(subreq); |
437 | 0 | if (tevent_req_nterror(req, status)) { |
438 | 0 | return; |
439 | 0 | } |
440 | | |
441 | 0 | TALLOC_FREE(state->sock); |
442 | |
|
443 | 0 | resp = state->search.out.response; |
444 | |
|
445 | 0 | if (resp == NULL) { |
446 | 0 | tevent_req_nterror(req, NT_STATUS_NOT_FOUND); |
447 | 0 | return; |
448 | 0 | } |
449 | | |
450 | 0 | if (resp->num_attributes != 1 || |
451 | 0 | !strequal(resp->attributes[0].name, "netlogon") || |
452 | 0 | resp->attributes[0].num_values != 1 || |
453 | 0 | resp->attributes[0].values->length < 2) |
454 | 0 | { |
455 | 0 | tevent_req_nterror(req, NT_STATUS_UNEXPECTED_NETWORK_ERROR); |
456 | 0 | return; |
457 | 0 | } |
458 | | |
459 | 0 | state->response = talloc(state, struct netlogon_samlogon_response); |
460 | 0 | if (tevent_req_nomem(state->response, req)) { |
461 | 0 | return; |
462 | 0 | } |
463 | | |
464 | 0 | status = pull_netlogon_samlogon_response(resp->attributes[0].values, |
465 | 0 | state->response, |
466 | 0 | state->response); |
467 | 0 | if (tevent_req_nterror(req, status)) { |
468 | 0 | return; |
469 | 0 | } |
470 | 0 | tevent_req_done(req); |
471 | 0 | } |
472 | | |
473 | | static NTSTATUS cldap_netlogon_ping_recv( |
474 | | struct tevent_req *req, |
475 | | TALLOC_CTX *mem_ctx, |
476 | | struct netlogon_samlogon_response **response) |
477 | 0 | { |
478 | 0 | struct cldap_netlogon_ping_state *state = tevent_req_data( |
479 | 0 | req, struct cldap_netlogon_ping_state); |
480 | 0 | NTSTATUS status; |
481 | |
|
482 | 0 | if (tevent_req_is_nterror(req, &status)) { |
483 | 0 | return status; |
484 | 0 | } |
485 | 0 | *response = talloc_move(mem_ctx, &state->response); |
486 | 0 | tevent_req_received(req); |
487 | 0 | return NT_STATUS_OK; |
488 | 0 | } |
489 | | |
490 | | struct netlogon_ping_state { |
491 | | struct netlogon_samlogon_response *response; |
492 | | }; |
493 | | |
494 | | static void netlogon_ping_done_cldap(struct tevent_req *subreq); |
495 | | static void netlogon_ping_done_ldaps(struct tevent_req *subreq); |
496 | | |
497 | | static struct tevent_req *netlogon_ping_send( |
498 | | TALLOC_CTX *mem_ctx, |
499 | | struct tevent_context *ev, |
500 | | struct tsocket_address *server, |
501 | | enum client_netlogon_ping_protocol proto, |
502 | | const char *filter, |
503 | | struct timeval timeout) |
504 | 0 | { |
505 | 0 | struct tevent_req *req = NULL, *subreq = NULL; |
506 | 0 | struct netlogon_ping_state *state = NULL; |
507 | |
|
508 | 0 | req = tevent_req_create(mem_ctx, &state, struct netlogon_ping_state); |
509 | 0 | if (req == NULL) { |
510 | 0 | return NULL; |
511 | 0 | } |
512 | | |
513 | 0 | switch (proto) { |
514 | 0 | case CLIENT_NETLOGON_PING_CLDAP: |
515 | 0 | subreq = cldap_netlogon_ping_send(state, ev, server, filter); |
516 | 0 | if (tevent_req_nomem(subreq, req)) { |
517 | 0 | return tevent_req_post(req, ev); |
518 | 0 | } |
519 | 0 | tevent_req_set_callback(subreq, netlogon_ping_done_cldap, req); |
520 | 0 | break; |
521 | 0 | case CLIENT_NETLOGON_PING_LDAP: |
522 | 0 | case CLIENT_NETLOGON_PING_LDAPS: |
523 | 0 | case CLIENT_NETLOGON_PING_STARTTLS: |
524 | 0 | subreq = ldap_netlogon_send(state, ev, server, proto, filter); |
525 | 0 | if (tevent_req_nomem(subreq, req)) { |
526 | 0 | return tevent_req_post(req, ev); |
527 | 0 | } |
528 | 0 | tevent_req_set_callback(subreq, netlogon_ping_done_ldaps, req); |
529 | 0 | break; |
530 | 0 | default: |
531 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
532 | 0 | return tevent_req_post(req, ev); |
533 | 0 | break; |
534 | 0 | } |
535 | | |
536 | 0 | return req; |
537 | 0 | } |
538 | | |
539 | | static void netlogon_ping_done_cldap(struct tevent_req *subreq) |
540 | 0 | { |
541 | 0 | struct tevent_req *req = tevent_req_callback_data(subreq, |
542 | 0 | struct tevent_req); |
543 | 0 | struct netlogon_ping_state *state = tevent_req_data( |
544 | 0 | req, struct netlogon_ping_state); |
545 | 0 | NTSTATUS status; |
546 | |
|
547 | 0 | status = cldap_netlogon_ping_recv(subreq, state, &state->response); |
548 | 0 | if (tevent_req_nterror(req, status)) { |
549 | 0 | return; |
550 | 0 | } |
551 | 0 | tevent_req_done(req); |
552 | 0 | } |
553 | | |
554 | | static void netlogon_ping_done_ldaps(struct tevent_req *subreq) |
555 | 0 | { |
556 | 0 | struct tevent_req *req = tevent_req_callback_data(subreq, |
557 | 0 | struct tevent_req); |
558 | 0 | struct netlogon_ping_state *state = tevent_req_data( |
559 | 0 | req, struct netlogon_ping_state); |
560 | 0 | NTSTATUS status; |
561 | |
|
562 | 0 | status = ldap_netlogon_recv(subreq, state, &state->response); |
563 | 0 | TALLOC_FREE(subreq); |
564 | 0 | if (tevent_req_nterror(req, status)) { |
565 | 0 | return; |
566 | 0 | } |
567 | 0 | tevent_req_done(req); |
568 | 0 | } |
569 | | |
570 | | static NTSTATUS netlogon_ping_recv( |
571 | | struct tevent_req *req, |
572 | | TALLOC_CTX *mem_ctx, |
573 | | struct netlogon_samlogon_response **response) |
574 | 0 | { |
575 | 0 | struct netlogon_ping_state *state = tevent_req_data( |
576 | 0 | req, struct netlogon_ping_state); |
577 | 0 | NTSTATUS status; |
578 | |
|
579 | 0 | if (tevent_req_is_nterror(req, &status)) { |
580 | 0 | return status; |
581 | 0 | } |
582 | 0 | *response = talloc_move(mem_ctx, &state->response); |
583 | 0 | return NT_STATUS_OK; |
584 | 0 | } |
585 | | |
586 | | struct netlogon_pings_state { |
587 | | struct tevent_context *ev; |
588 | | |
589 | | struct tsocket_address **servers; |
590 | | size_t num_servers; |
591 | | size_t wanted_servers; |
592 | | struct timeval timeout; |
593 | | enum client_netlogon_ping_protocol proto; |
594 | | uint32_t required_flags; |
595 | | |
596 | | char *filter; |
597 | | size_t num_sent; |
598 | | size_t num_received; |
599 | | size_t num_good_received; |
600 | | struct tevent_req **reqs; |
601 | | struct netlogon_samlogon_response **responses; |
602 | | }; |
603 | | |
604 | | static void netlogon_pings_next(struct tevent_req *subreq); |
605 | | static void netlogon_pings_done(struct tevent_req *subreq); |
606 | | |
607 | | struct tevent_req *netlogon_pings_send(TALLOC_CTX *mem_ctx, |
608 | | struct tevent_context *ev, |
609 | | enum client_netlogon_ping_protocol proto, |
610 | | struct tsocket_address **servers, |
611 | | size_t num_servers, |
612 | | struct netlogon_ping_filter filter, |
613 | | size_t wanted_servers, |
614 | | struct timeval timeout) |
615 | 0 | { |
616 | 0 | struct tevent_req *req = NULL; |
617 | 0 | struct netlogon_pings_state *state = NULL; |
618 | 0 | char *filter_str = NULL; |
619 | 0 | size_t i; |
620 | |
|
621 | 0 | req = tevent_req_create(mem_ctx, &state, struct netlogon_pings_state); |
622 | 0 | if (req == NULL) { |
623 | 0 | return NULL; |
624 | 0 | } |
625 | 0 | state->ev = ev; |
626 | 0 | state->proto = proto; |
627 | 0 | state->servers = servers; |
628 | 0 | state->num_servers = num_servers; |
629 | 0 | state->wanted_servers = wanted_servers; |
630 | 0 | state->timeout = timeout; |
631 | 0 | state->required_flags = filter.required_flags; |
632 | |
|
633 | 0 | state->reqs = talloc_zero_array(state, |
634 | 0 | struct tevent_req *, |
635 | 0 | num_servers); |
636 | 0 | if (tevent_req_nomem(state->reqs, req)) { |
637 | 0 | return tevent_req_post(req, ev); |
638 | 0 | } |
639 | | |
640 | 0 | state->responses = talloc_zero_array( |
641 | 0 | state, struct netlogon_samlogon_response *, num_servers); |
642 | 0 | if (tevent_req_nomem(state->responses, req)) { |
643 | 0 | return tevent_req_post(req, ev); |
644 | 0 | } |
645 | | |
646 | 0 | filter_str = talloc_asprintf(state, |
647 | 0 | "(&(NtVer=%s)", |
648 | 0 | ldap_encode_ndr_uint32(state, |
649 | 0 | filter.ntversion)); |
650 | 0 | if (filter.domain != NULL) { |
651 | 0 | talloc_asprintf_addbuf(&filter_str, |
652 | 0 | "(DnsDomain=%s)", |
653 | 0 | filter.domain); |
654 | 0 | } |
655 | 0 | if (filter.acct_ctrl != -1) { |
656 | 0 | talloc_asprintf_addbuf( |
657 | 0 | &filter_str, |
658 | 0 | "(AAC=%s)", |
659 | 0 | ldap_encode_ndr_uint32(mem_ctx, filter.acct_ctrl)); |
660 | 0 | } |
661 | 0 | if (filter.domain_sid != NULL) { |
662 | 0 | talloc_asprintf_addbuf( |
663 | 0 | &filter_str, |
664 | 0 | "(domainSid=%s)", |
665 | 0 | ldap_encode_ndr_dom_sid(mem_ctx, filter.domain_sid)); |
666 | 0 | } |
667 | 0 | if (filter.domain_guid != NULL) { |
668 | 0 | talloc_asprintf_addbuf( |
669 | 0 | &filter_str, |
670 | 0 | "(DomainGuid=%s)", |
671 | 0 | ldap_encode_ndr_GUID(mem_ctx, filter.domain_guid)); |
672 | 0 | } |
673 | 0 | if (filter.hostname != NULL) { |
674 | 0 | talloc_asprintf_addbuf(&filter_str, |
675 | 0 | "(Host=%s)", |
676 | 0 | filter.hostname); |
677 | 0 | } |
678 | 0 | if (filter.user != NULL) { |
679 | 0 | talloc_asprintf_addbuf(&filter_str, "(User=%s)", filter.user); |
680 | 0 | } |
681 | 0 | talloc_asprintf_addbuf(&filter_str, ")"); |
682 | |
|
683 | 0 | if (tevent_req_nomem(filter_str, req)) { |
684 | 0 | return tevent_req_post(req, ev); |
685 | 0 | } |
686 | 0 | state->filter = filter_str; |
687 | |
|
688 | 0 | for (i = 0; i < wanted_servers; i++) { |
689 | 0 | state->reqs[i] = netlogon_ping_send(state->reqs, |
690 | 0 | state->ev, |
691 | 0 | state->servers[i], |
692 | 0 | state->proto, |
693 | 0 | state->filter, |
694 | 0 | state->timeout); |
695 | 0 | if (tevent_req_nomem(state->reqs[i], req)) { |
696 | 0 | return tevent_req_post(req, ev); |
697 | 0 | } |
698 | 0 | tevent_req_set_callback(state->reqs[i], |
699 | 0 | netlogon_pings_done, |
700 | 0 | req); |
701 | 0 | } |
702 | 0 | state->num_sent = wanted_servers; |
703 | 0 | if (state->num_sent < state->num_servers) { |
704 | | /* |
705 | | * After 100 milliseconds fire the next one |
706 | | */ |
707 | 0 | struct tevent_req *subreq = tevent_wakeup_send( |
708 | 0 | state, state->ev, timeval_current_ofs(0, 100000)); |
709 | 0 | if (tevent_req_nomem(subreq, req)) { |
710 | 0 | return tevent_req_post(req, ev); |
711 | 0 | } |
712 | 0 | tevent_req_set_callback(subreq, netlogon_pings_next, req); |
713 | 0 | } |
714 | | |
715 | 0 | return req; |
716 | 0 | } |
717 | | |
718 | | static void netlogon_pings_next(struct tevent_req *subreq) |
719 | 0 | { |
720 | 0 | struct tevent_req *req = tevent_req_callback_data(subreq, |
721 | 0 | struct tevent_req); |
722 | 0 | struct netlogon_pings_state *state = tevent_req_data( |
723 | 0 | req, struct netlogon_pings_state); |
724 | 0 | bool ret; |
725 | |
|
726 | 0 | ret = tevent_wakeup_recv(subreq); |
727 | 0 | TALLOC_FREE(subreq); |
728 | 0 | if (!ret) { |
729 | 0 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); |
730 | 0 | return; |
731 | 0 | } |
732 | | |
733 | 0 | subreq = netlogon_ping_send(state->reqs, |
734 | 0 | state->ev, |
735 | 0 | state->servers[state->num_sent], |
736 | 0 | state->proto, |
737 | 0 | state->filter, |
738 | 0 | state->timeout); |
739 | 0 | if (tevent_req_nomem(subreq, req)) { |
740 | 0 | return; |
741 | 0 | } |
742 | 0 | tevent_req_set_callback(subreq, netlogon_pings_done, req); |
743 | 0 | state->reqs[state->num_sent] = subreq; |
744 | 0 | state->num_sent += 1; |
745 | |
|
746 | 0 | if (state->num_sent < state->num_servers) { |
747 | | /* |
748 | | * After 100 milliseconds fire the next one |
749 | | */ |
750 | 0 | subreq = tevent_wakeup_send(state, |
751 | 0 | state->ev, |
752 | 0 | timeval_current_ofs(0, 100000)); |
753 | 0 | if (tevent_req_nomem(subreq, req)) { |
754 | 0 | return; |
755 | 0 | } |
756 | 0 | tevent_req_set_callback(subreq, netlogon_pings_next, req); |
757 | 0 | } |
758 | 0 | } |
759 | | |
760 | | static void netlogon_pings_done(struct tevent_req *subreq) |
761 | 0 | { |
762 | 0 | struct tevent_req *req = tevent_req_callback_data(subreq, |
763 | 0 | struct tevent_req); |
764 | 0 | struct netlogon_pings_state *state = tevent_req_data( |
765 | 0 | req, struct netlogon_pings_state); |
766 | 0 | struct netlogon_samlogon_response *response = NULL; |
767 | 0 | NTSTATUS status; |
768 | 0 | size_t i; |
769 | |
|
770 | 0 | for (i = 0; i < state->num_sent; i++) { |
771 | 0 | if (state->reqs[i] == subreq) { |
772 | 0 | break; |
773 | 0 | } |
774 | 0 | } |
775 | |
|
776 | 0 | if (i == state->num_sent) { |
777 | | /* |
778 | | * Got a response we did not fire... |
779 | | */ |
780 | 0 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); |
781 | 0 | return; |
782 | 0 | } |
783 | 0 | state->reqs[i] = NULL; |
784 | |
|
785 | 0 | status = netlogon_ping_recv(subreq, state, &response); |
786 | 0 | TALLOC_FREE(subreq); |
787 | 0 | state->num_received += 1; |
788 | |
|
789 | 0 | if (NT_STATUS_IS_OK(status)) { |
790 | 0 | enum netlogon_command cmd; |
791 | 0 | uint32_t ret_flags; |
792 | 0 | bool ok = true; |
793 | |
|
794 | 0 | switch (response->ntver) { |
795 | 0 | case NETLOGON_NT_VERSION_5EX: |
796 | 0 | ret_flags = response->data.nt5_ex.server_type; |
797 | 0 | cmd = response->data.nt5_ex.command; |
798 | 0 | ok &= !(cmd == LOGON_SAM_LOGON_PAUSE_RESPONSE || |
799 | 0 | cmd == LOGON_SAM_LOGON_PAUSE_RESPONSE_EX); |
800 | 0 | break; |
801 | 0 | case NETLOGON_NT_VERSION_5: |
802 | 0 | ret_flags = response->data.nt5.server_type; |
803 | 0 | cmd = response->data.nt5.command; |
804 | 0 | ok &= !(cmd == LOGON_SAM_LOGON_PAUSE_RESPONSE || |
805 | 0 | cmd == LOGON_SAM_LOGON_PAUSE_RESPONSE_EX); |
806 | 0 | break; |
807 | 0 | default: |
808 | 0 | ret_flags = 0; |
809 | 0 | break; |
810 | 0 | } |
811 | | |
812 | 0 | ok &= check_cldap_reply_required_flags(ret_flags, |
813 | 0 | state->required_flags); |
814 | 0 | if (ok) { |
815 | 0 | state->responses[i] = talloc_move(state->responses, |
816 | 0 | &response); |
817 | 0 | state->num_good_received += 1; |
818 | 0 | } |
819 | 0 | } |
820 | | |
821 | 0 | if (state->num_good_received >= state->wanted_servers) { |
822 | 0 | tevent_req_done(req); |
823 | 0 | return; |
824 | 0 | } |
825 | 0 | if (state->num_received < state->num_servers) { |
826 | | /* |
827 | | * Wait for more answers |
828 | | */ |
829 | 0 | return; |
830 | 0 | } |
831 | 0 | if (state->num_good_received == 1) { |
832 | | /* We require at least one DC */ |
833 | 0 | tevent_req_done(req); |
834 | 0 | return; |
835 | 0 | } |
836 | | /* |
837 | | * Everybody replied, but we did not get a single good |
838 | | * answers (see above) |
839 | | */ |
840 | 0 | tevent_req_nterror(req, NT_STATUS_NOT_FOUND); |
841 | 0 | return; |
842 | 0 | } |
843 | | |
844 | | NTSTATUS netlogon_pings_recv(struct tevent_req *req, |
845 | | TALLOC_CTX *mem_ctx, |
846 | | struct netlogon_samlogon_response ***responses) |
847 | 0 | { |
848 | 0 | struct netlogon_pings_state *state = tevent_req_data( |
849 | 0 | req, struct netlogon_pings_state); |
850 | 0 | NTSTATUS status; |
851 | |
|
852 | 0 | if (tevent_req_is_nterror(req, &status)) { |
853 | 0 | return status; |
854 | 0 | } |
855 | 0 | *responses = talloc_move(mem_ctx, &state->responses); |
856 | 0 | tevent_req_received(req); |
857 | 0 | return NT_STATUS_OK; |
858 | 0 | } |
859 | | |
860 | | NTSTATUS netlogon_pings(TALLOC_CTX *mem_ctx, |
861 | | enum client_netlogon_ping_protocol proto, |
862 | | struct tsocket_address **servers, |
863 | | int num_servers, |
864 | | struct netlogon_ping_filter filter, |
865 | | int wanted_servers, |
866 | | struct timeval timeout, |
867 | | struct netlogon_samlogon_response ***responses) |
868 | 0 | { |
869 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
870 | 0 | struct tevent_context *ev = NULL; |
871 | 0 | struct tevent_req *req = NULL; |
872 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
873 | |
|
874 | 0 | ev = samba_tevent_context_init(frame); |
875 | 0 | if (ev == NULL) { |
876 | 0 | goto fail; |
877 | 0 | } |
878 | 0 | req = netlogon_pings_send(frame, |
879 | 0 | ev, |
880 | 0 | proto, |
881 | 0 | servers, |
882 | 0 | num_servers, |
883 | 0 | filter, |
884 | 0 | wanted_servers, |
885 | 0 | timeout); |
886 | 0 | if (req == NULL) { |
887 | 0 | goto fail; |
888 | 0 | } |
889 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
890 | 0 | goto fail; |
891 | 0 | } |
892 | 0 | status = netlogon_pings_recv(req, mem_ctx, responses); |
893 | 0 | fail: |
894 | | TALLOC_FREE(frame); |
895 | 0 | return status; |
896 | 0 | } |