s4:libnet/py_net.c: "py_net_finddc" - add an "address" parameter
[sfrench/samba-autobuild/.git] / source4 / 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    
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 3 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, see <http://www.gnu.org/licenses/>.
20 */
21
22
23 #include "includes.h"
24 #include "libnet/libnet.h"
25 #include "libcli/auth/libcli_auth.h"
26 #include "../libcli/samsync/samsync.h"
27 #include "auth/gensec/gensec.h"
28 #include "auth/gensec/schannel.h"
29 #include "auth/credentials/credentials.h"
30 #include "libcli/auth/schannel.h"
31 #include "librpc/gen_ndr/ndr_netlogon.h"
32 #include "librpc/gen_ndr/ndr_netlogon_c.h"
33 #include "param/param.h"
34
35 NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamSync *r)
36 {
37         NTSTATUS nt_status, dbsync_nt_status;
38         TALLOC_CTX *samsync_ctx, *loop_ctx, *delta_ctx;
39         struct netlogon_creds_CredentialState *creds;
40         struct netr_DatabaseSync dbsync;
41         struct netr_Authenticator credential, return_authenticator;
42         struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
43         struct cli_credentials *machine_account;
44         struct dcerpc_pipe *p;
45         struct libnet_context *machine_net_ctx;
46         struct libnet_RpcConnect *c;
47         struct libnet_SamSync_state *state;
48         const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; 
49         unsigned int i;
50
51         samsync_ctx = talloc_named(mem_ctx, 0, "SamSync top context");
52
53         if (!r->in.machine_account) { 
54                 machine_account = cli_credentials_init(samsync_ctx);
55                 if (!machine_account) {
56                         talloc_free(samsync_ctx);
57                         return NT_STATUS_NO_MEMORY;
58                 }
59                 cli_credentials_set_conf(machine_account, ctx->lp_ctx);
60                 nt_status = cli_credentials_set_machine_account(machine_account, ctx->lp_ctx);
61                 if (!NT_STATUS_IS_OK(nt_status)) {
62                         r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?");
63                         talloc_free(samsync_ctx);
64                         return nt_status;
65                 }
66         } else {
67                 machine_account = r->in.machine_account;
68         }
69
70         /* We cannot do this unless we are a BDC.  Check, before we get odd errors later */
71         if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) {
72                 r->out.error_string
73                         = talloc_asprintf(mem_ctx, 
74                                           "Our join to domain %s is not as a BDC (%d), please rejoin as a BDC",
75                                           cli_credentials_get_domain(machine_account),
76                                           cli_credentials_get_secure_channel_type(machine_account));
77                 talloc_free(samsync_ctx);
78                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
79         }
80
81         c = talloc_zero(samsync_ctx, struct libnet_RpcConnect);
82         if (!c) {
83                 r->out.error_string = NULL;
84                 talloc_free(samsync_ctx);
85                 return NT_STATUS_NO_MEMORY;
86         }
87
88         c->level              = LIBNET_RPC_CONNECT_DC_INFO;
89         if (r->in.binding_string) {
90                 c->in.binding = r->in.binding_string;
91                 c->in.name    = NULL;
92         } else {
93                 c->in.binding = NULL;
94                 c->in.name    = cli_credentials_get_domain(machine_account);
95         }
96         
97         /* prepare connect to the NETLOGON pipe of PDC */
98         c->in.dcerpc_iface      = &ndr_table_netlogon;
99
100         /* We must do this as the machine, not as any command-line
101          * user.  So we override the credentials in the
102          * libnet_context */
103         machine_net_ctx = talloc(samsync_ctx, struct libnet_context);
104         if (!machine_net_ctx) {
105                 r->out.error_string = NULL;
106                 talloc_free(samsync_ctx);
107                 return NT_STATUS_NO_MEMORY;
108         }
109         *machine_net_ctx = *ctx;
110         machine_net_ctx->cred = machine_account;
111
112         /* connect to the NETLOGON pipe of the PDC */
113         nt_status = libnet_RpcConnect(machine_net_ctx, samsync_ctx, c);
114         if (!NT_STATUS_IS_OK(nt_status)) {
115                 if (r->in.binding_string) {
116                         r->out.error_string = talloc_asprintf(mem_ctx,
117                                                               "Connection to NETLOGON pipe of DC %s failed: %s",
118                                                               r->in.binding_string, c->out.error_string);
119                 } else {
120                         r->out.error_string = talloc_asprintf(mem_ctx,
121                                                               "Connection to NETLOGON pipe of DC for %s failed: %s",
122                                                               c->in.name, c->out.error_string);
123                 }
124                 talloc_free(samsync_ctx);
125                 return nt_status;
126         }
127
128         /* This makes a new pipe, on which we can do schannel.  We
129          * should do this in the RpcConnect code, but the abstaction
130          * layers do not suit yet */
131
132         nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p,
133                                                 c->out.dcerpc_pipe->binding);
134
135         if (!NT_STATUS_IS_OK(nt_status)) {
136                 r->out.error_string = talloc_asprintf(mem_ctx,
137                                                       "Secondary connection to NETLOGON pipe of DC %s failed: %s",
138                                                       dcerpc_server_name(p), nt_errstr(nt_status));
139                 talloc_free(samsync_ctx);
140                 return nt_status;
141         }
142
143         nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &ndr_table_netlogon,
144                                               machine_account, ctx->lp_ctx, DCERPC_AUTH_LEVEL_PRIVACY);
145
146         if (!NT_STATUS_IS_OK(nt_status)) {
147                 r->out.error_string = talloc_asprintf(mem_ctx,
148                                                       "SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s",
149                                                       dcerpc_server_name(p), nt_errstr(nt_status));
150                 talloc_free(samsync_ctx);
151                 return nt_status;
152         }
153
154         state = talloc(samsync_ctx, struct libnet_SamSync_state);
155         if (!state) {
156                 r->out.error_string = NULL;
157                 talloc_free(samsync_ctx);
158                 return nt_status;
159         }               
160
161         state->domain_name     = c->out.domain_name;
162         state->domain_sid      = c->out.domain_sid;
163         state->realm           = c->out.realm;
164         state->domain_guid     = c->out.guid;
165         state->machine_net_ctx = machine_net_ctx;
166         state->netlogon_pipe   = p;
167
168         /* initialise the callback layer.  It may wish to contact the
169          * server with ldap, now we know the name */
170         
171         if (r->in.init_fn) {
172                 char *error_string;
173                 nt_status = r->in.init_fn(samsync_ctx, 
174                                           r->in.fn_ctx,
175                                           state, 
176                                           &error_string); 
177                 if (!NT_STATUS_IS_OK(nt_status)) {
178                         r->out.error_string = talloc_steal(mem_ctx, error_string);
179                         talloc_free(samsync_ctx);
180                         return nt_status;
181                 }
182         }
183
184         /* get NETLOGON credentials */
185
186         nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, samsync_ctx, &creds);
187         if (!NT_STATUS_IS_OK(nt_status)) {
188                 r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer");
189                 talloc_free(samsync_ctx);
190                 return nt_status;
191         }
192
193         /* Setup details for the synchronisation */
194
195         ZERO_STRUCT(return_authenticator);
196
197         dbsync.in.logon_server = talloc_asprintf(samsync_ctx, "\\\\%s", dcerpc_server_name(p));
198         dbsync.in.computername = cli_credentials_get_workstation(machine_account);
199         dbsync.in.preferredmaximumlength = (uint32_t)-1;
200         dbsync.in.return_authenticator = &return_authenticator;
201         dbsync.out.return_authenticator = &return_authenticator;
202         dbsync.out.delta_enum_array = &delta_enum_array;
203
204         for (i=0;i< ARRAY_SIZE(database_ids); i++) {
205
206                 uint32_t sync_context = 0;
207
208                 dbsync.in.database_id = database_ids[i];
209                 dbsync.in.sync_context = &sync_context;
210                 dbsync.out.sync_context = &sync_context;
211                 
212                 do {
213                         uint32_t d;
214                         loop_ctx = talloc_named(samsync_ctx, 0, "DatabaseSync loop context");
215                         netlogon_creds_client_authenticator(creds, &credential);
216
217                         dbsync.in.credential = &credential;
218                         
219                         dbsync_nt_status = dcerpc_netr_DatabaseSync_r(p->binding_handle, loop_ctx, &dbsync);
220                         if (NT_STATUS_IS_OK(dbsync_nt_status) && !NT_STATUS_IS_OK(dbsync.out.result)) {
221                                 dbsync_nt_status = dbsync.out.result;
222                         }
223                         if (!NT_STATUS_IS_OK(dbsync_nt_status) &&
224                             !NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) {
225                                 r->out.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status));
226                                 talloc_free(samsync_ctx);
227                                 return nt_status;
228                         }
229                         
230                         if (!netlogon_creds_client_check(creds, &dbsync.out.return_authenticator->cred)) {
231                                 r->out.error_string = talloc_strdup(mem_ctx, "Credential chaining on incoming DatabaseSync failed");
232                                 talloc_free(samsync_ctx);
233                                 return NT_STATUS_ACCESS_DENIED;
234                         }
235                         
236                         dbsync.in.sync_context = dbsync.out.sync_context;
237                         
238                         /* For every single remote 'delta' entry: */
239                         for (d=0; d < delta_enum_array->num_deltas; d++) {
240                                 char *error_string = NULL;
241                                 delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context");
242                                 /* 'Fix' elements, by decrypting and
243                                  * de-obfuscating the data */
244                                 nt_status = samsync_fix_delta(delta_ctx, 
245                                                               creds, 
246                                                               dbsync.in.database_id,
247                                                               &delta_enum_array->delta_enum[d]);
248                                 if (!NT_STATUS_IS_OK(nt_status)) {
249                                         r->out.error_string = talloc_steal(mem_ctx, error_string);
250                                         talloc_free(samsync_ctx);
251                                         return nt_status;
252                                 }
253
254                                 /* Now call the callback.  This will
255                                  * do something like print the data or
256                                  * write to an ldb */
257                                 nt_status = r->in.delta_fn(delta_ctx, 
258                                                            r->in.fn_ctx,
259                                                            dbsync.in.database_id,
260                                                            &delta_enum_array->delta_enum[d],
261                                                            &error_string);
262                                 if (!NT_STATUS_IS_OK(nt_status)) {
263                                         r->out.error_string = talloc_steal(mem_ctx, error_string);
264                                         talloc_free(samsync_ctx);
265                                         return nt_status;
266                                 }
267                                 talloc_free(delta_ctx);
268                         }
269                         talloc_free(loop_ctx);
270                 } while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES));
271                 
272                 if (!NT_STATUS_IS_OK(dbsync_nt_status)) {
273                         r->out.error_string = talloc_asprintf(mem_ctx, "libnet_SamSync_netlogon failed: unexpected inconsistancy. Should not get error %s here", nt_errstr(nt_status));
274                         talloc_free(samsync_ctx);
275                         return dbsync_nt_status;
276                 }
277                 nt_status = NT_STATUS_OK;
278         }
279         talloc_free(samsync_ctx);
280         return nt_status;
281 }
282