diff options
-rw-r--r-- | source4/Makefile.in | 2 | ||||
-rw-r--r-- | source4/auth/auth_ntlmssp.c | 93 | ||||
-rw-r--r-- | source4/include/auth.h | 5 | ||||
-rw-r--r-- | source4/rpc_server/dcerpc_server.c | 44 | ||||
-rw-r--r-- | source4/rpc_server/dcerpc_server.h | 2 | ||||
-rw-r--r-- | source4/rpc_server/dcesrv_auth.c | 253 |
6 files changed, 373 insertions, 26 deletions
diff --git a/source4/Makefile.in b/source4/Makefile.in index e8705da4cc..2f35b8a47d 100644 --- a/source4/Makefile.in +++ b/source4/Makefile.in @@ -258,7 +258,7 @@ PLAINTEXT_AUTH_OBJ = auth/pampass.o auth/pass_check.o AUTH_OBJ = auth/auth.o auth/auth_sam.o \ auth/auth_unix.o auth/auth_util.o \ - auth/auth_builtin.o auth/auth_compat.o \ + auth/auth_builtin.o auth/auth_compat.o auth/auth_ntlmssp.o \ $(PLAINTEXT_AUTH_OBJ) $(UNIGRP_OBJ) MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o diff --git a/source4/auth/auth_ntlmssp.c b/source4/auth/auth_ntlmssp.c index b3dff8dbe6..940630b4c6 100644 --- a/source4/auth/auth_ntlmssp.c +++ b/source4/auth/auth_ntlmssp.c @@ -23,13 +23,59 @@ #include "includes.h" -static const uint8 *auth_ntlmssp_get_challenge(struct ntlmssp_state *ntlmssp_state) +/** + * Return the challenge as determined by the authentication subsystem + * @return an 8 byte random challenge + */ + +static const uint8 *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state) { AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; return auth_ntlmssp_state->auth_context->get_ntlm_challenge(auth_ntlmssp_state->auth_context); } -static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state) +/** + * Some authentication methods 'fix' the challenge, so we may not be able to set it + * + * @return If the effective challenge used by the auth subsystem may be modified + */ +static BOOL auth_ntlmssp_may_set_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; + struct auth_context *auth_context = auth_ntlmssp_state->auth_context; + + return auth_context->challenge_may_be_modified; +} + +/** + * NTLM2 authentication modifies the effective challange, + * @param challenge The new challenge value + */ +static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) +{ + AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; + struct auth_context *auth_context = auth_ntlmssp_state->auth_context; + + SMB_ASSERT(challenge->length == 8); + + auth_context->challenge = data_blob_talloc(auth_context->mem_ctx, + challenge->data, challenge->length); + + auth_context->challenge_set_by = "NTLMSSP callback (NTLM2)"; + + DEBUG(5, ("auth_context challenge set by %s\n", auth_context->challenge_set_by)); + DEBUG(5, ("challenge is: \n")); + dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length); + return NT_STATUS_OK; +} + +/** + * Check the password on an NTLMSSP login. + * + * Return the session keys used on the connection. + */ + +static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key) { AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; uint32 auth_flags = AUTH_FLAG_NONE; @@ -45,18 +91,17 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state) auth_flags |= AUTH_FLAG_NTLM_RESP; } else if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length > 24) { auth_flags |= AUTH_FLAG_NTLMv2_RESP; - }; + } +#if 0 /* the client has given us its machine name (which we otherwise would not get on port 445). we need to possibly reload smb.conf if smb.conf includes depend on the machine name */ - - sub_set_remote_machine(auth_ntlmssp_state->ntlmssp_state->workstation); - + set_remote_machine_name(auth_ntlmssp_state->ntlmssp_state->workstation, True); /* setup the string used by %U */ /* sub_set_smb_name checks for weird internally */ - sub_set_user_name(auth_ntlmssp_state->ntlmssp_state->user); - + sub_set_smb_name(auth_ntlmssp_state->ntlmssp_state->user); reload_services(True); +#endif nt_status = make_user_info_map(&user_info, auth_ntlmssp_state->ntlmssp_state->user, @@ -71,10 +116,28 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state) return nt_status; } - nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context, user_info, &auth_ntlmssp_state->server_info); - + nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context, + user_info, &auth_ntlmssp_state->server_info); + free_user_info(&user_info); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + if (auth_ntlmssp_state->server_info->nt_session_key.length) { + DEBUG(5, ("Got NT session key of length %u\n", auth_ntlmssp_state->server_info->nt_session_key.length)); + *nt_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, + auth_ntlmssp_state->server_info->nt_session_key.data, + auth_ntlmssp_state->server_info->nt_session_key.length); + } else if (auth_ntlmssp_state->server_info->lm_session_key.length) { + DEBUG(5, ("Got LM session key of length %u\n", auth_ntlmssp_state->server_info->lm_session_key.length)); + *lm_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, + auth_ntlmssp_state->server_info->lm_session_key.data, + auth_ntlmssp_state->server_info->lm_session_key.length); + } else { + *nt_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, + auth_ntlmssp_state->server_info->session_key, 16); + } return nt_status; } @@ -106,18 +169,20 @@ NTSTATUS auth_ntlmssp_start(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) (*auth_ntlmssp_state)->ntlmssp_state->auth_context = (*auth_ntlmssp_state); (*auth_ntlmssp_state)->ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge; + (*auth_ntlmssp_state)->ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge; + (*auth_ntlmssp_state)->ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge; (*auth_ntlmssp_state)->ntlmssp_state->check_password = auth_ntlmssp_check_password; (*auth_ntlmssp_state)->ntlmssp_state->server_role = lp_server_role(); return NT_STATUS_OK; } -NTSTATUS auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) +void auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) { TALLOC_CTX *mem_ctx = (*auth_ntlmssp_state)->mem_ctx; if ((*auth_ntlmssp_state)->ntlmssp_state) { - ntlmssp_server_end(&(*auth_ntlmssp_state)->ntlmssp_state); + ntlmssp_end(&(*auth_ntlmssp_state)->ntlmssp_state); } if ((*auth_ntlmssp_state)->auth_context) { ((*auth_ntlmssp_state)->auth_context->free)(&(*auth_ntlmssp_state)->auth_context); @@ -127,12 +192,10 @@ NTSTATUS auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) } talloc_destroy(mem_ctx); *auth_ntlmssp_state = NULL; - return NT_STATUS_OK; } NTSTATUS auth_ntlmssp_update(AUTH_NTLMSSP_STATE *auth_ntlmssp_state, const DATA_BLOB request, DATA_BLOB *reply) { - return ntlmssp_server_update(auth_ntlmssp_state->ntlmssp_state, request, reply); + return ntlmssp_update(auth_ntlmssp_state->ntlmssp_state, request, reply); } - diff --git a/source4/include/auth.h b/source4/include/auth.h index 89e46e3782..84faba2b0d 100644 --- a/source4/include/auth.h +++ b/source4/include/auth.h @@ -78,8 +78,9 @@ typedef struct auth_serversupplied_info NT_USER_TOKEN *ptok; uint8 session_key[16]; - uint8 first_8_lm_hash[8]; + DATA_BLOB nt_session_key; + DATA_BLOB lm_session_key; uint32 sam_fill_level; /* How far is this structure filled? */ @@ -95,6 +96,8 @@ struct auth_context { /* Who set this up in the first place? */ const char *challenge_set_by; + BOOL challenge_may_be_modified; + struct auth_methods *challenge_set_method; /* What order are the various methods in? Try to stop it changing under us */ struct auth_methods *auth_method_list; diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index 7fa7a7aa8b..16b573cfad 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -369,7 +369,8 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) return NT_STATUS_NO_MEMORY; } - status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, NULL); + status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, + call->dce->auth_state.auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -377,7 +378,26 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length); DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *); + DLIST_ADD_END(call->dce->call_list, call, struct dcesrv_call_state *); + + return NT_STATUS_OK; +} + + +/* + handle a auth3 request +*/ +static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call) +{ + /* handle the auth3 in the auth code */ + if (!dcesrv_auth_auth3(call)) { + return dcesrv_fault(call, DCERPC_FAULT_OTHER); + } + + talloc_destroy(call->mem_ctx); + /* we don't send a reply to a auth3 request, except by a + fault */ return NT_STATUS_OK; } @@ -473,10 +493,8 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) pkt.u.response.stub_and_verifier.data = stub.data; pkt.u.response.stub_and_verifier.length = length; - - status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, NULL); - if (!NT_STATUS_IS_OK(status)) { - return status; + if (!dcesrv_auth_response(call, &rep->data, &pkt)) { + return dcesrv_fault(call, DCERPC_FAULT_OTHER); } SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length); @@ -487,6 +505,8 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) stub.length -= length; } while (stub.length != 0); + DLIST_ADD_END(call->dce->call_list, call, struct dcesrv_call_state *); + return NT_STATUS_OK; } @@ -568,6 +588,13 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce) dce_partial_advance(dce, blob.length); + /* we have to check the signing here, before combining the + pdus */ + if (call->pkt.ptype == DCERPC_PKT_REQUEST && + !dcesrv_auth_request(call)) { + return dcesrv_fault(call, DCERPC_FAULT_OTHER); + } + /* see if this is a continued packet */ if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) { struct dcesrv_call_state *call2 = call; @@ -623,6 +650,9 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce) case DCERPC_PKT_BIND: status = dcesrv_bind(call); break; + case DCERPC_PKT_AUTH3: + status = dcesrv_auth3(call); + break; case DCERPC_PKT_REQUEST: status = dcesrv_request(call); break; @@ -634,9 +664,7 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce) /* if we are going to be sending a reply then add it to the list of pending calls. We add it to the end to keep the call list in the order we will answer */ - if (NT_STATUS_IS_OK(status)) { - DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *); - } else { + if (!NT_STATUS_IS_OK(status)) { talloc_destroy(mem_ctx); } diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h index a2be1d8e06..e731a5719c 100644 --- a/source4/rpc_server/dcerpc_server.h +++ b/source4/rpc_server/dcerpc_server.h @@ -72,7 +72,7 @@ struct dcesrv_handle { /* hold the authentication state information */ struct dcesrv_auth { - struct ntlmssp_state *ntlmssp_state; + struct auth_ntlmssp_state *ntlmssp_state; struct dcerpc_auth *auth_info; }; diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c index aea79f2927..f290c741cb 100644 --- a/source4/rpc_server/dcesrv_auth.c +++ b/source4/rpc_server/dcesrv_auth.c @@ -25,10 +25,49 @@ /* parse any auth information from a dcerpc bind request + return False if we can't handle the auth request for some + reason (in which case we send a bind_nak) */ BOOL dcesrv_auth_bind(struct dcesrv_call_state *call) { struct dcerpc_packet *pkt = &call->pkt; + struct dcesrv_state *dce = call->dce; + NTSTATUS status; + + if (pkt->u.bind.auth_info.length == 0) { + dce->auth_state.auth_info = NULL; + return True; + } + + dce->auth_state.auth_info = talloc_p(dce->mem_ctx, struct dcerpc_auth); + if (!dce->auth_state.auth_info) { + return False; + } + + status = ndr_pull_struct_blob(&pkt->u.bind.auth_info, + call->mem_ctx, + dce->auth_state.auth_info, + (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + if (dce->auth_state.auth_info->auth_type != DCERPC_AUTH_TYPE_NTLMSSP) { + /* only do NTLMSSP for now */ + DEBUG(2,("auth_type %d not supported\n", dce->auth_state.auth_info->auth_type)); + return False; + } + + if (dce->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_INTEGRITY && + dce->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) { + DEBUG(2,("auth_level %d not supported\n", dce->auth_state.auth_info->auth_level)); + return False; + } + + status = auth_ntlmssp_start(&dce->auth_state.ntlmssp_state); + if (!NT_STATUS_IS_OK(status)) { + return False; + } return True; } @@ -38,5 +77,219 @@ BOOL dcesrv_auth_bind(struct dcesrv_call_state *call) */ BOOL dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct dcerpc_packet *pkt) { + struct dcesrv_state *dce = call->dce; + NTSTATUS status; + + if (!call->dce->auth_state.ntlmssp_state) { + return True; + } + + status = auth_ntlmssp_update(dce->auth_state.ntlmssp_state, + dce->auth_state.auth_info->credentials, + &dce->auth_state.auth_info->credentials); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + return False; + } + + dce->auth_state.auth_info->auth_pad_length = 0; + dce->auth_state.auth_info->auth_reserved = 0; + + return True; +} + + +/* + process the final stage of a NTLMSSP auth request +*/ +BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call) +{ + struct dcerpc_packet *pkt = &call->pkt; + struct dcesrv_state *dce = call->dce; + NTSTATUS status; + + if (!dce->auth_state.auth_info || + !dce->auth_state.ntlmssp_state || + pkt->u.auth.auth_info.length == 0) { + return False; + } + + status = ndr_pull_struct_blob(&pkt->u.auth.auth_info, + call->mem_ctx, + dce->auth_state.auth_info, + (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + if (dce->auth_state.auth_info->auth_type != DCERPC_AUTH_TYPE_NTLMSSP) { + return False; + } + if (dce->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_INTEGRITY && + dce->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) { + return False; + } + + status = auth_ntlmssp_update(dce->auth_state.ntlmssp_state, + dce->auth_state.auth_info->credentials, + &dce->auth_state.auth_info->credentials); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + switch (dce->auth_state.auth_info->auth_level) { + case DCERPC_AUTH_LEVEL_PRIVACY: + case DCERPC_AUTH_LEVEL_INTEGRITY: + /* setup for signing */ + status = ntlmssp_sign_init(dce->auth_state.ntlmssp_state->ntlmssp_state); + break; + } + + return True; +} + + +/* + check credentials on a request +*/ +BOOL dcesrv_auth_request(struct dcesrv_call_state *call) +{ + struct dcerpc_packet *pkt = &call->pkt; + struct dcesrv_state *dce = call->dce; + DATA_BLOB auth_blob; + struct dcerpc_auth auth; + struct ndr_pull *ndr; + NTSTATUS status; + + if (!dce->auth_state.auth_info || + !dce->auth_state.ntlmssp_state) { + return True; + } + + auth_blob.length = 8 + pkt->auth_length; + + /* check for a valid length */ + if (pkt->u.request.stub_and_verifier.length < auth_blob.length) { + return False; + } + + auth_blob.data = + pkt->u.request.stub_and_verifier.data + + pkt->u.request.stub_and_verifier.length - auth_blob.length; + pkt->u.request.stub_and_verifier.length -= auth_blob.length; + + /* pull the auth structure */ + ndr = ndr_pull_init_blob(&auth_blob, call->mem_ctx); + if (!ndr) { + return False; + } + + status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + /* check signature or unseal the packet */ + switch (dce->auth_state.auth_info->auth_level) { + case DCERPC_AUTH_LEVEL_PRIVACY: + status = ntlmssp_unseal_packet(dce->auth_state.ntlmssp_state->ntlmssp_state, + pkt->u.request.stub_and_verifier.data, + pkt->u.request.stub_and_verifier.length, + &auth.credentials); + break; + + case DCERPC_AUTH_LEVEL_INTEGRITY: + status = ntlmssp_check_packet(dce->auth_state.ntlmssp_state->ntlmssp_state, + pkt->u.request.stub_and_verifier.data, + pkt->u.request.stub_and_verifier.length, + &auth.credentials); + break; + + default: + status = NT_STATUS_INVALID_LEVEL; + break; + } + + /* remove the indicated amount of paddiing */ + if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) { + return False; + } + pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length; + + return NT_STATUS_IS_OK(status); +} + + +/* + push a signed or sealed dcerpc request packet into a blob +*/ +BOOL dcesrv_auth_response(struct dcesrv_call_state *call, + DATA_BLOB *blob, struct dcerpc_packet *pkt) +{ + struct dcesrv_state *dce = call->dce; + NTSTATUS status; + struct ndr_push *ndr; + + /* non-signed packets are simple */ + if (!dce->auth_state.auth_info || !dce->auth_state.ntlmssp_state) { + status = dcerpc_push_auth(blob, call->mem_ctx, pkt, NULL); + return NT_STATUS_IS_OK(status); + } + + ndr = ndr_push_init_ctx(call->mem_ctx); + if (!ndr) { + return False; + } + + status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + /* pad to 8 byte multiple */ + dce->auth_state.auth_info->auth_pad_length = NDR_ALIGN(ndr, 8); + ndr_push_zero(ndr, dce->auth_state.auth_info->auth_pad_length); + + /* sign or seal the packet */ + switch (dce->auth_state.auth_info->auth_level) { + case DCERPC_AUTH_LEVEL_PRIVACY: + status = ntlmssp_seal_packet(dce->auth_state.ntlmssp_state->ntlmssp_state, + ndr->data + DCERPC_REQUEST_LENGTH, + ndr->offset - DCERPC_REQUEST_LENGTH, + &dce->auth_state.auth_info->credentials); + break; + + case DCERPC_AUTH_LEVEL_INTEGRITY: + status = ntlmssp_sign_packet(dce->auth_state.ntlmssp_state->ntlmssp_state, + ndr->data + DCERPC_REQUEST_LENGTH, + ndr->offset - DCERPC_REQUEST_LENGTH, + &dce->auth_state.auth_info->credentials); + break; + default: + status = NT_STATUS_INVALID_LEVEL; + break; + } + + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + /* add the auth verifier */ + status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, dce->auth_state.auth_info); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + /* extract the whole packet as a blob */ + *blob = ndr_push_blob(ndr); + + /* fill in the fragment length and auth_length, we can't fill + in these earlier as we don't know the signature length (it + could be variable length) */ + SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, blob->length); + SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, dce->auth_state.auth_info->credentials.length); + + data_blob_free(&dce->auth_state.auth_info->credentials); + return True; } |