netlogon: Implement SendToSam along with its winbind forwarding
authorGarming Sam <garming@catalyst.net.nz>
Tue, 11 Apr 2017 03:51:50 +0000 (15:51 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 30 May 2017 06:06:07 +0000 (08:06 +0200)
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>
12 files changed:
libcli/auth/netlogon_creds_cli.c
libcli/auth/netlogon_creds_cli.h
librpc/idl/netlogon.idl
librpc/idl/winbind.idl
source3/rpc_server/netlogon/srv_netlog_nt.c
source3/winbindd/winbindd_dual_srv.c
source3/winbindd/winbindd_irpc.c
source4/auth/ntlm/auth_sam.c
source4/auth/ntlm/auth_winbind.c
source4/auth/sam.c
source4/kdc/hdb-samba4.c
source4/rpc_server/netlogon/dcerpc_netlogon.c

index ff30354d60e32149ca399500f7a6e6f6bcbe4bfa..fcab81491c60978d5682baf7545aa09fe50fec18 100644 (file)
@@ -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;
+}
index 949e03bd46096c643c8d42c1f65d77b1586b3cc2..7c737dd920a5744fe7dddff391b351c0604d4068 100644 (file)
@@ -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 */
index e4b499fd85efbe32c5484be93974c8020d06f50a..4d1a0ef237f6b3f1625fda0e0047eb784910ef64 100644 (file)
@@ -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 */
index 05db6b96b81c8ced9afd56311832282fbb501b17..737d66abe701f757ba934358a61a56b23b051c77 100644 (file)
@@ -211,4 +211,9 @@ interface winbind
                [in] uint32 flags,
                [out,ref] lsa_ForestTrustInformation **forest_trust_info
                );
+
+       NTSTATUS winbind_SendToSam(
+               [in] netr_SendToSamBase message
+               );
+
 }
index c36e6590b5c4934255d66f94e54c0bb9cd7fccc0..83e68417c767df6514c49893499bcedd3eb787d4 100644 (file)
@@ -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;
 }
 
 /****************************************************************
index 8007c7d38d1673e8f653766faa67c05e3fd6c3cc..0b17b78bd6ba3978f6d9c1e85a2912d11def2c34 100644 (file)
@@ -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;
+}
index c87707a4c0f11dc57062ec3dd19b6e8081676141..c6c786c8be162fd31b300658ac496c269ff65249 100644 (file)
@@ -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;
 }
index fed5dd3f3086e694b4b1fefdc954fcabacab3c6a..ee4a054c8c7fb342e95246eb35bce26cae1de2df 100644 (file)
@@ -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;
index 41819dca60527fcd7df89b0e851286d19c76a5c9..84f278ddd852fa1ca0d4a94b9409b7b873b0717b 100644 (file)
@@ -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);
                        }
                }
        }
index 9119ef54f439b11cbdb94c07eb9aa9e18527da2a..49e34baf145f1c86e28eb07595c5f299b351b6ac 100644 (file)
@@ -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) {
index 81ac60e38ba34d96dbe5478e5fa8f96d1b40a234..552eeeedf6b378fb85c6bd522c8eea73f513adb2 100644 (file)
@@ -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:
index 420dcee3a4783a34a006630ecb91187e3f2843c0..e41cd17da122f781f4f9aa011237a8663797ab94 100644 (file)
@@ -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;
 }