Coverage Report

Created: 2026-05-11 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/utils/ntlm.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * NTLM Utils
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/config.h>
21
22
#include <winpr/ntlm.h>
23
#include <winpr/assert.h>
24
25
#include <winpr/crt.h>
26
#include <winpr/crypto.h>
27
28
/**
29
 * Define NTOWFv1(Password, User, Domain) as
30
 *  MD4(UNICODE(Password))
31
 * EndDefine
32
 */
33
34
BOOL NTOWFv1W(LPCWSTR Password, UINT32 PasswordLengthInBytes, BYTE* NtHash)
35
67
{
36
67
  if (!Password || !NtHash)
37
0
    return FALSE;
38
39
67
  if (!winpr_Digest(WINPR_MD_MD4, Password, PasswordLengthInBytes, NtHash,
40
67
                    WINPR_MD4_DIGEST_LENGTH))
41
0
    return FALSE;
42
43
67
  return TRUE;
44
67
}
45
46
BOOL NTOWFv1A(LPCSTR Password, UINT32 PasswordLengthInBytes, BYTE* NtHash)
47
0
{
48
0
  LPWSTR PasswordW = nullptr;
49
0
  BOOL result = FALSE;
50
0
  size_t pwdCharLength = 0;
51
52
0
  if (!NtHash)
53
0
    return FALSE;
54
55
0
  if (!Password && (PasswordLengthInBytes > 0))
56
0
    return FALSE;
57
58
0
  if (Password)
59
0
  {
60
0
    PasswordW = ConvertUtf8NToWCharAlloc(Password, PasswordLengthInBytes, &pwdCharLength);
61
0
    if (!PasswordW)
62
0
      return FALSE;
63
0
  }
64
65
0
  if (!NTOWFv1W(PasswordW, WINPR_ASSERTING_INT_CAST(UINT32, pwdCharLength * sizeof(WCHAR)),
66
0
                NtHash))
67
0
    goto out_fail;
68
69
0
  result = TRUE;
70
0
out_fail:
71
0
  free(PasswordW);
72
0
  return result;
73
0
}
74
75
/**
76
 * Define NTOWFv2(Password, User, Domain) as
77
 *  HMAC_MD5(MD4(UNICODE(Password)),
78
 *    UNICODE(ConcatenationOf(UpperCase(User), Domain)))
79
 * EndDefine
80
 */
81
82
BOOL NTOWFv2W(LPCWSTR Password, UINT32 PasswordLengthInBytes, LPCWSTR User,
83
              UINT32 UserLengthInBytes, LPCWSTR Domain, UINT32 DomainLengthInBytes, BYTE* NtHash)
