s3:trusts_util: pass dcname to trust_pw_change()
[Samba.git] / source3 / libsmb / trusts_util.c
blob47b79b7336955dca1e3d52fce4de74bb09a827ff
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 "secrets.h"
28 #include "passdb.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;
41 char *g_lock_key;
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);
47 return 0;
50 char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
51 enum netr_SchannelType sec_channel_type,
52 int security)
55 * use secure defaults.
57 size_t min = 128;
58 size_t max = 255;
60 switch (sec_channel_type) {
61 case SEC_CHAN_WKSTA:
62 case SEC_CHAN_BDC:
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.
69 min = 14;
70 max = 14;
72 break;
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)
81 min = 120;
82 max = 120;
83 break;
84 /* fall through */
85 case SEC_CHAN_DOMAIN:
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.
91 min = 14;
92 max = 14;
93 break;
94 default:
95 break;
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,
109 const char *domain,
110 const char *dcname,
111 bool force)
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, };
124 int 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;
129 NTSTATUS status;
130 bool ok;
132 state = talloc_zero(frame, struct trust_pw_change_state);
133 if (state == NULL) {
134 TALLOC_FREE(frame);
135 return NT_STATUS_NO_MEMORY;
138 state->g_ctx = g_lock_ctx_init(state, msg_ctx);
139 if (state->g_ctx == NULL) {
140 TALLOC_FREE(frame);
141 return NT_STATUS_NO_MEMORY;
144 state->g_lock_key = talloc_asprintf(state,
145 "trust_password_change_%s",
146 domain);
147 if (state->g_lock_key == NULL) {
148 TALLOC_FREE(frame);
149 return NT_STATUS_NO_MEMORY;
152 g_timeout = timeval_current_ofs(10, 0);
153 status = g_lock_lock(state->g_ctx,
154 state->g_lock_key,
155 G_LOCK_WRITE, g_timeout);
156 if (!NT_STATUS_IS_OK(status)) {
157 DEBUG(1, ("could not get g_lock on [%s]!\n",
158 state->g_lock_key));
159 TALLOC_FREE(frame);
160 return status;
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)));
169 TALLOC_FREE(frame);
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",
176 domain));
177 TALLOC_FREE(frame);
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) {
188 case SEC_CHAN_WKSTA:
189 case SEC_CHAN_BDC:
190 break;
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)));
197 TALLOC_FREE(frame);
198 return status;
201 new_trust_version = &new_version;
202 break;
203 default:
204 TALLOC_FREE(frame);
205 return NT_STATUS_NOT_SUPPORTED;
208 timeout = lp_machine_password_timeout();
209 if (timeout == 0) {
210 if (!force) {
211 DEBUG(10,("machine password never expires\n"));
212 TALLOC_FREE(frame);
213 return NT_STATUS_OK;
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)) {
225 TALLOC_FREE(frame);
226 return NT_STATUS_OK;
229 context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
230 if (context_name == NULL) {
231 TALLOC_FREE(frame);
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,
241 lp_security());
242 if (new_trust_passwd == NULL) {
243 DEBUG(0, ("trust_pw_new_value() failed\n"));
244 TALLOC_FREE(frame);
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.
255 * TODO:
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,
262 *current_nt_hash,
263 previous_nt_hash);
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)));
267 TALLOC_FREE(frame);
268 return 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) {
282 case SEC_CHAN_WKSTA:
283 case SEC_CHAN_BDC:
284 ok = secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type);
285 if (!ok) {
286 DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n",
287 domain));
288 TALLOC_FREE(frame);
289 return NT_STATUS_INTERNAL_DB_CORRUPTION;
291 break;
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);
301 if (!ok) {
302 DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
303 domain));
304 TALLOC_FREE(frame);
305 return NT_STATUS_INTERNAL_DB_CORRUPTION;
307 break;
309 default:
310 smb_panic("Unsupported secure channel type");
311 break;
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,
318 new_trust_passwd,
319 new_trust_version);
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,
324 nt_errstr(status)));
325 TALLOC_FREE(frame);
326 return status;
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);
334 if (!ok) {
335 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
336 domain));
337 TALLOC_FREE(frame);
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",
344 domain));
345 TALLOC_FREE(frame);
346 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
350 * Now we verify the new password.
352 status = netlogon_creds_cli_auth(context, b,
353 *current_nt_hash,
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)));
358 TALLOC_FREE(frame);
359 return 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));
366 TALLOC_FREE(frame);
367 return NT_STATUS_OK;