Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/libads/trusts_util.c
Line
Count
Source
1
/*
2
 *  Unix SMB/CIFS implementation.
3
 *  Routines to operate on various trust relationships
4
 *  Copyright (C) Andrew Bartlett                   2001
5
 *  Copyright (C) Rafal Szczesniak                  2003
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 3 of the License, or
10
 *  (at your option) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "includes.h"
22
#include "../libcli/auth/libcli_auth.h"
23
#include "../libcli/auth/netlogon_creds_cli.h"
24
#include "rpc_client/cli_netlogon.h"
25
#include "rpc_client/cli_pipe.h"
26
#include "../librpc/gen_ndr/ndr_netlogon.h"
27
#include "lib/krb5_wrap/krb5_samba.h"
28
#include "librpc/gen_ndr/secrets.h"
29
#include "secrets.h"
30
#include "ads.h"
31
#include "passdb.h"
32
#include "source3/include/messages.h"
33
#include "source3/include/g_lock.h"
34
#include "lib/util/util_tdb.h"
35
36
/*********************************************************
37
 Change the domain password on the PDC.
38
 Do most of the legwork ourselves.  Caller must have
39
 already setup the connection to the NETLOGON pipe
40
**********************************************************/
41
42
struct trust_pw_change_state {
43
  struct g_lock_ctx *g_ctx;
44
  char *g_lock_key;
45
};
46
47
static int trust_pw_change_state_destructor(struct trust_pw_change_state *state)
48
0
{
49
0
  g_lock_unlock(state->g_ctx,
50
0
          string_term_tdb_data(state->g_lock_key));
51
0
  return 0;
52
0
}
53
54
char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
55
       enum netr_SchannelType sec_channel_type,
56
       int security)
57
0
{
58
  /*
59
   * use secure defaults, which match
60
   * what windows uses for computer passwords.
61
   *
62
   * We used to have min=128 and max=255 here, but
63
   * it's a bad idea because of bugs in the Windows
64
   * RODC/RWDC PasswordUpdateForward handling via
65
   * NetrLogonSendToSam.
66
   *
67
   * See https://bugzilla.samba.org/show_bug.cgi?id=14984
68
   */
69
0
  size_t min = 120;
70
0
  size_t max = 120;
71
72
0
  switch (sec_channel_type) {
73
0
  case SEC_CHAN_WKSTA:
74
0
  case SEC_CHAN_BDC:
75
0
    if (security == SEC_DOMAIN) {
76
      /*
77
       * The maximum length of a trust account password.
78
       * Used when we randomly create it, 15 char passwords
79
       * exceed NT4's max password length.
80
       */
81
0
      min = 14;
82
0
      max = 14;
83
0
    }
84
0
    break;
85
0
  case SEC_CHAN_DNS_DOMAIN:
86
    /*
87
     * new_len * 2 = 498 bytes is the largest possible length
88
     * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
89
     * and a confounder with at least 2 bytes is required.
90
     *
91
     * Windows uses new_len = 120 => 240 bytes (utf16)
92
     */
93
0
    min = 120;
94
0
    max = 120;
95
0
    break;
96
0
  case SEC_CHAN_DOMAIN:
97
    /*
98
     * The maximum length of a trust account password.
99
     * Used when we randomly create it, 15 char passwords
100
     * exceed NT4's max password length.
101
     */
102
0
    min = 14;
103
0
    max = 14;
104
0
    break;
105
0
  default:
106
0
    break;
107
0
  }
108
109
  /*
110
   * Create a random machine account password
111
   * We create a random buffer and convert that to utf8.
112
   * This is similar to what windows is doing.
113
   */
114
0
  return generate_random_machine_password(mem_ctx, min, max);
115
0
}
116
117
/*
118
 * Temporary function to wrap cli_auth in a lck
119
 */
120
121
static NTSTATUS netlogon_creds_cli_lck_auth(
122
  struct netlogon_creds_cli_context *context,
123
  struct dcerpc_binding_handle *b,
124
  uint8_t num_nt_hashes,
125
  const struct samr_Password * const *nt_hashes,
126
  uint8_t *idx_nt_hashes)
