ctdb-server: Clean up connection tracking functions
[samba4-gss.git] / source3 / libads / trusts_util.c
blob6f805f2365e4da8bb5b5984e1aa3e72cf01071a1
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
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/>.
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 "librpc/gen_ndr/secrets.h"
28 #include "secrets.h"
29 #include "ads.h"
30 #include "passdb.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;
44 char *g_lock_key;
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));
51 return 0;
54 char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
55 enum netr_SchannelType sec_channel_type,
56 int security)
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
65 * NetrLogonSendToSam.
67 * See https://bugzilla.samba.org/show_bug.cgi?id=14984
69 size_t min = 120;
70 size_t max = 120;
72 switch (sec_channel_type) {
73 case SEC_CHAN_WKSTA:
74 case SEC_CHAN_BDC:
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.
81 min = 14;
82 max = 14;
84 break;
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)
93 min = 120;
94 max = 120;
95 break;
96 case SEC_CHAN_DOMAIN:
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.
102 min = 14;
103 max = 14;
104 break;
105 default:
106 break;
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;
129 NTSTATUS status;
131 status = netlogon_creds_cli_lck(
132 context, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
133 talloc_tos(), &lck);
134 if (!NT_STATUS_IS_OK(status)) {
135 DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
136 nt_errstr(status));
137 return status;
140 status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes,
141 idx_nt_hashes);
142 TALLOC_FREE(lck);
144 return status;
147 NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
148 struct messaging_context *msg_ctx,
149 struct dcerpc_binding_handle *b,
150 const char *domain,
151 const char *dcname,
152 bool force)
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;
163 uint8_t idx = 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, };
172 int timeout = 0;
173 struct timeval tv = { 0, };
174 char *new_trust_pw_str = NULL;
175 size_t len = 0;
176 DATA_BLOB new_trust_pw_blob = data_blob_null;
177 uint32_t new_version = 0;
178 uint32_t *new_trust_version = NULL;
179 NTSTATUS status;
180 bool ok;
182 state = talloc_zero(frame, struct trust_pw_change_state);
183 if (state == NULL) {
184 TALLOC_FREE(frame);
185 return NT_STATUS_NO_MEMORY;
188 state->g_ctx = g_lock_ctx_init(state, msg_ctx);
189 if (state->g_ctx == NULL) {
190 TALLOC_FREE(frame);
191 return NT_STATUS_NO_MEMORY;
194 state->g_lock_key = talloc_asprintf(state,
195 "trust_password_change_%s",
196 domain);
197 if (state->g_lock_key == NULL) {
198 TALLOC_FREE(frame);
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",
208 state->g_lock_key));
209 TALLOC_FREE(frame);
210 return status;
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)));
219 TALLOC_FREE(frame);
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",
226 domain));
227 TALLOC_FREE(frame);
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) {
239 case SEC_CHAN_WKSTA:
240 case SEC_CHAN_BDC:
241 break;
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)));
248 TALLOC_FREE(frame);
249 return status;
252 new_trust_version = &new_version;
253 break;
254 default:
255 TALLOC_FREE(frame);
256 return NT_STATUS_NOT_SUPPORTED;
259 timeout = lp_machine_password_timeout();
260 if (timeout == 0) {
261 if (!force) {
262 DEBUG(10,("machine password never expires\n"));
263 TALLOC_FREE(frame);
264 return NT_STATUS_OK;
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)) {
276 TALLOC_FREE(frame);
277 return NT_STATUS_OK;
280 context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
281 if (context_name == NULL) {
282 TALLOC_FREE(frame);
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,
292 lp_security());
293 if (new_trust_pw_str == NULL) {
294 DEBUG(0, ("trust_pw_new_value() failed\n"));
295 TALLOC_FREE(frame);
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);
304 if (!ok) {
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));
312 TALLOC_FREE(frame);
313 return status;
315 talloc_keep_secret(new_trust_pw_blob.data);
317 switch (sec_channel_type) {
319 case SEC_CHAN_WKSTA:
320 case SEC_CHAN_BDC:
321 status = secrets_prepare_password_change(domain,
322 dcname,
323 new_trust_pw_str,
324 frame,
325 &info,
326 &prev,
327 #ifdef HAVE_ADS
328 sync_pw2keytabs);
329 #else
330 NULL);
331 #endif
332 if (!NT_STATUS_IS_OK(status)) {
333 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
334 domain));
335 TALLOC_FREE(frame);
336 return NT_STATUS_INTERNAL_DB_CORRUPTION;
338 TALLOC_FREE(new_trust_pw_str);
340 if (prev != NULL) {
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 "
348 "recover...\n",
349 current_timestring(talloc_tos(), false),
350 __func__, domain,
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),
357 __func__, domain,
358 nt_errstr(prev->local_status),
359 nt_errstr(prev->remote_status),
360 prev->change_server,
361 nt_time_string(talloc_tos(),
362 prev->change_time)));
365 idx_current = idx;
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;
380 break;
382 case SEC_CHAN_DNS_DOMAIN:
383 case SEC_CHAN_DOMAIN:
384 idx_current = idx;
385 nt_hashes[idx++] = current_nt_hash;
386 if (previous_nt_hash != NULL) {
387 nt_hashes[idx++] = previous_nt_hash;
389 break;
391 default:
392 smb_panic("Unsupported secure channel type");
393 break;
395 num_nt_hashes = idx;
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.
404 * TODO:
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,
411 num_nt_hashes,
412 nt_hashes,
413 &idx_nt_hashes);
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)));
417 TALLOC_FREE(frame);
418 return 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,
430 info,
431 #ifdef HAVE_ADS
432 sync_pw2keytabs);
433 #else
434 NULL);
435 #endif
436 if (!NT_STATUS_IS_OK(status)) {
437 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
438 domain));
439 TALLOC_FREE(frame);
440 return NT_STATUS_INTERNAL_DB_CORRUPTION;
443 DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
444 current_timestring(talloc_tos(), false),
445 __func__, domain));
446 TALLOC_FREE(frame);
447 return NT_STATUS_OK;
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));
456 if (info == NULL) {
457 TALLOC_FREE(frame);
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,
464 info);
465 if (!NT_STATUS_IS_OK(status)) {
466 DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
467 domain));
468 TALLOC_FREE(frame);
469 return NT_STATUS_INTERNAL_DB_CORRUPTION;
471 TALLOC_FREE(frame);
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) {
486 case SEC_CHAN_WKSTA:
487 case SEC_CHAN_BDC:
489 * we called secrets_prepare_password_change() above.
491 break;
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);
501 if (!ok) {
502 DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
503 domain));
504 TALLOC_FREE(frame);
505 return NT_STATUS_INTERNAL_DB_CORRUPTION;
507 TALLOC_FREE(new_trust_pw_str);
508 break;
510 default:
511 smb_panic("Unsupported secure channel type");
512 break;
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,
519 &new_trust_pw_blob,
520 new_trust_version);
521 if (!NT_STATUS_IS_OK(status)) {
522 NTSTATUS status2;
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 "
528 "- %s (%s)\n",
529 current_timestring(talloc_tos(), false),
530 __func__, domain, context_name,
531 nt_errstr(status),
532 ok ? "connected": "disconnected"));
534 if (!ok) {
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,
542 status,
543 info);
544 fn = "secrets_failed_password_change";
545 } else {
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,
553 status,
554 info);
555 fn = "secrets_defer_password_change";
557 if (!NT_STATUS_IS_OK(status2)) {
558 DEBUG(0, ("%s() failed for domain %s!\n",
559 fn, domain));
560 TALLOC_FREE(frame);
561 return NT_STATUS_INTERNAL_DB_CORRUPTION;
564 TALLOC_FREE(frame);
565 return status;
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) {
574 case SEC_CHAN_WKSTA:
575 case SEC_CHAN_BDC:
576 status = secrets_finish_password_change(
577 info->next_change->change_server,
578 info->next_change->change_time,
579 info,
580 #ifdef HAVE_ADS
581 sync_pw2keytabs);
582 #else
583 NULL);
584 #endif
585 if (!NT_STATUS_IS_OK(status)) {
586 DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
587 domain));
588 TALLOC_FREE(frame);
589 return NT_STATUS_INTERNAL_DB_CORRUPTION;
592 DEBUG(0,("%s : %s(%s): Finished password change.\n",
593 current_timestring(talloc_tos(), false),
594 __func__, domain));
595 break;
597 case SEC_CHAN_DNS_DOMAIN:
598 case SEC_CHAN_DOMAIN:
600 * we used pdb_set_trusteddom_pw().
602 break;
604 default:
605 smb_panic("Unsupported secure channel type");
606 break;
609 ok = cli_credentials_set_utf16_password(creds,
610 &new_trust_pw_blob,
611 CRED_SPECIFIED);
612 if (!ok) {
613 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
614 domain));
615 TALLOC_FREE(frame);
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",
622 domain));
623 TALLOC_FREE(frame);
624 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
628 * Now we verify the new password.
630 idx = 0;
631 nt_hashes[idx++] = current_nt_hash;
632 num_nt_hashes = idx;
633 status = netlogon_creds_cli_lck_auth(context, b,
634 num_nt_hashes,
635 nt_hashes,
636 &idx_nt_hashes);
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)));
640 TALLOC_FREE(frame);
641 return 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));
648 TALLOC_FREE(frame);
649 return NT_STATUS_OK;