ff7f256eb775bec7f24edd5f19b0b35c0b4bfb27
[samba.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 char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
51                          enum netr_SchannelType sec_channel_type,
52                          int security)
53 {
54         /*
55          * use secure defaults.
56          */
57         size_t min = 128;
58         size_t max = 255;
59
60         switch (sec_channel_type) {
61         case SEC_CHAN_WKSTA:
62         case SEC_CHAN_BDC:
63                 if (security == SEC_DOMAIN) {
64                         /*
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.
68                          */
69                         min = 14;
70                         max = 14;
71                 }
72                 break;
73         case SEC_CHAN_DNS_DOMAIN:
74                 /*
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.
78                  *
79                  * Windows uses new_len = 120 => 240 bytes (utf16)
80                  */
81                 min = 120;
82                 max = 120;
83                 break;
84                 /* fall through */
85         case SEC_CHAN_DOMAIN:
86                 /*
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.
90                  */
91                 min = 14;
92                 max = 14;
93                 break;
94         default:
95                 break;
96         }
97
98         /*
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.
102          */
103         return generate_random_machine_password(mem_ctx, min, max);
104 }
105
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)
112 {
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;
120         uint8_t idx = 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, };
129         int timeout = 0;
130         struct timeval tv = { 0, };
131         char *new_trust_pw_str = NULL;
132         size_t len = 0;
133         DATA_BLOB new_trust_pw_blob = data_blob_null;
134         uint32_t new_version = 0;
135         uint32_t *new_trust_version = NULL;
136         NTSTATUS status;
137         bool ok;
138
139         state = talloc_zero(frame, struct trust_pw_change_state);
140         if (state == NULL) {
141                 TALLOC_FREE(frame);
142                 return NT_STATUS_NO_MEMORY;
143         }
144
145         state->g_ctx = g_lock_ctx_init(state, msg_ctx);
146         if (state->g_ctx == NULL) {
147                 TALLOC_FREE(frame);
148                 return NT_STATUS_NO_MEMORY;
149         }
150
151         state->g_lock_key = talloc_asprintf(state,
152                                 "trust_password_change_%s",
153                                 domain);
154         if (state->g_lock_key == NULL) {
155                 TALLOC_FREE(frame);
156                 return NT_STATUS_NO_MEMORY;
157         }
158
159         g_timeout = timeval_current_ofs(10, 0);
160         status = g_lock_lock(state->g_ctx,
161                              state->g_lock_key,
162                              G_LOCK_WRITE, g_timeout);
163         if (!NT_STATUS_IS_OK(status)) {
164                 DEBUG(1, ("could not get g_lock on [%s]!\n",
165                           state->g_lock_key));
166                 TALLOC_FREE(frame);
167                 return status;
168         }
169
170         talloc_set_destructor(state, trust_pw_change_state_destructor);
171
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)));
176                 TALLOC_FREE(frame);
177                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
178         }
179
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",
183                           domain));
184                 TALLOC_FREE(frame);
185                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
186         }
187         previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
188
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);
192
193         new_version = old_version + 1;
194
195         switch (sec_channel_type) {
196         case SEC_CHAN_WKSTA:
197         case SEC_CHAN_BDC:
198                 break;
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)));
205                         TALLOC_FREE(frame);
206                         return status;
207                 }
208
209                 new_trust_version = &new_version;
210                 break;
211         default:
212                 TALLOC_FREE(frame);
213                 return NT_STATUS_NOT_SUPPORTED;
214         }
215
216         timeout = lp_machine_password_timeout();
217         if (timeout == 0) {
218                 if (!force) {
219                         DEBUG(10,("machine password never expires\n"));
220                         TALLOC_FREE(frame);
221                         return NT_STATUS_OK;
222                 }
223         }
224
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)));
231
232         if (!force && !timeval_expired(&tv)) {
233                 TALLOC_FREE(frame);
234                 return NT_STATUS_OK;
235         }
236
237         context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
238         if (context_name == NULL) {
239                 TALLOC_FREE(frame);
240                 return NT_STATUS_NO_MEMORY;
241         }
242
243         /*
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.
247          */
248         new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
249                                               lp_security());
250         if (new_trust_pw_str == NULL) {
251                 DEBUG(0, ("trust_pw_new_value() failed\n"));
252                 TALLOC_FREE(frame);
253                 return NT_STATUS_NO_MEMORY;
254         }
255
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);
261         if (!ok) {
262                 status = NT_STATUS_UNMAPPABLE_CHARACTER;
263                 if (errno == ENOMEM) {
264                         status = NT_STATUS_NO_MEMORY;
265                 }
266                 DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
267                         "failed for of %s - %s\n",
268                         domain, nt_errstr(status));
269                 TALLOC_FREE(frame);
270                 return status;
271         }
272
273         idx_current = idx;
274         nt_hashes[idx++] = current_nt_hash;
275         if (previous_nt_hash != NULL) {
276                 nt_hashes[idx++] = previous_nt_hash;
277         }
278         num_nt_hashes = idx;
279
280         DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
281                  current_timestring(talloc_tos(), false),
282                  __func__, domain, context_name));
283
284         /*
285          * Check which password the dc knows about.
286          *
287          * TODO:
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.
292          */
293         status = netlogon_creds_cli_auth(context, b,
294                                          num_nt_hashes,
295                                          nt_hashes,
296                                          &idx_nt_hashes);
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)));
300                 TALLOC_FREE(frame);
301                 return status;
302         }
303
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));
309                 TALLOC_FREE(frame);
310                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
311         }
312
313         DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
314                  current_timestring(talloc_tos(), false),
315                  __func__, domain, context_name));
316
317         /*
318          * Return the result of trying to write the new password
319          * back into the trust account file.
320          */
321
322         switch (sec_channel_type) {
323
324         case SEC_CHAN_WKSTA:
325         case SEC_CHAN_BDC:
326                 ok = secrets_store_machine_password(new_trust_pw_str,
327                                                     domain,
328                                                     sec_channel_type);
329                 if (!ok) {
330                         DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n",
331                                   domain));
332                         TALLOC_FREE(frame);
333                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
334                 }
335                 TALLOC_FREE(new_trust_pw_str);
336                 break;
337
338         case SEC_CHAN_DNS_DOMAIN:
339         case SEC_CHAN_DOMAIN:
340                 /*
341                  * we need to get the sid first for the
342                  * pdb_set_trusteddom_pw call
343                  */
344                 ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str,
345                                            &td->security_identifier);
346                 if (!ok) {
347                         DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
348                                   domain));
349                         TALLOC_FREE(frame);
350                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
351                 }
352                 TALLOC_FREE(new_trust_pw_str);
353                 break;
354
355         default:
356                 smb_panic("Unsupported secure channel type");
357                 break;
358         }
359
360         DEBUG(0,("%s : %s(%s): Changed password locally\n",
361                  current_timestring(talloc_tos(), false), __func__, domain));
362
363         status = netlogon_creds_cli_ServerPasswordSet(context, b,
364                                                       &new_trust_pw_blob,
365                                                       new_trust_version);
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,
370                          nt_errstr(status)));
371                 TALLOC_FREE(frame);
372                 return status;
373         }
374
375         DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
376                  current_timestring(talloc_tos(), false),
377                  __func__, domain, context_name));
378
379         ok = cli_credentials_set_utf16_password(creds,
380                                                 &new_trust_pw_blob,
381                                                 CRED_SPECIFIED);
382         if (!ok) {
383                 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
384                           domain));
385                 TALLOC_FREE(frame);
386                 return NT_STATUS_NO_MEMORY;
387         }
388
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",
392                           domain));
393                 TALLOC_FREE(frame);
394                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
395         }
396
397         /*
398          * Now we verify the new password.
399          */
400         idx = 0;
401         idx_current = idx;
402         nt_hashes[idx++] = current_nt_hash;
403         num_nt_hashes = idx;
404         status = netlogon_creds_cli_auth(context, b,
405                                          num_nt_hashes,
406                                          nt_hashes,
407                                          &idx_nt_hashes);
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)));
411                 TALLOC_FREE(frame);
412                 return status;
413         }
414
415         DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
416                  current_timestring(talloc_tos(), false),
417                  __func__, domain, context_name));
418
419         TALLOC_FREE(frame);
420         return NT_STATUS_OK;
421 }