From e5c6a3e36147103e87d1c55173f4b54ba6134904 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 3 Oct 2005 13:46:11 +0000 Subject: r10683: Samba3's wbinfo -t should give the correct answer now. Tridge, if you have time, you might want to look at the segfault I was still seeing. Now I store the handle to the netlogon pipe in the global winbind state and free it on the next entry into check_machacc. The problem seems to be that talloc_free()ing a pipe struct from within a callback function on that pipe is not possible. I think I can live with that, but it has been not really obvious. To reproduce the segfault you might want to look at putting a talloc_free(state->getcreds->out.netlogon) into wbsrv_samba3_check_machacc_receive_creds. This is called from a dcerpc callback function. In particular if the check failed it would be nice if I could delete the pipe directly and not post a different event to some winbind queue. I tried to delete the pipe from a timed event triggered immediately, but this also fails because the inner loop seems to hit the same event again, calling it twice. Volker (This used to be commit 5436d7764812bb632ba865e633005ed07923b57f) --- source4/include/structs.h | 1 + source4/librpc/rpc/dcerpc_smb.c | 2 +- source4/winbind/wb_async_helpers.c | 224 ++++++++++++++++++++++++++++++++++++- source4/winbind/wb_async_helpers.h | 10 ++ source4/winbind/wb_samba3_cmd.c | 177 +++++++++++++++++++++-------- source4/winbind/wb_server.h | 1 + 6 files changed, 365 insertions(+), 50 deletions(-) diff --git a/source4/include/structs.h b/source4/include/structs.h index 8f3f2c2db5..896e69c1a3 100644 --- a/source4/include/structs.h +++ b/source4/include/structs.h @@ -256,6 +256,7 @@ struct nbtd_interface; struct wins_server; struct wb_finddcs; +struct wb_get_schannel_creds; struct cldap_socket; struct cldapd_server; diff --git a/source4/librpc/rpc/dcerpc_smb.c b/source4/librpc/rpc/dcerpc_smb.c index 5fb4f7c950..b63a3080f5 100644 --- a/source4/librpc/rpc/dcerpc_smb.c +++ b/source4/librpc/rpc/dcerpc_smb.c @@ -470,7 +470,7 @@ static void pipe_open_recv(struct smbcli_request *req) smb->fnum = state->open->ntcreatex.out.fnum; smb->tree = talloc_reference(smb, state->tree); smb->server_name= strupper_talloc( - smb, state->tree->session->transport->socket->hostname); + smb, state->tree->session->transport->called.name); if (smb->server_name == NULL) { ctx->status = NT_STATUS_NO_MEMORY; goto done; diff --git a/source4/winbind/wb_async_helpers.c b/source4/winbind/wb_async_helpers.c index a802f0e45e..7d7d2b6929 100644 --- a/source4/winbind/wb_async_helpers.c +++ b/source4/winbind/wb_async_helpers.c @@ -30,6 +30,10 @@ #include "lib/messaging/irpc.h" #include "librpc/gen_ndr/irpc.h" #include "librpc/gen_ndr/ndr_irpc.h" +#include "libcli/raw/libcliraw.h" +#include "librpc/rpc/dcerpc_composite.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "libcli/auth/credentials.h" struct finddcs_state { struct wb_finddcs *io; @@ -164,7 +168,6 @@ struct composite_context *wb_finddcs_send(struct wb_finddcs *io, state = talloc(c, struct finddcs_state); if (state == NULL) goto failed; - state->io = io; make_nbt_name(&name, io->in.domain, 0x1c); @@ -204,3 +207,222 @@ NTSTATUS wb_finddcs(struct wb_finddcs *io, TALLOC_CTX *mem_ctx, struct composite_context *c = wb_finddcs_send(io, ev); return wb_finddcs_recv(c, mem_ctx); } + +struct get_schannel_creds_state { + struct composite_context *ctx; + struct dcerpc_pipe *p; + struct wb_get_schannel_creds *io; + struct netr_ServerReqChallenge *r; + + struct creds_CredentialState creds_state; + struct netr_Credential netr_cred; + uint32_t negotiate_flags; + struct netr_ServerAuthenticate2 *a; +}; + +static void get_schannel_creds_recv_auth(struct rpc_request *req); +static void get_schannel_creds_recv_chal(struct rpc_request *req); +static void get_schannel_creds_recv_pipe(struct composite_context *ctx); + +struct composite_context *wb_get_schannel_creds_send(struct wb_get_schannel_creds *io, + struct event_context *ev) +{ + struct composite_context *result, *ctx; + struct get_schannel_creds_state *state; + + result = talloc_zero(NULL, struct composite_context); + if (result == NULL) goto failed; + result->state = COMPOSITE_STATE_IN_PROGRESS; + result->event_ctx = ev; + + state = talloc(result, struct get_schannel_creds_state); + if (state == NULL) goto failed; + result->private_data = state; + + state->io = io; + + state->p = dcerpc_pipe_init(state, ev); + if (state->p == NULL) goto failed; + + ctx = dcerpc_pipe_open_smb_send(state->p->conn, state->io->in.tree, + "\\netlogon"); + if (ctx == NULL) goto failed; + + ctx->async.fn = get_schannel_creds_recv_pipe; + ctx->async.private_data = state; + state->ctx = result; + return result; + + failed: + talloc_free(result); + return NULL; +} + +static void get_schannel_creds_recv_pipe(struct composite_context *ctx) +{ + struct get_schannel_creds_state *state = + talloc_get_type(ctx->async.private_data, + struct get_schannel_creds_state); + struct rpc_request *req; + + state->ctx->status = dcerpc_pipe_open_smb_recv(ctx); + if (!NT_STATUS_IS_OK(state->ctx->status)) goto done; + + state->ctx->status = dcerpc_bind_auth_none(state->p, + DCERPC_NETLOGON_UUID, + DCERPC_NETLOGON_VERSION); + if (!NT_STATUS_IS_OK(state->ctx->status)) goto done; + + state->r = talloc(state, struct netr_ServerReqChallenge); + if (state->r == NULL) { + state->ctx->status = NT_STATUS_NO_MEMORY; + goto done; + } + + state->r->in.computer_name = + cli_credentials_get_workstation(state->io->in.creds); + state->r->in.server_name = + talloc_asprintf(state->r, "\\\\%s", + dcerpc_server_name(state->p)); + state->r->in.credentials = talloc(state->r, struct netr_Credential); + state->r->out.credentials = talloc(state->r, struct netr_Credential); + + if ((state->r->in.server_name == NULL) || + (state->r->in.credentials == NULL) || + (state->r->out.credentials == NULL)) { + state->ctx->status = NT_STATUS_NO_MEMORY; + goto done; + } + generate_random_buffer(state->r->in.credentials->data, + sizeof(state->r->in.credentials->data)); + + req = dcerpc_netr_ServerReqChallenge_send(state->p, state, state->r); + if (req == NULL) { + state->ctx->status = NT_STATUS_NO_MEMORY; + goto done; + } + + req->async.callback = get_schannel_creds_recv_chal; + req->async.private = state; + return; + + done: + if (!NT_STATUS_IS_OK(state->ctx->status)) { + state->ctx->state = COMPOSITE_STATE_ERROR; + } + if ((state->ctx->state >= COMPOSITE_STATE_DONE) && + (state->ctx->async.fn != NULL)) { + state->ctx->async.fn(state->ctx); + } +} + +static void get_schannel_creds_recv_chal(struct rpc_request *req) +{ + struct get_schannel_creds_state *state = + talloc_get_type(req->async.private, + struct get_schannel_creds_state); + const struct samr_Password *mach_pwd; + + state->ctx->status = dcerpc_ndr_request_recv(req); + if (!NT_STATUS_IS_OK(state->ctx->status)) goto done; + state->ctx->status = state->r->out.result; + if (!NT_STATUS_IS_OK(state->ctx->status)) goto done; + + mach_pwd = cli_credentials_get_nt_hash(state->io->in.creds, state); + if (mach_pwd == NULL) { + state->ctx->status = NT_STATUS_NO_MEMORY; + goto done; + } + + state->negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS; + + creds_client_init(&state->creds_state, state->r->in.credentials, + state->r->out.credentials, mach_pwd, + &state->netr_cred, state->negotiate_flags); + + state->a = talloc(state, struct netr_ServerAuthenticate2); + if (state->a == NULL) { + state->ctx->status = NT_STATUS_NO_MEMORY; + goto done; + } + + state->a->in.server_name = + talloc_reference(state->a, state->r->in.server_name); + state->a->in.account_name = + cli_credentials_get_username(state->io->in.creds); + state->a->in.secure_channel_type = + cli_credentials_get_secure_channel_type(state->io->in.creds); + state->a->in.computer_name = + cli_credentials_get_workstation(state->io->in.creds); + state->a->in.negotiate_flags = &state->negotiate_flags; + state->a->out.negotiate_flags = &state->negotiate_flags; + state->a->in.credentials = &state->netr_cred; + state->a->out.credentials = &state->netr_cred; + + req = dcerpc_netr_ServerAuthenticate2_send(state->p, state, state->a); + if (req == NULL) { + state->ctx->status = NT_STATUS_NO_MEMORY; + goto done; + } + + req->async.callback = get_schannel_creds_recv_auth; + req->async.private = state; + return; + + state->io->out.netlogon = state->p; + state->ctx->state = COMPOSITE_STATE_DONE; + + done: + if (!NT_STATUS_IS_OK(state->ctx->status)) { + state->ctx->state = COMPOSITE_STATE_ERROR; + } + if ((state->ctx->state >= COMPOSITE_STATE_DONE) && + (state->ctx->async.fn != NULL)) { + state->ctx->async.fn(state->ctx); + } +} + +static void get_schannel_creds_recv_auth(struct rpc_request *req) +{ + struct get_schannel_creds_state *state = + talloc_get_type(req->async.private, + struct get_schannel_creds_state); + + state->ctx->status = dcerpc_ndr_request_recv(req); + DEBUG(5, ("result: %s\n", nt_errstr(state->ctx->status))); + if (!NT_STATUS_IS_OK(state->ctx->status)) goto done; + state->ctx->status = state->a->out.result; + DEBUG(5, ("result: %s\n", nt_errstr(state->ctx->status))); + if (!NT_STATUS_IS_OK(state->ctx->status)) goto done; + + state->ctx->state = COMPOSITE_STATE_DONE; + + done: + if (!NT_STATUS_IS_OK(state->ctx->status)) { + state->ctx->state = COMPOSITE_STATE_ERROR; + } + if ((state->ctx->state >= COMPOSITE_STATE_DONE) && + (state->ctx->async.fn != NULL)) { + state->ctx->async.fn(state->ctx); + } +} + +NTSTATUS wb_get_schannel_creds_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx) +{ + NTSTATUS status = composite_wait(c); + struct get_schannel_creds_state *state = + talloc_get_type(c->private_data, + struct get_schannel_creds_state); + state->io->out.netlogon = talloc_steal(mem_ctx, state->p); + talloc_free(c); + return status; +} + +NTSTATUS wb_get_schannel_creds(struct wb_get_schannel_creds *io, + TALLOC_CTX *mem_ctx, + struct event_context *ev) +{ + struct composite_context *c = wb_get_schannel_creds_send(io, ev); + return wb_get_schannel_creds_recv(c, mem_ctx); +} diff --git a/source4/winbind/wb_async_helpers.h b/source4/winbind/wb_async_helpers.h index 6c4f1d84a8..b37a66e972 100644 --- a/source4/winbind/wb_async_helpers.h +++ b/source4/winbind/wb_async_helpers.h @@ -34,3 +34,13 @@ struct wb_finddcs { } *dcs; } out; }; + +struct wb_get_schannel_creds { + struct { + struct cli_credentials *creds; + struct smbcli_tree *tree; + } in; + struct { + struct dcerpc_pipe *netlogon; + } out; +}; diff --git a/source4/winbind/wb_samba3_cmd.c b/source4/winbind/wb_samba3_cmd.c index 7b3cd79cf4..570a166532 100644 --- a/source4/winbind/wb_samba3_cmd.c +++ b/source4/winbind/wb_samba3_cmd.c @@ -32,6 +32,8 @@ #include "libcli/composite/composite.h" #include "libcli/smb_composite/smb_composite.h" #include "include/version.h" +#include "librpc/rpc/dcerpc_composite.h" +#include "lib/events/events.h" NTSTATUS wbsrv_samba3_interface_version(struct wbsrv_samba3_call *s3call) { @@ -77,46 +79,53 @@ NTSTATUS wbsrv_samba3_ping(struct wbsrv_samba3_call *s3call) return NT_STATUS_OK; } +#define null_no_memory_done(x) do { \ + if ((x) == NULL) { status = NT_STATUS_NO_MEMORY; goto done; } \ + } while (0) + struct check_machacc_state { struct wb_finddcs *io; struct smb_composite_connect *conn; + struct wb_get_schannel_creds *getcreds; }; -static void wbsrv_samba3_check_machacc_receive_tree(struct composite_context *action) +static void wbsrv_samba3_check_machacc_receive_creds(struct composite_context *action); +static void wbsrv_samba3_check_machacc_receive_tree(struct composite_context *action); +static void wbsrv_samba3_check_machacc_receive_dcs(struct composite_context *action); + +NTSTATUS wbsrv_samba3_check_machacc(struct wbsrv_samba3_call *s3call) { - struct wbsrv_samba3_call *s3call = - talloc_get_type(action->async.private_data, - struct wbsrv_samba3_call); - struct check_machacc_state *state = - talloc_get_type(s3call->private_data, - struct check_machacc_state); - NTSTATUS status; + struct composite_context *resolve_req; + struct check_machacc_state *state; + struct wbsrv_service *service = + s3call->call->wbconn->listen_socket->service; - status = smb_composite_connect_recv(action, state); - WBSRV_SAMBA3_SET_STRING(s3call->response.data.auth.nt_status_string, - nt_errstr(status)); - WBSRV_SAMBA3_SET_STRING(s3call->response.data.auth.error_string, - nt_errstr(status)); - s3call->response.data.auth.pam_error = nt_status_to_pam(status); + DEBUG(5, ("check_machacc called\n")); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(5, ("Connect failed: %s\n", nt_errstr(status))); - goto done; + if (service->netlogon != NULL) { + talloc_free(service->netlogon); } - s3call->response.result = WINBINDD_OK; - - done: - if (!NT_STATUS_IS_OK(status)) { - s3call->response.result = WINBINDD_ERROR; - } + state = talloc(s3call, struct check_machacc_state); + NT_STATUS_HAVE_NO_MEMORY(state); - status = wbsrv_send_reply(s3call->call); - if (!NT_STATUS_IS_OK(status)) { - wbsrv_terminate_connection(s3call->call->wbconn, - "wbsrv_queue_reply() failed"); - return; - } + state->io = talloc(s3call, struct wb_finddcs); + NT_STATUS_HAVE_NO_MEMORY(state->io); + s3call->private_data = state; + + state->io->in.msg_ctx = s3call->call->wbconn->conn->msg_ctx; + state->io->in.domain = lp_workgroup(); + + resolve_req = wb_finddcs_send(state->io, s3call->call->event_ctx); + NT_STATUS_HAVE_NO_MEMORY(resolve_req); + + /* setup the callbacks */ + resolve_req->async.fn = wbsrv_samba3_check_machacc_receive_dcs; + resolve_req->async.private_data = s3call; + + /* tell the caller we reply later */ + s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; + return NT_STATUS_OK; } static void wbsrv_samba3_check_machacc_receive_dcs(struct composite_context *action) @@ -188,31 +197,103 @@ static void wbsrv_samba3_check_machacc_receive_dcs(struct composite_context *act } } -NTSTATUS wbsrv_samba3_check_machacc(struct wbsrv_samba3_call *s3call) +static void wbsrv_samba3_check_machacc_receive_tree(struct composite_context *action) { - struct composite_context *resolve_req; - struct check_machacc_state *state; + struct wbsrv_samba3_call *s3call = + talloc_get_type(action->async.private_data, + struct wbsrv_samba3_call); + struct check_machacc_state *state = + talloc_get_type(s3call->private_data, + struct check_machacc_state); + struct composite_context *ctx; + NTSTATUS status; + struct cli_credentials *creds; - DEBUG(5, ("check_machacc called\n")); + status = smb_composite_connect_recv(action, state); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5, ("Connect failed: %s\n", nt_errstr(status))); + goto done; + } - state = talloc(s3call, struct check_machacc_state); - NT_STATUS_HAVE_NO_MEMORY(state); + state->getcreds = talloc(state, struct wb_get_schannel_creds); + null_no_memory_done(state->getcreds); - state->io = talloc(s3call, struct wb_finddcs); - NT_STATUS_HAVE_NO_MEMORY(state->io); - s3call->private_data = state; + creds = cli_credentials_init(state); + null_no_memory_done(creds); + cli_credentials_set_conf(creds); + status = cli_credentials_set_machine_account(creds); + if (!NT_STATUS_IS_OK(status)) goto done; - state->io->in.msg_ctx = s3call->call->wbconn->conn->msg_ctx; - state->io->in.domain = lp_workgroup(); + state->getcreds->in.tree = state->conn->out.tree; + state->getcreds->in.creds = creds; - resolve_req = wb_finddcs_send(state->io, s3call->call->event_ctx); - NT_STATUS_HAVE_NO_MEMORY(resolve_req); + ctx = wb_get_schannel_creds_send(state->getcreds, + s3call->call->event_ctx); + null_no_memory_done(ctx); - /* setup the callbacks */ - resolve_req->async.fn = wbsrv_samba3_check_machacc_receive_dcs; - resolve_req->async.private_data = s3call; + ctx->async.fn = wbsrv_samba3_check_machacc_receive_creds; + ctx->async.private_data = s3call; - /* tell the caller we reply later */ - s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; - return NT_STATUS_OK; + return; + + done: + s3call->response.result = WINBINDD_OK; + + if (!NT_STATUS_IS_OK(status)) { + s3call->response.result = WINBINDD_ERROR; + WBSRV_SAMBA3_SET_STRING(s3call->response.data.auth.nt_status_string, + nt_errstr(status)); + WBSRV_SAMBA3_SET_STRING(s3call->response.data.auth.error_string, + nt_errstr(status)); + s3call->response.data.auth.pam_error = nt_status_to_pam(status); + + } + + status = wbsrv_send_reply(s3call->call); + if (!NT_STATUS_IS_OK(status)) { + wbsrv_terminate_connection(s3call->call->wbconn, + "wbsrv_queue_reply() failed"); + return; + } +} + +static void wbsrv_samba3_check_machacc_receive_creds(struct composite_context *action) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(action->async.private_data, + struct wbsrv_samba3_call); + struct check_machacc_state *state = + talloc_get_type(s3call->private_data, + struct check_machacc_state); + struct wbsrv_service *service = + s3call->call->wbconn->listen_socket->service; + + NTSTATUS status; + + status = wb_get_schannel_creds_recv(action, service); + service->netlogon = state->getcreds->out.netlogon; + + talloc_unlink(state, state->conn->out.tree); /* The pipe owns it now */ + state->conn->out.tree = NULL; + + if (!NT_STATUS_IS_OK(status)) goto done; + + s3call->response.result = WINBINDD_OK; + done: + if (!NT_STATUS_IS_OK(status)) { + s3call->response.result = WINBINDD_ERROR; + WBSRV_SAMBA3_SET_STRING(s3call->response.data.auth.nt_status_string, + nt_errstr(status)); + WBSRV_SAMBA3_SET_STRING(s3call->response.data.auth.error_string, + nt_errstr(status)); + s3call->response.data.auth.pam_error = nt_status_to_pam(status); + + } + + status = wbsrv_send_reply(s3call->call); + if (!NT_STATUS_IS_OK(status)) { + wbsrv_terminate_connection(s3call->call->wbconn, + "wbsrv_queue_reply() failed"); + return; + } } diff --git a/source4/winbind/wb_server.h b/source4/winbind/wb_server.h index 0c00394f7d..3838354d52 100644 --- a/source4/winbind/wb_server.h +++ b/source4/winbind/wb_server.h @@ -32,6 +32,7 @@ /* this struct stores global data for the winbind task */ struct wbsrv_service { struct task_server *task; + struct dcerpc_pipe *netlogon; }; /* -- cgit