84
67
{
85
67
  BYTE NtHashV1[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
86
87
67
  if (!Domain && (DomainLengthInBytes > 0))
88
0
    return FALSE;
89
67
  if (!User || !Password || !NtHash)
90
0
    return FALSE;
91
92
67
  if (!NTOWFv1W(Password, PasswordLengthInBytes, NtHashV1))
93
0
    return FALSE;
94
95
67
  return NTOWFv2FromHashW(NtHashV1, User, UserLengthInBytes, Domain, DomainLengthInBytes, NtHash);
96
67
}
97
98
BOOL NTOWFv2A(LPCSTR Password, UINT32 PasswordLengthInBytes, LPCSTR User, UINT32 UserLengthInBytes,
99
              LPCSTR Domain, UINT32 DomainLengthInBytes, BYTE* NtHash)
100
0
{
101
0
  LPWSTR UserW = nullptr;
102
0
  LPWSTR DomainW = nullptr;
103
0
  LPWSTR PasswordW = nullptr;
104
0
  BOOL result = FALSE;
105
0
  size_t userCharLength = 0;
106
0
  size_t domainCharLength = 0;
107
0
  size_t pwdCharLength = 0;
108
109
0
  if (!NtHash)
110
0
    return FALSE;
111
112
0
  if (!User && (UserLengthInBytes > 0))
113
0
    return FALSE;
114
0
  if (!Password && (PasswordLengthInBytes > 0))
115
0
    return FALSE;
116
0
  if (!Domain && (DomainLengthInBytes > 0))
117
0
    return FALSE;
118
119
0
  if (User)
120
0
  {
121
0
    UserW = ConvertUtf8NToWCharAlloc(User, UserLengthInBytes, &userCharLength);
122
0
    if (!UserW)
123
0
      goto out_fail;
124
0
  }
125
0
  if (Domain)
126
0
  {
127
0
    DomainW = ConvertUtf8NToWCharAlloc(Domain, DomainLengthInBytes, &domainCharLength);
128
0
    if (!DomainW)
129
0
      goto out_fail;
130
0
  }
131
0
  if (Password)
132
0
  {
133
0
    PasswordW = ConvertUtf8NToWCharAlloc(Password, PasswordLengthInBytes, &pwdCharLength);
134
0
    if (!PasswordW)
135
0
      goto out_fail;
136
0
  }
137
138
0
  if (!NTOWFv2W(PasswordW, (UINT32)pwdCharLength * sizeof(WCHAR), UserW,
139
0
                (UINT32)userCharLength * sizeof(WCHAR), DomainW,
140
0
                (UINT32)domainCharLength * sizeof(WCHAR), NtHash))
141
0
    goto out_fail;
142
143
0
  result = TRUE;
144
0
out_fail:
145
0
  free(UserW);
146
0
  free(DomainW);
147
0
  free(PasswordW);
148
0
  return result;
149
0
}
150
151
BOOL NTOWFv2FromHashW(const BYTE* NtHashV1, LPCWSTR User, UINT32 UserLengthInBytes, LPCWSTR Domain,
152
                      UINT32 DomainLengthInBytes, BYTE* NtHash)
153
67
{
154
67
  BYTE result = FALSE;
155
156
67
  if (!NtHash || !NtHashV1)
157
0
    return FALSE;
158
159
67
  if (!User && (UserLengthInBytes > 0))
160
0
    return FALSE;
161
67
  if (!Domain && (DomainLengthInBytes > 0))
162
0
    return FALSE;
163
164
67
  if (!User)
165
0
    return FALSE;
166
167
67
  BYTE* buffer = (BYTE*)malloc(UserLengthInBytes + DomainLengthInBytes);
168
67
  if (!buffer)
169
0
    return FALSE;
170
171
  /* Concatenate(UpperCase(User), Domain) */
172
67
  CopyMemory(buffer, User, UserLengthInBytes);
173
67
  CharUpperBuffW((LPWSTR)buffer, UserLengthInBytes / 2);
174
175
67
  if (DomainLengthInBytes > 0)
176
67
  {
177
67
    CopyMemory(&buffer[UserLengthInBytes], Domain, DomainLengthInBytes);
178
67
  }
179
180
  /* Compute the HMAC-MD5 hash of the above value using the NTLMv1 hash as the key, the result is
181
   * the NTLMv2 hash */
182
67
  if (!winpr_HMAC(WINPR_MD_MD5, NtHashV1, 16, buffer, UserLengthInBytes + DomainLengthInBytes,
183
67
                  NtHash, WINPR_MD5_DIGEST_LENGTH))
184
0
    goto out_fail;
185
186
67
  result = TRUE;
187
67
out_fail:
188
67
  free(buffer);
189
67
  return result;
190
67
}
191
192
BOOL NTOWFv2FromHashA(const BYTE* NtHashV1, LPCSTR User, UINT32 UserLengthInBytes, LPCSTR Domain,
193
                      UINT32 DomainLengthInBytes, BYTE* NtHash)
194
0
{
195
0
  LPWSTR UserW = nullptr;
196
0
  LPWSTR DomainW = nullptr;
197
0
  BOOL result = FALSE;
198
0
  size_t userCharLength = 0;
199
0
  size_t domainCharLength = 0;
200
201
0
  if (!NtHash || !NtHashV1)
202
0
    return FALSE;
203
204
0
  if (!User && (UserLengthInBytes > 0))
205
0
    return FALSE;
206
0
  if (!Domain && (DomainLengthInBytes > 0))
207
0
    return FALSE;
208
209
0
  if (User)
210
0
  {
211
0
    UserW = ConvertUtf8NToWCharAlloc(User, UserLengthInBytes, &userCharLength);
212
0
    if (!UserW)
213
0
      goto out_fail;
214
0
  }
215
216
0
  if (Domain)
217
0
  {
218
0
    DomainW = ConvertUtf8NToWCharAlloc(Domain, DomainLengthInBytes, &domainCharLength);
219
0
    if (!DomainW)
220
0
      goto out_fail;
221
0
  }
222
223
0
  if (!NTOWFv2FromHashW(NtHashV1, UserW, (UINT32)userCharLength * sizeof(WCHAR), DomainW,
224
0
                        (UINT32)domainCharLength * sizeof(WCHAR), NtHash))
225
0
    goto out_fail;
226
227
0
  result = TRUE;
228
0
out_fail:
229
0
  free(UserW);
230
0
  free(DomainW);
231
0
  return result;
232
0
}