Fix net rpc vampire, based on an *amazing* piece of debugging work by "Cooper S....
[nivanova/samba-autobuild/.git] / source3 / libnet / libnet_samsync.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    Copyright (C) Guenther Deschner <gd@samba.org> 2008
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23
24 #include "includes.h"
25 #include "libnet/libnet.h"
26
27 /**
28  * Decrypt and extract the user's passwords.
29  *
30  * The writes decrypted (no longer 'RID encrypted' or arcfour encrypted)
31  * passwords back into the structure
32  */
33
34 static NTSTATUS fix_user(TALLOC_CTX *mem_ctx,
35                          DATA_BLOB *session_key,
36                          enum netr_SamDatabaseID database_id,
37                          struct netr_DELTA_ENUM *delta)
38 {
39
40         uint32_t rid = delta->delta_id_union.rid;
41         struct netr_DELTA_USER *user = delta->delta_union.user;
42         struct samr_Password lm_hash;
43         struct samr_Password nt_hash;
44         unsigned char zero_buf[16];
45
46         memset(zero_buf, '\0', sizeof(zero_buf));
47
48         /* Note that win2000 may send us all zeros
49          * for the hashes if it doesn't
50          * think this channel is secure enough. */
51         if (user->lm_password_present) {
52                 if (memcmp(user->lmpassword.hash, zero_buf, 16) != 0) {
53                         sam_pwd_hash(rid, user->lmpassword.hash, lm_hash.hash, 0);
54                 } else {
55                         memset(lm_hash.hash, '\0', sizeof(lm_hash.hash));
56                 }
57                 user->lmpassword = lm_hash;
58         }
59
60         if (user->nt_password_present) {
61                 if (memcmp(user->ntpassword.hash, zero_buf, 16) != 0) {
62                         sam_pwd_hash(rid, user->ntpassword.hash, nt_hash.hash, 0);
63                 } else {
64                         memset(nt_hash.hash, '\0', sizeof(nt_hash.hash));
65                 }
66                 user->ntpassword = nt_hash;
67         }
68
69         if (user->user_private_info.SensitiveData) {
70                 DATA_BLOB data;
71                 struct netr_USER_KEYS keys;
72                 enum ndr_err_code ndr_err;
73                 data.data = user->user_private_info.SensitiveData;
74                 data.length = user->user_private_info.DataLength;
75                 SamOEMhashBlob(data.data, data.length, session_key);
76                 user->user_private_info.SensitiveData = data.data;
77                 user->user_private_info.DataLength = data.length;
78
79                 ndr_err = ndr_pull_struct_blob(&data, mem_ctx, NULL, &keys,
80                         (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS);
81                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
82                         dump_data(10, data.data, data.length);
83                         return ndr_map_error2ntstatus(ndr_err);
84                 }
85
86                 /* Note that win2000 may send us all zeros
87                  * for the hashes if it doesn't
88                  * think this channel is secure enough. */
89                 if (keys.keys.keys2.lmpassword.length == 16) {
90                         if (memcmp(keys.keys.keys2.lmpassword.pwd.hash,
91                                         zero_buf, 16) != 0) {
92                                 sam_pwd_hash(rid,
93                                         keys.keys.keys2.lmpassword.pwd.hash,
94                                         lm_hash.hash, 0);
95                         } else {
96                                 memset(lm_hash.hash, '\0', sizeof(lm_hash.hash));
97                         }
98                         user->lmpassword = lm_hash;
99                         user->lm_password_present = true;
100                 }
101                 if (keys.keys.keys2.ntpassword.length == 16) {
102                         if (memcmp(keys.keys.keys2.ntpassword.pwd.hash,
103                                                 zero_buf, 16) != 0) {
104                                 sam_pwd_hash(rid,
105                                         keys.keys.keys2.ntpassword.pwd.hash,
106                                         nt_hash.hash, 0);
107                         } else {
108                                 memset(nt_hash.hash, '\0', sizeof(nt_hash.hash));
109                         }
110                         user->ntpassword = nt_hash;
111                         user->nt_password_present = true;
112                 }
113                 /* TODO: rid decrypt history fields */
114         }
115         return NT_STATUS_OK;
116 }
117
118 /**
119  * Decrypt and extract the secrets
120  *
121  * The writes decrypted secrets back into the structure
122  */
123 static NTSTATUS fix_secret(TALLOC_CTX *mem_ctx,
124                            DATA_BLOB *session_key,
125                            enum netr_SamDatabaseID database_id,
126                            struct netr_DELTA_ENUM *delta)
127 {
128         struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
129
130         SamOEMhashBlob(secret->current_cipher.cipher_data,
131                        secret->current_cipher.maxlen,
132                        session_key);
133
134         SamOEMhashBlob(secret->old_cipher.cipher_data,
135                        secret->old_cipher.maxlen,
136                        session_key);
137
138         return NT_STATUS_OK;
139 }
140
141 /**
142  * Fix up the delta, dealing with encryption issues so that the final
143  * callback need only do the printing or application logic
144  */
145
146 static NTSTATUS samsync_fix_delta(TALLOC_CTX *mem_ctx,
147                                   DATA_BLOB *session_key,
148                                   enum netr_SamDatabaseID database_id,
149                                   struct netr_DELTA_ENUM *delta)
150 {
151         NTSTATUS status = NT_STATUS_OK;
152
153         switch (delta->delta_type) {
154                 case NETR_DELTA_USER:
155
156                         status = fix_user(mem_ctx,
157                                           session_key,
158                                           database_id,
159                                           delta);
160                         break;
161                 case NETR_DELTA_SECRET:
162
163                         status = fix_secret(mem_ctx,
164                                             session_key,
165                                             database_id,
166                                             delta);
167                         break;
168                 default:
169                         break;
170         }
171
172         return status;
173 }
174
175 /**
176  * Fix up the delta, dealing with encryption issues so that the final
177  * callback need only do the printing or application logic
178  */
179
180 static NTSTATUS samsync_fix_delta_array(TALLOC_CTX *mem_ctx,
181                                         DATA_BLOB *session_key,
182                                         enum netr_SamDatabaseID database_id,
183                                         struct netr_DELTA_ENUM_ARRAY *r)
184 {
185         NTSTATUS status;
186         int i;
187
188         for (i = 0; i < r->num_deltas; i++) {
189
190                 status = samsync_fix_delta(mem_ctx,
191                                            session_key,
192                                            database_id,
193                                            &r->delta_enum[i]);
194                 if (!NT_STATUS_IS_OK(status)) {
195                         return status;
196                 }
197         }
198
199         return NT_STATUS_OK;
200 }
201
202 /**
203  * libnet_samsync_init_context
204  */
205
206 NTSTATUS libnet_samsync_init_context(TALLOC_CTX *mem_ctx,
207                                      const struct dom_sid *domain_sid,
208                                      struct samsync_context **ctx_p)
209 {
210         struct samsync_context *ctx;
211
212         *ctx_p = NULL;
213
214         ctx = TALLOC_ZERO_P(mem_ctx, struct samsync_context);
215         NT_STATUS_HAVE_NO_MEMORY(ctx);
216
217         if (domain_sid) {
218                 ctx->domain_sid = sid_dup_talloc(mem_ctx, domain_sid);
219                 NT_STATUS_HAVE_NO_MEMORY(ctx->domain_sid);
220
221                 ctx->domain_sid_str = sid_string_talloc(mem_ctx, ctx->domain_sid);
222                 NT_STATUS_HAVE_NO_MEMORY(ctx->domain_sid_str);
223         }
224
225         *ctx_p = ctx;
226
227         return NT_STATUS_OK;
228 }
229
230 /**
231  * samsync_database_str
232  */
233
234 static const char *samsync_database_str(enum netr_SamDatabaseID database_id)
235 {
236
237         switch (database_id) {
238                 case SAM_DATABASE_DOMAIN:
239                         return "DOMAIN";
240                 case SAM_DATABASE_BUILTIN:
241                         return "BUILTIN";
242                 case SAM_DATABASE_PRIVS:
243                         return "PRIVS";
244                 default:
245                         return "unknown";
246         }
247 }
248
249 /**
250  * samsync_debug_str
251  */
252
253 static const char *samsync_debug_str(TALLOC_CTX *mem_ctx,
254                                      enum net_samsync_mode mode,
255                                      enum netr_SamDatabaseID database_id)
256 {
257         const char *action = NULL;
258
259         switch (mode) {
260                 case NET_SAMSYNC_MODE_DUMP:
261                         action = "Dumping (to stdout)";
262                         break;
263                 case NET_SAMSYNC_MODE_FETCH_PASSDB:
264                         action = "Fetching (to passdb)";
265                         break;
266                 case NET_SAMSYNC_MODE_FETCH_LDIF:
267                         action = "Fetching (to ldif)";
268                         break;
269                 case NET_SAMSYNC_MODE_FETCH_KEYTAB:
270                         action = "Fetching (to keytab)";
271                         break;
272                 default:
273                         action = "Unknown";
274                         break;
275         }
276
277         return talloc_asprintf(mem_ctx, "%s %s database",
278                                 action, samsync_database_str(database_id));
279 }
280
281 /**
282  * libnet_samsync
283  */
284
285 NTSTATUS libnet_samsync(enum netr_SamDatabaseID database_id,
286                         struct samsync_context *ctx)
287 {
288         NTSTATUS result;
289         TALLOC_CTX *mem_ctx;
290         const char *logon_server = ctx->cli->desthost;
291         const char *computername = global_myname();
292         struct netr_Authenticator credential;
293         struct netr_Authenticator return_authenticator;
294         uint16_t restart_state = 0;
295         uint32_t sync_context = 0;
296         const char *debug_str;
297         DATA_BLOB session_key;
298
299         ZERO_STRUCT(return_authenticator);
300
301         if (!(mem_ctx = talloc_init("libnet_samsync"))) {
302                 return NT_STATUS_NO_MEMORY;
303         }
304
305         debug_str = samsync_debug_str(mem_ctx, ctx->mode, database_id);
306         if (debug_str) {
307                 d_fprintf(stderr, "%s\n", debug_str);
308         }
309
310         do {
311                 struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
312                 NTSTATUS callback_status;
313
314                 netlogon_creds_client_step(ctx->cli->dc, &credential);
315
316                 result = rpccli_netr_DatabaseSync2(ctx->cli, mem_ctx,
317                                                    logon_server,
318                                                    computername,
319                                                    &credential,
320                                                    &return_authenticator,
321                                                    database_id,
322                                                    restart_state,
323                                                    &sync_context,
324                                                    &delta_enum_array,
325                                                    0xffff);
326                 if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) {
327                         return result;
328                 }
329
330                 /* Check returned credentials. */
331                 if (!netlogon_creds_client_check(ctx->cli->dc,
332                                                  &return_authenticator.cred)) {
333                         DEBUG(0,("credentials chain check failed\n"));
334                         return NT_STATUS_ACCESS_DENIED;
335                 }
336
337                 if (NT_STATUS_IS_ERR(result)) {
338                         break;
339                 }
340
341                 session_key = data_blob_const(ctx->cli->dc->sess_key, 16);
342
343                 samsync_fix_delta_array(mem_ctx,
344                                         &session_key,
345                                         database_id,
346                                         delta_enum_array);
347
348                 /* Process results */
349                 callback_status = ctx->delta_fn(mem_ctx, database_id,
350                                                 delta_enum_array,
351                                                 NT_STATUS_IS_OK(result), ctx);
352                 if (!NT_STATUS_IS_OK(callback_status)) {
353                         result = callback_status;
354                         goto out;
355                 }
356
357                 TALLOC_FREE(delta_enum_array);
358
359                 /* Increment sync_context */
360                 sync_context += 1;
361
362         } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
363
364  out:
365         if (NT_STATUS_IS_ERR(result) && !ctx->error_message) {
366
367                 ctx->error_message = talloc_asprintf(ctx,
368                         "Failed to fetch %s database: %s",
369                         samsync_database_str(database_id),
370                         nt_errstr(result));
371
372                 if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) {
373
374                         ctx->error_message =
375                                 talloc_asprintf_append(ctx->error_message,
376                                         "\nPerhaps %s is a Windows native mode domain?",
377                                         ctx->domain_name);
378                 }
379         }
380
381         talloc_destroy(mem_ctx);
382
383         return result;
384 }
385
386 /**
387  * pull_netr_AcctLockStr
388  */
389
390 NTSTATUS pull_netr_AcctLockStr(TALLOC_CTX *mem_ctx,
391                                struct lsa_BinaryString *r,
392                                struct netr_AcctLockStr **str_p)
393 {
394         struct netr_AcctLockStr *str;
395         enum ndr_err_code ndr_err;
396         DATA_BLOB blob;
397
398         if (!mem_ctx || !r || !str_p) {
399                 return NT_STATUS_INVALID_PARAMETER;
400         }
401
402         *str_p = NULL;
403
404         str = TALLOC_ZERO_P(mem_ctx, struct netr_AcctLockStr);
405         if (!str) {
406                 return NT_STATUS_NO_MEMORY;
407         }
408
409         blob = data_blob_const(r->array, r->length);
410
411         ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, NULL, str,
412                        (ndr_pull_flags_fn_t)ndr_pull_netr_AcctLockStr);
413
414         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
415                 return ndr_map_error2ntstatus(ndr_err);
416         }
417
418         *str_p = str;
419
420         return NT_STATUS_OK;
421 }
422