1
0
mirror of https://github.com/samba-team/samba.git synced 2025-10-26 23:33:15 +03:00
Files
samba-mirror/source/rpc_server/dcesrv_auth.c
Andrew Tridgell bb1af644a5 a fairly large commit!
This adds support for bigendian rpc in the client. I have installed
SUN pcnetlink locally and am using it to test the samba4 rpc
code. This allows us to easily find places where we have stuffed up
the types (such as 2 uint16 versus a uint32), as testing both
big-endian and little-endian easily shows which is correct. I have now
used this to fix several bugs like that in the samba4 IDL.

In order to make this work I also had to redefine a GUID as a true
structure, not a blob. From the pcnetlink wire it is clear that it is
indeed defined as a structure (the byte order changes). This required
changing lots of Samba code to use a GUID as a structure.

I also had to fix the if_version code in dcerpc syntax IDs, as it
turns out they are a single uint32 not two uint16s.

The big-endian support is a bit ugly at the moment, and breaks the
layering in some places. More work is needed, especially on the server
side.
-

296 lines
8.0 KiB
C

/*
Unix SMB/CIFS implementation.
server side dcerpc authentication code
Copyright (C) Andrew Tridgell 2003
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
/*
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;
}
/*
add any auth information needed in a bind ack
*/
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, 0);
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;
}