Line data Source code
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
6 : *
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.
11 : *
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.
16 : *
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/>.
19 : */
20 :
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"
34 :
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 : **********************************************************/
40 :
41 : struct trust_pw_change_state {
42 : struct g_lock_ctx *g_ctx;
43 : char *g_lock_key;
44 : };
45 :
46 6 : static int trust_pw_change_state_destructor(struct trust_pw_change_state *state)
47 : {
48 6 : g_lock_unlock(state->g_ctx,
49 6 : string_term_tdb_data(state->g_lock_key));
50 6 : return 0;
51 : }
52 :
53 64 : char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
54 : enum netr_SchannelType sec_channel_type,
55 : int security)
56 : {
57 : /*
58 : * use secure defaults.
59 : */
60 64 : size_t min = 128;
61 64 : size_t max = 255;
62 :
63 64 : switch (sec_channel_type) {
64 64 : case SEC_CHAN_WKSTA:
65 : case SEC_CHAN_BDC:
66 64 : if (security == SEC_DOMAIN) {
67 : /*
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.
71 : */
72 18 : min = 14;
73 18 : max = 14;
74 : }
75 64 : break;
76 0 : case SEC_CHAN_DNS_DOMAIN:
77 : /*
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.
81 : *
82 : * Windows uses new_len = 120 => 240 bytes (utf16)
83 : */
84 0 : min = 120;
85 0 : max = 120;
86 0 : break;
87 0 : case SEC_CHAN_DOMAIN:
88 : /*
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.
92 : */
93 0 : min = 14;
94 0 : max = 14;
95 0 : break;
96 0 : default:
97 0 : break;
98 : }
99 :
100 : /*
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.
104 : */
105 64 : return generate_random_machine_password(mem_ctx, min, max);
106 : }
107 :
108 : /*
109 : * Temporary function to wrap cli_auth in a lck
110 : */
111 :
112 12 : 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)
118 : {
119 : struct netlogon_creds_cli_lck *lck;
120 : NTSTATUS status;
121 :
122 12 : status = netlogon_creds_cli_lck(
123 : context, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
124 : talloc_tos(), &lck);
125 12 : if (!NT_STATUS_IS_OK(status)) {
126 0 : DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
127 : nt_errstr(status));
128 0 : return status;
129 : }
130 :
131 12 : status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes,
132 : idx_nt_hashes);
133 12 : TALLOC_FREE(lck);
134 :
135 12 : return status;
136 : }
137 :
138 6 : 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)
144 : {
145 6 : TALLOC_CTX *frame = talloc_stackframe();
146 6 : const char *context_name = NULL;
147 : struct trust_pw_change_state *state;
148 6 : struct cli_credentials *creds = NULL;
149 6 : struct secrets_domain_info1 *info = NULL;
150 6 : struct secrets_domain_info1_change *prev = NULL;
151 6 : const struct samr_Password *current_nt_hash = NULL;
152 6 : const struct samr_Password *previous_nt_hash = NULL;
153 6 : uint8_t num_nt_hashes = 0;
154 6 : uint8_t idx = 0;
155 6 : const struct samr_Password *nt_hashes[1+3] = { NULL, };
156 6 : uint8_t idx_nt_hashes = 0;
157 6 : uint8_t idx_current = UINT8_MAX;
158 6 : enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
159 : time_t pass_last_set_time;
160 6 : uint32_t old_version = 0;
161 6 : struct pdb_trusted_domain *td = NULL;
162 6 : struct timeval g_timeout = { 0, };
163 6 : int timeout = 0;
164 6 : struct timeval tv = { 0, };
165 6 : char *new_trust_pw_str = NULL;
166 6 : size_t len = 0;
167 6 : DATA_BLOB new_trust_pw_blob = data_blob_null;
168 6 : uint32_t new_version = 0;
169 6 : uint32_t *new_trust_version = NULL;
170 : NTSTATUS status;
171 : bool ok;
172 :
173 6 : state = talloc_zero(frame, struct trust_pw_change_state);
174 6 : if (state == NULL) {
175 0 : TALLOC_FREE(frame);
176 0 : return NT_STATUS_NO_MEMORY;
177 : }
178 :
179 6 : state->g_ctx = g_lock_ctx_init(state, msg_ctx);
180 6 : if (state->g_ctx == NULL) {
181 0 : TALLOC_FREE(frame);
182 0 : return NT_STATUS_NO_MEMORY;
183 : }
184 :
185 6 : state->g_lock_key = talloc_asprintf(state,
186 : "trust_password_change_%s",
187 : domain);
188 6 : if (state->g_lock_key == NULL) {
189 0 : TALLOC_FREE(frame);
190 0 : return NT_STATUS_NO_MEMORY;
191 : }
192 :
193 6 : g_timeout = timeval_current_ofs(10, 0);
194 6 : status = g_lock_lock(state->g_ctx,
195 6 : string_term_tdb_data(state->g_lock_key),
196 : G_LOCK_WRITE, g_timeout);
197 6 : if (!NT_STATUS_IS_OK(status)) {
198 0 : DEBUG(1, ("could not get g_lock on [%s]!\n",
199 : state->g_lock_key));
200 0 : TALLOC_FREE(frame);
201 0 : return status;
202 : }
203 :
204 6 : talloc_set_destructor(state, trust_pw_change_state_destructor);
205 :
206 6 : status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
207 6 : if (!NT_STATUS_IS_OK(status)) {
208 0 : DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
209 : domain, nt_errstr(status)));
210 0 : TALLOC_FREE(frame);
211 0 : return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
212 : }
213 :
214 6 : current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
215 6 : if (current_nt_hash == NULL) {
216 0 : DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
217 : domain));
218 0 : TALLOC_FREE(frame);
219 0 : return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
220 : }
221 6 : previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
222 :
223 6 : old_version = cli_credentials_get_kvno(creds);
224 6 : pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
225 6 : sec_channel_type = cli_credentials_get_secure_channel_type(creds);
226 :
227 6 : new_version = old_version + 1;
228 :
229 6 : switch (sec_channel_type) {
230 6 : case SEC_CHAN_WKSTA:
231 : case SEC_CHAN_BDC:
232 6 : break;
233 0 : case SEC_CHAN_DNS_DOMAIN:
234 : case SEC_CHAN_DOMAIN:
235 0 : status = pdb_get_trusted_domain(frame, domain, &td);
236 0 : if (!NT_STATUS_IS_OK(status)) {
237 0 : DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
238 : domain, nt_errstr(status)));
239 0 : TALLOC_FREE(frame);
240 0 : return status;
241 : }
242 :
243 0 : new_trust_version = &new_version;
244 0 : break;
245 0 : default:
246 0 : TALLOC_FREE(frame);
247 0 : return NT_STATUS_NOT_SUPPORTED;
248 : }
249 :
250 6 : timeout = lp_machine_password_timeout();
251 6 : if (timeout == 0) {
252 0 : if (!force) {
253 0 : DEBUG(10,("machine password never expires\n"));
254 0 : TALLOC_FREE(frame);
255 0 : return NT_STATUS_OK;
256 : }
257 : }
258 :
259 6 : tv.tv_sec = pass_last_set_time;
260 6 : DEBUG(10, ("password last changed %s\n",
261 : timeval_string(talloc_tos(), &tv, false)));
262 6 : tv.tv_sec += timeout;
263 6 : DEBUGADD(10, ("password valid until %s\n",
264 : timeval_string(talloc_tos(), &tv, false)));
265 :
266 6 : if (!force && !timeval_expired(&tv)) {
267 0 : TALLOC_FREE(frame);
268 0 : return NT_STATUS_OK;
269 : }
270 :
271 6 : context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
272 6 : if (context_name == NULL) {
273 0 : TALLOC_FREE(frame);
274 0 : return NT_STATUS_NO_MEMORY;
275 : }
276 :
277 : /*
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.
281 : */
282 6 : new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
283 : lp_security());
284 6 : if (new_trust_pw_str == NULL) {
285 0 : DEBUG(0, ("trust_pw_new_value() failed\n"));
286 0 : TALLOC_FREE(frame);
287 0 : return NT_STATUS_NO_MEMORY;
288 : }
289 :
290 6 : len = strlen(new_trust_pw_str);
291 6 : 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 6 : if (!ok) {
296 0 : status = NT_STATUS_UNMAPPABLE_CHARACTER;
297 0 : if (errno == ENOMEM) {
298 0 : status = NT_STATUS_NO_MEMORY;
299 : }
300 0 : DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
301 : "failed for of %s - %s\n",
302 : domain, nt_errstr(status));
303 0 : TALLOC_FREE(frame);
304 0 : return status;
305 : }
306 :
307 6 : switch (sec_channel_type) {
308 :
309 6 : case SEC_CHAN_WKSTA:
310 : case SEC_CHAN_BDC:
311 6 : status = secrets_prepare_password_change(domain, dcname,
312 : new_trust_pw_str,
313 : frame, &info, &prev);
314 6 : if (!NT_STATUS_IS_OK(status)) {
315 0 : DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
316 : domain));
317 0 : TALLOC_FREE(frame);
318 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
319 : }
320 6 : TALLOC_FREE(new_trust_pw_str);
321 :
322 6 : if (prev != NULL) {
323 : /*
324 : * We had a failure before we changed the password.
325 : */
326 0 : nt_hashes[idx++] = &prev->password->nt_hash;
327 :
328 0 : 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 0 : 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)));
345 : }
346 :
347 6 : idx_current = idx;
348 6 : nt_hashes[idx++] = &info->password->nt_hash;
349 6 : if (info->old_password != NULL) {
350 0 : nt_hashes[idx++] = &info->old_password->nt_hash;
351 : }
352 6 : if (info->older_password != NULL) {
353 0 : nt_hashes[idx++] = &info->older_password->nt_hash;
354 : }
355 :
356 : /*
357 : * We use the password that's already persistent in
358 : * our database in order to handle failures.
359 : */
360 6 : data_blob_clear_free(&new_trust_pw_blob);
361 6 : new_trust_pw_blob = info->next_change->password->cleartext_blob;
362 6 : break;
363 :
364 0 : case SEC_CHAN_DNS_DOMAIN:
365 : case SEC_CHAN_DOMAIN:
366 0 : idx_current = idx;
367 0 : nt_hashes[idx++] = current_nt_hash;
368 0 : if (previous_nt_hash != NULL) {
369 0 : nt_hashes[idx++] = previous_nt_hash;
370 : }
371 0 : break;
372 :
373 0 : default:
374 0 : smb_panic("Unsupported secure channel type");
375 : break;
376 : }
377 6 : num_nt_hashes = idx;
378 :
379 6 : DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
380 : current_timestring(talloc_tos(), false),
381 : __func__, domain, context_name));
382 :
383 : /*
384 : * Check which password the dc knows about.
385 : *
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.
391 : */
392 6 : status = netlogon_creds_cli_lck_auth(context, b,
393 : num_nt_hashes,
394 : nt_hashes,
395 : &idx_nt_hashes);
396 6 : if (!NT_STATUS_IS_OK(status)) {
397 0 : DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
398 : context_name, num_nt_hashes, nt_errstr(status)));
399 0 : TALLOC_FREE(frame);
400 0 : return status;
401 : }
402 :
403 6 : if (prev != NULL && idx_nt_hashes == 0) {
404 0 : 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));
408 :
409 0 : status = secrets_finish_password_change(prev->password->change_server,
410 0 : prev->password->change_time,
411 : info);
412 0 : if (!NT_STATUS_IS_OK(status)) {
413 0 : DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
414 : domain));
415 0 : TALLOC_FREE(frame);
416 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
417 : }
418 :
419 0 : DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
420 : current_timestring(talloc_tos(), false),
421 : __func__, domain));
422 0 : TALLOC_FREE(frame);
423 0 : return NT_STATUS_OK;
424 : }
425 :
426 6 : if (idx_nt_hashes != idx_current) {
427 0 : 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));
431 :
432 0 : if (info == NULL) {
433 0 : TALLOC_FREE(frame);
434 0 : return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
435 : }
436 :
437 0 : status = secrets_defer_password_change(dcname,
438 0 : NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE,
439 0 : NT_STATUS_NOT_COMMITTED,
440 : info);
441 0 : if (!NT_STATUS_IS_OK(status)) {
442 0 : DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
443 : domain));
444 0 : TALLOC_FREE(frame);
445 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
446 : }
447 0 : TALLOC_FREE(frame);
448 0 : return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
449 : }
450 :
451 6 : DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
452 : current_timestring(talloc_tos(), false),
453 : __func__, domain, context_name));
454 :
455 : /*
456 : * Return the result of trying to write the new password
457 : * back into the trust account file.
458 : */
459 :
460 6 : switch (sec_channel_type) {
461 :
462 6 : case SEC_CHAN_WKSTA:
463 : case SEC_CHAN_BDC:
464 : /*
465 : * we called secrets_prepare_password_change() above.
466 : */
467 6 : break;
468 :
469 0 : case SEC_CHAN_DNS_DOMAIN:
470 : case SEC_CHAN_DOMAIN:
471 : /*
472 : * we need to get the sid first for the
473 : * pdb_set_trusteddom_pw call
474 : */
475 0 : ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str,
476 0 : &td->security_identifier);
477 0 : if (!ok) {
478 0 : DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
479 : domain));
480 0 : TALLOC_FREE(frame);
481 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
482 : }
483 0 : TALLOC_FREE(new_trust_pw_str);
484 0 : break;
485 :
486 0 : default:
487 0 : smb_panic("Unsupported secure channel type");
488 : break;
489 : }
490 :
491 6 : DEBUG(0,("%s : %s(%s): Changed password locally\n",
492 : current_timestring(talloc_tos(), false), __func__, domain));
493 :
494 6 : status = netlogon_creds_cli_ServerPasswordSet(context, b,
495 : &new_trust_pw_blob,
496 : new_trust_version);
497 6 : if (!NT_STATUS_IS_OK(status)) {
498 : NTSTATUS status2;
499 0 : const char *fn = NULL;
500 :
501 0 : ok = dcerpc_binding_handle_is_connected(b);
502 :
503 0 : 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"));
509 :
510 0 : if (!ok) {
511 : /*
512 : * The connection is broken, we don't
513 : * know if the password was changed,
514 : * we hope to have more luck next time.
515 : */
516 0 : status2 = secrets_failed_password_change(dcname,
517 0 : NT_STATUS_NOT_COMMITTED,
518 : status,
519 : info);
520 0 : fn = "secrets_failed_password_change";
521 : } else {
522 : /*
523 : * The server rejected the change, we don't
524 : * retry and defer the change to the next
525 : * "machine password timeout" interval.
526 : */
527 0 : status2 = secrets_defer_password_change(dcname,
528 0 : NT_STATUS_NOT_COMMITTED,
529 : status,
530 : info);
531 0 : fn = "secrets_defer_password_change";
532 : }
533 0 : if (!NT_STATUS_IS_OK(status2)) {
534 0 : DEBUG(0, ("%s() failed for domain %s!\n",
535 : fn, domain));
536 0 : TALLOC_FREE(frame);
537 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
538 : }
539 :
540 0 : TALLOC_FREE(frame);
541 0 : return status;
542 : }
543 :
544 6 : DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
545 : current_timestring(talloc_tos(), false),
546 : __func__, domain, context_name));
547 :
548 6 : switch (sec_channel_type) {
549 :
550 6 : case SEC_CHAN_WKSTA:
551 : case SEC_CHAN_BDC:
552 18 : status = secrets_finish_password_change(
553 6 : info->next_change->change_server,
554 6 : info->next_change->change_time,
555 : info);
556 6 : if (!NT_STATUS_IS_OK(status)) {
557 0 : DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
558 : domain));
559 0 : TALLOC_FREE(frame);
560 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
561 : }
562 :
563 6 : DEBUG(0,("%s : %s(%s): Finished password change.\n",
564 : current_timestring(talloc_tos(), false),
565 : __func__, domain));
566 6 : break;
567 :
568 0 : case SEC_CHAN_DNS_DOMAIN:
569 : case SEC_CHAN_DOMAIN:
570 : /*
571 : * we used pdb_set_trusteddom_pw().
572 : */
573 0 : break;
574 :
575 0 : default:
576 0 : smb_panic("Unsupported secure channel type");
577 : break;
578 : }
579 :
580 6 : ok = cli_credentials_set_utf16_password(creds,
581 : &new_trust_pw_blob,
582 : CRED_SPECIFIED);
583 6 : if (!ok) {
584 0 : DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
585 : domain));
586 0 : TALLOC_FREE(frame);
587 0 : return NT_STATUS_NO_MEMORY;
588 : }
589 :
590 6 : current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
591 6 : if (current_nt_hash == NULL) {
592 0 : DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
593 : domain));
594 0 : TALLOC_FREE(frame);
595 0 : return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
596 : }
597 :
598 : /*
599 : * Now we verify the new password.
600 : */
601 6 : idx = 0;
602 6 : nt_hashes[idx++] = current_nt_hash;
603 6 : num_nt_hashes = idx;
604 6 : status = netlogon_creds_cli_lck_auth(context, b,
605 : num_nt_hashes,
606 : nt_hashes,
607 : &idx_nt_hashes);
608 6 : if (!NT_STATUS_IS_OK(status)) {
609 0 : DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
610 : context_name, nt_errstr(status)));
611 0 : TALLOC_FREE(frame);
612 0 : return status;
613 : }
614 :
615 6 : DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
616 : current_timestring(talloc_tos(), false),
617 : __func__, domain, context_name));
618 :
619 6 : TALLOC_FREE(frame);
620 6 : return NT_STATUS_OK;
621 : }
|