s3:idmap_ad: add support for ADS_AUTH_SASL_{STARTTLS,LDAPS}
[Samba.git] / source3 / libsmb / trusts_util.c
blobc40eb7918025b503a9fd98224b4a7c5cd82d004a
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 "passdb.h"
30 #include "libsmb/libsmb.h"
31 #include "source3/include/messages.h"
32 #include "source3/include/g_lock.h"
33 #include "lib/util/util_tdb.h"
35 /*********************************************************
36 Change the domain password on the PDC.
37 Do most of the legwork ourselves. Caller must have
38 already setup the connection to the NETLOGON pipe
39 **********************************************************/
41 struct trust_pw_change_state {
42 struct g_lock_ctx *g_ctx;
43 char *g_lock_key;
46 static int trust_pw_change_state_destructor(struct trust_pw_change_state *state)
48 g_lock_unlock(state->g_ctx,
49 string_term_tdb_data(state->g_lock_key));
50 return 0;
53 char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
54 enum netr_SchannelType sec_channel_type,
55 int security)
58 * use secure defaults, which match
59 * what windows uses for computer passwords.
61 * We used to have min=128 and max=255 here, but
62 * it's a bad idea because of bugs in the Windows
63 * RODC/RWDC PasswordUpdateForward handling via
64 * NetrLogonSendToSam.
66 * See https://bugzilla.samba.org/show_bug.cgi?id=14984
68 size_t min = 120;
69 size_t max = 120;
71 switch (sec_channel_type) {
72 case SEC_CHAN_WKSTA:
73 case SEC_CHAN_BDC:
74 if (security == SEC_DOMAIN) {
76 * The maximum length of a trust account password.
77 * Used when we randomly create it, 15 char passwords
78 * exceed NT4's max password length.
80 min = 14;
81 max = 14;
83 break;
84 case SEC_CHAN_DNS_DOMAIN:
86 * new_len * 2 = 498 bytes is the largest possible length
87 * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
88 * and a confounder with at least 2 bytes is required.
90 * Windows uses new_len = 120 => 240 bytes (utf16)
92 min = 120;
93 max = 120;
94 break;
95 case SEC_CHAN_DOMAIN:
97 * The maximum length of a trust account password.
98 * Used when we randomly create it, 15 char passwords
99 * exceed NT4's max password length.
101 min = 14;
102 max = 14;
103 break;
104 default:
105 break;
109 * Create a random machine account password
110 * We create a random buffer and convert that to utf8.
111 * This is similar to what windows is doing.
113 return generate_random_machine_password(mem_ctx, min, max);
117 * Temporary function to wrap cli_auth in a lck
120 static NTSTATUS netlogon_creds_cli_lck_auth(
121 struct netlogon_creds_cli_context *context,
122 struct dcerpc_binding_handle *b,
123 uint8_t num_nt_hashes,
124 const struct samr_Password * const *nt_hashes,
125 uint8_t *idx_nt_hashes)
127 struct netlogon_creds_cli_lck *lck;
128 NTSTATUS status;
130 status = netlogon_creds_cli_lck(
131 context, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
132 talloc_tos(), &lck);
133 if (!NT_STATUS_IS_OK(status)) {
134 DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
135 nt_errstr(status));
136 return status;
139 status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes,
140 idx_nt_hashes);
141 TALLOC_FREE(lck);
143 return status;
146 NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
147 struct messaging_context *msg_ctx,
148 struct dcerpc_binding_handle *b,
149 const char *domain,
150 const char *dcname,
151 bool force)
153 TALLOC_CTX *frame = talloc_stackframe();
154 const char *context_name = NULL;
155 struct trust_pw_change_state *state;
156 struct cli_credentials *creds = NULL;
157 struct secrets_domain_info1 *info = NULL;
158 struct secrets_domain_info1_change *prev = NULL;
159 const struct samr_Password *current_nt_hash = NULL;
160 const struct samr_Password *previous_nt_hash = NULL;
161 uint8_t num_nt_hashes = 0;
162 uint8_t idx = 0;
163 const struct samr_Password *nt_hashes[1+3] = { NULL, };
164 uint8_t idx_nt_hashes = 0;
165 uint8_t idx_current = UINT8_MAX;
166 enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
167 time_t pass_last_set_time;
168 uint32_t old_version = 0;
169 struct pdb_trusted_domain *td = NULL;
170 struct timeval g_timeout = { 0, };
171 int timeout = 0;
172 struct timeval tv = { 0, };
173 char *new_trust_pw_str = NULL;
174 size_t len = 0;
175 DATA_BLOB new_trust_pw_blob = data_blob_null;
176 uint32_t new_version = 0;
177 uint32_t *new_trust_version = NULL;
178 NTSTATUS status;
179 bool ok;
181 state = talloc_zero(frame, struct trust_pw_change_state);
182 if (state == NULL) {
183 TALLOC_FREE(frame);
184 return NT_STATUS_NO_MEMORY;
187 state->g_ctx = g_lock_ctx_init(state, msg_ctx);
188 if (state->g_ctx == NULL) {
189 TALLOC_FREE(frame);
190 return NT_STATUS_NO_MEMORY;
193 state->g_lock_key = talloc_asprintf(state,
194 "trust_password_change_%s",
195 domain);
196 if (state->g_lock_key == NULL) {
197 TALLOC_FREE(frame);
198 return NT_STATUS_NO_MEMORY;
201 g_timeout = timeval_current_ofs(10, 0);
202 status = g_lock_lock(state->g_ctx,
203 string_term_tdb_data(state->g_lock_key),
204 G_LOCK_WRITE, g_timeout, NULL, NULL);
205 if (!NT_STATUS_IS_OK(status)) {
206 DEBUG(1, ("could not get g_lock on [%s]!\n",
207 state->g_lock_key));
208 TALLOC_FREE(frame);
209 return status;
212 talloc_set_destructor(state, trust_pw_change_state_destructor);
214 status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
215 if (!NT_STATUS_IS_OK(status)) {
216 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
217 domain, nt_errstr(status)));
218 TALLOC_FREE(frame);
219 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
222 current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
223 if (current_nt_hash == NULL) {
224 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
225 domain));
226 TALLOC_FREE(frame);
227 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
229 previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
231 old_version = cli_credentials_get_kvno(creds);
232 pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
233 sec_channel_type = cli_credentials_get_secure_channel_type(creds);
235 new_version = old_version + 1;
237 switch (sec_channel_type) {
238 case SEC_CHAN_WKSTA:
239 case SEC_CHAN_BDC:
240 break;
241 case SEC_CHAN_DNS_DOMAIN:
242 case SEC_CHAN_DOMAIN:
243 status = pdb_get_trusted_domain(frame, domain, &td);
244 if (!NT_STATUS_IS_OK(status)) {
245 DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
246 domain, nt_errstr(status)));
247 TALLOC_FREE(frame);
248 return status;
251 new_trust_version = &new_version;
252 break;
253 default:
254 TALLOC_FREE(frame);
255 return NT_STATUS_NOT_SUPPORTED;
258 timeout = lp_machine_password_timeout();
259 if (timeout == 0) {
260 if (!force) {
261 DEBUG(10,("machine password never expires\n"));
262 TALLOC_FREE(frame);
263 return NT_STATUS_OK;
267 tv.tv_sec = pass_last_set_time;
268 DEBUG(10, ("password last changed %s\n",
269 timeval_string(talloc_tos(), &tv, false)));
270 tv.tv_sec += timeout;
271 DEBUGADD(10, ("password valid until %s\n",
272 timeval_string(talloc_tos(), &tv, false)));
274 if (!force && !timeval_expired(&tv)) {
275 TALLOC_FREE(frame);
276 return NT_STATUS_OK;
279 context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
280 if (context_name == NULL) {
281 TALLOC_FREE(frame);
282 return NT_STATUS_NO_MEMORY;
286 * Create a random machine account password
287 * We create a random buffer and convert that to utf8.
288 * This is similar to what windows is doing.
290 new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
291 lp_security());
292 if (new_trust_pw_str == NULL) {
293 DEBUG(0, ("trust_pw_new_value() failed\n"));
294 TALLOC_FREE(frame);
295 return NT_STATUS_NO_MEMORY;
298 len = strlen(new_trust_pw_str);
299 ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16,
300 new_trust_pw_str, len,
301 (void **)&new_trust_pw_blob.data,
302 &new_trust_pw_blob.length);
303 if (!ok) {
304 status = NT_STATUS_UNMAPPABLE_CHARACTER;
305 if (errno == ENOMEM) {
306 status = NT_STATUS_NO_MEMORY;
308 DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
309 "failed for of %s - %s\n",
310 domain, nt_errstr(status));
311 TALLOC_FREE(frame);
312 return status;
314 talloc_keep_secret(new_trust_pw_blob.data);
316 switch (sec_channel_type) {
318 case SEC_CHAN_WKSTA:
319 case SEC_CHAN_BDC:
320 status = secrets_prepare_password_change(domain, dcname,
321 new_trust_pw_str,
322 frame, &info, &prev);
323 if (!NT_STATUS_IS_OK(status)) {
324 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
325 domain));
326 TALLOC_FREE(frame);
327 return NT_STATUS_INTERNAL_DB_CORRUPTION;
329 TALLOC_FREE(new_trust_pw_str);
331 if (prev != NULL) {
333 * We had a failure before we changed the password.
335 nt_hashes[idx++] = &prev->password->nt_hash;
337 DEBUG(0,("%s : %s(%s): A password change was already "
338 "started against '%s' at %s. Trying to "
339 "recover...\n",
340 current_timestring(talloc_tos(), false),
341 __func__, domain,
342 prev->password->change_server,
343 nt_time_string(talloc_tos(),
344 prev->password->change_time)));
345 DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] "
346 "against '%s' at %s.\n",
347 current_timestring(talloc_tos(), false),
348 __func__, domain,
349 nt_errstr(prev->local_status),
350 nt_errstr(prev->remote_status),
351 prev->change_server,
352 nt_time_string(talloc_tos(),
353 prev->change_time)));
356 idx_current = idx;
357 nt_hashes[idx++] = &info->password->nt_hash;
358 if (info->old_password != NULL) {
359 nt_hashes[idx++] = &info->old_password->nt_hash;
361 if (info->older_password != NULL) {
362 nt_hashes[idx++] = &info->older_password->nt_hash;
366 * We use the password that's already persistent in
367 * our database in order to handle failures.
369 data_blob_free(&new_trust_pw_blob);
370 new_trust_pw_blob = info->next_change->password->cleartext_blob;
371 break;
373 case SEC_CHAN_DNS_DOMAIN:
374 case SEC_CHAN_DOMAIN:
375 idx_current = idx;
376 nt_hashes[idx++] = current_nt_hash;
377 if (previous_nt_hash != NULL) {
378 nt_hashes[idx++] = previous_nt_hash;
380 break;
382 default:
383 smb_panic("Unsupported secure channel type");
384 break;
386 num_nt_hashes = idx;
388 DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
389 current_timestring(talloc_tos(), false),
390 __func__, domain, context_name));
393 * Check which password the dc knows about.
395 * TODO:
396 * If the previous password is the only password in common with the dc,
397 * we better skip the password change, or use something like
398 * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
399 * local secrets before doing the change.
401 status = netlogon_creds_cli_lck_auth(context, b,
402 num_nt_hashes,
403 nt_hashes,
404 &idx_nt_hashes);
405 if (!NT_STATUS_IS_OK(status)) {
406 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
407 context_name, num_nt_hashes, nt_errstr(status)));
408 TALLOC_FREE(frame);
409 return status;
412 if (prev != NULL && idx_nt_hashes == 0) {
413 DEBUG(0,("%s : %s(%s): Verified new password remotely "
414 "without changing %s\n",
415 current_timestring(talloc_tos(), false),
416 __func__, domain, context_name));
418 status = secrets_finish_password_change(prev->password->change_server,
419 prev->password->change_time,
420 info);
421 if (!NT_STATUS_IS_OK(status)) {
422 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
423 domain));
424 TALLOC_FREE(frame);
425 return NT_STATUS_INTERNAL_DB_CORRUPTION;
428 DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
429 current_timestring(talloc_tos(), false),
430 __func__, domain));
431 TALLOC_FREE(frame);
432 return NT_STATUS_OK;
435 if (idx_nt_hashes != idx_current) {
436 DEBUG(0,("%s : %s(%s): Verified older password remotely "
437 "skip changing %s\n",
438 current_timestring(talloc_tos(), false),
439 __func__, domain, context_name));
441 if (info == NULL) {
442 TALLOC_FREE(frame);
443 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
446 status = secrets_defer_password_change(dcname,
447 NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE,
448 NT_STATUS_NOT_COMMITTED,
449 info);
450 if (!NT_STATUS_IS_OK(status)) {
451 DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
452 domain));
453 TALLOC_FREE(frame);
454 return NT_STATUS_INTERNAL_DB_CORRUPTION;
456 TALLOC_FREE(frame);
457 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
460 DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
461 current_timestring(talloc_tos(), false),
462 __func__, domain, context_name));
465 * Return the result of trying to write the new password
466 * back into the trust account file.
469 switch (sec_channel_type) {
471 case SEC_CHAN_WKSTA:
472 case SEC_CHAN_BDC:
474 * we called secrets_prepare_password_change() above.
476 break;
478 case SEC_CHAN_DNS_DOMAIN:
479 case SEC_CHAN_DOMAIN:
481 * we need to get the sid first for the
482 * pdb_set_trusteddom_pw call
484 ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str,
485 &td->security_identifier);
486 if (!ok) {
487 DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
488 domain));
489 TALLOC_FREE(frame);
490 return NT_STATUS_INTERNAL_DB_CORRUPTION;
492 TALLOC_FREE(new_trust_pw_str);
493 break;
495 default:
496 smb_panic("Unsupported secure channel type");
497 break;
500 DEBUG(0,("%s : %s(%s): Changed password locally\n",
501 current_timestring(talloc_tos(), false), __func__, domain));
503 status = netlogon_creds_cli_ServerPasswordSet(context, b,
504 &new_trust_pw_blob,
505 new_trust_version);
506 if (!NT_STATUS_IS_OK(status)) {
507 NTSTATUS status2;
508 const char *fn = NULL;
510 ok = dcerpc_binding_handle_is_connected(b);
512 DEBUG(0,("%s : %s(%s) remote password change with %s failed "
513 "- %s (%s)\n",
514 current_timestring(talloc_tos(), false),
515 __func__, domain, context_name,
516 nt_errstr(status),
517 ok ? "connected": "disconnected"));
519 if (!ok) {
521 * The connection is broken, we don't
522 * know if the password was changed,
523 * we hope to have more luck next time.
525 status2 = secrets_failed_password_change(dcname,
526 NT_STATUS_NOT_COMMITTED,
527 status,
528 info);
529 fn = "secrets_failed_password_change";
530 } else {
532 * The server rejected the change, we don't
533 * retry and defer the change to the next
534 * "machine password timeout" interval.
536 status2 = secrets_defer_password_change(dcname,
537 NT_STATUS_NOT_COMMITTED,
538 status,
539 info);
540 fn = "secrets_defer_password_change";
542 if (!NT_STATUS_IS_OK(status2)) {
543 DEBUG(0, ("%s() failed for domain %s!\n",
544 fn, domain));
545 TALLOC_FREE(frame);
546 return NT_STATUS_INTERNAL_DB_CORRUPTION;
549 TALLOC_FREE(frame);
550 return status;
553 DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
554 current_timestring(talloc_tos(), false),
555 __func__, domain, context_name));
557 switch (sec_channel_type) {
559 case SEC_CHAN_WKSTA:
560 case SEC_CHAN_BDC:
561 status = secrets_finish_password_change(
562 info->next_change->change_server,
563 info->next_change->change_time,
564 info);
565 if (!NT_STATUS_IS_OK(status)) {
566 DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
567 domain));
568 TALLOC_FREE(frame);
569 return NT_STATUS_INTERNAL_DB_CORRUPTION;
572 DEBUG(0,("%s : %s(%s): Finished password change.\n",
573 current_timestring(talloc_tos(), false),
574 __func__, domain));
575 break;
577 case SEC_CHAN_DNS_DOMAIN:
578 case SEC_CHAN_DOMAIN:
580 * we used pdb_set_trusteddom_pw().
582 break;
584 default:
585 smb_panic("Unsupported secure channel type");
586 break;
589 ok = cli_credentials_set_utf16_password(creds,
590 &new_trust_pw_blob,
591 CRED_SPECIFIED);
592 if (!ok) {
593 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
594 domain));
595 TALLOC_FREE(frame);
596 return NT_STATUS_NO_MEMORY;
599 current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
600 if (current_nt_hash == NULL) {
601 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
602 domain));
603 TALLOC_FREE(frame);
604 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
608 * Now we verify the new password.
610 idx = 0;
611 nt_hashes[idx++] = current_nt_hash;
612 num_nt_hashes = idx;
613 status = netlogon_creds_cli_lck_auth(context, b,
614 num_nt_hashes,
615 nt_hashes,
616 &idx_nt_hashes);
617 if (!NT_STATUS_IS_OK(status)) {
618 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
619 context_name, nt_errstr(status)));
620 TALLOC_FREE(frame);
621 return status;
624 DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
625 current_timestring(talloc_tos(), false),
626 __func__, domain, context_name));
628 TALLOC_FREE(frame);
629 return NT_STATUS_OK;