Skip Menu |
 

From: "Amandeep Gautam" <amandeepgautam5@gmail.com>
Subject: Bugs while authenticating to azure files
To: krb5-bugs@mit.edu
Date: Mon, 26 Jul 2021 07:40:39 -0700
Download (untitled) / with headers
text/plain 1.5KiB
Hi,
   We are using MIT krb5 with libsmb2: https://github.com/sahlberg/libsmb2/ and GSS implementation from here: https://github.com/gssapi/gss-ntlmssp. I have attached the packet taces for the following:
-- Packet traces for authentication with azure [tcp_azure.pcap].
-- Packet traces of authentication with windows server (which also requires MIC, encryption) [tcp_new_win_enc_test.pcap].
-- Packet traces for authentication with Samba [tcp_azure_old.pcap]

In authentication with Azure files Samba server, the session setup response from the server (4th packet of 4) has a security blob of length only 9. In function, src/lib/gssapi/spnego/spnego_mech.c: get_negTokenResp we return error code that it is a defective token. As a result of this, we clean up the gss context and then we are not able to retrieve the session key (which would be required for encryption).

I tried the same with the Samba client and it works.

What could be the problem?
-- Is the token defective and clean up of the context is the right approach? In this case, it needs to be handled in another layer. Not sure if that is possible.
-- If we have received the MIC in the 3rd packet of session setup response, do not make it mandatory to send MIC in the 4th packet. Can this be a solution?
-- Do not clean up when we get the defective token from the 4th packet. 

Regards,
Aman

P.S.: I do not have much idea on how the protocol works, and the above understanding is gained by putting working on this issue. Please pardon any mistakes in the bug analysis.
Download tcp_azure.pcap
application/octet-stream 2.5KiB

Message body not shown because it is not plain text.

Download tcp_new_win_enc_test.pcap
application/octet-stream 4.9KiB

Message body not shown because it is not plain text.

Download tcp_azure_old.pcap
application/octet-stream 7.8KiB

Message body not shown because it is not plain text.

Thanks for the comprehensive interop issue report.  I have one question:

Show quoted text
> In function, src/lib/gssapi/spnego/spnego_mech.c: get_negTokenResp we return error code that it is a defective token.

This function should only return a defective token error if the packet has a bad length, which does not appear to be the case.  Is it possible that the error came from handle_mic() at the first else-if clause?

Some background:

