ldb_kv: Use ldb_msg_add_steal_value() in msg_add_distinguished_name()
[Samba.git] / source3 / libsmb / trusts_util.c
blob3ebf67f231e52fba773ad03ec632d2e9ee5c80d0
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 ourselfs. 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.
60 size_t min = 128;
61 size_t max = 255;
63 switch (sec_channel_type) {
64 case SEC_CHAN_WKSTA:
65 case SEC_CHAN_BDC:
66 if (security == SEC_DOMAIN) {
68 * The maximum length of a trust account password.
69 * Used when we randomly create it, 15 char passwords
70 * exceed NT4's max password length.
72 min = 14;
73 max = 14;
75 break;
76 case SEC_CHAN_DNS_DOMAIN:
78 * new_len * 2 = 498 bytes is the largest possible length
79 * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
80 * and a confounder with at least 2 bytes is required.
82 * Windows uses new_len = 120 => 240 bytes (utf16)
84 min = 120;
85 max = 120;
86 break;
87 case SEC_CHAN_DOMAIN:
89 * The maximum length of a trust account password.
90 * Used when we randomly create it, 15 char passwords
91 * exceed NT4's max password length.
93 min = 14;
94 max = 14;
95 break;
96 default:
97 break;
101 * Create a random machine account password
102 * We create a random buffer and convert that to utf8.
103 * This is similar to what windows is doing.
105 return generate_random_machine_password(mem_ctx, min, max);
109 * Temporary function to wrap cli_auth in a lck
112 static NTSTATUS netlogon_creds_cli_lck_auth(
113 struct netlogon_creds_cli_context *context,
114 struct dcerpc_binding_handle *b,
115 uint8_t num_nt_hashes,
116 const struct samr_Password * const *nt_hashes,
117 uint8_t *idx_nt_hashes)
119 struct netlogon_creds_cli_lck *lck;
120 NTSTATUS status;
122 status = netlogon_creds_cli_lck(
123 context, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
124 talloc_tos(), &lck);
125 if (!NT_STATUS_IS_OK(status)) {
126 DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
127 nt_errstr(status));
128 return status;
131 status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes,
132 idx_nt_hashes);
133 TALLOC_FREE(lck);
135 return status;
138 NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
139 struct messaging_context *msg_ctx,
140 struct dcerpc_binding_handle *b,
141 const char *domain,
142 const char *dcname,
143 bool force)
145 TALLOC_CTX *frame = talloc_stackframe();
146 const char *context_name = NULL;
147 struct trust_pw_change_state *state;
148 struct cli_credentials *creds = NULL;
149 struct secrets_domain_info1 *info = NULL;
150 struct secrets_domain_info1_change *prev = NULL;
151 const struct samr_Password *current_nt_hash = NULL;
152 const struct samr_Password *previous_nt_hash = NULL;
153 uint8_t num_nt_hashes = 0;
154 uint8_t idx = 0;
155 const struct samr_Password *nt_hashes[1+3] = { NULL, };
156 uint8_t idx_nt_hashes = 0;
157 uint8_t idx_current = UINT8_MAX;
158 enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
159 time_t pass_last_set_time;
160 uint32_t old_version = 0;
161 struct pdb_trusted_domain *td = NULL;
162 struct timeval g_timeout = { 0, };
163 int timeout = 0;
164 struct timeval tv = { 0, };
165 char *new_trust_pw_str = NULL;
166 size_t len = 0;
167 DATA_BLOB new_trust_pw_blob = data_blob_null;
168 uint32_t new_version = 0;
169 uint32_t *new_trust_version = NULL;
170 NTSTATUS status;
171 bool ok;
173 state = talloc_zero(frame, struct trust_pw_change_state);
174 if (state == NULL) {
175 TALLOC_FREE(frame);
176 return NT_STATUS_NO_MEMORY;
179 state->g_ctx = g_lock_ctx_init(state, msg_ctx);
180 if (state->g_ctx == NULL) {
181 TALLOC_FREE(frame);
182 return NT_STATUS_NO_MEMORY;
185 state->g_lock_key = talloc_asprintf(state,
186 "trust_password_change_%s",
187 domain);
188 if (state->g_lock_key == NULL) {
189 TALLOC_FREE(frame);
190 return NT_STATUS_NO_MEMORY;
193 g_timeout = timeval_current_ofs(10, 0);
194 status = g_lock_lock(state->g_ctx,
195 string_term_tdb_data(state->g_lock_key),
196 G_LOCK_WRITE, g_timeout);
197 if (!NT_STATUS_IS_OK(status)) {
198 DEBUG(1, ("could not get g_lock on [%s]!\n",
199 state->g_lock_key));
200 TALLOC_FREE(frame);
201 return status;
204 talloc_set_destructor(state, trust_pw_change_state_destructor);
206 status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
207 if (!NT_STATUS_IS_OK(status)) {
208 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
209 domain, nt_errstr(status)));
210 TALLOC_FREE(frame);
211 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
214 current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
215 if (current_nt_hash == NULL) {
216 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
217 domain));
218 TALLOC_FREE(frame);
219 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
221 previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
223 old_version = cli_credentials_get_kvno(creds);
224 pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
225 sec_channel_type = cli_credentials_get_secure_channel_type(creds);
227 new_version = old_version + 1;
229 switch (sec_channel_type) {
230 case SEC_CHAN_WKSTA:
231 case SEC_CHAN_BDC:
232 break;
233 case SEC_CHAN_DNS_DOMAIN:
234 case SEC_CHAN_DOMAIN:
235 status = pdb_get_trusted_domain(frame, domain, &td);
236 if (!NT_STATUS_IS_OK(status)) {
237 DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
238 domain, nt_errstr(status)));
239 TALLOC_FREE(frame);
240 return status;
243 new_trust_version = &new_version;
244 break;
245 default:
246 TALLOC_FREE(frame);
247 return NT_STATUS_NOT_SUPPORTED;
250 timeout = lp_machine_password_timeout();
251 if (timeout == 0) {
252 if (!force) {
253 DEBUG(10,("machine password never expires\n"));
254 TALLOC_FREE(frame);
255 return NT_STATUS_OK;
259 tv.tv_sec = pass_last_set_time;
260 DEBUG(10, ("password last changed %s\n",
261 timeval_string(talloc_tos(), &tv, false)));
262 tv.tv_sec += timeout;
263 DEBUGADD(10, ("password valid until %s\n",
264 timeval_string(talloc_tos(), &tv, false)));
266 if (!force && !timeval_expired(&tv)) {
267 TALLOC_FREE(frame);
268 return NT_STATUS_OK;
271 context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
272 if (context_name == NULL) {
273 TALLOC_FREE(frame);
274 return NT_STATUS_NO_MEMORY;
278 * Create a random machine account password
279 * We create a random buffer and convert that to utf8.
280 * This is similar to what windows is doing.
282 new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
283 lp_security());
284 if (new_trust_pw_str == NULL) {
285 DEBUG(0, ("trust_pw_new_value() failed\n"));
286 TALLOC_FREE(frame);
287 return NT_STATUS_NO_MEMORY;
290 len = strlen(new_trust_pw_str);
291 ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16,
292 new_trust_pw_str, len,
293 (void **)&new_trust_pw_blob.data,
294 &new_trust_pw_blob.length);
295 if (!ok) {
296 status = NT_STATUS_UNMAPPABLE_CHARACTER;
297 if (errno == ENOMEM) {
298 status = NT_STATUS_NO_MEMORY;
300 DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
301 "failed for of %s - %s\n",
302 domain, nt_errstr(status));
303 TALLOC_FREE(frame);
304 return status;
307 switch (sec_channel_type) {
309 case SEC_CHAN_WKSTA:
310 case SEC_CHAN_BDC:
311 status = secrets_prepare_password_change(domain, dcname,
312 new_trust_pw_str,
313 frame, &info, &prev);
314 if (!NT_STATUS_IS_OK(status)) {
315 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
316 domain));
317 TALLOC_FREE(frame);
318 return NT_STATUS_INTERNAL_DB_CORRUPTION;
320 TALLOC_FREE(new_trust_pw_str);
322 if (prev != NULL) {
324 * We had a failure before we changed the password.
326 nt_hashes[idx++] = &prev->password->nt_hash;
328 DEBUG(0,("%s : %s(%s): A password change was already "
329 "started against '%s' at %s. Trying to "
330 "recover...\n",
331 current_timestring(talloc_tos(), false),
332 __func__, domain,
333 prev->password->change_server,
334 nt_time_string(talloc_tos(),
335 prev->password->change_time)));
336 DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] "
337 "against '%s' at %s.\n",
338 current_timestring(talloc_tos(), false),
339 __func__, domain,
340 nt_errstr(prev->local_status),
341 nt_errstr(prev->remote_status),
342 prev->change_server,
343 nt_time_string(talloc_tos(),
344 prev->change_time)));
347 idx_current = idx;
348 nt_hashes[idx++] = &info->password->nt_hash;
349 if (info->old_password != NULL) {
350 nt_hashes[idx++] = &info->old_password->nt_hash;
352 if (info->older_password != NULL) {
353 nt_hashes[idx++] = &info->older_password->nt_hash;
357 * We use the password that's already persitent in
358 * our database in order to handle failures.
360 data_blob_clear_free(&new_trust_pw_blob);
361 new_trust_pw_blob = info->next_change->password->cleartext_blob;
362 break;
364 case SEC_CHAN_DNS_DOMAIN:
365 case SEC_CHAN_DOMAIN:
366 idx_current = idx;
367 nt_hashes[idx++] = current_nt_hash;
368 if (previous_nt_hash != NULL) {
369 nt_hashes[idx++] = previous_nt_hash;
371 break;
373 default:
374 smb_panic("Unsupported secure channel type");
375 break;
377 num_nt_hashes = idx;
379 DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
380 current_timestring(talloc_tos(), false),
381 __func__, domain, context_name));
384 * Check which password the dc knows about.
386 * TODO:
387 * If the previous password is the only password in common with the dc,
388 * we better skip the password change, or use something like
389 * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
390 * local secrets before doing the change.
392 status = netlogon_creds_cli_lck_auth(context, b,
393 num_nt_hashes,
394 nt_hashes,
395 &idx_nt_hashes);
396 if (!NT_STATUS_IS_OK(status)) {
397 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
398 context_name, num_nt_hashes, nt_errstr(status)));
399 TALLOC_FREE(frame);
400 return status;
403 if (prev != NULL && idx_nt_hashes == 0) {
404 DEBUG(0,("%s : %s(%s): Verified new password remotely "
405 "without changing %s\n",
406 current_timestring(talloc_tos(), false),
407 __func__, domain, context_name));
409 status = secrets_finish_password_change(prev->password->change_server,
410 prev->password->change_time,
411 info);
412 if (!NT_STATUS_IS_OK(status)) {
413 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
414 domain));
415 TALLOC_FREE(frame);
416 return NT_STATUS_INTERNAL_DB_CORRUPTION;
419 DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
420 current_timestring(talloc_tos(), false),
421 __func__, domain));
422 TALLOC_FREE(frame);
423 return NT_STATUS_OK;
426 if (idx_nt_hashes != idx_current) {
427 DEBUG(0,("%s : %s(%s): Verified older password remotely "
428 "skip changing %s\n",
429 current_timestring(talloc_tos(), false),
430 __func__, domain, context_name));
432 if (info == NULL) {
433 TALLOC_FREE(frame);
434 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
437 status = secrets_defer_password_change(dcname,
438 NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE,
439 NT_STATUS_NOT_COMMITTED,
440 info);
441 if (!NT_STATUS_IS_OK(status)) {
442 DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
443 domain));
444 TALLOC_FREE(frame);
445 return NT_STATUS_INTERNAL_DB_CORRUPTION;
447 TALLOC_FREE(frame);
448 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
451 DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
452 current_timestring(talloc_tos(), false),
453 __func__, domain, context_name));
456 * Return the result of trying to write the new password
457 * back into the trust account file.
460 switch (sec_channel_type) {
462 case SEC_CHAN_WKSTA:
463 case SEC_CHAN_BDC:
465 * we called secrets_prepare_password_change() above.
467 break;
469 case SEC_CHAN_DNS_DOMAIN:
470 case SEC_CHAN_DOMAIN:
472 * we need to get the sid first for the
473 * pdb_set_trusteddom_pw call
475 ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str,
476 &td->security_identifier);
477 if (!ok) {
478 DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
479 domain));
480 TALLOC_FREE(frame);
481 return NT_STATUS_INTERNAL_DB_CORRUPTION;
483 TALLOC_FREE(new_trust_pw_str);
484 break;
486 default:
487 smb_panic("Unsupported secure channel type");
488 break;
491 DEBUG(0,("%s : %s(%s): Changed password locally\n",
492 current_timestring(talloc_tos(), false), __func__, domain));
494 status = netlogon_creds_cli_ServerPasswordSet(context, b,
495 &new_trust_pw_blob,
496 new_trust_version);
497 if (!NT_STATUS_IS_OK(status)) {
498 NTSTATUS status2;
499 const char *fn = NULL;
501 ok = dcerpc_binding_handle_is_connected(b);
503 DEBUG(0,("%s : %s(%s) remote password change with %s failed "
504 "- %s (%s)\n",
505 current_timestring(talloc_tos(), false),
506 __func__, domain, context_name,
507 nt_errstr(status),
508 ok ? "connected": "disconnected"));
510 if (!ok) {
512 * The connection is broken, we don't
513 * know if the password was changed,
514 * we hope to have more luck next time.
516 status2 = secrets_failed_password_change(dcname,
517 NT_STATUS_NOT_COMMITTED,
518 status,
519 info);
520 fn = "secrets_failed_password_change";
521 } else {
523 * The server rejected the change, we don't
524 * retry and defer the change to the next
525 * "machine password timeout" interval.
527 status2 = secrets_defer_password_change(dcname,
528 NT_STATUS_NOT_COMMITTED,
529 status,
530 info);
531 fn = "secrets_defer_password_change";
533 if (!NT_STATUS_IS_OK(status2)) {
534 DEBUG(0, ("%s() failed for domain %s!\n",
535 fn, domain));
536 TALLOC_FREE(frame);
537 return NT_STATUS_INTERNAL_DB_CORRUPTION;
540 TALLOC_FREE(frame);
541 return status;
544 DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
545 current_timestring(talloc_tos(), false),
546 __func__, domain, context_name));
548 switch (sec_channel_type) {
550 case SEC_CHAN_WKSTA:
551 case SEC_CHAN_BDC:
552 status = secrets_finish_password_change(
553 info->next_change->change_server,
554 info->next_change->change_time,
555 info);
556 if (!NT_STATUS_IS_OK(status)) {
557 DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
558 domain));
559 TALLOC_FREE(frame);
560 return NT_STATUS_INTERNAL_DB_CORRUPTION;
563 DEBUG(0,("%s : %s(%s): Finished password change.\n",
564 current_timestring(talloc_tos(), false),
565 __func__, domain));
566 break;
568 case SEC_CHAN_DNS_DOMAIN:
569 case SEC_CHAN_DOMAIN:
571 * we used pdb_set_trusteddom_pw().
573 break;
575 default:
576 smb_panic("Unsupported secure channel type");
577 break;
580 ok = cli_credentials_set_utf16_password(creds,
581 &new_trust_pw_blob,
582 CRED_SPECIFIED);
583 if (!ok) {
584 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
585 domain));
586 TALLOC_FREE(frame);
587 return NT_STATUS_NO_MEMORY;
590 current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
591 if (current_nt_hash == NULL) {
592 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
593 domain));
594 TALLOC_FREE(frame);
595 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
599 * Now we verify the new password.
601 idx = 0;
602 idx_current = idx;
603 nt_hashes[idx++] = current_nt_hash;
604 num_nt_hashes = idx;
605 status = netlogon_creds_cli_lck_auth(context, b,
606 num_nt_hashes,
607 nt_hashes,
608 &idx_nt_hashes);
609 if (!NT_STATUS_IS_OK(status)) {
610 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
611 context_name, nt_errstr(status)));
612 TALLOC_FREE(frame);
613 return status;
616 DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
617 current_timestring(talloc_tos(), false),
618 __func__, domain, context_name));
620 TALLOC_FREE(frame);
621 return NT_STATUS_OK;