127
0
{
128
0
  struct netlogon_creds_cli_lck *lck;
129
0
  NTSTATUS status;
130
131
0
  status = netlogon_creds_cli_lck(
132
0
    context, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
133
0
    talloc_tos(), &lck);
134
0
  if (!NT_STATUS_IS_OK(status)) {
135
0
    DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
136
0
          nt_errstr(status));
137
0
    return status;
138
0
  }
139
140
0
  status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes,
141
0
           idx_nt_hashes);
142
0
  TALLOC_FREE(lck);
143
144
0
  return status;
145
0
}
146
147
static NTSTATUS extract_nt_hash_and_pwd(TALLOC_CTX *mem_ctx,
148
          const struct secrets_domain_info1_password *pw,
149
          const struct samr_Password **nt_hash,
150
          const char **_pwd)
151
0
{
152
0
  char *pwd = NULL;
153
0
  size_t pwd_len = 0;
154
0
  bool ok;
155
156
0
  ok = convert_string_talloc(mem_ctx,
157
0
           CH_UTF16MUNGED, CH_UTF8,
158
0
           pw->cleartext_blob.data,
159
0
           pw->cleartext_blob.length,
160
0
           &pwd, &pwd_len);
161
0
  if (!ok) {
162
0
    NTSTATUS status = NT_STATUS_UNMAPPABLE_CHARACTER;
163
0
    if (errno == ENOMEM) {
164
0
      status = NT_STATUS_NO_MEMORY;
165
0
    }
166
0
    return status;
167
0
  }
168
0
  talloc_keep_secret(pwd);
169
170
0
  *_pwd = pwd;
171
0
  *nt_hash = &pw->nt_hash;
172
0
  return NT_STATUS_OK;
173
0
}
174
175
NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
176
       struct messaging_context *msg_ctx,
177
       struct dcerpc_binding_handle *b,
178
       const char *domain,
179
       const char *dcname,
180
       bool force)
181
0
{
182
0
  TALLOC_CTX *frame = talloc_stackframe();
183
0
  const char *context_name = NULL;
184
0
  struct trust_pw_change_state *state;
185
0
  struct cli_credentials *creds = NULL;
186
0
  struct secrets_domain_info1 *info = NULL;
187
0
  struct secrets_domain_info1_change *prev = NULL;
188
0
  const struct samr_Password *current_nt_hash = NULL;
189
0
  const struct samr_Password *previous_nt_hash = NULL;
190
0
  uint8_t num_passwords = 0;
191
0
  uint8_t idx = 0;
192
0
  const struct samr_Password *nt_hashes[1+3] = { NULL, };
193
0
  uint8_t idx_passwords = 0;
194
0
  uint8_t idx_current = UINT8_MAX;
195
0
  const char *passwords[1+3] = { NULL, };
196
0
  enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
197
0
  struct netlogon_creds_CredentialState *ncreds = NULL;
198
0
  time_t pass_last_set_time;
199
0
  uint32_t old_version = 0;
200
0
  struct pdb_trusted_domain *td = NULL;
201
0
  struct timeval g_timeout = { 0, };
202
0
  int timeout = 0;
203
0
  struct timeval tv = { 0, };
204
0
  char *new_trust_pw_str = NULL;
205
0
  size_t len = 0;
206
0
  DATA_BLOB new_trust_pw_blob = data_blob_null;
207
0
  uint32_t new_version = 0;
208
0
  uint32_t *new_trust_version = NULL;
209
0
  const char *account_principal = NULL;
210
0
  const char *explicit_kdc = NULL;
211
0
  char *cache_name = NULL;
212
0
  const char *check_password = NULL;
213
0
  NTSTATUS status;
214
0
  bool ok;
215
216
0
  state = talloc_zero(frame, struct trust_pw_change_state);
217
0
  if (state == NULL) {
218
0
    TALLOC_FREE(frame);
219
0
    return NT_STATUS_NO_MEMORY;
220
0
  }
221
222
0
  state->g_ctx = g_lock_ctx_init(state, msg_ctx);
223
0
  if (state->g_ctx == NULL) {
224
0
    TALLOC_FREE(frame);
225
0
    return NT_STATUS_NO_MEMORY;
226
0
  }
227
228
0
  state->g_lock_key = talloc_asprintf(state,
229
0
        "trust_password_change_%s",
230
0
        domain);
231
0
  if (state->g_lock_key == NULL) {
232
0
    TALLOC_FREE(frame);
233
0
    return NT_STATUS_NO_MEMORY;
234
0
  }
235
236
0
  g_timeout = timeval_current_ofs(10, 0);
237
0
  status = g_lock_lock(state->g_ctx,
238
0
           string_term_tdb_data(state->g_lock_key),
239
0
           G_LOCK_WRITE, g_timeout, NULL, NULL);
240
0
  if (!NT_STATUS_IS_OK(status)) {
241
0
    DEBUG(1, ("could not get g_lock on [%s]!\n",
242
0
        state->g_lock_key));
243
0
    TALLOC_FREE(frame);
244
0
    return status;
245
0
  }
246
247
0
  talloc_set_destructor(state, trust_pw_change_state_destructor);
248
249
0
  status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
250
0
  if (!NT_STATUS_IS_OK(status)) {
251
0
    DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
252
0
        domain, nt_errstr(status)));
