Merge branch 'v4-0-stable' into newmaster
[samba.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_samsync.h"
26 #include "../lib/crypto/crypto.h"
27 #include "../libcli/samsync/samsync.h"
28 #include "../libcli/auth/libcli_auth.h"
29 #include "../librpc/gen_ndr/ndr_netlogon.h"
30 #include "../librpc/gen_ndr/cli_netlogon.h"
31 #include "../libcli/security/dom_sid.h"
32
33 /**
34  * Fix up the delta, dealing with encryption issues so that the final
35  * callback need only do the printing or application logic
36  */
37
38 static NTSTATUS samsync_fix_delta_array(TALLOC_CTX *mem_ctx,
39                                         struct netlogon_creds_CredentialState *creds,
40                                         enum netr_SamDatabaseID database_id,
41                                         struct netr_DELTA_ENUM_ARRAY *r)
42 {
43         NTSTATUS status;
44         int i;
45
46         for (i = 0; i < r->num_deltas; i++) {
47
48                 status = samsync_fix_delta(mem_ctx,
49                                            creds,
50                                            database_id,
51                                            &r->delta_enum[i]);
52                 if (!NT_STATUS_IS_OK(status)) {
53                         return status;
54                 }
55         }
56
57         return NT_STATUS_OK;
58 }
59
60 /**
61  * libnet_samsync_init_context
62  */
63
64 NTSTATUS libnet_samsync_init_context(TALLOC_CTX *mem_ctx,
65                                      const struct dom_sid *domain_sid,
66                                      struct samsync_context **ctx_p)
67 {
68         struct samsync_context *ctx;
69
70         *ctx_p = NULL;
71
72         ctx = TALLOC_ZERO_P(mem_ctx, struct samsync_context);
73         NT_STATUS_HAVE_NO_MEMORY(ctx);
74
75         if (domain_sid) {
76                 ctx->domain_sid = dom_sid_dup(mem_ctx, domain_sid);
77                 NT_STATUS_HAVE_NO_MEMORY(ctx->domain_sid);
78
79                 ctx->domain_sid_str = sid_string_talloc(mem_ctx, ctx->domain_sid);
80                 NT_STATUS_HAVE_NO_MEMORY(ctx->domain_sid_str);
81         }
82
83         *ctx_p = ctx;
84
85         return NT_STATUS_OK;
86 }
87
88 /**
89  * samsync_database_str
90  */
91
92 static const char *samsync_database_str(enum netr_SamDatabaseID database_id)
93 {
94
95         switch (database_id) {
96                 case SAM_DATABASE_DOMAIN:
97                         return "DOMAIN";
98                 case SAM_DATABASE_BUILTIN:
99                         return "BUILTIN";
100                 case SAM_DATABASE_PRIVS:
101                         return "PRIVS";
102                 default:
103                         return "unknown";
104         }
105 }
106
107 /**
108  * samsync_debug_str
109  */
110
111 static const char *samsync_debug_str(TALLOC_CTX *mem_ctx,
112                                      enum net_samsync_mode mode,
113                                      enum netr_SamDatabaseID database_id)
114 {
115         const char *action = NULL;
116
117         switch (mode) {
118                 case NET_SAMSYNC_MODE_DUMP:
119                         action = "Dumping (to stdout)";
120                         break;
121                 case NET_SAMSYNC_MODE_FETCH_PASSDB:
122                         action = "Fetching (to passdb)";
123                         break;
124                 case NET_SAMSYNC_MODE_FETCH_LDIF:
125                         action = "Fetching (to ldif)";
126                         break;
127                 case NET_SAMSYNC_MODE_FETCH_KEYTAB:
128                         action = "Fetching (to keytab)";
129                         break;
130                 default:
131                         action = "Unknown";
132                         break;
133         }
134
135         return talloc_asprintf(mem_ctx, "%s %s database",
136                                 action, samsync_database_str(database_id));
137 }
138
139 /**
140  * libnet_samsync
141  */
142
143 static void libnet_init_netr_ChangeLogEntry(struct samsync_object *o,
144                                             struct netr_ChangeLogEntry *e)
145 {
146         ZERO_STRUCTP(e);
147
148         e->db_index             = o->database_id;
149         e->delta_type           = o->object_type;
150
151         switch (e->delta_type) {
152                 case NETR_DELTA_DOMAIN:
153                 case NETR_DELTA_DELETE_GROUP:
154                 case NETR_DELTA_RENAME_GROUP:
155                 case NETR_DELTA_DELETE_USER:
156                 case NETR_DELTA_RENAME_USER:
157                 case NETR_DELTA_DELETE_ALIAS:
158                 case NETR_DELTA_RENAME_ALIAS:
159                 case NETR_DELTA_DELETE_TRUST:
160                 case NETR_DELTA_DELETE_ACCOUNT:
161                 case NETR_DELTA_DELETE_SECRET:
162                 case NETR_DELTA_DELETE_GROUP2:
163                 case NETR_DELTA_DELETE_USER2:
164                 case NETR_DELTA_MODIFY_COUNT:
165                         break;
166                 case NETR_DELTA_USER:
167                 case NETR_DELTA_GROUP:
168                 case NETR_DELTA_GROUP_MEMBER:
169                 case NETR_DELTA_ALIAS:
170                 case NETR_DELTA_ALIAS_MEMBER:
171                         e->object_rid = o->object_identifier.rid;
172                         break;
173                 case NETR_DELTA_SECRET:
174                         e->object.object_name = o->object_identifier.name;
175                         e->flags = NETR_CHANGELOG_NAME_INCLUDED;
176                         break;
177                 case NETR_DELTA_TRUSTED_DOMAIN:
178                 case NETR_DELTA_ACCOUNT:
179                 case NETR_DELTA_POLICY:
180                         e->object.object_sid = o->object_identifier.sid;
181                         e->flags = NETR_CHANGELOG_SID_INCLUDED;
182                         break;
183                 default:
184                         break;
185         }
186 }
187
188 /**
189  * libnet_samsync_delta
190  */
191
192 static NTSTATUS libnet_samsync_delta(TALLOC_CTX *mem_ctx,
193                                      enum netr_SamDatabaseID database_id,
194                                      uint64_t *sequence_num,
195                                      struct samsync_context *ctx,
196                                      struct netr_ChangeLogEntry *e)
197 {
198         NTSTATUS result;
199         NTSTATUS callback_status;
200         const char *logon_server = ctx->cli->desthost;
201         const char *computername = global_myname();
202         struct netr_Authenticator credential;
203         struct netr_Authenticator return_authenticator;
204         uint16_t restart_state = 0;
205         uint32_t sync_context = 0;
206
207         ZERO_STRUCT(return_authenticator);
208
209         do {
210                 struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
211
212                 netlogon_creds_client_authenticator(ctx->cli->dc, &credential);
213
214                 if (ctx->single_object_replication &&
215                     !ctx->force_full_replication) {
216                         result = rpccli_netr_DatabaseRedo(ctx->cli, mem_ctx,
217                                                           logon_server,
218                                                           computername,
219                                                           &credential,
220                                                           &return_authenticator,
221                                                           *e,
222                                                           0,
223                                                           &delta_enum_array);
224                 } else if (!ctx->force_full_replication &&
225                            sequence_num && (*sequence_num > 0)) {
226                         result = rpccli_netr_DatabaseDeltas(ctx->cli, mem_ctx,
227                                                             logon_server,
228                                                             computername,
229                                                             &credential,
230                                                             &return_authenticator,
231                                                             database_id,
232                                                             sequence_num,
233                                                             &delta_enum_array,
234                                                             0xffff);
235                 } else {
236                         result = rpccli_netr_DatabaseSync2(ctx->cli, mem_ctx,
237                                                            logon_server,
238                                                            computername,
239                                                            &credential,
240                                                            &return_authenticator,
241                                                            database_id,
242                                                            restart_state,
243                                                            &sync_context,
244                                                            &delta_enum_array,
245                                                            0xffff);
246                 }
247
248                 if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) {
249                         return result;
250                 }
251
252                 /* Check returned credentials. */
253                 if (!netlogon_creds_client_check(ctx->cli->dc,
254                                                  &return_authenticator.cred)) {
255                         DEBUG(0,("credentials chain check failed\n"));
256                         return NT_STATUS_ACCESS_DENIED;
257                 }
258
259                 if (NT_STATUS_IS_ERR(result)) {
260                         break;
261                 }
262
263                 samsync_fix_delta_array(mem_ctx,
264                                         ctx->cli->dc,
265                                         database_id,
266                                         delta_enum_array);
267
268                 /* Process results */
269                 callback_status = ctx->ops->process_objects(mem_ctx, database_id,
270                                                             delta_enum_array,
271                                                             sequence_num,
272                                                             ctx);
273                 if (!NT_STATUS_IS_OK(callback_status)) {
274                         result = callback_status;
275                         goto out;
276                 }
277
278                 TALLOC_FREE(delta_enum_array);
279
280         } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
281
282  out:
283
284         return result;
285 }
286
287 /**
288  * libnet_samsync
289  */
290
291 NTSTATUS libnet_samsync(enum netr_SamDatabaseID database_id,
292                         struct samsync_context *ctx)
293 {
294         NTSTATUS status = NT_STATUS_OK;
295         NTSTATUS callback_status;
296         TALLOC_CTX *mem_ctx;
297         const char *debug_str;
298         uint64_t sequence_num = 0;
299         int i = 0;
300
301         if (!(mem_ctx = talloc_new(ctx))) {
302                 return NT_STATUS_NO_MEMORY;
303         }
304
305         if (!ctx->ops) {
306                 return NT_STATUS_INVALID_PARAMETER;
307         }
308
309         if (ctx->ops->startup) {
310                 status = ctx->ops->startup(mem_ctx, ctx,
311                                            database_id, &sequence_num);
312                 if (!NT_STATUS_IS_OK(status)) {
313                         goto done;
314                 }
315         }
316
317         debug_str = samsync_debug_str(mem_ctx, ctx->mode, database_id);
318         if (debug_str) {
319                 d_fprintf(stderr, "%s\n", debug_str);
320         }
321
322         if (!ctx->single_object_replication) {
323                 status = libnet_samsync_delta(mem_ctx, database_id,
324                                               &sequence_num, ctx, NULL);
325                 goto done;
326         }
327
328         for (i=0; i<ctx->num_objects; i++) {
329
330                 struct netr_ChangeLogEntry e;
331
332                 if (ctx->objects[i].database_id != database_id) {
333                         continue;
334                 }
335
336                 libnet_init_netr_ChangeLogEntry(&ctx->objects[i], &e);
337
338                 status = libnet_samsync_delta(mem_ctx, database_id,
339                                               &sequence_num, ctx, &e);
340                 if (!NT_STATUS_IS_OK(status)) {
341                         goto done;
342                 }
343         }
344
345  done:
346
347         if (NT_STATUS_IS_OK(status) && ctx->ops->finish) {
348                 callback_status = ctx->ops->finish(mem_ctx, ctx,
349                                                    database_id, sequence_num);
350                 if (!NT_STATUS_IS_OK(callback_status)) {
351                         status = callback_status;
352                 }
353         }
354
355         if (NT_STATUS_IS_ERR(status) && !ctx->error_message) {
356
357                 ctx->error_message = talloc_asprintf(ctx,
358                         "Failed to fetch %s database: %s",
359                         samsync_database_str(database_id),
360                         nt_errstr(status));
361
362                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
363
364                         ctx->error_message =
365                                 talloc_asprintf_append(ctx->error_message,
366                                         "\nPerhaps %s is a Windows native mode domain?",
367                                         ctx->domain_name);
368                 }
369         }
370
371         talloc_destroy(mem_ctx);
372
373         return status;
374 }
375
376 /**
377  * pull_netr_AcctLockStr
378  */
379
380 NTSTATUS pull_netr_AcctLockStr(TALLOC_CTX *mem_ctx,
381                                struct lsa_BinaryString *r,
382                                struct netr_AcctLockStr **str_p)
383 {
384         struct netr_AcctLockStr *str;
385         enum ndr_err_code ndr_err;
386         DATA_BLOB blob;
387
388         if (!mem_ctx || !r || !str_p) {
389                 return NT_STATUS_INVALID_PARAMETER;
390         }
391
392         *str_p = NULL;
393
394         str = TALLOC_ZERO_P(mem_ctx, struct netr_AcctLockStr);
395         if (!str) {
396                 return NT_STATUS_NO_MEMORY;
397         }
398
399         blob = data_blob_const(r->array, r->length);
400
401         ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, str,
402                        (ndr_pull_flags_fn_t)ndr_pull_netr_AcctLockStr);
403
404         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
405                 return ndr_map_error2ntstatus(ndr_err);
406         }
407
408         *str_p = str;
409
410         return NT_STATUS_OK;
411 }
412