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