#include "auth/credentials/credentials.h"
#include "param/param.h"
#include "libcli/resolve/resolve.h"
+#include "lib/http/http.h"
+#include "lib/util/util_net.h"
struct dcerpc_pipe_connect {
struct dcecli_connection *conn;
} smb;
};
+struct dcerpc_binding_smbXcli_pointers_ref;
+
+struct dcerpc_binding_smbXcli_pointers {
+ struct smbXcli_conn *conn;
+ struct dcerpc_binding_smbXcli_pointers_ref *conn_ref;
+ struct smbXcli_session *sess;
+ struct dcerpc_binding_smbXcli_pointers_ref *sess_ref;
+ struct smbXcli_tcon *tcon;
+ struct dcerpc_binding_smbXcli_pointers_ref *tcon_ref;
+};
+
+struct dcerpc_binding_smbXcli_pointers_ref {
+ struct dcerpc_binding_smbXcli_pointers *p;
+};
+
+static int dcerpc_binding_smbXcli_pointers_ref_destructor(
+ struct dcerpc_binding_smbXcli_pointers_ref *ref)
+{
+ struct dcerpc_binding_smbXcli_pointers *p;
+
+ if (ref->p == NULL) {
+ return 0;
+ }
+
+ p = ref->p;
+
+ p->conn_ref->p = NULL;
+ p->sess_ref->p = NULL;
+ p->tcon_ref->p = NULL;
+
+ if (p->conn_ref != ref) {
+ talloc_free(p->conn_ref);
+ }
+
+ if (p->sess_ref != ref) {
+ talloc_free(p->sess_ref);
+ }
+
+ if (p->tcon_ref != ref) {
+ talloc_free(p->tcon_ref);
+ }
+
+ ZERO_STRUCTP(p);
+ return 0;
+}
+
+static int dcerpc_binding_smbXcli_pointers_destructor(
+ struct dcerpc_binding_smbXcli_pointers *p)
+{
+ if (p->conn_ref != NULL) {
+ p->conn_ref->p = NULL;
+ }
+
+ if (p->sess_ref != NULL) {
+ p->sess_ref->p = NULL;
+ }
+
+ if (p->tcon_ref != NULL) {
+ p->tcon_ref->p = NULL;
+ }
+
+ talloc_free(p->conn_ref);
+ talloc_free(p->sess_ref);
+ talloc_free(p->tcon_ref);
+
+ ZERO_STRUCTP(p);
+ return 0;
+}
+
+NTSTATUS dcerpc_binding_set_smbXcli_pointers(struct dcerpc_binding *b,
+ struct smbXcli_conn *conn,
+ struct smbXcli_session *sess,
+ struct smbXcli_tcon *tcon)
+{
+ struct dcerpc_binding_smbXcli_pointers *p;
+ struct dcerpc_binding_smbXcli_pointers_ref *r;
+ NTSTATUS status;
+
+ if (!smbXcli_conn_is_connected(conn)) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ if (sess == NULL) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ if (tcon == NULL) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ p = talloc_zero(b, struct dcerpc_binding_smbXcli_pointers);
+ if (p == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_set_destructor(p, dcerpc_binding_smbXcli_pointers_destructor);
+
+ r = talloc_zero(conn, struct dcerpc_binding_smbXcli_pointers_ref);
+ if (r == NULL) {
+ TALLOC_FREE(p);
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_set_destructor(r, dcerpc_binding_smbXcli_pointers_ref_destructor);
+ r->p = p;
+ p->conn_ref = r;
+
+ r = talloc_zero(sess, struct dcerpc_binding_smbXcli_pointers_ref);
+ if (r == NULL) {
+ TALLOC_FREE(p);
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_set_destructor(r, dcerpc_binding_smbXcli_pointers_ref_destructor);
+ r->p = p;
+ p->sess_ref = r;
+
+ r = talloc_zero(tcon, struct dcerpc_binding_smbXcli_pointers_ref);
+ if (r == NULL) {
+ TALLOC_FREE(p);
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_set_destructor(r, dcerpc_binding_smbXcli_pointers_ref_destructor);
+ r->p = p;
+ p->tcon_ref = r;
+
+ p->conn = conn;
+ p->sess = sess;
+ p->tcon = tcon;
+
+ status = dcerpc_binding_set_pointer_option(b, "connection",
+ struct dcerpc_binding_smbXcli_pointers, p);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(p);
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS dcerpc_binding_get_smbXcli_pointers(const struct dcerpc_binding *b,
+ struct smbXcli_conn **conn,
+ struct smbXcli_session **sess,
+ struct smbXcli_tcon **tcon)
+{
+ struct dcerpc_binding_smbXcli_pointers *p;
+
+ p = dcerpc_binding_get_pointer_option(b, "connection",
+ struct dcerpc_binding_smbXcli_pointers);
+ if (p == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ if (!smbXcli_conn_is_connected(p->conn)) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ if (p->sess == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ if (p->tcon == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ *conn = p->conn;
+ *sess = p->sess;
+ *tcon = p->tcon;
+
+ return NT_STATUS_OK;
+}
+
struct pipe_np_smb_state {
struct smb_composite_connect conn;
struct dcerpc_pipe_connect io;
}
static void continue_smb_open(struct composite_context *c);
+static void continue_smb2_connect(struct tevent_req *subreq);
+static void continue_smbXcli_connect(struct tevent_req *subreq);
/*
Stage 2 of ncacn_np_smb: Open a named pipe after successful smb connection
s->io.smb.pipe_name = dcerpc_binding_get_string_option(s->io.binding,
"endpoint");
+ c->status = dcerpc_binding_set_smbXcli_pointers(s->io.binding,
+ s->io.smb.conn,
+ s->io.smb.session,
+ s->io.smb.tcon);
+ if (!composite_is_ok(c)) return;
+
continue_smb_open(c);
}
{
struct composite_context *c;
struct pipe_np_smb_state *s;
- struct composite_context *conn_req;
+ struct tevent_req *subreq = NULL;
struct smb_composite_connect *conn;
uint32_t flags;
+ const char *target_hostname = NULL;
+ const char *dest_address = NULL;
+ const char *calling_name = NULL;
/* composite context allocation and setup */
c = composite_create(mem_ctx, io->conn->event_ctx);
return c;
}
+ if (s->io.creds == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
/* prepare smb connection parameters: we're connecting to IPC$ share on
remote rpc server */
+ target_hostname = dcerpc_binding_get_string_option(s->io.binding, "target_hostname");
conn->in.dest_host = dcerpc_binding_get_string_option(s->io.binding, "host");
conn->in.dest_ports = lpcfg_smb_ports(lp_ctx);
- conn->in.called_name =
- dcerpc_binding_get_string_option(s->io.binding, "target_hostname");
+ conn->in.called_name = target_hostname;
if (conn->in.called_name == NULL) {
conn->in.called_name = "*SMBSERVER";
}
conn->in.fallback_to_anonymous = false;
}
- /* send smb connect request */
- conn_req = smb_composite_connect_send(conn, s->io.conn,
- s->io.resolve_ctx,
- c->event_ctx);
- if (composite_nomem(conn_req, c)) return c;
+ conn->in.options.min_protocol = lpcfg_client_ipc_min_protocol(lp_ctx);
+ conn->in.options.max_protocol = lpcfg_client_ipc_max_protocol(lp_ctx);
+ if ((flags & DCERPC_SMB1) && (flags & DCERPC_SMB2)) {
+ /* auto */
+ } else if (flags & DCERPC_SMB2) {
+ if (conn->in.options.min_protocol < PROTOCOL_SMB2_02) {
+ conn->in.options.min_protocol = PROTOCOL_SMB2_02;
+ }
+ if (conn->in.options.max_protocol < PROTOCOL_SMB2_02) {
+ conn->in.options.max_protocol = PROTOCOL_LATEST;
+ }
+ } else if (flags & DCERPC_SMB1) {
+ conn->in.options.min_protocol = PROTOCOL_NT1;
+ conn->in.options.max_protocol = PROTOCOL_NT1;
+ } else {
+ /* auto */
+ }
+
+ conn->in.options.signing = lpcfg_client_ipc_signing(lp_ctx);
+
+ if (s->conn.in.credentials != NULL) {
+ calling_name = cli_credentials_get_workstation(s->conn.in.credentials);
+ }
+ if (calling_name == NULL) {
+ calling_name = "SMBCLIENT";
+ }
+
+ if (target_hostname == NULL) {
+ target_hostname = conn->in.dest_host;
+ }
+
+ if (is_ipaddress(conn->in.dest_host)) {
+ dest_address = conn->in.dest_host;
+ }
+
+ subreq = smb_connect_nego_send(s,
+ c->event_ctx,
+ s->io.resolve_ctx,
+ &conn->in.options,
+ conn->in.socket_options,
+ conn->in.dest_host,
+ dest_address,
+ conn->in.dest_ports,
+ target_hostname,
+ conn->in.called_name,
+ calling_name);
+ if (composite_nomem(subreq, c)) return c;
+ tevent_req_set_callback(subreq,
+ continue_smbXcli_connect,
+ c);
- composite_continue(c, conn_req, continue_smb_connect, c);
return c;
}
+static void continue_smbXcli_connect(struct tevent_req *subreq)
+{
+ struct composite_context *c =
+ tevent_req_callback_data(subreq,
+ struct composite_context);
+ struct pipe_np_smb_state *s =
+ talloc_get_type_abort(c->private_data,
+ struct pipe_np_smb_state);
+ struct smb_composite_connect *conn = &s->conn;
+ struct composite_context *creq = NULL;
+ enum protocol_types protocol;
+
+ c->status = smb_connect_nego_recv(subreq, s,
+ &conn->in.existing_conn);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ protocol = smbXcli_conn_protocol(conn->in.existing_conn);
+ if (protocol >= PROTOCOL_SMB2_02) {
+ /*
+ * continue with smb2 session setup/tree connect
+ * on the established connection.
+ */
+ subreq = smb2_connect_send(s, c->event_ctx,
+ conn->in.dest_host,
+ conn->in.dest_ports,
+ conn->in.service,
+ s->io.resolve_ctx,
+ conn->in.credentials,
+ conn->in.fallback_to_anonymous,
+ &conn->in.existing_conn,
+ 0, /* previous_session_id */
+ &conn->in.options,
+ conn->in.socket_options,
+ conn->in.gensec_settings);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, continue_smb2_connect, c);
+ return;
+ }
+
+ /*
+ * continue with smb1 session setup/tree connect
+ * on the established connection.
+ */
+ creq = smb_composite_connect_send(conn, s->io.conn,
+ s->io.resolve_ctx,
+ c->event_ctx);
+ if (composite_nomem(creq, c)) return;
+
+ composite_continue(c, creq, continue_smb_connect, c);
+ return;
+}
+
/*
Receive result of a rpc connection to a rpc pipe on SMB
s->io.smb.pipe_name = dcerpc_binding_get_string_option(s->io.binding,
"endpoint");
- continue_smb_open(c);
-}
-
-
-/*
- Initiate async open of a rpc connection request on SMB2 using
- the binding structure to determine the endpoint and options
-*/
-static struct composite_context *dcerpc_pipe_connect_ncacn_np_smb2_send(
- TALLOC_CTX *mem_ctx,
- struct dcerpc_pipe_connect *io,
- struct loadparm_context *lp_ctx)
-{
- struct composite_context *c;
- struct pipe_np_smb_state *s;
- struct tevent_req *subreq;
- struct smbcli_options options;
- const char *host;
- uint32_t flags;
-
- /* composite context allocation and setup */
- c = composite_create(mem_ctx, io->conn->event_ctx);
- if (c == NULL) return NULL;
-
- s = talloc_zero(c, struct pipe_np_smb_state);
- if (composite_nomem(s, c)) return c;
- c->private_data = s;
-
- s->io = *io;
-
- if (smbXcli_conn_is_connected(s->io.smb.conn)) {
- continue_smb_open(c);
- return c;
- }
-
- host = dcerpc_binding_get_string_option(s->io.binding, "host");
- flags = dcerpc_binding_get_flags(s->io.binding);
-
- /*
- * provide proper credentials - user supplied or anonymous in case this is
- * schannel connection
- */
- if (flags & DCERPC_SCHANNEL) {
- s->io.creds = cli_credentials_init_anon(mem_ctx);
- if (composite_nomem(s->io.creds, c)) return c;
- }
+ c->status = dcerpc_binding_set_pointer_option(s->io.binding,
+ "connection",
+ struct smbXcli_conn,
+ s->io.smb.conn);
+ if (!composite_is_ok(c)) return;
- lpcfg_smbcli_options(lp_ctx, &options);
-
- /* send smb2 connect request */
- subreq = smb2_connect_send(s, c->event_ctx,
- host,
- lpcfg_parm_string_list(mem_ctx, lp_ctx, NULL, "smb2", "ports", NULL),
- "IPC$",
- s->io.resolve_ctx,
- s->io.creds,
- 0, /* previous_session_id */
- &options,
- lpcfg_socket_options(lp_ctx),
- lpcfg_gensec_settings(mem_ctx, lp_ctx));
- if (composite_nomem(subreq, c)) return c;
- tevent_req_set_callback(subreq, continue_smb2_connect, c);
- return c;
-}
+ c->status = dcerpc_binding_set_pointer_option(s->io.binding,
+ "connection",
+ struct smbXcli_session,
+ s->io.smb.session);
+ if (!composite_is_ok(c)) return;
+ c->status = dcerpc_binding_set_pointer_option(s->io.binding,
+ "connection",
+ struct smbXcli_tcon,
+ s->io.smb.tcon);
+ if (!composite_is_ok(c)) return;
-/*
- Receive result of a rpc connection to a rpc pipe on SMB2
-*/
-static NTSTATUS dcerpc_pipe_connect_ncacn_np_smb2_recv(struct composite_context *c)
-{
- NTSTATUS status = composite_wait(c);
-
- talloc_free(c);
- return status;
+ continue_smb_open(c);
}
struct pipe_http_state {
struct dcerpc_pipe_connect io;
const char *localaddr;
+ const char *target_hostname;
const char *rpc_server;
uint32_t rpc_server_port;
char *rpc_proxy;
uint32_t http_proxy_port;
bool use_tls;
bool use_proxy;
- bool use_ntlm;
+ enum http_auth_method http_auth;
struct loadparm_context *lp_ctx;
};
s->io.conn->transport.stream = stream;
s->io.conn->transport.write_queue = queue;
s->io.conn->transport.pending_reads = 0;
+ s->io.conn->server_name = strupper_talloc(s->io.conn,
+ s->target_hostname);
composite_done(c);
}
"localaddress");
/* RPC server and port (the endpoint) */
s->rpc_server = dcerpc_binding_get_string_option(io->binding, "host");
+ s->target_hostname = dcerpc_binding_get_string_option(io->binding,
+ "target_hostname");
endpoint = dcerpc_binding_get_string_option(io->binding, "endpoint");
if (endpoint == NULL) {
composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
opt = dcerpc_binding_get_string_option(io->binding, "HttpAuthOption");
if (opt) {
if (strcasecmp(opt, "basic") == 0) {
- s->use_ntlm = false;
+ s->http_auth = HTTP_AUTH_BASIC;
} else if (strcasecmp(opt, "ntlm") == 0) {
- s->use_ntlm = true;
+ s->http_auth = HTTP_AUTH_NTLM;
+ } else if (strcasecmp(opt, "negotiate") == 0) {
+ s->http_auth = HTTP_AUTH_NEGOTIATE;
} else {
composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
return c;
}
} else {
- s->use_ntlm = true;
+ s->http_auth = HTTP_AUTH_NTLM;
}
subreq = dcerpc_pipe_open_roh_send(s->io.conn, s->localaddr,
s->http_proxy, s->http_proxy_port,
s->use_tls, s->use_proxy,
s->io.creds, io->resolve_ctx,
- s->lp_ctx, s->use_ntlm);
+ s->lp_ctx, s->http_auth);
if (composite_nomem(subreq, c)) return c;
tevent_req_set_callback(subreq, continue_pipe_open_ncacn_http, c);
static void continue_map_binding(struct composite_context *ctx);
static void continue_connect(struct composite_context *c, struct pipe_connect_state *s);
-static void continue_pipe_connect_ncacn_np_smb2(struct composite_context *ctx);
static void continue_pipe_connect_ncacn_np_smb(struct composite_context *ctx);
static void continue_pipe_connect_ncacn_ip_tcp(struct composite_context *ctx);
static void continue_pipe_connect_ncacn_http(struct composite_context *ctx);
struct dcerpc_pipe_connect pc;
/* potential exits to another stage by sending an async request */
- struct composite_context *ncacn_np_smb2_req;
struct composite_context *ncacn_np_smb_req;
struct composite_context *ncacn_ip_tcp_req;
struct composite_context *ncacn_http_req;
struct composite_context *ncacn_unix_req;
struct composite_context *ncalrpc_req;
enum dcerpc_transport_t transport;
- uint32_t flags;
+ const char *endpoint = NULL;
/* dcerpc pipe connect input parameters */
ZERO_STRUCT(pc);
pc.resolve_ctx = lpcfg_resolve_context(s->lp_ctx);
transport = dcerpc_binding_get_transport(s->binding);
- flags = dcerpc_binding_get_flags(s->binding);
/* connect dcerpc pipe depending on required transport */
switch (transport) {
case NCACN_NP:
- if (flags & DCERPC_SMB2) {
- /* new varient of SMB a.k.a. SMB2 */
- ncacn_np_smb2_req = dcerpc_pipe_connect_ncacn_np_smb2_send(c, &pc, s->lp_ctx);
- composite_continue(c, ncacn_np_smb2_req, continue_pipe_connect_ncacn_np_smb2, c);
- return;
-
- } else {
- /* good old ordinary SMB */
- ncacn_np_smb_req = dcerpc_pipe_connect_ncacn_np_smb_send(c, &pc, s->lp_ctx);
- composite_continue(c, ncacn_np_smb_req, continue_pipe_connect_ncacn_np_smb, c);
- return;
+ /*
+ * SMB1/2/3...
+ */
+
+ endpoint = dcerpc_binding_get_string_option(pc.binding,
+ "endpoint");
+ if (endpoint != NULL) {
+ NTSTATUS status;
+
+ pc.smb.pipe_name = endpoint;
+
+ status = dcerpc_binding_get_smbXcli_pointers(pc.binding,
+ &pc.smb.conn,
+ &pc.smb.session,
+ &pc.smb.tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCT(pc.smb);
+ }
}
- break;
+
+ ncacn_np_smb_req = dcerpc_pipe_connect_ncacn_np_smb_send(c, &pc, s->lp_ctx);
+ composite_continue(c, ncacn_np_smb_req, continue_pipe_connect_ncacn_np_smb, c);
+ return;
case NCACN_IP_TCP:
ncacn_ip_tcp_req = dcerpc_pipe_connect_ncacn_ip_tcp_send(c, &pc);
}
-/*
- Stage 3 of pipe_connect_b: Receive result of pipe connect request on
- named pipe on smb2
-*/
-static void continue_pipe_connect_ncacn_np_smb2(struct composite_context *ctx)
-{
- struct composite_context *c = talloc_get_type(ctx->async.private_data,
- struct composite_context);
- struct pipe_connect_state *s = talloc_get_type(c->private_data,
- struct pipe_connect_state);
-
- c->status = dcerpc_pipe_connect_ncacn_np_smb2_recv(ctx);
- if (!composite_is_ok(c)) return;
-
- continue_pipe_connect(c, s);
-}
-
-
/*
Stage 3 of pipe_connect_b: Receive result of pipe connect request on
named pipe on smb
{
struct composite_context *auth_bind_req;
- s->pipe->binding = dcerpc_binding_dup(s->pipe, s->binding);
- if (composite_nomem(s->pipe->binding, c)) {
- return;
- }
+ s->pipe->binding = talloc_move(s->pipe, &s->binding);
- auth_bind_req = dcerpc_pipe_auth_send(s->pipe, s->binding, s->table,
+ auth_bind_req = dcerpc_pipe_auth_send(s->pipe, s->pipe->binding, s->table,
s->credentials, s->lp_ctx);
composite_continue(c, auth_bind_req, continue_pipe_auth, c);
}