1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-22 13:34:15 +03:00

s4:auth/gensec/spnego: add support for fragmented spnego messages

metze
This commit is contained in:
Stefan Metzmacher 2011-12-24 00:27:45 +01:00
parent b3f8f7e8a3
commit 891318ee4c
2 changed files with 206 additions and 4 deletions

View File

@ -518,7 +518,7 @@ static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx,
(*gensec_security) = talloc_zero(mem_ctx, struct gensec_security);
NT_STATUS_HAVE_NO_MEMORY(*gensec_security);
(*gensec_security)->max_update_size = UINT32_MAX;
(*gensec_security)->max_update_size = 0;
SMB_ASSERT(settings->lp_ctx != NULL);
(*gensec_security)->settings = talloc_reference(*gensec_security, settings);

View File

@ -30,6 +30,7 @@
#include "auth/gensec/gensec_proto.h"
#include "auth/gensec/gensec_toplevel_proto.h"
#include "param/param.h"
#include "lib/util/asn1.h"
_PUBLIC_ NTSTATUS gensec_spnego_init(void);
@ -51,6 +52,16 @@ struct spnego_state {
const char *neg_oid;
DATA_BLOB mech_types;
/*
* The following is used to implement
* the update token fragmentation
*/
size_t in_needed;
DATA_BLOB in_frag;
size_t out_max_length;
DATA_BLOB out_frag;
NTSTATUS out_status;
};
@ -58,7 +69,7 @@ static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_securi
{
struct spnego_state *spnego_state;
spnego_state = talloc(gensec_security, struct spnego_state);
spnego_state = talloc_zero(gensec_security, struct spnego_state);
if (!spnego_state) {
return NT_STATUS_NO_MEMORY;
}
@ -68,6 +79,8 @@ static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_securi
spnego_state->sub_sec_security = NULL;
spnego_state->no_response_expected = false;
spnego_state->mech_types = data_blob(NULL, 0);
spnego_state->out_max_length = gensec_max_update_size(gensec_security);
spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
gensec_security->private_data = spnego_state;
return NT_STATUS_OK;
@ -77,7 +90,7 @@ static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_securi
{
struct spnego_state *spnego_state;
spnego_state = talloc(gensec_security, struct spnego_state);
spnego_state = talloc_zero(gensec_security, struct spnego_state);
if (!spnego_state) {
return NT_STATUS_NO_MEMORY;
}
@ -87,6 +100,8 @@ static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_securi
spnego_state->sub_sec_security = NULL;
spnego_state->no_response_expected = false;
spnego_state->mech_types = data_blob(NULL, 0);
spnego_state->out_max_length = gensec_max_update_size(gensec_security);
spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
gensec_security->private_data = spnego_state;
return NT_STATUS_OK;
@ -1130,6 +1145,193 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
return NT_STATUS_INVALID_PARAMETER;
}
static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
const DATA_BLOB in, DATA_BLOB *full_in)
{
struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
size_t expected;
uint8_t *buf;
NTSTATUS status;
bool ok;
*full_in = data_blob_null;
if (spnego_state->in_needed == 0) {
size_t size = 0;
/*
* try to work out the size of the full
* input token, it might be fragmented
*/
status = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
if (!NT_STATUS_IS_OK(status) &&
!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
status = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
}
if (NT_STATUS_IS_OK(status) ||
NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
spnego_state->in_needed = size;
} else {
/*
* If it is not an asn1 message
* just call the next layer.
*/
spnego_state->in_needed = in.length;
}
}
if (spnego_state->in_needed > UINT16_MAX) {
/*
* limit the incoming message to 0xFFFF
* to avoid DoS attacks.
*/
return NT_STATUS_INVALID_BUFFER_SIZE;
}
if ((spnego_state->in_needed > 0) && (in.length == 0)) {
/*
* If we reach this, we know we got at least
* part of an asn1 message, getting 0 means
* the remote peer wants us to spin.
*/
return NT_STATUS_INVALID_PARAMETER;
}
expected = spnego_state->in_needed - spnego_state->in_frag.length;
if (in.length > expected) {
/*
* we got more than expected
*/
return NT_STATUS_INVALID_PARAMETER;
}
if (in.length == spnego_state->in_needed) {
/*
* if the in.length contains the full blob
* we are done.
*
* Note: this implies spnego_state->in_frag.length == 0,
* but we do not need to check this explicitly
* because we already know that we did not get
* more than expected.
*/
*full_in = in;
return NT_STATUS_OK;
}
ok = data_blob_append(spnego_state, &spnego_state->in_frag,
in.data, in.length);
if (!ok) {
return NT_STATUS_NO_MEMORY;
}
if (spnego_state->in_needed > spnego_state->in_frag.length) {
return NT_STATUS_MORE_PROCESSING_REQUIRED;
}
*full_in = spnego_state->in_frag;
return NT_STATUS_OK;
}
static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
DATA_BLOB *_out)
{
struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
size_t new_length;
uint8_t *buf;
DATA_BLOB out = data_blob_null;
*_out = data_blob_null;
if (spnego_state->out_frag.length == 0) {
return spnego_state->out_status;
}
/*
* There is still more data to be delivered
* to the remote peer.
*/
if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
/*
* Fast path, we can deliver everything
*/
*_out = spnego_state->out_frag;
talloc_steal(out_mem_ctx, _out->data);
spnego_state->out_frag = data_blob_null;
return spnego_state->out_status;
}
out = spnego_state->out_frag;
/*
* copy the remaining bytes
*/
spnego_state->out_frag = data_blob_talloc(spnego_state,
out.data + spnego_state->out_max_length,
out.length - spnego_state->out_max_length);
if (spnego_state->out_frag.data == NULL) {
return NT_STATUS_NO_MEMORY;
}
/*
* truncate the buffer
*/
data_blob_realloc(spnego_state, &out, spnego_state->out_max_length);
talloc_steal(out_mem_ctx, out.data);
*_out = out;
return NT_STATUS_MORE_PROCESSING_REQUIRED;
}
static NTSTATUS gensec_spnego_update_wrapper(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
struct tevent_context *ev,
const DATA_BLOB in, DATA_BLOB *out)
{
struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
DATA_BLOB full_in = data_blob_null;
NTSTATUS status;
*out = data_blob_null;
if (spnego_state->out_frag.length > 0) {
if (in.length > 0) {
return NT_STATUS_INVALID_PARAMETER;
}
return gensec_spnego_update_out(gensec_security,
out_mem_ctx,
out);
}
status = gensec_spnego_update_in(gensec_security,
in, &full_in);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = gensec_spnego_update(gensec_security,
spnego_state, ev,
full_in,
&spnego_state->out_frag);
data_blob_free(&spnego_state->in_frag);
spnego_state->in_needed = 0;
if (!NT_STATUS_IS_OK(status) &&
!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
return status;
}
spnego_state->out_status = status;
return gensec_spnego_update_out(gensec_security,
out_mem_ctx,
out);
}
static void gensec_spnego_want_feature(struct gensec_security *gensec_security,
uint32_t feature)
{
@ -1168,7 +1370,7 @@ static const struct gensec_security_ops gensec_spnego_security_ops = {
.oid = gensec_spnego_oids,
.client_start = gensec_spnego_client_start,
.server_start = gensec_spnego_server_start,
.update = gensec_spnego_update,
.update = gensec_spnego_update_wrapper,
.seal_packet = gensec_spnego_seal_packet,
.sign_packet = gensec_spnego_sign_packet,
.sig_size = gensec_spnego_sig_size,