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
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.
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.
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/>.
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 "librpc/gen_ndr/secrets.h"
31 #include "libsmb/libsmb.h"
32 #include "source3/include/messages.h"
33 #include "source3/include/g_lock.h"
34 #include "lib/util/util_tdb.h"
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 **********************************************************/
42 struct trust_pw_change_state
{
43 struct g_lock_ctx
*g_ctx
;
47 static int trust_pw_change_state_destructor(struct trust_pw_change_state
*state
)
49 g_lock_unlock(state
->g_ctx
,
50 string_term_tdb_data(state
->g_lock_key
));
54 char *trust_pw_new_value(TALLOC_CTX
*mem_ctx
,
55 enum netr_SchannelType sec_channel_type
,
59 * use secure defaults, which match
60 * what windows uses for computer passwords.
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
67 * See https://bugzilla.samba.org/show_bug.cgi?id=14984
72 switch (sec_channel_type
) {
75 if (security
== SEC_DOMAIN
) {
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.
85 case SEC_CHAN_DNS_DOMAIN
:
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.
91 * Windows uses new_len = 120 => 240 bytes (utf16)
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.
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.
114 return generate_random_machine_password(mem_ctx
, min
, max
);
118 * Temporary function to wrap cli_auth in a lck
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
)
128 struct netlogon_creds_cli_lck
*lck
;
131 status
= netlogon_creds_cli_lck(
132 context
, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE
,
134 if (!NT_STATUS_IS_OK(status
)) {
135 DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
140 status
= netlogon_creds_cli_auth(context
, b
, num_nt_hashes
, nt_hashes
,
147 NTSTATUS
trust_pw_change(struct netlogon_creds_cli_context
*context
,
148 struct messaging_context
*msg_ctx
,
149 struct dcerpc_binding_handle
*b
,
154 TALLOC_CTX
*frame
= talloc_stackframe();
155 const char *context_name
= NULL
;
156 struct trust_pw_change_state
*state
;
157 struct cli_credentials
*creds
= NULL
;
158 struct secrets_domain_info1
*info
= NULL
;
159 struct secrets_domain_info1_change
*prev
= NULL
;
160 const struct samr_Password
*current_nt_hash
= NULL
;
161 const struct samr_Password
*previous_nt_hash
= NULL
;
162 uint8_t num_nt_hashes
= 0;
164 const struct samr_Password
*nt_hashes
[1+3] = { NULL
, };
165 uint8_t idx_nt_hashes
= 0;
166 uint8_t idx_current
= UINT8_MAX
;
167 enum netr_SchannelType sec_channel_type
= SEC_CHAN_NULL
;
168 time_t pass_last_set_time
;
169 uint32_t old_version
= 0;
170 struct pdb_trusted_domain
*td
= NULL
;
171 struct timeval g_timeout
= { 0, };
173 struct timeval tv
= { 0, };
174 char *new_trust_pw_str
= NULL
;
176 DATA_BLOB new_trust_pw_blob
= data_blob_null
;
177 uint32_t new_version
= 0;
178 uint32_t *new_trust_version
= NULL
;
182 state
= talloc_zero(frame
, struct trust_pw_change_state
);
185 return NT_STATUS_NO_MEMORY
;
188 state
->g_ctx
= g_lock_ctx_init(state
, msg_ctx
);
189 if (state
->g_ctx
== NULL
) {
191 return NT_STATUS_NO_MEMORY
;
194 state
->g_lock_key
= talloc_asprintf(state
,
195 "trust_password_change_%s",
197 if (state
->g_lock_key
== NULL
) {
199 return NT_STATUS_NO_MEMORY
;
202 g_timeout
= timeval_current_ofs(10, 0);
203 status
= g_lock_lock(state
->g_ctx
,
204 string_term_tdb_data(state
->g_lock_key
),
205 G_LOCK_WRITE
, g_timeout
, NULL
, NULL
);
206 if (!NT_STATUS_IS_OK(status
)) {
207 DEBUG(1, ("could not get g_lock on [%s]!\n",
213 talloc_set_destructor(state
, trust_pw_change_state_destructor
);
215 status
= pdb_get_trust_credentials(domain
, NULL
, frame
, &creds
);
216 if (!NT_STATUS_IS_OK(status
)) {
217 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
218 domain
, nt_errstr(status
)));
220 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
;
223 current_nt_hash
= cli_credentials_get_nt_hash(creds
, frame
);
224 if (current_nt_hash
== NULL
) {
225 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
228 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
;
230 previous_nt_hash
= cli_credentials_get_old_nt_hash(creds
, frame
);
232 old_version
= cli_credentials_get_kvno(creds
);
233 pass_last_set_time
= cli_credentials_get_password_last_changed_time(creds
);
234 sec_channel_type
= cli_credentials_get_secure_channel_type(creds
);
236 new_version
= old_version
+ 1;
238 switch (sec_channel_type
) {
242 case SEC_CHAN_DNS_DOMAIN
:
243 case SEC_CHAN_DOMAIN
:
244 status
= pdb_get_trusted_domain(frame
, domain
, &td
);
245 if (!NT_STATUS_IS_OK(status
)) {
246 DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
247 domain
, nt_errstr(status
)));
252 new_trust_version
= &new_version
;
256 return NT_STATUS_NOT_SUPPORTED
;
259 timeout
= lp_machine_password_timeout();
262 DEBUG(10,("machine password never expires\n"));
268 tv
.tv_sec
= pass_last_set_time
;
269 DEBUG(10, ("password last changed %s\n",
270 timeval_string(talloc_tos(), &tv
, false)));
271 tv
.tv_sec
+= timeout
;
272 DEBUGADD(10, ("password valid until %s\n",
273 timeval_string(talloc_tos(), &tv
, false)));
275 if (!force
&& !timeval_expired(&tv
)) {
280 context_name
= netlogon_creds_cli_debug_string(context
, talloc_tos());
281 if (context_name
== NULL
) {
283 return NT_STATUS_NO_MEMORY
;
287 * Create a random machine account password
288 * We create a random buffer and convert that to utf8.
289 * This is similar to what windows is doing.
291 new_trust_pw_str
= trust_pw_new_value(frame
, sec_channel_type
,
293 if (new_trust_pw_str
== NULL
) {
294 DEBUG(0, ("trust_pw_new_value() failed\n"));
296 return NT_STATUS_NO_MEMORY
;
299 len
= strlen(new_trust_pw_str
);
300 ok
= convert_string_talloc(frame
, CH_UNIX
, CH_UTF16
,
301 new_trust_pw_str
, len
,
302 (void **)&new_trust_pw_blob
.data
,
303 &new_trust_pw_blob
.length
);
305 status
= NT_STATUS_UNMAPPABLE_CHARACTER
;
306 if (errno
== ENOMEM
) {
307 status
= NT_STATUS_NO_MEMORY
;
309 DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
310 "failed for of %s - %s\n",
311 domain
, nt_errstr(status
));
315 talloc_keep_secret(new_trust_pw_blob
.data
);
317 switch (sec_channel_type
) {
321 status
= secrets_prepare_password_change(domain
,
332 if (!NT_STATUS_IS_OK(status
)) {
333 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
336 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
338 TALLOC_FREE(new_trust_pw_str
);
342 * We had a failure before we changed the password.
344 nt_hashes
[idx
++] = &prev
->password
->nt_hash
;
346 DEBUG(0,("%s : %s(%s): A password change was already "
347 "started against '%s' at %s. Trying to "
349 current_timestring(talloc_tos(), false),
351 prev
->password
->change_server
,
352 nt_time_string(talloc_tos(),
353 prev
->password
->change_time
)));
354 DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] "
355 "against '%s' at %s.\n",
356 current_timestring(talloc_tos(), false),
358 nt_errstr(prev
->local_status
),
359 nt_errstr(prev
->remote_status
),
361 nt_time_string(talloc_tos(),
362 prev
->change_time
)));
366 nt_hashes
[idx
++] = &info
->password
->nt_hash
;
367 if (info
->old_password
!= NULL
) {
368 nt_hashes
[idx
++] = &info
->old_password
->nt_hash
;
370 if (info
->older_password
!= NULL
) {
371 nt_hashes
[idx
++] = &info
->older_password
->nt_hash
;
375 * We use the password that's already persistent in
376 * our database in order to handle failures.
378 data_blob_free(&new_trust_pw_blob
);
379 new_trust_pw_blob
= info
->next_change
->password
->cleartext_blob
;
382 case SEC_CHAN_DNS_DOMAIN
:
383 case SEC_CHAN_DOMAIN
:
385 nt_hashes
[idx
++] = current_nt_hash
;
386 if (previous_nt_hash
!= NULL
) {
387 nt_hashes
[idx
++] = previous_nt_hash
;
392 smb_panic("Unsupported secure channel type");
397 DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
398 current_timestring(talloc_tos(), false),
399 __func__
, domain
, context_name
));
402 * Check which password the dc knows about.
405 * If the previous password is the only password in common with the dc,
406 * we better skip the password change, or use something like
407 * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
408 * local secrets before doing the change.
410 status
= netlogon_creds_cli_lck_auth(context
, b
,
414 if (!NT_STATUS_IS_OK(status
)) {
415 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
416 context_name
, num_nt_hashes
, nt_errstr(status
)));
421 if (prev
!= NULL
&& idx_nt_hashes
== 0) {
422 DEBUG(0,("%s : %s(%s): Verified new password remotely "
423 "without changing %s\n",
424 current_timestring(talloc_tos(), false),
425 __func__
, domain
, context_name
));
427 status
= secrets_finish_password_change(
428 prev
->password
->change_server
,
429 prev
->password
->change_time
,
436 if (!NT_STATUS_IS_OK(status
)) {
437 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
440 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
443 DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
444 current_timestring(talloc_tos(), false),
450 if (idx_nt_hashes
!= idx_current
) {
451 DEBUG(0,("%s : %s(%s): Verified older password remotely "
452 "skip changing %s\n",
453 current_timestring(talloc_tos(), false),
454 __func__
, domain
, context_name
));
458 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
;
461 status
= secrets_defer_password_change(dcname
,
462 NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
,
463 NT_STATUS_NOT_COMMITTED
,
465 if (!NT_STATUS_IS_OK(status
)) {
466 DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
469 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
472 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
;
475 DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
476 current_timestring(talloc_tos(), false),
477 __func__
, domain
, context_name
));
480 * Return the result of trying to write the new password
481 * back into the trust account file.
484 switch (sec_channel_type
) {
489 * we called secrets_prepare_password_change() above.
493 case SEC_CHAN_DNS_DOMAIN
:
494 case SEC_CHAN_DOMAIN
:
496 * we need to get the sid first for the
497 * pdb_set_trusteddom_pw call
499 ok
= pdb_set_trusteddom_pw(domain
, new_trust_pw_str
,
500 &td
->security_identifier
);
502 DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
505 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
507 TALLOC_FREE(new_trust_pw_str
);
511 smb_panic("Unsupported secure channel type");
515 DEBUG(0,("%s : %s(%s): Changed password locally\n",
516 current_timestring(talloc_tos(), false), __func__
, domain
));
518 status
= netlogon_creds_cli_ServerPasswordSet(context
, b
,
521 if (!NT_STATUS_IS_OK(status
)) {
523 const char *fn
= NULL
;
525 ok
= dcerpc_binding_handle_is_connected(b
);
527 DEBUG(0,("%s : %s(%s) remote password change with %s failed "
529 current_timestring(talloc_tos(), false),
530 __func__
, domain
, context_name
,
532 ok
? "connected": "disconnected"));
536 * The connection is broken, we don't
537 * know if the password was changed,
538 * we hope to have more luck next time.
540 status2
= secrets_failed_password_change(dcname
,
541 NT_STATUS_NOT_COMMITTED
,
544 fn
= "secrets_failed_password_change";
547 * The server rejected the change, we don't
548 * retry and defer the change to the next
549 * "machine password timeout" interval.
551 status2
= secrets_defer_password_change(dcname
,
552 NT_STATUS_NOT_COMMITTED
,
555 fn
= "secrets_defer_password_change";
557 if (!NT_STATUS_IS_OK(status2
)) {
558 DEBUG(0, ("%s() failed for domain %s!\n",
561 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
568 DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
569 current_timestring(talloc_tos(), false),
570 __func__
, domain
, context_name
));
572 switch (sec_channel_type
) {
576 status
= secrets_finish_password_change(
577 info
->next_change
->change_server
,
578 info
->next_change
->change_time
,
585 if (!NT_STATUS_IS_OK(status
)) {
586 DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
589 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
592 DEBUG(0,("%s : %s(%s): Finished password change.\n",
593 current_timestring(talloc_tos(), false),
597 case SEC_CHAN_DNS_DOMAIN
:
598 case SEC_CHAN_DOMAIN
:
600 * we used pdb_set_trusteddom_pw().
605 smb_panic("Unsupported secure channel type");
609 ok
= cli_credentials_set_utf16_password(creds
,
613 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
616 return NT_STATUS_NO_MEMORY
;
619 current_nt_hash
= cli_credentials_get_nt_hash(creds
, frame
);
620 if (current_nt_hash
== NULL
) {
621 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
624 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
;
628 * Now we verify the new password.
631 nt_hashes
[idx
++] = current_nt_hash
;
633 status
= netlogon_creds_cli_lck_auth(context
, b
,
637 if (!NT_STATUS_IS_OK(status
)) {
638 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
639 context_name
, nt_errstr(status
)));
644 DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
645 current_timestring(talloc_tos(), false),
646 __func__
, domain
, context_name
));