* RFC 4178 (SPNEGO) section 5 only requires a mechlistMIC exchange if the negotiated mech is not the most-preferred mech of one of the two parties.  Otherwise the MIC exchange is optional (unless the negotiated mech has no MIC support, in which case it's impossible).

* RFC 4178 section 5 (c) (I) says, for the acceptor processing the final initiator token: "If a mechlistMIC token was included and is correctly verified, GSS_Accept_sec_context() indicates GSS_S_COMPLETE.  The output negotiation message contains a mechlistMIC token and an accept_complete state."

In this exchange both parties prefer NTLMSSP, so one would expect the MIC exchange to be optional.   However, Microsoft imposes an additional constraint for NTLMSSP.  This is both observed and documented; [MS-SPNG] Appendix A says:

    If NTLM authentication is most preferred by the client and the server, and the
    client includes a MIC in AUTHENTICATE_MESSAGE ([MS-NLMP] section
    2.2.1.3), then the mechListMIC field becomes mandatory in order for the
    authentication to succeed. Windows clients in this case send an NTLM token
    instead of an SPNEGO token.

Accordingly, we have a helper mech_requires_mechlistMIC(), so that NTLMSSP can report that it's behaving in a way that causes a SPNEGO MIC to be required.  In our code, the MIC requirement is treated as symmetrical; if we send one, we believe we must receive one, and (following RFC 4178 5.c.I) if we receive one, we send one.

From the packet traces it would appear that the Azure server does not believe a MIC is required (i.e. it is not behaving according to [MS-SPNG] Appendix A) and isn't sending a MIC in response to the initiator's MIC (i.e. it it violates RFC 4178 section 5(c)(I)).  This apparent misbehavior could be accomodated in two ways:

1. Following the hint in [MS-SPNG] Appendix A, the application could send an NTLM token instead of a SPNEGO token.  Generally if a client prefers NTLM, it can only do NTLM, so negotiation isn't required.  However, it may be difficult for an application using MIT krb5 to implement this due to the abstraction boundaries; the application doesn't know that the client is only capable of NTLM and the SPNEGO layer can't generate a non-SPNEGO token.

2. In our SPNEGO layer, a true result from mech_requires_mechlistMIC() could cause a MIC token to be generated, but not required from the other party.
 
Date: Mon, 26 Jul 2021 16:46:42 -0700
To: rt@kerborg-prod-app-1.mit.edu
Subject: Re: [krbdev.mit.edu #9018] Bugs while authenticating to azure files
From: "Amandeep Gautam" <amandeepgautam5@gmail.com>
Download (untitled) / with headers
text/plain 5.4KiB
Hi Greg,
   Thanks for the prompt reply. I think you are right. I mistook mechListMIC set to GSS_C_NO_BUFFER as an invalid token. These 2 are different. I added some more logs and here is more detailed information:

In, init_ctx_cont the buf->length is 9. From init_ctx_cont, we call get_negTokenResp, which returns empty mechListMIC token with status: GSS_S_COMPLETE.
Progressing in init_ctx_cont, we hit the following if-else block


        ....
        } else if (!sc->mech_complete ||
                   (sc->mic_reqd &&
                    (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
                /* Not obviously done; we may decide we're done later in
                 * init_ctx_call_init or handle_mic. */
                *tokflag = CONT_TOKEN_SEND;
                ret = GSS_S_COMPLETE;
        } else {
                /* mech finished on last pass and no MIC required, so done. */
                *tokflag = NO_TOKEN_SEND;
                ret = GSS_S_COMPLETE;
        }

        ...

After returning from here, we return back to spnego_gss_init_sec_context function (from where we called init_ctx_cont). In this function, the handle_mic call leads to the problem. Code snippet for the call:

        ...
        negState = ACCEPT_INCOMPLETE;
        if (spnego_ctx->mech_complete &&
            (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
                        ret = handle_mic(minor_status,
                                         mechListMIC_in,
                                         (mechtok_out.length != 0),
                                         spnego_ctx, &mechListMIC_out,
                                         &negState, &send_token);
                        if (HARD_ERROR(ret))
                                goto cleanup;
        ...

To be more specific, in handle_mic function, I hit the following section:

        ...
        } else if (sc->mic_reqd && !send_mechtok) {
                /*
                 * If the peer sends the final mechanism token, it
                 * must send the MIC with that token if the
                 * negotiation requires MICs.
                 */
                *negState = REJECT;
                *tokflag = ERROR_TOKEN_SEND;
                return GSS_S_DEFECTIVE_TOKEN;
        }
        ...

So, I think you were right.

About, the suggestions that were made, I think what you are saying is that (1) is not possible (or very hard). If you can add more details on (2), I will be happy to make the change and test it.
Basically, I do not understand if the problem is
(a) The assumption that: if we send one, we should receive one? OR
(b) Sending MIC in the first place.

Regards,
Aman


On Mon, Jul 26, 2021 at 11:53 AM Greg Hudson via RT <rt@kerborg-prod-app-1.mit.edu> wrote:
Show quoted text
Thanks for the comprehensive interop issue report.  I have one question:

> In function, src/lib/gssapi/spnego/spnego_mech.c: get_negTokenResp we return
error code that it is a defective token.

This function should only return a defective token error if the packet has a
bad length, which does not appear to be the case.  Is it possible that the
error came from handle_mic() at the first else-if clause?

Some background:

* RFC 4178 (SPNEGO) section 5 only requires a mechlistMIC exchange if the
negotiated mech is not the most-preferred mech of one of the two parties.
Otherwise the MIC exchange is optional (unless the negotiated mech has no MIC
support, in which case it's impossible).

* RFC 4178 section 5 (c) (I) says, for the acceptor processing the final
initiator token: "If a mechlistMIC token was included and is correctly
verified, GSS_Accept_sec_context() indicates GSS_S_COMPLETE.  The output
negotiation message contains a mechlistMIC token and an accept_complete
state."

In this exchange both parties prefer NTLMSSP, so one would expect the MIC
exchange to be optional.   However, Microsoft imposes an additional constraint
for NTLMSSP.  This is both observed and documented; [MS-SPNG] Appendix A says:

    If NTLM authentication is most preferred by the client and the server, and
the
    client includes a MIC in AUTHENTICATE_MESSAGE ([MS-NLMP] section
    2.2.1.3), then the mechListMIC field becomes mandatory in order for the
    authentication to succeed. Windows clients in this case send an NTLM token
    instead of an SPNEGO token.

Accordingly, we have a helper mech_requires_mechlistMIC(), so that NTLMSSP can
report that it's behaving in a way that causes a SPNEGO MIC to be required.
In our code, the MIC requirement is treated as symmetrical; if we send one, we
believe we must receive one, and (following RFC 4178 5.c.I) if we receive one,
we send one.

From the packet traces it would appear that the Azure server does not believe
a MIC is required (i.e. it is not behaving according to [MS-SPNG] Appendix A)
and isn't sending a MIC in response to the initiator's MIC (i.e. it it
violates RFC 4178 section 5(c)(I)).  This apparent misbehavior could be
accomodated in two ways:

1. Following the hint in [MS-SPNG] Appendix A, the application could send an
NTLM token instead of a SPNEGO token.  Generally if a client prefers NTLM, it
can only do NTLM, so negotiation isn't required.  However, it may be difficult
for an application using MIT krb5 to implement this due to the abstraction
boundaries; the application doesn't know that the client is only capable of
NTLM and the SPNEGO layer can't generate a non-SPNEGO token.

2. In our SPNEGO layer, a true result from mech_requires_mechlistMIC() could
cause a MIC token to be generated, but not required from the other party.


To: rt@kerborg-prod-app-1.mit.edu, krb5-bugs@mit.edu
From: "Amandeep Gautam" <amandeepgautam5@gmail.com>
Subject: Re: [krbdev.mit.edu #9018] Bugs while authenticating to azure files
Date: Tue, 27 Jul 2021 11:59:31 -0700
Download (untitled) / with headers
text/plain 8.3KiB
Download (untitled) / with headers
text/html 12.6KiB
Sending my response to wider audience as I forgot to add the main mailing list initially.

Hi Greg,
   Thanks for the prompt reply. I think you are right. I mistook mechListMIC set to GSS_C_NO_BUFFER as an invalid token. These 2 are different. I added some more logs and here is more detailed information:

In, init_ctx_cont the buf->length is 9. From init_ctx_cont, we call get_negTokenResp, which returns empty mechListMIC token with status: GSS_S_COMPLETE.
Progressing in init_ctx_cont, we hit the following if-else block


        ....
        } else if (!sc->mech_complete ||
                   (sc->mic_reqd &&
                    (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
                /* Not obviously done; we may decide we're done later in
                 * init_ctx_call_init or handle_mic. */
                *tokflag = CONT_TOKEN_SEND;
                ret = GSS_S_COMPLETE;
        } else {
                /* mech finished on last pass and no MIC required, so done. */
                *tokflag = NO_TOKEN_SEND;
                ret = GSS_S_COMPLETE;
        }

        ...

After returning from here, we return back to spnego_gss_init_sec_context function (from where we called init_ctx_cont). In this function, the handle_mic call leads to the problem. Code snippet for the call:

        ...
        negState = ACCEPT_INCOMPLETE;
        if (spnego_ctx->mech_complete &&
            (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
                        ret = handle_mic(minor_status,
                                         mechListMIC_in,
                                         (mechtok_out.length != 0),
                                         spnego_ctx, &mechListMIC_out,
                                         &negState, &send_token);
                        if (HARD_ERROR(ret))
                                goto cleanup;
        ...

To be more specific, in handle_mic function, I hit the following section:

        ...
        } else if (sc->mic_reqd && !send_mechtok) {
                /*
                 * If the peer sends the final mechanism token, it
                 * must send the MIC with that token if the
                 * negotiation requires MICs.
                 */
                *negState = REJECT;
                *tokflag = ERROR_TOKEN_SEND;
                return GSS_S_DEFECTIVE_TOKEN;
        }
        ...

So, I think you were right.

About the suggestions that were made, I think what you are saying is that (1) is not possible (or very hard). If you can add more details on (2), I will be happy to make the change and test it.
Basically, I do not understand if the problem is
(a) The assumption that: if we send one, we should receive one? OR
(b) Sending MIC in the first place.

Regards,
Aman

On Mon, Jul 26, 2021 at 4:46 PM Amandeep Gautam <amandeepgautam5@gmail.com> wrote:
Show quoted text
Hi Greg,
   Thanks for the prompt reply. I think you are right. I mistook mechListMIC set to GSS_C_NO_BUFFER as an invalid token. These 2 are different. I added some more logs and here is more detailed information:

In, init_ctx_cont the buf->length is 9. From init_ctx_cont, we call get_negTokenResp, which returns empty mechListMIC token with status: GSS_S_COMPLETE.
Progressing in init_ctx_cont, we hit the following if-else block


        ....
        } else if (!sc->mech_complete ||
                   (sc->mic_reqd &&
                    (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
                /* Not obviously done; we may decide we're done later in
                 * init_ctx_call_init or handle_mic. */
                *tokflag = CONT_TOKEN_SEND;
                ret = GSS_S_COMPLETE;
        } else {
                /* mech finished on last pass and no MIC required, so done. */
                *tokflag = NO_TOKEN_SEND;
                ret = GSS_S_COMPLETE;
        }

        ...

After returning from here, we return back to spnego_gss_init_sec_context function (from where we called init_ctx_cont). In this function, the handle_mic call leads to the problem. Code snippet for the call:

        ...
        negState = ACCEPT_INCOMPLETE;
        if (spnego_ctx->mech_complete &&
            (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
                        ret = handle_mic(minor_status,
                                         mechListMIC_in,
                                         (mechtok_out.length != 0),
                                         spnego_ctx, &mechListMIC_out,
                                         &negState, &send_token);
                        if (HARD_ERROR(ret))
                                goto cleanup;
        ...

To be more specific, in handle_mic function, I hit the following section:

        ...
        } else if (sc->mic_reqd && !send_mechtok) {
                /*
                 * If the peer sends the final mechanism token, it
                 * must send the MIC with that token if the
                 * negotiation requires MICs.
                 */
                *negState = REJECT;
                *tokflag = ERROR_TOKEN_SEND;
                return GSS_S_DEFECTIVE_TOKEN;
        }
        ...

So, I think you were right.

About, the suggestions that were made, I think what you are saying is that (1) is not possible (or very hard). If you can add more details on (2), I will be happy to make the change and test it.
Basically, I do not understand if the problem is
(a) The assumption that: if we send one, we should receive one? OR
(b) Sending MIC in the first place.

Regards,
Aman


On Mon, Jul 26, 2021 at 11:53 AM Greg Hudson via RT <rt@kerborg-prod-app-1.mit.edu> wrote:
Thanks for the comprehensive interop issue report.  I have one question:

> In function, src/lib/gssapi/spnego/spnego_mech.c: get_negTokenResp we return
error code that it is a defective token.

This function should only return a defective token error if the packet has a
bad length, which does not appear to be the case.  Is it possible that the
error came from handle_mic() at the first else-if clause?

Some background:

* RFC 4178 (SPNEGO) section 5 only requires a mechlistMIC exchange if the
negotiated mech is not the most-preferred mech of one of the two parties.
Otherwise the MIC exchange is optional (unless the negotiated mech has no MIC
support, in which case it's impossible).

* RFC 4178 section 5 (c) (I) says, for the acceptor processing the final
initiator token: "If a mechlistMIC token was included and is correctly
verified, GSS_Accept_sec_context() indicates GSS_S_COMPLETE.  The output
negotiation message contains a mechlistMIC token and an accept_complete
state."

In this exchange both parties prefer NTLMSSP, so one would expect the MIC
exchange to be optional.   However, Microsoft imposes an additional constraint
for NTLMSSP.  This is both observed and documented; [MS-SPNG] Appendix A says:

    If NTLM authentication is most preferred by the client and the server, and
the
    client includes a MIC in AUTHENTICATE_MESSAGE ([MS-NLMP] section
    2.2.1.3), then the mechListMIC field becomes mandatory in order for the
    authentication to succeed. Windows clients in this case send an NTLM token
    instead of an SPNEGO token.

Accordingly, we have a helper mech_requires_mechlistMIC(), so that NTLMSSP can
report that it's behaving in a way that causes a SPNEGO MIC to be required.
In our code, the MIC requirement is treated as symmetrical; if we send one, we
believe we must receive one, and (following RFC 4178 5.c.I) if we receive one,
we send one.

From the packet traces it would appear that the Azure server does not believe
a MIC is required (i.e. it is not behaving according to [MS-SPNG] Appendix A)
and isn't sending a MIC in response to the initiator's MIC (i.e. it it
violates RFC 4178 section 5(c)(I)).  This apparent misbehavior could be
accomodated in two ways:

1. Following the hint in [MS-SPNG] Appendix A, the application could send an
NTLM token instead of a SPNEGO token.  Generally if a client prefers NTLM, it
can only do NTLM, so negotiation isn't required.  However, it may be difficult
for an application using MIT krb5 to implement this due to the abstraction
boundaries; the application doesn't know that the client is only capable of
NTLM and the SPNEGO layer can't generate a non-SPNEGO token.

2. In our SPNEGO layer, a true result from mech_requires_mechlistMIC() could
cause a MIC token to be generated, but not required from the other party.


Subject: Re: [krbdev.mit.edu #9018] Bugs while authenticating to azure files
Date: Wed, 28 Jul 2021 13:44:15 -0700
To: rt@kerborg-prod-app-1.mit.edu, krb5-bugs@mit.edu
From: "Amandeep Gautam" <amandeepgautam5@gmail.com>
Download (untitled) / with headers
text/plain 8.7KiB
Gentle ping.

On Tue, Jul 27, 2021 at 11:59 AM Amandeep Gautam <amandeepgautam5@gmail.com> wrote:
Show quoted text
Sending my response to wider audience as I forgot to add the main mailing list initially.

Hi Greg,
   Thanks for the prompt reply. I think you are right. I mistook mechListMIC set to GSS_C_NO_BUFFER as an invalid token. These 2 are different. I added some more logs and here is more detailed information:

In, init_ctx_cont the buf->length is 9. From init_ctx_cont, we call get_negTokenResp, which returns empty mechListMIC token with status: GSS_S_COMPLETE.
Progressing in init_ctx_cont, we hit the following if-else block


        ....
        } else if (!sc->mech_complete ||
                   (sc->mic_reqd &&
                    (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
                /* Not obviously done; we may decide we're done later in
                 * init_ctx_call_init or handle_mic. */
                *tokflag = CONT_TOKEN_SEND;
                ret = GSS_S_COMPLETE;
        } else {
                /* mech finished on last pass and no MIC required, so done. */
                *tokflag = NO_TOKEN_SEND;
                ret = GSS_S_COMPLETE;
        }

        ...

After returning from here, we return back to spnego_gss_init_sec_context function (from where we called init_ctx_cont). In this function, the handle_mic call leads to the problem. Code snippet for the call:

        ...
        negState = ACCEPT_INCOMPLETE;
        if (spnego_ctx->mech_complete &&
            (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
                        ret = handle_mic(minor_status,
                                         mechListMIC_in,
                                         (mechtok_out.length != 0),
                                         spnego_ctx, &mechListMIC_out,
                                         &negState, &send_token);
                        if (HARD_ERROR(ret))
                                goto cleanup;
        ...

To be more specific, in handle_mic function, I hit the following section:

        ...
        } else if (sc->mic_reqd && !send_mechtok) {
                /*
                 * If the peer sends the final mechanism token, it
                 * must send the MIC with that token if the
                 * negotiation requires MICs.
                 */
                *negState = REJECT;
                *tokflag = ERROR_TOKEN_SEND;
                return GSS_S_DEFECTIVE_TOKEN;
        }
        ...

So, I think you were right.

About the suggestions that were made, I think what you are saying is that (1) is not possible (or very hard). If you can add more details on (2), I will be happy to make the change and test it.
Basically, I do not understand if the problem is
(a) The assumption that: if we send one, we should receive one? OR
(b) Sending MIC in the first place.

Regards,
Aman

On Mon, Jul 26, 2021 at 4:46 PM Amandeep Gautam <amandeepgautam5@gmail.com> wrote:
Hi Greg,
   Thanks for the prompt reply. I think you are right. I mistook mechListMIC set to GSS_C_NO_BUFFER as an invalid token. These 2 are different. I added some more logs and here is more detailed information:

In, init_ctx_cont the buf->length is 9. From init_ctx_cont, we call get_negTokenResp, which returns empty mechListMIC token with status: GSS_S_COMPLETE.
Progressing in init_ctx_cont, we hit the following if-else block


        ....
        } else if (!sc->mech_complete ||
                   (sc->mic_reqd &&
                    (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
                /* Not obviously done; we may decide we're done later in
                 * init_ctx_call_init or handle_mic. */
                *tokflag = CONT_TOKEN_SEND;
                ret = GSS_S_COMPLETE;
        } else {
                /* mech finished on last pass and no MIC required, so done. */
                *tokflag = NO_TOKEN_SEND;
                ret = GSS_S_COMPLETE;
        }

        ...

After returning from here, we return back to spnego_gss_init_sec_context function (from where we called init_ctx_cont). In this function, the handle_mic call leads to the problem. Code snippet for the call:

        ...
        negState = ACCEPT_INCOMPLETE;
        if (spnego_ctx->mech_complete &&
            (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
                        ret = handle_mic(minor_status,
                                         mechListMIC_in,
                                         (mechtok_out.length != 0),
                                         spnego_ctx, &mechListMIC_out,
                                         &negState, &send_token);
                        if (HARD_ERROR(ret))
                                goto cleanup;
        ...

To be more specific, in handle_mic function, I hit the following section:

        ...
        } else if (sc->mic_reqd && !send_mechtok) {
                /*
                 * If the peer sends the final mechanism token, it
                 * must send the MIC with that token if the
                 * negotiation requires MICs.
                 */
                *negState = REJECT;
                *tokflag = ERROR_TOKEN_SEND;
                return GSS_S_DEFECTIVE_TOKEN;
        }
        ...

So, I think you were right.

About, the suggestions that were made, I think what you are saying is that (1) is not possible (or very hard). If you can add more details on (2), I will be happy to make the change and test it.
Basically, I do not understand if the problem is
(a) The assumption that: if we send one, we should receive one? OR
(b) Sending MIC in the first place.

Regards,
Aman


On Mon, Jul 26, 2021 at 11:53 AM Greg Hudson via RT <rt@kerborg-prod-app-1.mit.edu> wrote:
Thanks for the comprehensive interop issue report.  I have one question:

> In function, src/lib/gssapi/spnego/spnego_mech.c: get_negTokenResp we return
error code that it is a defective token.

This function should only return a defective token error if the packet has a
bad length, which does not appear to be the case.  Is it possible that the
error came from handle_mic() at the first else-if clause?

Some background:

* RFC 4178 (SPNEGO) section 5 only requires a mechlistMIC exchange if the
negotiated mech is not the most-preferred mech of one of the two parties.
Otherwise the MIC exchange is optional (unless the negotiated mech has no MIC
support, in which case it's impossible).

* RFC 4178 section 5 (c) (I) says, for the acceptor processing the final
initiator token: "If a mechlistMIC token was included and is correctly
verified, GSS_Accept_sec_context() indicates GSS_S_COMPLETE.  The output
negotiation message contains a mechlistMIC token and an accept_complete
state."

In this exchange both parties prefer NTLMSSP, so one would expect the MIC
exchange to be optional.   However, Microsoft imposes an additional constraint
for NTLMSSP.  This is both observed and documented; [MS-SPNG] Appendix A says:

    If NTLM authentication is most preferred by the client and the server, and
the
    client includes a MIC in AUTHENTICATE_MESSAGE ([MS-NLMP] section
    2.2.1.3), then the mechListMIC field becomes mandatory in order for the
    authentication to succeed. Windows clients in this case send an NTLM token
    instead of an SPNEGO token.

Accordingly, we have a helper mech_requires_mechlistMIC(), so that NTLMSSP can
report that it's behaving in a way that causes a SPNEGO MIC to be required.
In our code, the MIC requirement is treated as symmetrical; if we send one, we
believe we must receive one, and (following RFC 4178 5.c.I) if we receive one,
we send one.

From the packet traces it would appear that the Azure server does not believe
a MIC is required (i.e. it is not behaving according to [MS-SPNG] Appendix A)
and isn't sending a MIC in response to the initiator's MIC (i.e. it it
violates RFC 4178 section 5(c)(I)).  This apparent misbehavior could be
accomodated in two ways:

1. Following the hint in [MS-SPNG] Appendix A, the application could send an
NTLM token instead of a SPNEGO token.  Generally if a client prefers NTLM, it
can only do NTLM, so negotiation isn't required.  However, it may be difficult
for an application using MIT krb5 to implement this due to the abstraction
boundaries; the application doesn't know that the client is only capable of
NTLM and the SPNEGO layer can't generate a non-SPNEGO token.

2. In our SPNEGO layer, a true result from mech_requires_mechlistMIC() could
cause a MIC token to be generated, but not required from the other party.


On Tue Jul 27 15:00:53 2021, amandeepgautam5@gmail.com wrote:
Show quoted text
Sending my response to wider audience as I forgot to add the main mailing list initially.
Although that's a good instinct for most public lists, it isn't necessary for this one.  krb5-bugs@mit.edu is fed from the bug tracker and doesn't accept mail directly.
Show quoted text
If you can add more details on (2), I will be happy to make the change and test it.
If mech_requires_mechlistMIC() returns true, we want to send a MIC (because some Windows servers erroneously require one) but not require receiving one (because apparently some Windows servers erroneously don't send one if they receive one).  Unfortunately, this will require some pretty close attention to detail, as the code currently assumes symmetric MIC requirements.  One option is to split the mec_reqd flag into separate send and receive flags, but then each piece of code that uses it has to be analyzed for which half to pay attention to.  The other is to move the mech_requires_mechlistMIC() check to the code that decides whether to send a MIC; that, too requires, some pretty close attention to the state machine.

I can't really provide more guidance without doing the work myself.  SPNEGO as specified is very complicated, and SPNEGO as badly implemented by other endpoints is more so.