s3:libsmb: let trust_pw_change() verify the new password at the end.
[nivanova/samba-autobuild/.git] / source3 / libsmb / trusts_util.c
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 "secrets.h"
28 #include "passdb.h"
29 #include "libsmb/libsmb.h"
30 #include "source3/include/messages.h"
31 #include "source3/include/g_lock.h"
32
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 **********************************************************/
38
39 struct trust_pw_change_state {
40         struct g_lock_ctx *g_ctx;
41         char *g_lock_key;
42 };
43
44 static int trust_pw_change_state_destructor(struct trust_pw_change_state *state)
45 {
46         g_lock_unlock(state->g_ctx, state->g_lock_key);
47         return 0;
48 }
49
50 NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
51                          struct messaging_context *msg_ctx,
52                          struct dcerpc_binding_handle *b,
53                          const char *domain,
54                          bool force)
55 {
56         TALLOC_CTX *frame = talloc_stackframe();
57         const char *context_name = NULL;
58         struct trust_pw_change_state *state;
59         struct cli_credentials *creds = NULL;
60         const struct samr_Password *current_nt_hash = NULL;
61         const struct samr_Password *previous_nt_hash = NULL;
62         enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
63         time_t pass_last_set_time;
64         uint32_t old_version = 0;
65         struct pdb_trusted_domain *td = NULL;
66         struct timeval g_timeout = { 0, };
67         int timeout = 0;
68         struct timeval tv = { 0, };
69         size_t new_len = DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH;
70         uint8_t new_password_buffer[256 * 2] = { 0, };
71         char *new_trust_passwd = NULL;
72         size_t len = 0;
73         uint32_t new_version = 0;
74         uint32_t *new_trust_version = NULL;
75         NTSTATUS status;
76         bool ok;
77
78         state = talloc_zero(frame, struct trust_pw_change_state);
79         if (state == NULL) {
80                 TALLOC_FREE(frame);
81                 return NT_STATUS_NO_MEMORY;
82         }
83
84         state->g_ctx = g_lock_ctx_init(state, msg_ctx);
85         if (state->g_ctx == NULL) {
86                 TALLOC_FREE(frame);
87                 return NT_STATUS_NO_MEMORY;
88         }
89
90         state->g_lock_key = talloc_asprintf(state,
91                                 "trust_password_change_%s",
92                                 domain);
93         if (state->g_lock_key == NULL) {
94                 TALLOC_FREE(frame);
95                 return NT_STATUS_NO_MEMORY;
96         }
97
98         g_timeout = timeval_current_ofs(10, 0);
99         status = g_lock_lock(state->g_ctx,
100                              state->g_lock_key,
101                              G_LOCK_WRITE, g_timeout);
102         if (!NT_STATUS_IS_OK(status)) {
103                 DEBUG(1, ("could not get g_lock on [%s]!\n",
104                           state->g_lock_key));
105                 TALLOC_FREE(frame);
106                 return status;
107         }
108
109         talloc_set_destructor(state, trust_pw_change_state_destructor);
110
111         status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
112         if (!NT_STATUS_IS_OK(status)) {
113                 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
114                           domain, nt_errstr(status)));
115                 TALLOC_FREE(frame);
116                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
117         }
118
119         current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
120         if (current_nt_hash == NULL) {
121                 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
122                           domain));
123                 TALLOC_FREE(frame);
124                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
125         }
126
127         old_version = cli_credentials_get_kvno(creds);
128         pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
129         sec_channel_type = cli_credentials_get_secure_channel_type(creds);
130
131         new_version = old_version + 1;
132
133         switch (sec_channel_type) {
134         case SEC_CHAN_WKSTA:
135         case SEC_CHAN_BDC:
136                 break;
137         case SEC_CHAN_DNS_DOMAIN:
138                 /*
139                  * new_len * 2 = 498 bytes is the largest possible length
140                  * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
141                  * and a confounder with at least 2 bytes is required.
142                  *
143                  * Windows uses new_len = 120 => 240 bytes.
144                  */
145                 new_len = 120;
146
147                 /* fall through */
148         case SEC_CHAN_DOMAIN:
149                 status = pdb_get_trusted_domain(frame, domain, &td);
150                 if (!NT_STATUS_IS_OK(status)) {
151                         DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
152                                   domain, nt_errstr(status)));
153                         TALLOC_FREE(frame);
154                         return status;
155                 }
156
157                 new_trust_version = &new_version;
158                 break;
159         default:
160                 TALLOC_FREE(frame);
161                 return NT_STATUS_NOT_SUPPORTED;
162         }
163
164         timeout = lp_machine_password_timeout();
165         if (timeout == 0) {
166                 if (!force) {
167                         DEBUG(10,("machine password never expires\n"));
168                         TALLOC_FREE(frame);
169                         return NT_STATUS_OK;
170                 }
171         }
172
173         tv.tv_sec = pass_last_set_time;
174         DEBUG(10, ("password last changed %s\n",
175                    timeval_string(talloc_tos(), &tv, false)));
176         tv.tv_sec += timeout;
177         DEBUGADD(10, ("password valid until %s\n",
178                       timeval_string(talloc_tos(), &tv, false)));
179
180         if (!force && !timeval_expired(&tv)) {
181                 TALLOC_FREE(frame);
182                 return NT_STATUS_OK;
183         }
184
185         context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
186         if (context_name == NULL) {
187                 TALLOC_FREE(frame);
188                 return NT_STATUS_NO_MEMORY;
189         }
190
191         /*
192          * Create a random machine account password
193          * We create a random buffer and convert that to utf8.
194          * This is similar to what windows is doing.
195          */
196         generate_secret_buffer(new_password_buffer, new_len * 2);
197         ok = convert_string_talloc(frame,
198                                    CH_UTF16MUNGED, CH_UTF8,
199                                    new_password_buffer, new_len * 2,
200                                    (void *)&new_trust_passwd, &len);
201         ZERO_STRUCT(new_password_buffer);
202         if (!ok) {
203                 DEBUG(0, ("convert_string_talloc failed\n"));
204                 TALLOC_FREE(frame);
205                 return NT_STATUS_NO_MEMORY;
206         }
207
208         /*
209          * We could use cli_credentials_get_old_nt_hash(creds, frame) to
210          * set previous_nt_hash.
211          *
212          * But we want to check if the dc has our current password and only do
213          * a change if that's the case. So we keep previous_nt_hash = NULL.
214          *
215          * TODO:
216          * If the previous password is the only password in common with the dc,
217          * we better skip the password change, or use something like
218          * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
219          * local secrets before doing the change.
220          */
221         status = netlogon_creds_cli_auth(context, b,
222                                          *current_nt_hash,
223                                          previous_nt_hash);
224         if (!NT_STATUS_IS_OK(status)) {
225                 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old password - %s!\n",
226                           context_name, nt_errstr(status)));
227                 TALLOC_FREE(frame);
228                 return status;
229         }
230
231         DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
232                  current_timestring(talloc_tos(), false),
233                  __func__, domain, context_name));
234
235         /*
236          * Return the result of trying to write the new password
237          * back into the trust account file.
238          */
239
240         switch (sec_channel_type) {
241
242         case SEC_CHAN_WKSTA:
243         case SEC_CHAN_BDC:
244                 ok = secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type);
245                 if (!ok) {
246                         DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n",
247                                   domain));
248                         TALLOC_FREE(frame);
249                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
250                 }
251                 break;
252
253         case SEC_CHAN_DNS_DOMAIN:
254         case SEC_CHAN_DOMAIN:
255                 /*
256                  * we need to get the sid first for the
257                  * pdb_set_trusteddom_pw call
258                  */
259                 ok = pdb_set_trusteddom_pw(domain, new_trust_passwd,
260                                            &td->security_identifier);
261                 if (!ok) {
262                         DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
263                                   domain));
264                         TALLOC_FREE(frame);
265                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
266                 }
267                 break;
268
269         default:
270                 smb_panic("Unsupported secure channel type");
271                 break;
272         }
273
274         DEBUG(0,("%s : %s(%s): Changed password locally\n",
275                  current_timestring(talloc_tos(), false), __func__, domain));
276
277         status = netlogon_creds_cli_ServerPasswordSet(context, b,
278                                                       new_trust_passwd,
279                                                       new_trust_version);
280         if (!NT_STATUS_IS_OK(status)) {
281                 DEBUG(0,("%s : %s(%s) remote password change set with %s failed - %s\n",
282                          current_timestring(talloc_tos(), false),
283                          __func__, domain, context_name,
284                          nt_errstr(status)));
285                 TALLOC_FREE(frame);
286                 return status;
287         }
288
289         DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
290                  current_timestring(talloc_tos(), false),
291                  __func__, domain, context_name));
292
293         ok = cli_credentials_set_password(creds, new_trust_passwd, CRED_SPECIFIED);
294         if (!ok) {
295                 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
296                           domain));
297                 TALLOC_FREE(frame);
298                 return NT_STATUS_NO_MEMORY;
299         }
300
301         current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
302         if (current_nt_hash == NULL) {
303                 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
304                           domain));
305                 TALLOC_FREE(frame);
306                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
307         }
308
309         /*
310          * Now we verify the new password.
311          */
312         status = netlogon_creds_cli_auth(context, b,
313                                          *current_nt_hash,
314                                          NULL); /* previous_nt_hash */
315         if (!NT_STATUS_IS_OK(status)) {
316                 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
317                           context_name, nt_errstr(status)));
318                 TALLOC_FREE(frame);
319                 return status;
320         }
321
322         DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
323                  current_timestring(talloc_tos(), false),
324                  __func__, domain, context_name));
325
326         TALLOC_FREE(frame);
327         return NT_STATUS_OK;
328 }