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