253
0
    TALLOC_FREE(frame);
254
0
    return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
255
0
  }
256
257
0
  current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
258
0
  if (current_nt_hash == NULL) {
259
0
    DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
260
0
        domain));
261
0
    TALLOC_FREE(frame);
262
0
    return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
263
0
  }
264
0
  previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
265
266
0
  old_version = cli_credentials_get_kvno(creds);
267
0
  pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
268
0
  sec_channel_type = cli_credentials_get_secure_channel_type(creds);
269
270
0
  new_version = old_version + 1;
271
272
0
  switch (sec_channel_type) {
273
0
  case SEC_CHAN_WKSTA:
274
0
  case SEC_CHAN_BDC:
275
0
    break;
276
0
  case SEC_CHAN_DNS_DOMAIN:
277
0
  case SEC_CHAN_DOMAIN:
278
0
    status = pdb_get_trusted_domain(frame, domain, &td);
279
0
    if (!NT_STATUS_IS_OK(status)) {
280
0
      DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
281
0
          domain, nt_errstr(status)));
282
0
      TALLOC_FREE(frame);
283
0
      return status;
284
0
    }
285
286
0
    new_trust_version = &new_version;
287
0
    break;
288
0
  default:
289
0
    TALLOC_FREE(frame);
290
0
    return NT_STATUS_NOT_SUPPORTED;
291
0
  }
292
293
0
  timeout = lp_machine_password_timeout();
294
0
  if (timeout == 0) {
295
0
    if (!force) {
296
0
      DEBUG(10,("machine password never expires\n"));
297
0
      TALLOC_FREE(frame);
298
0
      return NT_STATUS_OK;
299
0
    }
300
0
  }
301
302
0
  tv.tv_sec = pass_last_set_time;
303
0
  DEBUG(10, ("password last changed %s\n",
304
0
       timeval_string(talloc_tos(), &tv, false)));
305
0
  tv.tv_sec += timeout;
306
0
  DEBUGADD(10, ("password valid until %s\n",
307
0
          timeval_string(talloc_tos(), &tv, false)));
308
309
0
  if (!force && !timeval_expired(&tv)) {
310
0
    TALLOC_FREE(frame);
311
0
    return NT_STATUS_OK;
312
0
  }
313
314
0
  context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
315
0
  if (context_name == NULL) {
316
0
    TALLOC_FREE(frame);
317
0
    return NT_STATUS_NO_MEMORY;
318
0
  }
319
320
  /*
321
   * Create a random machine account password
322
   * We create a random buffer and convert that to utf8.
323
   * This is similar to what windows is doing.
324
   */
325
0
  new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
326
0
                lp_security());
327
0
  if (new_trust_pw_str == NULL) {
328
0
    DEBUG(0, ("trust_pw_new_value() failed\n"));
329
0
    TALLOC_FREE(frame);
330
0
    return NT_STATUS_NO_MEMORY;
331
0
  }
332
333
0
  len = strlen(new_trust_pw_str);
334
0
  ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16,
335
0
           new_trust_pw_str, len,
336
0
           &new_trust_pw_blob.data,
337
0
           &new_trust_pw_blob.length);
