mirror of
https://github.com/samba-team/samba.git
synced 2024-12-31 17:18:04 +03:00
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>
This commit is contained in:
parent
8ae968193b
commit
fd29e28d52
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -211,4 +211,9 @@ interface winbind
|
||||
[in] uint32 flags,
|
||||
[out,ref] lsa_ForestTrustInformation **forest_trust_info
|
||||
);
|
||||
|
||||
NTSTATUS winbind_SendToSam(
|
||||
[in] netr_SendToSamBase message
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user