Coverage Report

Created: 2026-09-03 07:24

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