Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- From b51b33f2bc5d1497ddf5bd107f791c101695000d Mon Sep 17 00:00:00 2001
- From: Nicolas Williams <[email protected]>
- Date: Mon, 14 Sep 2015 12:27:52 -0400
- Subject: [PATCH] Fix SPNEGO context aliasing bugs [CVE-2015-2695]
- The SPNEGO mechanism currently replaces its context handle with the
- mechanism context handle upon establishment, under the assumption that
- most GSS functions are only called after context establishment. This
- assumption is incorrect, and can lead to aliasing violations for some
- programs. Maintain the SPNEGO context structure after context
- establishment and refer to it in all GSS methods. Add initiate and
- opened flags to the SPNEGO context structure for use in
- gss_inquire_context() prior to context establishment.
- CVE-2015-2695:
- In MIT krb5 1.5 and later, applications which call
- gss_inquire_context() on a partially-established SPNEGO context can
- cause the GSS-API library to read from a pointer using the wrong type,
- generally causing a process crash. This bug may go unnoticed, because
- the most common SPNEGO authentication scenario establishes the context
- after just one call to gss_accept_sec_context(). Java server
- applications using the native JGSS provider are vulnerable to this
- bug. A carefully crafted SPNEGO packet might allow the
- gss_inquire_context() call to succeed with attacker-determined
- results, but applications should not make access control decisions
- based on gss_inquire_context() results prior to context establishment.
- CVSSv2 Vector: AV:N/AC:M/Au:N/C:N/I:N/A:C/E:POC/RL:OF/RC:C
- [[email protected]: several bugfixes, style changes, and edge-case
- behavior changes; commit message and CVE description]
- ticket: 8244
- target_version: 1.14
- tags: pullup
- Patch content is adjusted by Howard Guo <[email protected]> to work with
- this older version of Kerberos.
- diff -rupN krb5-1.6.3/src/lib/gssapi/spnego/gssapiP_spnego.h krb5-1.6.3-patched/src/lib/gssapi/spnego/gssapiP_spnego.h
- --- krb5-1.6.3/src/lib/gssapi/spnego/gssapiP_spnego.h 2015-10-28 11:16:05.520166617 +0100
- +++ krb5-1.6.3-patched/src/lib/gssapi/spnego/gssapiP_spnego.h 2015-10-28 11:54:29.719199162 +0100
- @@ -95,6 +95,8 @@ typedef struct {
- int firstpass;
- int mech_complete;
- int nego_done;
- + int initiate;
- + int opened;
- OM_uint32 ctx_flags;
- gss_name_t internal_name;
- gss_OID actual_mech;
- diff -rupN krb5-1.6.3/src/lib/gssapi/spnego/spnego_mech.c krb5-1.6.3-patched/src/lib/gssapi/spnego/spnego_mech.c
- --- krb5-1.6.3/src/lib/gssapi/spnego/spnego_mech.c 2015-10-28 11:16:05.520166617 +0100
- +++ krb5-1.6.3-patched/src/lib/gssapi/spnego/spnego_mech.c 2015-10-28 13:06:16.223968028 +0100
- @@ -76,7 +76,7 @@ static OM_uint32 get_available_mechs(OM_
- gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
- static void release_spnego_ctx(spnego_gss_ctx_id_t *);
- static void check_spnego_options(spnego_gss_ctx_id_t);
- -static spnego_gss_ctx_id_t create_spnego_ctx(void);
- +static spnego_gss_ctx_id_t create_spnego_ctx(int);
- static int put_req_flags(unsigned char **, OM_uint32, unsigned int);
- static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
- static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
- @@ -291,7 +291,7 @@ check_spnego_options(spnego_gss_ctx_id_t
- }
- static spnego_gss_ctx_id_t
- -create_spnego_ctx(void)
- +create_spnego_ctx(int initiate)
- {
- spnego_gss_ctx_id_t spnego_ctx = NULL;
- spnego_ctx = (spnego_gss_ctx_id_t)
- @@ -313,6 +313,8 @@ create_spnego_ctx(void)
- spnego_ctx->mic_rcvd = 0;
- spnego_ctx->mech_complete = 0;
- spnego_ctx->nego_done = 0;
- + spnego_ctx->opened = 0;
- + spnego_ctx->initiate = initiate;
- spnego_ctx->internal_name = GSS_C_NO_NAME;
- spnego_ctx->actual_mech = GSS_C_NO_OID;
- @@ -462,7 +464,7 @@ init_ctx_new(OM_uint32 *minor_status,
- if (ret != GSS_S_COMPLETE)
- return ret;
- - sc = create_spnego_ctx();
- + sc = create_spnego_ctx(1);
- if (sc == NULL)
- return GSS_S_FAILURE;
- @@ -480,10 +482,7 @@ init_ctx_new(OM_uint32 *minor_status,
- ret = GSS_S_FAILURE;
- goto cleanup;
- }
- - /*
- - * The actual context is not yet determined, set the output
- - * context handle to refer to the spnego context itself.
- - */
- +
- sc->ctx_handle = GSS_C_NO_CONTEXT;
- *ctx = (gss_ctx_id_t)sc;
- *tokflag = INIT_TOKEN_SEND;
- @@ -837,14 +836,9 @@ cleanup:
- }
- gss_release_buffer(&tmpmin, &mechtok_out);
- if (ret == GSS_S_COMPLETE) {
- - /*
- - * Now, switch the output context to refer to the
- - * negotiated mechanism's context.
- - */
- - *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
- + spnego_ctx->opened = 1;
- if (actual_mech != NULL)
- *actual_mech = spnego_ctx->actual_mech;
- - release_spnego_ctx(&spnego_ctx);
- } else if (ret != GSS_S_CONTINUE_NEEDED) {
- if (spnego_ctx != NULL) {
- gss_delete_sec_context(&tmpmin,
- @@ -938,7 +932,7 @@ acc_ctx_new(OM_uint32 *minor_status,
- ret = GSS_S_BAD_MECH;
- goto cleanup;
- }
- - sc = create_spnego_ctx();
- + sc = create_spnego_ctx(0);
- if (sc == NULL) {
- ret = GSS_S_FAILURE;
- *return_token = NO_TOKEN_SEND;
- @@ -1259,12 +1253,12 @@ cleanup:
- }
- }
- if (ret == GSS_S_COMPLETE) {
- + sc->opened = 1;
- *context_handle = (gss_ctx_id_t)sc->ctx_handle;
- if (sc->internal_name != GSS_C_NO_NAME &&
- src_name != NULL) {
- *src_name = sc->internal_name;
- }
- - release_spnego_ctx(&sc);
- }
- gss_release_buffer(&tmpmin, &mechtok_out);
- if (mechtok_in != GSS_C_NO_BUFFER) {
- @@ -1473,8 +1467,14 @@ spnego_gss_process_context_token(void *c
- const gss_buffer_t token_buffer)
- {
- OM_uint32 ret;
- + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
- +
- + /* SPNEGO doesn't have its own context tokens. */
- + if (!sc->opened)
- + return (GSS_S_DEFECTIVE_TOKEN);
- +
- ret = gss_process_context_token(minor_status,
- - context_handle,
- + sc->ctx_handle,
- token_buffer);
- return (ret);
- @@ -1493,17 +1493,9 @@ spnego_gss_delete_sec_context(void *cont
- if (context_handle == NULL)
- return (GSS_S_FAILURE);
- - /*
- - * If this is still an SPNEGO mech, release it locally.
- - */
- - if (*ctx != NULL &&
- - (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
- - (void) release_spnego_ctx(ctx);
- - } else {
- - ret = gss_delete_sec_context(minor_status,
- - context_handle,
- - output_token);
- - }
- + (void) gss_delete_sec_context(minor_status, &(*ctx)->ctx_handle,
- + output_token);
- + (void) release_spnego_ctx(ctx);
- return (ret);
- }
- @@ -1515,8 +1507,13 @@ spnego_gss_context_time(void *context,
- OM_uint32 *time_rec)
- {
- OM_uint32 ret;
- + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
- +
- + if (sc->ctx_handle == GSS_C_NO_CONTEXT)
- + return (GSS_S_NO_CONTEXT);
- +
- ret = gss_context_time(minor_status,
- - context_handle,
- + sc->ctx_handle,
- time_rec);
- return (ret);
- }
- @@ -1528,9 +1525,20 @@ spnego_gss_export_sec_context(void *cont
- gss_buffer_t interprocess_token)
- {
- OM_uint32 ret;
- + spnego_gss_ctx_id_t sc = *(spnego_gss_ctx_id_t *)context_handle;
- +
- + /* We don't currently support exporting partially established
- + * contexts. */
- + if (!sc->opened)
- + return GSS_S_UNAVAILABLE;
- +
- ret = gss_export_sec_context(minor_status,
- - context_handle,
- + &sc->ctx_handle,
- interprocess_token);
- + if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
- + release_spnego_ctx(&sc);
- + *context_handle = GSS_C_NO_CONTEXT;
- + }
- return (ret);
- }
- @@ -1540,11 +1548,12 @@ spnego_gss_import_sec_context(void *cont
- const gss_buffer_t interprocess_token,
- gss_ctx_id_t *context_handle)
- {
- - OM_uint32 ret;
- - ret = gss_import_sec_context(minor_status,
- - interprocess_token,
- - context_handle);
- - return (ret);
- + /*
- + * Until we implement partial context exports, there are no SPNEGO
- + * exported context tokens, only tokens for underlying mechs. So just
- + * return an error for now.
- + */
- + return GSS_S_UNAVAILABLE;
- }
- OM_uint32
- @@ -1557,19 +1566,51 @@ spnego_gss_inquire_context(void *context
- gss_OID *mech_type,
- OM_uint32 *ctx_flags,
- int *locally_initiated,
- - int *open)
- + int *opened)
- {
- OM_uint32 ret = GSS_S_COMPLETE;
- + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
- - ret = gss_inquire_context(minor_status,
- - context_handle,
- - src_name,
- - targ_name,
- - lifetime_rec,
- - mech_type,
- - ctx_flags,
- - locally_initiated,
- - open);
- + if (src_name != NULL)
- + *src_name = GSS_C_NO_NAME;
- + if (targ_name != NULL)
- + *targ_name = GSS_C_NO_NAME;
- + if (lifetime_rec != NULL)
- + *lifetime_rec = 0;
- + if (mech_type != NULL)
- + *mech_type = (gss_OID)gss_mech_spnego;
- + if (ctx_flags != NULL)
- + *ctx_flags = 0;
- + if (locally_initiated != NULL)
- + *locally_initiated = sc->initiate;
- + if (opened != NULL)
- + *opened = sc->opened;
- +
- + if (sc->ctx_handle != GSS_C_NO_CONTEXT) {
- + ret = gss_inquire_context(minor_status, sc->ctx_handle,
- + src_name, targ_name, lifetime_rec,
- + mech_type, ctx_flags, NULL, NULL);
- + }
- +
- + if (!sc->opened) {
- + /*
- + * We are still doing SPNEGO negotiation, so report SPNEGO as
- + * the OID. After negotiation is complete we will report the
- + * underlying mechanism OID.
- + */
- + if (mech_type != NULL)
- + *mech_type = (gss_OID)gss_mech_spnego;
- +
- + /*
- + * Remove flags we don't support with partially-established
- + * contexts. (Change this to keep GSS_C_TRANS_FLAG if we add
- + * support for exporting partial SPNEGO contexts.)
- + */
- + if (ctx_flags != NULL) {
- + *ctx_flags &= ~GSS_C_PROT_READY_FLAG;
- + *ctx_flags &= ~GSS_C_TRANS_FLAG;
- + }
- + }
- return (ret);
- }
- @@ -1584,8 +1625,13 @@ spnego_gss_wrap_size_limit(void *context
- OM_uint32 *max_input_size)
- {
- OM_uint32 ret;
- + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
- +
- + if (sc->ctx_handle == GSS_C_NO_CONTEXT)
- + return (GSS_S_NO_CONTEXT);
- +
- ret = gss_wrap_size_limit(minor_status,
- - context_handle,
- + sc->ctx_handle,
- conf_req_flag,
- qop_req,
- req_output_size,
Advertisement
Add Comment
Please, Sign In to add comment