s3:libsmb: add trust_pw_new_value() helper function
[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 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                          bool force)
111 {
112         TALLOC_CTX *frame = talloc_stackframe();
113         const char *context_name = NULL;
114         struct trust_pw_change_state *state;
115         struct cli_credentials *creds = NULL;
116         const struct samr_Password *current_nt_hash = NULL;
117         const struct samr_Password *previous_nt_hash = NULL;
118         enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
119         time_t pass_last_set_time;
120         uint32_t old_version = 0;
121         struct pdb_trusted_domain *td = NULL;
122         struct timeval g_timeout = { 0, };
123         int timeout = 0;
124         struct timeval tv = { 0, };
125         size_t new_len = DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH;
126         uint8_t new_password_buffer[256 * 2] = { 0, };
127         char *new_trust_passwd = NULL;
128         size_t len = 0;
129         uint32_t new_version = 0;
130         uint32_t *new_trust_version = NULL;
131         NTSTATUS status;
132         bool ok;
133
134         state = talloc_zero(frame, struct trust_pw_change_state);
135         if (state == NULL) {
136                 TALLOC_FREE(frame);
137                 return NT_STATUS_NO_MEMORY;
138         }
139
140         state->g_ctx = g_lock_ctx_init(state, msg_ctx);
141         if (state->g_ctx == NULL) {
142                 TALLOC_FREE(frame);
143                 return NT_STATUS_NO_MEMORY;
144         }
145
146         state->g_lock_key = talloc_asprintf(state,
147                                 "trust_password_change_%s",
148                                 domain);
149         if (state->g_lock_key == NULL) {
150                 TALLOC_FREE(frame);
151                 return NT_STATUS_NO_MEMORY;
152         }
153
154         g_timeout = timeval_current_ofs(10, 0);
155         status = g_lock_lock(state->g_ctx,
156                              state->g_lock_key,
157                              G_LOCK_WRITE, g_timeout);
158         if (!NT_STATUS_IS_OK(status)) {
159                 DEBUG(1, ("could not get g_lock on [%s]!\n",
160                           state->g_lock_key));
161                 TALLOC_FREE(frame);
162                 return status;
163         }
164
165         talloc_set_destructor(state, trust_pw_change_state_destructor);
166
167         status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
168         if (!NT_STATUS_IS_OK(status)) {
169                 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
170                           domain, nt_errstr(status)));
171                 TALLOC_FREE(frame);
172                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
173         }
174
175         current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
176         if (current_nt_hash == NULL) {
177                 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
178                           domain));
179                 TALLOC_FREE(frame);
180                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
181         }
182
183         old_version = cli_credentials_get_kvno(creds);
184         pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
185         sec_channel_type = cli_credentials_get_secure_channel_type(creds);
186
187         new_version = old_version + 1;
188
189         switch (sec_channel_type) {
190         case SEC_CHAN_WKSTA:
191         case SEC_CHAN_BDC:
192                 break;
193         case SEC_CHAN_DNS_DOMAIN:
194                 /*
195                  * new_len * 2 = 498 bytes is the largest possible length
196                  * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
197                  * and a confounder with at least 2 bytes is required.
198                  *
199                  * Windows uses new_len = 120 => 240 bytes.
200                  */
201                 new_len = 120;
202
203                 /* fall through */
204         case SEC_CHAN_DOMAIN:
205                 status = pdb_get_trusted_domain(frame, domain, &td);
206                 if (!NT_STATUS_IS_OK(status)) {
207                         DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
208                                   domain, nt_errstr(status)));
209                         TALLOC_FREE(frame);
210                         return status;
211                 }
212
213                 new_trust_version = &new_version;
214                 break;
215         default:
216                 TALLOC_FREE(frame);
217                 return NT_STATUS_NOT_SUPPORTED;
218         }
219
220         timeout = lp_machine_password_timeout();
221         if (timeout == 0) {
222                 if (!force) {
223                         DEBUG(10,("machine password never expires\n"));
224                         TALLOC_FREE(frame);
225                         return NT_STATUS_OK;
226                 }
227         }
228
229         tv.tv_sec = pass_last_set_time;
230         DEBUG(10, ("password last changed %s\n",
231                    timeval_string(talloc_tos(), &tv, false)));
232         tv.tv_sec += timeout;
233         DEBUGADD(10, ("password valid until %s\n",
234                       timeval_string(talloc_tos(), &tv, false)));
235
236         if (!force && !timeval_expired(&tv)) {
237                 TALLOC_FREE(frame);
238                 return NT_STATUS_OK;
239         }
240
241         context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
242         if (context_name == NULL) {
243                 TALLOC_FREE(frame);
244                 return NT_STATUS_NO_MEMORY;
245         }
246
247         /*
248          * Create a random machine account password
249          * We create a random buffer and convert that to utf8.
250          * This is similar to what windows is doing.
251          */
252         generate_secret_buffer(new_password_buffer, new_len * 2);
253         ok = convert_string_talloc(frame,
254                                    CH_UTF16MUNGED, CH_UTF8,
255                                    new_password_buffer, new_len * 2,
256                                    (void *)&new_trust_passwd, &len);
257         ZERO_STRUCT(new_password_buffer);
258         if (!ok) {
259                 DEBUG(0, ("convert_string_talloc failed\n"));
260                 TALLOC_FREE(frame);
261                 return NT_STATUS_NO_MEMORY;
262         }
263
264         /*
265          * We could use cli_credentials_get_old_nt_hash(creds, frame) to
266          * set previous_nt_hash.
267          *
268          * But we want to check if the dc has our current password and only do
269          * a change if that's the case. So we keep previous_nt_hash = NULL.
270          *
271          * TODO:
272          * If the previous password is the only password in common with the dc,
273          * we better skip the password change, or use something like
274          * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
275          * local secrets before doing the change.
276          */
277         status = netlogon_creds_cli_auth(context, b,
278                                          *current_nt_hash,
279                                          previous_nt_hash);
280         if (!NT_STATUS_IS_OK(status)) {
281                 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old password - %s!\n",
282                           context_name, nt_errstr(status)));
283                 TALLOC_FREE(frame);
284                 return status;
285         }
286
287         DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
288                  current_timestring(talloc_tos(), false),
289                  __func__, domain, context_name));
290
291         /*
292          * Return the result of trying to write the new password
293          * back into the trust account file.
294          */
295
296         switch (sec_channel_type) {
297
298         case SEC_CHAN_WKSTA:
299         case SEC_CHAN_BDC:
300                 ok = secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type);
301                 if (!ok) {
302                         DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n",
303                                   domain));
304                         TALLOC_FREE(frame);
305                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
306                 }
307                 break;
308
309         case SEC_CHAN_DNS_DOMAIN:
310         case SEC_CHAN_DOMAIN:
311                 /*
312                  * we need to get the sid first for the
313                  * pdb_set_trusteddom_pw call
314                  */
315                 ok = pdb_set_trusteddom_pw(domain, new_trust_passwd,
316                                            &td->security_identifier);
317                 if (!ok) {
318                         DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
319                                   domain));
320                         TALLOC_FREE(frame);
321                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
322                 }
323                 break;
324
325         default:
326                 smb_panic("Unsupported secure channel type");
327                 break;
328         }
329
330         DEBUG(0,("%s : %s(%s): Changed password locally\n",
331                  current_timestring(talloc_tos(), false), __func__, domain));
332
333         status = netlogon_creds_cli_ServerPasswordSet(context, b,
334                                                       new_trust_passwd,
335                                                       new_trust_version);
336         if (!NT_STATUS_IS_OK(status)) {
337                 DEBUG(0,("%s : %s(%s) remote password change set with %s failed - %s\n",
338                          current_timestring(talloc_tos(), false),
339                          __func__, domain, context_name,
340                          nt_errstr(status)));
341                 TALLOC_FREE(frame);
342                 return status;
343         }
344
345         DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
346                  current_timestring(talloc_tos(), false),
347                  __func__, domain, context_name));
348
349         ok = cli_credentials_set_password(creds, new_trust_passwd, CRED_SPECIFIED);
350         if (!ok) {
351                 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
352                           domain));
353                 TALLOC_FREE(frame);
354                 return NT_STATUS_NO_MEMORY;
355         }
356
357         current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
358         if (current_nt_hash == NULL) {
359                 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
360                           domain));
361                 TALLOC_FREE(frame);
362                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
363         }
364
365         /*
366          * Now we verify the new password.
367          */
368         status = netlogon_creds_cli_auth(context, b,
369                                          *current_nt_hash,
370                                          NULL); /* previous_nt_hash */
371         if (!NT_STATUS_IS_OK(status)) {
372                 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
373                           context_name, nt_errstr(status)));
374                 TALLOC_FREE(frame);
375                 return status;
376         }
377
378         DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
379                  current_timestring(talloc_tos(), false),
380                  __func__, domain, context_name));
381
382         TALLOC_FREE(frame);
383         return NT_STATUS_OK;
384 }