summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/auth/gensec/gensec.c18
-rw-r--r--source4/auth/gensec/gensec.h2
-rw-r--r--source4/auth/gensec/gensec_gssapi.c148
3 files changed, 134 insertions, 34 deletions
diff --git a/source4/auth/gensec/gensec.c b/source4/auth/gensec/gensec.c
index 2d347b65b1..13ee95bad3 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 4be97dfeaa..d21008f034 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 270ae37703..e8597dc73b 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)