r14464: Don't include ndr_BASENAME.h files unless strictly required, instead
[kai/samba.git] / source4 / libnet / libnet_vampire.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    Extract the user/system database from a remote SamSync server
5
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23
24 #include "includes.h"
25 #include "libnet/libnet.h"
26 #include "libcli/auth/libcli_auth.h"
27 #include "auth/gensec/schannel_proto.h"
28 #include "librpc/gen_ndr/ndr_netlogon.h"
29 #include "librpc/gen_ndr/ndr_netlogon_c.h"
30
31
32 /**
33  * Decrypt and extract the user's passwords.  
34  * 
35  * The writes decrypted (no longer 'RID encrypted' or arcfour encrypted) passwords back into the structure
36  */
37 static NTSTATUS fix_user(TALLOC_CTX *mem_ctx,
38                          struct creds_CredentialState *creds,
39                          enum netr_SamDatabaseID database,
40                          struct netr_DELTA_ENUM *delta,
41                          char **error_string) 
42 {
43
44         uint32_t rid = delta->delta_id_union.rid;
45         struct netr_DELTA_USER *user = delta->delta_union.user;
46         struct samr_Password lm_hash;
47         struct samr_Password nt_hash;
48         const char *username = user->account_name.string;
49         NTSTATUS nt_status;
50
51         if (user->lm_password_present) {
52                 sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0);
53                 user->lmpassword = lm_hash;
54         }
55
56         if (user->nt_password_present) {
57                 sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0);
58                 user->ntpassword = nt_hash;
59         }
60
61         if (user->user_private_info.SensitiveData) {
62                 DATA_BLOB data;
63                 struct netr_USER_KEYS keys;
64                 data.data = user->user_private_info.SensitiveData;
65                 data.length = user->user_private_info.DataLength;
66                 creds_arcfour_crypt(creds, data.data, data.length);
67                 user->user_private_info.SensitiveData = data.data;
68                 user->user_private_info.DataLength = data.length;
69
70                 nt_status = ndr_pull_struct_blob(&data, mem_ctx, &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS);
71                 if (NT_STATUS_IS_OK(nt_status)) {
72                         if (keys.keys.keys2.lmpassword.length == 16) {
73                                 sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0);
74                                 user->lmpassword = lm_hash;
75                                 user->lm_password_present = True;
76                         }
77                         if (keys.keys.keys2.ntpassword.length == 16) {
78                                 sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0);
79                                 user->ntpassword = nt_hash;
80                                 user->nt_password_present = True;
81                         }
82                 } else {
83                         *error_string = talloc_asprintf(mem_ctx, "Failed to parse Sensitive Data for %s:\n", username);
84                         dump_data(10, data.data, data.length);
85                         return nt_status;
86                 }
87         }
88         return NT_STATUS_OK;
89 }
90
91 /**
92  * Decrypt and extract the secrets
93  * 
94  * The writes decrypted secrets back into the structure
95  */
96 static NTSTATUS fix_secret(TALLOC_CTX *mem_ctx,
97                            struct creds_CredentialState *creds,
98                            enum netr_SamDatabaseID database,
99                            struct netr_DELTA_ENUM *delta,
100                            char **error_string) 
101 {
102         struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
103         creds_arcfour_crypt(creds, secret->current_cipher.cipher_data, 
104                             secret->current_cipher.maxlen); 
105
106         creds_arcfour_crypt(creds, secret->old_cipher.cipher_data, 
107                             secret->old_cipher.maxlen); 
108
109         return NT_STATUS_OK;
110 }
111
112 /**
113  * Fix up the delta, dealing with encryption issues so that the final
114  * callback need only do the printing or application logic
115  */
116
117 static NTSTATUS fix_delta(TALLOC_CTX *mem_ctx,          
118                           struct creds_CredentialState *creds,
119                           enum netr_SamDatabaseID database,
120                           struct netr_DELTA_ENUM *delta,
121                           char **error_string)
122 {
123         NTSTATUS nt_status = NT_STATUS_OK;
124         *error_string = NULL;
125         switch (delta->delta_type) {
126         case NETR_DELTA_USER:
127         {
128                 nt_status = fix_user(mem_ctx, 
129                                      creds,
130                                      database,
131                                      delta,
132                                      error_string);
133                 break;
134         }
135         case NETR_DELTA_SECRET:
136         {
137                 nt_status = fix_secret(mem_ctx, 
138                                        creds,
139                                        database,
140                                        delta,
141                                        error_string);
142                 break;
143         }
144         default:
145                 break;
146         }
147         return nt_status;
148 }
149
150 NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamSync *r)
151 {
152         NTSTATUS nt_status, dbsync_nt_status;
153         TALLOC_CTX *samsync_ctx, *loop_ctx, *delta_ctx;
154         struct creds_CredentialState *creds;
155         struct netr_DatabaseSync dbsync;
156         struct cli_credentials *machine_account;
157         struct dcerpc_pipe *p;
158         struct libnet_context *machine_net_ctx;
159         struct libnet_RpcConnectDCInfo *c;
160         struct libnet_SamSync_state *state;
161         const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; 
162         int i;
163
164         samsync_ctx = talloc_named(mem_ctx, 0, "SamSync top context");
165
166         if (!r->in.machine_account) { 
167                 machine_account = cli_credentials_init(samsync_ctx);
168                 if (!machine_account) {
169                         talloc_free(samsync_ctx);
170                         return NT_STATUS_NO_MEMORY;
171                 }
172                 cli_credentials_set_conf(machine_account);
173                 nt_status = cli_credentials_set_machine_account(machine_account);
174                 if (!NT_STATUS_IS_OK(nt_status)) {
175                         r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?");
176                         talloc_free(samsync_ctx);
177                         return nt_status;
178                 }
179         } else {
180                 machine_account = r->in.machine_account;
181         }
182
183         /* We cannot do this unless we are a BDC.  Check, before we get odd errors later */
184         if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) {
185                 r->out.error_string
186                         = talloc_asprintf(mem_ctx, 
187                                           "Our join to domain %s is not as a BDC (%d), please rejoin as a BDC",
188                                           
189                                           cli_credentials_get_domain(machine_account),
190                                           cli_credentials_get_secure_channel_type(machine_account));
191                 talloc_free(samsync_ctx);
192                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
193         }
194
195         c = talloc(samsync_ctx, struct libnet_RpcConnectDCInfo);
196         if (!c) {
197                 r->out.error_string = NULL;
198                 talloc_free(samsync_ctx);
199                 return NT_STATUS_NO_MEMORY;
200         }
201
202         if (r->in.binding_string) {
203                 c->level      = LIBNET_RPC_CONNECT_BINDING;
204                 c->in.binding = r->in.binding_string;
205         } else {
206                 /* prepare connect to the NETLOGON pipe of PDC */
207                 c->level      = LIBNET_RPC_CONNECT_PDC;
208                 c->in.name    = cli_credentials_get_domain(machine_account);
209         }
210         c->in.dcerpc_iface      = &dcerpc_table_netlogon;
211
212         /* We must do this as the machine, not as any command-line
213          * user.  So we override the credentials in the
214          * libnet_context */
215         machine_net_ctx = talloc(samsync_ctx, struct libnet_context);
216         if (!machine_net_ctx) {
217                 r->out.error_string = NULL;
218                 talloc_free(samsync_ctx);
219                 return NT_STATUS_NO_MEMORY;
220         }
221         *machine_net_ctx = *ctx;
222         machine_net_ctx->cred = machine_account;
223
224         /* connect to the NETLOGON pipe of the PDC */
225         nt_status = libnet_RpcConnectDCInfo(machine_net_ctx, c);
226         if (!NT_STATUS_IS_OK(nt_status)) {
227                 if (r->in.binding_string) {
228                         r->out.error_string = talloc_asprintf(mem_ctx,
229                                                               "Connection to NETLOGON pipe of DC %s failed: %s",
230                                                               r->in.binding_string, c->out.error_string);
231                 } else {
232                         r->out.error_string = talloc_asprintf(mem_ctx,
233                                                               "Connection to NETLOGON pipe of DC for %s failed: %s",
234                                                               c->in.name, c->out.error_string);
235                 }
236                 talloc_free(samsync_ctx);
237                 return nt_status;
238         }
239
240         /* This makes a new pipe, on which we can do schannel.  We
241          * should do this in the RpcConnect code, but the abstaction
242          * layers do not suit yet */
243
244         nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p,
245                                                 c->out.dcerpc_pipe->binding);
246
247         if (!NT_STATUS_IS_OK(nt_status)) {
248                 r->out.error_string = talloc_asprintf(mem_ctx,
249                                                       "Secondary connection to NETLOGON pipe of DC %s failed: %s",
250                                                       dcerpc_server_name(p), nt_errstr(nt_status));
251                 talloc_free(samsync_ctx);
252                 return nt_status;
253         }
254
255         nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &dcerpc_table_netlogon,
256                                               machine_account, DCERPC_AUTH_LEVEL_PRIVACY);
257
258         if (!NT_STATUS_IS_OK(nt_status)) {
259                 r->out.error_string = talloc_asprintf(mem_ctx,
260                                                       "SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s",
261                                                       dcerpc_server_name(p), nt_errstr(nt_status));
262                 talloc_free(samsync_ctx);
263                 return nt_status;
264         }
265
266         state = talloc(samsync_ctx, struct libnet_SamSync_state);
267         if (!state) {
268                 r->out.error_string = NULL;
269                 talloc_free(samsync_ctx);
270                 return nt_status;
271         }               
272
273         state->domain_name     = c->out.domain_name;
274         state->domain_sid      = c->out.domain_sid;
275         state->realm           = c->out.realm;
276         state->domain_guid     = c->out.guid;
277         state->machine_net_ctx = machine_net_ctx;
278         state->netlogon_pipe   = p;
279
280         /* initialise the callback layer.  It may wish to contact the
281          * server with ldap, now we know the name */
282         
283         if (r->in.init_fn) {
284                 char *error_string;
285                 nt_status = r->in.init_fn(samsync_ctx, 
286                                           r->in.fn_ctx,
287                                           state, 
288                                           &error_string); 
289                 if (!NT_STATUS_IS_OK(nt_status)) {
290                         r->out.error_string = talloc_steal(mem_ctx, error_string);
291                         talloc_free(samsync_ctx);
292                         return nt_status;
293                 }
294         }
295
296         /* get NETLOGON credentails */
297
298         nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, samsync_ctx, &creds);
299         if (!NT_STATUS_IS_OK(nt_status)) {
300                 r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer");
301                 talloc_free(samsync_ctx);
302                 return nt_status;
303         }
304
305         /* Setup details for the syncronisation */
306         dbsync.in.logon_server = talloc_asprintf(samsync_ctx, "\\\\%s", dcerpc_server_name(p));
307         dbsync.in.computername = cli_credentials_get_workstation(machine_account);
308         dbsync.in.preferredmaximumlength = (uint32_t)-1;
309         ZERO_STRUCT(dbsync.in.return_authenticator);
310
311         for (i=0;i< ARRAY_SIZE(database_ids); i++) { 
312                 dbsync.in.sync_context = 0;
313                 dbsync.in.database_id = database_ids[i]; 
314                 
315                 do {
316                         int d;
317                         loop_ctx = talloc_named(samsync_ctx, 0, "DatabaseSync loop context");
318                         creds_client_authenticator(creds, &dbsync.in.credential);
319                         
320                         dbsync_nt_status = dcerpc_netr_DatabaseSync(p, loop_ctx, &dbsync);
321                         if (!NT_STATUS_IS_OK(dbsync_nt_status) &&
322                             !NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) {
323                                 r->out.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status));
324                                 talloc_free(samsync_ctx);
325                                 return nt_status;
326                         }
327                         
328                         if (!creds_client_check(creds, &dbsync.out.return_authenticator.cred)) {
329                                 r->out.error_string = talloc_strdup(mem_ctx, "Credential chaining on incoming DatabaseSync failed");
330                                 talloc_free(samsync_ctx);
331                                 return NT_STATUS_ACCESS_DENIED;
332                         }
333                         
334                         dbsync.in.sync_context = dbsync.out.sync_context;
335                         
336                         /* For every single remote 'delta' entry: */
337                         for (d=0; d < dbsync.out.delta_enum_array->num_deltas; d++) {
338                                 char *error_string = NULL;
339                                 delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context");
340                                 /* 'Fix' elements, by decrypting and
341                                  * de-obfustiating the data */
342                                 nt_status = fix_delta(delta_ctx, 
343                                                       creds, 
344                                                       dbsync.in.database_id,
345                                                       &dbsync.out.delta_enum_array->delta_enum[d], 
346                                                       &error_string);
347                                 if (!NT_STATUS_IS_OK(nt_status)) {
348                                         r->out.error_string = talloc_steal(mem_ctx, error_string);
349                                         talloc_free(samsync_ctx);
350                                         return nt_status;
351                                 }
352
353                                 /* Now call the callback.  This will
354                                  * do something like print the data or
355                                  * write to an ldb */
356                                 nt_status = r->in.delta_fn(delta_ctx, 
357                                                            r->in.fn_ctx,
358                                                            dbsync.in.database_id,
359                                                            &dbsync.out.delta_enum_array->delta_enum[d], 
360                                                            &error_string);
361                                 if (!NT_STATUS_IS_OK(nt_status)) {
362                                         r->out.error_string = talloc_steal(mem_ctx, error_string);
363                                         talloc_free(samsync_ctx);
364                                         return nt_status;
365                                 }
366                                 talloc_free(delta_ctx);
367                         }
368                         talloc_free(loop_ctx);
369                 } while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES));
370                 
371                 if (!NT_STATUS_IS_OK(dbsync_nt_status)) {
372                         r->out.error_string = talloc_asprintf(mem_ctx, "libnet_SamSync_netlogon failed: unexpected inconsistancy. Should not get error %s here", nt_errstr(nt_status));
373                         talloc_free(samsync_ctx);
374                         return dbsync_nt_status;
375                 }
376                 nt_status = NT_STATUS_OK;
377         }
378         talloc_free(samsync_ctx);
379         return nt_status;
380 }
381