2 Unix SMB/CIFS implementation.
4 Copyright (C) Volker Lendecke 2005
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 a composite API for initializing a domain
25 #include "libcli/composite/composite.h"
26 #include "libcli/smb_composite/smb_composite.h"
27 #include "winbind/wb_async_helpers.h"
28 #include "winbind/wb_server.h"
29 #include "smbd/service_stream.h"
31 #include "librpc/gen_ndr/nbt.h"
32 #include "librpc/gen_ndr/samr.h"
33 #include "lib/messaging/irpc.h"
34 #include "librpc/gen_ndr/irpc.h"
35 #include "librpc/gen_ndr/ndr_irpc.h"
36 #include "libcli/raw/libcliraw.h"
37 #include "librpc/gen_ndr/ndr_netlogon.h"
38 #include "librpc/gen_ndr/ndr_lsa.h"
39 #include "libcli/auth/credentials.h"
42 /* Helper to initialize LSA with different auth methods and opening the lsa
45 struct init_lsa_state {
46 struct composite_context *ctx;
47 struct dcerpc_pipe *lsa_pipe;
50 struct cli_credentials *creds;
52 struct lsa_ObjectAttribute objectattr;
53 struct lsa_OpenPolicy2 openpolicy;
54 struct policy_handle *handle;
57 static void init_lsa_recv_pipe(struct composite_context *ctx);
58 static void init_lsa_recv_openpol(struct rpc_request *req);
60 static struct composite_context *wb_init_lsa_send(struct smbcli_tree *tree,
62 struct cli_credentials *creds)
64 struct composite_context *result, *ctx;
65 struct init_lsa_state *state;
67 result = talloc(NULL, struct composite_context);
68 if (result == NULL) goto failed;
69 result->state = COMPOSITE_STATE_IN_PROGRESS;
70 result->event_ctx = tree->session->transport->socket->event.ctx;
72 state = talloc(result, struct init_lsa_state);
73 if (state == NULL) goto failed;
75 result->private_data = state;
77 state->auth_type = auth_type;
80 state->lsa_pipe = dcerpc_pipe_init(state, result->event_ctx);
81 if (state->lsa_pipe == NULL) goto failed;
83 ctx = dcerpc_pipe_open_smb_send(state->lsa_pipe->conn, tree,
85 ctx->async.fn = init_lsa_recv_pipe;
86 ctx->async.private_data = state;
94 static void init_lsa_recv_pipe(struct composite_context *ctx)
96 struct init_lsa_state *state =
97 talloc_get_type(ctx->async.private_data,
98 struct init_lsa_state);
99 struct rpc_request *req;
101 state->ctx->status = dcerpc_pipe_open_smb_recv(ctx);
102 if (!comp_is_ok(state->ctx)) return;
104 switch (state->auth_type) {
105 case DCERPC_AUTH_TYPE_NONE:
107 dcerpc_bind_auth_none(state->lsa_pipe,
109 DCERPC_LSARPC_VERSION);
111 case DCERPC_AUTH_TYPE_NTLMSSP:
112 case DCERPC_AUTH_TYPE_SCHANNEL:
113 if (state->creds == NULL) {
114 comp_error(state->ctx, NT_STATUS_INTERNAL_ERROR);
117 state->lsa_pipe->conn->flags |= (DCERPC_SIGN | DCERPC_SEAL);
119 dcerpc_bind_auth_password(state->lsa_pipe,
121 DCERPC_LSARPC_VERSION,
127 state->ctx->status = NT_STATUS_INTERNAL_ERROR;
131 state->handle = talloc(state, struct policy_handle);
132 if (comp_nomem(state->handle, state->ctx)) return;
134 state->openpolicy.in.system_name =
135 talloc_asprintf(state, "\\\\%s",
136 dcerpc_server_name(state->lsa_pipe));
137 ZERO_STRUCT(state->objectattr);
138 state->openpolicy.in.attr = &state->objectattr;
139 state->openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
140 state->openpolicy.out.handle = state->handle;
142 req = dcerpc_lsa_OpenPolicy2_send(state->lsa_pipe, state,
144 rpc_cont(state->ctx, req, init_lsa_recv_openpol, state);
147 static void init_lsa_recv_openpol(struct rpc_request *req)
149 struct init_lsa_state *state =
150 talloc_get_type(req->async.private,
151 struct init_lsa_state);
153 state->ctx->status = dcerpc_ndr_request_recv(req);
154 if (!comp_is_ok(state->ctx)) return;
155 state->ctx->status = state->openpolicy.out.result;
156 if (!comp_is_ok(state->ctx)) return;
158 comp_done(state->ctx);
161 static NTSTATUS wb_init_lsa_recv(struct composite_context *c,
163 struct dcerpc_pipe **lsa_pipe,
164 struct policy_handle **lsa_policy)
166 NTSTATUS status = composite_wait(c);
167 if (NT_STATUS_IS_OK(status)) {
168 struct init_lsa_state *state =
169 talloc_get_type(c->private_data,
170 struct init_lsa_state);
171 *lsa_pipe = talloc_steal(mem_ctx, state->lsa_pipe);
172 *lsa_policy = talloc_steal(mem_ctx, state->handle);
179 * Initialize a domain:
181 * - With schannel credentials, try to open the SMB connection with the machine
182 * creds. Fall back to anonymous.
184 * - If we have schannel creds, do the auth2 and open the schannel'ed netlogon
187 * - Open LSA. If we have machine creds, try to open with ntlmssp. Fall back
188 * to schannel and then to anon bind.
190 * - With queryinfopolicy, verify that we're talking to the right domain
192 * A bit complex, but with all the combinations I think it's the best we can
193 * get. NT4, W2k3 and W2k all have different combinations, but in the end we
194 * have a signed&sealed lsa connection on all of them.
196 * Not sure if it is overkill, but it seems to work.
199 struct init_domain_state {
200 struct composite_context *ctx;
201 struct wbsrv_domain *domain;
204 struct nbt_dc_name *dcs;
206 struct smb_composite_connect conn;
208 struct dcerpc_pipe *auth2_pipe;
209 struct dcerpc_pipe *netlogon_pipe;
211 struct dcerpc_pipe *lsa_pipe;
212 struct policy_handle *lsa_policy;
214 struct lsa_QueryInfoPolicy queryinfo;
217 static void init_domain_recv_dcs(struct composite_context *ctx);
218 static void init_domain_recv_authsmb(struct composite_context *ctx);
219 static void init_domain_anonsmb(struct init_domain_state *state);
220 static void init_domain_recv_anonsmb(struct composite_context *ctx);
221 static void init_domain_openpipes(struct init_domain_state *state);
222 static void init_domain_openlsa(struct init_domain_state *state);
223 static void init_domain_recv_netlogoncreds(struct composite_context *ctx);
224 static void init_domain_recv_netlogonpipe(struct composite_context *ctx);
225 static void init_domain_recv_lsa_ntlmssp(struct composite_context *ctx);
226 static void init_domain_recv_lsa_schannel(struct composite_context *ctx);
227 static void init_domain_recv_lsa_none(struct composite_context *ctx);
228 static void init_domain_check_lsa(struct init_domain_state *state);
229 static void init_domain_recv_queryinfo(struct rpc_request *req);
231 struct composite_context *wb_init_domain_send(struct wbsrv_domain *domain,
232 struct event_context *event_ctx,
233 struct messaging_context *msg_ctx)
235 struct composite_context *result, *ctx;
236 struct init_domain_state *state;
238 result = talloc(domain, struct composite_context);
239 if (result == NULL) goto failed;
240 result->state = COMPOSITE_STATE_IN_PROGRESS;
241 result->async.fn = NULL;
242 result->event_ctx = event_ctx;
244 state = talloc_zero(result, struct init_domain_state);
245 if (state == NULL) goto failed;
247 result->private_data = state;
249 state->domain = domain;
251 if (state->domain->schannel_creds != NULL) {
252 talloc_free(state->domain->schannel_creds);
255 state->domain->schannel_creds = cli_credentials_init(state->domain);
256 if (state->domain->schannel_creds == NULL) goto failed;
257 cli_credentials_set_conf(state->domain->schannel_creds);
259 cli_credentials_set_machine_account(state->domain->
261 if (!NT_STATUS_IS_OK(state->ctx->status)) goto failed;
263 ctx = wb_finddcs_send(domain->name, domain->sid, event_ctx, msg_ctx);
264 if (ctx == NULL) goto failed;
266 ctx->async.fn = init_domain_recv_dcs;
267 ctx->async.private_data = state;
275 static void init_domain_recv_dcs(struct composite_context *ctx)
277 struct init_domain_state *state =
278 talloc_get_type(ctx->async.private_data,
279 struct init_domain_state);
281 state->ctx->status = wb_finddcs_recv(ctx, state, &state->num_dcs,
283 if (!comp_is_ok(state->ctx)) return;
285 if (state->num_dcs < 1) {
286 comp_error(state->ctx, NT_STATUS_NO_LOGON_SERVERS);
290 state->conn.in.dest_host = state->dcs[0].address;
291 state->conn.in.port = 0;
292 state->conn.in.called_name = state->dcs[0].name;
293 state->conn.in.service = "IPC$";
294 state->conn.in.service_type = "IPC";
295 state->conn.in.workgroup = state->domain->name;
297 if (state->domain->schannel_creds != NULL) {
298 /* Try to connect as workstation */
299 state->conn.in.credentials = state->domain->schannel_creds;
300 ctx = smb_composite_connect_send(&state->conn, state,
301 state->ctx->event_ctx);
302 comp_cont(state->ctx, ctx, init_domain_recv_authsmb, state);
306 init_domain_anonsmb(state);
309 static void init_domain_recv_authsmb(struct composite_context *ctx)
311 struct init_domain_state *state =
312 talloc_get_type(ctx->async.private_data,
313 struct init_domain_state);
315 state->ctx->status = smb_composite_connect_recv(ctx, state);
316 if (NT_STATUS_IS_OK(state->ctx->status)) {
317 init_domain_openpipes(state);
321 init_domain_anonsmb(state);
324 static void init_domain_anonsmb(struct init_domain_state *state)
326 struct composite_context *ctx;
328 state->conn.in.credentials = cli_credentials_init(state);
329 if (comp_nomem(state->conn.in.credentials, state->ctx)) return;
330 cli_credentials_set_conf(state->conn.in.credentials);
331 cli_credentials_set_anonymous(state->conn.in.credentials);
332 ctx = smb_composite_connect_send(&state->conn, state,
333 state->ctx->event_ctx);
334 comp_cont(state->ctx, ctx, init_domain_recv_anonsmb, state);
337 static void init_domain_recv_anonsmb(struct composite_context *ctx)
339 struct init_domain_state *state =
340 talloc_get_type(ctx->async.private_data,
341 struct init_domain_state);
343 state->ctx->status = smb_composite_connect_recv(ctx, state);
344 if (!comp_is_ok(state->ctx)) return;
346 init_domain_openpipes(state);
349 static void init_domain_openpipes(struct init_domain_state *state)
351 struct composite_context *ctx;
353 if (state->domain->schannel_creds == NULL) {
354 /* No chance to open netlogon */
355 init_domain_openlsa(state);
359 ctx = wb_get_schannel_creds_send(state->domain->schannel_creds,
360 state->conn.out.tree,
361 state->ctx->event_ctx);
362 comp_cont(state->ctx, ctx, init_domain_recv_netlogoncreds, state);
365 static void init_domain_recv_netlogoncreds(struct composite_context *ctx)
367 struct init_domain_state *state =
368 talloc_get_type(ctx->async.private_data,
369 struct init_domain_state);
370 struct smbcli_tree *tree = NULL;
372 state->ctx->status = wb_get_schannel_creds_recv(ctx, state,
374 if (!comp_is_ok(state->ctx)) return;
376 talloc_unlink(state, state->conn.out.tree); /* The pipe owns it now */
378 state->netlogon_pipe = dcerpc_pipe_init(state, state->ctx->event_ctx);
379 if (comp_nomem(state->netlogon_pipe, state->ctx)) return;
381 if (state->auth2_pipe != NULL) {
382 tree = dcerpc_smb_tree(state->auth2_pipe->conn);
386 comp_error(state->ctx, NT_STATUS_INTERNAL_ERROR);
390 ctx = dcerpc_pipe_open_smb_send(state->netlogon_pipe->conn, tree,
392 comp_cont(state->ctx, ctx, init_domain_recv_netlogonpipe, state);
395 static void init_domain_recv_netlogonpipe(struct composite_context *ctx)
397 struct init_domain_state *state =
398 talloc_get_type(ctx->async.private_data,
399 struct init_domain_state);
401 state->ctx->status = dcerpc_pipe_open_smb_recv(ctx);
402 if (!comp_is_ok(state->ctx)) return;
404 state->netlogon_pipe->conn->flags |= (DCERPC_SIGN | DCERPC_SEAL);
406 dcerpc_bind_auth_password(state->netlogon_pipe,
407 DCERPC_NETLOGON_UUID,
408 DCERPC_NETLOGON_VERSION,
409 state->domain->schannel_creds,
410 DCERPC_AUTH_TYPE_SCHANNEL,
412 if (!comp_is_ok(state->ctx)) return;
414 init_domain_openlsa(state);
417 static void init_domain_openlsa(struct init_domain_state *state)
419 struct composite_context *ctx;
421 if (state->domain->schannel_creds != NULL) {
422 ctx = wb_init_lsa_send(state->conn.out.tree,
423 DCERPC_AUTH_TYPE_NTLMSSP,
424 state->domain->schannel_creds);
425 comp_cont(state->ctx, ctx,
426 init_domain_recv_lsa_ntlmssp, state);
430 ctx = wb_init_lsa_send(state->conn.out.tree, DCERPC_AUTH_TYPE_NONE,
432 comp_cont(state->ctx, ctx, init_domain_recv_lsa_none, state);
435 static void init_domain_recv_lsa_ntlmssp(struct composite_context *ctx)
437 struct init_domain_state *state =
438 talloc_get_type(ctx->async.private_data,
439 struct init_domain_state);
441 state->ctx->status = wb_init_lsa_recv(ctx, state, &state->lsa_pipe,
443 if (NT_STATUS_IS_OK(state->ctx->status)) {
444 init_domain_check_lsa(state);
448 ctx = wb_init_lsa_send(state->conn.out.tree,
449 DCERPC_AUTH_TYPE_SCHANNEL,
450 state->domain->schannel_creds);
451 comp_cont(state->ctx, ctx, init_domain_recv_lsa_schannel, state);
454 static void init_domain_recv_lsa_schannel(struct composite_context *ctx)
456 struct init_domain_state *state =
457 talloc_get_type(ctx->async.private_data,
458 struct init_domain_state);
460 state->ctx->status = wb_init_lsa_recv(ctx, state, &state->lsa_pipe,
462 if (NT_STATUS_IS_OK(state->ctx->status)) {
463 init_domain_check_lsa(state);
467 ctx = wb_init_lsa_send(state->conn.out.tree,
468 DCERPC_AUTH_TYPE_NONE, NULL);
469 comp_cont(state->ctx, ctx, init_domain_recv_lsa_none, state);
472 static void init_domain_recv_lsa_none(struct composite_context *ctx)
474 struct init_domain_state *state =
475 talloc_get_type(ctx->async.private_data,
476 struct init_domain_state);
478 state->ctx->status = wb_init_lsa_recv(ctx, state, &state->lsa_pipe,
480 if (!comp_is_ok(state->ctx)) return;
482 init_domain_check_lsa(state);
485 static void init_domain_check_lsa(struct init_domain_state *state)
487 struct rpc_request *req;
489 if (state->auth2_pipe == NULL) {
490 /* Give the tree to the LSA pipe, otherwise it has been given
491 * to the auth2 pipe already */
492 talloc_unlink(state, state->conn.out.tree);
493 state->conn.out.tree = NULL;
496 state->queryinfo.in.handle = state->lsa_policy;
497 state->queryinfo.in.level = LSA_POLICY_INFO_ACCOUNT_DOMAIN;
499 req = dcerpc_lsa_QueryInfoPolicy_send(state->lsa_pipe, state,
501 rpc_cont(state->ctx, req, init_domain_recv_queryinfo, state);
504 static void init_domain_recv_queryinfo(struct rpc_request *req)
506 struct init_domain_state *state =
507 talloc_get_type(req->async.private, struct init_domain_state);
508 struct lsa_DomainInfo *dominfo;
510 state->ctx->status = dcerpc_ndr_request_recv(req);
511 if (!comp_is_ok(state->ctx)) return;
512 state->ctx->status = state->queryinfo.out.result;
513 if (!comp_is_ok(state->ctx)) return;
515 dominfo = &state->queryinfo.out.info->account_domain;
517 if (strcasecmp(state->domain->name, dominfo->name.string) != 0) {
518 DEBUG(2, ("Expected domain name %s, DC %s said %s\n",
520 dcerpc_server_name(state->lsa_pipe),
521 dominfo->name.string));
522 comp_error(state->ctx, NT_STATUS_INVALID_DOMAIN_STATE);
526 if (!dom_sid_equal(state->domain->sid, dominfo->sid)) {
527 DEBUG(2, ("Expected domain sid %s, DC %s said %s\n",
528 dom_sid_string(state, state->domain->sid),
529 dcerpc_server_name(state->lsa_pipe),
530 dom_sid_string(state, dominfo->sid)));
531 comp_error(state->ctx, NT_STATUS_INVALID_DOMAIN_STATE);
535 comp_done(state->ctx);
538 NTSTATUS wb_init_domain_recv(struct composite_context *c)
540 NTSTATUS status = composite_wait(c);
541 if (NT_STATUS_IS_OK(status)) {
542 struct init_domain_state *state =
543 talloc_get_type(c->private_data,
544 struct init_domain_state);
545 struct wbsrv_domain *domain = state->domain;
547 talloc_free(domain->netlogon_auth2_pipe);
548 domain->netlogon_auth2_pipe =
549 talloc_steal(domain, state->auth2_pipe);
551 talloc_free(domain->netlogon_pipe);
552 domain->netlogon_pipe =
553 talloc_steal(domain, state->netlogon_pipe);
555 talloc_free(domain->lsa_pipe);
557 talloc_steal(domain, state->lsa_pipe);
559 talloc_free(domain->lsa_policy);
561 talloc_steal(domain, state->lsa_policy);
563 domain->initialized = True;
569 NTSTATUS wb_init_domain(struct wbsrv_domain *domain,
570 struct event_context *event_ctx,
571 struct messaging_context *messaging_ctx)
573 struct composite_context *c =
574 wb_init_domain_send(domain, event_ctx, messaging_ctx);
575 return wb_init_domain_recv(c);