*/
#include "includes.h"
+#include <tevent.h>
#include "auth/auth.h"
#include "libcli/composite/composite.h"
#include "libcli/auth/libcli_auth.h"
struct dcerpc_pipe *pipe;
struct dcerpc_pipe *pipe2;
struct dcerpc_binding *binding;
+ bool dcerpc_schannel_auto;
struct cli_credentials *credentials;
struct netlogon_creds_CredentialState *creds;
- uint32_t negotiate_flags;
+ uint32_t local_negotiate_flags;
+ uint32_t remote_negotiate_flags;
struct netr_Credential credentials1;
struct netr_Credential credentials2;
struct netr_Credential credentials3;
static void continue_secondary_connection(struct composite_context *ctx);
static void continue_bind_auth_none(struct composite_context *ctx);
-static void continue_srv_challenge(struct rpc_request *req);
-static void continue_srv_auth2(struct rpc_request *req);
+static void continue_srv_challenge(struct tevent_req *subreq);
+static void continue_srv_auth2(struct tevent_req *subreq);
+static void continue_get_capabilities(struct tevent_req *subreq);
/*
{
struct composite_context *c;
struct schannel_key_state *s;
- struct rpc_request *srv_challenge_req;
+ struct tevent_req *subreq;
c = talloc_get_type(ctx->async.private_data, struct composite_context);
s = talloc_get_type(c->private_data, struct schannel_key_state);
/*
request a netlogon challenge - a rpc request over opened secondary pipe
*/
- srv_challenge_req = dcerpc_netr_ServerReqChallenge_send(s->pipe2, c, &s->r);
- if (composite_nomem(srv_challenge_req, c)) return;
+ subreq = dcerpc_netr_ServerReqChallenge_r_send(s, c->event_ctx,
+ s->pipe2->binding_handle,
+ &s->r);
+ if (composite_nomem(subreq, c)) return;
- composite_continue_rpc(c, srv_challenge_req, continue_srv_challenge, c);
+ tevent_req_set_callback(subreq, continue_srv_challenge, c);
}
Stage 5 of schannel_key: Receive a challenge and perform authentication
on the netlogon pipe
*/
-static void continue_srv_challenge(struct rpc_request *req)
+static void continue_srv_challenge(struct tevent_req *subreq)
{
struct composite_context *c;
struct schannel_key_state *s;
- struct rpc_request *srv_auth2_req;
- c = talloc_get_type(req->async.private_data, struct composite_context);
+ c = tevent_req_callback_data(subreq, struct composite_context);
s = talloc_get_type(c->private_data, struct schannel_key_state);
/* receive rpc request result - netlogon challenge */
- c->status = dcerpc_netr_ServerReqChallenge_recv(req);
+ c->status = dcerpc_netr_ServerReqChallenge_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
if (!composite_is_ok(c)) return;
/* prepare credentials for auth2 request */
s->a.in.secure_channel_type =
cli_credentials_get_secure_channel_type(s->credentials);
s->a.in.computer_name = cli_credentials_get_workstation(s->credentials);
- s->a.in.negotiate_flags = &s->negotiate_flags;
+ s->a.in.negotiate_flags = &s->local_negotiate_flags;
s->a.in.credentials = &s->credentials3;
- s->a.out.negotiate_flags = &s->negotiate_flags;
+ s->a.out.negotiate_flags = &s->remote_negotiate_flags;
s->a.out.return_credentials = &s->credentials3;
s->creds = netlogon_creds_client_init(s,
s->a.in.account_name,
s->a.in.computer_name,
+ s->a.in.secure_channel_type,
&s->credentials1, &s->credentials2,
- s->mach_pwd, &s->credentials3, s->negotiate_flags);
+ s->mach_pwd, &s->credentials3,
+ s->local_negotiate_flags);
if (composite_nomem(s->creds, c)) {
return;
}
/*
authenticate on the netlogon pipe - a rpc request over secondary pipe
*/
- srv_auth2_req = dcerpc_netr_ServerAuthenticate2_send(s->pipe2, c, &s->a);
- if (composite_nomem(srv_auth2_req, c)) return;
+ subreq = dcerpc_netr_ServerAuthenticate2_r_send(s, c->event_ctx,
+ s->pipe2->binding_handle,
+ &s->a);
+ if (composite_nomem(subreq, c)) return;
- composite_continue_rpc(c, srv_auth2_req, continue_srv_auth2, c);
+ tevent_req_set_callback(subreq, continue_srv_auth2, c);
}
Stage 6 of schannel_key: Receive authentication request result and verify
received credentials
*/
-static void continue_srv_auth2(struct rpc_request *req)
+static void continue_srv_auth2(struct tevent_req *subreq)
{
struct composite_context *c;
struct schannel_key_state *s;
- c = talloc_get_type(req->async.private_data, struct composite_context);
+ c = tevent_req_callback_data(subreq, struct composite_context);
s = talloc_get_type(c->private_data, struct schannel_key_state);
/* receive rpc request result - auth2 credentials */
- c->status = dcerpc_netr_ServerAuthenticate2_recv(req);
+ c->status = dcerpc_netr_ServerAuthenticate2_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
if (!composite_is_ok(c)) return;
+ if (!NT_STATUS_EQUAL(s->a.out.result, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_IS_OK(s->a.out.result)) {
+ composite_error(c, s->a.out.result);
+ return;
+ }
+
+ /*
+ * Strong keys could be unsupported (NT4) or disables. So retry with the
+ * flags returned by the server. - asn
+ */
+ if (NT_STATUS_EQUAL(s->a.out.result, NT_STATUS_ACCESS_DENIED)) {
+ uint32_t lf = s->local_negotiate_flags;
+ const char *ln = NULL;
+ uint32_t rf = s->remote_negotiate_flags;
+ const char *rn = NULL;
+
+ if (!s->dcerpc_schannel_auto) {
+ composite_error(c, s->a.out.result);
+ return;
+ }
+ s->dcerpc_schannel_auto = false;
+
+ if (lf & NETLOGON_NEG_SUPPORTS_AES) {
+ ln = "aes";
+ if (rf & NETLOGON_NEG_SUPPORTS_AES) {
+ composite_error(c, s->a.out.result);
+ return;
+ }
+ } else if (lf & NETLOGON_NEG_STRONG_KEYS) {
+ ln = "strong";
+ if (rf & NETLOGON_NEG_STRONG_KEYS) {
+ composite_error(c, s->a.out.result);
+ return;
+ }
+ } else {
+ ln = "des";
+ }
+
+ if (rf & NETLOGON_NEG_SUPPORTS_AES) {
+ rn = "aes";
+ } else if (rf & NETLOGON_NEG_STRONG_KEYS) {
+ rn = "strong";
+ } else {
+ rn = "des";
+ }
+
+ DEBUG(3, ("Server doesn't support %s keys, downgrade to %s"
+ "and retry! local[0x%08X] remote[0x%08X]\n",
+ ln, rn, lf, rf));
+
+ s->local_negotiate_flags = s->remote_negotiate_flags;
+
+ generate_random_buffer(s->credentials1.data,
+ sizeof(s->credentials1.data));
+
+ subreq = dcerpc_netr_ServerReqChallenge_r_send(s,
+ c->event_ctx,
+ s->pipe2->binding_handle,
+ &s->r);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_srv_challenge, c);
+ return;
+ }
+
+ s->creds->negotiate_flags = s->remote_negotiate_flags;
+
/* verify credentials */
if (!netlogon_creds_client_check(s->creds, s->a.out.return_credentials)) {
composite_error(c, NT_STATUS_UNSUCCESSFUL);
composite_done(c);
}
-
/*
Initiate establishing a schannel key using netlogon challenge
on a secondary pipe
struct composite_context *c;
struct schannel_key_state *s;
struct composite_context *epm_map_req;
+ enum netr_SchannelType schannel_type = cli_credentials_get_secure_channel_type(credentials);
/* composite context allocation and setup */
c = composite_create(mem_ctx, p->conn->event_ctx);
/* store parameters in the state structure */
s->pipe = p;
s->credentials = credentials;
+ s->local_negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
/* allocate credentials */
- /* type of authentication depends on schannel type */
if (s->pipe->conn->flags & DCERPC_SCHANNEL_128) {
- s->negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
- } else {
- s->negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ s->local_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ }
+ if (s->pipe->conn->flags & DCERPC_SCHANNEL_AES) {
+ s->local_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ s->local_negotiate_flags |= NETLOGON_NEG_SUPPORTS_AES;
+ }
+ if (s->pipe->conn->flags & DCERPC_SCHANNEL_AUTO) {
+ s->local_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ s->local_negotiate_flags |= NETLOGON_NEG_SUPPORTS_AES;
+ s->dcerpc_schannel_auto = true;
+ }
+
+ /* type of authentication depends on schannel type */
+ if (schannel_type == SEC_CHAN_RODC) {
+ s->local_negotiate_flags |= NETLOGON_NEG_RODC_PASSTHROUGH;
}
/* allocate binding structure */
- s->binding = talloc(c, struct dcerpc_binding);
+ s->binding = talloc_zero(c, struct dcerpc_binding);
if (composite_nomem(s->binding, c)) return c;
*s->binding = *s->pipe->binding;
const struct ndr_interface_table *table;
struct loadparm_context *lp_ctx;
uint8_t auth_level;
+ struct netlogon_creds_CredentialState *creds_state;
+ struct netlogon_creds_CredentialState save_creds_state;
+ struct netr_Authenticator auth;
+ struct netr_Authenticator return_auth;
+ union netr_Capabilities capabilities;
+ struct netr_LogonGetCapabilities c;
};
struct composite_context);
struct auth_schannel_state *s = talloc_get_type(c->private_data,
struct auth_schannel_state);
+ NTSTATUS status;
/* receive schannel key */
- c->status = dcerpc_schannel_key_recv(ctx);
+ status = c->status = dcerpc_schannel_key_recv(ctx);
if (!composite_is_ok(c)) {
- DEBUG(1, ("Failed to setup credentials: %s\n", nt_errstr(c->status)));
+ DEBUG(1, ("Failed to setup credentials: %s\n", nt_errstr(status)));
return;
}
/* send bind auth request with received creds */
auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, s->credentials,
- lp_gensec_settings(c, s->lp_ctx),
+ lpcfg_gensec_settings(c, s->lp_ctx),
DCERPC_AUTH_TYPE_SCHANNEL, s->auth_level,
NULL);
if (composite_nomem(auth_req, c)) return;
{
struct composite_context *c = talloc_get_type(ctx->async.private_data,
struct composite_context);
+ struct auth_schannel_state *s = talloc_get_type(c->private_data,
+ struct auth_schannel_state);
+ struct tevent_req *subreq;
c->status = dcerpc_bind_auth_recv(ctx);
if (!composite_is_ok(c)) return;
+ /* if we have a AES encrypted connection, verify the capabilities */
+ if (ndr_syntax_id_equal(&s->table->syntax_id,
+ &ndr_table_netlogon.syntax_id)) {
+ ZERO_STRUCT(s->return_auth);
+
+ s->creds_state = cli_credentials_get_netlogon_creds(s->credentials);
+ if (composite_nomem(s->creds_state, c)) return;
+
+ s->save_creds_state = *s->creds_state;
+ netlogon_creds_client_authenticator(&s->save_creds_state, &s->auth);
+
+ s->c.in.server_name = talloc_asprintf(c,
+ "\\\\%s",
+ dcerpc_server_name(s->pipe));
+ if (composite_nomem(s->c.in.server_name, c)) return;
+ s->c.in.computer_name = cli_credentials_get_workstation(s->credentials);
+ s->c.in.credential = &s->auth;
+ s->c.in.return_authenticator = &s->return_auth;
+ s->c.in.query_level = 1;
+
+ s->c.out.capabilities = &s->capabilities;
+ s->c.out.return_authenticator = &s->return_auth;
+
+ DEBUG(5, ("We established a AES connection, verifying logon "
+ "capabilities\n"));
+
+ subreq = dcerpc_netr_LogonGetCapabilities_r_send(s,
+ c->event_ctx,
+ s->pipe->binding_handle,
+ &s->c);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_get_capabilities, c);
+ return;
+ }
+
+ composite_done(c);
+}
+
+/*
+ Stage 4 of auth_schannel: Get the Logon Capablities and verify them.
+*/
+static void continue_get_capabilities(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct auth_schannel_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct auth_schannel_state);
+
+ /* receive rpc request result */
+ c->status = dcerpc_netr_LogonGetCapabilities_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ if (s->creds_state->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ } else {
+ /* This is probably NT */
+ composite_done(c);
+ return;
+ }
+ } else if (!composite_is_ok(c)) {
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(s->c.out.result, NT_STATUS_NOT_IMPLEMENTED)) {
+ if (s->creds_state->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ /* This means AES isn't supported. */
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /* This is probably an old Samba version */
+ composite_done(c);
+ return;
+ }
+
+ /* verify credentials */
+ if (!netlogon_creds_client_check(&s->save_creds_state,
+ &s->c.out.return_authenticator->cred)) {
+ composite_error(c, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+
+ *s->creds_state = s->save_creds_state;
+
+ if (!NT_STATUS_IS_OK(s->c.out.result)) {
+ composite_error(c, s->c.out.result);
+ return;
+ }
+
+ /* compare capabilities */
+ if (s->creds_state->negotiate_flags != s->capabilities.server_capabilities) {
+ DEBUG(2, ("The client capabilities don't match the server "
+ "capabilities: local[0x%08X] remote[0x%08X]\n",
+ s->creds_state->negotiate_flags,
+ s->capabilities.server_capabilities));
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /* TODO: Add downgrade dectection. */
+
composite_done(c);
}