build: Move uid_wrapper to third_party
[Samba.git] / source3 / libsmb / trusts_util.c
blob27e77e6cc60fc2ad32783896fdcc116913f70b9f
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);
108 * Temporary function to wrap cli_auth in a lck
111 static NTSTATUS netlogon_creds_cli_lck_auth(
112 struct netlogon_creds_cli_context *context,
113 struct dcerpc_binding_handle *b,
114 uint8_t num_nt_hashes,
115 const struct samr_Password * const *nt_hashes,
116 uint8_t *idx_nt_hashes)
118 struct netlogon_creds_cli_lck *lck;
119 NTSTATUS status;
121 status = netlogon_creds_cli_lck(
122 context, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
123 talloc_tos(), &lck);
124 if (!NT_STATUS_IS_OK(status)) {
125 DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
126 nt_errstr(status));
127 return status;
130 status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes,
131 idx_nt_hashes);
132 TALLOC_FREE(lck);
134 return status;
137 NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
138 struct messaging_context *msg_ctx,
139 struct dcerpc_binding_handle *b,
140 const char *domain,
141 const char *dcname,
142 bool force)
144 TALLOC_CTX *frame = talloc_stackframe();
145 const char *context_name = NULL;
146 struct trust_pw_change_state *state;
147 struct cli_credentials *creds = NULL;
148 struct secrets_domain_info1 *info = NULL;
149 struct secrets_domain_info1_change *prev = NULL;
150 const struct samr_Password *current_nt_hash = NULL;
151 const struct samr_Password *previous_nt_hash = NULL;
152 uint8_t num_nt_hashes = 0;
153 uint8_t idx = 0;
154 const struct samr_Password *nt_hashes[1+3] = { NULL, };
155 uint8_t idx_nt_hashes = 0;
156 uint8_t idx_current = UINT8_MAX;
157 enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
158 time_t pass_last_set_time;
159 uint32_t old_version = 0;
160 struct pdb_trusted_domain *td = NULL;
161 struct timeval g_timeout = { 0, };
162 int timeout = 0;
163 struct timeval tv = { 0, };
164 char *new_trust_pw_str = NULL;
165 size_t len = 0;
166 DATA_BLOB new_trust_pw_blob = data_blob_null;
167 uint32_t new_version = 0;
168 uint32_t *new_trust_version = NULL;
169 NTSTATUS status;
170 bool ok;
172 state = talloc_zero(frame, struct trust_pw_change_state);
173 if (state == NULL) {
174 TALLOC_FREE(frame);
175 return NT_STATUS_NO_MEMORY;
178 state->g_ctx = g_lock_ctx_init(state, msg_ctx);
179 if (state->g_ctx == NULL) {
180 TALLOC_FREE(frame);
181 return NT_STATUS_NO_MEMORY;
184 state->g_lock_key = talloc_asprintf(state,
185 "trust_password_change_%s",
186 domain);
187 if (state->g_lock_key == NULL) {
188 TALLOC_FREE(frame);
189 return NT_STATUS_NO_MEMORY;
192 g_timeout = timeval_current_ofs(10, 0);
193 status = g_lock_lock(state->g_ctx,
194 state->g_lock_key,
195 G_LOCK_WRITE, g_timeout);
196 if (!NT_STATUS_IS_OK(status)) {
197 DEBUG(1, ("could not get g_lock on [%s]!\n",
198 state->g_lock_key));
199 TALLOC_FREE(frame);
200 return status;
203 talloc_set_destructor(state, trust_pw_change_state_destructor);
205 status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
206 if (!NT_STATUS_IS_OK(status)) {
207 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
208 domain, nt_errstr(status)));
209 TALLOC_FREE(frame);
210 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
213 current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
214 if (current_nt_hash == NULL) {
215 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
216 domain));
217 TALLOC_FREE(frame);
218 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
220 previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
222 old_version = cli_credentials_get_kvno(creds);
223 pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
224 sec_channel_type = cli_credentials_get_secure_channel_type(creds);
226 new_version = old_version + 1;
228 switch (sec_channel_type) {
229 case SEC_CHAN_WKSTA:
230 case SEC_CHAN_BDC:
231 break;
232 case SEC_CHAN_DNS_DOMAIN:
233 case SEC_CHAN_DOMAIN:
234 status = pdb_get_trusted_domain(frame, domain, &td);
235 if (!NT_STATUS_IS_OK(status)) {
236 DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
237 domain, nt_errstr(status)));
238 TALLOC_FREE(frame);
239 return status;
242 new_trust_version = &new_version;
243 break;
244 default:
245 TALLOC_FREE(frame);
246 return NT_STATUS_NOT_SUPPORTED;
249 timeout = lp_machine_password_timeout();
250 if (timeout == 0) {
251 if (!force) {
252 DEBUG(10,("machine password never expires\n"));
253 TALLOC_FREE(frame);
254 return NT_STATUS_OK;
258 tv.tv_sec = pass_last_set_time;
259 DEBUG(10, ("password last changed %s\n",
260 timeval_string(talloc_tos(), &tv, false)));
261 tv.tv_sec += timeout;
262 DEBUGADD(10, ("password valid until %s\n",
263 timeval_string(talloc_tos(), &tv, false)));
265 if (!force && !timeval_expired(&tv)) {
266 TALLOC_FREE(frame);
267 return NT_STATUS_OK;
270 context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
271 if (context_name == NULL) {
272 TALLOC_FREE(frame);
273 return NT_STATUS_NO_MEMORY;
277 * Create a random machine account password
278 * We create a random buffer and convert that to utf8.
279 * This is similar to what windows is doing.
281 new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
282 lp_security());
283 if (new_trust_pw_str == NULL) {
284 DEBUG(0, ("trust_pw_new_value() failed\n"));
285 TALLOC_FREE(frame);
286 return NT_STATUS_NO_MEMORY;
289 len = strlen(new_trust_pw_str);
290 ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16,
291 new_trust_pw_str, len,
292 (void **)&new_trust_pw_blob.data,
293 &new_trust_pw_blob.length);
294 if (!ok) {
295 status = NT_STATUS_UNMAPPABLE_CHARACTER;
296 if (errno == ENOMEM) {
297 status = NT_STATUS_NO_MEMORY;
299 DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
300 "failed for of %s - %s\n",
301 domain, nt_errstr(status));
302 TALLOC_FREE(frame);
303 return status;
306 switch (sec_channel_type) {
308 case SEC_CHAN_WKSTA:
309 case SEC_CHAN_BDC:
310 status = secrets_prepare_password_change(domain, dcname,
311 new_trust_pw_str,
312 frame, &info, &prev);
313 if (!NT_STATUS_IS_OK(status)) {
314 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
315 domain));
316 TALLOC_FREE(frame);
317 return NT_STATUS_INTERNAL_DB_CORRUPTION;
319 TALLOC_FREE(new_trust_pw_str);
321 if (prev != NULL) {
323 * We had a failure before we changed the password.
325 nt_hashes[idx++] = &prev->password->nt_hash;
327 DEBUG(0,("%s : %s(%s): A password change was already "
328 "started against '%s' at %s. Trying to "
329 "recover...\n",
330 current_timestring(talloc_tos(), false),
331 __func__, domain,
332 prev->password->change_server,
333 nt_time_string(talloc_tos(),
334 prev->password->change_time)));
335 DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] "
336 "against '%s' at %s.\n",
337 current_timestring(talloc_tos(), false),
338 __func__, domain,
339 nt_errstr(prev->local_status),
340 nt_errstr(prev->remote_status),
341 prev->change_server,
342 nt_time_string(talloc_tos(),
343 prev->change_time)));
346 idx_current = idx;
347 nt_hashes[idx++] = &info->password->nt_hash;
348 if (info->old_password != NULL) {
349 nt_hashes[idx++] = &info->old_password->nt_hash;
351 if (info->older_password != NULL) {
352 nt_hashes[idx++] = &info->older_password->nt_hash;
356 * We use the password that's already persitent in
357 * our database in order to handle failures.
359 data_blob_clear_free(&new_trust_pw_blob);
360 new_trust_pw_blob = info->next_change->password->cleartext_blob;
361 break;
363 case SEC_CHAN_DNS_DOMAIN:
364 case SEC_CHAN_DOMAIN:
365 idx_current = idx;
366 nt_hashes[idx++] = current_nt_hash;
367 if (previous_nt_hash != NULL) {
368 nt_hashes[idx++] = previous_nt_hash;
370 break;
372 default:
373 smb_panic("Unsupported secure channel type");
374 break;
376 num_nt_hashes = idx;
378 DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
379 current_timestring(talloc_tos(), false),
380 __func__, domain, context_name));
383 * Check which password the dc knows about.
385 * TODO:
386 * If the previous password is the only password in common with the dc,
387 * we better skip the password change, or use something like
388 * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
389 * local secrets before doing the change.
391 status = netlogon_creds_cli_lck_auth(context, b,
392 num_nt_hashes,
393 nt_hashes,
394 &idx_nt_hashes);
395 if (!NT_STATUS_IS_OK(status)) {
396 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
397 context_name, num_nt_hashes, nt_errstr(status)));
398 TALLOC_FREE(frame);
399 return status;
402 if (prev != NULL && idx_nt_hashes == 0) {
403 DEBUG(0,("%s : %s(%s): Verified new password remotely "
404 "without changing %s\n",
405 current_timestring(talloc_tos(), false),
406 __func__, domain, context_name));
408 status = secrets_finish_password_change(prev->password->change_server,
409 prev->password->change_time,
410 info);
411 if (!NT_STATUS_IS_OK(status)) {
412 DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
413 domain));
414 TALLOC_FREE(frame);
415 return NT_STATUS_INTERNAL_DB_CORRUPTION;
418 DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
419 current_timestring(talloc_tos(), false),
420 __func__, domain));
421 TALLOC_FREE(frame);
422 return NT_STATUS_OK;
425 if (idx_nt_hashes != idx_current) {
426 DEBUG(0,("%s : %s(%s): Verified older password remotely "
427 "skip changing %s\n",
428 current_timestring(talloc_tos(), false),
429 __func__, domain, context_name));
431 if (info == NULL) {
432 TALLOC_FREE(frame);
433 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
436 status = secrets_defer_password_change(dcname,
437 NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE,
438 NT_STATUS_NOT_COMMITTED,
439 info);
440 if (!NT_STATUS_IS_OK(status)) {
441 DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
442 domain));
443 TALLOC_FREE(frame);
444 return NT_STATUS_INTERNAL_DB_CORRUPTION;
446 TALLOC_FREE(frame);
447 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
450 DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
451 current_timestring(talloc_tos(), false),
452 __func__, domain, context_name));
455 * Return the result of trying to write the new password
456 * back into the trust account file.
459 switch (sec_channel_type) {
461 case SEC_CHAN_WKSTA:
462 case SEC_CHAN_BDC:
464 * we called secrets_prepare_password_change() above.
466 break;
468 case SEC_CHAN_DNS_DOMAIN:
469 case SEC_CHAN_DOMAIN:
471 * we need to get the sid first for the
472 * pdb_set_trusteddom_pw call
474 ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str,
475 &td->security_identifier);
476 if (!ok) {
477 DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
478 domain));
479 TALLOC_FREE(frame);
480 return NT_STATUS_INTERNAL_DB_CORRUPTION;
482 TALLOC_FREE(new_trust_pw_str);
483 break;
485 default:
486 smb_panic("Unsupported secure channel type");
487 break;
490 DEBUG(0,("%s : %s(%s): Changed password locally\n",
491 current_timestring(talloc_tos(), false), __func__, domain));
493 status = netlogon_creds_cli_ServerPasswordSet(context, b,
494 &new_trust_pw_blob,
495 new_trust_version);
496 if (!NT_STATUS_IS_OK(status)) {
497 NTSTATUS status2;
498 const char *fn = NULL;
500 ok = dcerpc_binding_handle_is_connected(b);
502 DEBUG(0,("%s : %s(%s) remote password change with %s failed "
503 "- %s (%s)\n",
504 current_timestring(talloc_tos(), false),
505 __func__, domain, context_name,
506 nt_errstr(status),
507 ok ? "connected": "disconnected"));
509 if (!ok) {
511 * The connection is broken, we don't
512 * know if the password was changed,
513 * we hope to have more luck next time.
515 status2 = secrets_failed_password_change(dcname,
516 NT_STATUS_NOT_COMMITTED,
517 status,
518 info);
519 fn = "secrets_failed_password_change";
520 } else {
522 * The server rejected the change, we don't
523 * retry and defer the change to the next
524 * "machine password timeout" interval.
526 status2 = secrets_defer_password_change(dcname,
527 NT_STATUS_NOT_COMMITTED,
528 status,
529 info);
530 fn = "secrets_defer_password_change";
532 if (!NT_STATUS_IS_OK(status2)) {
533 DEBUG(0, ("%s() failed for domain %s!\n",
534 fn, domain));
535 TALLOC_FREE(frame);
536 return NT_STATUS_INTERNAL_DB_CORRUPTION;
539 TALLOC_FREE(frame);
540 return status;
543 DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
544 current_timestring(talloc_tos(), false),
545 __func__, domain, context_name));
547 switch (sec_channel_type) {
549 case SEC_CHAN_WKSTA:
550 case SEC_CHAN_BDC:
551 status = secrets_finish_password_change(
552 info->next_change->change_server,
553 info->next_change->change_time,
554 info);
555 if (!NT_STATUS_IS_OK(status)) {
556 DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
557 domain));
558 TALLOC_FREE(frame);
559 return NT_STATUS_INTERNAL_DB_CORRUPTION;
562 DEBUG(0,("%s : %s(%s): Finished password change.\n",
563 current_timestring(talloc_tos(), false),
564 __func__, domain));
565 break;
567 case SEC_CHAN_DNS_DOMAIN:
568 case SEC_CHAN_DOMAIN:
570 * we used pdb_set_trusteddom_pw().
572 break;
574 default:
575 smb_panic("Unsupported secure channel type");
576 break;
579 ok = cli_credentials_set_utf16_password(creds,
580 &new_trust_pw_blob,
581 CRED_SPECIFIED);
582 if (!ok) {
583 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
584 domain));
585 TALLOC_FREE(frame);
586 return NT_STATUS_NO_MEMORY;
589 current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
590 if (current_nt_hash == NULL) {
591 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
592 domain));
593 TALLOC_FREE(frame);
594 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
598 * Now we verify the new password.
600 idx = 0;
601 idx_current = idx;
602 nt_hashes[idx++] = current_nt_hash;
603 num_nt_hashes = idx;
604 status = netlogon_creds_cli_lck_auth(context, b,
605 num_nt_hashes,
606 nt_hashes,
607 &idx_nt_hashes);
608 if (!NT_STATUS_IS_OK(status)) {
609 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
610 context_name, nt_errstr(status)));
611 TALLOC_FREE(frame);
612 return status;
615 DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
616 current_timestring(talloc_tos(), false),
617 __func__, domain, context_name));
619 TALLOC_FREE(frame);
620 return NT_STATUS_OK;