338
0
  if (!ok) {
339
0
    status = NT_STATUS_UNMAPPABLE_CHARACTER;
340
0
    if (errno == ENOMEM) {
341
0
      status = NT_STATUS_NO_MEMORY;
342
0
    }
343
0
    DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
344
0
      "failed for of %s - %s\n",
345
0
      domain, nt_errstr(status));
346
0
    TALLOC_FREE(frame);
347
0
    return status;
348
0
  }
349
0
  talloc_keep_secret(new_trust_pw_blob.data);
350
351
0
  switch (sec_channel_type) {
352
353
0
  case SEC_CHAN_WKSTA:
354
0
  case SEC_CHAN_BDC:
355
0
    status = secrets_prepare_password_change(domain,
356
0
               dcname,
357
0
               new_trust_pw_str,
358
0
               frame,
359
0
               &info,
360
0
               &prev,
361
0
#ifdef HAVE_ADS
362
0
               sync_pw2keytabs,
363
#else
364
               NULL,
365
#endif
366
0
               NULL /* opt_host */);
367
0
    if (!NT_STATUS_IS_OK(status)) {
368
0
      DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
369
0
          domain));
370
0
      TALLOC_FREE(frame);
371
0
      return NT_STATUS_INTERNAL_DB_CORRUPTION;
372
0
    }
373
0
    TALLOC_FREE(new_trust_pw_str);
374
375
0
    if (prev != NULL) {
376
      /*
377
       * We had a failure before we changed the password.
378
       */
379
0
      status = extract_nt_hash_and_pwd(frame,
380
0
               prev->password,
381
0
               &nt_hashes[idx],
382
0
               &passwords[idx]);
383
0
      if (!NT_STATUS_IS_OK(status)) {
384
0
        DEBUG(0, ("extract_nt_hash_and_pwd(%s) failed "
385
0
            "for prev->password - %s!\n",
386
0
            domain, nt_errstr(status)));
387
0
        TALLOC_FREE(frame);
388
0
        return status;
389
0
      }
390
0
      idx += 1;
391
392
0
      DEBUG(0,("%s : %s(%s): A password change was already "
393
0
         "started against '%s' at %s. Trying to "
394
0
         "recover...\n",
395
0
         current_timestring(talloc_tos(), false),
396
0
         __func__, domain,
397
0
         prev->password->change_server,
398
0
         nt_time_string(talloc_tos(),
399
0
         prev->password->change_time)));
400
0
      DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] "
401
0
         "against '%s' at %s.\n",
402
0
         current_timestring(talloc_tos(), false),
403
0
         __func__, domain,
404
0
         nt_errstr(prev->local_status),
405
0
         nt_errstr(prev->remote_status),
406
0
         prev->change_server,
407
0
         nt_time_string(talloc_tos(),
408
0
         prev->change_time)));
409
0
    }
410
411
0
    idx_current = idx;
412
0
    status = extract_nt_hash_and_pwd(frame,
413
0
             info->password,
414
0
             &nt_hashes[idx],
415
0
             &passwords[idx]);
416
0
    if (!NT_STATUS_IS_OK(status)) {
417
0
      DEBUG(0, ("extract_nt_hash_and_pwd(%s) failed "
418
0
          "for info->password - %s!\n",
419
0
          domain, nt_errstr(status)));
420
0
      TALLOC_FREE(frame);
421
0
      return status;
422
0
    }
423
0
    idx += 1;
424
0
    if (info->old_password != NULL) {
425
0
      status = extract_nt_hash_and_pwd(frame,
426
0
               info->old_password,
427
0
               &nt_hashes[idx],
428
0
               &passwords[idx]);
429
0
      if (!NT_STATUS_IS_OK(status)) {
430
0
        DEBUG(0, ("extract_nt_hash_and_pwd(%s) failed "
431
0
            "for info->old_password - %s!\n",
432
0
            domain, nt_errstr(status)));
433
0
        TALLOC_FREE(frame);
434
0
        return status;
435
0
      }
436
0
      idx += 1;
437
0
    }
