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/>.
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"
29 #include "libsmb/libsmb.h"
30 #include "source3/include/messages.h"
31 #include "source3/include/g_lock.h"
33 /*********************************************************
34 Change the domain password on the PDC.
35 Do most of the legwork ourselfs. Caller must have
36 already setup the connection to the NETLOGON pipe
37 **********************************************************/
39 struct trust_pw_change_state {
40 struct g_lock_ctx *g_ctx;
44 static int trust_pw_change_state_destructor(struct trust_pw_change_state *state)
46 g_lock_unlock(state->g_ctx, state->g_lock_key);
50 char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
51 enum netr_SchannelType sec_channel_type,
55 * use secure defaults.
60 switch (sec_channel_type) {
63 if (security == SEC_DOMAIN) {
65 * The maximum length of a trust account password.
66 * Used when we randomly create it, 15 char passwords
67 * exceed NT4's max password length.
73 case SEC_CHAN_DNS_DOMAIN:
75 * new_len * 2 = 498 bytes is the largest possible length
76 * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
77 * and a confounder with at least 2 bytes is required.
79 * Windows uses new_len = 120 => 240 bytes (utf16)
87 * The maximum length of a trust account password.
88 * Used when we randomly create it, 15 char passwords
89 * exceed NT4's max password length.
99 * Create a random machine account password
100 * We create a random buffer and convert that to utf8.
101 * This is similar to what windows is doing.
103 return generate_random_machine_password(mem_ctx, min, max);
106 NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
107 struct messaging_context *msg_ctx,
108 struct dcerpc_binding_handle *b,
113 TALLOC_CTX *frame = talloc_stackframe();
114 const char *context_name = NULL;
115 struct trust_pw_change_state *state;
116 struct cli_credentials *creds = NULL;
117 const struct samr_Password *current_nt_hash = NULL;
118 const struct samr_Password *previous_nt_hash = NULL;
119 uint8_t num_nt_hashes = 0;
121 const struct samr_Password *nt_hashes[1+1] = { NULL, };
122 uint8_t idx_nt_hashes = 0;
123 uint8_t idx_current = UINT8_MAX;
124 enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
125 time_t pass_last_set_time;
126 uint32_t old_version = 0;
127 struct pdb_trusted_domain *td = NULL;
128 struct timeval g_timeout = { 0, };
130 struct timeval tv = { 0, };
131 char *new_trust_pw_str = NULL;
133 DATA_BLOB new_trust_pw_blob = data_blob_null;
134 uint32_t new_version = 0;
135 uint32_t *new_trust_version = NULL;
139 state = talloc_zero(frame, struct trust_pw_change_state);
142 return NT_STATUS_NO_MEMORY;
145 state->g_ctx = g_lock_ctx_init(state, msg_ctx);
146 if (state->g_ctx == NULL) {
148 return NT_STATUS_NO_MEMORY;
151 state->g_lock_key = talloc_asprintf(state,
152 "trust_password_change_%s",
154 if (state->g_lock_key == NULL) {
156 return NT_STATUS_NO_MEMORY;
159 g_timeout = timeval_current_ofs(10, 0);
160 status = g_lock_lock(state->g_ctx,
162 G_LOCK_WRITE, g_timeout);
163 if (!NT_STATUS_IS_OK(status)) {
164 DEBUG(1, ("could not get g_lock on [%s]!\n",
170 talloc_set_destructor(state, trust_pw_change_state_destructor);
172 status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
173 if (!NT_STATUS_IS_OK(status)) {
174 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
175 domain, nt_errstr(status)));
177 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
180 current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
181 if (current_nt_hash == NULL) {
182 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
185 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
187 previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
189 old_version = cli_credentials_get_kvno(creds);
190 pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
191 sec_channel_type = cli_credentials_get_secure_channel_type(creds);
193 new_version = old_version + 1;
195 switch (sec_channel_type) {
199 case SEC_CHAN_DNS_DOMAIN:
200 case SEC_CHAN_DOMAIN:
201 status = pdb_get_trusted_domain(frame, domain, &td);
202 if (!NT_STATUS_IS_OK(status)) {
203 DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
204 domain, nt_errstr(status)));
209 new_trust_version = &new_version;
213 return NT_STATUS_NOT_SUPPORTED;
216 timeout = lp_machine_password_timeout();
219 DEBUG(10,("machine password never expires\n"));
225 tv.tv_sec = pass_last_set_time;
226 DEBUG(10, ("password last changed %s\n",
227 timeval_string(talloc_tos(), &tv, false)));
228 tv.tv_sec += timeout;
229 DEBUGADD(10, ("password valid until %s\n",
230 timeval_string(talloc_tos(), &tv, false)));
232 if (!force && !timeval_expired(&tv)) {
237 context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
238 if (context_name == NULL) {
240 return NT_STATUS_NO_MEMORY;
244 * Create a random machine account password
245 * We create a random buffer and convert that to utf8.
246 * This is similar to what windows is doing.
248 new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
250 if (new_trust_pw_str == NULL) {
251 DEBUG(0, ("trust_pw_new_value() failed\n"));
253 return NT_STATUS_NO_MEMORY;
256 len = strlen(new_trust_pw_str);
257 ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16,
258 new_trust_pw_str, len,
259 (void **)&new_trust_pw_blob.data,
260 &new_trust_pw_blob.length);
262 status = NT_STATUS_UNMAPPABLE_CHARACTER;
263 if (errno == ENOMEM) {
264 status = NT_STATUS_NO_MEMORY;
266 DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
267 "failed for of %s - %s\n",
268 domain, nt_errstr(status));
274 nt_hashes[idx++] = current_nt_hash;
275 if (previous_nt_hash != NULL) {
276 nt_hashes[idx++] = previous_nt_hash;
280 DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
281 current_timestring(talloc_tos(), false),
282 __func__, domain, context_name));
285 * Check which password the dc knows about.
288 * If the previous password is the only password in common with the dc,
289 * we better skip the password change, or use something like
290 * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
291 * local secrets before doing the change.
293 status = netlogon_creds_cli_auth(context, b,
297 if (!NT_STATUS_IS_OK(status)) {
298 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
299 context_name, num_nt_hashes, nt_errstr(status)));
304 if (idx_nt_hashes != idx_current) {
305 DEBUG(0,("%s : %s(%s): Verified older password remotely "
306 "skip changing %s\n",
307 current_timestring(talloc_tos(), false),
308 __func__, domain, context_name));
310 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
313 DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
314 current_timestring(talloc_tos(), false),
315 __func__, domain, context_name));
318 * Return the result of trying to write the new password
319 * back into the trust account file.
322 switch (sec_channel_type) {
326 ok = secrets_store_machine_password(new_trust_pw_str,
330 DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n",
333 return NT_STATUS_INTERNAL_DB_CORRUPTION;
335 TALLOC_FREE(new_trust_pw_str);
338 case SEC_CHAN_DNS_DOMAIN:
339 case SEC_CHAN_DOMAIN:
341 * we need to get the sid first for the
342 * pdb_set_trusteddom_pw call
344 ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str,
345 &td->security_identifier);
347 DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
350 return NT_STATUS_INTERNAL_DB_CORRUPTION;
352 TALLOC_FREE(new_trust_pw_str);
356 smb_panic("Unsupported secure channel type");
360 DEBUG(0,("%s : %s(%s): Changed password locally\n",
361 current_timestring(talloc_tos(), false), __func__, domain));
363 status = netlogon_creds_cli_ServerPasswordSet(context, b,
366 if (!NT_STATUS_IS_OK(status)) {
367 DEBUG(0,("%s : %s(%s) remote password change set with %s failed - %s\n",
368 current_timestring(talloc_tos(), false),
369 __func__, domain, context_name,
375 DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
376 current_timestring(talloc_tos(), false),
377 __func__, domain, context_name));
379 ok = cli_credentials_set_utf16_password(creds,
383 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
386 return NT_STATUS_NO_MEMORY;
389 current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
390 if (current_nt_hash == NULL) {
391 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
394 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
398 * Now we verify the new password.
402 nt_hashes[idx++] = current_nt_hash;
404 status = netlogon_creds_cli_auth(context, b,
408 if (!NT_STATUS_IS_OK(status)) {
409 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
410 context_name, nt_errstr(status)));
415 DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
416 current_timestring(talloc_tos(), false),
417 __func__, domain, context_name));