r6927: Make it easier to program with the SamSync callback interface, perform
[samba.git] / source / libnet / libnet_vampire.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21
22 #include "includes.h"
23 #include "libnet/libnet.h"
24 #include "librpc/gen_ndr/ndr_netlogon.h"
25 #include "librpc/gen_ndr/ndr_samr.h"
26
27 static NTSTATUS vampire_samdump_handle_user(TALLOC_CTX *mem_ctx,
28                                             struct creds_CredentialState *creds,
29                                             struct netr_DELTA_ENUM *delta) 
30 {
31         uint32_t rid = delta->delta_id_union.rid;
32         struct netr_DELTA_USER *user = delta->delta_union.user;
33         const char *username = user->account_name.string;
34         char *hex_lm_password;
35         char *hex_nt_password;
36
37         hex_lm_password = smbpasswd_sethexpwd(mem_ctx, 
38                                               user->lm_password_present ? &user->lmpassword : NULL, 
39                                               user->acct_flags);
40         hex_nt_password = smbpasswd_sethexpwd(mem_ctx, 
41                                               user->nt_password_present ? &user->ntpassword : NULL, 
42                                               user->acct_flags);
43
44         printf("%s:%d:%s:%s:%s:LCT-%08X\n", username,
45                rid, hex_lm_password, hex_nt_password,
46                smbpasswd_encode_acb_info(mem_ctx, user->acct_flags),
47                (unsigned int)nt_time_to_unix(user->last_password_change));
48
49         return NT_STATUS_OK;
50 }
51
52 static NTSTATUS libnet_samdump_fn(TALLOC_CTX *mem_ctx,          
53                                   void *private,                        
54                                   struct creds_CredentialState *creds,
55                                   enum netr_SamDatabaseID database,
56                                   struct netr_DELTA_ENUM *delta,
57                                   char **error_string)
58 {
59         NTSTATUS nt_status = NT_STATUS_OK;
60         *error_string = NULL;
61         switch (database) {
62         case SAM_DATABASE_DOMAIN: 
63         {
64                 switch (delta->delta_type) {
65                 case NETR_DELTA_USER:
66                 {
67                         nt_status = vampire_samdump_handle_user(mem_ctx, 
68                                                                 creds,
69                                                                 delta);
70                         break;
71                 }
72                 }
73                 break;
74         }
75         }
76         return nt_status;
77 }
78
79 /**
80  * Decrypt and extract the user's passwords.  
81  * 
82  * The writes decrypted (no longer 'RID encrypted' or arcfour encrypted) passwords back into the structure
83  */
84 static NTSTATUS fix_user(TALLOC_CTX *mem_ctx,
85                          struct creds_CredentialState *creds,
86                          enum netr_SamDatabaseID database,
87                          struct netr_DELTA_ENUM *delta,
88                          char **error_string) 
89 {
90
91         uint32_t rid = delta->delta_id_union.rid;
92         struct netr_DELTA_USER *user = delta->delta_union.user;
93         struct samr_Password lm_hash;
94         struct samr_Password nt_hash;
95         const char *username = user->account_name.string;
96         NTSTATUS nt_status;
97
98         if (user->lm_password_present) {
99                 sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0);
100                 user->lmpassword = lm_hash;
101         }
102
103         if (user->nt_password_present) {
104                 sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0);
105                 user->ntpassword = nt_hash;
106         }
107
108         if (user->user_private_info.SensitiveData) {
109                 DATA_BLOB data;
110                 struct netr_USER_KEYS keys;
111                 data.data = user->user_private_info.SensitiveData;
112                 data.length = user->user_private_info.DataLength;
113                 creds_arcfour_crypt(creds, data.data, data.length);
114                 user->user_private_info.SensitiveData = data.data;
115                 user->user_private_info.DataLength = data.length;
116
117                 nt_status = ndr_pull_struct_blob(&data, mem_ctx, &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS);
118                 if (NT_STATUS_IS_OK(nt_status)) {
119                         if (keys.keys.keys2.lmpassword.length == 16) {
120                                 sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0);
121                                 user->lmpassword = lm_hash;
122                                 user->lm_password_present = True;
123                         }
124                         if (keys.keys.keys2.ntpassword.length == 16) {
125                                 sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0);
126                                 user->ntpassword = nt_hash;
127                                 user->nt_password_present = True;
128                         }
129                 } else {
130                         *error_string = talloc_asprintf(mem_ctx, "Failed to parse Sensitive Data for %s:\n", username);
131                         dump_data(10, data.data, data.length);
132                         return nt_status;
133                 }
134         }
135         return NT_STATUS_OK;
136 }
137
138 /**
139  * Fix up the delta, dealing with encryption issues so that the final
140  * callback need only do the printing or application logic
141  */
142
143 static NTSTATUS fix_delta(TALLOC_CTX *mem_ctx,          
144                           struct creds_CredentialState *creds,
145                           enum netr_SamDatabaseID database,
146                           struct netr_DELTA_ENUM *delta,
147                           char **error_string)
148 {
149         NTSTATUS nt_status = NT_STATUS_OK;
150         *error_string = NULL;
151         switch (delta->delta_type) {
152         case NETR_DELTA_USER:
153         {
154                 nt_status = fix_user(mem_ctx, 
155                                      creds,
156                                      database,
157                                      delta,
158                                      error_string);
159                 break;
160         }
161         }
162         return nt_status;
163 }
164
165 static NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SamSync *r)
166 {
167         NTSTATUS nt_status, dbsync_nt_status;
168         TALLOC_CTX *loop_ctx, *delta_ctx;
169         struct creds_CredentialState *creds;
170         struct netr_DatabaseSync dbsync;
171         struct cli_credentials *machine_account;
172         struct dcerpc_binding *b;
173         struct dcerpc_pipe *p;
174         const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; 
175         int i;
176
177         /* TODO: This is bogus */
178         const char **bindings = lp_passwordserver();
179         const char *binding;
180
181         if (bindings && bindings[0]) {
182                 binding = bindings[0];
183         }
184
185         machine_account = cli_credentials_init(mem_ctx);
186         if (!machine_account) {
187                 return NT_STATUS_NO_MEMORY;
188         }
189
190         cli_credentials_set_conf(machine_account);
191         nt_status = cli_credentials_set_machine_account(machine_account);
192         
193         if (!NT_STATUS_IS_OK(nt_status)) {
194                 r->netlogon.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?");
195                 return nt_status;
196         }
197         
198         if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) {
199                 r->netlogon.error_string
200                         = talloc_asprintf(mem_ctx, 
201                                           "Our join to domain %s is not as a BDC (%d), please rejoin as a BDC",
202                                           
203                                           cli_credentials_get_domain(machine_account),
204                                           cli_credentials_get_secure_channel_type(machine_account));
205                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
206         }
207
208         /* Connect to DC (take a binding string for now) */
209
210         nt_status = dcerpc_parse_binding(mem_ctx, binding, &b);
211         if (!NT_STATUS_IS_OK(nt_status)) {
212                 r->netlogon.error_string = talloc_asprintf(mem_ctx, "Bad binding string %s\n", binding);
213                 return NT_STATUS_INVALID_PARAMETER;
214         }
215
216         /* We like schannel */
217         b->flags &= ~DCERPC_AUTH_OPTIONS;
218         b->flags |= DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128;
219
220         /* Setup schannel */
221         nt_status = dcerpc_pipe_connect_b(mem_ctx, &p, b, 
222                                           DCERPC_NETLOGON_UUID,
223                                           DCERPC_NETLOGON_VERSION,
224                                           machine_account);
225
226         if (!NT_STATUS_IS_OK(nt_status)) {
227                 return nt_status;
228         }
229
230         /* get NETLOGON credentails */
231
232         nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, mem_ctx, &creds);
233         if (!NT_STATUS_IS_OK(nt_status)) {
234                 r->netlogon.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer");
235                 return nt_status;
236         }
237
238         dbsync.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
239         dbsync.in.computername = cli_credentials_get_workstation(machine_account);
240         dbsync.in.preferredmaximumlength = (uint32_t)-1;
241         ZERO_STRUCT(dbsync.in.return_authenticator);
242
243         for (i=0;i< ARRAY_SIZE(database_ids); i++) { 
244                 dbsync.in.sync_context = 0;
245                 dbsync.in.database_id = database_ids[i]; 
246                 
247                 do {
248                         int d;
249                         loop_ctx = talloc_named(mem_ctx, 0, "DatabaseSync loop context");
250                         creds_client_authenticator(creds, &dbsync.in.credential);
251                         
252                         dbsync_nt_status = dcerpc_netr_DatabaseSync(p, loop_ctx, &dbsync);
253                         if (!NT_STATUS_IS_OK(dbsync_nt_status) &&
254                             !NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) {
255                                 r->netlogon.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status));
256                                 return nt_status;
257                         }
258                         
259                         if (!creds_client_check(creds, &dbsync.out.return_authenticator.cred)) {
260                                 r->netlogon.error_string = talloc_strdup(mem_ctx, "Credential chaining failed");
261                                 return NT_STATUS_ACCESS_DENIED;
262                         }
263                         
264                         dbsync.in.sync_context = dbsync.out.sync_context;
265                         
266                         for (d=0; d < dbsync.out.delta_enum_array->num_deltas; d++) {
267                                 char *error_string = NULL;
268                                 delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context");
269                                 nt_status = fix_delta(delta_ctx, 
270                                                       creds, 
271                                                       dbsync.in.database_id,
272                                                       &dbsync.out.delta_enum_array->delta_enum[d], 
273                                                       &error_string);
274                                 if (!NT_STATUS_IS_OK(nt_status)) {
275                                         r->netlogon.error_string = talloc_steal(mem_ctx, error_string);
276                                         talloc_free(delta_ctx);
277                                         return nt_status;
278                                 }
279                                 nt_status = r->netlogon.delta_fn(delta_ctx, 
280                                                                  r->netlogon.fn_ctx,
281                                                                  creds,
282                                                                  dbsync.in.database_id,
283                                                                  &dbsync.out.delta_enum_array->delta_enum[d], 
284                                                                  &error_string);
285                                 if (!NT_STATUS_IS_OK(nt_status)) {
286                                         r->netlogon.error_string = talloc_steal(mem_ctx, error_string);
287                                         talloc_free(delta_ctx);
288                                         return nt_status;
289                                 }
290                                 talloc_free(delta_ctx);
291                         }
292                         talloc_free(loop_ctx);
293                 } while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES));
294                 nt_status = dbsync_nt_status;
295         }
296         return nt_status;
297 }
298
299 NTSTATUS libnet_SamDump_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SamDump *r)
300 {
301         NTSTATUS nt_status;
302         union libnet_SamSync r2;
303
304         r2.netlogon.level = LIBNET_SAMDUMP_NETLOGON;
305         r2.netlogon.error_string = NULL;
306         r2.netlogon.delta_fn = libnet_samdump_fn;
307         r2.netlogon.fn_ctx = NULL;
308         nt_status = libnet_SamSync_netlogon(ctx, mem_ctx, &r2);
309         r->generic.error_string = r2.netlogon.error_string;
310
311         
312         return nt_status;
313 }
314
315
316
317 NTSTATUS libnet_SamDump_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SamDump *r)
318 {
319         NTSTATUS nt_status;
320         union libnet_SamDump r2;
321
322         r2.generic.level = LIBNET_SAMDUMP_NETLOGON;
323         r2.generic.error_string = NULL;
324         nt_status = libnet_SamDump(ctx, mem_ctx, &r2);
325         r->generic.error_string = r2.netlogon.error_string;
326
327         
328         return nt_status;
329 }
330
331 NTSTATUS libnet_SamDump(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SamDump *r)
332 {
333         switch (r->generic.level) {
334         case LIBNET_SAMDUMP_GENERIC:
335                 return libnet_SamDump_generic(ctx, mem_ctx, r);
336         case LIBNET_SAMDUMP_NETLOGON:
337                 return libnet_SamDump_netlogon(ctx, mem_ctx, r);
338         }
339
340         return NT_STATUS_INVALID_LEVEL;
341 }