438
0
    if (info->older_password != NULL) {
439
0
      status = extract_nt_hash_and_pwd(frame,
440
0
               info->older_password,
441
0
               &nt_hashes[idx],
442
0
               &passwords[idx]);
443
0
      if (!NT_STATUS_IS_OK(status)) {
444
0
        DEBUG(0, ("extract_nt_hash_and_pwd(%s) failed "
445
0
            "for info->older_password - %s!\n",
446
0
            domain, nt_errstr(status)));
447
0
        TALLOC_FREE(frame);
448
0
        return status;
449
0
      }
450
0
      idx += 1;
451
0
    }
452
453
    /*
454
     * We use the password that's already persistent in
455
     * our database in order to handle failures.
456
     */
457
0
    data_blob_free(&new_trust_pw_blob);
458
0
    new_trust_pw_blob = info->next_change->password->cleartext_blob;
459
0
    break;
460
461
0
  case SEC_CHAN_DNS_DOMAIN:
462
0
  case SEC_CHAN_DOMAIN:
463
0
    idx_current = idx;
464
0
    nt_hashes[idx] = current_nt_hash;
465
0
    passwords[idx] = cli_credentials_get_password(creds);
466
0
    idx += 1;
467
0
    if (previous_nt_hash != NULL) {
468
0
      nt_hashes[idx] = previous_nt_hash;
469
0
      passwords[idx] = cli_credentials_get_old_password(creds);
470
0
      idx += 1;
471
0
    }
472
0
    break;
473
474
0
  default:
475
0
    smb_panic("Unsupported secure channel type");
476
0
    break;
477
0
  }
478
0
  num_passwords = idx;
479
480
0
  DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
481
0
     current_timestring(talloc_tos(), false),
482
0
     __func__, domain, context_name));
483
484
  /*
485
   * Check which password the dc knows about.
486
   *
487
   * TODO:
488
   * If the previous password is the only password in common with the dc,
489
   * we better skip the password change, or use something like
490
   * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
491
   * local secrets before doing the change.
492
   */
493
0
  status = netlogon_creds_cli_lck_auth(context, b,
494
0
               num_passwords,
495
0
               nt_hashes,
496
0
               &idx_passwords);
497
0
  if (!NT_STATUS_IS_OK(status)) {
498
0
    DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
499
0
        context_name, num_passwords, nt_errstr(status)));
500
0
    TALLOC_FREE(frame);
501
0
    return status;
502
0
  }
503
504
0
  status = netlogon_creds_cli_get(context, frame, &ncreds);
505
0
  if (!NT_STATUS_IS_OK(status)) {
506
0
    DBG_ERR("netlogon_creds_cli_get(%s) failed  %s!\n",
507
0
      context_name, nt_errstr(status));
508
0
    TALLOC_FREE(frame);
509
0
    return status;
510
0
  }
