mirror of
https://github.com/samba-team/samba.git
synced 2025-01-03 01:18:10 +03:00
eb8767a076
netlogon_ping.c depends on it but itself has fewer dependencies than cldap.c, so we can use it in more places Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org>
884 lines
22 KiB
C
884 lines
22 KiB
C
/*
|
|
* Samba Unix/Linux SMB client library
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "replace.h"
|
|
#include <tevent.h>
|
|
#include "netlogon_ping.h"
|
|
#include "libcli/netlogon/netlogon_proto.h"
|
|
#include "libcli/ldap/ldap_ndr.h"
|
|
#include "libcli/ldap/ldap_message.h"
|
|
#include "libcli/cldap/cldap.h"
|
|
#include "source3/include/tldap.h"
|
|
#include "source3/include/tldap_util.h"
|
|
#include "source3/lib/tldap_tls_connect.h"
|
|
#include "lib/util/tevent_unix.h"
|
|
#include "lib/util/tevent_ntstatus.h"
|
|
#include "source4/lib/tls/tls.h"
|
|
#include "source3/libads/cldap.h"
|
|
#include "librpc/gen_ndr/netlogon.h"
|
|
|
|
#define RETURN_ON_FALSE(x) \
|
|
if (!(x)) \
|
|
return false;
|
|
|
|
bool check_cldap_reply_required_flags(uint32_t ret_flags, uint32_t req_flags)
|
|
{
|
|
if (req_flags == 0) {
|
|
return true;
|
|
}
|
|
|
|
if (req_flags & DS_PDC_REQUIRED)
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_PDC);
|
|
|
|
if (req_flags & DS_GC_SERVER_REQUIRED)
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_GC);
|
|
|
|
if (req_flags & DS_ONLY_LDAP_NEEDED)
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_LDAP);
|
|
|
|
if ((req_flags & DS_DIRECTORY_SERVICE_REQUIRED) ||
|
|
(req_flags & DS_DIRECTORY_SERVICE_PREFERRED))
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS);
|
|
|
|
if (req_flags & DS_KDC_REQUIRED)
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_KDC);
|
|
|
|
if (req_flags & DS_TIMESERV_REQUIRED)
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_TIMESERV);
|
|
|
|
if (req_flags & DS_WEB_SERVICE_REQUIRED)
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_ADS_WEB_SERVICE);
|
|
|
|
if (req_flags & DS_WRITABLE_REQUIRED)
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_WRITABLE);
|
|
|
|
if (req_flags & DS_DIRECTORY_SERVICE_6_REQUIRED)
|
|
RETURN_ON_FALSE(ret_flags &
|
|
(NBT_SERVER_SELECT_SECRET_DOMAIN_6 |
|
|
NBT_SERVER_FULL_SECRET_DOMAIN_6));
|
|
|
|
if (req_flags & DS_DIRECTORY_SERVICE_8_REQUIRED)
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS_8);
|
|
|
|
if (req_flags & DS_DIRECTORY_SERVICE_9_REQUIRED)
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS_9);
|
|
|
|
if (req_flags & DS_DIRECTORY_SERVICE_10_REQUIRED)
|
|
RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS_10);
|
|
|
|
return true;
|
|
}
|
|
|
|
struct ldap_netlogon_state {
|
|
struct tevent_context *ev;
|
|
struct tsocket_address *local;
|
|
struct tsocket_address *remote;
|
|
enum client_netlogon_ping_protocol proto;
|
|
const char *filter;
|
|
|
|
struct tstream_context *plain;
|
|
struct tldap_context *tldap;
|
|
struct tstream_tls_params *tls_params;
|
|
|
|
struct netlogon_samlogon_response *response;
|
|
};
|
|
|
|
static void ldap_netlogon_connected(struct tevent_req *subreq);
|
|
static void ldap_netlogon_starttls_done(struct tevent_req *subreq);
|
|
static void ldap_netlogon_tls_set_up(struct tevent_req *subreq);
|
|
static void ldap_netlogon_search(struct tevent_req *req);
|
|
static void ldap_netlogon_searched(struct tevent_req *subreq);
|
|
|
|
static struct tevent_req *ldap_netlogon_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
const struct tsocket_address *server,
|
|
enum client_netlogon_ping_protocol proto,
|
|
const char *filter)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct ldap_netlogon_state *state = NULL;
|
|
uint16_t port;
|
|
int ret;
|
|
|
|
req = tevent_req_create(mem_ctx, &state, struct ldap_netlogon_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
state->filter = filter;
|
|
state->proto = proto;
|
|
|
|
state->remote = tsocket_address_copy(server, state);
|
|
if (tevent_req_nomem(state->remote, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
port = (proto == CLIENT_NETLOGON_PING_LDAPS) ? 636 : 389;
|
|
|
|
ret = tsocket_address_inet_set_port(state->remote, port);
|
|
if (ret != 0) {
|
|
tevent_req_nterror(req, map_nt_error_from_unix_common(errno));
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
ret = tsocket_address_inet_from_strings(
|
|
state, "ip", NULL, 0, &state->local);
|
|
if (ret != 0) {
|
|
tevent_req_nterror(req, map_nt_error_from_unix_common(errno));
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
subreq = tstream_inet_tcp_connect_send(state,
|
|
state->ev,
|
|
state->local,
|
|
state->remote);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, ldap_netlogon_connected, req);
|
|
|
|
return req;
|
|
}
|
|
|
|
static void ldap_netlogon_connected(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(subreq,
|
|
struct tevent_req);
|
|
struct ldap_netlogon_state *state = tevent_req_data(
|
|
req, struct ldap_netlogon_state);
|
|
int ret, err;
|
|
NTSTATUS status;
|
|
|
|
ret = tstream_inet_tcp_connect_recv(
|
|
subreq, &err, state, &state->plain, NULL);
|
|
TALLOC_FREE(subreq);
|
|
if (ret == -1) {
|
|
tevent_req_nterror(req, map_nt_error_from_unix_common(err));
|
|
return;
|
|
}
|
|
|
|
state->tldap = tldap_context_create_from_plain_stream(
|
|
state, &state->plain);
|
|
if (tevent_req_nomem(state->tldap, req)) {
|
|
return;
|
|
}
|
|
|
|
if (state->proto == CLIENT_NETLOGON_PING_LDAP) {
|
|
ldap_netlogon_search(req);
|
|
return;
|
|
}
|
|
|
|
status = tstream_tls_params_client(state,
|
|
false,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
"NORMAL",
|
|
TLS_VERIFY_PEER_NO_CHECK,
|
|
NULL,
|
|
&state->tls_params);
|
|
if (tevent_req_nterror(req, status)) {
|
|
DBG_ERR("tstream_tls_params_client(NO_CHECK): %s\n",
|
|
nt_errstr(status));
|
|
return;
|
|
}
|
|
|
|
if (state->proto == CLIENT_NETLOGON_PING_STARTTLS) {
|
|
subreq = tldap_extended_send(state,
|
|
state->ev,
|
|
state->tldap,
|
|
LDB_EXTENDED_START_TLS_OID,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq,
|
|
ldap_netlogon_starttls_done,
|
|
req);
|
|
return;
|
|
}
|
|
|
|
subreq = tldap_tls_connect_send(state,
|
|
state->ev,
|
|
state->tldap,
|
|
state->tls_params);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, ldap_netlogon_tls_set_up, req);
|
|
}
|
|
|
|
static void ldap_netlogon_starttls_done(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(subreq,
|
|
struct tevent_req);
|
|
struct ldap_netlogon_state *state = tevent_req_data(
|
|
req, struct ldap_netlogon_state);
|
|
TLDAPRC rc;
|
|
|
|
rc = tldap_extended_recv(subreq, NULL, NULL, NULL);
|
|
TALLOC_FREE(subreq);
|
|
if (!TLDAP_RC_IS_SUCCESS(rc)) {
|
|
tevent_req_nterror(req, NT_STATUS_LDAP(TLDAP_RC_V(rc)));
|
|
return;
|
|
}
|
|
|
|
subreq = tldap_tls_connect_send(state,
|
|
state->ev,
|
|
state->tldap,
|
|
state->tls_params);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, ldap_netlogon_tls_set_up, req);
|
|
}
|
|
|
|
static void ldap_netlogon_tls_set_up(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(subreq,
|
|
struct tevent_req);
|
|
TLDAPRC rc;
|
|
|
|
rc = tldap_tls_connect_recv(subreq);
|
|
TALLOC_FREE(subreq);
|
|
if (!TLDAP_RC_IS_SUCCESS(rc)) {
|
|
tevent_req_nterror(req, NT_STATUS_LDAP(TLDAP_RC_V(rc)));
|
|
return;
|
|
}
|
|
|
|
ldap_netlogon_search(req);
|
|
}
|
|
|
|
static void ldap_netlogon_search(struct tevent_req *req)
|
|
{
|
|
struct ldap_netlogon_state *state = tevent_req_data(
|
|
req, struct ldap_netlogon_state);
|
|
static const char *attrs[] = {"netlogon"};
|
|
struct tevent_req *subreq = NULL;
|
|
|
|
subreq = tldap_search_all_send(state,
|
|
state->ev,
|
|
state->tldap,
|
|
"",
|
|
TLDAP_SCOPE_BASE,
|
|
state->filter,
|
|
attrs,
|
|
ARRAY_SIZE(attrs),
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
0);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, ldap_netlogon_searched, req);
|
|
}
|
|
|
|
static void ldap_netlogon_searched(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(subreq,
|
|
struct tevent_req);
|
|
struct ldap_netlogon_state *state = tevent_req_data(
|
|
req, struct ldap_netlogon_state);
|
|
struct tldap_message **msgs = NULL;
|
|
DATA_BLOB blob = {.data = NULL};
|
|
NTSTATUS status;
|
|
TLDAPRC rc;
|
|
bool ok;
|
|
|
|
rc = tldap_search_all_recv(subreq, state, &msgs, NULL);
|
|
TALLOC_FREE(subreq);
|
|
if (!TLDAP_RC_IS_SUCCESS(rc)) {
|
|
tevent_req_nterror(req, NT_STATUS_LDAP(TLDAP_RC_V(rc)));
|
|
return;
|
|
}
|
|
|
|
if (talloc_array_length(msgs) != 1) {
|
|
tevent_req_nterror(req,
|
|
NT_STATUS_LDAP(TLDAP_RC_V(
|
|
TLDAP_NO_RESULTS_RETURNED)));
|
|
return;
|
|
}
|
|
|
|
ok = tldap_get_single_valueblob(msgs[0], "netlogon", &blob);
|
|
if (!ok) {
|
|
tevent_req_nterror(req,
|
|
NT_STATUS_LDAP(TLDAP_RC_V(
|
|
TLDAP_NO_RESULTS_RETURNED)));
|
|
return;
|
|
}
|
|
|
|
state->response = talloc(state, struct netlogon_samlogon_response);
|
|
if (tevent_req_nomem(state->response, req)) {
|
|
return;
|
|
}
|
|
|
|
status = pull_netlogon_samlogon_response(&blob,
|
|
state->response,
|
|
state->response);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
tevent_req_nterror(req, status);
|
|
return;
|
|
}
|
|
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
static NTSTATUS ldap_netlogon_recv(
|
|
struct tevent_req *req,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct netlogon_samlogon_response **response)
|
|
{
|
|
struct ldap_netlogon_state *state = tevent_req_data(
|
|
req, struct ldap_netlogon_state);
|
|
NTSTATUS status;
|
|
|
|
if (tevent_req_is_nterror(req, &status)) {
|
|
return status;
|
|
}
|
|
*response = talloc_move(mem_ctx, &state->response);
|
|
tevent_req_received(req);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
struct cldap_netlogon_ping_state {
|
|
struct cldap_socket *sock;
|
|
struct cldap_search search;
|
|
struct netlogon_samlogon_response *response;
|
|
};
|
|
|
|
static void cldap_netlogon_ping_done(struct tevent_req *subreq);
|
|
|
|
static struct tevent_req *cldap_netlogon_ping_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
const struct tsocket_address *server,
|
|
const char *filter)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cldap_netlogon_ping_state *state = NULL;
|
|
struct tsocket_address *server_389 = NULL;
|
|
static const char *const attr[] = {"NetLogon", NULL};
|
|
int ret;
|
|
NTSTATUS status;
|
|
|
|
req = tevent_req_create(mem_ctx,
|
|
&state,
|
|
struct cldap_netlogon_ping_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
server_389 = tsocket_address_copy(server, state);
|
|
if (tevent_req_nomem(server_389, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
ret = tsocket_address_inet_set_port(server_389, 389);
|
|
if (ret != 0) {
|
|
tevent_req_nterror(req, map_nt_error_from_unix_common(errno));
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
status = cldap_socket_init(state, NULL, server_389, &state->sock);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
state->search = (struct cldap_search){
|
|
.in.filter = filter,
|
|
.in.attributes = attr,
|
|
.in.timeout = 2,
|
|
.in.retries = 2,
|
|
};
|
|
|
|
subreq = cldap_search_send(state, ev, state->sock, &state->search);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cldap_netlogon_ping_done, req);
|
|
return req;
|
|
}
|
|
|
|
static void cldap_netlogon_ping_done(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(subreq,
|
|
struct tevent_req);
|
|
struct cldap_netlogon_ping_state *state = tevent_req_data(
|
|
req, struct cldap_netlogon_ping_state);
|
|
struct ldap_SearchResEntry *resp = NULL;
|
|
NTSTATUS status;
|
|
|
|
status = cldap_search_recv(subreq, state, &state->search);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
TALLOC_FREE(state->sock);
|
|
|
|
resp = state->search.out.response;
|
|
|
|
if (resp == NULL) {
|
|
tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
if (resp->num_attributes != 1 ||
|
|
!strequal(resp->attributes[0].name, "netlogon") ||
|
|
resp->attributes[0].num_values != 1 ||
|
|
resp->attributes[0].values->length < 2)
|
|
{
|
|
tevent_req_nterror(req, NT_STATUS_UNEXPECTED_NETWORK_ERROR);
|
|
return;
|
|
}
|
|
|
|
state->response = talloc(state, struct netlogon_samlogon_response);
|
|
if (tevent_req_nomem(state->response, req)) {
|
|
return;
|
|
}
|
|
|
|
status = pull_netlogon_samlogon_response(resp->attributes[0].values,
|
|
state->response,
|
|
state->response);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
static NTSTATUS cldap_netlogon_ping_recv(
|
|
struct tevent_req *req,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct netlogon_samlogon_response **response)
|
|
{
|
|
struct cldap_netlogon_ping_state *state = tevent_req_data(
|
|
req, struct cldap_netlogon_ping_state);
|
|
NTSTATUS status;
|
|
|
|
if (tevent_req_is_nterror(req, &status)) {
|
|
return status;
|
|
}
|
|
*response = talloc_move(mem_ctx, &state->response);
|
|
tevent_req_received(req);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
struct netlogon_ping_state {
|
|
struct netlogon_samlogon_response *response;
|
|
};
|
|
|
|
static void netlogon_ping_done_cldap(struct tevent_req *subreq);
|
|
static void netlogon_ping_done_ldaps(struct tevent_req *subreq);
|
|
|
|
static struct tevent_req *netlogon_ping_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tsocket_address *server,
|
|
enum client_netlogon_ping_protocol proto,
|
|
const char *filter,
|
|
struct timeval timeout)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct netlogon_ping_state *state = NULL;
|
|
|
|
req = tevent_req_create(mem_ctx, &state, struct netlogon_ping_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
switch (proto) {
|
|
case CLIENT_NETLOGON_PING_CLDAP:
|
|
subreq = cldap_netlogon_ping_send(state, ev, server, filter);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, netlogon_ping_done_cldap, req);
|
|
break;
|
|
case CLIENT_NETLOGON_PING_LDAP:
|
|
case CLIENT_NETLOGON_PING_LDAPS:
|
|
case CLIENT_NETLOGON_PING_STARTTLS:
|
|
subreq = ldap_netlogon_send(state, ev, server, proto, filter);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, netlogon_ping_done_ldaps, req);
|
|
break;
|
|
default:
|
|
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
|
return tevent_req_post(req, ev);
|
|
break;
|
|
}
|
|
|
|
return req;
|
|
}
|
|
|
|
static void netlogon_ping_done_cldap(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(subreq,
|
|
struct tevent_req);
|
|
struct netlogon_ping_state *state = tevent_req_data(
|
|
req, struct netlogon_ping_state);
|
|
NTSTATUS status;
|
|
|
|
status = cldap_netlogon_ping_recv(subreq, state, &state->response);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
static void netlogon_ping_done_ldaps(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(subreq,
|
|
struct tevent_req);
|
|
struct netlogon_ping_state *state = tevent_req_data(
|
|
req, struct netlogon_ping_state);
|
|
NTSTATUS status;
|
|
|
|
status = ldap_netlogon_recv(subreq, state, &state->response);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
static NTSTATUS netlogon_ping_recv(
|
|
struct tevent_req *req,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct netlogon_samlogon_response **response)
|
|
{
|
|
struct netlogon_ping_state *state = tevent_req_data(
|
|
req, struct netlogon_ping_state);
|
|
NTSTATUS status;
|
|
|
|
if (tevent_req_is_nterror(req, &status)) {
|
|
return status;
|
|
}
|
|
*response = talloc_move(mem_ctx, &state->response);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
struct netlogon_pings_state {
|
|
struct tevent_context *ev;
|
|
|
|
struct tsocket_address **servers;
|
|
size_t num_servers;
|
|
size_t min_servers;
|
|
struct timeval timeout;
|
|
enum client_netlogon_ping_protocol proto;
|
|
uint32_t required_flags;
|
|
|
|
char *filter;
|
|
size_t num_sent;
|
|
size_t num_received;
|
|
size_t num_good_received;
|
|
struct tevent_req **reqs;
|
|
struct netlogon_samlogon_response **responses;
|
|
};
|
|
|
|
static void netlogon_pings_next(struct tevent_req *subreq);
|
|
static void netlogon_pings_done(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *netlogon_pings_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
enum client_netlogon_ping_protocol proto,
|
|
struct tsocket_address **servers,
|
|
size_t num_servers,
|
|
struct netlogon_ping_filter filter,
|
|
size_t min_servers,
|
|
struct timeval timeout)
|
|
{
|
|
struct tevent_req *req = NULL;
|
|
struct netlogon_pings_state *state = NULL;
|
|
char *filter_str = NULL;
|
|
size_t i;
|
|
|
|
req = tevent_req_create(mem_ctx, &state, struct netlogon_pings_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
state->proto = proto;
|
|
state->servers = servers;
|
|
state->num_servers = num_servers;
|
|
state->min_servers = min_servers;
|
|
state->timeout = timeout;
|
|
state->required_flags = filter.required_flags;
|
|
|
|
state->reqs = talloc_zero_array(state,
|
|
struct tevent_req *,
|
|
num_servers);
|
|
if (tevent_req_nomem(state->reqs, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
state->responses = talloc_zero_array(
|
|
state, struct netlogon_samlogon_response *, num_servers);
|
|
if (tevent_req_nomem(state->responses, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
filter_str = talloc_asprintf(state,
|
|
"(&(NtVer=%s)",
|
|
ldap_encode_ndr_uint32(state,
|
|
filter.ntversion));
|
|
if (filter.domain != NULL) {
|
|
talloc_asprintf_addbuf(&filter_str,
|
|
"(DnsDomain=%s)",
|
|
filter.domain);
|
|
}
|
|
if (filter.acct_ctrl != -1) {
|
|
talloc_asprintf_addbuf(
|
|
&filter_str,
|
|
"(AAC=%s)",
|
|
ldap_encode_ndr_uint32(mem_ctx, filter.acct_ctrl));
|
|
}
|
|
if (filter.domain_sid != NULL) {
|
|
talloc_asprintf_addbuf(
|
|
&filter_str,
|
|
"(domainSid=%s)",
|
|
ldap_encode_ndr_dom_sid(mem_ctx, filter.domain_sid));
|
|
}
|
|
if (filter.domain_guid != NULL) {
|
|
talloc_asprintf_addbuf(
|
|
&filter_str,
|
|
"(DomainGuid=%s)",
|
|
ldap_encode_ndr_GUID(mem_ctx, filter.domain_guid));
|
|
}
|
|
if (filter.hostname != NULL) {
|
|
talloc_asprintf_addbuf(&filter_str,
|
|
"(Host=%s)",
|
|
filter.hostname);
|
|
}
|
|
if (filter.user != NULL) {
|
|
talloc_asprintf_addbuf(&filter_str, "(User=%s)", filter.user);
|
|
}
|
|
talloc_asprintf_addbuf(&filter_str, ")");
|
|
|
|
if (tevent_req_nomem(filter_str, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
state->filter = filter_str;
|
|
|
|
for (i = 0; i < min_servers; i++) {
|
|
state->reqs[i] = netlogon_ping_send(state->reqs,
|
|
state->ev,
|
|
state->servers[i],
|
|
state->proto,
|
|
state->filter,
|
|
state->timeout);
|
|
if (tevent_req_nomem(state->reqs[i], req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(state->reqs[i],
|
|
netlogon_pings_done,
|
|
req);
|
|
}
|
|
state->num_sent = min_servers;
|
|
if (state->num_sent < state->num_servers) {
|
|
/*
|
|
* After 100 milliseconds fire the next one
|
|
*/
|
|
struct tevent_req *subreq = tevent_wakeup_send(
|
|
state, state->ev, timeval_current_ofs(0, 100000));
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, netlogon_pings_next, req);
|
|
}
|
|
|
|
return req;
|
|
}
|
|
|
|
static void netlogon_pings_next(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(subreq,
|
|
struct tevent_req);
|
|
struct netlogon_pings_state *state = tevent_req_data(
|
|
req, struct netlogon_pings_state);
|
|
bool ret;
|
|
|
|
ret = tevent_wakeup_recv(subreq);
|
|
TALLOC_FREE(subreq);
|
|
if (!ret) {
|
|
tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
|
|
return;
|
|
}
|
|
|
|
subreq = netlogon_ping_send(state->reqs,
|
|
state->ev,
|
|
state->servers[state->num_sent],
|
|
state->proto,
|
|
state->filter,
|
|
state->timeout);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, netlogon_pings_done, req);
|
|
state->reqs[state->num_sent] = subreq;
|
|
state->num_sent += 1;
|
|
|
|
if (state->num_sent < state->num_servers) {
|
|
/*
|
|
* After 100 milliseconds fire the next one
|
|
*/
|
|
subreq = tevent_wakeup_send(state,
|
|
state->ev,
|
|
timeval_current_ofs(0, 100000));
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, netlogon_pings_next, req);
|
|
}
|
|
}
|
|
|
|
static void netlogon_pings_done(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(subreq,
|
|
struct tevent_req);
|
|
struct netlogon_pings_state *state = tevent_req_data(
|
|
req, struct netlogon_pings_state);
|
|
struct netlogon_samlogon_response *response = NULL;
|
|
NTSTATUS status;
|
|
size_t i;
|
|
|
|
for (i = 0; i < state->num_sent; i++) {
|
|
if (state->reqs[i] == subreq) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == state->num_sent) {
|
|
/*
|
|
* Got a response we did not fire...
|
|
*/
|
|
tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
|
|
return;
|
|
}
|
|
state->reqs[i] = NULL;
|
|
|
|
status = netlogon_ping_recv(subreq, state, &response);
|
|
TALLOC_FREE(subreq);
|
|
state->num_received += 1;
|
|
|
|
if (NT_STATUS_IS_OK(status)) {
|
|
uint32_t ret_flags;
|
|
bool ok;
|
|
|
|
switch (response->ntver) {
|
|
case NETLOGON_NT_VERSION_5EX:
|
|
ret_flags = response->data.nt5_ex.server_type;
|
|
break;
|
|
case NETLOGON_NT_VERSION_5:
|
|
ret_flags = response->data.nt5.server_type;
|
|
break;
|
|
default:
|
|
ret_flags = 0;
|
|
break;
|
|
}
|
|
|
|
ok = check_cldap_reply_required_flags(ret_flags,
|
|
state->required_flags);
|
|
if (ok) {
|
|
state->responses[i] = talloc_move(state->responses,
|
|
&response);
|
|
state->num_good_received += 1;
|
|
}
|
|
}
|
|
|
|
if (state->num_good_received >= state->min_servers) {
|
|
tevent_req_done(req);
|
|
return;
|
|
}
|
|
if (state->num_received == state->num_servers) {
|
|
/*
|
|
* Everybody replied, but we did not get enough good
|
|
* answers (see above)
|
|
*/
|
|
tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
|
|
return;
|
|
}
|
|
/*
|
|
* Wait for more answers
|
|
*/
|
|
}
|
|
|
|
NTSTATUS netlogon_pings_recv(struct tevent_req *req,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct netlogon_samlogon_response ***responses)
|
|
{
|
|
struct netlogon_pings_state *state = tevent_req_data(
|
|
req, struct netlogon_pings_state);
|
|
NTSTATUS status;
|
|
|
|
if (tevent_req_is_nterror(req, &status)) {
|
|
return status;
|
|
}
|
|
*responses = talloc_move(mem_ctx, &state->responses);
|
|
tevent_req_received(req);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
NTSTATUS netlogon_pings(TALLOC_CTX *mem_ctx,
|
|
enum client_netlogon_ping_protocol proto,
|
|
struct tsocket_address **servers,
|
|
int num_servers,
|
|
struct netlogon_ping_filter filter,
|
|
int min_servers,
|
|
struct timeval timeout,
|
|
struct netlogon_samlogon_response ***responses)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct tevent_context *ev = NULL;
|
|
struct tevent_req *req = NULL;
|
|
NTSTATUS status = NT_STATUS_NO_MEMORY;
|
|
|
|
ev = samba_tevent_context_init(frame);
|
|
if (ev == NULL) {
|
|
goto fail;
|
|
}
|
|
req = netlogon_pings_send(frame,
|
|
ev,
|
|
proto,
|
|
servers,
|
|
num_servers,
|
|
filter,
|
|
min_servers,
|
|
timeout);
|
|
if (req == NULL) {
|
|
goto fail;
|
|
}
|
|
if (!tevent_req_poll_ntstatus(req, ev, &status)) {
|
|
goto fail;
|
|
}
|
|
status = netlogon_pings_recv(req, mem_ctx, responses);
|
|
fail:
|
|
TALLOC_FREE(frame);
|
|
return status;
|
|
}
|