Convert all uint32/16/8 to _t in source3/libsmb.
[bbaumbach/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 NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
51                          struct messaging_context *msg_ctx,
52                          struct dcerpc_binding_handle *b,
53                          const char *domain,
54                          bool force)
55 {
56         TALLOC_CTX *frame = talloc_stackframe();
57         struct trust_pw_change_state *state;
58         struct cli_credentials *creds = NULL;
59         const struct samr_Password *current_nt_hash = NULL;
60         const struct samr_Password *previous_nt_hash = NULL;
61         enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
62         time_t pass_last_set_time;
63         uint32_t old_version = 0;
64         struct pdb_trusted_domain *td = NULL;
65         struct timeval g_timeout = { 0, };
66         int timeout = 0;
67         struct timeval tv = { 0, };
68         size_t new_len = DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH;
69         uint8_t new_password_buffer[256 * 2] = { 0, };
70         char *new_trust_passwd = NULL;
71         size_t len = 0;
72         uint32_t new_version = 0;
73         uint32_t *new_trust_version = NULL;
74         NTSTATUS status;
75         bool ok;
76
77         state = talloc_zero(frame, struct trust_pw_change_state);
78         if (state == NULL) {
79                 TALLOC_FREE(frame);
80                 return NT_STATUS_NO_MEMORY;
81         }
82
83         state->g_ctx = g_lock_ctx_init(state, msg_ctx);
84         if (state->g_ctx == NULL) {
85                 TALLOC_FREE(frame);
86                 return NT_STATUS_NO_MEMORY;
87         }
88
89         state->g_lock_key = talloc_asprintf(state,
90                                 "trust_password_change_%s",
91                                 domain);
92         if (state->g_lock_key == NULL) {
93                 TALLOC_FREE(frame);
94                 return NT_STATUS_NO_MEMORY;
95         }
96
97         g_timeout = timeval_current_ofs(10, 0);
98         status = g_lock_lock(state->g_ctx,
99                              state->g_lock_key,
100                              G_LOCK_WRITE, g_timeout);
101         if (!NT_STATUS_IS_OK(status)) {
102                 DEBUG(1, ("could not get g_lock on [%s]!\n",
103                           state->g_lock_key));
104                 TALLOC_FREE(frame);
105                 return status;
106         }
107
108         talloc_set_destructor(state, trust_pw_change_state_destructor);
109
110         status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
111         if (!NT_STATUS_IS_OK(status)) {
112                 DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
113                           domain, nt_errstr(status)));
114                 TALLOC_FREE(frame);
115                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
116         }
117
118         current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
119         if (current_nt_hash == NULL) {
120                 DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
121                           domain));
122                 TALLOC_FREE(frame);
123                 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
124         }
125
126         old_version = cli_credentials_get_kvno(creds);
127         pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
128         sec_channel_type = cli_credentials_get_secure_channel_type(creds);
129
130         new_version = old_version + 1;
131
132         switch (sec_channel_type) {
133         case SEC_CHAN_WKSTA:
134         case SEC_CHAN_BDC:
135                 break;
136         case SEC_CHAN_DNS_DOMAIN:
137                 /*
138                  * new_len * 2 = 498 bytes is the largest possible length
139                  * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
140                  * and a confounder with at least 2 bytes is required.
141                  *
142                  * Windows uses new_len = 120 => 240 bytes.
143                  */
144                 new_len = 120;
145
146                 /* fall through */
147         case SEC_CHAN_DOMAIN:
148                 status = pdb_get_trusted_domain(frame, domain, &td);
149                 if (!NT_STATUS_IS_OK(status)) {
150                         DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
151                                   domain, nt_errstr(status)));
152                         TALLOC_FREE(frame);
153                         return status;
154                 }
155
156                 new_trust_version = &new_version;
157                 break;
158         default:
159                 TALLOC_FREE(frame);
160                 return NT_STATUS_NOT_SUPPORTED;
161         }
162
163         timeout = lp_machine_password_timeout();
164         if (timeout == 0) {
165                 if (!force) {
166                         DEBUG(10,("machine password never expires\n"));
167                         TALLOC_FREE(frame);
168                         return NT_STATUS_OK;
169                 }
170         }
171
172         tv.tv_sec = pass_last_set_time;
173         DEBUG(10, ("password last changed %s\n",
174                    timeval_string(talloc_tos(), &tv, false)));
175         tv.tv_sec += timeout;
176         DEBUGADD(10, ("password valid until %s\n",
177                       timeval_string(talloc_tos(), &tv, false)));
178
179         if (!force && !timeval_expired(&tv)) {
180                 TALLOC_FREE(frame);
181                 return NT_STATUS_OK;
182         }
183
184         /*
185          * Create a random machine account password
186          * We create a random buffer and convert that to utf8.
187          * This is similar to what windows is doing.
188          */
189         generate_secret_buffer(new_password_buffer, new_len * 2);
190         ok = convert_string_talloc(frame,
191                                    CH_UTF16MUNGED, CH_UTF8,
192                                    new_password_buffer, new_len * 2,
193                                    (void *)&new_trust_passwd, &len);
194         ZERO_STRUCT(new_password_buffer);
195         if (!ok) {
196                 DEBUG(0, ("convert_string_talloc failed\n"));
197                 TALLOC_FREE(frame);
198                 return NT_STATUS_NO_MEMORY;
199         }
200
201         /*
202          * We could use cli_credentials_get_old_nt_hash(creds, frame) to
203          * set previous_nt_hash.
204          *
205          * But we want to check if the dc has our current password and only do
206          * a change if that's the case. So we keep previous_nt_hash = NULL.
207          *
208          * TODO:
209          * If the previous password is the only password in common with the dc,
210          * we better skip the password change, or use something like
211          * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
212          * local secrets before doing the change.
213          */
214         status = netlogon_creds_cli_auth(context, b,
215                                          *current_nt_hash,
216                                          previous_nt_hash);
217         if (!NT_STATUS_IS_OK(status)) {
218                 DEBUG(0, ("netlogon_creds_cli_auth for domain %s - %s!\n",
219                           domain, nt_errstr(status)));
220                 TALLOC_FREE(frame);
221                 return status;
222         }
223
224         /*
225          * Return the result of trying to write the new password
226          * back into the trust account file.
227          */
228
229         switch (sec_channel_type) {
230
231         case SEC_CHAN_WKSTA:
232         case SEC_CHAN_BDC:
233                 ok = secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type);
234                 if (!ok) {
235                         DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n",
236                                   domain));
237                         TALLOC_FREE(frame);
238                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
239                 }
240                 break;
241
242         case SEC_CHAN_DNS_DOMAIN:
243         case SEC_CHAN_DOMAIN:
244                 /*
245                  * we need to get the sid first for the
246                  * pdb_set_trusteddom_pw call
247                  */
248                 ok = pdb_set_trusteddom_pw(domain, new_trust_passwd,
249                                            &td->security_identifier);
250                 if (!ok) {
251                         DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
252                                   domain));
253                         TALLOC_FREE(frame);
254                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
255                 }
256                 break;
257
258         default:
259                 smb_panic("Unsupported secure channel type");
260                 break;
261         }
262
263         DEBUG(1,("%s : %s(%s): Changed password locally\n",
264                  current_timestring(talloc_tos(), false), __func__, domain));
265
266         status = netlogon_creds_cli_ServerPasswordSet(context, b,
267                                                       new_trust_passwd,
268                                                       new_trust_version);
269         if (!NT_STATUS_IS_OK(status)) {
270                 DEBUG(0,("%s : %s(%s) remote password change set failed - %s\n",
271                          current_timestring(talloc_tos(), false), __func__,
272                          domain, nt_errstr(status)));
273                 TALLOC_FREE(frame);
274                 return status;
275         }
276
277         DEBUG(1,("%s : %s(%s): Changed password remotely.\n",
278                  current_timestring(talloc_tos(), false), __func__, domain));
279
280         TALLOC_FREE(frame);
281         return NT_STATUS_OK;
282 }