OpenVPN 3 Core Library
Loading...
Searching...
No Matches
ntlm.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// Low-level methods used to implement NTLMv2 proxy authentication
13
14#ifndef OPENVPN_PROXY_NTLM_H
15#define OPENVPN_PROXY_NTLM_H
16
17#include <cstring>
18#include <string>
19#include <vector>
20#include <cstdint> // for std::uint32_t, uint64_t
21
29#include <openvpn/time/time.hpp>
32
33namespace openvpn::HTTPProxy {
34
35class NTLM
36{
37 public:
38 /*
39 * NTLMv2 handshake
40 * http://davenport.sourceforge.net/ntlm.html
41 *
42 */
43
44 static std::string phase_1()
45 {
46 return "TlRMTVNTUAABAAAAAgIAAA==";
47 }
48
49 static std::string phase_3(DigestFactory &digest_factory,
50 const std::string &phase_2_response,
51 const std::string &dom_username,
52 const std::string &password,
53 StrongRandomAPI &rng)
54 {
55 // sanity checks
56 if (dom_username.empty())
57 throw Exception("username is blank");
58 if (password.empty())
59 throw Exception("password is blank");
60
61 if (phase_2_response.size() < 32)
62 throw Exception("phase2 base64 response from server too short (" + std::to_string(phase_2_response.size()) + ")");
63
64 // split domain\username
65 std::string domain;
66 std::string username;
67 split_domain_username(dom_username, domain, username);
68
69 // convert password from utf-8 to utf-16 and take an MD4 hash of it
70 BufferPtr password_u = Unicode::string_to_utf16(password);
71 DigestInstance::Ptr md4_ctx(digest_factory.new_digest(CryptoAlgs::MD4));
72 md4_ctx->update(password_u->c_data(), password_u->size());
73 unsigned char md4_hash[21];
74 md4_ctx->final(md4_hash);
75 std::memset(md4_hash + 16, 0, 5); // pad to 21 bytes
76
77 // decode phase_2_response from base64 to raw data
78 BufferAllocated response(phase_2_response.size(), 0);
79 base64->decode(response, phase_2_response);
80
81 if (response.size() < 32)
82 throw Exception("phase2 decoded response from server too short (" + std::to_string(response.size()) + ")");
83
84 // extract the challenge from bytes 24-31 in the response
85 unsigned char challenge[8];
86 for (size_t i = 0; i < 8; ++i)
87 challenge[i] = response[i + 24];
88
89 // concatenate uppercase(username) + domain,
90 // convert to utf-16, and run it through HMAC-MD5
91 // keyed to md4_hash
92 const std::string ud = string::to_upper_copy(username) + domain;
94 HMACInstance::Ptr hmac_ctx1(digest_factory.new_hmac(CryptoAlgs::MD5, md4_hash, 16));
95 hmac_ctx1->update(ud_u->c_data(), ud_u->size());
96 unsigned char ntlmv2_hash[16];
97 hmac_ctx1->final(ntlmv2_hash);
98
99 // NTLMv2 Blob
100 unsigned char ntlmv2_response[144];
101 unsigned char *ntlmv2_blob = ntlmv2_response + 16; // inside ntlmv2_response, length: 128
102 memset(ntlmv2_blob, 0, 128); // clear blob buffer
103 ntlmv2_blob[0x00] = 1; // signature
104 ntlmv2_blob[0x01] = 1; // signature
105 ntlmv2_blob[0x04] = 0; // reserved
106 store_win_time(ntlmv2_blob + 0x08); // 64-bit Windows-style timestamp
107 rng.rand_bytes(ntlmv2_blob + 0x10, 8); // 64-bit client nonce
108 ntlmv2_blob[0x18] = 0; // unknown, zero should work
109
110 // add target information block to the blob
111 size_t tib_len = 0;
112 if (response[0x16] & 0x80u) // check for Target Information block (TIB)
113 {
114 tib_len = response[0x28]; // get TIB size
115 if (tib_len > 96)
116 tib_len = 96;
117 const size_t tib_offset = response[0x2c];
118 if (tib_offset + tib_len < response.size())
119 {
120 const unsigned char *tib_ptr = response.c_data() + tib_offset; // get TIB pointer
121 std::memcpy(&ntlmv2_blob[0x1c], tib_ptr, tib_len); // copy TIB into the blob
122 }
123 else
124 tib_len = 0;
125 }
126 ntlmv2_blob[0x1c + tib_len] = 0; // unknown, zero works
127
128 // Get blob length
129 const size_t ntlmv2_blob_size = 0x20 + tib_len;
130
131 // Add challenge from message 2
132 std::memcpy(&ntlmv2_response[8], challenge, 8);
133
134 // hmac-md5
135 HMACInstance::Ptr hmac_ctx2(digest_factory.new_hmac(CryptoAlgs::MD5, ntlmv2_hash, 16));
136 hmac_ctx2->update(&ntlmv2_response[8], ntlmv2_blob_size + 8);
137 unsigned char ntlmv2_hmacmd5[16];
138 hmac_ctx2->final(ntlmv2_hmacmd5);
139
140 // add hmac-md5 result to the blob
141 // Note: This overwrites challenge previously written at ntlmv2_response[8..15]
142 std::memcpy(ntlmv2_response, ntlmv2_hmacmd5, 16);
143
144 // start building phase3 message (what we return to caller)
146 std::strcpy((char *)phase3.data(), "NTLMSSP"); // signature
147 phase3[8] = 3; // type 3
148
149 // NTLMv2 response
150 add_security_buffer(0x14, ntlmv2_response, numeric_cast<unsigned char>(ntlmv2_blob_size + 16), phase3);
151
152 // username
153 add_security_buffer(0x24, username.c_str(), numeric_cast<unsigned char>(username.length()), phase3);
154
155 // Set domain. If <domain> is empty, default domain will be used (i.e. proxy's domain).
156 add_security_buffer(0x1c, domain.c_str(), numeric_cast<unsigned char>(domain.size()), phase3);
157
158 // other security buffers will be empty
159 const unsigned char phase3_size = static_cast<unsigned char>(phase3.size());
160 phase3[0x10] = phase3_size; // lm not used
161 phase3[0x30] = phase3_size; // no workstation name supplied
162 phase3[0x38] = phase3_size; // no session key
163
164 // flags
165 phase3[0x3c] = 0x02; // negotiate oem
166 phase3[0x3d] = 0x02; // negotiate ntlm
167
168 return base64->encode(phase3);
169 }
170
171 private:
172 // adds security buffer data to a message and sets security buffer's offset and length
173 static void add_security_buffer(const size_t sb_offset,
174 const void *data,
175 const unsigned char length,
176 Buffer &msg_buf)
177 {
178 msg_buf[sb_offset] = length;
179 msg_buf[sb_offset + 2] = length;
180 msg_buf[sb_offset + 4] = msg_buf.size() & 0xff;
181 msg_buf[sb_offset + 5] = (msg_buf.size() >> 8) & 0xff;
182 msg_buf.write((unsigned char *)data, length);
183 }
184
185 // store 64-bit windows time into a little-endian 8-byte buffer
186 static void store_win_time(unsigned char *dest)
187 {
188 const std::uint64_t wt = Time::win_time();
189 dest[0] = (unsigned char)wt;
190 dest[1] = (unsigned char)(wt >> 8);
191 dest[2] = (unsigned char)(wt >> 16);
192 dest[3] = (unsigned char)(wt >> 24);
193 dest[4] = (unsigned char)(wt >> 32);
194 dest[5] = (unsigned char)(wt >> 40);
195 dest[6] = (unsigned char)(wt >> 48);
196 dest[7] = (unsigned char)(wt >> 56);
197 }
198
199 static void split_domain_username(const std::string &combined, std::string &domain, std::string &username)
200 {
201 typedef std::vector<std::string> StringList;
202 StringList sl;
203 sl.reserve(2);
204 Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, combined, '\\', 1);
205 if (sl.size() == 1)
206 {
207 domain = "";
208 username = sl[0];
209 }
210 else if (sl.size() == 2)
211 {
212 domain = sl[0];
213 username = sl[1];
214 }
215 else
216 throw Exception("split_domain_username failed");
217 }
218};
219} // namespace openvpn::HTTPProxy
220
221#endif
std::string encode(const V &data) const
Definition base64.hpp:139
size_t decode(void *data, size_t len, const std::string &str) const
Definition base64.hpp:186
const T * c_data() const
Returns a const pointer to the start of the buffer.
Definition buffer.hpp:1177
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1225
T * data()
Get a mutable pointer to the start of the array.
Definition buffer.hpp:1433
void write(const T *data, const size_t size)
Write data to the buffer.
Definition buffer.hpp:1546
virtual HMACInstance::Ptr new_hmac(const CryptoAlgs::Type digest_type, const unsigned char *key, const size_t key_size)=0
virtual DigestInstance::Ptr new_digest(const CryptoAlgs::Type digest_type)=0
virtual size_t final(unsigned char *out)=0
virtual void update(const unsigned char *in, const size_t size)=0
static void store_win_time(unsigned char *dest)
Definition ntlm.hpp:186
static void split_domain_username(const std::string &combined, std::string &domain, std::string &username)
Definition ntlm.hpp:199
static std::string phase_1()
Definition ntlm.hpp:44
static void add_security_buffer(const size_t sb_offset, const void *data, const unsigned char length, Buffer &msg_buf)
Definition ntlm.hpp:173
static std::string phase_3(DigestFactory &digest_factory, const std::string &phase_2_response, const std::string &dom_username, const std::string &password, StrongRandomAPI &rng)
Definition ntlm.hpp:49
virtual void rand_bytes(unsigned char *buf, size_t size)=0
Fill a buffer with random bytes.
Abstract base class for cryptographically strong random number generators.
Definition randapi.hpp:228
static uint64_t win_time()
Definition time.hpp:438
BufferPtr string_to_utf16(const STRING &str)
Definition unicode.hpp:213
std::string to_upper_copy(const std::string &str)
Definition string.hpp:577
const Base64 * base64
Definition base64.hpp:299
@ ARRAY
if enabled, use as array
Definition buffer.hpp:873
@ GROW
if enabled, buffer will grow (otherwise buffer_full exception will be thrown)
Definition buffer.hpp:872
@ CONSTRUCT_ZERO
if enabled, constructors/init will zero allocated space
Definition buffer.hpp:870