CVE-2016-2110: libcli/auth: pass server_timestamp to SMBNTLMv2encrypt_hash()
[samba.git] / auth / credentials / credentials_ntlm.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    User credentials handling
5
6    Copyright (C) Andrew Tridgell      2001
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
8    Copyright (C) Stefan Metzmacher 2005
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
26 #include "../lib/crypto/crypto.h"
27 #include "libcli/auth/libcli_auth.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/credentials_internal.h"
30
31 _PUBLIC_ NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, 
32                                            int *flags,
33                                            DATA_BLOB challenge,
34                                            const NTTIME *server_timestamp,
35                                            DATA_BLOB target_info,
36                                            DATA_BLOB *_lm_response, DATA_BLOB *_nt_response, 
37                                            DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key) 
38 {
39         const char *user, *domain;
40         DATA_BLOB lm_response, nt_response;
41         DATA_BLOB lm_session_key, session_key;
42         const struct samr_Password *nt_hash;
43         lm_session_key = data_blob(NULL, 0);
44
45         /* We may already have an NTLM response we prepared earlier.
46          * This is used for NTLM pass-though authentication */
47         if (cred->nt_response.data || cred->lm_response.data) {
48                 *_nt_response = cred->nt_response;
49                 *_lm_response = cred->lm_response;
50
51                 if (!cred->lm_response.data) {
52                         *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
53                 }
54                 *_lm_session_key = data_blob(NULL, 0);
55                 *_session_key = data_blob(NULL, 0);
56                 return NT_STATUS_OK;
57         }
58
59         nt_hash = cli_credentials_get_nt_hash(cred, mem_ctx);
60
61         cli_credentials_get_ntlm_username_domain(cred, mem_ctx, &user, &domain);
62
63         /* If we are sending a username@realm login (see function
64          * above), then we will not send LM, it will not be
65          * accepted */
66         if (cred->principal_obtained > cred->username_obtained) {
67                 *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
68         }
69
70         /* Likewise if we are a machine account (avoid protocol downgrade attacks) */
71         if (cred->machine_account) {
72                 *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
73         }
74         
75         if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) {
76                 return NT_STATUS_ACCESS_DENIED;
77         }
78
79         if (!nt_hash) {
80                 static const uint8_t zeros[16];
81                 /* do nothing - blobs are zero length */
82
83                 /* session key is all zeros */
84                 session_key = data_blob_talloc(mem_ctx, zeros, 16);
85                 lm_session_key = data_blob_talloc(mem_ctx, zeros, 16);
86
87                 lm_response = data_blob(NULL, 0);
88                 nt_response = data_blob(NULL, 0);
89                 
90                 /* not doing NTLM2 without a password */
91                 *flags &= ~CLI_CRED_NTLM2;
92         } else if (*flags & CLI_CRED_NTLMv2_AUTH) {
93
94                 if (!target_info.length) {
95                         /* be lazy, match win2k - we can't do NTLMv2 without it */
96                         DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
97                         return NT_STATUS_INVALID_PARAMETER;
98                 }
99
100                 /* TODO: if the remote server is standalone, then we should replace 'domain'
101                    with the server name as supplied above */
102                 
103                 if (!SMBNTLMv2encrypt_hash(mem_ctx,
104                                            user, 
105                                            domain, 
106                                            nt_hash->hash, &challenge, 
107                                            server_timestamp, &target_info,
108                                            &lm_response, &nt_response, 
109                                            NULL, &session_key)) {
110                         return NT_STATUS_NO_MEMORY;
111                 }
112
113                 /* LM Key is incompatible... */
114                 *flags &= ~CLI_CRED_LANMAN_AUTH;
115                 if (lm_response.length != 0) {
116                         /*
117                          * We should not expose the lm key.
118                          */
119                         memset(lm_response.data, 0, lm_response.length);
120                 }
121         } else if (*flags & CLI_CRED_NTLM2) {
122                 MD5_CTX md5_session_nonce_ctx;
123                 uint8_t session_nonce[16];
124                 uint8_t session_nonce_hash[16];
125                 uint8_t user_session_key[16];
126                 
127                 lm_response = data_blob_talloc(mem_ctx, NULL, 24);
128                 generate_random_buffer(lm_response.data, 8);
129                 memset(lm_response.data+8, 0, 16);
130
131                 memcpy(session_nonce, challenge.data, 8);
132                 memcpy(&session_nonce[8], lm_response.data, 8);
133         
134                 MD5Init(&md5_session_nonce_ctx);
135                 MD5Update(&md5_session_nonce_ctx, challenge.data, 8);
136                 MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
137                 MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
138
139                 DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
140                 DEBUG(5, ("challenge is: \n"));
141                 dump_data(5, session_nonce_hash, 8);
142                 
143                 nt_response = data_blob_talloc(mem_ctx, NULL, 24);
144                 SMBOWFencrypt(nt_hash->hash,
145                               session_nonce_hash,
146                               nt_response.data);
147                 
148                 session_key = data_blob_talloc(mem_ctx, NULL, 16);
149
150                 SMBsesskeygen_ntv1(nt_hash->hash, user_session_key);
151                 hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data);
152                 dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
153
154                 /* LM Key is incompatible... */
155                 *flags &= ~CLI_CRED_LANMAN_AUTH;
156         } else {
157                 uint8_t lm_hash[16];
158                 nt_response = data_blob_talloc(mem_ctx, NULL, 24);
159                 SMBOWFencrypt(nt_hash->hash, challenge.data,
160                               nt_response.data);
161                 
162                 session_key = data_blob_talloc(mem_ctx, NULL, 16);
163                 SMBsesskeygen_ntv1(nt_hash->hash, session_key.data);
164                 dump_data_pw("NT session key:\n", session_key.data, session_key.length);
165
166                 /* lanman auth is insecure, it may be disabled.  
167                    We may also not have a password */
168                 if (*flags & CLI_CRED_LANMAN_AUTH) {
169                         const char *password;
170                         password = cli_credentials_get_password(cred);
171                         if (!password) {
172                                 lm_response = nt_response;
173                         } else {
174                                 lm_response = data_blob_talloc(mem_ctx, NULL, 24);
175                                 if (!SMBencrypt(password,challenge.data,
176                                                 lm_response.data)) {
177                                         /* If the LM password was too long (and therefore the LM hash being
178                                            of the first 14 chars only), don't send it.
179
180                                            We don't have any better options but to send the NT response 
181                                         */
182                                         data_blob_free(&lm_response);
183                                         lm_response = nt_response;
184                                         /* LM Key is incompatible with 'long' passwords */
185                                         *flags &= ~CLI_CRED_LANMAN_AUTH;
186                                 } else if (E_deshash(password, lm_hash)) {
187                                         lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
188                                         memcpy(lm_session_key.data, lm_hash, 8);
189                                         memset(&lm_session_key.data[8], '\0', 8);
190                                         
191                                         if (!(*flags & CLI_CRED_NTLM_AUTH)) {
192                                                 session_key = lm_session_key;
193                                         }
194                                 }
195                         }
196                 } else {
197                         const char *password;
198
199                         /* LM Key is incompatible... */
200                         lm_response = nt_response;
201                         *flags &= ~CLI_CRED_LANMAN_AUTH;
202
203                         password = cli_credentials_get_password(cred);
204                         if (password && E_deshash(password, lm_hash)) {
205                                 lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
206                                 memcpy(lm_session_key.data, lm_hash, 8);
207                                 memset(&lm_session_key.data[8], '\0', 8);
208                         }
209                 }
210         }
211         if (_lm_response) {
212                 *_lm_response = lm_response;
213         }
214         if (_nt_response) {
215                 *_nt_response = nt_response;
216         }
217         if (_lm_session_key) {
218                 *_lm_session_key = lm_session_key;
219         }
220         if (_session_key) {
221                 *_session_key = session_key;
222         }
223         return NT_STATUS_OK;
224 }
225
226 /*
227  * Set a utf16 password on the credentials context, including an indication
228  * of 'how' the password was obtained
229  *
230  * This is required because the nt_hash is calculated over the raw utf16 blob,
231  * which might not be completely valid utf16, which means the conversion
232  * from CH_UTF16MUNGED to CH_UTF8 might loose information.
233  */
234 _PUBLIC_ bool cli_credentials_set_utf16_password(struct cli_credentials *cred,
235                                                  const DATA_BLOB *password_utf16,
236                                                  enum credentials_obtained obtained)
237 {
238         if (password_utf16 == NULL) {
239                 return cli_credentials_set_password(cred, NULL, obtained);
240         }
241
242         if (obtained >= cred->password_obtained) {
243                 struct samr_Password *nt_hash = NULL;
244                 char *password_talloc = NULL;
245                 size_t password_len = 0;
246                 bool ok;
247
248                 nt_hash = talloc(cred, struct samr_Password);
249                 if (nt_hash == NULL) {
250                         return false;
251                 }
252
253                 ok = convert_string_talloc(cred,
254                                            CH_UTF16MUNGED, CH_UTF8,
255                                            password_utf16->data,
256                                            password_utf16->length,
257                                            (void *)&password_talloc,
258                                            &password_len);
259                 if (!ok) {
260                         TALLOC_FREE(nt_hash);
261                         return false;
262                 }
263
264                 ok = cli_credentials_set_password(cred, password_talloc, obtained);
265                 TALLOC_FREE(password_talloc);
266                 if (!ok) {
267                         TALLOC_FREE(nt_hash);
268                         return false;
269                 }
270
271                 mdfour(nt_hash->hash, password_utf16->data, password_utf16->length);
272                 cred->nt_hash = nt_hash;
273                 return true;
274         }
275
276         return false;
277 }
278
279 /*
280  * Set a old utf16 password on the credentials context.
281  *
282  * This is required because the nt_hash is calculated over the raw utf16 blob,
283  * which might not be completely valid utf16, which means the conversion
284  * from CH_UTF16MUNGED to CH_UTF8 might loose information.
285  */
286 _PUBLIC_ bool cli_credentials_set_old_utf16_password(struct cli_credentials *cred,
287                                                      const DATA_BLOB *password_utf16)
288 {
289         struct samr_Password *nt_hash = NULL;
290         char *password_talloc = NULL;
291         size_t password_len = 0;
292         bool ok;
293
294         if (password_utf16 == NULL) {
295                 return cli_credentials_set_old_password(cred, NULL, CRED_SPECIFIED);
296         }
297
298         nt_hash = talloc(cred, struct samr_Password);
299         if (nt_hash == NULL) {
300                 return false;
301         }
302
303         ok = convert_string_talloc(cred,
304                                    CH_UTF16MUNGED, CH_UTF8,
305                                    password_utf16->data,
306                                    password_utf16->length,
307                                    (void *)&password_talloc,
308                                    &password_len);
309         if (!ok) {
310                 TALLOC_FREE(nt_hash);
311                 return false;
312         }
313
314         ok = cli_credentials_set_old_password(cred, password_talloc, CRED_SPECIFIED);
315         TALLOC_FREE(password_talloc);
316         if (!ok) {
317                 TALLOC_FREE(nt_hash);
318                 return false;
319         }
320
321         mdfour(nt_hash->hash, password_utf16->data, password_utf16->length);
322         cred->old_nt_hash = nt_hash;
323         return true;
324 }
325
326 _PUBLIC_ bool cli_credentials_set_nt_hash(struct cli_credentials *cred,
327                                  const struct samr_Password *nt_hash, 
328                                  enum credentials_obtained obtained)
329 {
330         if (obtained >= cred->password_obtained) {
331                 cli_credentials_set_password(cred, NULL, obtained);
332                 if (nt_hash) {
333                         cred->nt_hash = talloc(cred, struct samr_Password);
334                         if (cred->nt_hash == NULL) {
335                                 return false;
336                         }
337                         *cred->nt_hash = *nt_hash;
338                 } else {
339                         cred->nt_hash = NULL;
340                 }
341                 return true;
342         }
343
344         return false;
345 }
346
347 _PUBLIC_ bool cli_credentials_set_old_nt_hash(struct cli_credentials *cred,
348                                               const struct samr_Password *nt_hash)
349 {
350         cli_credentials_set_old_password(cred, NULL, CRED_SPECIFIED);
351         if (nt_hash) {
352                 cred->old_nt_hash = talloc(cred, struct samr_Password);
353                 if (cred->old_nt_hash == NULL) {
354                         return false;
355                 }
356                 *cred->old_nt_hash = *nt_hash;
357         } else {
358                 cred->old_nt_hash = NULL;
359         }
360
361         return true;
362 }
363
364 _PUBLIC_ bool cli_credentials_set_ntlm_response(struct cli_credentials *cred,
365                                                 const DATA_BLOB *lm_response, 
366                                                 const DATA_BLOB *nt_response, 
367                                                 enum credentials_obtained obtained)
368 {
369         if (obtained >= cred->password_obtained) {
370                 cli_credentials_set_password(cred, NULL, obtained);
371                 if (nt_response) {
372                         cred->nt_response = data_blob_talloc(cred, nt_response->data, nt_response->length);
373                         talloc_steal(cred, cred->nt_response.data);
374                 }
375                 if (nt_response) {
376                         cred->lm_response = data_blob_talloc(cred, lm_response->data, lm_response->length);
377                 }
378                 return true;
379         }
380
381         return false;
382 }
383