WHATSNEW: Fix typo.
[Samba.git] / source3 / libsmb / trusts_util.c
blob57cd542e08aa43052c09bf82d259972d76de8208
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"
34 /*********************************************************
35 Change the domain password on the PDC.
36 Do most of the legwork ourselfs. Caller must have
37 already setup the connection to the NETLOGON pipe
38 **********************************************************/
40 struct trust_pw_change_state {
41 struct g_lock_ctx *g_ctx;
42 char *g_lock_key;
45 static int trust_pw_change_state_destructor(struct trust_pw_change_state *state)
47 g_lock_unlock(state->g_ctx, state->g_lock_key);
48 return 0;
51 char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
52 enum netr_SchannelType sec_channel_type,
53 int security)
56 * use secure defaults.
58 size_t min = 128;
59 size_t max = 255;
61 switch (sec_channel_type) {
62 case SEC_CHAN_WKSTA:
63 case SEC_CHAN_BDC:
64 if (security == SEC_DOMAIN) {
66 * The maximum length of a trust account password.
67 * Used when we randomly create it, 15 char passwords
68 * exceed NT4's max password length.
70 min = 14;
71 max = 14;
73 break;
74 case SEC_CHAN_DNS_DOMAIN:
76 * new_len * 2 = 498 bytes is the largest possible length
77 * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
78 * and a confounder with at least 2 bytes is required.
80 * Windows uses new_len = 120 => 240 bytes (utf16)
82 min = 120;
83 max = 120;
84 break;
85 /* fall through */
86 case SEC_CHAN_DOMAIN:
88 * The maximum length of a trust account password.
89 * Used when we randomly create it, 15 char passwords
90 * exceed NT4's max password length.
92 min = 14;
93 max = 14;
94 break;
95 default:
96 break;
100 * Create a random machine account password
101 * We create a random buffer and convert that to utf8.
102 * This is similar to what windows is doing.
104 return generate_random_machine_password(mem_ctx, min, max);
107 NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
108 struct messaging_context *msg_ctx,
109 struct dcerpc_binding_handle *b,
110 const char *domain,
111 const char *dcname,
112 bool force)
114 TALLOC_CTX *frame = talloc_stackframe();
115 const char *context_name = NULL;
116 struct trust_pw_change_state *state;
117 struct cli_credentials *creds = NULL;
118 struct secrets_domain_info1 *info = NULL;
119 struct secrets_domain_info1_change *prev = NULL;
120 const struct samr_Password *current_nt_hash = NULL;
121 const struct samr_Password *previous_nt_hash = NULL;
122 uint8_t num_nt_hashes = 0;
123 uint8_t idx = 0;
124 const struct samr_Password *nt_hashes[1+3] = { NULL, };
125 uint8_t idx_nt_hashes = 0;
126 uint8_t idx_current = UINT8_MAX;
127 enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
128 time_t pass_last_set_time;
129 uint32_t old_version = 0;
130 struct pdb_trusted_domain *td = NULL;
131 struct timeval g_timeout = { 0, };
132 int timeout = 0;
133 struct timeval tv = { 0, };
134 char *new_trust_pw_str = NULL;
135 size_t len = 0;
136 DATA_BLOB new_trust_pw_blob = data_blob_null;
137 uint32_t new_version = 0;
138 uint32_t *new_trust_version = NULL;
139 NTSTATUS status;
140 bool ok;
142 state = talloc_zero(frame, struct trust_pw_change_state);
143 if (state == NULL) {
144 TALLOC_FREE(frame);
145 return NT_STATUS_NO_MEMORY;
148 state->g_ctx = g_lock_ctx_init(state, msg_ctx);
149 if (state->g_ctx == NULL) {
150 TALLOC_FREE(frame);
151 return NT_STATUS_NO_MEMORY;
154 state->g_lock_key = talloc_asprintf(state,
155 "trust_password_change_%s",
156 domain);
157 if (state->g_lock_key == NULL) {
158 TALLOC_FREE(frame);
159 return NT_STATUS_NO_MEMORY;
162 g_timeout = timeval_current_ofs(10, 0);
163 status = g_lock_lock(state->g_ctx,
164 state->g_lock_key,
165 G_LOCK_WRITE, g_timeout);
166 if (!NT_STATUS_IS_OK(status)) {
167 DEBUG(1, ("could not get g_lock on [%s]!\n",
168 state->g_lock_key));
169 TALLOC_FREE(frame);
170 return status;
173 talloc_set_destructor(state, trust_pw_change_state_destructor);
175 status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
176 if (!NT_STATUS_IS_OK(status)) {
177 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
178 domain, nt_errstr(status)));
179 TALLOC_FREE(frame);
180 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
183 current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
184 if (current_nt_hash == NULL) {
185 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
186 domain));
187 TALLOC_FREE(frame);
188 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
190 previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
192 old_version = cli_credentials_get_kvno(creds);
193 pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
194 sec_channel_type = cli_credentials_get_secure_channel_type(creds);
196 new_version = old_version + 1;
198 switch (sec_channel_type) {
199 case SEC_CHAN_WKSTA:
200 case SEC_CHAN_BDC:
201 break;
202 case SEC_CHAN_DNS_DOMAIN:
203 case SEC_CHAN_DOMAIN:
204 status = pdb_get_trusted_domain(frame, domain, &td);
205 if (!NT_STATUS_IS_OK(status)) {
206 DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
207 domain, nt_errstr(status)));
208 TALLOC_FREE(frame);
209 return status;
212 new_trust_version = &new_version;
213 break;
214 default:
215 TALLOC_FREE(frame);
216 return NT_STATUS_NOT_SUPPORTED;
219 timeout = lp_machine_password_timeout();
220 if (timeout == 0) {
221 if (!force) {
222 DEBUG(10,("machine password never expires\n"));
223 TALLOC_FREE(frame);
224 return NT_STATUS_OK;
228 tv.tv_sec = pass_last_set_time;
229 DEBUG(10, ("password last changed %s\n",
230 timeval_string(talloc_tos(), &tv, false)));
231 tv.tv_sec += timeout;
232 DEBUGADD(10, ("password valid until %s\n",
233 timeval_string(talloc_tos(), &tv, false)));
235 if (!force && !timeval_expired(&tv)) {
236 TALLOC_FREE(frame);
237 return NT_STATUS_OK;
240 context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
241 if (context_name == NULL) {
242 TALLOC_FREE(frame);
243 return NT_STATUS_NO_MEMORY;
247 * Create a random machine account password
248 * We create a random buffer and convert that to utf8.
249 * This is similar to what windows is doing.
251 new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
252 lp_security());
253 if (new_trust_pw_str == NULL) {
254 DEBUG(0, ("trust_pw_new_value() failed\n"));
255 TALLOC_FREE(frame);
256 return NT_STATUS_NO_MEMORY;
259 len = strlen(new_trust_pw_str);
260 ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16,
261 new_trust_pw_str, len,
262 (void **)&new_trust_pw_blob.data,
263 &new_trust_pw_blob.length);
264 if (!ok) {
265 status = NT_STATUS_UNMAPPABLE_CHARACTER;
266 if (errno == ENOMEM) {
267 status = NT_STATUS_NO_MEMORY;
269 DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
270 "failed for of %s - %s\n",
271 domain, nt_errstr(status));
272 TALLOC_FREE(frame);
273 return status;
276 switch (sec_channel_type) {
278 case SEC_CHAN_WKSTA:
279 case SEC_CHAN_BDC:
280 status = secrets_prepare_password_change(domain, dcname,
281 new_trust_pw_str,
282 frame, &info, &prev);
283 if (!NT_STATUS_IS_OK(status)) {
284 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
285 domain));
286 TALLOC_FREE(frame);
287 return NT_STATUS_INTERNAL_DB_CORRUPTION;
289 TALLOC_FREE(new_trust_pw_str);
291 if (prev != NULL) {
293 * We had a failure before we changed the password.
295 nt_hashes[idx++] = &prev->password->nt_hash;
297 DEBUG(0,("%s : %s(%s): A password change was already "
298 "started against '%s' at %s. Trying to "
299 "recover...\n",
300 current_timestring(talloc_tos(), false),
301 __func__, domain,
302 prev->password->change_server,
303 nt_time_string(talloc_tos(),
304 prev->password->change_time)));
305 DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] "
306 "against '%s' at %s.\n",
307 current_timestring(talloc_tos(), false),
308 __func__, domain,
309 nt_errstr(prev->local_status),
310 nt_errstr(prev->remote_status),
311 prev->change_server,
312 nt_time_string(talloc_tos(),
313 prev->change_time)));
316 idx_current = idx;
317 nt_hashes[idx++] = &info->password->nt_hash;
318 if (info->old_password != NULL) {
319 nt_hashes[idx++] = &info->old_password->nt_hash;
321 if (info->older_password != NULL) {
322 nt_hashes[idx++] = &info->older_password->nt_hash;
326 * We use the password that's already persitent in
327 * our database in order to handle failures.
329 data_blob_clear_free(&new_trust_pw_blob);
330 new_trust_pw_blob = info->next_change->password->cleartext_blob;
331 break;
333 case SEC_CHAN_DNS_DOMAIN:
334 case SEC_CHAN_DOMAIN:
335 idx_current = idx;
336 nt_hashes[idx++] = current_nt_hash;
337 if (previous_nt_hash != NULL) {
338 nt_hashes[idx++] = previous_nt_hash;
340 break;
342 default:
343 smb_panic("Unsupported secure channel type");
344 break;
346 num_nt_hashes = idx;
348 DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
349 current_timestring(talloc_tos(), false),
350 __func__, domain, context_name));
353 * Check which password the dc knows about.
355 * TODO:
356 * If the previous password is the only password in common with the dc,
357 * we better skip the password change, or use something like
358 * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
359 * local secrets before doing the change.
361 status = netlogon_creds_cli_auth(context, b,
362 num_nt_hashes,
363 nt_hashes,
364 &idx_nt_hashes);
365 if (!NT_STATUS_IS_OK(status)) {
366 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
367 context_name, num_nt_hashes, nt_errstr(status)));
368 TALLOC_FREE(frame);
369 return status;
372 if (prev != NULL && idx_nt_hashes == 0) {
373 DEBUG(0,("%s : %s(%s): Verified new password remotely "
374 "without changing %s\n",
375 current_timestring(talloc_tos(), false),
376 __func__, domain, context_name));
378 status = secrets_finish_password_change(prev->password->change_server,
379 prev->password->change_time,
380 info);
381 if (!NT_STATUS_IS_OK(status)) {
382 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
383 domain));
384 TALLOC_FREE(frame);
385 return NT_STATUS_INTERNAL_DB_CORRUPTION;
388 DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
389 current_timestring(talloc_tos(), false),
390 __func__, domain));
391 TALLOC_FREE(frame);
392 return NT_STATUS_OK;
395 if (idx_nt_hashes != idx_current) {
396 DEBUG(0,("%s : %s(%s): Verified older password remotely "
397 "skip changing %s\n",
398 current_timestring(talloc_tos(), false),
399 __func__, domain, context_name));
401 if (info == NULL) {
402 TALLOC_FREE(frame);
403 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
406 status = secrets_defer_password_change(dcname,
407 NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE,
408 NT_STATUS_NOT_COMMITTED,
409 info);
410 if (!NT_STATUS_IS_OK(status)) {
411 DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
412 domain));
413 TALLOC_FREE(frame);
414 return NT_STATUS_INTERNAL_DB_CORRUPTION;
416 TALLOC_FREE(frame);
417 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
420 DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
421 current_timestring(talloc_tos(), false),
422 __func__, domain, context_name));
425 * Return the result of trying to write the new password
426 * back into the trust account file.
429 switch (sec_channel_type) {
431 case SEC_CHAN_WKSTA:
432 case SEC_CHAN_BDC:
434 * we called secrets_prepare_password_change() above.
436 break;
438 case SEC_CHAN_DNS_DOMAIN:
439 case SEC_CHAN_DOMAIN:
441 * we need to get the sid first for the
442 * pdb_set_trusteddom_pw call
444 ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str,
445 &td->security_identifier);
446 if (!ok) {
447 DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
448 domain));
449 TALLOC_FREE(frame);
450 return NT_STATUS_INTERNAL_DB_CORRUPTION;
452 TALLOC_FREE(new_trust_pw_str);
453 break;
455 default:
456 smb_panic("Unsupported secure channel type");
457 break;
460 DEBUG(0,("%s : %s(%s): Changed password locally\n",
461 current_timestring(talloc_tos(), false), __func__, domain));
463 status = netlogon_creds_cli_ServerPasswordSet(context, b,
464 &new_trust_pw_blob,
465 new_trust_version);
466 if (!NT_STATUS_IS_OK(status)) {
467 NTSTATUS status2;
468 const char *fn = NULL;
470 ok = dcerpc_binding_handle_is_connected(b);
472 DEBUG(0,("%s : %s(%s) remote password change with %s failed "
473 "- %s (%s)\n",
474 current_timestring(talloc_tos(), false),
475 __func__, domain, context_name,
476 nt_errstr(status),
477 ok ? "connected": "disconnected"));
479 if (!ok) {
481 * The connection is broken, we don't
482 * know if the password was changed,
483 * we hope to have more luck next time.
485 status2 = secrets_failed_password_change(dcname,
486 NT_STATUS_NOT_COMMITTED,
487 status,
488 info);
489 fn = "secrets_failed_password_change";
490 } else {
492 * The server rejected the change, we don't
493 * retry and defer the change to the next
494 * "machine password timeout" interval.
496 status2 = secrets_defer_password_change(dcname,
497 NT_STATUS_NOT_COMMITTED,
498 status,
499 info);
500 fn = "secrets_defer_password_change";
502 if (!NT_STATUS_IS_OK(status2)) {
503 DEBUG(0, ("%s() failed for domain %s!\n",
504 fn, domain));
505 TALLOC_FREE(frame);
506 return NT_STATUS_INTERNAL_DB_CORRUPTION;
509 TALLOC_FREE(frame);
510 return status;
513 DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
514 current_timestring(talloc_tos(), false),
515 __func__, domain, context_name));
517 switch (sec_channel_type) {
519 case SEC_CHAN_WKSTA:
520 case SEC_CHAN_BDC:
521 status = secrets_finish_password_change(
522 info->next_change->change_server,
523 info->next_change->change_time,
524 info);
525 if (!NT_STATUS_IS_OK(status)) {
526 DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
527 domain));
528 TALLOC_FREE(frame);
529 return NT_STATUS_INTERNAL_DB_CORRUPTION;
532 DEBUG(0,("%s : %s(%s): Finished password change.\n",
533 current_timestring(talloc_tos(), false),
534 __func__, domain));
535 break;
537 case SEC_CHAN_DNS_DOMAIN:
538 case SEC_CHAN_DOMAIN:
540 * we used pdb_set_trusteddom_pw().
542 break;
544 default:
545 smb_panic("Unsupported secure channel type");
546 break;
549 ok = cli_credentials_set_utf16_password(creds,
550 &new_trust_pw_blob,
551 CRED_SPECIFIED);
552 if (!ok) {
553 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
554 domain));
555 TALLOC_FREE(frame);
556 return NT_STATUS_NO_MEMORY;
559 current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
560 if (current_nt_hash == NULL) {
561 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
562 domain));
563 TALLOC_FREE(frame);
564 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
568 * Now we verify the new password.
570 idx = 0;
571 idx_current = idx;
572 nt_hashes[idx++] = current_nt_hash;
573 num_nt_hashes = idx;
574 status = netlogon_creds_cli_auth(context, b,
575 num_nt_hashes,
576 nt_hashes,
577 &idx_nt_hashes);
578 if (!NT_STATUS_IS_OK(status)) {
579 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
580 context_name, nt_errstr(status)));
581 TALLOC_FREE(frame);
582 return status;
585 DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
586 current_timestring(talloc_tos(), false),
587 __func__, domain, context_name));
589 TALLOC_FREE(frame);
590 return NT_STATUS_OK;