-
Notifications
You must be signed in to change notification settings - Fork 27
gss_wrap/gss_unwrap should work on connection without NEGOTIATE_SEAL #77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
…tion (#71373) * Implement NTLM quirk in NegotiateStreamPal.Encrypt/Decrypt NegotiateStream on non-encrypted connections with NTLM sends the messages in a special `<signature token><plain text message>` format. That's not something that gss_wrap/gss_unwrap would produce. It can be produced through gss_get_mic/gss_verify_mic calls though so let's do that. * Remove MakeSignature/VerifySignature from SspiCli interop The method names were misleading since they wrapped the EncryptMessage and DecryptMessage native APIs and not the MakeSignature/VerifySignature APIs that also exist. * Remove unused sequenceNumber parameters in NegotiateStreamPal.Encrypt/Decrypt The SSPI / GSSAPI providers keep track of the sequence numbers themselves. * Replace NTAuthentication.MakeSignature/VerifySignature with Wrap/Unwrap This maps directly to the semantics of gss_wrap/gss_unwrap methods that are used in many specifications. It replaces the misleading name which in SSPI API is an equivalent of gss_get_mic/gss_verify_mic. It also fixes the declaration to actually decode the buffers both on Windows and Unix. In NTLM the content of the message is sealed and needs to be decoded. Note that previously on Unix the VerifySignature API didn't decode the content. On Windows it did decode the content inside a buffer that was passed as ReadOnlySpan<byte> but it didn't communicate back the offset of the decoded data. The SMTP GSSAPI authentication code was thus reading incorrect data. In case the underlying authentication was Kerberos the data were not encrypted and they were located at the beginning of the buffer so it was not an issue. In case the underlying authentication was NTLM it was looking at the NTLM signature token which luckily happens to always start with the bytes 01 00 00 00. That exactly matched the expected value by accident. * Fix processing of last SMTP GSSAPI token The last token in the GSSAPI SASL authentication mechanism is a bit mask that specifies the supported security protections offered by the server and the maximum token size. The client is supposed to choose one of the protections and reply back. Relax the check to actually support servers that offer anything but "no protection". As long as the server also offers no protection we can choose it. * Update unit test to use the new Wrap/Unwrap APIs * Reset NTLM keys after successful Negotiate authentication Updated the managed NTLM implementation and the fake servers to implement the specification quirk: MS-SPNG section 3.2.5.1 NTLM RC4 Key State for MechListMIC and First Signed Message specifies that the RC4 sealing keys are reset back to the initial state for the first message. Since the managed implementation doesn't expose encryption yet it didn't affect any observable behavior. Likewise the fake servers didn't need this code path yet. * Add GSSAPI authentication test to the loopback SMTP server * Workaround for gssapi/gss-ntlmssp#77 * Expose the confidentiality flag from the native gss_wrap/unwrap APIs * Allow default credentials for NTLM server-side on Linux/macOS
@filipnavara I finally came around to take a second good look at this. So it seem the main issue here is my return of ENOSUP when NTLMSSP_NEGOTIATE_SEAL is not negotiated. In this case I should check if NEGOTIATE_SIGN and NEGOTIATE_KEY_EXCHANGE are instead set and continue as normal calling ntlmv2-sign ... but in order to do that I would need a seal_handle, so I need to always create a seal handle regardless of negotiated flags. Is this what you are suggesting? |
And of curse if NEGOTIATE_SIGN is set we are already guaranteed to have a seal_handle ... so this is just about removing the flag check from ntlm_seal and ntlm_unseal ... |
Yep
I think so. |
@filipnavara should this be conditional to anything? |
Oh wait, I should juts apply the signature, but not seal the content, perhaps ? |
NVM my last comment. It sounds like what we are doing here is that if the app ask to gss_wrap something we wrap it regardless of what we negotiated with the peer. |
Good question. Unfortunately I don't have a good answer to that at the moment. If you have
If you have I am not sure what should be the behavior if you don't have |
seal_handle is always generated regardless, so I am just going to return EINVAL if it is missing. Flags can also be checked by applications so they have all the tools needed to decide whether to use gss_warp() or just gss_get_mic() if the protocol requires a choice be made. |
So according to Issue gssapi#77 we have an interop issue if we prevent the use of gss_wrap when sealing has not been negotiated. On the technical side, whether we negotiate sealing or not we always create a seal handle with RC4. Change behavior to allow applications to still wrap/unwrap data if they want, even though the negotiation marked sealing as not selected. The worst thing that can happen is that the peer application does no like sealed content and bails. Applications that need to avoid seeling should already just use gss_get_mic() anyway and they can check the returned GSS flags to see if sealing was negotiated (returned as GSS_CONF_FLAG), so applications still have all they need to make their choice and be compatible with whatever peer they need to speak to. Thanks to Filip Navara for finding this. Signed-off-by: Simo Sorce <simo@redhat.com>
So according to Issue gssapi#77 we have an interop issue if we prevent the use of gss_wrap when sealing has not been negotiated. On the technical side, whether we negotiate sealing or not we always create a seal handle with RC4. Change behavior to allow applications to still wrap/unwrap data if they want, even though the negotiation marked sealing as not selected. The worst thing that can happen is that the peer application does no like sealed content and bails. Applications that need to avoid seeling should already just use gss_get_mic() anyway and they can check the returned GSS flags to see if sealing was negotiated (returned as GSS_CONF_FLAG), so applications still have all they need to make their choice and be compatible with whatever peer they need to speak to. Thanks to Filip Navara for finding this. Signed-off-by: Simo Sorce <simo@redhat.com>
Btw I wonder if any of the dotnet test suite can be easily run in a container w/o rebuilding all of it, I think it might be a good idea to run at least some dotnet tests when I change gssntlmssp to insure things don't break. |
The dotnet test suites actually have very small coverage right now so it's likely not worth the effort, at least not yet. Some of the tests require complex Docker setup. I started working on expanding them and making it all easier to run but it's pretty slow effort so far. That said, I am aware that there is interest in the area of sharing the testing infrastructure and I regularly discuss that both with the .NET Networking team and other people working on various libraries in the ecosystem... |
So according to Issue #77 we have an interop issue if we prevent the use of gss_wrap when sealing has not been negotiated. On the technical side, whether we negotiate sealing or not we always create a seal handle with RC4. Change behavior to allow applications to still wrap/unwrap data if they want, even though the negotiation marked sealing as not selected. The worst thing that can happen is that the peer application does no like sealed content and bails. Applications that need to avoid seeling should already just use gss_get_mic() anyway and they can check the returned GSS flags to see if sealing was negotiated (returned as GSS_CONF_FLAG), so applications still have all they need to make their choice and be compatible with whatever peer they need to speak to. Thanks to Filip Navara for finding this. Signed-off-by: Simo Sorce <simo@redhat.com>
Uh oh!
There was an error while loading. Please reload this page.
Let's start with a bit of a background.
RFC 2222 originally specified the GSSAPI SASL mechanism for authentication. It didn't specifically tie the mechanism to Kerberos unlike the later RFC 4752 revision. As a last step of the authentication exchange the server sends a token with supported protection schemes and maximum token size to the client. This is encoded with
GSS_Wrap
API withconf_flag == 0
. The client decodes it withGSS_Unwrap
, chooses the preferred protection for rest of communication, and then encodes the reply withGSS_Wrap
and sends it back to server.Microsoft Exchange implements this mechanism for authentication to SMTP, IMAP and POP3 services. It works with Negotiate, Kerberos and NTLM protocols. The NTLM one is particularly tricky because the semantics for
GSS_Warp
/GSS_Unwrap
are not fully covered by the specifications. So I went ahead, set up an Exchange server with Windows AD and tested what is really sent on the wire.In case NEGOTIATE_SEAL is negotiated in the initial authentication the gss-ntlmssp library is already interoperable. The
conf_flag
is ignored (not a surprise since NTLM doesn't have a way to transmit the flag on the wire like Kerberos does), sealing and signing is applied.In case NEGOTIATE_SEAL is NOT negotiated and NEGOTIATE_SIGN+NEGOTIATE_KEY_EXHANGE were negotiated the tokens should still be sealed with the computed key and signed. gss-ntlmssp currently doesn't implement that.
I didn't check the behavior for other flag combinations yet. I am not particularly interested in them because anything without MIC calculation downgrades the security beyond a point where latest Windows versions may reject it.
Notably, I would expect the
GSS_Wrap
behavior to align with the behavior of theEncryptMessage
(NTLM) API on Windows with the data buffer not having theSECBUFFER_READONLY
flag and theconf_flag
flag matching theSECQOP_WRAP_NO_ENCRYPT
flag. That means always sealing the data (at least for NTLM2 w/ key exchange) and ignoring theconf_flag
flag.Confusingly, the NegotiateStream specification is also defined in terms of
GSS_Wrap
/GSS_Unwrap
methods. Unlike the SMTP authentication it behaves differently on NTLM where it sends the messages as<signature token><unsealed message>
ifNEGOTIATE_SEAL
was not specified. This difference is caused by a special NTLM code path in theNegotiateStream
code that passes theSECBUFFER_READONLY
flag to theEncryptMessage
/DecryptMessage
methods. The equivalent GSSAPI transformation is to callgss_get_mic
to produce the<signature token>
and usegss_verify_mic
to verify it. This cannot be expressed withGSS_Wrap
/GSS_Unwrap
directly.The text was updated successfully, but these errors were encountered: