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