511
512
0
  if (ncreds->authenticate_kerberos) {
513
0
    const struct dcerpc_binding *bd =
514
0
      dcerpc_binding_handle_get_binding(b);
515
0
    const char *host = NULL;
516
0
    krb5_context kctx = NULL;
517
0
    krb5_ccache kccid = NULL;
518
0
    int ret;
519
520
0
    SMB_ASSERT(idx_passwords == 0);
521
522
0
    account_principal = cli_credentials_get_principal(creds,
523
0
                  frame);
524
0
    if (account_principal == NULL) {
525
0
      status = NT_STATUS_NO_MEMORY;
526
0
      DEBUG(0, ("cli_credentials_get_principal[%s] %s!\n",
527
0
          context_name,
528
0
          nt_errstr(status)));
529
0
      TALLOC_FREE(frame);
530
0
      return status;
531
0
    }
532
533
0
    host = dcerpc_binding_get_string_option(bd, "host");
534
0
    if (host == NULL) {
535
0
      status = NT_STATUS_INTERNAL_ERROR;
536
0
      DEBUG(0, ("dcerpc_binding_get_string_option(host)[%s] %s!\n",
537
0
          context_name,
538
0
          nt_errstr(status)));
539
0
      TALLOC_FREE(frame);
540
0
      return status;
541
0
    }
542
0
    if (!is_ipaddress(host)) {
543
0
      status = NT_STATUS_INTERNAL_ERROR;
544
0
      DEBUG(0, ("is_ipaddress(%s)[%s] => false %s!\n",
545
0
          host,
546
0
          context_name,
547
0
          nt_errstr(status)));
548
0
      TALLOC_FREE(frame);
549
0
      return status;
550
0
    }
551
0
    explicit_kdc = host;
552
553
0
    ret = smb_krb5_init_context_common(&kctx);
554
0
    if (ret != 0) {
555
0
      status = krb5_to_nt_status(ret);
556
0
      DEBUG(0, ("smb_krb5_init_context_common[%s] %s!\n",
557
0
          context_name,
558
0
          nt_errstr(status)));
559
0
      TALLOC_FREE(frame);
560
0
      return status;
561
0
    }
562
563
0
    ret = smb_krb5_cc_new_unique_memory(kctx,
564
0
                frame,
565
0
                &cache_name,
566
0
                &kccid);
567
0
    if (ret != 0) {
568
0
      krb5_free_context(kctx);
569
0
      status = krb5_to_nt_status(ret);
570
0
      DEBUG(0, ("smb_krb5_cc_new_unique_memory[%s] %s!\n",
571
0
          context_name,
572
0
          nt_errstr(status)));
573
0
      TALLOC_FREE(frame);
574
0
      return status;
575
0
    }
576
577
0
    ret = kerberos_kinit_passwords_ext(account_principal,
578
0
               num_passwords,
579
0
               passwords,
580
0
               nt_hashes,
581
0
               &idx_passwords,
582
0
               explicit_kdc,
583
0
               cache_name,
584
0
               NULL,
585
0
               NULL,
586
0
               NULL,
587
0
               &status);
588
0
    krb5_cc_destroy(kctx, kccid);
589
0
    krb5_free_context(kctx);
590
0
    if (ret != 0) {
591
0
      DEBUG(0, ("kerberos_kinit_passwords_ext(%s)[%s] "
592
0
          "failed for old passwords (%u) - %s!\n",
593
0
          account_principal,
594
0
          context_name,
595
0
          num_passwords,
596
0
          nt_errstr(status)));
597
0
      TALLOC_FREE(frame);
598
0
      return status;
599
0
    }
600
0
  }
601
602
0
  if (prev != NULL && idx_passwords == 0) {
603
0
    DEBUG(0,("%s : %s(%s): Verified new password remotely "
604
0
       "without changing %s\n",
605
0
       current_timestring(talloc_tos(), false),
606
0
       __func__, domain, context_name));
607
608
0
    status = secrets_finish_password_change(
609
0
      prev->password->change_server,
610
0
      prev->password->change_time,
611
0
      info,
612
0
#ifdef HAVE_ADS
613
0
      sync_pw2keytabs,
614
#else
615
      NULL,
616
#endif
617
0
      prev->password->change_server);
618
0
    if (!NT_STATUS_IS_OK(status)) {
619
0
      DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
620
0
          domain));
621
0
      TALLOC_FREE(frame);
622
0
      return NT_STATUS_INTERNAL_DB_CORRUPTION;
623
0
    }
624
625
0
    DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
626
0
       current_timestring(talloc_tos(), false),
627
0
       __func__, domain));
628
0
    TALLOC_FREE(frame);
629
0
    return NT_STATUS_OK;
630
0
  }
631
632
0
  if (idx_passwords != idx_current) {
633
0
    DEBUG(0,("%s : %s(%s): Verified older password remotely "
634
0
       "skip changing %s\n",
635
0
       current_timestring(talloc_tos(), false),
636
0
       __func__, domain, context_name));
637
638
0
    if (info == NULL) {
639
0
      TALLOC_FREE(frame);
640
0
      return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
641
0
    }
642
643
0
    status = secrets_defer_password_change(dcname,
644
0
          NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE,
645
0
          NT_STATUS_NOT_COMMITTED,
646
0
          info);
647
0
    if (!NT_STATUS_IS_OK(status)) {
648
0
      DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
649
0
          domain));
650
0
      TALLOC_FREE(frame);
651
0
      return NT_STATUS_INTERNAL_DB_CORRUPTION;
652
0
    }
653
0
    TALLOC_FREE(frame);
654
0
    return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
655
0
  }
656
657
0
  DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
658
0
     current_timestring(talloc_tos(), false),
659
0
     __func__, domain, context_name));
