c9389ea7ddc028a90349cf7cf5a6e37ebb122a61
[jelmer/samba4-debian.git] / source / winbind / wb_init_domain.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    A composite API for initializing a domain
5
6    Copyright (C) Volker Lendecke 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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "libcli/composite/composite.h"
25 #include "libcli/smb_composite/smb_composite.h"
26 #include "winbind/wb_async_helpers.h"
27 #include "winbind/wb_server.h"
28 #include "smbd/service_stream.h"
29
30 #include "librpc/gen_ndr/nbt.h"
31 #include "librpc/gen_ndr/samr.h"
32 #include "lib/messaging/irpc.h"
33 #include "librpc/gen_ndr/irpc.h"
34 #include "librpc/gen_ndr/ndr_irpc.h"
35 #include "libcli/raw/libcliraw.h"
36 #include "librpc/gen_ndr/ndr_netlogon.h"
37 #include "librpc/gen_ndr/ndr_lsa.h"
38 #include "libcli/auth/credentials.h"
39
40
41 /*
42  * Initialize a domain:
43  *
44  * - With schannel credentials, try to open the SMB connection with the
45  *   machine creds. This works against W2k3SP1 with an NTLMSSP session
46  *   setup. Fall back to anonymous.
47  *
48  * - If we have schannel creds, do the auth2 and open the schannel'ed netlogon
49  *   pipe.
50  *
51  * - Open LSA. If we have machine creds, try to open with ntlmssp. Fall back
52  *   to schannel and then to anon bind.
53  *
54  * - With queryinfopolicy, verify that we're talking to the right domain
55  *
56  * A bit complex, but with all the combinations I think it's the best we can
57  * get. NT4, W2k3 and W2k all have different combinations, but in the end we
58  * have a signed&sealed lsa connection on all of them.
59  *
60  * Not sure if it is overkill, but it seems to work.
61  */
62
63 struct init_domain_state {
64         struct composite_context *ctx;
65         struct wbsrv_domain *domain;
66
67         int num_dcs;
68         struct nbt_dc_name *dcs;
69
70         struct smb_composite_connect conn;
71
72         struct dcerpc_pipe *auth2_pipe;
73         struct dcerpc_pipe *netlogon_pipe;
74
75         struct dcerpc_pipe *lsa_pipe;
76         struct policy_handle *lsa_policy;
77
78         struct lsa_QueryInfoPolicy queryinfo;
79 };
80
81 static void init_domain_recv_dcs(struct composite_context *ctx);
82 static void init_domain_recv_tree(struct composite_context *ctx);
83 static void init_domain_recv_netlogoncreds(struct composite_context *ctx);
84 static void init_domain_recv_netlogonpipe(struct composite_context *ctx);
85 static void init_domain_recv_lsa(struct composite_context *ctx);
86 static void init_domain_recv_queryinfo(struct rpc_request *req);
87
88 struct composite_context *wb_init_domain_send(struct wbsrv_domain *domain,
89                                               struct event_context *event_ctx,
90                                               struct messaging_context *msg_ctx)
91 {
92         struct composite_context *result, *ctx;
93         struct init_domain_state *state;
94
95         result = talloc(domain, struct composite_context);
96         if (result == NULL) goto failed;
97         result->state = COMPOSITE_STATE_IN_PROGRESS;
98         result->async.fn = NULL;
99         result->event_ctx = event_ctx;
100
101         state = talloc_zero(result, struct init_domain_state);
102         if (state == NULL) goto failed;
103         state->ctx = result;
104         result->private_data = state;
105
106         state->domain = domain;
107
108         if (state->domain->schannel_creds != NULL) {
109                 talloc_free(state->domain->schannel_creds);
110         }
111
112         state->domain->schannel_creds = cli_credentials_init(state->domain);
113         if (state->domain->schannel_creds == NULL) goto failed;
114         cli_credentials_set_conf(state->domain->schannel_creds);
115         state->ctx->status =
116                 cli_credentials_set_machine_account(state->domain->
117                                                     schannel_creds);
118         if (!NT_STATUS_IS_OK(state->ctx->status)) goto failed;
119
120         ctx = wb_finddcs_send(domain->name, domain->sid, event_ctx, msg_ctx);
121         if (ctx == NULL) goto failed;
122
123         ctx->async.fn = init_domain_recv_dcs;
124         ctx->async.private_data = state;
125         return result;
126
127  failed:
128         talloc_free(result);
129         return NULL;
130 }
131
132 static void init_domain_recv_dcs(struct composite_context *ctx)
133 {
134         struct init_domain_state *state =
135                 talloc_get_type(ctx->async.private_data,
136                                 struct init_domain_state);
137
138         state->ctx->status = wb_finddcs_recv(ctx, state, &state->num_dcs,
139                                              &state->dcs);
140         if (!composite_is_ok(state->ctx)) return;
141
142         if (state->num_dcs < 1) {
143                 composite_error(state->ctx, NT_STATUS_NO_LOGON_SERVERS);
144                 return;
145         }
146
147         state->conn.in.dest_host = state->dcs[0].address;
148         state->conn.in.port = 0;
149         state->conn.in.called_name = state->dcs[0].name;
150         state->conn.in.service = "IPC$";
151         state->conn.in.service_type = "IPC";
152         state->conn.in.workgroup = state->domain->name;
153
154         state->conn.in.credentials = state->domain->schannel_creds;
155
156         if (state->conn.in.credentials == NULL) {
157                 state->conn.in.credentials = cli_credentials_init(state);
158                 if (composite_nomem(state->conn.in.credentials, state->ctx)) {
159                         return;
160                 }
161                 cli_credentials_set_conf(state->conn.in.credentials);
162                 cli_credentials_set_anonymous(state->conn.in.credentials);
163         }
164                 
165         state->conn.in.fallback_to_anonymous = True;
166
167         ctx = smb_composite_connect_send(&state->conn, state,
168                                          state->ctx->event_ctx);
169         composite_continue(state->ctx, ctx, init_domain_recv_tree, state);
170 }
171
172 static void init_domain_recv_tree(struct composite_context *ctx)
173 {
174         struct init_domain_state *state =
175                 talloc_get_type(ctx->async.private_data,
176                                 struct init_domain_state);
177
178         state->ctx->status = smb_composite_connect_recv(ctx, state);
179         if (!composite_is_ok(state->ctx)) return;
180
181         if (state->domain->schannel_creds == NULL) {
182                 /* No chance to open netlogon */
183                 ctx = wb_connect_lsa_send(state->conn.out.tree, NULL);
184                 composite_continue(state->ctx, ctx,
185                                    init_domain_recv_lsa, state);
186                 return;
187         }
188
189         ctx = wb_get_schannel_creds_send(state->domain->schannel_creds,
190                                          state->conn.out.tree,
191                                          state->ctx->event_ctx);
192         composite_continue(state->ctx, ctx,
193                            init_domain_recv_netlogoncreds, state);
194 }
195
196 static void init_domain_recv_netlogoncreds(struct composite_context *ctx)
197 {
198         struct init_domain_state *state =
199                 talloc_get_type(ctx->async.private_data,
200                                 struct init_domain_state);
201         struct smbcli_tree *tree = NULL;
202
203         state->ctx->status = wb_get_schannel_creds_recv(ctx, state,
204                                                         &state->auth2_pipe);
205         if (!composite_is_ok(state->ctx)) return;
206
207         talloc_unlink(state, state->conn.out.tree); /* The pipe owns it now */
208
209         state->netlogon_pipe = dcerpc_pipe_init(state, state->ctx->event_ctx);
210         if (composite_nomem(state->netlogon_pipe, state->ctx)) return;
211
212         if (state->auth2_pipe != NULL) {
213                 tree = dcerpc_smb_tree(state->auth2_pipe->conn);
214         }
215
216         if (tree == NULL) {
217                 composite_error(state->ctx, NT_STATUS_INTERNAL_ERROR);
218                 return;
219         }
220
221         ctx = dcerpc_pipe_open_smb_send(state->netlogon_pipe->conn, tree,
222                                         "\\netlogon");
223         composite_continue(state->ctx, ctx,
224                            init_domain_recv_netlogonpipe, state);
225 }
226
227 static void init_domain_recv_netlogonpipe(struct composite_context *ctx)
228 {
229         struct init_domain_state *state =
230                 talloc_get_type(ctx->async.private_data,
231                                 struct init_domain_state);
232
233         state->ctx->status = dcerpc_pipe_open_smb_recv(ctx);
234         if (!composite_is_ok(state->ctx)) return;
235
236         state->netlogon_pipe->conn->flags |= (DCERPC_SIGN | DCERPC_SEAL);
237         state->ctx->status =
238                 dcerpc_bind_auth_password(state->netlogon_pipe,
239                                           DCERPC_NETLOGON_UUID,
240                                           DCERPC_NETLOGON_VERSION, 
241                                           state->domain->schannel_creds,
242                                           DCERPC_AUTH_TYPE_SCHANNEL,
243                                           NULL);
244
245         ctx = wb_connect_lsa_send(state->conn.out.tree,
246                                   state->domain->schannel_creds);
247         composite_continue(state->ctx, ctx, init_domain_recv_lsa, state);
248 }
249
250 static void init_domain_recv_lsa(struct composite_context *ctx)
251 {
252         struct init_domain_state *state =
253                 talloc_get_type(ctx->async.private_data,
254                                 struct init_domain_state);
255
256         struct rpc_request *req;
257         uint8_t auth_type;
258
259         state->ctx->status = wb_connect_lsa_recv(ctx, state, &auth_type,
260                                                  &state->lsa_pipe,
261                                                  &state->lsa_policy);
262         if (!composite_is_ok(state->ctx)) return;
263
264         if (state->auth2_pipe == NULL) {
265                 /* Give the tree to the LSA pipe. If auth2_pipe exists we have
266                  * given it to that already */
267                 talloc_unlink(state, state->conn.out.tree);
268                 state->conn.out.tree = NULL;
269         }
270
271         state->queryinfo.in.handle = state->lsa_policy;
272         state->queryinfo.in.level = LSA_POLICY_INFO_ACCOUNT_DOMAIN;
273
274         req = dcerpc_lsa_QueryInfoPolicy_send(state->lsa_pipe, state,
275                                               &state->queryinfo);
276         composite_continue_rpc(state->ctx, req,
277                                init_domain_recv_queryinfo, state);
278 }
279
280 static void init_domain_recv_queryinfo(struct rpc_request *req)
281 {
282         struct init_domain_state *state =
283                 talloc_get_type(req->async.private, struct init_domain_state);
284         struct lsa_DomainInfo *dominfo;
285
286         state->ctx->status = dcerpc_ndr_request_recv(req);
287         if (!composite_is_ok(state->ctx)) return;
288         state->ctx->status = state->queryinfo.out.result;
289         if (!composite_is_ok(state->ctx)) return;
290
291         dominfo = &state->queryinfo.out.info->account_domain;
292
293         if (strcasecmp(state->domain->name, dominfo->name.string) != 0) {
294                 DEBUG(2, ("Expected domain name %s, DC %s said %s\n",
295                           state->domain->name,
296                           dcerpc_server_name(state->lsa_pipe),
297                           dominfo->name.string));
298                 composite_error(state->ctx, NT_STATUS_INVALID_DOMAIN_STATE);
299                 return;
300         }
301
302         if (!dom_sid_equal(state->domain->sid, dominfo->sid)) {
303                 DEBUG(2, ("Expected domain sid %s, DC %s said %s\n",
304                           dom_sid_string(state, state->domain->sid),
305                           dcerpc_server_name(state->lsa_pipe),
306                           dom_sid_string(state, dominfo->sid)));
307                 composite_error(state->ctx, NT_STATUS_INVALID_DOMAIN_STATE);
308                 return;
309         }
310
311         composite_done(state->ctx);
312 }
313
314 NTSTATUS wb_init_domain_recv(struct composite_context *c)
315 {
316         NTSTATUS status = composite_wait(c);
317         if (NT_STATUS_IS_OK(status)) {
318                 struct init_domain_state *state =
319                         talloc_get_type(c->private_data,
320                                         struct init_domain_state);
321                 struct wbsrv_domain *domain = state->domain;
322
323                 talloc_free(domain->netlogon_auth2_pipe);
324                 domain->netlogon_auth2_pipe =
325                         talloc_steal(domain, state->auth2_pipe);
326
327                 talloc_free(domain->netlogon_pipe);
328                 domain->netlogon_pipe =
329                         talloc_steal(domain, state->netlogon_pipe);
330
331                 talloc_free(domain->lsa_pipe);
332                 domain->lsa_pipe =
333                         talloc_steal(domain, state->lsa_pipe);
334
335                 talloc_free(domain->lsa_policy);
336                 domain->lsa_policy =
337                         talloc_steal(domain, state->lsa_policy);
338
339                 domain->initialized = True;
340         }
341         talloc_free(c);
342         return status;
343 }
344
345 NTSTATUS wb_init_domain(struct wbsrv_domain *domain,
346                         struct event_context *event_ctx,
347                         struct messaging_context *messaging_ctx)
348 {
349         struct composite_context *c =
350                 wb_init_domain_send(domain, event_ctx, messaging_ctx);
351         return wb_init_domain_recv(c);
352 }