mirror of
https://github.com/pocoproject/poco.git
synced 2025-03-31 07:58:24 +02:00
NTLM (proxy) authentication support for HTTPClientSession
This commit is contained in:
parent
da7de5e586
commit
0f3f11a3b2
@ -32,7 +32,8 @@ objects = \
|
||||
RemoteSyslogChannel RemoteSyslogListener SMTPChannel \
|
||||
WebSocket WebSocketImpl \
|
||||
OAuth10Credentials OAuth20Credentials \
|
||||
PollSet UDPClient UDPServerParams
|
||||
PollSet UDPClient UDPServerParams \
|
||||
NTLMCredentials HTTPNTLMCredentials
|
||||
|
||||
target = PocoNet
|
||||
target_version = $(LIBVERSION)
|
||||
|
@ -33,6 +33,9 @@ class HTTPResponse;
|
||||
class Net_API HTTPAuthenticationParams: public NameValueCollection
|
||||
/// Collection of name-value pairs of HTTP authentication header (i.e.
|
||||
/// "realm", "qop", "nonce" in case of digest authentication header).
|
||||
///
|
||||
/// For NTLM, the base64-encoded NTLM message is available from
|
||||
/// the "NTLM" property.
|
||||
{
|
||||
public:
|
||||
HTTPAuthenticationParams();
|
||||
@ -89,6 +92,7 @@ public:
|
||||
/// request or response authentication header.
|
||||
|
||||
static const std::string REALM;
|
||||
static const std::string NTLM;
|
||||
static const std::string WWW_AUTHENTICATE;
|
||||
static const std::string PROXY_AUTHENTICATE;
|
||||
|
||||
|
@ -20,6 +20,9 @@
|
||||
|
||||
#include "Poco/Net/Net.h"
|
||||
#include "Poco/Net/HTTPSession.h"
|
||||
#include "Poco/Net/HTTPBasicCredentials.h"
|
||||
#include "Poco/Net/HTTPDigestCredentials.h"
|
||||
#include "Poco/Net/HTTPNTLMCredentials.h"
|
||||
#include "Poco/Net/SocketAddress.h"
|
||||
#include "Poco/SharedPtr.h"
|
||||
#include <istream>
|
||||
@ -62,11 +65,20 @@ class Net_API HTTPClientSession: public HTTPSession
|
||||
/// set up a session through a proxy.
|
||||
{
|
||||
public:
|
||||
enum ProxyAuthentication
|
||||
{
|
||||
PROXY_AUTH_NONE, /// No proxy authentication
|
||||
PROXY_AUTH_HTTP_BASIC, /// HTTP Basic proxy authentication (default, if username and password are supplied)
|
||||
PROXY_AUTH_HTTP_DIGEST, /// HTTP Digest proxy authentication
|
||||
PROXY_AUTH_NTLM /// NTLMv2 proxy authentication
|
||||
};
|
||||
|
||||
struct ProxyConfig
|
||||
/// HTTP proxy server configuration.
|
||||
{
|
||||
ProxyConfig():
|
||||
port(HTTP_PORT)
|
||||
port(HTTP_PORT),
|
||||
authMethod(PROXY_AUTH_HTTP_BASIC)
|
||||
{
|
||||
}
|
||||
|
||||
@ -82,6 +94,9 @@ public:
|
||||
/// A regular expression defining hosts for which the proxy should be bypassed,
|
||||
/// e.g. "localhost|127\.0\.0\.1|192\.168\.0\.\d+". Can also be an empty
|
||||
/// string to disable proxy bypassing.
|
||||
|
||||
ProxyAuthentication authMethod;
|
||||
/// The authentication method to use - HTTP Basic or NTLM.
|
||||
};
|
||||
|
||||
HTTPClientSession();
|
||||
@ -275,6 +290,9 @@ protected:
|
||||
int write(const char* buffer, std::streamsize length);
|
||||
/// Tries to re-connect if keep-alive is on.
|
||||
|
||||
std::ostream& sendRequestImpl(const HTTPRequest& request);
|
||||
/// Sends the given HTTPRequest over an existing connection.
|
||||
|
||||
virtual std::string proxyRequestPrefix() const;
|
||||
/// Returns the prefix prepended to the URI for proxy requests
|
||||
/// (e.g., "http://myhost.com").
|
||||
@ -286,10 +304,20 @@ protected:
|
||||
/// Sets the proxy credentials (Proxy-Authorization header), if
|
||||
/// proxy username and password have been set.
|
||||
|
||||
void proxyAuthenticateImpl(HTTPRequest& request);
|
||||
void proxyAuthenticateImpl(HTTPRequest& request, const ProxyConfig& proxyConfig);
|
||||
/// Sets the proxy credentials (Proxy-Authorization header), if
|
||||
/// proxy username and password have been set.
|
||||
|
||||
void proxyAuthenticateDigest(HTTPRequest& request);
|
||||
/// Initiates a HTTP Digest authentication handshake with the proxy.
|
||||
|
||||
void proxyAuthenticateNTLM(HTTPRequest& request);
|
||||
/// Initiates a HTTP NTLM authentication handshake with the proxy.
|
||||
|
||||
void sendChallengeRequest(const HTTPRequest& request, HTTPResponse& response);
|
||||
/// Sends a probe request for Digest and NTLM authentication
|
||||
/// to obtain the server challenge.
|
||||
|
||||
StreamSocket proxyConnect();
|
||||
/// Sends a CONNECT request to the proxy server and returns
|
||||
/// a StreamSocket for the resulting connection.
|
||||
@ -310,6 +338,10 @@ private:
|
||||
bool _responseReceived;
|
||||
Poco::SharedPtr<std::ostream> _pRequestStream;
|
||||
Poco::SharedPtr<std::istream> _pResponseStream;
|
||||
HTTPBasicCredentials _proxyBasicCreds;
|
||||
HTTPDigestCredentials _proxyDigestCreds;
|
||||
HTTPNTLMCredentials _proxyNTLMCreds;
|
||||
bool _ntlmProxyAuthenticated;
|
||||
|
||||
static ProxyConfig _globalProxyConfig;
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
|
||||
#include "Poco/Net/HTTPDigestCredentials.h"
|
||||
#include "Poco/Net/HTTPNTLMCredentials.h"
|
||||
|
||||
|
||||
namespace Poco {
|
||||
@ -37,7 +38,7 @@ class HTTPResponse;
|
||||
|
||||
class Net_API HTTPCredentials
|
||||
/// This is a utility class for working with HTTP
|
||||
/// authentication (basic or digest) in HTTPRequest objects.
|
||||
/// authentication (Basic, Digest or NTLM) in HTTPRequest objects.
|
||||
///
|
||||
/// Usage is as follows:
|
||||
/// First, create a HTTPCredentials object containing
|
||||
@ -132,17 +133,26 @@ public:
|
||||
static bool isDigestCredentials(const std::string& header);
|
||||
/// Returns true if authentication header is for Digest authentication.
|
||||
|
||||
static bool isNTLMCredentials(const std::string& header);
|
||||
/// Returns true if authentication header is for NTLM authentication.
|
||||
|
||||
static bool hasBasicCredentials(const HTTPRequest& request);
|
||||
/// Returns true if Authorization with Basic credentials header is present in the request.
|
||||
/// Returns true if an Authorization header with Basic credentials is present in the request.
|
||||
|
||||
static bool hasDigestCredentials(const HTTPRequest& request);
|
||||
/// Returns true if Authorization with Digest credentials header is present in the request.
|
||||
/// Returns true if an Authorization header with Digest credentials is present in the request.
|
||||
|
||||
static bool hasNTLMCredentials(const HTTPRequest& request);
|
||||
/// Returns true if an Authorization header with NTLM credentials is present in the request.
|
||||
|
||||
static bool hasProxyBasicCredentials(const HTTPRequest& request);
|
||||
/// Returns true if Authorization with Basic credentials header is present in the request.
|
||||
/// Returns true if a Proxy-Authorization header with Basic credentials is present in the request.
|
||||
|
||||
static bool hasProxyDigestCredentials(const HTTPRequest& request);
|
||||
/// Returns true if Authorization with Digest credentials header is present in the request.
|
||||
/// Returns true if a Proxy-Authorization header with Digest credentials is present in the request.
|
||||
|
||||
static bool hasProxyNTLMCredentials(const HTTPRequest& request);
|
||||
/// Returns true if a Proxy-Authorization header with Digest credentials is present in the request.
|
||||
|
||||
static void extractCredentials(const std::string& userInfo, std::string& username, std::string& password);
|
||||
/// Extracts username and password from user:password information string.
|
||||
@ -155,6 +165,7 @@ private:
|
||||
HTTPCredentials& operator = (const HTTPCredentials&);
|
||||
|
||||
HTTPDigestCredentials _digest;
|
||||
HTTPNTLMCredentials _ntlm;
|
||||
};
|
||||
|
||||
|
||||
|
@ -139,6 +139,7 @@ public:
|
||||
static const std::string CONTENT_TYPE;
|
||||
static const std::string TRANSFER_ENCODING;
|
||||
static const std::string CONNECTION;
|
||||
static const std::string PROXY_CONNECTION;
|
||||
|
||||
static const std::string CONNECTION_KEEP_ALIVE;
|
||||
static const std::string CONNECTION_CLOSE;
|
||||
@ -153,13 +154,16 @@ protected:
|
||||
/// Creates the HTTPMessage and sets
|
||||
/// the version.
|
||||
|
||||
HTTPMessage(const HTTPMessage& other);
|
||||
/// Copy constructor.
|
||||
|
||||
HTTPMessage& operator = (const HTTPMessage& other);
|
||||
/// Assignment operator.
|
||||
|
||||
virtual ~HTTPMessage();
|
||||
/// Destroys the HTTPMessage.
|
||||
|
||||
private:
|
||||
HTTPMessage(const HTTPMessage&);
|
||||
HTTPMessage& operator = (const HTTPMessage&);
|
||||
|
||||
std::string _version;
|
||||
};
|
||||
|
||||
|
128
Net/include/Poco/Net/HTTPNTLMCredentials.h
Normal file
128
Net/include/Poco/Net/HTTPNTLMCredentials.h
Normal file
@ -0,0 +1,128 @@
|
||||
//
|
||||
// HTTPNTLMCredentials.h
|
||||
//
|
||||
// Library: Net
|
||||
// Package: HTTP
|
||||
// Module: HTTPNTLMCredentials
|
||||
//
|
||||
// Definition of the HTTPNTLMCredentials class.
|
||||
//
|
||||
// Copyright (c) 2019, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#ifndef Net_HTTPNTLMCredentials_INCLUDED
|
||||
#define Net_HTTPNTLMCredentials_INCLUDED
|
||||
|
||||
|
||||
#include "Poco/Net/Net.h"
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace Net {
|
||||
|
||||
|
||||
class HTTPRequest;
|
||||
class HTTPResponse;
|
||||
|
||||
|
||||
class Net_API HTTPNTLMCredentials
|
||||
/// This is a utility class for working with
|
||||
/// HTTP NTLMv2 Authentication in HTTPRequest
|
||||
/// objects.
|
||||
{
|
||||
public:
|
||||
HTTPNTLMCredentials();
|
||||
/// Creates an empty HTTPNTLMCredentials object.
|
||||
|
||||
HTTPNTLMCredentials(const std::string& username, const std::string& password);
|
||||
/// Creates a HTTPNTLMCredentials object with the given username and password.
|
||||
|
||||
~HTTPNTLMCredentials();
|
||||
/// Destroys the HTTPNTLMCredentials.
|
||||
|
||||
void reset();
|
||||
/// Resets the HTTPNTLMCredentials object to a clean state.
|
||||
|
||||
void setUsername(const std::string& username);
|
||||
/// Sets the username.
|
||||
|
||||
const std::string& getUsername() const;
|
||||
/// Returns the username.
|
||||
|
||||
void setPassword(const std::string& password);
|
||||
/// Sets the password.
|
||||
|
||||
const std::string& getPassword() const;
|
||||
/// Returns the password.
|
||||
|
||||
void authenticate(HTTPRequest& request, const HTTPResponse& response);
|
||||
/// Parses WWW-Authenticate header of the HTTPResponse, initializes
|
||||
/// internal state, and adds authentication information to the given HTTPRequest.
|
||||
|
||||
void authenticate(HTTPRequest& request, const std::string& ntlmChallengeBase64);
|
||||
/// Initializes internal state according to information from the
|
||||
/// ntlmChallengeBase64, and adds authentication
|
||||
/// information to the given HTTPRequest.
|
||||
///
|
||||
/// Throws InvalidArgumentException if responseAuthParams is
|
||||
/// invalid.
|
||||
|
||||
void updateAuthInfo(HTTPRequest& request);
|
||||
/// Updates internal state and adds authentication information to
|
||||
/// the given HTTPRequest.
|
||||
|
||||
void proxyAuthenticate(HTTPRequest& request, const HTTPResponse& response);
|
||||
/// Parses Proxy-Authenticate header of the HTTPResponse, initializes
|
||||
/// internal state, and adds proxy authentication information to the given HTTPRequest.
|
||||
|
||||
void proxyAuthenticate(HTTPRequest& request, const std::string& ntlmChallengeBase64);
|
||||
/// Initializes internal state according to information from the
|
||||
/// HTTPAuthenticationParams of the response, and adds proxy authentication
|
||||
/// information to the given HTTPRequest.
|
||||
///
|
||||
/// Throws InvalidArgumentException if HTTPAuthenticationParams is
|
||||
/// invalid or some required parameter is missing.
|
||||
/// Throws NotImplementedException in case of unsupported digest
|
||||
/// algorithm or quality of protection method.
|
||||
|
||||
void updateProxyAuthInfo(HTTPRequest& request);
|
||||
/// Updates internal state and adds proxy authentication information to
|
||||
/// the given HTTPRequest.
|
||||
|
||||
static const std::string SCHEME;
|
||||
|
||||
private:
|
||||
HTTPNTLMCredentials(const HTTPNTLMCredentials&);
|
||||
HTTPNTLMCredentials& operator = (const HTTPNTLMCredentials&);
|
||||
|
||||
std::string createNTLMMessage(const std::string& ntlmChallengeBase64);
|
||||
static void splitUsername(const std::string& usernameAndDomain, std::string& username, std::string& domain);
|
||||
|
||||
std::string _username;
|
||||
std::string _password;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// inlines
|
||||
//
|
||||
inline const std::string& HTTPNTLMCredentials::getUsername() const
|
||||
{
|
||||
return _username;
|
||||
}
|
||||
|
||||
|
||||
inline const std::string& HTTPNTLMCredentials::getPassword() const
|
||||
{
|
||||
return _password;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::Net
|
||||
|
||||
|
||||
#endif // Net_HTTPNTLMCredentials_INCLUDED
|
@ -39,7 +39,7 @@ public:
|
||||
HTTPRequest();
|
||||
/// Creates a GET / HTTP/1.0 HTTP request.
|
||||
|
||||
HTTPRequest(const std::string& version);
|
||||
explicit HTTPRequest(const std::string& version);
|
||||
/// Creates a GET / HTTP/1.x request with
|
||||
/// the given version (HTTP/1.0 or HTTP/1.1).
|
||||
|
||||
@ -49,9 +49,15 @@ public:
|
||||
HTTPRequest(const std::string& method, const std::string& uri, const std::string& version);
|
||||
/// Creates a HTTP request with the given method, URI and version.
|
||||
|
||||
HTTPRequest(const HTTPRequest& other);
|
||||
/// Creates a HTTP request by copying another one.
|
||||
|
||||
virtual ~HTTPRequest();
|
||||
/// Destroys the HTTPRequest.
|
||||
|
||||
HTTPRequest& operator = (const HTTPRequest&);
|
||||
/// Assignment operator.
|
||||
|
||||
void setMethod(const std::string& method);
|
||||
/// Sets the method.
|
||||
|
||||
@ -103,6 +109,9 @@ public:
|
||||
/// Sets the authentication scheme and information for
|
||||
/// this request.
|
||||
|
||||
void removeCredentials();
|
||||
/// Removes any credentials from the request.
|
||||
|
||||
bool getExpectContinue() const;
|
||||
/// Returns true if the request contains an
|
||||
/// "Expect: 100-continue" header.
|
||||
@ -126,6 +135,9 @@ public:
|
||||
/// Sets the proxy authentication scheme and information for
|
||||
/// this request.
|
||||
|
||||
void removeProxyCredentials();
|
||||
/// Removes any proxy credentials from the request.
|
||||
|
||||
void write(std::ostream& ostr) const;
|
||||
/// Writes the HTTP request to the given
|
||||
/// output stream.
|
||||
@ -173,9 +185,6 @@ private:
|
||||
|
||||
std::string _method;
|
||||
std::string _uri;
|
||||
|
||||
HTTPRequest(const HTTPRequest&);
|
||||
HTTPRequest& operator = (const HTTPRequest&);
|
||||
};
|
||||
|
||||
|
||||
|
@ -122,17 +122,23 @@ public:
|
||||
/// Creates the HTTPResponse with the given version, status
|
||||
/// and reason phrase.
|
||||
|
||||
HTTPResponse(HTTPStatus status);
|
||||
explicit HTTPResponse(HTTPStatus status);
|
||||
/// Creates the HTTPResponse with the given status
|
||||
/// an an appropriate reason phrase.
|
||||
/// and an appropriate reason phrase.
|
||||
|
||||
HTTPResponse(const std::string& version, HTTPStatus status);
|
||||
/// Creates the HTTPResponse with the given version, status
|
||||
/// an an appropriate reason phrase.
|
||||
/// and an appropriate reason phrase.
|
||||
|
||||
HTTPResponse(const HTTPResponse& other);
|
||||
/// Creates the HTTPResponse by copying another one.
|
||||
|
||||
virtual ~HTTPResponse();
|
||||
/// Destroys the HTTPResponse.
|
||||
|
||||
HTTPResponse& operator = (const HTTPResponse& other);
|
||||
/// Assignment operator.
|
||||
|
||||
void setStatus(HTTPStatus status);
|
||||
/// Sets the HTTP status code.
|
||||
///
|
||||
@ -268,9 +274,6 @@ private:
|
||||
|
||||
HTTPStatus _status;
|
||||
std::string _reason;
|
||||
|
||||
HTTPResponse(const HTTPResponse&);
|
||||
HTTPResponse& operator = (const HTTPResponse&);
|
||||
};
|
||||
|
||||
|
||||
|
171
Net/include/Poco/Net/NTLMCredentials.h
Normal file
171
Net/include/Poco/Net/NTLMCredentials.h
Normal file
@ -0,0 +1,171 @@
|
||||
//
|
||||
// NTLMCredentials.h
|
||||
//
|
||||
// Library: Net
|
||||
// Package: HTTP
|
||||
// Module: NTLMCredentials
|
||||
//
|
||||
// Definition of the NTLMCredentials class.
|
||||
//
|
||||
// Copyright (c) 2019, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#ifndef Net_NTLMCredentials_INCLUDED
|
||||
#define Net_NTLMCredentials_INCLUDED
|
||||
|
||||
|
||||
#include "Poco/Net/Net.h"
|
||||
#include "Poco/BinaryReader.h"
|
||||
#include "Poco/BinaryWriter.h"
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace Net {
|
||||
|
||||
|
||||
class Net_API NTLMCredentials
|
||||
/// This is a utility class for working with
|
||||
/// NTLMv2 Authentication.
|
||||
///
|
||||
/// Note: This implementation is based on the
|
||||
/// "The NTLM Authentication Protocol and Security Support Provider"
|
||||
/// document written by Eric Glass and avilable from
|
||||
/// http://davenport.sourceforge.net/ntlm.html
|
||||
/// and the NT LAN Manager (NTLM) Authentication Protocol
|
||||
/// [MS-NLMP] document by Microsoft.
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
NTLM_MESSAGE_TYPE_NEGOTIATE = 0x01,
|
||||
NTLM_MESSAGE_TYPE_CHALLENGE = 0x02,
|
||||
NTLM_MESSAGE_TYPE_AUTHENTICATE = 0x03
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
NTLM_FLAG_NEGOTIATE_UNICODE = 0x00000001,
|
||||
NTLM_FLAG_NEGOTIATE_OEM = 0x00000002,
|
||||
NTLM_FLAG_REQUEST_TARGET = 0x00000004,
|
||||
NTLM_FLAG_NEGOTIATE_NTLM = 0x00000200,
|
||||
NTLM_FLAG_DOMAIN_SUPPLIED = 0x00001000,
|
||||
NTLM_FLAG_WORKST_SUPPLIED = 0x00002000,
|
||||
NTLM_FLAG_NEGOTIATE_LOCAL = 0x00004000,
|
||||
NTLM_FLAG_NEGOTIATE_ALWAYS_SIGN = 0x00008000,
|
||||
NTLM_FLAG_NEGOTIATE_NTLM2_KEY = 0x00080000,
|
||||
NTLM_FLAG_TARGET_DOMAIN = 0x00010000,
|
||||
NTLM_FLAG_TARGET_SERVER = 0x00020000,
|
||||
NTLM_FLAG_TARGET_SHARE = 0x00040000,
|
||||
NTLM_FLAG_NEGOTIATE_TARGET = 0x00800000,
|
||||
NTLM_FLAG_NEGOTIATE_128 = 0x20000000,
|
||||
NTLM_FLAG_NEGOTIATE_56 = 0x80000000
|
||||
};
|
||||
|
||||
struct NegotiateMessage
|
||||
/// This message is sent from the client to initiate NTLM authentication.
|
||||
{
|
||||
NegotiateMessage(): flags(0) {}
|
||||
|
||||
Poco::UInt32 flags;
|
||||
std::string domain;
|
||||
std::string workstation;
|
||||
};
|
||||
|
||||
struct ChallengeMessage
|
||||
/// This message is sent back by the server and contains the NTLM challenge.
|
||||
{
|
||||
ChallengeMessage(): flags(0) {}
|
||||
|
||||
Poco::UInt32 flags;
|
||||
std::vector<unsigned char> challenge;
|
||||
std::string target;
|
||||
std::vector<unsigned char> targetInfo;
|
||||
};
|
||||
|
||||
struct AuthenticateMessage
|
||||
/// This message is sent from the client to authenticate itself by providing
|
||||
/// a response to the server challenge.
|
||||
{
|
||||
AuthenticateMessage(): flags(0) {}
|
||||
|
||||
Poco::UInt32 flags;
|
||||
std::vector<unsigned char> lmResponse;
|
||||
std::vector<unsigned char> ntlmResponse;
|
||||
std::string target;
|
||||
std::string username;
|
||||
std::string workstation;
|
||||
};
|
||||
|
||||
struct BufferDesc
|
||||
{
|
||||
BufferDesc():
|
||||
length(0),
|
||||
reserved(0),
|
||||
offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
BufferDesc(Poco::UInt16 len, Poco::UInt32 off):
|
||||
length(len),
|
||||
reserved(len),
|
||||
offset(off)
|
||||
{
|
||||
}
|
||||
|
||||
Poco::UInt16 length;
|
||||
Poco::UInt16 reserved;
|
||||
Poco::UInt32 offset;
|
||||
};
|
||||
|
||||
static std::vector<unsigned char> createNonce();
|
||||
/// Creates an 8-byte client nonce for NTLM authentication.
|
||||
|
||||
static Poco::UInt64 createTimestamp();
|
||||
/// Creates the NTLM timestamp in tenths of a microsecond since January 1, 1601,
|
||||
/// using the current system time.
|
||||
|
||||
static std::vector<unsigned char> createPasswordHash(const std::string& password);
|
||||
/// Creates the NTLM password hash (MD4 of UTF-16-converted password).
|
||||
|
||||
static std::vector<unsigned char> createNTLMv2Hash(const std::string& username, const std::string& target, const std::string& password);
|
||||
/// Creates the NTLMv2 hash, which is the HMAC-MD5 of the concatenated UTF-16 uppercase username and target,
|
||||
/// using the password hash as HMAC passphrase.
|
||||
|
||||
static std::vector<unsigned char> createLMv2Response(const std::vector<unsigned char>& ntlm2Hash, const std::vector<unsigned char>& challenge, const std::vector<unsigned char>& nonce);
|
||||
/// Creates the LMv2 response by computing the HMAC-MD5 of the challenge and nonce, using the
|
||||
/// ntlm2Hash (see createNTLMv2Hash()) as HMAC passphrase.
|
||||
|
||||
static std::vector<unsigned char> createNTLMv2Response(const std::vector<unsigned char>& ntlm2Hash, const std::vector<unsigned char>& challenge, const std::vector<unsigned char>& nonce, const std::vector<unsigned char>& targetInfo, Poco::UInt64 timestamp);
|
||||
/// Creates the NTLMv2 response by creating the "blob" and prepending its HMAC-MD5, using the ntlm2Hash as HMAC passphrase.
|
||||
|
||||
static std::vector<unsigned char> formatNegotiateMessage(const NegotiateMessage& message);
|
||||
/// Creates the NTLM Type 1 Negotiate message used for initiating NTLM authentication from the client.
|
||||
|
||||
static bool parseChallengeMessage(const unsigned char* buffer, std::size_t size, ChallengeMessage& message);
|
||||
/// Parses a NTLM Type 2 Challenge message.
|
||||
///
|
||||
/// Returns true if the message was parsed successfully, otherwise false.
|
||||
|
||||
static std::vector<unsigned char> formatAuthenticateMessage(const AuthenticateMessage& message);
|
||||
/// Creates the NTLM Type 1 Authenticate message used for initiating NTLM authentication from the client.
|
||||
|
||||
static void readBufferDesc(Poco::BinaryReader& reader, BufferDesc& desc);
|
||||
/// Reads a buffer descriptor.
|
||||
|
||||
static void writeBufferDesc(Poco::BinaryWriter& writer, const BufferDesc& desc);
|
||||
/// Writes a buffer descriptor.
|
||||
|
||||
static const std::string NTLMSSP;
|
||||
/// Message signature string.
|
||||
};
|
||||
|
||||
|
||||
} } // namespace Poco::Net
|
||||
|
||||
|
||||
#endif // Net_NTLMCredentials_INCLUDED
|
@ -13,13 +13,13 @@
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/Exception.h"
|
||||
#include "Poco/Net/HTTPAuthenticationParams.h"
|
||||
#include "Poco/Net/HTTPRequest.h"
|
||||
#include "Poco/Net/HTTPResponse.h"
|
||||
#include "Poco/Net/NetException.h"
|
||||
#include "Poco/String.h"
|
||||
#include "Poco/Ascii.h"
|
||||
#include "Poco/Exception.h"
|
||||
|
||||
|
||||
using Poco::icompare;
|
||||
@ -66,6 +66,7 @@ namespace Net {
|
||||
|
||||
|
||||
const std::string HTTPAuthenticationParams::REALM("realm");
|
||||
const std::string HTTPAuthenticationParams::NTLM("NTLM");
|
||||
const std::string HTTPAuthenticationParams::WWW_AUTHENTICATE("WWW-Authenticate");
|
||||
const std::string HTTPAuthenticationParams::PROXY_AUTHENTICATE("Proxy-Authenticate");
|
||||
|
||||
@ -135,20 +136,25 @@ void HTTPAuthenticationParams::fromResponse(const HTTPResponse& response, const
|
||||
bool found = false;
|
||||
while (!found && it != response.end() && icompare(it->first, header) == 0)
|
||||
{
|
||||
const std::string& header = it->second;
|
||||
if (icompare(header, 0, 6, "Basic ") == 0)
|
||||
const std::string& headerValue = it->second;
|
||||
if (icompare(headerValue, 0, 6, "Basic ") == 0)
|
||||
{
|
||||
parse(header.begin() + 6, header.end());
|
||||
parse(headerValue.begin() + 6, headerValue.end());
|
||||
found = true;
|
||||
}
|
||||
else if (icompare(header, 0, 7, "Digest ") == 0)
|
||||
else if (icompare(headerValue, 0, 7, "Digest ") == 0)
|
||||
{
|
||||
parse(header.begin() + 7, header.end());
|
||||
parse(headerValue.begin() + 7, headerValue.end());
|
||||
found = true;
|
||||
}
|
||||
else if (icompare(headerValue, 0, 5, "NTLM ") == 0)
|
||||
{
|
||||
set(NTLM, headerValue.substr(5));
|
||||
found = true;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (!found) throw NotAuthenticatedException("No Basic or Digest authentication header found");
|
||||
if (!found) throw NotAuthenticatedException("No Basic, Digest or NTLM authentication header found");
|
||||
}
|
||||
|
||||
|
||||
@ -166,8 +172,14 @@ void HTTPAuthenticationParams::setRealm(const std::string& realm)
|
||||
|
||||
std::string HTTPAuthenticationParams::toString() const
|
||||
{
|
||||
ConstIterator iter = begin();
|
||||
std::string result;
|
||||
if (size() == 1 && find(NTLM) != end())
|
||||
{
|
||||
result = get(NTLM);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConstIterator iter = begin();
|
||||
|
||||
if (iter != end())
|
||||
{
|
||||
@ -180,7 +192,7 @@ std::string HTTPAuthenticationParams::toString() const
|
||||
result.append(", ");
|
||||
formatParameter(result, iter->first, iter->second);
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "Poco/Net/HTTPStream.h"
|
||||
#include "Poco/Net/HTTPFixedLengthStream.h"
|
||||
#include "Poco/Net/HTTPChunkedStream.h"
|
||||
#include "Poco/Net/HTTPBasicCredentials.h"
|
||||
#include "Poco/Net/HTTPCredentials.h"
|
||||
#include "Poco/Net/NetException.h"
|
||||
#include "Poco/NumberFormatter.h"
|
||||
#include "Poco/CountingStream.h"
|
||||
@ -45,7 +45,8 @@ HTTPClientSession::HTTPClientSession():
|
||||
_reconnect(false),
|
||||
_mustReconnect(false),
|
||||
_expectResponseBody(false),
|
||||
_responseReceived(false)
|
||||
_responseReceived(false),
|
||||
_ntlmProxyAuthenticated(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -58,7 +59,8 @@ HTTPClientSession::HTTPClientSession(const StreamSocket& socket):
|
||||
_reconnect(false),
|
||||
_mustReconnect(false),
|
||||
_expectResponseBody(false),
|
||||
_responseReceived(false)
|
||||
_responseReceived(false),
|
||||
_ntlmProxyAuthenticated(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -71,7 +73,8 @@ HTTPClientSession::HTTPClientSession(const SocketAddress& address):
|
||||
_reconnect(false),
|
||||
_mustReconnect(false),
|
||||
_expectResponseBody(false),
|
||||
_responseReceived(false)
|
||||
_responseReceived(false),
|
||||
_ntlmProxyAuthenticated(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -84,7 +87,8 @@ HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port)
|
||||
_reconnect(false),
|
||||
_mustReconnect(false),
|
||||
_expectResponseBody(false),
|
||||
_responseReceived(false)
|
||||
_responseReceived(false),
|
||||
_ntlmProxyAuthenticated(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -97,7 +101,8 @@ HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port,
|
||||
_reconnect(false),
|
||||
_mustReconnect(false),
|
||||
_expectResponseBody(false),
|
||||
_responseReceived(false)
|
||||
_responseReceived(false),
|
||||
_ntlmProxyAuthenticated(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -195,8 +200,6 @@ std::ostream& HTTPClientSession::sendRequest(HTTPRequest& request)
|
||||
{
|
||||
_pRequestStream = 0;
|
||||
_pResponseStream = 0;
|
||||
clearException();
|
||||
_responseReceived = false;
|
||||
|
||||
bool keepAlive = getKeepAlive();
|
||||
if (((connected() && !keepAlive) || mustReconnect()) && !_host.empty())
|
||||
@ -207,17 +210,43 @@ std::ostream& HTTPClientSession::sendRequest(HTTPRequest& request)
|
||||
try
|
||||
{
|
||||
if (!connected())
|
||||
{
|
||||
_ntlmProxyAuthenticated = false;
|
||||
reconnect();
|
||||
}
|
||||
if (!keepAlive)
|
||||
{
|
||||
request.setKeepAlive(false);
|
||||
}
|
||||
if (!request.has(HTTPRequest::HOST) && !_host.empty())
|
||||
{
|
||||
request.setHost(_host, _port);
|
||||
}
|
||||
if (!_proxyConfig.host.empty() && !bypassProxy())
|
||||
{
|
||||
request.setURI(proxyRequestPrefix() + request.getURI());
|
||||
std::string prefix = proxyRequestPrefix();
|
||||
if (!prefix.empty() && request.getURI().compare(0, 7, "http://") != 0 && request.getURI().compare(0, 8, "https://") != 0)
|
||||
request.setURI(prefix + request.getURI());
|
||||
if (keepAlive) request.set(HTTPMessage::PROXY_CONNECTION, HTTPMessage::CONNECTION_KEEP_ALIVE);
|
||||
proxyAuthenticate(request);
|
||||
}
|
||||
_reconnect = keepAlive;
|
||||
return sendRequestImpl(request);
|
||||
}
|
||||
catch (Exception&)
|
||||
{
|
||||
close();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::ostream& HTTPClientSession::sendRequestImpl(const HTTPRequest& request)
|
||||
{
|
||||
_pRequestStream = 0;
|
||||
_pResponseStream = 0;
|
||||
clearException();
|
||||
_responseReceived = false;
|
||||
_expectResponseBody = request.getMethod() != HTTPRequest::HTTP_HEAD;
|
||||
const std::string& method = request.getMethod();
|
||||
if (request.getChunkedTransferEncoding())
|
||||
@ -252,12 +281,6 @@ std::ostream& HTTPClientSession::sendRequest(HTTPRequest& request)
|
||||
_lastRequest.update();
|
||||
return *_pRequestStream;
|
||||
}
|
||||
catch (Exception&)
|
||||
{
|
||||
close();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::istream& HTTPClientSession::receiveResponse(HTTPResponse& response)
|
||||
@ -409,17 +432,86 @@ bool HTTPClientSession::mustReconnect() const
|
||||
|
||||
void HTTPClientSession::proxyAuthenticate(HTTPRequest& request)
|
||||
{
|
||||
proxyAuthenticateImpl(request);
|
||||
proxyAuthenticateImpl(request, _proxyConfig);
|
||||
}
|
||||
|
||||
|
||||
void HTTPClientSession::proxyAuthenticateImpl(HTTPRequest& request)
|
||||
void HTTPClientSession::proxyAuthenticateImpl(HTTPRequest& request, const ProxyConfig& proxyConfig)
|
||||
{
|
||||
if (!_proxyConfig.username.empty())
|
||||
switch (proxyConfig.authMethod)
|
||||
{
|
||||
HTTPBasicCredentials creds(_proxyConfig.username, _proxyConfig.password);
|
||||
creds.proxyAuthenticate(request);
|
||||
case PROXY_AUTH_NONE:
|
||||
break;
|
||||
|
||||
case PROXY_AUTH_HTTP_BASIC:
|
||||
_proxyBasicCreds.setUsername(proxyConfig.username);
|
||||
_proxyBasicCreds.setPassword(proxyConfig.password);
|
||||
_proxyBasicCreds.proxyAuthenticate(request);
|
||||
break;
|
||||
|
||||
case PROXY_AUTH_HTTP_DIGEST:
|
||||
if (HTTPCredentials::hasDigestCredentials(request))
|
||||
{
|
||||
_proxyDigestCreds.updateProxyAuthInfo(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
_proxyDigestCreds.setUsername(proxyConfig.username);
|
||||
_proxyDigestCreds.setPassword(proxyConfig.password);
|
||||
proxyAuthenticateDigest(request);
|
||||
}
|
||||
|
||||
case PROXY_AUTH_NTLM:
|
||||
if (_ntlmProxyAuthenticated)
|
||||
{
|
||||
_proxyNTLMCreds.updateProxyAuthInfo(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
_proxyNTLMCreds.setUsername(proxyConfig.username);
|
||||
_proxyNTLMCreds.setPassword(proxyConfig.password);
|
||||
proxyAuthenticateNTLM(request);
|
||||
_ntlmProxyAuthenticated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HTTPClientSession::proxyAuthenticateDigest(HTTPRequest& request)
|
||||
{
|
||||
HTTPResponse response;
|
||||
request.set(HTTPMessage::PROXY_CONNECTION, HTTPMessage::CONNECTION_KEEP_ALIVE);
|
||||
sendChallengeRequest(request, response);
|
||||
_proxyDigestCreds.proxyAuthenticate(request, response);
|
||||
}
|
||||
|
||||
|
||||
void HTTPClientSession::proxyAuthenticateNTLM(HTTPRequest& request)
|
||||
{
|
||||
HTTPResponse response;
|
||||
request.set(HTTPMessage::PROXY_CONNECTION, HTTPMessage::CONNECTION_KEEP_ALIVE);
|
||||
_proxyNTLMCreds.proxyAuthenticate(request, std::string());
|
||||
sendChallengeRequest(request, response);
|
||||
_proxyNTLMCreds.proxyAuthenticate(request, response);
|
||||
}
|
||||
|
||||
|
||||
void HTTPClientSession::sendChallengeRequest(const HTTPRequest& request, HTTPResponse& response)
|
||||
{
|
||||
if (!connected())
|
||||
{
|
||||
reconnect();
|
||||
}
|
||||
|
||||
HTTPRequest challengeRequest(request);
|
||||
if (challengeRequest.hasContentLength())
|
||||
{
|
||||
challengeRequest.setContentLength(0);
|
||||
}
|
||||
|
||||
sendRequestImpl(challengeRequest);
|
||||
std::istream& istr = receiveResponse(response);
|
||||
while (istr.good()) istr.get();
|
||||
}
|
||||
|
||||
|
||||
@ -433,9 +525,9 @@ StreamSocket HTTPClientSession::proxyConnect()
|
||||
NumberFormatter::append(targetAddress, _port);
|
||||
HTTPRequest proxyRequest(HTTPRequest::HTTP_CONNECT, targetAddress, HTTPMessage::HTTP_1_1);
|
||||
HTTPResponse proxyResponse;
|
||||
proxyRequest.set("Proxy-Connection", "keep-alive");
|
||||
proxyRequest.set("Host", getHost());
|
||||
proxyAuthenticateImpl(proxyRequest);
|
||||
proxyRequest.set(HTTPMessage::PROXY_CONNECTION, HTTPMessage::CONNECTION_KEEP_ALIVE);
|
||||
proxyRequest.set(HTTPRequest::HOST, getHost());
|
||||
proxySession.proxyAuthenticateImpl(proxyRequest, _proxyConfig);
|
||||
proxySession.setKeepAlive(true);
|
||||
proxySession.sendRequest(proxyRequest);
|
||||
proxySession.receiveResponse(proxyResponse);
|
||||
|
@ -85,6 +85,13 @@ void HTTPCredentials::authenticate(HTTPRequest& request, const HTTPResponse& res
|
||||
_digest.authenticate(request, HTTPAuthenticationParams(iter->second.substr(7)));
|
||||
return;
|
||||
}
|
||||
else if (isNTLMCredentials(iter->second))
|
||||
{
|
||||
_ntlm.setUsername(_digest.getUsername());
|
||||
_ntlm.setPassword(_digest.getPassword());
|
||||
_ntlm.authenticate(request, iter->second.substr(5));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +110,10 @@ void HTTPCredentials::updateAuthInfo(HTTPRequest& request)
|
||||
{
|
||||
_digest.updateAuthInfo(request);
|
||||
}
|
||||
else if (isNTLMCredentials(authorization))
|
||||
{
|
||||
_ntlm.updateAuthInfo(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,6 +132,13 @@ void HTTPCredentials::proxyAuthenticate(HTTPRequest& request, const HTTPResponse
|
||||
_digest.proxyAuthenticate(request, HTTPAuthenticationParams(iter->second.substr(7)));
|
||||
return;
|
||||
}
|
||||
else if (isNTLMCredentials(iter->second))
|
||||
{
|
||||
_ntlm.setUsername(_digest.getUsername());
|
||||
_ntlm.setPassword(_digest.getPassword());
|
||||
_ntlm.proxyAuthenticate(request, iter->second.substr(5));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,6 +157,10 @@ void HTTPCredentials::updateProxyAuthInfo(HTTPRequest& request)
|
||||
{
|
||||
_digest.updateProxyAuthInfo(request);
|
||||
}
|
||||
else if (isNTLMCredentials(authorization))
|
||||
{
|
||||
_ntlm.updateProxyAuthInfo(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,6 +177,12 @@ bool HTTPCredentials::isDigestCredentials(const std::string& header)
|
||||
}
|
||||
|
||||
|
||||
bool HTTPCredentials::isNTLMCredentials(const std::string& header)
|
||||
{
|
||||
return icompare(header, 0, 4, "NTLM") == 0 && (header.size() > 5 ? Poco::Ascii::isSpace(header[5]) : true);
|
||||
}
|
||||
|
||||
|
||||
bool HTTPCredentials::hasBasicCredentials(const HTTPRequest& request)
|
||||
{
|
||||
return request.has(HTTPRequest::AUTHORIZATION) && isBasicCredentials(request.get(HTTPRequest::AUTHORIZATION));
|
||||
@ -167,6 +195,12 @@ bool HTTPCredentials::hasDigestCredentials(const HTTPRequest& request)
|
||||
}
|
||||
|
||||
|
||||
bool HTTPCredentials::hasNTLMCredentials(const HTTPRequest& request)
|
||||
{
|
||||
return request.has(HTTPRequest::AUTHORIZATION) && isNTLMCredentials(request.get(HTTPRequest::AUTHORIZATION));
|
||||
}
|
||||
|
||||
|
||||
bool HTTPCredentials::hasProxyBasicCredentials(const HTTPRequest& request)
|
||||
{
|
||||
return request.has(HTTPRequest::PROXY_AUTHORIZATION) && isBasicCredentials(request.get(HTTPRequest::PROXY_AUTHORIZATION));
|
||||
@ -179,6 +213,12 @@ bool HTTPCredentials::hasProxyDigestCredentials(const HTTPRequest& request)
|
||||
}
|
||||
|
||||
|
||||
bool HTTPCredentials::hasProxyNTLMCredentials(const HTTPRequest& request)
|
||||
{
|
||||
return request.has(HTTPRequest::PROXY_AUTHORIZATION) && isNTLMCredentials(request.get(HTTPRequest::PROXY_AUTHORIZATION));
|
||||
}
|
||||
|
||||
|
||||
void HTTPCredentials::extractCredentials(const std::string& userInfo, std::string& username, std::string& password)
|
||||
{
|
||||
const std::string::size_type p = userInfo.find(':');
|
||||
|
@ -38,6 +38,7 @@ const std::string HTTPMessage::CONTENT_LENGTH = "Content-Length";
|
||||
const std::string HTTPMessage::CONTENT_TYPE = "Content-Type";
|
||||
const std::string HTTPMessage::TRANSFER_ENCODING = "Transfer-Encoding";
|
||||
const std::string HTTPMessage::CONNECTION = "Connection";
|
||||
const std::string HTTPMessage::PROXY_CONNECTION = "Proxy-Connection";
|
||||
const std::string HTTPMessage::CONNECTION_KEEP_ALIVE = "Keep-Alive";
|
||||
const std::string HTTPMessage::CONNECTION_CLOSE = "Close";
|
||||
const std::string HTTPMessage::EMPTY;
|
||||
@ -55,11 +56,29 @@ HTTPMessage::HTTPMessage(const std::string& version):
|
||||
}
|
||||
|
||||
|
||||
HTTPMessage::HTTPMessage(const HTTPMessage& other):
|
||||
MessageHeader(other),
|
||||
_version(other._version)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HTTPMessage::~HTTPMessage()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HTTPMessage& HTTPMessage::operator = (const HTTPMessage& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
MessageHeader::operator = (other);
|
||||
_version = other._version;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void HTTPMessage::setVersion(const std::string& version)
|
||||
{
|
||||
_version = version;
|
||||
|
190
Net/src/HTTPNTLMCredentials.cpp
Normal file
190
Net/src/HTTPNTLMCredentials.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
//
|
||||
// HTTPNTLMCredentials.cpp
|
||||
//
|
||||
// Library: Net
|
||||
// Package: HTTP
|
||||
// Module: HTTPNTLMCredentials
|
||||
//
|
||||
// Copyright (c) 2019, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/Net/HTTPNTLMCredentials.h"
|
||||
#include "Poco/Net/NTLMCredentials.h"
|
||||
#include "Poco/Net/HTTPAuthenticationParams.h"
|
||||
#include "Poco/Net/HTTPRequest.h"
|
||||
#include "Poco/Net/HTTPResponse.h"
|
||||
#include "Poco/DateTime.h"
|
||||
#include "Poco/NumberFormatter.h"
|
||||
#include "Poco/Exception.h"
|
||||
#include "Poco/Base64Encoder.h"
|
||||
#include "Poco/Base64Decoder.h"
|
||||
#include "Poco/MemoryStream.h"
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace Net {
|
||||
|
||||
|
||||
const std::string HTTPNTLMCredentials::SCHEME = "NTLM";
|
||||
|
||||
|
||||
HTTPNTLMCredentials::HTTPNTLMCredentials()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HTTPNTLMCredentials::HTTPNTLMCredentials(const std::string& username, const std::string& password):
|
||||
_username(username),
|
||||
_password(password)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HTTPNTLMCredentials::~HTTPNTLMCredentials()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void HTTPNTLMCredentials::reset()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void HTTPNTLMCredentials::setUsername(const std::string& username)
|
||||
{
|
||||
_username = username;
|
||||
}
|
||||
|
||||
|
||||
void HTTPNTLMCredentials::setPassword(const std::string& password)
|
||||
{
|
||||
_password = password;
|
||||
}
|
||||
|
||||
|
||||
void HTTPNTLMCredentials::authenticate(HTTPRequest& request, const HTTPResponse& response)
|
||||
{
|
||||
HTTPAuthenticationParams params(response);
|
||||
authenticate(request, params.get(HTTPAuthenticationParams::NTLM, ""));
|
||||
}
|
||||
|
||||
|
||||
void HTTPNTLMCredentials::authenticate(HTTPRequest& request, const std::string& ntlmChallengeBase64)
|
||||
{
|
||||
std::string ntlmMessage = createNTLMMessage(ntlmChallengeBase64);
|
||||
request.setCredentials(SCHEME, ntlmMessage);
|
||||
}
|
||||
|
||||
|
||||
void HTTPNTLMCredentials::updateAuthInfo(HTTPRequest& request)
|
||||
{
|
||||
request.removeCredentials();
|
||||
}
|
||||
|
||||
|
||||
void HTTPNTLMCredentials::proxyAuthenticate(HTTPRequest& request, const HTTPResponse& response)
|
||||
{
|
||||
HTTPAuthenticationParams params(response, HTTPAuthenticationParams::PROXY_AUTHENTICATE);
|
||||
proxyAuthenticate(request, params.get(HTTPAuthenticationParams::NTLM, ""));
|
||||
}
|
||||
|
||||
|
||||
void HTTPNTLMCredentials::proxyAuthenticate(HTTPRequest& request, const std::string& ntlmChallengeBase64)
|
||||
{
|
||||
std::string ntlmMessage = createNTLMMessage(ntlmChallengeBase64);
|
||||
request.setProxyCredentials(SCHEME, ntlmMessage);
|
||||
}
|
||||
|
||||
|
||||
void HTTPNTLMCredentials::updateProxyAuthInfo(HTTPRequest& request)
|
||||
{
|
||||
request.removeProxyCredentials();
|
||||
}
|
||||
|
||||
|
||||
std::string HTTPNTLMCredentials::createNTLMMessage(const std::string& responseAuthParams)
|
||||
{
|
||||
if (responseAuthParams.empty())
|
||||
{
|
||||
NTLMCredentials::NegotiateMessage negotiateMsg;
|
||||
std::string username;
|
||||
splitUsername(_username, username, negotiateMsg.domain);
|
||||
std::vector<unsigned char> negotiateBuf = NTLMCredentials::formatNegotiateMessage(negotiateMsg);
|
||||
|
||||
std::ostringstream ostr;
|
||||
Poco::Base64Encoder base64(ostr);
|
||||
base64.rdbuf()->setLineLength(0);
|
||||
base64.write(reinterpret_cast<const char*>(&negotiateBuf[0]), negotiateBuf.size());
|
||||
base64.close();
|
||||
return ostr.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
Poco::MemoryInputStream istr(responseAuthParams.data(), responseAuthParams.size());
|
||||
Poco::Base64Decoder debase64(istr);
|
||||
std::vector<unsigned char> buffer(responseAuthParams.size());
|
||||
debase64.read(reinterpret_cast<char*>(&buffer[0]), buffer.size());
|
||||
std::size_t size = debase64.gcount();
|
||||
|
||||
Poco::Net::NTLMCredentials::ChallengeMessage challengeMsg;
|
||||
if (NTLMCredentials::parseChallengeMessage(&buffer[0], size, challengeMsg))
|
||||
{
|
||||
std::string username;
|
||||
std::string domain;
|
||||
splitUsername(_username, username, domain);
|
||||
|
||||
NTLMCredentials::AuthenticateMessage authenticateMsg;
|
||||
authenticateMsg.flags = challengeMsg.flags;
|
||||
authenticateMsg.target = challengeMsg.target;
|
||||
authenticateMsg.username = username;
|
||||
|
||||
std::vector<unsigned char> nonce = NTLMCredentials::createNonce();
|
||||
Poco::UInt64 timestamp = NTLMCredentials::createTimestamp();
|
||||
std::vector<unsigned char> ntlm2Hash = NTLMCredentials::createNTLMv2Hash(username, challengeMsg.target, _password);
|
||||
authenticateMsg.lmResponse = NTLMCredentials::createLMv2Response(ntlm2Hash, challengeMsg.challenge, nonce);
|
||||
authenticateMsg.ntlmResponse = Poco::Net::NTLMCredentials::createNTLMv2Response(ntlm2Hash, challengeMsg.challenge, nonce, challengeMsg.targetInfo, timestamp);
|
||||
|
||||
std::vector<unsigned char> authenticateBuf = Poco::Net::NTLMCredentials::formatAuthenticateMessage(authenticateMsg);
|
||||
|
||||
std::ostringstream ostr;
|
||||
Poco::Base64Encoder base64(ostr);
|
||||
base64.rdbuf()->setLineLength(0);
|
||||
base64.write(reinterpret_cast<const char*>(&authenticateBuf[0]), authenticateBuf.size());
|
||||
base64.close();
|
||||
return ostr.str();
|
||||
}
|
||||
else throw Poco::InvalidArgumentException("Invalid NTLM challenge");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HTTPNTLMCredentials::splitUsername(const std::string& usernameAndDomain, std::string& username, std::string& domain)
|
||||
{
|
||||
std::string::size_type pos = usernameAndDomain.find('\\');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
domain.assign(usernameAndDomain, 0, pos);
|
||||
username.assign(usernameAndDomain, pos + 1);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = usernameAndDomain.find('@');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
username.assign(usernameAndDomain, 0, pos);
|
||||
domain.assign(usernameAndDomain, pos + 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
username = usernameAndDomain;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} } // namespace Poco::Net
|
@ -74,11 +74,31 @@ HTTPRequest::HTTPRequest(const std::string& method, const std::string& uri, cons
|
||||
}
|
||||
|
||||
|
||||
HTTPRequest::HTTPRequest(const HTTPRequest& other):
|
||||
HTTPMessage(other),
|
||||
_method(other._method),
|
||||
_uri(other._uri)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HTTPRequest::~HTTPRequest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HTTPRequest& HTTPRequest::operator = (const HTTPRequest& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
HTTPMessage::operator = (other);
|
||||
_method = other._method;
|
||||
_uri = other._uri;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void HTTPRequest::setMethod(const std::string& method)
|
||||
{
|
||||
_method = method;
|
||||
@ -172,6 +192,12 @@ void HTTPRequest::setCredentials(const std::string& scheme, const std::string& a
|
||||
}
|
||||
|
||||
|
||||
void HTTPRequest::removeCredentials()
|
||||
{
|
||||
erase(AUTHORIZATION);
|
||||
}
|
||||
|
||||
|
||||
bool HTTPRequest::hasProxyCredentials() const
|
||||
{
|
||||
return has(PROXY_AUTHORIZATION);
|
||||
@ -190,6 +216,12 @@ void HTTPRequest::setProxyCredentials(const std::string& scheme, const std::stri
|
||||
}
|
||||
|
||||
|
||||
void HTTPRequest::removeProxyCredentials()
|
||||
{
|
||||
erase(PROXY_AUTHORIZATION);
|
||||
}
|
||||
|
||||
|
||||
void HTTPRequest::write(std::ostream& ostr) const
|
||||
{
|
||||
ostr << _method << " " << _uri << " " << getVersion() << "\r\n";
|
||||
|
@ -140,11 +140,31 @@ HTTPResponse::HTTPResponse(const std::string& version, HTTPStatus status):
|
||||
}
|
||||
|
||||
|
||||
HTTPResponse::HTTPResponse(const HTTPResponse& other):
|
||||
HTTPMessage(other),
|
||||
_status(other._status),
|
||||
_reason(other._reason)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HTTPResponse::~HTTPResponse()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HTTPResponse& HTTPResponse::operator = (const HTTPResponse& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
HTTPMessage::operator = (other);
|
||||
_status = other._status;
|
||||
_reason = other._reason;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void HTTPResponse::setStatus(HTTPStatus status)
|
||||
{
|
||||
_status = status;
|
||||
|
329
Net/src/NTLMCredentials.cpp
Normal file
329
Net/src/NTLMCredentials.cpp
Normal file
@ -0,0 +1,329 @@
|
||||
//
|
||||
// NTLMCredentials.cpp
|
||||
//
|
||||
// Library: Net
|
||||
// Package: HTTP
|
||||
// Module: NTLMCredentials
|
||||
//
|
||||
// Copyright (c) 2019, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/Net/NTLMCredentials.h"
|
||||
#include "Poco/HMACEngine.h"
|
||||
#include "Poco/MD4Engine.h"
|
||||
#include "Poco/MD5Engine.h"
|
||||
#include "Poco/DigestStream.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
#include "Poco/UTF8Encoding.h"
|
||||
#include "Poco/UTF16Encoding.h"
|
||||
#include "Poco/TextConverter.h"
|
||||
#include "Poco/UTF8String.h"
|
||||
#include "Poco/Random.h"
|
||||
#include "Poco/Timestamp.h"
|
||||
#include "Poco/MemoryStream.h"
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace Net {
|
||||
|
||||
|
||||
const std::string NTLMCredentials::NTLMSSP("NTLMSSP");
|
||||
|
||||
|
||||
std::vector<unsigned char> NTLMCredentials::createNonce()
|
||||
{
|
||||
Poco::MD5Engine md5;
|
||||
Poco::Random rnd;
|
||||
rnd.seed();
|
||||
|
||||
Poco::UInt32 n = rnd.next();
|
||||
md5.update(&n, sizeof(n));
|
||||
|
||||
Poco::Timestamp ts;
|
||||
md5.update(&ts, sizeof(ts));
|
||||
|
||||
Poco::DigestEngine::Digest d = md5.digest();
|
||||
d.resize(8);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
Poco::UInt64 NTLMCredentials::createTimestamp()
|
||||
{
|
||||
const Poco::UInt64 EPOCH_DELTA_SECONDS = 11644473600; // seconds between January 1, 1970 and January 1, 1601
|
||||
|
||||
Poco::Timestamp now;
|
||||
Poco::UInt64 ts = now.epochMicroseconds();
|
||||
ts += EPOCH_DELTA_SECONDS*1000000; // since January 1, 1601
|
||||
ts *= 10; // tenths of a microsecond
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
std::vector<unsigned char> NTLMCredentials::createPasswordHash(const std::string& password)
|
||||
{
|
||||
Poco::UTF8Encoding utf8;
|
||||
Poco::UTF16Encoding utf16(Poco::UTF16Encoding::LITTLE_ENDIAN_BYTE_ORDER);
|
||||
Poco::TextConverter converter(utf8, utf16);
|
||||
|
||||
std::string utf16Password;
|
||||
converter.convert(password, utf16Password);
|
||||
|
||||
Poco::MD4Engine md4;
|
||||
md4.update(utf16Password);
|
||||
return md4.digest();
|
||||
}
|
||||
|
||||
|
||||
std::vector<unsigned char> NTLMCredentials::createNTLMv2Hash(const std::string& username, const std::string& target, const std::string& password)
|
||||
{
|
||||
Poco::UTF8Encoding utf8;
|
||||
Poco::UTF16Encoding utf16(Poco::UTF16Encoding::LITTLE_ENDIAN_BYTE_ORDER);
|
||||
Poco::TextConverter converter(utf8, utf16);
|
||||
|
||||
std::vector<unsigned char> passwordHash = createPasswordHash(password);
|
||||
|
||||
std::string userDomain = Poco::UTF8::toUpper(username);
|
||||
userDomain += target;
|
||||
|
||||
std::string utf16UserDomain;
|
||||
converter.convert(userDomain, utf16UserDomain);
|
||||
|
||||
std::string passwordHashString(reinterpret_cast<const char*>(&passwordHash[0]), passwordHash.size());
|
||||
Poco::HMACEngine<Poco::MD5Engine> hmac(passwordHashString);
|
||||
hmac.update(utf16UserDomain);
|
||||
return hmac.digest();
|
||||
}
|
||||
|
||||
|
||||
std::vector<unsigned char> NTLMCredentials::createLMv2Response(const std::vector<unsigned char>& ntlm2Hash, const std::vector<unsigned char>& challenge, const std::vector<unsigned char>& nonce)
|
||||
{
|
||||
poco_assert (challenge.size() == 8);
|
||||
poco_assert (nonce.size() == 8);
|
||||
|
||||
std::vector<unsigned char> lm2Response;
|
||||
|
||||
std::string ntlm2HashString(reinterpret_cast<const char*>(&ntlm2Hash[0]), ntlm2Hash.size());
|
||||
Poco::HMACEngine<Poco::MD5Engine> hmac2(ntlm2HashString);
|
||||
hmac2.update(&challenge[0], challenge.size());
|
||||
hmac2.update(&nonce[0], nonce.size());
|
||||
lm2Response = hmac2.digest();
|
||||
lm2Response.insert(lm2Response.end(), nonce.begin(), nonce.end());
|
||||
|
||||
return lm2Response;
|
||||
}
|
||||
|
||||
|
||||
std::vector<unsigned char> NTLMCredentials::createNTLMv2Response(const std::vector<unsigned char>& ntlm2Hash, const std::vector<unsigned char>& challenge, const std::vector<unsigned char>& nonce, const std::vector<unsigned char>& targetInfo, Poco::UInt64 timestamp)
|
||||
{
|
||||
poco_assert (challenge.size() == 8);
|
||||
poco_assert (nonce.size() == 8);
|
||||
|
||||
std::vector<unsigned char> blob;
|
||||
blob.resize(28 + targetInfo.size() + 4 + 16);
|
||||
|
||||
Poco::MemoryOutputStream blobStream(reinterpret_cast<char*>(&blob[16]), blob.size() - 16);
|
||||
Poco::BinaryWriter writer(blobStream, Poco::BinaryWriter::LITTLE_ENDIAN_BYTE_ORDER);
|
||||
writer << Poco::UInt32(0x0101);
|
||||
writer << Poco::UInt32(0);
|
||||
writer << timestamp;
|
||||
writer.writeRaw(reinterpret_cast<const char*>(&nonce[0]), nonce.size());
|
||||
writer << Poco::UInt32(0);
|
||||
writer.writeRaw(reinterpret_cast<const char*>(&targetInfo[0]), targetInfo.size());
|
||||
writer << Poco::UInt32(0);
|
||||
|
||||
poco_assert_dbg (blobStream.charsWritten() == blob.size() - 16);
|
||||
|
||||
std::string ntlm2HashString(reinterpret_cast<const char*>(&ntlm2Hash[0]), ntlm2Hash.size());
|
||||
Poco::HMACEngine<Poco::MD5Engine> hmac2(ntlm2HashString);
|
||||
hmac2.update(&challenge[0], challenge.size());
|
||||
hmac2.update(&blob[16], blob.size() - 16);
|
||||
Poco::DigestEngine::Digest d = hmac2.digest();
|
||||
|
||||
poco_assert_dbg (d.size() == 16);
|
||||
|
||||
std::memcpy(&blob[0], &d[0], 16);
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
|
||||
std::vector<unsigned char> NTLMCredentials::formatNegotiateMessage(const NegotiateMessage& message)
|
||||
{
|
||||
Poco::UTF8Encoding utf8;
|
||||
Poco::UTF16Encoding utf16(Poco::UTF16Encoding::LITTLE_ENDIAN_BYTE_ORDER);
|
||||
Poco::TextConverter converter(utf8, utf16);
|
||||
|
||||
std::string utf16Domain;
|
||||
converter.convert(message.domain, utf16Domain);
|
||||
|
||||
std::string utf16Workstation;
|
||||
converter.convert(message.workstation, utf16Workstation);
|
||||
|
||||
std::size_t size = 8 // signature
|
||||
+ 4 // type
|
||||
+ 4 // flags
|
||||
+ 8 + utf16Domain.size()
|
||||
+ 8 + utf16Workstation.size();
|
||||
|
||||
Poco::UInt32 flags = message.flags | NTLM_FLAG_NEGOTIATE_UNICODE | NTLM_FLAG_REQUEST_TARGET | NTLM_FLAG_NEGOTIATE_NTLM | NTLM_FLAG_NEGOTIATE_NTLM2_KEY | NTLM_FLAG_NEGOTIATE_ALWAYS_SIGN;
|
||||
if (!utf16Domain.empty()) flags |= NTLM_FLAG_DOMAIN_SUPPLIED;
|
||||
if (!utf16Workstation.empty()) flags |= NTLM_FLAG_WORKST_SUPPLIED;
|
||||
|
||||
BufferDesc domainDesc(utf16Domain.size(), 8 + 4 + 4 + 8);
|
||||
BufferDesc workstDesc(utf16Workstation.size(), domainDesc.offset + domainDesc.length);
|
||||
|
||||
std::vector<unsigned char> buffer(size);
|
||||
Poco::MemoryOutputStream bufferStream(reinterpret_cast<char*>(&buffer[0]), buffer.size());
|
||||
Poco::BinaryWriter writer(bufferStream, Poco::BinaryWriter::LITTLE_ENDIAN_BYTE_ORDER);
|
||||
writer.writeRaw(NTLMSSP.c_str(), 8);
|
||||
writer << Poco::UInt32(NTLM_MESSAGE_TYPE_NEGOTIATE);
|
||||
writer << flags;
|
||||
writeBufferDesc(writer, domainDesc);
|
||||
writeBufferDesc(writer, workstDesc);
|
||||
writer.writeRaw(utf16Domain);
|
||||
writer.writeRaw(utf16Workstation);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
bool NTLMCredentials::parseChallengeMessage(const unsigned char* buffer, std::size_t size, ChallengeMessage& message)
|
||||
{
|
||||
Poco::MemoryInputStream istr(reinterpret_cast<const char*>(buffer), size);
|
||||
Poco::BinaryReader reader(istr, Poco::BinaryReader::LITTLE_ENDIAN_BYTE_ORDER);
|
||||
|
||||
std::string signature;
|
||||
reader.readRaw(7, signature);
|
||||
if (signature != NTLMSSP) return false;
|
||||
|
||||
Poco::UInt8 zero;
|
||||
reader >> zero;
|
||||
if (zero != 0) return false;
|
||||
|
||||
Poco::UInt32 type;
|
||||
reader >> type;
|
||||
if (type != NTLM_MESSAGE_TYPE_CHALLENGE) return false;
|
||||
|
||||
BufferDesc targetDesc;
|
||||
readBufferDesc(reader, targetDesc);
|
||||
if (targetDesc.offset + targetDesc.length > size) return false;
|
||||
|
||||
reader >> message.flags;
|
||||
|
||||
message.challenge.resize(8);
|
||||
reader.readRaw(reinterpret_cast<char*>(&message.challenge[0]), 8);
|
||||
|
||||
if (message.flags & NTLM_FLAG_NEGOTIATE_TARGET)
|
||||
{
|
||||
Poco::UInt64 reserved;
|
||||
reader >> reserved;
|
||||
}
|
||||
|
||||
BufferDesc targetInfoDesc;
|
||||
if (message.flags & NTLM_FLAG_NEGOTIATE_TARGET)
|
||||
{
|
||||
readBufferDesc(reader, targetInfoDesc);
|
||||
if (targetInfoDesc.offset + targetInfoDesc.length > size) return false;
|
||||
}
|
||||
|
||||
if (targetDesc.length > 0)
|
||||
{
|
||||
if (message.flags & NTLM_FLAG_NEGOTIATE_UNICODE)
|
||||
{
|
||||
Poco::UTF16Encoding utf16(Poco::UTF16Encoding::LITTLE_ENDIAN_BYTE_ORDER);
|
||||
Poco::UTF8Encoding utf8;
|
||||
Poco::TextConverter converter(utf16, utf8);
|
||||
converter.convert(buffer + targetDesc.offset, targetDesc.length, message.target);
|
||||
if (targetDesc.reserved == 0) message.target.resize(std::strlen(message.target.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
message.target.assign(buffer + targetDesc.offset, buffer + targetDesc.offset + targetDesc.length);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetInfoDesc.length > 0)
|
||||
{
|
||||
message.targetInfo.assign(buffer + targetInfoDesc.offset, buffer + targetInfoDesc.offset + targetInfoDesc.length);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::vector<unsigned char> NTLMCredentials::formatAuthenticateMessage(const AuthenticateMessage& message)
|
||||
{
|
||||
Poco::UTF8Encoding utf8;
|
||||
Poco::UTF16Encoding utf16(Poco::UTF16Encoding::LITTLE_ENDIAN_BYTE_ORDER);
|
||||
Poco::TextConverter converter(utf8, utf16);
|
||||
|
||||
std::string utf16Target;
|
||||
converter.convert(message.target, utf16Target);
|
||||
|
||||
std::string utf16Username;
|
||||
converter.convert(message.username, utf16Username);
|
||||
|
||||
std::string utf16Workstation;
|
||||
converter.convert(message.workstation, utf16Workstation);
|
||||
|
||||
std::size_t size = 8 // signature
|
||||
+ 4 // type
|
||||
+ 8 + message.lmResponse.size()
|
||||
+ 8 + message.ntlmResponse.size()
|
||||
+ 8 + utf16Target.size()
|
||||
+ 8 + utf16Username.size()
|
||||
+ 8 + utf16Workstation.size()
|
||||
+ 8 // session key
|
||||
+ 4; // flags
|
||||
|
||||
Poco::UInt32 flags = message.flags | NTLM_FLAG_NEGOTIATE_UNICODE;
|
||||
|
||||
BufferDesc lmDesc(message.lmResponse.size(), 64);
|
||||
BufferDesc ntlmDesc(message.ntlmResponse.size(), lmDesc.offset + lmDesc.length);
|
||||
BufferDesc targetDesc(utf16Target.size(), ntlmDesc.offset + ntlmDesc.length);
|
||||
BufferDesc usernameDesc(utf16Username.size(), targetDesc.offset + targetDesc.length);
|
||||
BufferDesc workstDesc(utf16Workstation.size(), usernameDesc.offset + usernameDesc.length);
|
||||
BufferDesc sessionKeyDesc(0, workstDesc.offset + workstDesc.length);
|
||||
|
||||
std::vector<unsigned char> buffer(size);
|
||||
Poco::MemoryOutputStream bufferStream(reinterpret_cast<char*>(&buffer[0]), buffer.size());
|
||||
Poco::BinaryWriter writer(bufferStream, Poco::BinaryWriter::LITTLE_ENDIAN_BYTE_ORDER);
|
||||
writer.writeRaw(NTLMSSP.c_str(), 8);
|
||||
writer << Poco::UInt32(NTLM_MESSAGE_TYPE_AUTHENTICATE);
|
||||
writeBufferDesc(writer, lmDesc);
|
||||
writeBufferDesc(writer, ntlmDesc);
|
||||
writeBufferDesc(writer, targetDesc);
|
||||
writeBufferDesc(writer, usernameDesc);
|
||||
writeBufferDesc(writer, workstDesc);
|
||||
writeBufferDesc(writer, sessionKeyDesc);
|
||||
writer << flags;
|
||||
writer.writeRaw(reinterpret_cast<const char*>(&message.lmResponse[0]), message.lmResponse.size());
|
||||
writer.writeRaw(reinterpret_cast<const char*>(&message.ntlmResponse[0]), message.ntlmResponse.size());
|
||||
writer.writeRaw(utf16Target);
|
||||
writer.writeRaw(utf16Username);
|
||||
writer.writeRaw(utf16Workstation);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentials::readBufferDesc(Poco::BinaryReader& reader, BufferDesc& desc)
|
||||
{
|
||||
reader >> desc.length >> desc.reserved >> desc.offset;
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentials::writeBufferDesc(Poco::BinaryWriter& writer, const BufferDesc& desc)
|
||||
{
|
||||
writer << desc.length << desc.reserved << desc.offset;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::Net
|
@ -27,7 +27,8 @@ objects = \
|
||||
WebSocketTest WebSocketTestSuite \
|
||||
SyslogTest \
|
||||
OAuth10CredentialsTest OAuth20CredentialsTest OAuthTestSuite \
|
||||
PollSetTest UDPServerTest UDPServerTestSuite
|
||||
PollSetTest UDPServerTest UDPServerTestSuite \
|
||||
NTLMCredentialsTest
|
||||
|
||||
target = testrunner
|
||||
target_version = 1
|
||||
|
@ -140,6 +140,13 @@ void HTTPCredentialsTest::testAuthenticationParams()
|
||||
assertTrue (params["realm"] == "TestDigest");
|
||||
assertTrue (params["nonce"] == "212573bb90170538efad012978ab811f%lu");
|
||||
assertTrue (params.size() == 2);
|
||||
|
||||
params.clear();
|
||||
response.set("WWW-Authenticate", "NTLM TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA");
|
||||
params.fromResponse(response);
|
||||
|
||||
assertTrue (params["NTLM"] == "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA");
|
||||
assertTrue (params.size() == 1);
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "HTTPResponseTest.h"
|
||||
#include "HTTPCookieTest.h"
|
||||
#include "HTTPCredentialsTest.h"
|
||||
#include "NTLMCredentialsTest.h"
|
||||
|
||||
|
||||
CppUnit::Test* HTTPTestSuite::suite()
|
||||
@ -23,6 +24,7 @@ CppUnit::Test* HTTPTestSuite::suite()
|
||||
pSuite->addTest(HTTPResponseTest::suite());
|
||||
pSuite->addTest(HTTPCookieTest::suite());
|
||||
pSuite->addTest(HTTPCredentialsTest::suite());
|
||||
pSuite->addTest(NTLMCredentialsTest::suite());
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
279
Net/testsuite/src/NTLMCredentialsTest.cpp
Normal file
279
Net/testsuite/src/NTLMCredentialsTest.cpp
Normal file
@ -0,0 +1,279 @@
|
||||
//
|
||||
// NTLMCredentialsTest.cpp
|
||||
//
|
||||
// Copyright (c) 2014, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "NTLMCredentialsTest.h"
|
||||
#include "CppUnit/TestCaller.h"
|
||||
#include "CppUnit/TestSuite.h"
|
||||
#include "Poco/Net/NTLMCredentials.h"
|
||||
#include "Poco/DigestEngine.h"
|
||||
|
||||
|
||||
using Poco::Net::NTLMCredentials;
|
||||
|
||||
|
||||
NTLMCredentialsTest::NTLMCredentialsTest(const std::string& name): CppUnit::TestCase(name)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
NTLMCredentialsTest::~NTLMCredentialsTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::testNonce()
|
||||
{
|
||||
std::vector<unsigned char> nonce1 = NTLMCredentials::createNonce();
|
||||
assertTrue (nonce1.size() == 8);
|
||||
|
||||
std::vector<unsigned char> nonce2 = NTLMCredentials::createNonce();
|
||||
assertTrue (nonce2.size() == 8);
|
||||
|
||||
assertTrue (nonce1 != nonce2);
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::testPasswordHash()
|
||||
{
|
||||
std::vector<unsigned char> passHash = NTLMCredentials::createPasswordHash("SecREt01");
|
||||
std::string passHashHex = Poco::DigestEngine::digestToHex(passHash);
|
||||
assertTrue (passHashHex == "cd06ca7c7e10c99b1d33b7485a2ed808");
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::testNTLMv2Hash()
|
||||
{
|
||||
std::vector<unsigned char> ntlm2Hash = NTLMCredentials::createNTLMv2Hash("user", "DOMAIN", "SecREt01");
|
||||
std::string ntlm2HashHex = Poco::DigestEngine::digestToHex(ntlm2Hash);
|
||||
assertTrue (ntlm2HashHex == "04b8e0ba74289cc540826bab1dee63ae");
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::testLMv2Response()
|
||||
{
|
||||
static const unsigned char CHALLENGE[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
|
||||
static const unsigned char NONCE[] = {0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
|
||||
std::vector<unsigned char> challenge(CHALLENGE, CHALLENGE + 8);
|
||||
std::vector<unsigned char> nonce(NONCE, NONCE + 8);
|
||||
|
||||
std::vector<unsigned char> ntlm2Hash = NTLMCredentials::createNTLMv2Hash("user", "DOMAIN", "SecREt01");
|
||||
std::vector<unsigned char> lm2Response = NTLMCredentials::createLMv2Response(ntlm2Hash, challenge, nonce);
|
||||
|
||||
std::string lm2ResponseHex = Poco::DigestEngine::digestToHex(lm2Response);
|
||||
assertTrue (lm2ResponseHex == "d6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344");
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::testNTLMv2Response()
|
||||
{
|
||||
static const unsigned char CHALLENGE[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
|
||||
static const unsigned char NONCE[] = {0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
static const unsigned char TARGET_INFO[] = {
|
||||
0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x4f, 0x00,
|
||||
0x4d, 0x00, 0x41, 0x00, 0x49, 0x00, 0x4e, 0x00,
|
||||
0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x45, 0x00,
|
||||
0x52, 0x00, 0x56, 0x00, 0x45, 0x00, 0x52, 0x00,
|
||||
0x04, 0x00, 0x14, 0x00, 0x64, 0x00, 0x6f, 0x00,
|
||||
0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00,
|
||||
0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00,
|
||||
0x03, 0x00, 0x22, 0x00, 0x73, 0x00, 0x65, 0x00,
|
||||
0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00,
|
||||
0x2e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00,
|
||||
0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00,
|
||||
0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00
|
||||
};
|
||||
|
||||
std::vector<unsigned char> challenge(CHALLENGE, CHALLENGE + 8);
|
||||
std::vector<unsigned char> nonce(NONCE, NONCE + 8);
|
||||
std::vector<unsigned char> targetInfo(TARGET_INFO, TARGET_INFO + sizeof(TARGET_INFO));
|
||||
|
||||
std::vector<unsigned char> ntlm2Hash = NTLMCredentials::createNTLMv2Hash("user", "DOMAIN", "SecREt01");
|
||||
Poco::UInt64 timestamp = Poco::UInt64(12700317600)*10000000;
|
||||
|
||||
std::vector<unsigned char> ntlm2Response = NTLMCredentials::createNTLMv2Response(
|
||||
ntlm2Hash,
|
||||
challenge,
|
||||
nonce,
|
||||
targetInfo,
|
||||
timestamp);
|
||||
|
||||
std::string ntlm2ResponseHex = Poco::DigestEngine::digestToHex(ntlm2Response);
|
||||
|
||||
assertTrue (ntlm2ResponseHex ==
|
||||
"cbabbca713eb795d04c97abc01ee4983"
|
||||
"01010000000000000090d336b734c301"
|
||||
"ffffff00112233440000000002000c00"
|
||||
"44004f004d00410049004e0001000c00"
|
||||
"53004500520056004500520004001400"
|
||||
"64006f006d00610069006e002e006300"
|
||||
"6f006d00030022007300650072007600"
|
||||
"650072002e0064006f006d0061006900"
|
||||
"6e002e0063006f006d00000000000000"
|
||||
"0000"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::testFormatNegotiateMessage()
|
||||
{
|
||||
NTLMCredentials::NegotiateMessage msg1;
|
||||
msg1.flags = 0;
|
||||
|
||||
std::vector<unsigned char> msg1Buffer = NTLMCredentials::formatNegotiateMessage(msg1);
|
||||
std::string msg1BufferHex = Poco::DigestEngine::digestToHex(msg1Buffer);
|
||||
|
||||
assertTrue (msg1BufferHex == "4e544c4d53535000010000000582080000000000180000000000000018000000");
|
||||
|
||||
msg1.domain = "DOMAIN";
|
||||
msg1Buffer = NTLMCredentials::formatNegotiateMessage(msg1);
|
||||
msg1BufferHex = Poco::DigestEngine::digestToHex(msg1Buffer);
|
||||
|
||||
assertTrue (msg1BufferHex == "4e544c4d5353500001000000059208000c000c0018000000000000002400000044004f004d00410049004e00");
|
||||
|
||||
msg1.workstation = "WORKST";
|
||||
msg1Buffer = NTLMCredentials::formatNegotiateMessage(msg1);
|
||||
msg1BufferHex = Poco::DigestEngine::digestToHex(msg1Buffer);
|
||||
|
||||
assertTrue (msg1BufferHex == "4e544c4d535350000100000005b208000c000c00180000000c000c002400000044004f004d00410049004e0057004f0052004b0053005400");
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::testParseChallengeMessage()
|
||||
{
|
||||
const unsigned char BUFFER[] = {
|
||||
0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00,
|
||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
|
||||
};
|
||||
|
||||
NTLMCredentials::ChallengeMessage msg2;
|
||||
bool ok = NTLMCredentials::parseChallengeMessage(BUFFER, sizeof(BUFFER), msg2);
|
||||
|
||||
assertTrue (ok);
|
||||
assertTrue (msg2.flags == (NTLMCredentials::NTLM_FLAG_NEGOTIATE_OEM | NTLMCredentials::NTLM_FLAG_NEGOTIATE_NTLM));
|
||||
assertTrue (msg2.challenge.size() == 8);
|
||||
assertTrue (msg2.targetInfo.empty());
|
||||
|
||||
assertTrue (msg2.challenge[0] == 0x01);
|
||||
assertTrue (msg2.challenge[1] == 0x23);
|
||||
assertTrue (msg2.challenge[7] == 0xef);
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::testParseChallengeMessageWithTargetInfo()
|
||||
{
|
||||
const unsigned char BUFFER[] = {
|
||||
0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00,
|
||||
0x30, 0x00, 0x00, 0x00, 0x01, 0x02, 0x81, 0x00,
|
||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x62, 0x00, 0x62, 0x00, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x41, 0x00,
|
||||
0x49, 0x00, 0x4e, 0x00, 0x02, 0x00, 0x0c, 0x00,
|
||||
0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x41, 0x00,
|
||||
0x49, 0x00, 0x4e, 0x00, 0x01, 0x00, 0x0c, 0x00,
|
||||
0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56, 0x00,
|
||||
0x45, 0x00, 0x52, 0x00, 0x04, 0x00, 0x14, 0x00,
|
||||
0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00,
|
||||
0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x63, 0x00,
|
||||
0x6f, 0x00, 0x6d, 0x00, 0x03, 0x00, 0x22, 0x00,
|
||||
0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00,
|
||||
0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x64, 0x00,
|
||||
0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00,
|
||||
0x6e, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00,
|
||||
0x6d, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
NTLMCredentials::ChallengeMessage msg2;
|
||||
bool ok = NTLMCredentials::parseChallengeMessage(BUFFER, sizeof(BUFFER), msg2);
|
||||
|
||||
assertTrue (ok);
|
||||
assertTrue (msg2.flags == (NTLMCredentials::NTLM_FLAG_NEGOTIATE_UNICODE | NTLMCredentials::NTLM_FLAG_NEGOTIATE_NTLM | NTLMCredentials::NTLM_FLAG_NEGOTIATE_TARGET | NTLMCredentials::NTLM_FLAG_TARGET_DOMAIN));
|
||||
assertTrue (msg2.challenge.size() == 8);
|
||||
assertTrue (msg2.target == "DOMAIN");
|
||||
|
||||
assertTrue (msg2.targetInfo.size() == 98);
|
||||
|
||||
assertTrue (msg2.challenge[0] == 0x01);
|
||||
assertTrue (msg2.challenge[1] == 0x23);
|
||||
assertTrue (msg2.challenge[7] == 0xef);
|
||||
|
||||
assertTrue (msg2.targetInfo[0] == 0x02);
|
||||
assertTrue (msg2.targetInfo[1] == 0x00);
|
||||
assertTrue (msg2.targetInfo[97] == 0x00);
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::testFormatAuthenticateMessage()
|
||||
{
|
||||
const unsigned char LM[] = {
|
||||
0xc3, 0x37, 0xcd, 0x5c, 0xbd, 0x44, 0xfc, 0x97,
|
||||
0x82, 0xa6, 0x67, 0xaf, 0x6d, 0x42, 0x7c, 0x6d,
|
||||
0xe6, 0x7c, 0x20, 0xc2, 0xd3, 0xe7, 0x7c, 0x56
|
||||
};
|
||||
const unsigned char NTLM[] = {
|
||||
0x25, 0xa9, 0x8c, 0x1c, 0x31, 0xe8, 0x18, 0x47,
|
||||
0x46, 0x6b, 0x29, 0xb2, 0xdf, 0x46, 0x80, 0xf3,
|
||||
0x99, 0x58, 0xfb, 0x8c, 0x21, 0x3a, 0x9c, 0xc6
|
||||
};
|
||||
|
||||
NTLMCredentials::AuthenticateMessage msg3;
|
||||
msg3.flags = NTLMCredentials::NTLM_FLAG_NEGOTIATE_UNICODE | NTLMCredentials::NTLM_FLAG_NEGOTIATE_NTLM;
|
||||
msg3.target = "DOMAIN";
|
||||
msg3.username = "user";
|
||||
msg3.workstation = "WORKSTATION";
|
||||
msg3.lmResponse.assign(LM, LM + sizeof(LM));
|
||||
msg3.ntlmResponse.assign(NTLM, NTLM + sizeof(NTLM));
|
||||
|
||||
std::vector<unsigned char> msg3Buffer = NTLMCredentials::formatAuthenticateMessage(msg3);
|
||||
std::string msg3BufferHex = Poco::DigestEngine::digestToHex(msg3Buffer);
|
||||
|
||||
assertTrue (msg3BufferHex ==
|
||||
"4e544c4d5353500003000000180018004000000018001800"
|
||||
"580000000c000c0070000000080008007c00000016001600"
|
||||
"84000000000000009a00000001020000c337cd5cbd44fc97"
|
||||
"82a667af6d427c6de67c20c2d3e77c5625a98c1c31e81847"
|
||||
"466b29b2df4680f39958fb8c213a9cc644004f004d004100"
|
||||
"49004e00750073006500720057004f0052004b0053005400"
|
||||
"4100540049004f004e00"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::setUp()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void NTLMCredentialsTest::tearDown()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CppUnit::Test* NTLMCredentialsTest::suite()
|
||||
{
|
||||
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("NTLMCredentialsTest");
|
||||
|
||||
CppUnit_addTest(pSuite, NTLMCredentialsTest, testNonce);
|
||||
CppUnit_addTest(pSuite, NTLMCredentialsTest, testPasswordHash);
|
||||
CppUnit_addTest(pSuite, NTLMCredentialsTest, testNTLMv2Hash);
|
||||
CppUnit_addTest(pSuite, NTLMCredentialsTest, testLMv2Response);
|
||||
CppUnit_addTest(pSuite, NTLMCredentialsTest, testNTLMv2Response);
|
||||
CppUnit_addTest(pSuite, NTLMCredentialsTest, testFormatNegotiateMessage);
|
||||
CppUnit_addTest(pSuite, NTLMCredentialsTest, testParseChallengeMessage);
|
||||
CppUnit_addTest(pSuite, NTLMCredentialsTest, testParseChallengeMessageWithTargetInfo);
|
||||
CppUnit_addTest(pSuite, NTLMCredentialsTest, testFormatAuthenticateMessage);
|
||||
|
||||
return pSuite;
|
||||
}
|
46
Net/testsuite/src/NTLMCredentialsTest.h
Normal file
46
Net/testsuite/src/NTLMCredentialsTest.h
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// NTLMCredentialsTest.h
|
||||
//
|
||||
// Definition of the NTLMCredentialsTest class.
|
||||
//
|
||||
// Copyright (c) 2019, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#ifndef NTLMCredentialsTest_INCLUDED
|
||||
#define NTLMCredentialsTest_INCLUDED
|
||||
|
||||
|
||||
#include "Poco/Net/Net.h"
|
||||
#include "CppUnit/TestCase.h"
|
||||
|
||||
|
||||
class NTLMCredentialsTest: public CppUnit::TestCase
|
||||
{
|
||||
public:
|
||||
NTLMCredentialsTest(const std::string& name);
|
||||
~NTLMCredentialsTest();
|
||||
|
||||
void testNonce();
|
||||
void testPasswordHash();
|
||||
void testNTLMv2Hash();
|
||||
void testLMv2Response();
|
||||
void testNTLMv2Response();
|
||||
void testFormatNegotiateMessage();
|
||||
void testParseChallengeMessage();
|
||||
void testParseChallengeMessageWithTargetInfo();
|
||||
void testFormatAuthenticateMessage();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
||||
static CppUnit::Test* suite();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
#endif // NTLMCredentialsTest_INCLUDED
|
Loading…
x
Reference in New Issue
Block a user