content-type: text/plain; charset="utf-8" X-RT-Original-Encoding: utf-8 Content-Length: 8985 Gentle ping. On Tue, Jul 27, 2021 at 11:59 AM Amandeep Gautam wrote: > 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 > 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. >>> >>> >>>