s3/libsmb: adjust smb2 code for new idl structs & generated ndr push/pull funcs.
[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 "librpc/gen_ndr/secrets.h"
28 #include "secrets.h"
29 #include "passdb.h"
30 #include "libsmb/libsmb.h"
31 #include "source3/include/messages.h"
32 #include "source3/include/g_lock.h"
33 #include "lib/util/util_tdb.h"
34
35 /*********************************************************
36  Change the domain password on the PDC.
37  Do most of the legwork ourselfs.  Caller must have
38  already setup the connection to the NETLOGON pipe
39 **********************************************************/
40
41 struct trust_pw_change_state {
42         struct g_lock_ctx *g_ctx;
43         char *g_lock_key;
44 };
45
46 static int trust_pw_change_state_destructor(struct trust_pw_change_state *state)
47 {
48         g_lock_unlock(state->g_ctx,
49                       string_term_tdb_data(state->g_lock_key));
50         return 0;
51 }
52
53 char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
54                          enum netr_SchannelType sec_channel_type,
55                          int security)
56 {
57         /*
58          * use secure defaults.
59          */
60         size_t min = 128;
61         size_t max = 255;
62
63         switch (sec_channel_type) {
64         case SEC_CHAN_WKSTA:
65         case SEC_CHAN_BDC:
66                 if (security == SEC_DOMAIN) {
67                         /*
68                          * The maximum length of a trust account password.
69                          * Used when we randomly create it, 15 char passwords
70                          * exceed NT4's max password length.
71                          */
72                         min = 14;
73                         max = 14;
74                 }
75                 break;
76         case SEC_CHAN_DNS_DOMAIN:
77                 /*
78                  * new_len * 2 = 498 bytes is the largest possible length
79                  * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
80                  * and a confounder with at least 2 bytes is required.
81                  *
82                  * Windows uses new_len = 120 => 240 bytes (utf16)
83                  */
84                 min = 120;
85                 max = 120;
86                 break;
87         case SEC_CHAN_DOMAIN:
88                 /*
89                  * The maximum length of a trust account password.
90                  * Used when we randomly create it, 15 char passwords
91                  * exceed NT4's max password length.
92                  */
93                 min = 14;
94                 max = 14;
95                 break;
96         default:
97                 break;
98         }
99
100         /*
101          * Create a random machine account password
102          * We create a random buffer and convert that to utf8.
103          * This is similar to what windows is doing.
104          */
105         return generate_random_machine_password(mem_ctx, min, max);
106 }
107
108 /*
109  * Temporary function to wrap cli_auth in a lck
110  */
111
112 static NTSTATUS netlogon_creds_cli_lck_auth(
113         struct netlogon_creds_cli_context *context,
114         struct dcerpc_binding_handle *b,
115         uint8_t num_nt_hashes,
116         const struct samr_Password * const *nt_hashes,
117         uint8_t *idx_nt_hashes)
118 {
119         struct netlogon_creds_cli_lck *lck;
120         NTSTATUS status;
121
122         status = netlogon_creds_cli_lck(
123                 context, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
124                 talloc_tos(), &lck);
125         if (!NT_STATUS_IS_OK(status)) {
126                 DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
127                             nt_errstr(status));
128                 return status;
129         }
130
131         status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes,
132                                          idx_nt_hashes);
133         TALLOC_FREE(lck);
134
135         return status;
136 }
137
138 NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
139                          struct messaging_context *msg_ctx,
140                          struct dcerpc_binding_handle *b,
141                          const char *domain,
142                          const char *dcname,
143                          bool force)
144 {
145         TALLOC_CTX *frame = talloc_stackframe();
146         const char *context_name = NULL;
147         struct trust_pw_change_state *state;
148         struct cli_credentials *creds = NULL;
149         struct secrets_domain_info1 *info = NULL;
150         struct secrets_domain_info1_change *prev = NULL;
151         const struct samr_Password *current_nt_hash = NULL;
152         const struct samr_Password *previous_nt_hash = NULL;
153         uint8_t num_nt_hashes = 0;
154         uint8_t idx = 0;
155         const struct samr_Password *nt_hashes[1+3] = { NULL, };
156         uint8_t idx_nt_hashes = 0;
157         uint8_t idx_current = UINT8_MAX;
158         enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
159         time_t pass_last_set_time;
160         uint32_t old_version = 0;
161         struct pdb_trusted_domain *td = NULL;
162         struct timeval g_timeout = { 0, };
163         int timeout = 0;
164         struct timeval tv = { 0, };
165         char *new_trust_pw_str = NULL;
166         size_t len = 0;
167         DATA_BLOB new_trust_pw_blob = data_blob_null;
168         uint32_t new_version = 0;
169         uint32_t *new_trust_version = NULL;
170         NTSTATUS status;
171         bool ok;
172
173         state = talloc_zero(frame, struct trust_pw_change_state);
174         if (state == NULL) {
175                 TALLOC_FREE(frame);
176                 return NT_STATUS_NO_MEMORY;
177         }
178
179         state->g_ctx = g_lock_ctx_init(state, msg_ctx);
180         if (state->g_ctx == NULL) {
181                 TALLOC_FREE(frame);
182                 return NT_STATUS_NO_MEMORY;
183         }
184
185         state->g_lock_key = talloc_asprintf(state,
186                                 "trust_password_change_%s",
187                                 domain);
188         if (state->g_lock_key == NULL) {
189                 TALLOC_FREE(frame);
190                 return NT_STATUS_NO_MEMORY;
191         }
192
193         g_timeout = timeval_current_ofs(10, 0);
194         status = g_lock_lock(state->g_ctx,
195                              string_term_tdb_data(state->g_lock_key),
196                              G_LOCK_WRITE, g_timeout);
197         if (!NT_STATUS_IS_OK(status)) {
198                 DEBUG(1, ("could not get g_lock on [%s]!\n",
199                           state->g_lock_key));
200                 TALLOC_FREE(frame);
201                 return status;
202         }
203
204         talloc_set_destructor(state, trust_pw_change_state_destructor);
205
206         status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
207         if (!NT_STATUS_IS_OK(status)) {
208                 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
209                           domain, nt_errstr(status)));
210                 TALLOC_FREE(frame);
211                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
212         }
213
214         current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
215         if (current_nt_hash == NULL) {
216                 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
217                           domain));
218                 TALLOC_FREE(frame);
219                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
220         }
221         previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
222
223         old_version = cli_credentials_get_kvno(creds);
224         pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
225         sec_channel_type = cli_credentials_get_secure_channel_type(creds);
226
227         new_version = old_version + 1;
228
229         switch (sec_channel_type) {
230         case SEC_CHAN_WKSTA:
231         case SEC_CHAN_BDC:
232                 break;
233         case SEC_CHAN_DNS_DOMAIN:
234         case SEC_CHAN_DOMAIN:
235                 status = pdb_get_trusted_domain(frame, domain, &td);
236                 if (!NT_STATUS_IS_OK(status)) {
237                         DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
238                                   domain, nt_errstr(status)));
239                         TALLOC_FREE(frame);
240                         return status;
241                 }
242
243                 new_trust_version = &new_version;
244                 break;
245         default:
246                 TALLOC_FREE(frame);
247                 return NT_STATUS_NOT_SUPPORTED;
248         }
249
250         timeout = lp_machine_password_timeout();
251         if (timeout == 0) {
252                 if (!force) {
253                         DEBUG(10,("machine password never expires\n"));
254                         TALLOC_FREE(frame);
255                         return NT_STATUS_OK;
256                 }
257         }
258
259         tv.tv_sec = pass_last_set_time;
260         DEBUG(10, ("password last changed %s\n",
261                    timeval_string(talloc_tos(), &tv, false)));
262         tv.tv_sec += timeout;
263         DEBUGADD(10, ("password valid until %s\n",
264                       timeval_string(talloc_tos(), &tv, false)));
265
266         if (!force && !timeval_expired(&tv)) {
267                 TALLOC_FREE(frame);
268                 return NT_STATUS_OK;
269         }
270
271         context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
272         if (context_name == NULL) {
273                 TALLOC_FREE(frame);
274                 return NT_STATUS_NO_MEMORY;
275         }
276
277         /*
278          * Create a random machine account password
279          * We create a random buffer and convert that to utf8.
280          * This is similar to what windows is doing.
281          */
282         new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
283                                               lp_security());
284         if (new_trust_pw_str == NULL) {
285                 DEBUG(0, ("trust_pw_new_value() failed\n"));
286                 TALLOC_FREE(frame);
287                 return NT_STATUS_NO_MEMORY;
288         }
289
290         len = strlen(new_trust_pw_str);
291         ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16,
292                                    new_trust_pw_str, len,
293                                    (void **)&new_trust_pw_blob.data,
294                                    &new_trust_pw_blob.length);
295         if (!ok) {
296                 status = NT_STATUS_UNMAPPABLE_CHARACTER;
297                 if (errno == ENOMEM) {
298                         status = NT_STATUS_NO_MEMORY;
299                 }
300                 DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
301                         "failed for of %s - %s\n",
302                         domain, nt_errstr(status));
303                 TALLOC_FREE(frame);
304                 return status;
305         }
306
307         switch (sec_channel_type) {
308
309         case SEC_CHAN_WKSTA:
310         case SEC_CHAN_BDC:
311                 status = secrets_prepare_password_change(domain, dcname,
312                                                          new_trust_pw_str,
313                                                          frame, &info, &prev);
314                 if (!NT_STATUS_IS_OK(status)) {
315                         DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
316                                   domain));
317                         TALLOC_FREE(frame);
318                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
319                 }
320                 TALLOC_FREE(new_trust_pw_str);
321
322                 if (prev != NULL) {
323                         /*
324                          * We had a failure before we changed the password.
325                          */
326                         nt_hashes[idx++] = &prev->password->nt_hash;
327
328                         DEBUG(0,("%s : %s(%s): A password change was already "
329                                  "started against '%s' at %s. Trying to "
330                                  "recover...\n",
331                                  current_timestring(talloc_tos(), false),
332                                  __func__, domain,
333                                  prev->password->change_server,
334                                  nt_time_string(talloc_tos(),
335                                  prev->password->change_time)));
336                         DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] "
337                                  "against '%s' at %s.\n",
338                                  current_timestring(talloc_tos(), false),
339                                  __func__, domain,
340                                  nt_errstr(prev->local_status),
341                                  nt_errstr(prev->remote_status),
342                                  prev->change_server,
343                                  nt_time_string(talloc_tos(),
344                                  prev->change_time)));
345                 }
346
347                 idx_current = idx;
348                 nt_hashes[idx++] = &info->password->nt_hash;
349                 if (info->old_password != NULL) {
350                         nt_hashes[idx++] = &info->old_password->nt_hash;
351                 }
352                 if (info->older_password != NULL) {
353                         nt_hashes[idx++] = &info->older_password->nt_hash;
354                 }
355
356                 /*
357                  * We use the password that's already persitent in
358                  * our database in order to handle failures.
359                  */
360                 data_blob_clear_free(&new_trust_pw_blob);
361                 new_trust_pw_blob = info->next_change->password->cleartext_blob;
362                 break;
363
364         case SEC_CHAN_DNS_DOMAIN:
365         case SEC_CHAN_DOMAIN:
366                 idx_current = idx;
367                 nt_hashes[idx++] = current_nt_hash;
368                 if (previous_nt_hash != NULL) {
369                         nt_hashes[idx++] = previous_nt_hash;
370                 }
371                 break;
372
373         default:
374                 smb_panic("Unsupported secure channel type");
375                 break;
376         }
377         num_nt_hashes = idx;
378
379         DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
380                  current_timestring(talloc_tos(), false),
381                  __func__, domain, context_name));
382
383         /*
384          * Check which password the dc knows about.
385          *
386          * TODO:
387          * If the previous password is the only password in common with the dc,
388          * we better skip the password change, or use something like
389          * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
390          * local secrets before doing the change.
391          */
392         status = netlogon_creds_cli_lck_auth(context, b,
393                                              num_nt_hashes,
394                                              nt_hashes,
395                                              &idx_nt_hashes);
396         if (!NT_STATUS_IS_OK(status)) {
397                 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
398                           context_name, num_nt_hashes, nt_errstr(status)));
399                 TALLOC_FREE(frame);
400                 return status;
401         }
402
403         if (prev != NULL && idx_nt_hashes == 0) {
404                 DEBUG(0,("%s : %s(%s): Verified new password remotely "
405                          "without changing %s\n",
406                          current_timestring(talloc_tos(), false),
407                          __func__, domain, context_name));
408
409                 status = secrets_finish_password_change(prev->password->change_server,
410                                                         prev->password->change_time,
411                                                         info);
412                 if (!NT_STATUS_IS_OK(status)) {
413                         DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
414                                   domain));
415                         TALLOC_FREE(frame);
416                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
417                 }
418
419                 DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
420                          current_timestring(talloc_tos(), false),
421                          __func__, domain));
422                 TALLOC_FREE(frame);
423                 return NT_STATUS_OK;
424         }
425
426         if (idx_nt_hashes != idx_current) {
427                 DEBUG(0,("%s : %s(%s): Verified older password remotely "
428                          "skip changing %s\n",
429                          current_timestring(talloc_tos(), false),
430                          __func__, domain, context_name));
431
432                 if (info == NULL) {
433                         TALLOC_FREE(frame);
434                         return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
435                 }
436
437                 status = secrets_defer_password_change(dcname,
438                                         NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE,
439                                         NT_STATUS_NOT_COMMITTED,
440                                         info);
441                 if (!NT_STATUS_IS_OK(status)) {
442                         DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
443                                   domain));
444                         TALLOC_FREE(frame);
445                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
446                 }
447                 TALLOC_FREE(frame);
448                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
449         }
450
451         DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
452                  current_timestring(talloc_tos(), false),
453                  __func__, domain, context_name));
454
455         /*
456          * Return the result of trying to write the new password
457          * back into the trust account file.
458          */
459
460         switch (sec_channel_type) {
461
462         case SEC_CHAN_WKSTA:
463         case SEC_CHAN_BDC:
464                 /*
465                  * we called secrets_prepare_password_change() above.
466                  */
467                 break;
468
469         case SEC_CHAN_DNS_DOMAIN:
470         case SEC_CHAN_DOMAIN:
471                 /*
472                  * we need to get the sid first for the
473                  * pdb_set_trusteddom_pw call
474                  */
475                 ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str,
476                                            &td->security_identifier);
477                 if (!ok) {
478                         DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
479                                   domain));
480                         TALLOC_FREE(frame);
481                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
482                 }
483                 TALLOC_FREE(new_trust_pw_str);
484                 break;
485
486         default:
487                 smb_panic("Unsupported secure channel type");
488                 break;
489         }
490
491         DEBUG(0,("%s : %s(%s): Changed password locally\n",
492                  current_timestring(talloc_tos(), false), __func__, domain));
493
494         status = netlogon_creds_cli_ServerPasswordSet(context, b,
495                                                       &new_trust_pw_blob,
496                                                       new_trust_version);
497         if (!NT_STATUS_IS_OK(status)) {
498                 NTSTATUS status2;
499                 const char *fn = NULL;
500
501                 ok = dcerpc_binding_handle_is_connected(b);
502
503                 DEBUG(0,("%s : %s(%s) remote password change with %s failed "
504                          "- %s (%s)\n",
505                          current_timestring(talloc_tos(), false),
506                          __func__, domain, context_name,
507                          nt_errstr(status),
508                          ok ? "connected": "disconnected"));
509
510                 if (!ok) {
511                         /*
512                          * The connection is broken, we don't
513                          * know if the password was changed,
514                          * we hope to have more luck next time.
515                          */
516                         status2 = secrets_failed_password_change(dcname,
517                                                         NT_STATUS_NOT_COMMITTED,
518                                                         status,
519                                                         info);
520                         fn = "secrets_failed_password_change";
521                 } else {
522                         /*
523                          * The server rejected the change, we don't
524                          * retry and defer the change to the next
525                          * "machine password timeout" interval.
526                          */
527                         status2 = secrets_defer_password_change(dcname,
528                                                         NT_STATUS_NOT_COMMITTED,
529                                                         status,
530                                                         info);
531                         fn = "secrets_defer_password_change";
532                 }
533                 if (!NT_STATUS_IS_OK(status2)) {
534                         DEBUG(0, ("%s() failed for domain %s!\n",
535                                   fn, domain));
536                         TALLOC_FREE(frame);
537                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
538                 }
539
540                 TALLOC_FREE(frame);
541                 return status;
542         }
543
544         DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
545                  current_timestring(talloc_tos(), false),
546                  __func__, domain, context_name));
547
548         switch (sec_channel_type) {
549
550         case SEC_CHAN_WKSTA:
551         case SEC_CHAN_BDC:
552                 status = secrets_finish_password_change(
553                                         info->next_change->change_server,
554                                         info->next_change->change_time,
555                                         info);
556                 if (!NT_STATUS_IS_OK(status)) {
557                         DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
558                                   domain));
559                         TALLOC_FREE(frame);
560                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
561                 }
562
563                 DEBUG(0,("%s : %s(%s): Finished password change.\n",
564                          current_timestring(talloc_tos(), false),
565                          __func__, domain));
566                 break;
567
568         case SEC_CHAN_DNS_DOMAIN:
569         case SEC_CHAN_DOMAIN:
570                 /*
571                  * we used pdb_set_trusteddom_pw().
572                  */
573                 break;
574
575         default:
576                 smb_panic("Unsupported secure channel type");
577                 break;
578         }
579
580         ok = cli_credentials_set_utf16_password(creds,
581                                                 &new_trust_pw_blob,
582                                                 CRED_SPECIFIED);
583         if (!ok) {
584                 DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
585                           domain));
586                 TALLOC_FREE(frame);
587                 return NT_STATUS_NO_MEMORY;
588         }
589
590         current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
591         if (current_nt_hash == NULL) {
592                 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
593                           domain));
594                 TALLOC_FREE(frame);
595                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
596         }
597
598         /*
599          * Now we verify the new password.
600          */
601         idx = 0;
602         idx_current = idx;
603         nt_hashes[idx++] = current_nt_hash;
604         num_nt_hashes = idx;
605         status = netlogon_creds_cli_lck_auth(context, b,
606                                              num_nt_hashes,
607                                              nt_hashes,
608                                              &idx_nt_hashes);
609         if (!NT_STATUS_IS_OK(status)) {
610                 DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
611                           context_name, nt_errstr(status)));
612                 TALLOC_FREE(frame);
613                 return status;
614         }
615
616         DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
617                  current_timestring(talloc_tos(), false),
618                  __func__, domain, context_name));
619
620         TALLOC_FREE(frame);
621         return NT_STATUS_OK;
622 }