-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
NTLM support in Android #32680
Comments
What makes the NTLM auth work in the old http handler? |
Previously termux krb5 package was used from their main set to compile corefx: https://github.com/termux/termux-packages/tree/master/packages/krb5. Current android build scripts are little out of sync (mainly they haven't been modernized with others), but I think if termux is an option in your build process, that package should work. |
If I had to guess it's the fact that |
@karelz we need a decision on how we can support NTLM on Android. The proposed solution is to include a fork of krb5 package in |
re: libkrb5 - took this as a motivation and renewed the android support; with #32800 and accompanying PRs, we were able to build libraries with kerberos support. I have not tested SocketsHttpHandler on the device but the compilation finds gssapi headers and links with library. Packages containing after the arm64 compilation:
|
@marek-safar @wfurt @davidsh may have more thoughts on general authentication and Kerberos integration into |
Strictly speaking 'libkrb5' doesn't support NTLM; either raw NTLM nor 'Negotiate' protocol where Kerberos falls back to NTLM. On Linux that requires a separate 'gss-ntlmssp' package to be installed which is a plug in to the GSS-API that 'libkrb5' generally supports on Linux. On MacOS, it turns out that their 'libkrb5' bundle includes NTLM support. But that is also because MacOS ships the Heimdal implementation of the GSS-API and not the MIT Kerberos implementation that Linux distros support.
I think we need to discuss this further. I'm currently not a fan of .NET Libraries owning a forked copy of 'libkrb5'. |
@davidsh Android is not really Linux and this is not the only area where we'll hit the difference (just FYI). How would you like to discuss this further? Are you fine to come up with a proposal how you would like to solve it? |
Also old curl handler had it's own implementation. If anything that may be a better path than maintaining Kerberos fork. (unless we need Kerberos as well) |
The managed CurlHandler didn't have any implementation. But the native 'libcurl' library did bundle an implementation of raw NTLM as well as 'Negotiate' (Kerberos ONLY, no fallback to NTLM) support. I don't see any advantage of using 'libcurl' for this. |
I was not proposing to use libcurl but more to take maybe a similar approach. e.g. code NTLM support instead of depending on Kerberos/GSSAPI. That can be optional on platforms where Kerberos is not available or does not work (like OSX) |
Triage: @steveisok @marek-safar we need clarification if this is targeted at Android native handler or Overall, it would be best if you can provide proposal what you plan to do. |
In this instance it's for Ideally, this would be about both NTLM & Negotiate/Kerb if we can get it. Apparently, libkrb5 has a dependency on openssl, so this is likely going to be wrapped up in a larger discussion. |
if packaging native krb bits would be an option, we would also need https://packages.ubuntu.com/bionic/gss-ntlmssp @am11. |
Personally, it seems to me like a big hammer to package for NTLM support in |
There have been several customers reporting this as regression when we drop the support for it in the non-default handler (see https://developercommunity.visualstudio.com/content/problem/756697/last-visual-studio-update-brakes-ntlm-authenticati.html). The default handler is not ".net compatible" handler and there could be legit reasons why some developers prefer it over the native one. /cc @karelz |
@wfurt, I built their master branch on Debian: https://pagure.io/gssntlmssp. The
then to build: full dependency tree is available here: https://paste2.org/YgwHM5L3. Haven't tried on Android yet, but I am anticipating some challenges in dependency acquisition from termux. IMHO, this package should be ported to termux (with upstream patches later on) rather than an isolated fork, mainly to DRY the effort and avoid extra work of keeping up with upstream etc. |
This doesn't only affect Http but also SqlClient where we had multiple customers reporting problems: |
Tried building gssntlmssp in Android rootfs and hit the first blocker; looks like we would need to port Samba's libwbclient (+its dependencies https://gitlab.com/samba-team/samba/-/blob/master/nsswitch/libwbclient/wscript), which is a required dependency of gssntlmssp and missing from termux: termux/termux-packages#4517. # on Ubuntu / Debian
git clone https://pagure.io/gssntlmssp.git
cd gssntlmssp
curl -sSLO https://raw.githubusercontent.com/am11/runtime/feature/gssntlmssp-build/tmp/build.sh
chmod +x build.sh
./build.sh # takes about 1.5 to 2 minutes to setup the rootfs (idempotently) currently failing in |
I have managed implementation of NTLMv2. SPNEGO will need little bit more work but it looks pretty simple. I'll move forward unless we come up with better plan. It assumes that platform can do md4 as that is not available as managed API. |
@wfurt is that a typo or do you really mean MD4 instead of 5? The former is not available on Android. |
Yes, NTLM uses MD4 and RC4 (iirc). |
(This is one of the reasons why I said...)
|
@SteveSyfuhs it'd be nice if just Kerberos/Negotiate were enough for these customers, but the scenarios I've seen revolve mostly about in-house usage where people have an app that needs to communicate with an MSSQL server or an IIS website that they secured with "Windows" authentication. I doubt (but don't know for sure) that these devices have access to the KDC in all cases, so NTLM is kinda the only option for them. Given how broken MD4 already is, including an internal managed implementation might be doable since it's not really a "security" API anymore 😄 Btw. I don't think this issue means we should only implement NTLM, if we can get Kerberos to work that'd be awesome. |
the algorithm uses md4(password) instead of password directly. I guess that provides predictable length and gives fixed bit set. I don't think this would be major weakness for typical password. On MacOS I use crypto directly (ressl/openssl) [DllImport("libcrypto", EntryPoint = "MD4_Init")]
private static extern unsafe int MD4_Init(void* ctx); This could be hidden somewhere in PAL IMHO. e.g. C implementation with good license would be sufficient. There is no need for rc4 in v2. It uses md5_Hmac for the actual authentication part. And there is public API for it already. While I agree with @SteveSyfuhs 's arguments, it still comes as platform gap on macOS (#887). |
Just to be clear "Windows" authentication supports Kerberos.
Before introducing a seriously insecure authentication protocol, it might be wise for folks to have something to back up this claim. I recognize line of sight issues can be serious, but also let's apply some reality to the situation before adding awful protocols that shouldn't exist anymore. :) |
@wfurt MD5 is available so it sounds like NTLMv2 would work :) @SteveSyfuhs I hear you and I think we should definitely advise people to use Kerberos wherever possible but the reaction we got when we accidentally broke NTLM on Android last time was quite substantial: https://developercommunity.visualstudio.com/content/problem/756697/last-visual-studio-update-brakes-ntlm-authenticati.html |
Moving it to 7.0. |
I would REALLY like to see IWA support on Android. So far I haven't found any way to make this work. For iOS I can use the NSUrlSessionHandler, but if SocketsHttpHandler got this support, it would make the story simpler. Below are my results from testing various handlers and various platforms. The inconsistencies across platforms are pretty glaring, and makes writing x-plat code really hard. UWP:- Xamarin.iOS:
Xamarin.Android:
.net6-android
.NET 5 Console (Windows):
Test code used: using System;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
namespace IwaTests
{
static class Tests
{
private const string serverUri = "myserver";
private const string username = "username";
private const string password = "password";
private const string domain = "domain";
private static NetworkCredential credential = new NetworkCredential(username, password, domain);
public static void Log(string message, Exception err = null)
{
System.Diagnostics.Debug.Write("****** " + message);
if (err != null)
System.Diagnostics.Debug.Write(" ERROR: " + err.Message);
System.Diagnostics.Debug.WriteLine(string.Empty);
}
public static async void TestAll()
{
Log("Testing HttpClientHandler");
try { await TestIwaHttpClientHandler(); }
catch(Exception ex) { Log("HttpClientHandler: ", ex); }
#if NETCOREAPP || __IOS__ || __ANDROID__
Log("Testing SocketsHttpHandler");
try { await TestIwaSocketsHttpHandler(); }
catch(Exception ex) { Log("SocketsHttpHandler: ", ex); }
#endif
#if __ANDROID__
Log("Testing AndroidClientHandler");
try { await TestIwaAndroidClientHandler(); }
catch (Exception ex) { Log("AndroidClientHandler: ", ex); }
Log("Testing MonoWebRequestHandler");
try { await TestIwaMonoWebRequestHandler(); }
catch (Exception ex) { Log("MonoWebRequestHandler: ", ex); }
#if !NETCOREAPP
Log("Testing ModernHttpClient");
try { await TestIwaModernHttpHandler(); }
catch (Exception ex) { Log("ModernHttpClient: ", ex); }
#endif
#endif
#if __IOS__
Log("Testing NSUrlSessionHandler");
try { await TestIwaNSUrlSessionHandler(); }
catch (Exception ex) { Log("NSUrlSessionHandler: ", ex); }
#endif
}
public static Task<HttpResponseMessage> TestIwaHttpClientHandler()
{
var handler = new HttpClientHandler();
handler.Credentials = credential;
var client= new HttpClient(handler);
return TestIwa(handler);
}
#if __IOS__
public static Task<HttpResponseMessage> TestIwaNSUrlSessionHandler()
{
var handler = new NSUrlSessionHandler();
handler.Credentials = credential;
return TestIwa(handler);
}
#endif
#if __ANDROID__
public static Task<HttpResponseMessage> TestIwaAndroidClientHandler()
{
var handler = new Xamarin.Android.Net.AndroidClientHandler();
handler.Credentials = credential;
return TestIwa(handler);
}
public static Task<HttpResponseMessage> TestIwaMonoWebRequestHandler()
{
// Based on https://github.com/EgorBo/NtlmHttpHandler/blob/master/NtlmHttpHandlerFactory.cs
Type monoHandlerType = Type.GetType("System.Net.Http.MonoWebRequestHandler, System.Net.Http");
ConstructorInfo[] internalMonoHandlerCtors = monoHandlerType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
object internalMonoHandler = internalMonoHandlerCtors[0].Invoke(null);
ConstructorInfo[] httpClientHandlerCtors =
typeof(HttpClientHandler).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
var handler = (HttpClientHandler)httpClientHandlerCtors[0].Invoke(new[] { internalMonoHandler });
handler.Credentials = credential;
return TestIwa(handler);
}
#if !NETCOREAPP
public static Task<HttpResponseMessage> TestIwaModernHttpHandler()
{
var handler = new ModernHttpClient.NativeMessageHandler();
handler.Credentials = credential;
return TestIwa(handler);
}
#endif
#endif
#if NETCOREAPP || __IOS__ || __ANDROID__
public static Task<HttpResponseMessage> TestIwaSocketsHttpHandler()
{
var handler = new HttpClientHandler();
handler.Credentials = credential;
return TestIwa(handler);
}
#endif
public static async Task<HttpResponseMessage> TestIwa(HttpMessageHandler handler)
{
var msg = new HttpRequestMessage(HttpMethod.Get, new Uri(serverUri));
using (var client = new HttpClient(handler, true))
{
var response = await client.SendAsync(msg);
Log("Got status code: " + response.StatusCode);
response = response.EnsureSuccessStatusCode(); // Should not throw
Log("Test OK");
return response;
}
}
}
} |
To sum up things, is the viable suggestion to add some |
It's going to be a little more complicated than that unfortunately. Ideally, I'd like NTLM support in both. @wfurt can we meet with you and get an understanding of what you were originally thinking? |
sure. My experience with Android is minimal but I can outline possible obstacles. I think it should be simple to set up a prototype at this point. |
Sure but at least there is a way to enable NTLM support by changing the default. Today the is no option on Android AFAIK. |
You can give it try if you want to @dotMorten. https://github.com/wfurt/Ntlm is simple wrapper around any handler. It reads and adds headers as needed ( I simply run out of time for 6.0 focusing on support for Quic and HTTP/3. While NTLM has many caveats and it is deemed weak I can understand that it is useful for legacy compact. As @SteveSyfuhs mentioned, we should probably also look into getting Kerberos working as more secure alternative. |
Closing in favor of #62264 |
@wfurt Thanks. I haven't been able to get it to authenticate successfully though. I ended up using the MD4 implementation referenced in the 62264 issue, and pulling in ASN1 from the runtime repo just to completely avoid dependencies, but I still end up with 401. Negotiate etc does seem to run fine, but never succeeds. |
Can you try pure ntlm @dotMorten? It is essentially the same but just less layers and perhaps little bit easier to debug. You can send me packet capture and I can take a look. I was planning to set up some emulator for testing but I did not get to it yet. |
@wfurt Pure ntlm? How do I set the client up for that? I can share my code, but you can't access any of my IWA servers. Do you know of any publicly accessible ones? Here's what I see in the output (with diag turned on). I truncated the auth strings, albeit it's just a test account on a test server:
|
It is the first option from the post @dotMorten e.g. 'Authorization: NTLM' vs 'Authorization: Negotiate`. The first option does not need any ASN and has raw NTLM data (base64). Can you try it agains simple IIS server? If so, then I would likely be able to reproduce it with your code anywhere. If IIS works, then we would need to figure out what is special about your internal servers. We would probably need the full exchange details (you can send it to my GH email if you don't want to post it publicly here) |
Some time ago in mono's corefx fork, we switched to
SocketsHttpHandler
and discovered that NTLM auth stopped working on Android. Originally, we thought it was just a configuration miss on our part, but it ended up turning out to be that kerberos is not available on Android and thus neither is libkrb-5.We have been able to suggest a workaround by having customers use our old http handler, but w/ .NET 5 I believe we are going to need to work this out. I'm opening this issue to start a discussion / gather ideas.
The text was updated successfully, but these errors were encountered: