NTLM (proxy) authentication support for HTTPClientSession

This commit is contained in:
Günter Obiltschnig 2019-03-18 16:58:57 +01:00
parent da7de5e586
commit 0f3f11a3b2
22 changed files with 1711 additions and 279 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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();
@ -110,47 +125,47 @@ public:
///
/// The host must not be changed once there is an
/// open connection to the server.
const std::string& getHost() const;
/// Returns the host name of the target HTTP server.
void setPort(Poco::UInt16 port);
/// Sets the port number of the target HTTP server.
///
/// The port number must not be changed once there is an
/// open connection to the server.
Poco::UInt16 getPort() const;
/// Returns the port number of the target HTTP server.
void setProxy(const std::string& host, Poco::UInt16 port = HTTPSession::HTTP_PORT);
/// Sets the proxy host name and port number.
void setProxyHost(const std::string& host);
/// Sets the host name of the proxy server.
void setProxyPort(Poco::UInt16 port);
/// Sets the port number of the proxy server.
const std::string& getProxyHost() const;
/// Returns the proxy host name.
Poco::UInt16 getProxyPort() const;
/// Returns the proxy port number.
void setProxyCredentials(const std::string& username, const std::string& password);
/// Sets the username and password for proxy authentication.
/// Only Basic authentication is supported.
void setProxyUsername(const std::string& username);
/// Sets the username for proxy authentication.
/// Only Basic authentication is supported.
const std::string& getProxyUsername() const;
/// Returns the username for proxy authentication.
void setProxyPassword(const std::string& password);
/// Sets the password for proxy authentication.
/// Sets the password for proxy authentication.
/// Only Basic authentication is supported.
const std::string& getProxyPassword() const;
@ -177,10 +192,10 @@ public:
void setKeepAliveTimeout(const Poco::Timespan& timeout);
/// Sets the connection timeout for HTTP connections.
const Poco::Timespan& getKeepAliveTimeout() const;
/// Returns the connection timeout for HTTP connections.
virtual std::ostream& sendRequest(HTTPRequest& request);
/// Sends the header for the given HTTP request to
/// the server.
@ -200,9 +215,9 @@ public:
/// be reused and persistent connections are enabled
/// to ensure a new connection will be set up
/// for the next request.
virtual std::istream& receiveResponse(HTTPResponse& response);
/// Receives the header for the response to the previous
/// Receives the header for the response to the previous
/// HTTP request.
///
/// The returned input stream can be used to read
@ -213,7 +228,7 @@ public:
/// It must be ensured that the response stream
/// is fully consumed before sending a new request
/// and persistent connections are enabled. Otherwise,
/// the unread part of the response body may be treated as
/// the unread part of the response body may be treated as
/// part of the next request's response header, resulting
/// in a Poco::Net::MessageException being thrown.
///
@ -224,17 +239,17 @@ public:
/// be reused and persistent connections are enabled
/// to ensure a new connection will be set up
/// for the next request.
virtual bool peekResponse(HTTPResponse& response);
/// If the request contains a "Expect: 100-continue" header,
/// (see HTTPRequest::setExpectContinue()) this method can be
/// used to check whether the server has sent a 100 Continue response
/// (see HTTPRequest::setExpectContinue()) this method can be
/// used to check whether the server has sent a 100 Continue response
/// before continuing with the request, i.e. sending the request body,
/// after calling sendRequest().
///
/// Returns true if the server has responded with 100 Continue,
/// otherwise false. The HTTPResponse object contains the
/// response sent by the server.
/// response sent by the server.
///
/// In any case, receiveResponse() must be called afterwards as well in
/// order to complete the request. The same HTTPResponse object
@ -254,11 +269,11 @@ public:
/// or receiveResponse() throws an exception, or
/// the request or response stream changes into
/// fail or bad state, but not eof state).
virtual bool secure() const;
/// Return true iff the session uses SSL or TLS,
/// or false otherwise.
bool bypassProxy() const;
/// Returns true if the proxy should be bypassed
/// for the current host.
@ -268,32 +283,45 @@ protected:
{
DEFAULT_KEEP_ALIVE_TIMEOUT = 8
};
void reconnect();
/// Connects the underlying socket to the HTTP server.
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").
virtual bool mustReconnect() const;
/// Checks if we can reuse a persistent connection.
virtual void proxyAuthenticate(HTTPRequest& request);
/// 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.
void proxyTunnel();
/// Calls proxyConnect() and attaches the resulting StreamSocket
/// to the HTTPClientSession.
@ -310,9 +338,13 @@ private:
bool _responseReceived;
Poco::SharedPtr<std::ostream> _pRequestStream;
Poco::SharedPtr<std::istream> _pResponseStream;
HTTPBasicCredentials _proxyBasicCreds;
HTTPDigestCredentials _proxyDigestCreds;
HTTPNTLMCredentials _proxyNTLMCreds;
bool _ntlmProxyAuthenticated;
static ProxyConfig _globalProxyConfig;
HTTPClientSession(const HTTPClientSession&);
HTTPClientSession& operator = (const HTTPClientSession&);

View File

@ -20,6 +20,7 @@
#include "Poco/Net/HTTPDigestCredentials.h"
#include "Poco/Net/HTTPNTLMCredentials.h"
namespace Poco {
@ -37,13 +38,13 @@ 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
/// the username and password.
/// Poco::Net::HTTPCredentials creds("user", "s3cr3t");
///
///
/// Second, send the HTTP request with Poco::Net::HTTPClientSession.
/// Poco::Net::HTTPClientSession session("pocoproject.org");
/// Poco::Net::HTTPRequest request(HTTPRequest::HTTP_GET, "/index.html", HTTPMessage::HTTP_1_1);
@ -51,7 +52,7 @@ class Net_API HTTPCredentials
/// Poco::Net::HTTPResponse;
/// std::istream& istr = session.receiveResponse(response);
///
/// If the server responds with a 401 status, authenticate the
/// If the server responds with a 401 status, authenticate the
/// request and resend it:
/// if (response.getStatus() == Poco::Net::HTTPResponse::HTTP_UNAUTHORIZED)
/// {
@ -61,7 +62,7 @@ class Net_API HTTPCredentials
/// }
///
/// To perform multiple authenticated requests, call updateAuthInfo()
/// instead of authenticate() on subsequent requests.
/// instead of authenticate() on subsequent requests.
/// creds.updateAuthInfo(request);
/// session.sendRequest(request);
/// ...
@ -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;
};
@ -172,7 +183,7 @@ inline const std::string& HTTPCredentials::getUsername() const
return _digest.getUsername();
}
inline void HTTPCredentials::setPassword(const std::string& password)
{
_digest.setPassword(password);

View File

@ -39,22 +39,22 @@ class Net_API HTTPMessage: public MessageHeader
public:
void setVersion(const std::string& version);
/// Sets the HTTP version for this message.
const std::string& getVersion() const;
/// Returns the HTTP version for this message.
void setContentLength(std::streamsize length);
/// Sets the Content-Length header.
///
/// If length is UNKNOWN_CONTENT_LENGTH, removes
/// the Content-Length header.
std::streamsize getContentLength() const;
/// Returns the content length for this message,
/// which may be UNKNOWN_CONTENT_LENGTH if
/// no Content-Length header is present.
#if defined(POCO_HAVE_INT64)
#if defined(POCO_HAVE_INT64)
void setContentLength64(Poco::Int64 length);
/// Sets the Content-Length header.
///
@ -89,30 +89,30 @@ public:
/// Normally, this is the value of the Transfer-Encoding
/// header field. If no such field is present,
/// returns IDENTITY_TRANSFER_CODING.
void setChunkedTransferEncoding(bool flag);
/// If flag is true, sets the Transfer-Encoding header to
/// chunked. Otherwise, removes the Transfer-Encoding
/// header.
bool getChunkedTransferEncoding() const;
/// Returns true if the Transfer-Encoding header is set
/// and its value is chunked.
void setContentType(const std::string& mediaType);
/// Sets the content type for this message.
///
/// Specify NO_CONTENT_TYPE to remove the
/// Content-Type header.
void setContentType(const MediaType& mediaType);
void setContentType(const MediaType& mediaType);
/// Sets the content type for this message.
const std::string& getContentType() const;
/// Returns the content type for this message.
///
/// If no Content-Type header is present,
/// returns UNKNOWN_CONTENT_TYPE.
/// If no Content-Type header is present,
/// returns UNKNOWN_CONTENT_TYPE.
void setKeepAlive(bool keepAlive);
/// Sets the value of the Connection header field.
@ -134,12 +134,13 @@ public:
static const int UNKNOWN_CONTENT_LENGTH;
static const std::string UNKNOWN_CONTENT_TYPE;
static const std::string CONTENT_LENGTH;
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;
};

View 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

View File

@ -38,20 +38,26 @@ class Net_API HTTPRequest: public HTTPMessage
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).
HTTPRequest(const std::string& method, const std::string& uri);
/// Creates a HTTP/1.0 request with the given method and URI.
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.
@ -60,20 +66,20 @@ public:
void setURI(const std::string& uri);
/// Sets the request URI.
const std::string& getURI() const;
/// Returns the request URI.
void setHost(const std::string& host);
/// Sets the value of the Host header field.
void setHost(const std::string& host, Poco::UInt16 port);
/// Sets the value of the Host header field.
///
/// If the given port number is a non-standard
/// port number (other than 80 or 443), it is
/// included in the Host header field.
const std::string& getHost() const;
/// Returns the value of the Host header field.
///
@ -83,7 +89,7 @@ public:
void setCookies(const NameValueCollection& cookies);
/// Adds a Cookie header with the names and
/// values from cookies.
void getCookies(NameValueCollection& cookies) const;
/// Fills cookies with the cookies extracted
/// from the Cookie headers in the request.
@ -91,18 +97,21 @@ public:
bool hasCredentials() const;
/// Returns true iff the request contains authentication
/// information in the form of an Authorization header.
void getCredentials(std::string& scheme, std::string& authInfo) const;
/// Returns the authentication scheme and additional authentication
/// information contained in this request.
///
/// Throws a NotAuthenticatedException if no authentication information
/// is contained in the request.
void setCredentials(const std::string& scheme, const std::string& authInfo);
/// 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.
@ -110,22 +119,25 @@ public:
void setExpectContinue(bool expectContinue);
/// Adds a "Expect: 100-continue" header to the request if
/// expectContinue is true, otherwise removes the Expect header.
bool hasProxyCredentials() const;
/// Returns true iff the request contains proxy authentication
/// information in the form of an Proxy-Authorization header.
void getProxyCredentials(std::string& scheme, std::string& authInfo) const;
/// Returns the proxy authentication scheme and additional proxy authentication
/// information contained in this request.
///
/// Throws a NotAuthenticatedException if no proxy authentication information
/// is contained in the request.
void setProxyCredentials(const std::string& scheme, const std::string& authInfo);
/// 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.
@ -133,7 +145,7 @@ public:
void read(std::istream& istr);
/// Reads the HTTP request from the
/// given input stream.
static const std::string HTTP_GET;
static const std::string HTTP_HEAD;
static const std::string HTTP_PUT;
@ -143,7 +155,7 @@ public:
static const std::string HTTP_TRACE;
static const std::string HTTP_CONNECT;
static const std::string HTTP_PATCH;
static const std::string HOST;
static const std::string COOKIE;
static const std::string AUTHORIZATION;
@ -158,7 +170,7 @@ protected:
///
/// Throws a NotAuthenticatedException if no authentication information
/// is contained in the request.
void setCredentials(const std::string& header, const std::string& scheme, const std::string& authInfo);
/// Writes the authentication scheme and information for
/// this request to the given header.
@ -170,12 +182,9 @@ private:
MAX_URI_LENGTH = 16384,
MAX_VERSION_LENGTH = 8
};
std::string _method;
std::string _uri;
HTTPRequest(const HTTPRequest&);
HTTPRequest& operator = (const HTTPRequest&);
};

View File

@ -113,7 +113,7 @@ public:
HTTPResponse();
/// Creates the HTTPResponse with OK status.
HTTPResponse(HTTPStatus status, const std::string& reason);
/// Creates the HTTPResponse with the given status
/// and reason phrase.
@ -121,41 +121,47 @@ public:
HTTPResponse(const std::string& version, HTTPStatus status, const std::string& reason);
/// 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.
///
/// Does not change the reason phrase.
HTTPStatus getStatus() const;
/// Returns the HTTP status code.
void setStatus(const std::string& status);
/// Sets the HTTP status code.
///
/// The string must contain a valid
/// HTTP numerical status code.
void setReason(const std::string& reason);
/// Sets the HTTP reason phrase.
const std::string& getReason() const;
/// Returns the HTTP reason phrase.
void setStatusAndReason(HTTPStatus status, const std::string& reason);
/// Sets the HTTP status code and reason phrase.
void setStatusAndReason(HTTPStatus status);
/// Sets the HTTP status code and reason phrase.
///
@ -163,7 +169,7 @@ public:
void setDate(const Poco::Timestamp& dateTime);
/// Sets the Date header to the given date/time value.
Poco::Timestamp getDate() const;
/// Returns the value of the Date header.
@ -187,7 +193,7 @@ public:
/// given input stream.
///
/// 100 Continue responses are ignored.
static const std::string& getReasonForStatus(HTTPStatus status);
/// Returns an appropriate reason phrase
/// for the given status code.
@ -254,7 +260,7 @@ public:
static const std::string HTTP_REASON_NOT_EXTENDED;
static const std::string HTTP_REASON_NETWORK_AUTHENTICATION_REQUIRED;
static const std::string HTTP_REASON_UNKNOWN;
static const std::string DATE;
static const std::string SET_COOKIE;
@ -265,12 +271,9 @@ private:
MAX_STATUS_LENGTH = 3,
MAX_REASON_LENGTH = 512
};
HTTPStatus _status;
std::string _reason;
HTTPResponse(const HTTPResponse&);
HTTPResponse& operator = (const HTTPResponse&);
};

View 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

View File

@ -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,21 +172,27 @@ void HTTPAuthenticationParams::setRealm(const std::string& realm)
std::string HTTPAuthenticationParams::toString() const
{
ConstIterator iter = begin();
std::string result;
if (iter != end())
if (size() == 1 && find(NTLM) != end())
{
formatParameter(result, iter->first, iter->second);
++iter;
result = get(NTLM);
}
for (; iter != end(); ++iter)
else
{
result.append(", ");
formatParameter(result, iter->first, iter->second);
}
ConstIterator iter = begin();
if (iter != end())
{
formatParameter(result, iter->first, iter->second);
++iter;
}
for (; iter != end(); ++iter)
{
result.append(", ");
formatParameter(result, iter->first, iter->second);
}
}
return result;
}

View File

@ -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,50 +210,28 @@ 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;
_expectResponseBody = request.getMethod() != HTTPRequest::HTTP_HEAD;
const std::string& method = request.getMethod();
if (request.getChunkedTransferEncoding())
{
HTTPHeaderOutputStream hos(*this);
request.write(hos);
_pRequestStream = new HTTPChunkedOutputStream(*this);
}
else if (request.hasContentLength())
{
Poco::CountingOutputStream cs;
request.write(cs);
#if POCO_HAVE_INT64
_pRequestStream = new HTTPFixedLengthOutputStream(*this, request.getContentLength64() + cs.chars());
#else
_pRequestStream = new HTTPFixedLengthOutputStream(*this, request.getContentLength() + cs.chars());
#endif
request.write(*_pRequestStream);
}
else if ((method != HTTPRequest::HTTP_PUT && method != HTTPRequest::HTTP_POST && method != HTTPRequest::HTTP_PATCH) || request.has(HTTPRequest::UPGRADE))
{
Poco::CountingOutputStream cs;
request.write(cs);
_pRequestStream = new HTTPFixedLengthOutputStream(*this, cs.chars());
request.write(*_pRequestStream);
}
else
{
_pRequestStream = new HTTPOutputStream(*this);
request.write(*_pRequestStream);
}
_lastRequest.update();
return *_pRequestStream;
return sendRequestImpl(request);
}
catch (Exception&)
{
@ -260,6 +241,48 @@ std::ostream& HTTPClientSession::sendRequest(HTTPRequest& request)
}
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())
{
HTTPHeaderOutputStream hos(*this);
request.write(hos);
_pRequestStream = new HTTPChunkedOutputStream(*this);
}
else if (request.hasContentLength())
{
Poco::CountingOutputStream cs;
request.write(cs);
#if POCO_HAVE_INT64
_pRequestStream = new HTTPFixedLengthOutputStream(*this, request.getContentLength64() + cs.chars());
#else
_pRequestStream = new HTTPFixedLengthOutputStream(*this, request.getContentLength() + cs.chars());
#endif
request.write(*_pRequestStream);
}
else if ((method != HTTPRequest::HTTP_PUT && method != HTTPRequest::HTTP_POST && method != HTTPRequest::HTTP_PATCH) || request.has(HTTPRequest::UPGRADE))
{
Poco::CountingOutputStream cs;
request.write(cs);
_pRequestStream = new HTTPFixedLengthOutputStream(*this, cs.chars());
request.write(*_pRequestStream);
}
else
{
_pRequestStream = new HTTPOutputStream(*this);
request.write(*_pRequestStream);
}
_lastRequest.update();
return *_pRequestStream;
}
std::istream& HTTPClientSession::receiveResponse(HTTPResponse& response)
{
_pRequestStream = 0;
@ -409,20 +432,89 @@ 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();
}
StreamSocket HTTPClientSession::proxyConnect()
{
ProxyConfig emptyProxyConfig;
@ -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);

