3

Using WinHTTP right now, and looking to switch over to cpprestsdk. I'm looking through the documentation, and I don't see anything about support for NTLM/Negotiate/Kerberos support. Am I missing something? I find it hard to believe that MS wouldn't have supported it, but I don't see any sample code on how you would use it.

The reason we need NTLM/Negotiate/Kerberos support is that we are running our client via RemoteApp, and want our users to only have to login once with their Domain Credentials when starting the app, and not have users prompted to enter passwords a second time.

bpeikes
  • 3,495
  • 9
  • 42
  • 80
  • 1
    I wish I could bump this 10x more times. Best of luck. – WhozCraig Nov 16 '19 at 07:16
  • https://github.com/Microsoft/cpprestsdk/issues/676 – Alan Birtles Nov 16 '19 at 07:19
  • @WhozCraig - Wondering if I should give up on cpprestsdk and move to libcurl, which seems to have a larger community of users. Thoughts? – bpeikes Nov 16 '19 at 20:17
  • @bpeikes Tough call. I know putting together REST calls with libcurl is more a pita than cpprestsdk (we have different products and use both), but if it provides NTLM-SSO as you want, it may be a viable alternative. Just make sure to wrap it up in a raii framework. It's pretty easy to accidentally leak things using libcurl (or any other handle-context-based library) in C++. I'm in your boat, btw. I like what MS did with casablanca. I just wish that would have gone a little further (as I'm sure you do too right about now). – WhozCraig Nov 16 '19 at 20:24

2 Answers2

0

It seems that Windows authentication is readily build into Casablanca (when used on a Windows machine). Take a look at src/http/client/http_client_winhttp.cpp. There you'll find a function "ChooseAuthScheme". To my understanding this will select the "most secure" authentication scheme that a server supplies. If the server e. g. claims to support both, "BASIC" and "NEGOTIATE", it will prefer and select the latter as the more secure scheme. Thus using Widows authentication should a very easy to use, just don't set any credentials (username/pwd) and try to connect to a server that supports Windows authentication and also announces this in the http "Authenticate" header (otherwise Casablanca will of course not attempt to use Windows authentication).

BUT I'm also trying to use Windows authentication in Casablanca and I'm currently facing two problems:

  • Windows authentication sort of works as explained above in my scenario. Bewilderingly however sometimes it does not. This goes as far as that I can connect to a server from machine A with user Foo logged in and I cannot connect from machine B with user Bar logged in at the the very same time and with all machines being in the very same network segment without any router or proxy in between. From a Fiddler log I can see that in case of a failure Casablanca attempts a connect without any authentication and then receives a http 403 unauthorized from the server (which is to be expected and perfectly fine) but afterwards fails to resend the request with the NEGOTIATE in the headers, it simply aborts. On the contrary in the successful case there is a resent with the credentials being a base64 encoded blob (that in fact has binary content and presumably only MS knows what it means). I'm currently unclear about what triggers this, why this happens and how to resolve this. It could well be a bug in Casablanca.
  • I have a special scenario with a server that has a mixed operation mode where users should be able to connect via BASIC or Windows authentication. Let's not reason about the rationale of the following scenario and best practices, I'm dealing with a TM1 database by IBM and it's just what they implemented. So the users admissible for basic and Windows authentication don't necessarily overlap, one group must authenticate via Windows integrated (let's say domain users) and the other one must use basic authentication (let's say external users). So far I found no way (without patching Casablanca) to clamp the SDK to some mode. If the server announces BASIC and NEGOTIATE it will always switch NEGOTIATE as the more secure mode, making basic authentication inaccessible and effectively locking out the BASIC group. So if you have a similar scenario, this could equally be a problem for you, ChooseAuthScheme() tests several different authentication methods, NEGOTIATE, NTLM, PASSPORT, DIGEST and finally BASIC, in this sequence and will stubbornly select the first one that's supported on both, client and server, discarding all other options.
Don Pedro
  • 335
  • 3
  • 7
0

Casablanca (CpprestSDK) fully supports NTLM authentication. If server rejects request with status code 401/403 and header WWW-Authenticate, library will handle it internally using most secure authentication method. In case of NTLM you could either specify login/password pair, or use automatic logon (Windows), based on calling thread current user token.

However, when I tried using auto-logon feature, it unexpectedly failed on some workstations (case 1 in Don Pedro answer).

Windows version of Cpprest uses WinHTTP internally. When you try to automatically authenticate on the remote server, automatic logon policy takes effect.

The automatic logon (auto-logon) policy determines when it is acceptable for WinHTTP to include the default credentials in a request. The default credentials are either the current thread token or the session token depending on whether WinHTTP is used in synchronous or asynchronous mode. The thread token is used in synchronous mode, and the session token is used in asynchronous mode. These default credentials are often the username and password used to log on to Microsoft Windows.

Default security level is set to WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM, which allows auto-logon only for intranet servers. Rules governing intranet/internet server classification are defined in the Windows internet options dialog and somewhat murky (at least in our case).

To insure correct auto-login I have lowered security level to WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW using request native handler config:

web::http::client::http_client_config make_config()
{
    web::http::client::http_client_config config;
    config.set_proxy(web::web_proxy::use_auto_discovery);

    if (!m_wsUser.empty()) {
        web::credentials cred(m_wsUser, m_wsPass);
        config.set_credentials(cred);
    }

    config.set_nativehandle_options([](web::http::client::native_handle handle) {
        DWORD dwOpt = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
        WinHttpSetOption(handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &dwOpt, sizeof(dwOpt));
    });

    return config;
}

In my case this approach was acceptable, because server and clients are always inside organization network boundary. Otherwise this solution is insecure and should not be used.

Ari0nhh
  • 5,720
  • 3
  • 28
  • 33