summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarming Sam <garming@catalyst.net.nz>2017-04-11 15:51:50 +1200
committerAndrew Bartlett <abartlet@samba.org>2017-05-30 08:06:07 +0200
commitfd29e28d5231a4e576979c0b116564b751be8831 (patch)
tree30b1dcba3607e5a502ab6335a5cebe665e04b979
parent8ae968193b7084af8bb0ccf7d624ff10e39d5715 (diff)
downloadsamba-fd29e28d5231a4e576979c0b116564b751be8831.tar.gz
netlogon: Implement SendToSam along with its winbind forwarding
This allows you to forward bad password count resets to 0. Currently, there is a missing access check for the RODC to ensure it only applies to cached users (msDS-Allowed-Password-Replication-Group). (further patches still need to address forcing a RWDC contact) Signed-off-by: Garming Sam <garming@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
-rw-r--r--libcli/auth/netlogon_creds_cli.c260
-rw-r--r--libcli/auth/netlogon_creds_cli.h11
-rw-r--r--librpc/idl/netlogon.idl39
-rw-r--r--librpc/idl/winbind.idl5
-rw-r--r--source3/rpc_server/netlogon/srv_netlog_nt.c6
-rw-r--r--source3/winbindd/winbindd_dual_srv.c25
-rw-r--r--source3/winbindd/winbindd_irpc.c23
-rw-r--r--source4/auth/ntlm/auth_sam.c56
-rw-r--r--source4/auth/ntlm/auth_winbind.c3
-rw-r--r--source4/auth/sam.c28
-rw-r--r--source4/kdc/hdb-samba4.c30
-rw-r--r--source4/rpc_server/netlogon/dcerpc_netlogon.c100
12 files changed, 571 insertions, 15 deletions
diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c
index ff30354d60e..fcab81491c6 100644
--- a/libcli/auth/netlogon_creds_cli.c
+++ b/libcli/auth/netlogon_creds_cli.c
@@ -31,6 +31,7 @@
#include "../libcli/auth/schannel.h"
#include "../librpc/gen_ndr/ndr_schannel.h"
#include "../librpc/gen_ndr/ndr_netlogon_c.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
#include "../librpc/gen_ndr/server_id.h"
#include "netlogon_creds_cli.h"
#include "source3/include/messages.h"
@@ -3415,3 +3416,262 @@ NTSTATUS netlogon_creds_cli_GetForestTrustInformation(
TALLOC_FREE(frame);
return status;
}
+
+struct netlogon_creds_cli_SendToSam_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+
+ char *srv_name_slash;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ DATA_BLOB opaque;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_SendToSam_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+static void netlogon_creds_cli_SendToSam_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_SendToSam_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ struct netr_SendToSamBase *message)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_SendToSam_state *state;
+ struct tevent_req *subreq;
+ enum ndr_err_code ndr_err;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_SendToSam_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+ context->server.computer);
+ if (tevent_req_nomem(state->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ndr_err = ndr_push_struct_blob(&state->opaque, mem_ctx, message,
+ (ndr_push_flags_fn_t)ndr_push_netr_SendToSamBase);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ dcerpc_binding_handle_auth_info(state->binding_handle,
+ &state->auth_type,
+ &state->auth_level);
+
+ subreq = netlogon_creds_cli_lock_send(state, state->ev,
+ state->context);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_SendToSam_locked,
+ req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_SendToSam_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_SendToSam_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_SendToSam_state);
+
+ if (state->creds == NULL) {
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ TALLOC_FREE(state->creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, &state->creds);
+}
+
+static void netlogon_creds_cli_SendToSam_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_SendToSam_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_SendToSam_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_SendToSam_state);
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+ switch (state->auth_level) {
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ } else {
+ uint32_t tmp = state->creds->negotiate_flags;
+
+ if (tmp & NETLOGON_NEG_AUTHENTICATED_RPC) {
+ /*
+ * if DCERPC_AUTH_TYPE_SCHANNEL is supported
+ * it should be used, which means
+ * we had a chance to verify no downgrade
+ * happened.
+ *
+ * This relies on netlogon_creds_cli_check*
+ * being called before, as first request after
+ * the DCERPC bind.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ }
+
+ /*
+ * we defer all callbacks in order to cleanup
+ * the database record.
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ state->tmp_creds = *state->creds;
+ netlogon_creds_client_authenticator(&state->tmp_creds,
+ &state->req_auth);
+ ZERO_STRUCT(state->rep_auth);
+
+ if (state->tmp_creds.negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(&state->tmp_creds,
+ state->opaque.data,
+ state->opaque.length);
+ } else {
+ netlogon_creds_arcfour_crypt(&state->tmp_creds,
+ state->opaque.data,
+ state->opaque.length);
+ }
+
+ subreq = dcerpc_netr_NetrLogonSendToSam_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->tmp_creds.computer_name,
+ &state->req_auth,
+ &state->rep_auth,
+ state->opaque.data,
+ state->opaque.length);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_SendToSam_done,
+ req);
+}
+
+static void netlogon_creds_cli_SendToSam_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_SendToSam_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_SendToSam_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ status = dcerpc_netr_NetrLogonSendToSam_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ return;
+ }
+
+ ok = netlogon_creds_client_check(&state->tmp_creds,
+ &state->rep_auth.cred);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ return;
+ }
+
+ *state->creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ &state->creds);
+
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ return;
+ }
+
+ /*
+ * Creds must be stored before we send back application errors
+ * e.g. NT_STATUS_NOT_IMPLEMENTED
+ */
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_SendToSam_cleanup(req, result);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_SendToSam(struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ struct netr_SendToSamBase *message)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_SendToSam_send(frame, ev, context, b, message);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ /* Ignore the result */
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/libcli/auth/netlogon_creds_cli.h b/libcli/auth/netlogon_creds_cli.h
index 949e03bd460..7c737dd920a 100644
--- a/libcli/auth/netlogon_creds_cli.h
+++ b/libcli/auth/netlogon_creds_cli.h
@@ -181,4 +181,15 @@ NTSTATUS netlogon_creds_cli_GetForestTrustInformation(
TALLOC_CTX *mem_ctx,
struct lsa_ForestTrustInformation **forest_trust_info);
+struct tevent_req *netlogon_creds_cli_SendToSam_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ struct netr_SendToSamBase *message);
+
+NTSTATUS netlogon_creds_cli_SendToSam(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ struct netr_SendToSamBase *message);
+
#endif /* NETLOGON_CREDS_CLI_H */
diff --git a/librpc/idl/netlogon.idl b/librpc/idl/netlogon.idl
index e4b499fd85e..4d1a0ef237f 100644
--- a/librpc/idl/netlogon.idl
+++ b/librpc/idl/netlogon.idl
@@ -1466,9 +1466,46 @@ interface netlogon
[out,ref] samr_Password *password
);
+ typedef [public] enum {
+ SendToSamUpdatePassword = 0,
+ SendToSamResetBadPasswordCount = 1,
+ SendToSamUpdatePasswordForward = 2,
+ SendToSamUpdateLastLogonTimestamp = 3,
+ SendToSamResetSmartCardPassword = 4
+ } netr_SendToSamType;
+
+ typedef struct {
+ GUID guid;
+ } netr_SendToSamResetBadPasswordCount;
+
+ typedef [nodiscriminant, public,switch_type(netr_SendToSamType)] union {
+ /* TODO Implement other SendToSam message types
+ * [case(SendToSamUpdatePassword)] netr_SendToSamUpdatePassword ...; */
+ [case(SendToSamResetBadPasswordCount)] netr_SendToSamResetBadPasswordCount reset_bad_password;
+ /*
+ * [case(SendToSamUpdatePasswordForward)] netrSendToSamUpdatePasswordForward ...;
+ * [case(SendToSamUpdateLastLogonTimestamp)] netrSendToSamUpdateLastLogonTimestamp ...;
+ * [case(SendToSamResetSmartCardPassword)] netrSendToSamResetSmartCardPassword ...;
+ */
+ [default];
+ } netr_SendToSamMessage;
+
+ typedef [public] struct {
+ netr_SendToSamType message_type;
+ uint32 message_size;
+ [switch_is(message_type), subcontext(0), subcontext_size(message_size)] netr_SendToSamMessage message;
+ } netr_SendToSamBase;
+
/****************/
/* Function 0x20 */
- [todo] WERROR netr_NETRLOGONSENDTOSAM();
+ NTSTATUS netr_NetrLogonSendToSam(
+ [in,unique] [string,charset(UTF16)] uint16 *server_name,
+ [in] [string,charset(UTF16)] uint16 *computer_name,
+ [in,ref] netr_Authenticator *credential,
+ [out,ref] netr_Authenticator *return_authenticator,
+ [in,ref] [size_is(buffer_len)] uint8 *opaque_buffer,
+ [in] uint32 buffer_len
+ );
/****************/
/* Function 0x21 */
diff --git a/librpc/idl/winbind.idl b/librpc/idl/winbind.idl
index 05db6b96b81..737d66abe70 100644
--- a/librpc/idl/winbind.idl
+++ b/librpc/idl/winbind.idl
@@ -211,4 +211,9 @@ interface winbind
[in] uint32 flags,
[out,ref] lsa_ForestTrustInformation **forest_trust_info
);
+
+ NTSTATUS winbind_SendToSam(
+ [in] netr_SendToSamBase message
+ );
+
}
diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c
index c36e6590b5c..83e68417c76 100644
--- a/source3/rpc_server/netlogon/srv_netlog_nt.c
+++ b/source3/rpc_server/netlogon/srv_netlog_nt.c
@@ -2277,11 +2277,11 @@ NTSTATUS _netr_ServerPasswordGet(struct pipes_struct *p,
/****************************************************************
****************************************************************/
-WERROR _netr_NETRLOGONSENDTOSAM(struct pipes_struct *p,
- struct netr_NETRLOGONSENDTOSAM *r)
+NTSTATUS _netr_NetrLogonSendToSam(struct pipes_struct *p,
+ struct netr_NetrLogonSendToSam *r)
{
p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
- return WERR_NOT_SUPPORTED;
+ return NT_STATUS_NOT_IMPLEMENTED;
}
/****************************************************************
diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
index 8007c7d38d1..0b17b78bd6b 100644
--- a/source3/winbindd/winbindd_dual_srv.c
+++ b/source3/winbindd/winbindd_dual_srv.c
@@ -1701,3 +1701,28 @@ done:
TALLOC_FREE(frame);
return WERR_OK;
}
+
+NTSTATUS _winbind_SendToSam(struct pipes_struct *p, struct winbind_SendToSam *r)
+{
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+ struct rpc_pipe_client *netlogon_pipe;
+
+ DEBUG(5, ("_winbind_SendToSam received\n"));
+ domain = wb_child_domain();
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ status = cm_connect_netlogon(domain, &netlogon_pipe);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ return status;
+ }
+
+ status = netlogon_creds_cli_SendToSam(domain->conn.netlogon_creds,
+ netlogon_pipe->binding_handle,
+ &r->in.message);
+
+ return status;
+}
diff --git a/source3/winbindd/winbindd_irpc.c b/source3/winbindd/winbindd_irpc.c
index c87707a4c0f..c6c786c8be1 100644
--- a/source3/winbindd/winbindd_irpc.c
+++ b/source3/winbindd/winbindd_irpc.c
@@ -255,6 +255,24 @@ static NTSTATUS wb_irpc_GetForestTrustInformation(struct irpc_message *msg,
domain, 45 /* timeout */);
}
+static NTSTATUS wb_irpc_SendToSam(struct irpc_message *msg,
+ struct winbind_SendToSam *req)
+{
+ /* TODO make sure that it is RWDC */
+ struct winbindd_domain *domain = find_our_domain();
+ if (domain == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ DEBUG(5, ("wb_irpc_SendToSam called\n"));
+
+ return wb_irpc_forward_rpc_call(msg, msg,
+ winbind_event_context(),
+ req, NDR_WINBIND_SENDTOSAM,
+ "winbind_SendToSam",
+ domain, IRPC_CALL_TIMEOUT);
+}
+
NTSTATUS wb_irpc_register(void)
{
NTSTATUS status;
@@ -281,6 +299,11 @@ NTSTATUS wb_irpc_register(void)
if (!NT_STATUS_IS_OK(status)) {
return status;
}
+ status = IRPC_REGISTER(winbind_imessaging_context(), winbind, WINBIND_SENDTOSAM,
+ wb_irpc_SendToSam, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
return NT_STATUS_OK;
}
diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c
index fed5dd3f308..ee4a054c8c7 100644
--- a/source4/auth/ntlm/auth_sam.c
+++ b/source4/auth/ntlm/auth_sam.c
@@ -32,6 +32,7 @@
#include "dsdb/common/util.h"
#include "param/param.h"
#include "librpc/gen_ndr/ndr_irpc_c.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
#include "lib/messaging/irpc.h"
#include "libcli/auth/libcli_auth.h"
#include "libds/common/roles.h"
@@ -103,6 +104,49 @@ static NTSTATUS authsam_password_ok(struct auth4_context *auth_context,
return NT_STATUS_OK;
}
+static void auth_sam_trigger_zero_password(TALLOC_CTX *mem_ctx,
+ struct imessaging_context *msg_ctx,
+ struct tevent_context *event_ctx,
+ struct netr_SendToSamBase *send_to_sam)
+{
+ struct dcerpc_binding_handle *irpc_handle;
+ struct winbind_SendToSam r;
+ struct tevent_req *req;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return;
+ }
+
+ irpc_handle = irpc_binding_handle_by_name(tmp_ctx, msg_ctx,
+ "winbind_server",
+ &ndr_table_winbind);
+ if (irpc_handle == NULL) {
+ DEBUG(1,(__location__ ": Unable to get binding handle for winbind\n"));
+ TALLOC_FREE(tmp_ctx);
+ return;
+ }
+
+ r.in.message = *send_to_sam;
+
+ /*
+ * This seem to rely on the current IRPC implementation,
+ * which delivers the message in the _send function.
+ *
+ * TODO: we need a ONE_WAY IRPC handle and register
+ * a callback and wait for it to be triggered!
+ */
+ req = dcerpc_winbind_SendToSam_r_send(tmp_ctx,
+ event_ctx,
+ irpc_handle,
+ &r);
+
+ /* we aren't interested in a reply */
+ talloc_free(req);
+ TALLOC_FREE(tmp_ctx);
+
+}
/*
send a message to the drepl server telling it to initiate a
@@ -482,6 +526,7 @@ static NTSTATUS authsam_authenticate(struct auth4_context *auth_context,
NTSTATUS nt_status;
bool interactive = (user_info->password_state == AUTH_PASSWORD_HASH);
uint32_t acct_flags = samdb_result_acct_flags(msg, NULL);
+ struct netr_SendToSamBase *send_to_sam = NULL;
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
if (!tmp_ctx) {
return NT_STATUS_NO_MEMORY;
@@ -533,7 +578,16 @@ static NTSTATUS authsam_authenticate(struct auth4_context *auth_context,
nt_status = authsam_logon_success_accounting(auth_context->sam_ctx,
msg, domain_dn,
- interactive);
+ interactive,
+ &send_to_sam);
+
+ if (send_to_sam != NULL) {
+ auth_sam_trigger_zero_password(tmp_ctx,
+ auth_context->msg_ctx,
+ auth_context->event_ctx,
+ send_to_sam);
+ }
+
if (!NT_STATUS_IS_OK(nt_status)) {
TALLOC_FREE(tmp_ctx);
return nt_status;
diff --git a/source4/auth/ntlm/auth_winbind.c b/source4/auth/ntlm/auth_winbind.c
index 41819dca605..84f278ddd85 100644
--- a/source4/auth/ntlm/auth_winbind.c
+++ b/source4/auth/ntlm/auth_winbind.c
@@ -225,7 +225,8 @@ static NTSTATUS winbind_check_password(struct auth_method_context *ctx,
if (NT_STATUS_IS_OK(status)) {
authsam_logon_success_accounting(ctx->auth_ctx->sam_ctx, msg,
domain_dn,
- user_info->flags & USER_INFO_INTERACTIVE_LOGON);
+ user_info->flags & USER_INFO_INTERACTIVE_LOGON,
+ NULL);
}
}
}
diff --git a/source4/auth/sam.c b/source4/auth/sam.c
index 9119ef54f43..49e34baf145 100644
--- a/source4/auth/sam.c
+++ b/source4/auth/sam.c
@@ -30,6 +30,7 @@
#include "dsdb/common/util.h"
#include "libcli/ldap/ldap_ndr.h"
#include "param/param.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
#define KRBTGT_ATTRS \
/* required for the krb5 kdc */ \
@@ -74,9 +75,14 @@ const char *user_attrs[] = {
*/
"lockoutTime",
+ /*
+ * Needed for SendToSAM requests
+ */
+ "objectGUID",
+
/* check 'allowed workstations' */
"userWorkstations",
-
+
/* required for user_info_dc, not access control: */
"displayName",
"scriptPath",
@@ -871,11 +877,13 @@ NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx
NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
const struct ldb_message *msg,
struct ldb_dn *domain_dn,
- bool interactive_or_kerberos)
+ bool interactive_or_kerberos,
+ struct netr_SendToSamBase **send_to_sam)
{
int ret;
NTSTATUS status;
int badPwdCount;
+ int dbBadPwdCount;
int64_t lockoutTime;
struct ldb_message *msg_mod;
TALLOC_CTX *mem_ctx;
@@ -890,8 +898,9 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
}
lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
+ dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
if (interactive_or_kerberos) {
- badPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
+ badPwdCount = dbBadPwdCount;
} else {
badPwdCount = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx,
domain_dn, msg);
@@ -971,13 +980,24 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
}
if (!am_rodc) {
- /* TODO Perform the (async) SendToSAM calls for MS-SAMS */
status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn,
lastLogonTimestamp, now);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
+ } else {
+ /* Perform the (async) SendToSAM calls for MS-SAMS */
+ if (dbBadPwdCount != 0 && send_to_sam != NULL) {
+ struct netr_SendToSamBase *base_msg;
+ struct GUID guid = samdb_result_guid(msg, "objectGUID");
+ base_msg = talloc_zero(msg, struct netr_SendToSamBase);
+
+ base_msg->message_type = SendToSamResetBadPasswordCount;
+ base_msg->message_size = 16;
+ base_msg->message.reset_bad_password.guid = guid;
+ *send_to_sam = base_msg;
+ }
}
if (msg_mod->num_elements > 0) {
diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c
index 81ac60e38ba..552eeeedf6b 100644
--- a/source4/kdc/hdb-samba4.c
+++ b/source4/kdc/hdb-samba4.c
@@ -296,6 +296,28 @@ hdb_samba4_check_s4u2self(krb5_context context, HDB *db,
return ret;
}
+static void reset_bad_password_netlogon(TALLOC_CTX *mem_ctx,
+ struct samba_kdc_db_context *kdc_db_ctx,
+ struct netr_SendToSamBase *send_to_sam)
+{
+ struct dcerpc_binding_handle *irpc_handle;
+ struct winbind_SendToSam req;
+
+ irpc_handle = irpc_binding_handle_by_name(mem_ctx, kdc_db_ctx->msg_ctx,
+ "winbind_server",
+ &ndr_table_winbind);
+
+ if (irpc_handle == NULL) {
+ DEBUG(0, ("No winbind_server running!\n"));
+ return;
+ }
+
+ req.in.message = *send_to_sam;
+
+ dcerpc_winbind_SendToSam_r_send(mem_ctx, kdc_db_ctx->ev_ctx,
+ irpc_handle, &req);
+}
+
static void send_bad_password_netlogon(TALLOC_CTX *mem_ctx,
struct samba_kdc_db_context *kdc_db_ctx,
struct auth_usersupplied_info *user_info)
@@ -396,8 +418,10 @@ static krb5_error_code hdb_samba4_auth_status(krb5_context context, HDB *db,
switch (hdb_auth_status) {
case HDB_AUTHZ_SUCCESS:
{
+ TALLOC_CTX *frame = talloc_stackframe();
struct samba_kdc_entry *p = talloc_get_type(entry->ctx,
struct samba_kdc_entry);
+ struct netr_SendToSamBase *send_to_sam = NULL;
/*
* TODO: We could log the AS-REQ authorization success here as
@@ -405,7 +429,11 @@ static krb5_error_code hdb_samba4_auth_status(krb5_context context, HDB *db,
* in the PAC here or re-calculate it.
*/
authsam_logon_success_accounting(kdc_db_ctx->samdb, p->msg,
- domain_dn, true);
+ domain_dn, true, &send_to_sam);
+ if (kdc_db_ctx->rodc && send_to_sam != NULL) {
+ reset_bad_password_netlogon(frame, kdc_db_ctx, send_to_sam);
+ }
+ talloc_free(frame);
break;
}
case HDB_AUTH_INVALID_SIGNATURE:
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index 420dcee3a47..e41cd17da12 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -2254,12 +2254,104 @@ static NTSTATUS dcesrv_netr_ServerPasswordGet(struct dcesrv_call_state *dce_call
/*
- netr_NETRLOGONSENDTOSAM
+ netr_NetrLogonSendToSam
*/
-static WERROR dcesrv_netr_NETRLOGONSENDTOSAM(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
- struct netr_NETRLOGONSENDTOSAM *r)
+static NTSTATUS dcesrv_netr_NetrLogonSendToSam(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NetrLogonSendToSam *r)
{
- DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+ struct netlogon_creds_CredentialState *creds;
+ struct ldb_context *sam_ctx;
+ NTSTATUS nt_status;
+ DATA_BLOB decrypted_blob;
+ enum ndr_err_code ndr_err;
+ struct netr_SendToSamBase base_msg = { 0 };
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ switch (creds->secure_channel_type) {
+ case SEC_CHAN_BDC:
+ case SEC_CHAN_RODC:
+ break;
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_DNS_DOMAIN:
+ case SEC_CHAN_DOMAIN:
+ case SEC_CHAN_NULL:
+ return NT_STATUS_INVALID_PARAMETER;
+ default:
+ DEBUG(1, ("Client asked for an invalid secure channel type: %d\n",
+ creds->secure_channel_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* Buffer is meant to be 16-bit aligned */
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_decrypt(creds, r->in.opaque_buffer, r->in.buffer_len);
+ } else {
+ netlogon_creds_arcfour_crypt(creds, r->in.opaque_buffer, r->in.buffer_len);
+ }
+
+ decrypted_blob.data = r->in.opaque_buffer;
+ decrypted_blob.length = r->in.buffer_len;
+
+ ndr_err = ndr_pull_struct_blob(&decrypted_blob, mem_ctx, &base_msg,
+ (ndr_pull_flags_fn_t)ndr_pull_netr_SendToSamBase);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ /* We only partially implement SendToSam */
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* Now 'send' to SAM */
+ switch (base_msg.message_type) {
+ case SendToSamResetBadPasswordCount:
+ {
+ struct ldb_message *msg = ldb_msg_new(mem_ctx);
+ struct ldb_dn *dn = NULL;
+ int ret = 0;
+
+
+ ret = dsdb_find_dn_by_guid(sam_ctx,
+ mem_ctx,
+ &base_msg.message.reset_bad_password.guid,
+ 0,
+ &dn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ msg->dn = dn;
+
+ ret = samdb_msg_add_int(sam_ctx, mem_ctx, msg, "badPwdCount", 0);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ret = dsdb_replace(sam_ctx, msg, 0);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ break;
+ }
+ default:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_OK;
}