View File

@ -75,34 +75,45 @@ void HTTPCredentials::authenticate(HTTPRequest& request, const HTTPResponse& res
{
for (HTTPResponse::ConstIterator iter = response.find(HTTPAuthenticationParams::WWW_AUTHENTICATE); iter != response.end(); ++iter)
{
if (isBasicCredentials(iter->second))
if (isBasicCredentials(iter->second))
{
HTTPBasicCredentials(_digest.getUsername(), _digest.getPassword()).authenticate(request);
return;
}
else if (isDigestCredentials(iter->second))
}
else if (isDigestCredentials(iter->second))
{
_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;
}
}
}
void HTTPCredentials::updateAuthInfo(HTTPRequest& request)
{
if (request.has(HTTPRequest::AUTHORIZATION))
if (request.has(HTTPRequest::AUTHORIZATION))
{
const std::string& authorization = request.get(HTTPRequest::AUTHORIZATION);
if (isBasicCredentials(authorization))
if (isBasicCredentials(authorization))
{
HTTPBasicCredentials(_digest.getUsername(), _digest.getPassword()).authenticate(request);
}
else if (isDigestCredentials(authorization))
}
else if (isDigestCredentials(authorization))
{
_digest.updateAuthInfo(request);
}
else if (isNTLMCredentials(authorization))
{
_ntlm.updateAuthInfo(request);
}
}
}
@ -111,34 +122,45 @@ void HTTPCredentials::proxyAuthenticate(HTTPRequest& request, const HTTPResponse
{
for (HTTPResponse::ConstIterator iter = response.find(HTTPAuthenticationParams::PROXY_AUTHENTICATE); iter != response.end(); ++iter)
{
if (isBasicCredentials(iter->second))
if (isBasicCredentials(iter->second))
{
HTTPBasicCredentials(_digest.getUsername(), _digest.getPassword()).proxyAuthenticate(request);
return;
}
else if (isDigestCredentials(iter->second))
}
else if (isDigestCredentials(iter->second))
{
_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;
}
}
}
void HTTPCredentials::updateProxyAuthInfo(HTTPRequest& request)
{
if (request.has(HTTPRequest::PROXY_AUTHORIZATION))
if (request.has(HTTPRequest::PROXY_AUTHORIZATION))
{
const std::string& authorization = request.get(HTTPRequest::PROXY_AUTHORIZATION);
if (isBasicCredentials(authorization))
if (isBasicCredentials(authorization))
{
HTTPBasicCredentials(_digest.getUsername(), _digest.getPassword()).proxyAuthenticate(request);
}
else if (isDigestCredentials(authorization))
}
else if (isDigestCredentials(authorization))
{
_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,16 +213,22 @@ 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(':');
if (p != std::string::npos)
if (p != std::string::npos)
{
username.assign(userInfo, 0, p);
password.assign(userInfo, p + 1, std::string::npos);
}
else
}
else
{
username.assign(userInfo);
password.clear();
@ -198,7 +238,7 @@ void HTTPCredentials::extractCredentials(const std::string& userInfo, std::strin
void HTTPCredentials::extractCredentials(const Poco::URI& uri, std::string& username, std::string& password)
{
if (!uri.getUserInfo().empty())
if (!uri.getUserInfo().empty())
{
extractCredentials(uri.getUserInfo(), username, password);
}

View File

@ -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;
@ -74,7 +93,7 @@ void HTTPMessage::setContentLength(std::streamsize length)
erase(CONTENT_LENGTH);
}
std::streamsize HTTPMessage::getContentLength() const
{
const std::string& contentLength = get(CONTENT_LENGTH, EMPTY);
@ -89,7 +108,7 @@ std::streamsize HTTPMessage::getContentLength() const
}
#if defined(POCO_HAVE_INT64)
#if defined(POCO_HAVE_INT64)
void HTTPMessage::setContentLength64(Poco::Int64 length)
{
if (length != UNKNOWN_CONTENT_LENGTH)
@ -98,7 +117,7 @@ void HTTPMessage::setContentLength64(Poco::Int64 length)
erase(CONTENT_LENGTH);
}
Poco::Int64 HTTPMessage::getContentLength64() const
{
const std::string& contentLength = get(CONTENT_LENGTH, EMPTY);
@ -108,7 +127,7 @@ Poco::Int64 HTTPMessage::getContentLength64() const
}
else return UNKNOWN_CONTENT_LENGTH;
}
#endif // defined(POCO_HAVE_INT64)
#endif // defined(POCO_HAVE_INT64)
void HTTPMessage::setTransferEncoding(const std::string& transferEncoding)
@ -134,13 +153,13 @@ void HTTPMessage::setChunkedTransferEncoding(bool flag)
setTransferEncoding(IDENTITY_TRANSFER_ENCODING);
}
bool HTTPMessage::getChunkedTransferEncoding() const
{
return icompare(getTransferEncoding(), CHUNKED_TRANSFER_ENCODING) == 0;
}
void HTTPMessage::setContentType(const std::string& mediaType)
{
if (mediaType.empty())
@ -155,7 +174,7 @@ void HTTPMessage::setContentType(const MediaType& mediaType)
setContentType(mediaType.toString());
}
const std::string& HTTPMessage::getContentType() const
{
return get(CONTENT_TYPE, UNKNOWN_CONTENT_TYPE);

View 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

View File

@ -50,7 +50,7 @@ HTTPRequest::HTTPRequest():
{
}
HTTPRequest::HTTPRequest(const std::string& version):
HTTPMessage(version),
_method(HTTP_GET),
@ -58,7 +58,7 @@ HTTPRequest::HTTPRequest(const std::string& version):
{
}
HTTPRequest::HTTPRequest(const std::string& method, const std::string& uri):
_method(method),
_uri(uri)
@ -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;
@ -96,7 +116,7 @@ void HTTPRequest::setHost(const std::string& host)
set(HOST, host);
}
void HTTPRequest::setHost(const std::string& host, Poco::UInt16 port)
{
std::string value;
@ -110,7 +130,7 @@ void HTTPRequest::setHost(const std::string& host, Poco::UInt16 port)
else
{
value.append(host);
}
}
if (port != 80 && port != 443)
{
@ -120,7 +140,7 @@ void HTTPRequest::setHost(const std::string& host, Poco::UInt16 port)
setHost(value);
}
const std::string& HTTPRequest::getHost() const
{
return get(HOST);
@ -142,7 +162,7 @@ void HTTPRequest::setCookies(const NameValueCollection& cookies)
add(COOKIE, cookie);
}
void HTTPRequest::getCookies(NameValueCollection& cookies) const
{
NameValueCollection::ConstIterator it = find(COOKIE);
@ -159,37 +179,49 @@ bool HTTPRequest::hasCredentials() const
return has(AUTHORIZATION);
}
void HTTPRequest::getCredentials(std::string& scheme, std::string& authInfo) const
{
getCredentials(AUTHORIZATION, scheme, authInfo);
}
void HTTPRequest::setCredentials(const std::string& scheme, const std::string& authInfo)
{
setCredentials(AUTHORIZATION, scheme, authInfo);
}
void HTTPRequest::removeCredentials()
{
erase(AUTHORIZATION);
}
bool HTTPRequest::hasProxyCredentials() const
{
return has(PROXY_AUTHORIZATION);
}
void HTTPRequest::getProxyCredentials(std::string& scheme, std::string& authInfo) const
{
getCredentials(PROXY_AUTHORIZATION, scheme, authInfo);
}
void HTTPRequest::setProxyCredentials(const std::string& scheme, const std::string& authInfo)
{
setCredentials(PROXY_AUTHORIZATION, scheme, authInfo);
}
void HTTPRequest::removeProxyCredentials()
{
erase(PROXY_AUTHORIZATION);
}
void HTTPRequest::write(std::ostream& ostr) const
{
ostr << _method << " " << _uri << " " << getVersion() << "\r\n";
@ -248,7 +280,7 @@ void HTTPRequest::getCredentials(const std::string& header, std::string& scheme,
else throw NotAuthenticatedException();
}
void HTTPRequest::setCredentials(const std::string& header, const std::string& scheme, const std::string& authInfo)
{
std::string auth(scheme);

View File

@ -108,7 +108,7 @@ HTTPResponse::HTTPResponse():
{
}
HTTPResponse::HTTPResponse(HTTPStatus status, const std::string& reason):
_status(status),
_reason(reason)
@ -116,7 +116,7 @@ HTTPResponse::HTTPResponse(HTTPStatus status, const std::string& reason):
}
HTTPResponse::HTTPResponse(const std::string& version, HTTPStatus status, const std::string& reason):
HTTPMessage(version),
_status(status),
@ -124,7 +124,7 @@ HTTPResponse::HTTPResponse(const std::string& version, HTTPStatus status, const
{
}
HTTPResponse::HTTPResponse(HTTPStatus status):
_status(status),
_reason(getReasonForStatus(status))
@ -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;
@ -155,8 +175,8 @@ void HTTPResponse::setStatus(const std::string& status)
{
setStatus((HTTPStatus) NumberParser::parse(status));
}
void HTTPResponse::setReason(const std::string& reason)
{
_reason = reason;
@ -169,7 +189,7 @@ void HTTPResponse::setStatusAndReason(HTTPStatus status, const std::string& reas
_reason = reason;
}
void HTTPResponse::setStatusAndReason(HTTPStatus status)
{
setStatusAndReason(status, getReasonForStatus(status));
@ -181,7 +201,7 @@ void HTTPResponse::setDate(const Poco::Timestamp& dateTime)
set(DATE, DateTimeFormatter::format(dateTime, DateTimeFormat::HTTP_FORMAT));
}
Poco::Timestamp HTTPResponse::getDate() const
{
const std::string& dateTime = get(DATE);
@ -225,7 +245,7 @@ void HTTPResponse::read(std::istream& istr)
std::string version;
std::string status;
std::string reason;
int ch = istr.get();
if (istr.bad()) throw NetException("Error reading HTTP response header");
if (ch == eof) throw NoMessageException();
@ -255,25 +275,25 @@ const std::string& HTTPResponse::getReasonForStatus(HTTPStatus status)
{
switch (status)
{
case HTTP_CONTINUE:
case HTTP_CONTINUE:
return HTTP_REASON_CONTINUE;
case HTTP_SWITCHING_PROTOCOLS:
case HTTP_SWITCHING_PROTOCOLS:
return HTTP_REASON_SWITCHING_PROTOCOLS;
case HTTP_PROCESSING:
return HTTP_REASON_PROCESSING;
case HTTP_OK:
case HTTP_OK:
return HTTP_REASON_OK;
case HTTP_CREATED:
case HTTP_CREATED:
return HTTP_REASON_CREATED;
case HTTP_ACCEPTED:
case HTTP_ACCEPTED:
return HTTP_REASON_ACCEPTED;
case HTTP_NONAUTHORITATIVE:
case HTTP_NONAUTHORITATIVE:
return HTTP_REASON_NONAUTHORITATIVE;
case HTTP_NO_CONTENT:
case HTTP_NO_CONTENT:
return HTTP_REASON_NO_CONTENT;
case HTTP_RESET_CONTENT:
case HTTP_RESET_CONTENT:
return HTTP_REASON_RESET_CONTENT;
case HTTP_PARTIAL_CONTENT:
case HTTP_PARTIAL_CONTENT:
return HTTP_REASON_PARTIAL_CONTENT;
case HTTP_MULTI_STATUS:
return HTTP_REASON_MULTI_STATUS;
@ -281,58 +301,58 @@ const std::string& HTTPResponse::getReasonForStatus(HTTPStatus status)
return HTTP_REASON_ALREADY_REPORTED;
case HTTP_IM_USED:
return HTTP_REASON_IM_USED;
case HTTP_MULTIPLE_CHOICES:
case HTTP_MULTIPLE_CHOICES:
return HTTP_REASON_MULTIPLE_CHOICES;
case HTTP_MOVED_PERMANENTLY:
case HTTP_MOVED_PERMANENTLY:
return HTTP_REASON_MOVED_PERMANENTLY;
case HTTP_FOUND:
case HTTP_FOUND:
return HTTP_REASON_FOUND;
case HTTP_SEE_OTHER:
case HTTP_SEE_OTHER:
return HTTP_REASON_SEE_OTHER;
case HTTP_NOT_MODIFIED:
case HTTP_NOT_MODIFIED:
return HTTP_REASON_NOT_MODIFIED;
case HTTP_USE_PROXY:
case HTTP_USE_PROXY:
return HTTP_REASON_USE_PROXY;
case HTTP_TEMPORARY_REDIRECT:
case HTTP_TEMPORARY_REDIRECT:
return HTTP_REASON_TEMPORARY_REDIRECT;
case HTTP_BAD_REQUEST:
case HTTP_BAD_REQUEST:
return HTTP_REASON_BAD_REQUEST;
case HTTP_UNAUTHORIZED:
case HTTP_UNAUTHORIZED:
return HTTP_REASON_UNAUTHORIZED;
case HTTP_PAYMENT_REQUIRED:
case HTTP_PAYMENT_REQUIRED:
return HTTP_REASON_PAYMENT_REQUIRED;
case HTTP_FORBIDDEN:
case HTTP_FORBIDDEN:
return HTTP_REASON_FORBIDDEN;
case HTTP_NOT_FOUND:
case HTTP_NOT_FOUND:
return HTTP_REASON_NOT_FOUND;
case HTTP_METHOD_NOT_ALLOWED:
return HTTP_REASON_METHOD_NOT_ALLOWED;
case HTTP_NOT_ACCEPTABLE:
case HTTP_NOT_ACCEPTABLE:
return HTTP_REASON_NOT_ACCEPTABLE;
case HTTP_PROXY_AUTHENTICATION_REQUIRED:
case HTTP_PROXY_AUTHENTICATION_REQUIRED:
return HTTP_REASON_PROXY_AUTHENTICATION_REQUIRED;
case HTTP_REQUEST_TIMEOUT:
case HTTP_REQUEST_TIMEOUT:
return HTTP_REASON_REQUEST_TIMEOUT;
case HTTP_CONFLICT:
case HTTP_CONFLICT:
return HTTP_REASON_CONFLICT;
case HTTP_GONE:
case HTTP_GONE:
return HTTP_REASON_GONE;
case HTTP_LENGTH_REQUIRED:
case HTTP_LENGTH_REQUIRED:
return HTTP_REASON_LENGTH_REQUIRED;
case HTTP_PRECONDITION_FAILED:
case HTTP_PRECONDITION_FAILED:
return HTTP_REASON_PRECONDITION_FAILED;
case HTTP_REQUEST_ENTITY_TOO_LARGE:
case HTTP_REQUEST_ENTITY_TOO_LARGE:
return HTTP_REASON_REQUEST_ENTITY_TOO_LARGE;
case HTTP_REQUEST_URI_TOO_LONG:
case HTTP_REQUEST_URI_TOO_LONG:
return HTTP_REASON_REQUEST_URI_TOO_LONG;
case HTTP_UNSUPPORTED_MEDIA_TYPE:
case HTTP_UNSUPPORTED_MEDIA_TYPE:
return HTTP_REASON_UNSUPPORTED_MEDIA_TYPE;
case HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
case HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
return HTTP_REASON_REQUESTED_RANGE_NOT_SATISFIABLE;
case HTTP_EXPECTATION_FAILED:
case HTTP_EXPECTATION_FAILED:
return HTTP_REASON_EXPECTATION_FAILED;
case HTTP_IM_A_TEAPOT:
return HTTP_REASON_IM_A_TEAPOT;
return HTTP_REASON_IM_A_TEAPOT;
case HTTP_ENCHANCE_YOUR_CALM:
return HTTP_REASON_ENCHANCE_YOUR_CALM;
case HTTP_MISDIRECTED_REQUEST:
@ -353,17 +373,17 @@ const std::string& HTTPResponse::getReasonForStatus(HTTPStatus status)
return HTTP_REASON_REQUEST_HEADER_FIELDS_TOO_LARGE;
case HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
return HTTP_REASON_UNAVAILABLE_FOR_LEGAL_REASONS;
case HTTP_INTERNAL_SERVER_ERROR:
case HTTP_INTERNAL_SERVER_ERROR:
return HTTP_REASON_INTERNAL_SERVER_ERROR;
case HTTP_NOT_IMPLEMENTED:
case HTTP_NOT_IMPLEMENTED:
return HTTP_REASON_NOT_IMPLEMENTED;
case HTTP_BAD_GATEWAY:
case HTTP_BAD_GATEWAY:
return HTTP_REASON_BAD_GATEWAY;
case HTTP_SERVICE_UNAVAILABLE:
return HTTP_REASON_SERVICE_UNAVAILABLE;
case HTTP_GATEWAY_TIMEOUT:
case HTTP_GATEWAY_TIMEOUT:
return HTTP_REASON_GATEWAY_TIMEOUT;
case HTTP_VERSION_NOT_SUPPORTED:
case HTTP_VERSION_NOT_SUPPORTED:
return HTTP_REASON_VERSION_NOT_SUPPORTED;
case HTTP_VARIANT_ALSO_NEGOTIATES:
return HTTP_REASON_VARIANT_ALSO_NEGOTIATES;
@ -375,7 +395,7 @@ const std::string& HTTPResponse::getReasonForStatus(HTTPStatus status)
return HTTP_REASON_NOT_EXTENDED;
case HTTP_NETWORK_AUTHENTICATION_REQUIRED:
return HTTP_REASON_NETWORK_AUTHENTICATION_REQUIRED;
default:
default:
return HTTP_REASON_UNKNOWN;
}
}

329
Net/src/NTLMCredentials.cpp Normal file
View 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

View File

@ -27,7 +27,8 @@ objects = \
WebSocketTest WebSocketTestSuite \
SyslogTest \
OAuth10CredentialsTest OAuth20CredentialsTest OAuthTestSuite \
PollSetTest UDPServerTest UDPServerTestSuite
PollSetTest UDPServerTest UDPServerTestSuite \
NTLMCredentialsTest
target = testrunner
target_version = 1

View File

@ -44,7 +44,7 @@ void HTTPCredentialsTest::testBasicCredentials()
{
HTTPRequest request;
assertTrue (!request.hasCredentials());
HTTPBasicCredentials cred("user", "secret");
cred.authenticate(request);
assertTrue (request.hasCredentials());
@ -53,7 +53,7 @@ void HTTPCredentialsTest::testBasicCredentials()
request.getCredentials(scheme, info);
assertTrue (scheme == "Basic");
assertTrue (info == "dXNlcjpzZWNyZXQ=");
HTTPBasicCredentials cred2(request);
assertTrue (cred2.getUsername() == "user");
assertTrue (cred2.getPassword() == "secret");
@ -64,7 +64,7 @@ void HTTPCredentialsTest::testProxyBasicCredentials()
{
HTTPRequest request;
assertTrue (!request.hasProxyCredentials());
HTTPBasicCredentials cred("user", "secret");
cred.proxyAuthenticate(request);
assertTrue (request.hasProxyCredentials());
@ -79,7 +79,7 @@ void HTTPCredentialsTest::testProxyBasicCredentials()
void HTTPCredentialsTest::testBadCredentials()
{
HTTPRequest request;
std::string scheme;
std::string info;
try
@ -90,12 +90,12 @@ void HTTPCredentialsTest::testBadCredentials()
catch (NotAuthenticatedException&)
{
}
request.setCredentials("Test", "SomeData");
request.getCredentials(scheme, info);
assertTrue (scheme == "Test");
assertTrue (info == "SomeData");
try
{
HTTPBasicCredentials cred(request);
@ -111,7 +111,7 @@ void HTTPCredentialsTest::testAuthenticationParams()
{
const std::string authInfo("nonce=\"212573bb90170538efad012978ab811f%lu\", realm=\"TestDigest\", response=\"40e4889cfbd0e561f71e3107a2863bc4\", uri=\"/digest/\", username=\"user\"");
HTTPAuthenticationParams params(authInfo);
assertTrue (params["nonce"] == "212573bb90170538efad012978ab811f%lu");
assertTrue (params["realm"] == "TestDigest");
assertTrue (params["response"] == "40e4889cfbd0e561f71e3107a2863bc4");
@ -119,7 +119,7 @@ void HTTPCredentialsTest::testAuthenticationParams()
assertTrue (params["username"] == "user");
assertTrue (params.size() == 5);
assertTrue (params.toString() == authInfo);
params.clear();
HTTPRequest request;
request.set("Authorization", "Digest " + authInfo);
@ -134,22 +134,29 @@ void HTTPCredentialsTest::testAuthenticationParams()
params.clear();
HTTPResponse response;
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
params.fromResponse(response);
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);
}
void HTTPCredentialsTest::testAuthenticationParamsMultipleHeaders()
{
HTTPResponse response;
response.add("WWW-Authenticate", "Unsupported realm=\"TestUnsupported\"");
response.add("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
response.add("WWW-Authenticate", "Unsupported realm=\"TestUnsupported\"");
response.add("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
HTTPAuthenticationParams params(response);
assertTrue (params["realm"] == "TestDigest");
assertTrue (params["nonce"] == "212573bb90170538efad012978ab811f%lu");
assertTrue (params.size() == 2);
@ -161,7 +168,7 @@ void HTTPCredentialsTest::testDigestCredentials()
HTTPDigestCredentials creds("user", "s3cr3t");
HTTPRequest request(HTTPRequest::HTTP_GET, "/digest/");
HTTPResponse response;
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
creds.authenticate(request, response);
std::string auth = request.get("Authorization");
assertTrue (auth == "Digest username=\"user\", nonce=\"212573bb90170538efad012978ab811f%lu\", realm=\"TestDigest\", uri=\"/digest/\", response=\"40e4889cfbd0e561f71e3107a2863bc4\"");
@ -173,9 +180,9 @@ void HTTPCredentialsTest::testDigestCredentialsQoP()
HTTPDigestCredentials creds("user", "s3cr3t");
HTTPRequest request(HTTPRequest::HTTP_GET, "/digest/");
HTTPResponse response;
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\", opaque=\"opaque\", qop=\"auth,auth-int\"");
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\", opaque=\"opaque\", qop=\"auth,auth-int\"");
creds.authenticate(request, response);
HTTPAuthenticationParams params(request);
assertTrue (params["nonce"] == "212573bb90170538efad012978ab811f%lu");
assertTrue (params["realm"] == "TestDigest");
@ -187,12 +194,12 @@ void HTTPCredentialsTest::testDigestCredentialsQoP()
assertTrue (params["nc"] == "00000001");
assertTrue (params["qop"] == "auth");
assertTrue (params.size() == 9);
std::string cnonce = params["cnonce"];
std::string aresp = params["response"];
params.clear();
creds.updateAuthInfo(request);
params.fromRequest(request);
assertTrue (params["nonce"] == "212573bb90170538efad012978ab811f%lu");
@ -213,8 +220,8 @@ void HTTPCredentialsTest::testCredentialsBasic()
HTTPCredentials creds("user", "s3cr3t");
HTTPRequest request(HTTPRequest::HTTP_GET, "/basic/");
HTTPResponse response;
response.set("WWW-Authenticate", "Basic realm=\"TestBasic\"");
creds.authenticate(request, response);
response.set("WWW-Authenticate", "Basic realm=\"TestBasic\"");
creds.authenticate(request, response);
assertTrue (request.get("Authorization") == "Basic dXNlcjpzM2NyM3Q=");
}
@ -224,8 +231,8 @@ void HTTPCredentialsTest::testProxyCredentialsBasic()
HTTPCredentials creds("user", "s3cr3t");
HTTPRequest request(HTTPRequest::HTTP_GET, "/basic/");
HTTPResponse response;
response.set("Proxy-Authenticate", "Basic realm=\"TestBasic\"");
creds.proxyAuthenticate(request, response);
response.set("Proxy-Authenticate", "Basic realm=\"TestBasic\"");
creds.proxyAuthenticate(request, response);
assertTrue (request.get("Proxy-Authorization") == "Basic dXNlcjpzM2NyM3Q=");
}
@ -235,7 +242,7 @@ void HTTPCredentialsTest::testCredentialsDigest()
HTTPCredentials creds("user", "s3cr3t");
HTTPRequest request(HTTPRequest::HTTP_GET, "/digest/");
HTTPResponse response;
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
creds.authenticate(request, response);
std::string auth = request.get("Authorization");
assertTrue (auth == "Digest username=\"user\", nonce=\"212573bb90170538efad012978ab811f%lu\", realm=\"TestDigest\", uri=\"/digest/\", response=\"40e4889cfbd0e561f71e3107a2863bc4\"");
@ -247,8 +254,8 @@ void HTTPCredentialsTest::testCredentialsDigestMultipleHeaders()
HTTPCredentials creds("user", "s3cr3t");
HTTPRequest request(HTTPRequest::HTTP_GET, "/digest/");
HTTPResponse response;
response.add("WWW-Authenticate", "Unsupported realm=\"TestUnsupported\"");
response.add("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
response.add("WWW-Authenticate", "Unsupported realm=\"TestUnsupported\"");
response.add("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
creds.authenticate(request, response);
std::string auth = request.get("Authorization");
assertTrue (auth == "Digest username=\"user\", nonce=\"212573bb90170538efad012978ab811f%lu\", realm=\"TestDigest\", uri=\"/digest/\", response=\"40e4889cfbd0e561f71e3107a2863bc4\"");
@ -260,8 +267,8 @@ void HTTPCredentialsTest::testProxyCredentialsDigest()
HTTPCredentials creds("user", "s3cr3t");
HTTPRequest request(HTTPRequest::HTTP_GET, "/digest/");
HTTPResponse response;
response.set("Proxy-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
creds.proxyAuthenticate(request, response);
response.set("Proxy-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
creds.proxyAuthenticate(request, response);
assertTrue (request.get("Proxy-Authorization") == "Digest username=\"user\", nonce=\"212573bb90170538efad012978ab811f%lu\", realm=\"TestDigest\", uri=\"/digest/\", response=\"40e4889cfbd0e561f71e3107a2863bc4\"");
}
@ -282,10 +289,10 @@ void HTTPCredentialsTest::testVerifyAuthInfo()
HTTPDigestCredentials creds("user", "s3cr3t");
HTTPRequest request(HTTPRequest::HTTP_GET, "/digest/");
HTTPResponse response;
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\"");
creds.authenticate(request, response);
assertTrue (creds.verifyAuthInfo(request));
request.set("Authorization", "Digest nonce=\"212573bb90170538efad012978ab811f%lu\", realm=\"TestDigest\", response=\"xxe4889cfbd0e561f71e3107a2863bc4\", uri=\"/digest/\", username=\"user\"");
assertTrue (!creds.verifyAuthInfo(request));
}
@ -296,10 +303,10 @@ void HTTPCredentialsTest::testVerifyAuthInfoQoP()
HTTPDigestCredentials creds("user", "s3cr3t");
HTTPRequest request(HTTPRequest::HTTP_GET, "/digest/");
HTTPResponse response;
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\", opaque=\"opaque\", qop=\"auth,auth-int\"");
response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\", opaque=\"opaque\", qop=\"auth,auth-int\"");
creds.authenticate(request, response);
assertTrue (creds.verifyAuthInfo(request));
request.set("Authorization", "Digest cnonce=\"f9c80ffd1c3bc4ee47ed92b704ba75a4\", nc=00000001, nonce=\"212573bb90170538efad012978ab811f%lu\", opaque=\"opaque\", qop=\"auth\", realm=\"TestDigest\", response=\"ff0e90b9aa019120ea0ed6e23ce95d9a\", uri=\"/digest/\", username=\"user\"");
assertTrue (!creds.verifyAuthInfo(request));
}

View File

@ -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;
}

View 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;
}

View 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