From 048d0c64f9505ad236b9bf138d10ee3e2bb08cec Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 21 Jul 2006 01:44:24 +0000 Subject: [PATCH] r17171: Add a gensec function to determine the maximum negotiated buffer size, and the maximum amount of user data that may be fitted into that. This is used in the new SASL code, to correctly honour SASL buffer sizes. Andrew Bartlett (This used to be commit cbbe99d9c1f0262e67a495fb098cacc09fd78e05) --- source4/auth/gensec/gensec.c | 18 ++++ source4/auth/gensec/gensec.h | 2 + source4/auth/gensec/gensec_gssapi.c | 148 +++++++++++++++++++++------- 3 files changed, 134 insertions(+), 34 deletions(-) diff --git a/source4/auth/gensec/gensec.c b/source4/auth/gensec/gensec.c index 2d347b65b1c..13ee95bad3b 100644 --- a/source4/auth/gensec/gensec.c +++ b/source4/auth/gensec/gensec.c @@ -815,6 +815,24 @@ size_t gensec_sig_size(struct gensec_security *gensec_security, size_t data_size return gensec_security->ops->sig_size(gensec_security, data_size); } +size_t gensec_max_input_size(struct gensec_security *gensec_security) +{ + if (!gensec_security->ops->max_input_size) { + return (1 << 17) - gensec_sig_size(gensec_security, 1 << 17); + } + + return gensec_security->ops->max_input_size(gensec_security); +} + +size_t gensec_max_wrapped_size(struct gensec_security *gensec_security) +{ + if (!gensec_security->ops->max_wrapped_size) { + return (1 << 17); + } + + return gensec_security->ops->max_wrapped_size(gensec_security); +} + _PUBLIC_ NTSTATUS gensec_wrap(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, diff --git a/source4/auth/gensec/gensec.h b/source4/auth/gensec/gensec.h index 4be97dfeaa4..d21008f034b 100644 --- a/source4/auth/gensec/gensec.h +++ b/source4/auth/gensec/gensec.h @@ -78,6 +78,8 @@ struct gensec_security_ops { const uint8_t *whole_pdu, size_t pdu_length, DATA_BLOB *sig); size_t (*sig_size)(struct gensec_security *gensec_security, size_t data_size); + size_t (*max_input_size)(struct gensec_security *gensec_security); + size_t (*max_wrapped_size)(struct gensec_security *gensec_security); NTSTATUS (*check_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, const uint8_t *data, size_t length, const uint8_t *whole_pdu, size_t pdu_length, diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 270ae377033..e8597dc73b0 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -67,8 +67,13 @@ struct gensec_gssapi_state { uint8_t sasl_protection; /* What was negotiated at the SASL * layer, independent of the GSSAPI * layer... */ + + size_t max_wrap_buf_size; }; +static size_t gensec_gssapi_max_input_size(struct gensec_security *gensec_security); +static size_t gensec_gssapi_max_wrapped_size(struct gensec_security *gensec_security); + static char *gssapi_error_string(TALLOC_CTX *mem_ctx, OM_uint32 maj_stat, OM_uint32 min_stat) { @@ -129,6 +134,9 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security) return NT_STATUS_NO_MEMORY; } + gensec_gssapi_state->max_wrap_buf_size + = lp_parm_int(-1, "gensec_gssapi", "max wrap buf size", 65535); + gensec_gssapi_state->sasl = False; gensec_gssapi_state->sasl_state = STAGE_GSS_NEG; @@ -490,6 +498,7 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, } break; } + /* These last two stages are only done if we were invoked as SASL */ case STAGE_SASL_SSF_NEG: { @@ -497,11 +506,17 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, case GENSEC_CLIENT: { uint8_t maxlength_proposed[4]; + uint8_t maxlength_accepted[4]; uint8_t security_supported; int conf_state; gss_qop_t qop_state; input_token.length = in.length; input_token.value = in.data; + + /* As a client, we have just send a + * zero-length blob to the server (after the + * normal GSSAPI exchange), and it has replied + * with it's SASL negotiation */ maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, @@ -521,10 +536,14 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, memcpy(maxlength_proposed, output_token.value, 4); gss_release_buffer(&min_stat, &output_token); - + /* first byte is the proposed security */ security_supported = maxlength_proposed[0]; maxlength_proposed[0] = '\0'; + + /* Rest is the proposed max wrap length */ + gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_proposed, 0), + gensec_gssapi_state->max_wrap_buf_size); gensec_gssapi_state->sasl_protection = 0; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { if (security_supported & NEG_SEAL) { @@ -540,13 +559,15 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, DEBUG(1, ("Remote server does not support unprotected connections")); return NT_STATUS_ACCESS_DENIED; } + + /* Send back the negotiated max length */ + + RSIVAL(maxlength_accepted, 0, gensec_gssapi_state->max_wrap_buf_size); + + maxlength_accepted[0] = gensec_gssapi_state->sasl_protection; - /* We just accept their max length, and send - * it back with the SASL flags */ - maxlength_proposed[0] = gensec_gssapi_state->sasl_protection; - - input_token.value = maxlength_proposed; - input_token.length = sizeof(maxlength_proposed); + input_token.value = maxlength_accepted; + input_token.length = sizeof(maxlength_accepted); maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, @@ -583,8 +604,14 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, uint8_t security_supported = 0x0; int conf_state; - /* TODO: Need some better ideas for this */ - RSIVAL(maxlength_proposed, 0, 0xFFFFFF); + /* As a server, we have just been sent a zero-length blob (note this, but it isn't fatal) */ + if (in.length != 0) { + DEBUG(1, ("SASL/GSSAPI: client sent non-zero length starting SASL negotiation!\n")); + } + + /* Give the client some idea what we will support */ + + RSIVAL(maxlength_proposed, 0, gensec_gssapi_state->max_wrap_buf_size); /* first byte is the proposed security */ maxlength_proposed[0] = '\0'; @@ -599,11 +626,9 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, /* If we don't support anything, this must be 0 */ RSIVAL(maxlength_proposed, 0, 0x0); } - + /* TODO: We may not wish to support this */ security_supported |= NEG_NONE; - - /* Ignore 'in' */ maxlength_proposed[0] = security_supported; input_token.value = maxlength_proposed; @@ -636,8 +661,8 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, /* This is s server-only stage */ case STAGE_SASL_SSF_ACCEPT: { - uint8_t maxlength_proposed[4]; - uint8_t security_proposed; + uint8_t maxlength_accepted[4]; + uint8_t security_accepted; int conf_state; gss_qop_t qop_state; input_token.length = in.length; @@ -659,24 +684,27 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, return NT_STATUS_INVALID_PARAMETER; } - memcpy(maxlength_proposed, output_token.value, 4); + memcpy(maxlength_accepted, output_token.value, 4); gss_release_buffer(&min_stat, &output_token); /* first byte is the proposed security */ - /* TODO: We should do something with the rest, but for now... */ - security_proposed = maxlength_proposed[0]; + security_accepted = maxlength_accepted[0]; + maxlength_accepted[0] = '\0'; + + /* Rest is the proposed max wrap length */ + gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_accepted, 0), + gensec_gssapi_state->max_wrap_buf_size); - maxlength_proposed[0] = 0x0; gensec_gssapi_state->sasl_protection = 0; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { - if (security_proposed & NEG_SEAL) { + if (security_accepted & NEG_SEAL) { gensec_gssapi_state->sasl_protection |= NEG_SEAL; } } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { - if (security_proposed & NEG_SIGN) { + if (security_accepted & NEG_SIGN) { gensec_gssapi_state->sasl_protection |= NEG_SIGN; } - } else if (security_proposed & NEG_NONE) { + } else if (security_accepted & NEG_NONE) { gensec_gssapi_state->sasl_protection |= NEG_NONE; } else { DEBUG(1, ("Remote client does not support unprotected connections, but we failed to negotiate anything better")); @@ -712,6 +740,16 @@ static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, int conf_state; input_token.length = in->length; input_token.value = in->data; + + if (gensec_gssapi_state->sasl) { + size_t max_input_size = gensec_gssapi_max_input_size(gensec_security); + if (max_input_size < in->length) { + DEBUG(1, ("gensec_gssapi_wrap: INPUT data (%u) is larger than SASL negotiated maximum size (%u)\n", + in->length, + (unsigned int)max_input_size)); + } + return NT_STATUS_INVALID_PARAMETER; + } maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, @@ -749,6 +787,14 @@ static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security, input_token.length = in->length; input_token.value = in->data; + if (gensec_gssapi_state->sasl) { + size_t max_wrapped_size = gensec_gssapi_max_wrapped_size(gensec_security); + if (max_wrapped_size < in->length) { + DEBUG(1, ("gensec_gssapi_unwrap: WRAPPED data is larger than SASL negotiated maximum size\n")); + return NT_STATUS_INVALID_PARAMETER; + } + } + maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, &input_token, @@ -797,7 +843,7 @@ static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, si &output_size); if (GSS_ERROR(maj_stat)) { TALLOC_CTX *mem_ctx = talloc_new(NULL); - DEBUG(1, ("gensec_gssapi_seal_packet: determinaing signature size with gss_wrap_size_limit failed: %s\n", + DEBUG(1, ("gensec_gssapi_sig_size: determinaing signature size with gsskrb5_wrap_size failed: %s\n", gssapi_error_string(mem_ctx, maj_stat, min_stat))); talloc_free(mem_ctx); return 0; @@ -811,6 +857,38 @@ static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, si return output_size - data_size; } +/* Find out the maximum input size negotiated on this connection */ + +static size_t gensec_gssapi_max_input_size(struct gensec_security *gensec_security) +{ + struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data; + OM_uint32 maj_stat, min_stat; + OM_uint32 max_input_size; + + maj_stat = gss_wrap_size_limit(&min_stat, + gensec_gssapi_state->gssapi_context, + gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), + GSS_C_QOP_DEFAULT, + gensec_gssapi_state->max_wrap_buf_size, + &max_input_size); + if (GSS_ERROR(maj_stat)) { + TALLOC_CTX *mem_ctx = talloc_new(NULL); + DEBUG(1, ("gensec_gssapi_max_input_size: determinaing signature size with gss_wrap_size_limit failed: %s\n", + gssapi_error_string(mem_ctx, maj_stat, min_stat))); + talloc_free(mem_ctx); + return 0; + } + + return max_input_size; +} + +/* Find out the maximum output size negotiated on this connection */ +static size_t gensec_gssapi_max_wrapped_size(struct gensec_security *gensec_security) +{ + struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data; + return gensec_gssapi_state->max_wrap_buf_size; +} + static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, @@ -1287,18 +1365,20 @@ static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = { /* As a server, this could in theory accept any GSSAPI mech */ static const struct gensec_security_ops gensec_gssapi_sasl_krb5_security_ops = { - .name = "gssapi_krb5_sasl", - .sasl_name = "GSSAPI", - .client_start = gensec_gssapi_sasl_client_start, - .server_start = gensec_gssapi_sasl_server_start, - .update = gensec_gssapi_update, - .session_key = gensec_gssapi_session_key, - .session_info = gensec_gssapi_session_info, - .wrap = gensec_gssapi_wrap, - .unwrap = gensec_gssapi_unwrap, - .have_feature = gensec_gssapi_have_feature, - .enabled = True, - .kerberos = True + .name = "gssapi_krb5_sasl", + .sasl_name = "GSSAPI", + .client_start = gensec_gssapi_sasl_client_start, + .server_start = gensec_gssapi_sasl_server_start, + .update = gensec_gssapi_update, + .session_key = gensec_gssapi_session_key, + .session_info = gensec_gssapi_session_info, + .max_input_size = gensec_gssapi_max_input_size, + .max_wrapped_size = gensec_gssapi_max_wrapped_size, + .wrap = gensec_gssapi_wrap, + .unwrap = gensec_gssapi_unwrap, + .have_feature = gensec_gssapi_have_feature, + .enabled = True, + .kerberos = True }; NTSTATUS gensec_gssapi_init(void) -- 2.34.1