660
661
  /*
662
   * Return the result of trying to write the new password
663
   * back into the trust account file.
664
   */
665
666
0
  switch (sec_channel_type) {
667
668
0
  case SEC_CHAN_WKSTA:
669
0
  case SEC_CHAN_BDC:
670
    /*
671
     * we called secrets_prepare_password_change() above.
672
     */
673
0
    break;
674
675
0
  case SEC_CHAN_DNS_DOMAIN:
676
0
  case SEC_CHAN_DOMAIN:
677
    /*
678
     * we need to get the sid first for the
679
     * pdb_set_trusteddom_pw call
680
     */
681
0
    ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str,
682
0
             &td->security_identifier);
683
0
    if (!ok) {
684
0
      DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
685
0
          domain));
686
0
      TALLOC_FREE(frame);
687
0
      return NT_STATUS_INTERNAL_DB_CORRUPTION;
688
0
    }
689
0
    TALLOC_FREE(new_trust_pw_str);
690
0
    break;
691
692
0
  default:
693
0
    smb_panic("Unsupported secure channel type");
694
0
    break;
695
0
  }
696
697
0
  DEBUG(0,("%s : %s(%s): Changed password locally\n",
698
0
     current_timestring(talloc_tos(), false), __func__, domain));
699
700
0
  status = netlogon_creds_cli_ServerPasswordSet(context, b,
701
0
                  &new_trust_pw_blob,
702
0
                  new_trust_version);
703
0
  if (!NT_STATUS_IS_OK(status)) {
704
0
    NTSTATUS status2;
705
0
    const char *fn = NULL;
706
707
0
    ok = dcerpc_binding_handle_is_connected(b);
708
709
0
    DEBUG(0,("%s : %s(%s) remote password change with %s failed "
710
0
       "- %s (%s)\n",
711
0
       current_timestring(talloc_tos(), false),
712
0
       __func__, domain, context_name,
713
0
       nt_errstr(status),
714
0
       ok ? "connected": "disconnected"));
715
716
0
    if (!ok) {
717
      /*
718
       * The connection is broken, we don't
719
       * know if the password was changed,
720
       * we hope to have more luck next time.
721
       */
722
0
      status2 = secrets_failed_password_change(dcname,
723
0
              NT_STATUS_NOT_COMMITTED,
724
0
              status,
725
0
              info);
726
0
      fn = "secrets_failed_password_change";
727
0
    } else {
728
      /*
729
       * The server rejected the change, we don't
730
       * retry and defer the change to the next
731
       * "machine password timeout" interval.
732
       */
733
0
      status2 = secrets_defer_password_change(dcname,
734
0
              NT_STATUS_NOT_COMMITTED,
735
0
              status,
736
0
              info);
737
0
      fn = "secrets_defer_password_change";
738
0
    }
739
0
    if (!NT_STATUS_IS_OK(status2)) {
740
0
      DEBUG(0, ("%s() failed for domain %s!\n",
741
0
          fn, domain));
742
0
      TALLOC_FREE(frame);
743
0
      return NT_STATUS_INTERNAL_DB_CORRUPTION;
744
0
    }
745
746
0
    TALLOC_FREE(frame);
747
0
    return status;
748
0
  }
749
750
0
  DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
751
0
     current_timestring(talloc_tos(), false),
752
0
     __func__, domain, context_name));
753
754
0
  switch (sec_channel_type) {
755
756
0
  case SEC_CHAN_WKSTA:
757
0
  case SEC_CHAN_BDC:
758
0
    status = secrets_finish_password_change(
759
0
      info->next_change->change_server,
760
0
      info->next_change->change_time,
761
0
      info,
762
0
#ifdef HAVE_ADS
763
0
      sync_pw2keytabs,
764
#else
765
      NULL,
766
#endif
767
0
      info->next_change->change_server);
768
0
    if (!NT_STATUS_IS_OK(status)) {
769
0
      DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
770
0
          domain));
771
0
      TALLOC_FREE(frame);
772
0
      return NT_STATUS_INTERNAL_DB_CORRUPTION;
773
0
    }
774
775
0
    DEBUG(0,("%s : %s(%s): Finished password change.\n",
776
0
       current_timestring(talloc_tos(), false),
777
0
       __func__, domain));
