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 enum netr_SchannelType sec_channel_type
= SEC_CHAN_NULL
;
120 time_t pass_last_set_time
;
121 uint32_t old_version
= 0;
122 struct pdb_trusted_domain
*td
= NULL
;
123 struct timeval g_timeout
= { 0, };
125 struct timeval tv
= { 0, };
126 char *new_trust_passwd
= NULL
;
127 uint32_t new_version
= 0;
128 uint32_t *new_trust_version
= NULL
;
132 state
= talloc_zero(frame
, struct trust_pw_change_state
);
135 return NT_STATUS_NO_MEMORY
;
138 state
->g_ctx
= g_lock_ctx_init(state
, msg_ctx
);
139 if (state
->g_ctx
== NULL
) {
141 return NT_STATUS_NO_MEMORY
;
144 state
->g_lock_key
= talloc_asprintf(state
,
145 "trust_password_change_%s",
147 if (state
->g_lock_key
== NULL
) {
149 return NT_STATUS_NO_MEMORY
;
152 g_timeout
= timeval_current_ofs(10, 0);
153 status
= g_lock_lock(state
->g_ctx
,
155 G_LOCK_WRITE
, g_timeout
);
156 if (!NT_STATUS_IS_OK(status
)) {
157 DEBUG(1, ("could not get g_lock on [%s]!\n",
163 talloc_set_destructor(state
, trust_pw_change_state_destructor
);
165 status
= pdb_get_trust_credentials(domain
, NULL
, frame
, &creds
);
166 if (!NT_STATUS_IS_OK(status
)) {
167 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
168 domain
, nt_errstr(status
)));
170 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
;
173 current_nt_hash
= cli_credentials_get_nt_hash(creds
, frame
);
174 if (current_nt_hash
== NULL
) {
175 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
178 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
;
181 old_version
= cli_credentials_get_kvno(creds
);
182 pass_last_set_time
= cli_credentials_get_password_last_changed_time(creds
);
183 sec_channel_type
= cli_credentials_get_secure_channel_type(creds
);
185 new_version
= old_version
+ 1;
187 switch (sec_channel_type
) {
191 case SEC_CHAN_DNS_DOMAIN
:
192 case SEC_CHAN_DOMAIN
:
193 status
= pdb_get_trusted_domain(frame
, domain
, &td
);
194 if (!NT_STATUS_IS_OK(status
)) {
195 DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
196 domain
, nt_errstr(status
)));
201 new_trust_version
= &new_version
;
205 return NT_STATUS_NOT_SUPPORTED
;
208 timeout
= lp_machine_password_timeout();
211 DEBUG(10,("machine password never expires\n"));
217 tv
.tv_sec
= pass_last_set_time
;
218 DEBUG(10, ("password last changed %s\n",
219 timeval_string(talloc_tos(), &tv
, false)));
220 tv
.tv_sec
+= timeout
;
221 DEBUGADD(10, ("password valid until %s\n",
222 timeval_string(talloc_tos(), &tv
, false)));
224 if (!force
&& !timeval_expired(&tv
)) {
229 context_name
= netlogon_creds_cli_debug_string(context
, talloc_tos());
230 if (context_name
== NULL
) {
232 return NT_STATUS_NO_MEMORY
;
236 * Create a random machine account password
237 * We create a random buffer and convert that to utf8.
238 * This is similar to what windows is doing.
240 new_trust_passwd
= trust_pw_new_value(frame
, sec_channel_type
,
242 if (new_trust_passwd
== NULL
) {
243 DEBUG(0, ("trust_pw_new_value() failed\n"));
245 return NT_STATUS_NO_MEMORY
;
249 * We could use cli_credentials_get_old_nt_hash(creds, frame) to
250 * set previous_nt_hash.
252 * But we want to check if the dc has our current password and only do
253 * a change if that's the case. So we keep previous_nt_hash = NULL.
256 * If the previous password is the only password in common with the dc,
257 * we better skip the password change, or use something like
258 * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
259 * local secrets before doing the change.
261 status
= netlogon_creds_cli_auth(context
, b
,
264 if (!NT_STATUS_IS_OK(status
)) {
265 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old password - %s!\n",
266 context_name
, nt_errstr(status
)));
271 DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
272 current_timestring(talloc_tos(), false),
273 __func__
, domain
, context_name
));
276 * Return the result of trying to write the new password
277 * back into the trust account file.
280 switch (sec_channel_type
) {
284 ok
= secrets_store_machine_password(new_trust_passwd
, domain
, sec_channel_type
);
286 DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n",
289 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
293 case SEC_CHAN_DNS_DOMAIN
:
294 case SEC_CHAN_DOMAIN
:
296 * we need to get the sid first for the
297 * pdb_set_trusteddom_pw call
299 ok
= pdb_set_trusteddom_pw(domain
, new_trust_passwd
,
300 &td
->security_identifier
);
302 DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
305 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
310 smb_panic("Unsupported secure channel type");
314 DEBUG(0,("%s : %s(%s): Changed password locally\n",
315 current_timestring(talloc_tos(), false), __func__
, domain
));
317 status
= netlogon_creds_cli_ServerPasswordSet(context
, b
,
320 if (!NT_STATUS_IS_OK(status
)) {
321 DEBUG(0,("%s : %s(%s) remote password change set with %s failed - %s\n",
322 current_timestring(talloc_tos(), false),
323 __func__
, domain
, context_name
,
329 DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
330 current_timestring(talloc_tos(), false),
331 __func__
, domain
, context_name
));
333 ok
= cli_credentials_set_password(creds
, new_trust_passwd
, CRED_SPECIFIED
);
335 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
338 return NT_STATUS_NO_MEMORY
;
341 current_nt_hash
= cli_credentials_get_nt_hash(creds
, frame
);
342 if (current_nt_hash
== NULL
) {
343 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
346 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
;
350 * Now we verify the new password.
352 status
= netlogon_creds_cli_auth(context
, b
,
354 NULL
); /* previous_nt_hash */
355 if (!NT_STATUS_IS_OK(status
)) {
356 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
357 context_name
, nt_errstr(status
)));
362 DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
363 current_timestring(talloc_tos(), false),
364 __func__
, domain
, context_name
));