lib/crypto: move gnutls error wrapper to own subsystem
[gd/samba-autobuild/.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 #include "lib/crypto/gnutls_helpers.h"
32 #include <gnutls/gnutls.h>
33 #include <gnutls/crypto.h>
34
35 #undef DBGC_CLASS
36 #define DBGC_CLASS DBGC_AUTH
37
38 _PUBLIC_ NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, 
39                                            int *flags,
40                                            DATA_BLOB challenge,
41                                            const NTTIME *server_timestamp,
42                                            DATA_BLOB target_info,
43                                            DATA_BLOB *_lm_response, DATA_BLOB *_nt_response, 
44                                            DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key) 
45 {
46         TALLOC_CTX *frame = talloc_stackframe();
47         const char *user = NULL;
48         const char *domain = NULL;
49         DATA_BLOB lm_response = data_blob_null;
50         DATA_BLOB nt_response = data_blob_null;
51         DATA_BLOB lm_session_key = data_blob_null;
52         DATA_BLOB session_key = data_blob_null;
53         const struct samr_Password *nt_hash = NULL;
54
55         if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) {
56                 TALLOC_FREE(frame);
57                 return NT_STATUS_INVALID_PARAMETER_MIX;
58         }
59
60         /* We may already have an NTLM response we prepared earlier.
61          * This is used for NTLM pass-though authentication */
62         if (cred->nt_response.data || cred->lm_response.data) {
63                 if (cred->nt_response.length != 0) {
64                         nt_response = data_blob_dup_talloc(frame,
65                                                            cred->nt_response);
66                         if (nt_response.data == NULL) {
67                                 TALLOC_FREE(frame);
68                                 return NT_STATUS_NO_MEMORY;
69                         }
70                 }
71                 if (cred->lm_response.length != 0) {
72                         lm_response = data_blob_dup_talloc(frame,
73                                                            cred->lm_response);
74                         if (lm_response.data == NULL) {
75                                 TALLOC_FREE(frame);
76                                 return NT_STATUS_NO_MEMORY;
77                         }
78                 }
79
80                 if (cred->lm_response.data == NULL) {
81                         *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
82                 }
83                 goto done;
84         }
85
86         nt_hash = cli_credentials_get_nt_hash(cred, frame);
87
88         cli_credentials_get_ntlm_username_domain(cred, frame, &user, &domain);
89         if (user == NULL) {
90                 TALLOC_FREE(frame);
91                 return NT_STATUS_NO_MEMORY;
92         }
93         if (domain == NULL) {
94                 TALLOC_FREE(frame);
95                 return NT_STATUS_NO_MEMORY;
96         }
97
98         /* If we are sending a username@realm login (see function
99          * above), then we will not send LM, it will not be
100          * accepted */
101         if (cred->principal_obtained > cred->username_obtained) {
102                 *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
103         }
104
105         /* Likewise if we are a machine account (avoid protocol downgrade attacks) */
106         if (cred->machine_account) {
107                 *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
108         }
109
110         if (!nt_hash) {
111                 /* do nothing - blobs are zero length */
112
113                 /* session key is all zeros */
114                 session_key = data_blob_talloc_zero(frame, 16);
115                 if (session_key.data == NULL) {
116                         TALLOC_FREE(frame);
117                         return NT_STATUS_NO_MEMORY;
118                 }
119                 lm_session_key = data_blob_talloc_zero(frame, 16);
120                 if (lm_session_key.data == NULL) {
121                         TALLOC_FREE(frame);
122                         return NT_STATUS_NO_MEMORY;
123                 }
124
125                 /* not doing NTLM2 without a password */
126                 *flags &= ~CLI_CRED_NTLM2;
127         } else if (*flags & CLI_CRED_NTLMv2_AUTH) {
128
129                 if (!target_info.length) {
130                         /* be lazy, match win2k - we can't do NTLMv2 without it */
131                         DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
132                         TALLOC_FREE(frame);
133                         return NT_STATUS_INVALID_PARAMETER;
134                 }
135
136                 /* TODO: if the remote server is standalone, then we should replace 'domain'
137                    with the server name as supplied above */
138                 
139                 if (!SMBNTLMv2encrypt_hash(frame,
140                                            user, 
141                                            domain, 
142                                            nt_hash->hash, &challenge, 
143                                            server_timestamp, &target_info,
144                                            &lm_response, &nt_response, 
145                                            NULL, &session_key)) {
146                         TALLOC_FREE(frame);
147                         return NT_STATUS_NO_MEMORY;
148                 }
149
150                 /* LM Key is incompatible... */
151                 *flags &= ~CLI_CRED_LANMAN_AUTH;
152                 if (lm_response.length != 0) {
153                         /*
154                          * We should not expose the lm key.
155                          */
156                         memset(lm_response.data, 0, lm_response.length);
157                 }
158         } else if (*flags & CLI_CRED_NTLM2) {
159                 uint8_t session_nonce[16];
160                 uint8_t session_nonce_hash[16];
161                 uint8_t user_session_key[16];
162                 int rc;
163
164                 lm_response = data_blob_talloc_zero(frame, 24);
165                 if (lm_response.data == NULL) {
166                         TALLOC_FREE(frame);
167                         return NT_STATUS_NO_MEMORY;
168                 }
169                 generate_random_buffer(lm_response.data, 8);
170
171                 memcpy(session_nonce, challenge.data, 8);
172                 memcpy(&session_nonce[8], lm_response.data, 8);
173
174                 rc = gnutls_hash_fast(GNUTLS_DIG_MD5,
175                                       session_nonce,
176                                       sizeof(session_nonce),
177                                       session_nonce_hash);
178                 if (rc < 0) {
179                         return gnutls_error_to_ntstatus(rc, NT_STATUS_NTLM_BLOCKED);
180                 }
181
182                 DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
183                 DEBUG(5, ("challenge is: \n"));
184                 dump_data(5, session_nonce_hash, 8);
185
186                 nt_response = data_blob_talloc_zero(frame, 24);
187                 if (nt_response.data == NULL) {
188                         TALLOC_FREE(frame);
189                         return NT_STATUS_NO_MEMORY;
190                 }
191                 SMBOWFencrypt(nt_hash->hash,
192                               session_nonce_hash,
193                               nt_response.data);
194
195                 ZERO_ARRAY(session_nonce_hash);
196
197                 session_key = data_blob_talloc_zero(frame, 16);
198                 if (session_key.data == NULL) {
199                         TALLOC_FREE(frame);
200                         return NT_STATUS_NO_MEMORY;
201                 }
202
203                 SMBsesskeygen_ntv1(nt_hash->hash, user_session_key);
204
205                 rc = gnutls_hmac_fast(GNUTLS_MAC_MD5,
206                                       user_session_key,
207                                       sizeof(user_session_key),
208                                       session_nonce,
209                                       sizeof(session_nonce),
210                                       session_key.data);
211                 if (rc < 0) {
212                         return gnutls_error_to_ntstatus(rc, NT_STATUS_NTLM_BLOCKED);
213                 }
214
215                 ZERO_ARRAY(user_session_key);
216
217                 dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
218
219                 /* LM Key is incompatible... */
220                 *flags &= ~CLI_CRED_LANMAN_AUTH;
221         } else {
222                 const char *password = cli_credentials_get_password(cred);
223                 uint8_t lm_hash[16];
224                 bool do_lm = false;
225
226                 nt_response = data_blob_talloc_zero(frame, 24);
227                 if (nt_response.data == NULL) {
228                         TALLOC_FREE(frame);
229                         return NT_STATUS_NO_MEMORY;
230                 }
231                 SMBOWFencrypt(nt_hash->hash, challenge.data,
232                               nt_response.data);
233
234                 session_key = data_blob_talloc_zero(frame, 16);
235                 if (session_key.data == NULL) {
236                         TALLOC_FREE(frame);
237                         return NT_STATUS_NO_MEMORY;
238                 }
239                 SMBsesskeygen_ntv1(nt_hash->hash, session_key.data);
240                 dump_data_pw("NT session key:\n", session_key.data, session_key.length);
241
242                 /* lanman auth is insecure, it may be disabled.  
243                    We may also not have a password */
244
245                 if (password != NULL) {
246                         do_lm = E_deshash(password, lm_hash);
247                 }
248
249                 if (*flags & CLI_CRED_LANMAN_AUTH && do_lm) {
250                         lm_response = data_blob_talloc_zero(frame, 24);
251                         if (lm_response.data == NULL) {
252                                 ZERO_STRUCT(lm_hash);
253                                 TALLOC_FREE(frame);
254                                 return NT_STATUS_NO_MEMORY;
255                         }
256
257                         SMBencrypt_hash(lm_hash,
258                                         challenge.data,
259                                         lm_response.data);
260                 } else {
261                         /* just copy the nt_response */
262                         lm_response = data_blob_dup_talloc(frame, nt_response);
263                         if (lm_response.data == NULL) {
264                                 ZERO_STRUCT(lm_hash);
265                                 TALLOC_FREE(frame);
266                                 return NT_STATUS_NO_MEMORY;
267                         }
268                 }
269
270                 if (do_lm) {
271                         lm_session_key = data_blob_talloc_zero(frame, 16);
272                         if (lm_session_key.data == NULL) {
273                                 ZERO_STRUCT(lm_hash);
274                                 TALLOC_FREE(frame);
275                                 return NT_STATUS_NO_MEMORY;
276                         }
277                         memcpy(lm_session_key.data, lm_hash, 8);
278
279                         if (!(*flags & CLI_CRED_NTLM_AUTH)) {
280                                 memcpy(session_key.data, lm_session_key.data, 16);
281                         }
282                         ZERO_STRUCT(lm_hash);
283                 }
284         }
285
286 done:
287         if (_lm_response != NULL) {
288                 talloc_steal(mem_ctx, lm_response.data);
289                 *_lm_response = lm_response;
290         } else {
291                 data_blob_clear(&lm_response);
292         }
293         if (_nt_response != NULL) {
294                 talloc_steal(mem_ctx, nt_response.data);
295                 *_nt_response = nt_response;
296         } else {
297                 data_blob_clear(&nt_response);
298         }
299         if (_lm_session_key != NULL) {
300                 talloc_steal(mem_ctx, lm_session_key.data);
301                 *_lm_session_key = lm_session_key;
302         } else {
303                 data_blob_clear(&lm_session_key);
304         }
305         if (_session_key != NULL) {
306                 talloc_steal(mem_ctx, session_key.data);
307                 *_session_key = session_key;
308         } else {
309                 data_blob_clear(&session_key);
310         }
311         TALLOC_FREE(frame);
312         return NT_STATUS_OK;
313 }
314
315 /*
316  * Set a utf16 password on the credentials context, including an indication
317  * of 'how' the password was obtained
318  *
319  * This is required because the nt_hash is calculated over the raw utf16 blob,
320  * which might not be completely valid utf16, which means the conversion
321  * from CH_UTF16MUNGED to CH_UTF8 might loose information.
322  */
323 _PUBLIC_ bool cli_credentials_set_utf16_password(struct cli_credentials *cred,
324                                                  const DATA_BLOB *password_utf16,
325                                                  enum credentials_obtained obtained)
326 {
327         cred->password_will_be_nt_hash = false;
328
329         if (password_utf16 == NULL) {
330                 return cli_credentials_set_password(cred, NULL, obtained);
331         }
332
333         if (obtained >= cred->password_obtained) {
334                 struct samr_Password *nt_hash = NULL;
335                 char *password_talloc = NULL;
336                 size_t password_len = 0;
337                 bool ok;
338
339                 nt_hash = talloc(cred, struct samr_Password);
340                 if (nt_hash == NULL) {
341                         return false;
342                 }
343
344                 ok = convert_string_talloc(cred,
345                                            CH_UTF16MUNGED, CH_UTF8,
346                                            password_utf16->data,
347                                            password_utf16->length,
348                                            (void *)&password_talloc,
349                                            &password_len);
350                 if (!ok) {
351                         TALLOC_FREE(nt_hash);
352                         return false;
353                 }
354
355                 ok = cli_credentials_set_password(cred, password_talloc, obtained);
356                 TALLOC_FREE(password_talloc);
357                 if (!ok) {
358                         TALLOC_FREE(nt_hash);
359                         return false;
360                 }
361
362                 mdfour(nt_hash->hash, password_utf16->data, password_utf16->length);
363                 cred->nt_hash = nt_hash;
364                 return true;
365         }
366
367         return false;
368 }
369
370 /*
371  * Set a old utf16 password on the credentials context.
372  *
373  * This is required because the nt_hash is calculated over the raw utf16 blob,
374  * which might not be completely valid utf16, which means the conversion
375  * from CH_UTF16MUNGED to CH_UTF8 might loose information.
376  */
377 _PUBLIC_ bool cli_credentials_set_old_utf16_password(struct cli_credentials *cred,
378                                                      const DATA_BLOB *password_utf16)
379 {
380         struct samr_Password *nt_hash = NULL;
381         char *password_talloc = NULL;
382         size_t password_len = 0;
383         bool ok;
384
385         if (password_utf16 == NULL) {
386                 return cli_credentials_set_old_password(cred, NULL, CRED_SPECIFIED);
387         }
388
389         nt_hash = talloc(cred, struct samr_Password);
390         if (nt_hash == NULL) {
391                 return false;
392         }
393
394         ok = convert_string_talloc(cred,
395                                    CH_UTF16MUNGED, CH_UTF8,
396                                    password_utf16->data,
397                                    password_utf16->length,
398                                    (void *)&password_talloc,
399                                    &password_len);
400         if (!ok) {
401                 TALLOC_FREE(nt_hash);
402                 return false;
403         }
404
405         ok = cli_credentials_set_old_password(cred, password_talloc, CRED_SPECIFIED);
406         TALLOC_FREE(password_talloc);
407         if (!ok) {
408                 TALLOC_FREE(nt_hash);
409                 return false;
410         }
411
412         mdfour(nt_hash->hash, password_utf16->data, password_utf16->length);
413         cred->old_nt_hash = nt_hash;
414         return true;
415 }
416
417 _PUBLIC_ void cli_credentials_set_password_will_be_nt_hash(struct cli_credentials *cred,
418                                                            bool val)
419 {
420         /*
421          * We set this here and the next cli_credentials_set_password()
422          * that resets the password or password callback
423          * will pick this up.
424          *
425          * cli_credentials_set_nt_hash() and
426          * cli_credentials_set_utf16_password() will reset this
427          * to false.
428          */
429         cred->password_will_be_nt_hash = val;
430 }
431
432 _PUBLIC_ bool cli_credentials_set_nt_hash(struct cli_credentials *cred,
433                                  const struct samr_Password *nt_hash, 
434                                  enum credentials_obtained obtained)
435 {
436         cred->password_will_be_nt_hash = false;
437
438         if (obtained >= cred->password_obtained) {
439                 cli_credentials_set_password(cred, NULL, obtained);
440                 if (nt_hash) {
441                         cred->nt_hash = talloc(cred, struct samr_Password);
442                         if (cred->nt_hash == NULL) {
443                                 return false;
444                         }
445                         *cred->nt_hash = *nt_hash;
446                 } else {
447                         cred->nt_hash = NULL;
448                 }
449                 return true;
450         }
451
452         return false;
453 }
454
455 _PUBLIC_ bool cli_credentials_set_old_nt_hash(struct cli_credentials *cred,
456                                               const struct samr_Password *nt_hash)
457 {
458         cli_credentials_set_old_password(cred, NULL, CRED_SPECIFIED);
459         if (nt_hash) {
460                 cred->old_nt_hash = talloc(cred, struct samr_Password);
461                 if (cred->old_nt_hash == NULL) {
462                         return false;
463                 }
464                 *cred->old_nt_hash = *nt_hash;
465         } else {
466                 cred->old_nt_hash = NULL;
467         }
468
469         return true;
470 }
471
472 _PUBLIC_ bool cli_credentials_set_ntlm_response(struct cli_credentials *cred,
473                                                 const DATA_BLOB *lm_response, 
474                                                 const DATA_BLOB *nt_response, 
475                                                 enum credentials_obtained obtained)
476 {
477         if (obtained >= cred->password_obtained) {
478                 cli_credentials_set_password(cred, NULL, obtained);
479                 if (nt_response) {
480                         cred->nt_response = data_blob_talloc(cred, nt_response->data, nt_response->length);
481                         talloc_steal(cred, cred->nt_response.data);
482                 }
483                 if (nt_response) {
484                         cred->lm_response = data_blob_talloc(cred, lm_response->data, lm_response->length);
485                 }
486                 return true;
487         }
488
489         return false;
490 }
491