778
0
    break;
779
780
0
  case SEC_CHAN_DNS_DOMAIN:
781
0
  case SEC_CHAN_DOMAIN:
782
    /*
783
     * we used pdb_set_trusteddom_pw().
784
     */
785
0
    break;
786
787
0
  default:
788
0
    smb_panic("Unsupported secure channel type");
789
0
    break;
790
0
  }
791
792
0
  ok = cli_credentials_set_utf16_password(creds,
793
0
            &new_trust_pw_blob,
794
0
            CRED_SPECIFIED);
795
0
  if (!ok) {
796
0
    DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
797
0
        domain));
798
0
    TALLOC_FREE(frame);
799
0
    return NT_STATUS_NO_MEMORY;
800
0
  }
801
802
0
  current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
803
0
  if (current_nt_hash == NULL) {
804
0
    DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
805
0
        domain));
806
0
    TALLOC_FREE(frame);
807
0
    return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
808
0
  }
809
810
0
  check_password = cli_credentials_get_password(creds);
811
0
  if (check_password == NULL) {
812
0
    DEBUG(0, ("cli_credentials_get_password failed for domain %s!\n",
813
0
        domain));
814
0
    TALLOC_FREE(frame);
815
0
    return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
816
0
  }
817
818
  /*
819
   * Now we verify the new password.
820
   */
821
0
  idx = 0;
822
0
  nt_hashes[idx] = current_nt_hash;
823
0
  passwords[idx] = check_password;
824
0
  idx += 1;
825
0
  num_passwords = idx;
826
0
  if (ncreds->authenticate_kerberos) {
827
0
    krb5_context kctx = NULL;
828
0
    krb5_ccache kccid = NULL;
829
0
    int ret;
830
831
0
    ret = smb_krb5_init_context_common(&kctx);
832
0
    if (ret != 0) {
833
0
      status = krb5_to_nt_status(ret);
834
0
      DEBUG(0, ("smb_krb5_init_context_common[%s] %s!\n",
835
0
          context_name,
836
0
          nt_errstr(status)));
837
0
      TALLOC_FREE(frame);
838
0
      return status;
839
0
    }
840
841
0
    ret = smb_krb5_cc_new_unique_memory(kctx,
842
0
                frame,
843
0
                &cache_name,
844
0
                &kccid);
845
0
    if (ret != 0) {
846
0
      krb5_free_context(kctx);
847
0
      status = krb5_to_nt_status(ret);
848
0
      DEBUG(0, ("smb_krb5_cc_new_unique_memory[%s] %s!\n",
849
0
          context_name,
850
0
          nt_errstr(status)));
851
0
      TALLOC_FREE(frame);
852
0
      return status;
853
0
    }
854
855
0
    ret = kerberos_kinit_passwords_ext(account_principal,
856
0
               num_passwords,
857
0
               passwords,
858
0
               nt_hashes,
859
0
               &idx_passwords,
860
0
               explicit_kdc,
861
0
               cache_name,
862
0
               NULL,
863
0
               NULL,
864
0
               NULL,
865
0
               &status);
866
0
    krb5_cc_destroy(kctx, kccid);
867
0
    krb5_free_context(kctx);
868
0
    if (ret != 0) {
869
0
      DEBUG(0, ("kerberos_kinit_passwords_ext(%s)[%s] "
870
0
          "failed for new password - %s!\n",
871
0
          account_principal,
872
0
          context_name,
873
0
          nt_errstr(status)));
874
0
      TALLOC_FREE(frame);
875
0
      return status;
876
0
    }
877
0
  } else {
878
0
    status = netlogon_creds_cli_lck_auth(context, b,
879
0
                 num_passwords,
880
0
                 nt_hashes,
881
0
                 &idx_passwords);
882
0
    if (!NT_STATUS_IS_OK(status)) {
883
0
      DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
884
0
          context_name, nt_errstr(status)));
885
0
      TALLOC_FREE(frame);
886
0
      return status;
887
0
    }
888
0
  }
889
890
0
  DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
891
0
     current_timestring(talloc_tos(), false),
892
0
     __func__, domain, context_name));
893
894
0
  TALLOC_FREE(frame);
895
0
  return NT_STATUS_OK;
896
0
}