To: | rt@krbdev.mit.edu |
Subject: | SPNEGO init/accept output parameter bugs |
Date: | Tue, 05 Nov 2019 13:47:37 -0500 |
From: | ghudson@mit.edu |
gss_init_sec_context() and gss_accept_sec_context() both output a token and either create or modify a security context. There are some additional outputs: actual_mech_type, ret_flags, and time_rec for gss_init_sec_context(), and src_name, mech_type, ret_flags, time_rec, and delegated_cred_handle for gss_accept_sec_context(). RFC 2744 sections 5.1 and 5.19 define some rules for how these parameters should be set when GSS_S_CONTINUE_NEEDED is returned.
Although the mechglue initializes most output parameters, it does not do so for ret_flags and time_rec. This is a separate bug, which impacts the following SPNEGO misbehaviors; if SPNEGO does not set these output parameters, the caller's value will remain unchanged.
The current SPNEGO implementation has the following bugs related to the additional output parameters:
* gss_init_sec_context() only sets ret_flags if the underlying mech is called and returns GSS_S_COMPLETE, or if SPNEGO itself will return GSS_S_COMPLETE. gss_accept_sec_context() initializes ret_flags to 0, and only sets ret_flags to another value if the underlying mech is called and returns GSS_S_COMPLETE; ret_flags could remain as 0 on SPNEGO's final return from gss_accept_sec_context(), if the MIC exchange forces an extra round trip.
* gss_init_sec_context() and gss_accept_sec_context() do not filter out PROT_READY from ret_flags as required by RFC 4178.
* gss_init_sec_context() and gss_accept_sec_context() pass time_rec directly to the underlying mech. gss_accept_sec_context() initializes time_rec to 0 but gss_init_sec_context() does not. Neither function will set time_rec to a meaningful value if the underlying mech is not called on the final SPNEGO call due to the MIC exchange. Note that time_rec is relative to the current time; if SPNEGO records time_rec from the underlying mech at one step and reports it to the caller at a later step, the time differential between steps could render that value inaccurate. SPNEGO could either adjust the value by the differential, or call gss_context_time() on the underlying context.
* gss_accept_sec_context() initializes mech_type to GSS_C_NO_OID and passes it directly to the underlying mech. If the underlying mech is not called on the final step, mech_type will remain as GSS_C_NO_OID.
* gss_accept_sec_context() initializes delegated_cred_handle to GSS_C_NO_CREDENTIAL and passes it directly to the underlying mech. SPNEGO could report a delegated cred to the caller too early (before the MIC exchange) and not on the final call.
* gss_accept_sec_context() could leak a value of src_name (recorded as sc->internal_name) if the underlying mech reports it twice. RFC 2744 doesn't explicitly prohibit this, although it seems like it should be unusual.
Although the mechglue initializes most output parameters, it does not do so for ret_flags and time_rec. This is a separate bug, which impacts the following SPNEGO misbehaviors; if SPNEGO does not set these output parameters, the caller's value will remain unchanged.
The current SPNEGO implementation has the following bugs related to the additional output parameters:
* gss_init_sec_context() only sets ret_flags if the underlying mech is called and returns GSS_S_COMPLETE, or if SPNEGO itself will return GSS_S_COMPLETE. gss_accept_sec_context() initializes ret_flags to 0, and only sets ret_flags to another value if the underlying mech is called and returns GSS_S_COMPLETE; ret_flags could remain as 0 on SPNEGO's final return from gss_accept_sec_context(), if the MIC exchange forces an extra round trip.
* gss_init_sec_context() and gss_accept_sec_context() do not filter out PROT_READY from ret_flags as required by RFC 4178.
* gss_init_sec_context() and gss_accept_sec_context() pass time_rec directly to the underlying mech. gss_accept_sec_context() initializes time_rec to 0 but gss_init_sec_context() does not. Neither function will set time_rec to a meaningful value if the underlying mech is not called on the final SPNEGO call due to the MIC exchange. Note that time_rec is relative to the current time; if SPNEGO records time_rec from the underlying mech at one step and reports it to the caller at a later step, the time differential between steps could render that value inaccurate. SPNEGO could either adjust the value by the differential, or call gss_context_time() on the underlying context.
* gss_accept_sec_context() initializes mech_type to GSS_C_NO_OID and passes it directly to the underlying mech. If the underlying mech is not called on the final step, mech_type will remain as GSS_C_NO_OID.
* gss_accept_sec_context() initializes delegated_cred_handle to GSS_C_NO_CREDENTIAL and passes it directly to the underlying mech. SPNEGO could report a delegated cred to the caller too early (before the MIC exchange) and not on the final call.
* gss_accept_sec_context() could leak a value of src_name (recorded as sc->internal_name) if the underlying mech reports it twice. RFC 2744 doesn't explicitly prohibit this, although it seems like it should be unusual.