diff options
author | Garming Sam <garming@catalyst.net.nz> | 2017-04-11 15:51:50 +1200 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2017-05-30 08:06:07 +0200 |
commit | fd29e28d5231a4e576979c0b116564b751be8831 (patch) | |
tree | 30b1dcba3607e5a502ab6335a5cebe665e04b979 | |
parent | 8ae968193b7084af8bb0ccf7d624ff10e39d5715 (diff) | |
download | samba-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.c | 260 | ||||
-rw-r--r-- | libcli/auth/netlogon_creds_cli.h | 11 | ||||
-rw-r--r-- | librpc/idl/netlogon.idl | 39 | ||||
-rw-r--r-- | librpc/idl/winbind.idl | 5 | ||||
-rw-r--r-- | source3/rpc_server/netlogon/srv_netlog_nt.c | 6 | ||||
-rw-r--r-- | source3/winbindd/winbindd_dual_srv.c | 25 | ||||
-rw-r--r-- | source3/winbindd/winbindd_irpc.c | 23 | ||||
-rw-r--r-- | source4/auth/ntlm/auth_sam.c | 56 | ||||
-rw-r--r-- | source4/auth/ntlm/auth_winbind.c | 3 | ||||
-rw-r--r-- | source4/auth/sam.c | 28 | ||||
-rw-r--r-- | source4/kdc/hdb-samba4.c | 30 | ||||
-rw-r--r-- | source4/rpc_server/netlogon/dcerpc_netlogon.c | 100 |
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; } |