| 1 | /*
|
|---|
| 2 | Unix SMB/CIFS implementation.
|
|---|
| 3 |
|
|---|
| 4 | SMB2 composite connection setup
|
|---|
| 5 |
|
|---|
| 6 | Copyright (C) Andrew Tridgell 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 | #include "includes.h"
|
|---|
| 23 | #include "libcli/raw/libcliraw.h"
|
|---|
| 24 | #include "libcli/raw/raw_proto.h"
|
|---|
| 25 | #include "libcli/smb2/smb2.h"
|
|---|
| 26 | #include "libcli/smb2/smb2_calls.h"
|
|---|
| 27 | #include "libcli/composite/composite.h"
|
|---|
| 28 | #include "libcli/resolve/resolve.h"
|
|---|
| 29 | #include "param/param.h"
|
|---|
| 30 |
|
|---|
| 31 | struct smb2_connect_state {
|
|---|
| 32 | struct cli_credentials *credentials;
|
|---|
| 33 | struct resolve_context *resolve_ctx;
|
|---|
| 34 | const char *host;
|
|---|
| 35 | const char *share;
|
|---|
| 36 | const char **ports;
|
|---|
| 37 | const char *socket_options;
|
|---|
| 38 | struct gensec_settings *gensec_settings;
|
|---|
| 39 | struct smbcli_options options;
|
|---|
| 40 | struct smb2_negprot negprot;
|
|---|
| 41 | struct smb2_tree_connect tcon;
|
|---|
| 42 | struct smb2_session *session;
|
|---|
| 43 | struct smb2_tree *tree;
|
|---|
| 44 | };
|
|---|
| 45 |
|
|---|
| 46 | /*
|
|---|
| 47 | continue after tcon reply
|
|---|
| 48 | */
|
|---|
| 49 | static void continue_tcon(struct smb2_request *req)
|
|---|
| 50 | {
|
|---|
| 51 | struct composite_context *c = talloc_get_type(req->async.private_data,
|
|---|
| 52 | struct composite_context);
|
|---|
| 53 | struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
|---|
| 54 | struct smb2_connect_state);
|
|---|
| 55 |
|
|---|
| 56 | c->status = smb2_tree_connect_recv(req, &state->tcon);
|
|---|
| 57 | if (!composite_is_ok(c)) return;
|
|---|
| 58 |
|
|---|
| 59 | state->tree->tid = state->tcon.out.tid;
|
|---|
| 60 |
|
|---|
| 61 | composite_done(c);
|
|---|
| 62 | }
|
|---|
| 63 |
|
|---|
| 64 | /*
|
|---|
| 65 | continue after a session setup
|
|---|
| 66 | */
|
|---|
| 67 | static void continue_session(struct composite_context *creq)
|
|---|
| 68 | {
|
|---|
| 69 | struct composite_context *c = talloc_get_type(creq->async.private_data,
|
|---|
| 70 | struct composite_context);
|
|---|
| 71 | struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
|---|
| 72 | struct smb2_connect_state);
|
|---|
| 73 | struct smb2_request *req;
|
|---|
| 74 |
|
|---|
| 75 | c->status = smb2_session_setup_spnego_recv(creq);
|
|---|
| 76 | if (!composite_is_ok(c)) return;
|
|---|
| 77 |
|
|---|
| 78 | state->tree = smb2_tree_init(state->session, state, true);
|
|---|
| 79 | if (composite_nomem(state->tree, c)) return;
|
|---|
| 80 |
|
|---|
| 81 | state->tcon.in.reserved = 0;
|
|---|
| 82 | state->tcon.in.path = talloc_asprintf(state, "\\\\%s\\%s",
|
|---|
| 83 | state->host, state->share);
|
|---|
| 84 | if (composite_nomem(state->tcon.in.path, c)) return;
|
|---|
| 85 |
|
|---|
| 86 | req = smb2_tree_connect_send(state->tree, &state->tcon);
|
|---|
| 87 | if (composite_nomem(req, c)) return;
|
|---|
| 88 |
|
|---|
| 89 | req->async.fn = continue_tcon;
|
|---|
| 90 | req->async.private_data = c;
|
|---|
| 91 | }
|
|---|
| 92 |
|
|---|
| 93 | /*
|
|---|
| 94 | continue after negprot reply
|
|---|
| 95 | */
|
|---|
| 96 | static void continue_negprot(struct smb2_request *req)
|
|---|
| 97 | {
|
|---|
| 98 | struct composite_context *c = talloc_get_type(req->async.private_data,
|
|---|
| 99 | struct composite_context);
|
|---|
| 100 | struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
|---|
| 101 | struct smb2_connect_state);
|
|---|
| 102 | struct smb2_transport *transport = req->transport;
|
|---|
| 103 | struct composite_context *creq;
|
|---|
| 104 |
|
|---|
| 105 | c->status = smb2_negprot_recv(req, c, &state->negprot);
|
|---|
| 106 | if (!composite_is_ok(c)) return;
|
|---|
| 107 |
|
|---|
| 108 | transport->negotiate.secblob = state->negprot.out.secblob;
|
|---|
| 109 | talloc_steal(transport, transport->negotiate.secblob.data);
|
|---|
| 110 | transport->negotiate.system_time = state->negprot.out.system_time;
|
|---|
| 111 | transport->negotiate.server_start_time = state->negprot.out.server_start_time;
|
|---|
| 112 | transport->negotiate.security_mode = state->negprot.out.security_mode;
|
|---|
| 113 | transport->negotiate.dialect_revision = state->negprot.out.dialect_revision;
|
|---|
| 114 |
|
|---|
| 115 | switch (transport->options.signing) {
|
|---|
| 116 | case SMB_SIGNING_OFF:
|
|---|
| 117 | if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
|
|---|
| 118 | composite_error(c, NT_STATUS_ACCESS_DENIED);
|
|---|
| 119 | return;
|
|---|
| 120 | }
|
|---|
| 121 | transport->signing_required = false;
|
|---|
| 122 | break;
|
|---|
| 123 | case SMB_SIGNING_SUPPORTED:
|
|---|
| 124 | if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
|
|---|
| 125 | transport->signing_required = true;
|
|---|
| 126 | } else {
|
|---|
| 127 | transport->signing_required = false;
|
|---|
| 128 | }
|
|---|
| 129 | break;
|
|---|
| 130 | case SMB_SIGNING_AUTO:
|
|---|
| 131 | if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) {
|
|---|
| 132 | transport->signing_required = true;
|
|---|
| 133 | } else {
|
|---|
| 134 | transport->signing_required = false;
|
|---|
| 135 | }
|
|---|
| 136 | break;
|
|---|
| 137 | case SMB_SIGNING_REQUIRED:
|
|---|
| 138 | if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) {
|
|---|
| 139 | transport->signing_required = true;
|
|---|
| 140 | } else {
|
|---|
| 141 | composite_error(c, NT_STATUS_ACCESS_DENIED);
|
|---|
| 142 | return;
|
|---|
| 143 | }
|
|---|
| 144 | break;
|
|---|
| 145 | }
|
|---|
| 146 |
|
|---|
| 147 | state->session = smb2_session_init(transport, state->gensec_settings, state, true);
|
|---|
| 148 | if (composite_nomem(state->session, c)) return;
|
|---|
| 149 |
|
|---|
| 150 | creq = smb2_session_setup_spnego_send(state->session, state->credentials);
|
|---|
| 151 |
|
|---|
| 152 | composite_continue(c, creq, continue_session, c);
|
|---|
| 153 | }
|
|---|
| 154 |
|
|---|
| 155 | /*
|
|---|
| 156 | continue after a socket connect completes
|
|---|
| 157 | */
|
|---|
| 158 | static void continue_socket(struct composite_context *creq)
|
|---|
| 159 | {
|
|---|
| 160 | struct composite_context *c = talloc_get_type(creq->async.private_data,
|
|---|
| 161 | struct composite_context);
|
|---|
| 162 | struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
|---|
| 163 | struct smb2_connect_state);
|
|---|
| 164 | struct smbcli_socket *sock;
|
|---|
| 165 | struct smb2_transport *transport;
|
|---|
| 166 | struct smb2_request *req;
|
|---|
| 167 | uint16_t dialects[3] = {
|
|---|
| 168 | SMB2_DIALECT_REVISION_000,
|
|---|
| 169 | SMB2_DIALECT_REVISION_202,
|
|---|
| 170 | SMB2_DIALECT_REVISION_210
|
|---|
| 171 | };
|
|---|
| 172 |
|
|---|
| 173 | c->status = smbcli_sock_connect_recv(creq, state, &sock);
|
|---|
| 174 | if (!composite_is_ok(c)) return;
|
|---|
| 175 |
|
|---|
| 176 | transport = smb2_transport_init(sock, state, &state->options);
|
|---|
| 177 | if (composite_nomem(transport, c)) return;
|
|---|
| 178 |
|
|---|
| 179 | ZERO_STRUCT(state->negprot);
|
|---|
| 180 | state->negprot.in.dialect_count = sizeof(dialects) / sizeof(dialects[0]);
|
|---|
| 181 | switch (transport->options.signing) {
|
|---|
| 182 | case SMB_SIGNING_OFF:
|
|---|
| 183 | state->negprot.in.security_mode = 0;
|
|---|
| 184 | break;
|
|---|
| 185 | case SMB_SIGNING_SUPPORTED:
|
|---|
| 186 | case SMB_SIGNING_AUTO:
|
|---|
| 187 | state->negprot.in.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
|
|---|
| 188 | break;
|
|---|
| 189 | case SMB_SIGNING_REQUIRED:
|
|---|
| 190 | state->negprot.in.security_mode =
|
|---|
| 191 | SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
|
|---|
| 192 | break;
|
|---|
| 193 | }
|
|---|
| 194 | state->negprot.in.capabilities = 0;
|
|---|
| 195 | unix_to_nt_time(&state->negprot.in.start_time, time(NULL));
|
|---|
| 196 | state->negprot.in.dialects = dialects;
|
|---|
| 197 |
|
|---|
| 198 | req = smb2_negprot_send(transport, &state->negprot);
|
|---|
| 199 | if (composite_nomem(req, c)) return;
|
|---|
| 200 |
|
|---|
| 201 | req->async.fn = continue_negprot;
|
|---|
| 202 | req->async.private_data = c;
|
|---|
| 203 | }
|
|---|
| 204 |
|
|---|
| 205 |
|
|---|
| 206 | /*
|
|---|
| 207 | continue after a resolve finishes
|
|---|
| 208 | */
|
|---|
| 209 | static void continue_resolve(struct composite_context *creq)
|
|---|
| 210 | {
|
|---|
| 211 | struct composite_context *c = talloc_get_type(creq->async.private_data,
|
|---|
| 212 | struct composite_context);
|
|---|
| 213 | struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
|---|
| 214 | struct smb2_connect_state);
|
|---|
| 215 | const char *addr;
|
|---|
| 216 | const char **ports;
|
|---|
| 217 | const char *default_ports[] = { "445", NULL };
|
|---|
| 218 |
|
|---|
| 219 | c->status = resolve_name_recv(creq, state, &addr);
|
|---|
| 220 | if (!composite_is_ok(c)) return;
|
|---|
| 221 |
|
|---|
| 222 | if (state->ports == NULL) {
|
|---|
| 223 | ports = default_ports;
|
|---|
| 224 | } else {
|
|---|
| 225 | ports = state->ports;
|
|---|
| 226 | }
|
|---|
| 227 |
|
|---|
| 228 | creq = smbcli_sock_connect_send(state, addr, ports, state->host, state->resolve_ctx, c->event_ctx, state->socket_options);
|
|---|
| 229 |
|
|---|
| 230 | composite_continue(c, creq, continue_socket, c);
|
|---|
| 231 | }
|
|---|
| 232 |
|
|---|
| 233 | /*
|
|---|
| 234 | a composite function that does a full negprot/sesssetup/tcon, returning
|
|---|
| 235 | a connected smb2_tree
|
|---|
| 236 | */
|
|---|
| 237 | struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx,
|
|---|
| 238 | const char *host,
|
|---|
| 239 | const char **ports,
|
|---|
| 240 | const char *share,
|
|---|
| 241 | struct resolve_context *resolve_ctx,
|
|---|
| 242 | struct cli_credentials *credentials,
|
|---|
| 243 | struct tevent_context *ev,
|
|---|
| 244 | struct smbcli_options *options,
|
|---|
| 245 | const char *socket_options,
|
|---|
| 246 | struct gensec_settings *gensec_settings)
|
|---|
| 247 | {
|
|---|
| 248 | struct composite_context *c;
|
|---|
| 249 | struct smb2_connect_state *state;
|
|---|
| 250 | struct nbt_name name;
|
|---|
| 251 | struct composite_context *creq;
|
|---|
| 252 |
|
|---|
| 253 | c = composite_create(mem_ctx, ev);
|
|---|
| 254 | if (c == NULL) return NULL;
|
|---|
| 255 |
|
|---|
| 256 | state = talloc(c, struct smb2_connect_state);
|
|---|
| 257 | if (composite_nomem(state, c)) return c;
|
|---|
| 258 | c->private_data = state;
|
|---|
| 259 |
|
|---|
| 260 | state->credentials = credentials;
|
|---|
| 261 | state->options = *options;
|
|---|
| 262 | state->host = talloc_strdup(c, host);
|
|---|
| 263 | if (composite_nomem(state->host, c)) return c;
|
|---|
| 264 | state->ports = talloc_reference(state, ports);
|
|---|
| 265 | state->share = talloc_strdup(c, share);
|
|---|
| 266 | if (composite_nomem(state->share, c)) return c;
|
|---|
| 267 | state->resolve_ctx = talloc_reference(state, resolve_ctx);
|
|---|
| 268 | state->socket_options = talloc_reference(state, socket_options);
|
|---|
| 269 | state->gensec_settings = talloc_reference(state, gensec_settings);
|
|---|
| 270 |
|
|---|
| 271 | ZERO_STRUCT(name);
|
|---|
| 272 | name.name = host;
|
|---|
| 273 |
|
|---|
| 274 | creq = resolve_name_send(resolve_ctx, state, &name, c->event_ctx);
|
|---|
| 275 | composite_continue(c, creq, continue_resolve, c);
|
|---|
| 276 | return c;
|
|---|
| 277 | }
|
|---|
| 278 |
|
|---|
| 279 | /*
|
|---|
| 280 | receive a connect reply
|
|---|
| 281 | */
|
|---|
| 282 | NTSTATUS smb2_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
|---|
| 283 | struct smb2_tree **tree)
|
|---|
| 284 | {
|
|---|
| 285 | NTSTATUS status;
|
|---|
| 286 | struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
|---|
| 287 | struct smb2_connect_state);
|
|---|
| 288 | status = composite_wait(c);
|
|---|
| 289 | if (NT_STATUS_IS_OK(status)) {
|
|---|
| 290 | *tree = talloc_steal(mem_ctx, state->tree);
|
|---|
| 291 | }
|
|---|
| 292 | talloc_free(c);
|
|---|
| 293 | return status;
|
|---|
| 294 | }
|
|---|
| 295 |
|
|---|
| 296 | /*
|
|---|
| 297 | sync version of smb2_connect
|
|---|
| 298 | */
|
|---|
| 299 | NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx,
|
|---|
| 300 | const char *host, const char **ports,
|
|---|
| 301 | const char *share,
|
|---|
| 302 | struct resolve_context *resolve_ctx,
|
|---|
| 303 | struct cli_credentials *credentials,
|
|---|
| 304 | struct smb2_tree **tree,
|
|---|
| 305 | struct tevent_context *ev,
|
|---|
| 306 | struct smbcli_options *options,
|
|---|
| 307 | const char *socket_options,
|
|---|
| 308 | struct gensec_settings *gensec_settings)
|
|---|
| 309 | {
|
|---|
| 310 | struct composite_context *c = smb2_connect_send(mem_ctx, host, ports,
|
|---|
| 311 | share, resolve_ctx,
|
|---|
| 312 | credentials, ev, options,
|
|---|
| 313 | socket_options,
|
|---|
| 314 | gensec_settings);
|
|---|
| 315 | return smb2_connect_recv(c, mem_ctx, tree);
|
|---|
| 316 | }
|
|---|