root/source4/librpc/rpc/dcerpc_smb2.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pipe_dead
  2. smb2_read_callback
  3. send_read_request_continue
  4. send_read_request
  5. smb2_trans_callback
  6. smb2_send_trans_request
  7. smb2_write_callback
  8. smb2_send_request
  9. smb2_shutdown_pipe
  10. smb2_peer_name
  11. smb2_target_hostname
  12. smb2_session_key
  13. dcerpc_pipe_open_smb2_send
  14. pipe_open_recv
  15. dcerpc_pipe_open_smb2_recv
  16. dcerpc_pipe_open_smb2
  17. dcerpc_smb2_tree

   1 /* 
   2    Unix SMB/CIFS implementation.
   3 
   4    dcerpc over SMB2 transport
   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/composite/composite.h"
  25 #include "libcli/smb2/smb2.h"
  26 #include "libcli/smb2/smb2_calls.h"
  27 #include "libcli/raw/ioctl.h"
  28 #include "librpc/rpc/dcerpc.h"
  29 #include "librpc/rpc/dcerpc_proto.h"
  30 
  31 /* transport private information used by SMB2 pipe transport */
  32 struct smb2_private {
  33         struct smb2_handle handle;
  34         struct smb2_tree *tree;
  35         const char *server_name;
  36         bool dead;
  37 };
  38 
  39 
  40 /*
  41   tell the dcerpc layer that the transport is dead
  42 */
  43 static void pipe_dead(struct dcerpc_connection *c, NTSTATUS status)
     /* [<][>][^][v][top][bottom][index][help] */
  44 {
  45         struct smb2_private *smb = (struct smb2_private *)c->transport.private_data;
  46 
  47         if (smb->dead) {
  48                 return;
  49         }
  50 
  51         smb->dead = true;
  52 
  53         if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
  54                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
  55         }
  56 
  57         if (NT_STATUS_EQUAL(NT_STATUS_OK, status)) {
  58                 status = NT_STATUS_END_OF_FILE;
  59         }
  60 
  61         if (c->transport.recv_data) {
  62                 c->transport.recv_data(c, NULL, status);
  63         }
  64 }
  65 
  66 
  67 /* 
  68    this holds the state of an in-flight call
  69 */
  70 struct smb2_read_state {
  71         struct dcerpc_connection *c;
  72         DATA_BLOB data;
  73 };
  74 
  75 /*
  76   called when a read request has completed
  77 */
  78 static void smb2_read_callback(struct smb2_request *req)
     /* [<][>][^][v][top][bottom][index][help] */
  79 {
  80         struct smb2_private *smb;
  81         struct smb2_read_state *state;
  82         struct smb2_read io;
  83         uint16_t frag_length;
  84         NTSTATUS status;
  85 
  86         state = talloc_get_type(req->async.private_data, struct smb2_read_state);
  87         smb = talloc_get_type(state->c->transport.private_data, struct smb2_private);
  88 
  89         status = smb2_read_recv(req, state, &io);
  90         if (NT_STATUS_IS_ERR(status)) {
  91                 pipe_dead(state->c, status);
  92                 talloc_free(state);
  93                 return;
  94         }
  95 
  96         if (!data_blob_append(state, &state->data, 
  97                                   io.out.data.data, io.out.data.length)) {
  98                 pipe_dead(state->c, NT_STATUS_NO_MEMORY);
  99                 talloc_free(state);
 100                 return;
 101         }
 102 
 103         if (state->data.length < 16) {
 104                 DEBUG(0,("dcerpc_smb2: short packet (length %d) in read callback!\n",
 105                          (int)state->data.length));
 106                 pipe_dead(state->c, NT_STATUS_INFO_LENGTH_MISMATCH);
 107                 talloc_free(state);
 108                 return;
 109         }
 110 
 111         frag_length = dcerpc_get_frag_length(&state->data);
 112 
 113         if (frag_length <= state->data.length) {
 114                 DATA_BLOB data = state->data;
 115                 struct dcerpc_connection *c = state->c;
 116                 talloc_steal(c, data.data);
 117                 talloc_free(state);
 118                 c->transport.recv_data(c, &data, NT_STATUS_OK);
 119                 return;
 120         }
 121 
 122         /* initiate another read request, as we only got part of a fragment */
 123         ZERO_STRUCT(io);
 124         io.in.file.handle = smb->handle;
 125         io.in.length = MIN(state->c->srv_max_xmit_frag, 
 126                            frag_length - state->data.length);
 127         if (io.in.length < 16) {
 128                 io.in.length = 16;
 129         }
 130         
 131         req = smb2_read_send(smb->tree, &io);
 132         if (req == NULL) {
 133                 pipe_dead(state->c, NT_STATUS_NO_MEMORY);
 134                 talloc_free(state);
 135                 return;
 136         }
 137 
 138         req->async.fn = smb2_read_callback;
 139         req->async.private_data = state;
 140 }
 141 
 142 
 143 /*
 144   trigger a read request from the server, possibly with some initial
 145   data in the read buffer
 146 */
 147 static NTSTATUS send_read_request_continue(struct dcerpc_connection *c, DATA_BLOB *blob)
     /* [<][>][^][v][top][bottom][index][help] */
 148 {
 149         struct smb2_private *smb = (struct smb2_private *)c->transport.private_data;
 150         struct smb2_read io;
 151         struct smb2_read_state *state;
 152         struct smb2_request *req;
 153 
 154         state = talloc(smb, struct smb2_read_state);
 155         if (state == NULL) {
 156                 return NT_STATUS_NO_MEMORY;
 157         }
 158 
 159         state->c = c;
 160         if (blob == NULL) {
 161                 state->data = data_blob(NULL, 0);
 162         } else {
 163                 state->data = *blob;
 164                 talloc_steal(state, state->data.data);
 165         }
 166 
 167         ZERO_STRUCT(io);
 168         io.in.file.handle = smb->handle;
 169 
 170         if (state->data.length >= 16) {
 171                 uint16_t frag_length = dcerpc_get_frag_length(&state->data);
 172                 io.in.length = frag_length - state->data.length;
 173         } else {
 174                 io.in.length = 0x2000;
 175         }
 176 
 177         req = smb2_read_send(smb->tree, &io);
 178         if (req == NULL) {
 179                 return NT_STATUS_NO_MEMORY;
 180         }
 181 
 182         req->async.fn = smb2_read_callback;
 183         req->async.private_data = state;
 184 
 185         return NT_STATUS_OK;
 186 }
 187 
 188 
 189 /*
 190   trigger a read request from the server
 191 */
 192 static NTSTATUS send_read_request(struct dcerpc_connection *c)
     /* [<][>][^][v][top][bottom][index][help] */
 193 {
 194         struct smb2_private *smb = (struct smb2_private *)c->transport.private_data;
 195 
 196         if (smb->dead) {
 197                 return NT_STATUS_CONNECTION_DISCONNECTED;
 198         }
 199 
 200         return send_read_request_continue(c, NULL);
 201 }
 202 
 203 /* 
 204    this holds the state of an in-flight trans call
 205 */
 206 struct smb2_trans_state {
 207         struct dcerpc_connection *c;
 208 };
 209 
 210 /*
 211   called when a trans request has completed
 212 */
 213 static void smb2_trans_callback(struct smb2_request *req)
     /* [<][>][^][v][top][bottom][index][help] */
 214 {
 215         struct smb2_trans_state *state = talloc_get_type(req->async.private_data,
 216                                                         struct smb2_trans_state);
 217         struct dcerpc_connection *c = state->c;
 218         NTSTATUS status;
 219         struct smb2_ioctl io;
 220 
 221         status = smb2_ioctl_recv(req, state, &io);
 222         if (NT_STATUS_IS_ERR(status)) {
 223                 pipe_dead(c, status);
 224                 return;
 225         }
 226 
 227         if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
 228                 DATA_BLOB data = io.out.out;
 229                 talloc_steal(c, data.data);
 230                 talloc_free(state);
 231                 c->transport.recv_data(c, &data, NT_STATUS_OK);
 232                 return;
 233         }
 234 
 235         /* there is more to receive - setup a read */
 236         send_read_request_continue(c, &io.out.out);
 237         talloc_free(state);
 238 }
 239 
 240 /*
 241   send a SMBtrans style request, using a named pipe read_write fsctl
 242 */
 243 static NTSTATUS smb2_send_trans_request(struct dcerpc_connection *c, DATA_BLOB *blob)
     /* [<][>][^][v][top][bottom][index][help] */
 244 {
 245         struct smb2_private *smb = talloc_get_type(c->transport.private_data,
 246                                                    struct smb2_private);
 247         struct smb2_ioctl io;
 248         struct smb2_trans_state *state;
 249         struct smb2_request *req;
 250 
 251         state = talloc(smb, struct smb2_trans_state);
 252         if (state == NULL) {
 253                 return NT_STATUS_NO_MEMORY;
 254         }
 255 
 256         state->c = c;
 257         
 258         ZERO_STRUCT(io);
 259         io.in.file.handle       = smb->handle;
 260         io.in.function          = FSCTL_NAMED_PIPE_READ_WRITE;
 261         io.in.max_response_size = 0x1000;
 262         io.in.flags             = 1;
 263         io.in.out               = *blob;
 264 
 265         req = smb2_ioctl_send(smb->tree, &io);
 266         if (req == NULL) {
 267                 talloc_free(state);
 268                 return NT_STATUS_NO_MEMORY;
 269         }
 270 
 271         req->async.fn = smb2_trans_callback;
 272         req->async.private_data = state;
 273 
 274         talloc_steal(state, req);
 275 
 276         return NT_STATUS_OK;
 277 }
 278 
 279 /*
 280   called when a write request has completed
 281 */
 282 static void smb2_write_callback(struct smb2_request *req)
     /* [<][>][^][v][top][bottom][index][help] */
 283 {
 284         struct dcerpc_connection *c = (struct dcerpc_connection *)req->async.private_data;
 285 
 286         if (!NT_STATUS_IS_OK(req->status)) {
 287                 DEBUG(0,("dcerpc_smb2: write callback error\n"));
 288                 pipe_dead(c, req->status);
 289         }
 290 
 291         smb2_request_destroy(req);
 292 }
 293 
 294 /* 
 295    send a packet to the server
 296 */
 297 static NTSTATUS smb2_send_request(struct dcerpc_connection *c, DATA_BLOB *blob, 
     /* [<][>][^][v][top][bottom][index][help] */
 298                                   bool trigger_read)
 299 {
 300         struct smb2_private *smb = (struct smb2_private *)c->transport.private_data;
 301         struct smb2_write io;
 302         struct smb2_request *req;
 303 
 304         if (smb->dead) {
 305                 return NT_STATUS_CONNECTION_DISCONNECTED;
 306         }
 307 
 308         if (trigger_read) {
 309                 return smb2_send_trans_request(c, blob);
 310         }
 311 
 312         ZERO_STRUCT(io);
 313         io.in.file.handle       = smb->handle;
 314         io.in.data              = *blob;
 315 
 316         req = smb2_write_send(smb->tree, &io);
 317         if (req == NULL) {
 318                 return NT_STATUS_NO_MEMORY;
 319         }
 320 
 321         req->async.fn = smb2_write_callback;
 322         req->async.private_data = c;
 323 
 324         return NT_STATUS_OK;
 325 }
 326 
 327 /* 
 328    shutdown SMB pipe connection
 329 */
 330 static NTSTATUS smb2_shutdown_pipe(struct dcerpc_connection *c, NTSTATUS status)
     /* [<][>][^][v][top][bottom][index][help] */
 331 {
 332         struct smb2_private *smb = (struct smb2_private *)c->transport.private_data;
 333         struct smb2_close io;
 334         struct smb2_request *req;
 335 
 336         /* maybe we're still starting up */
 337         if (!smb) return status;
 338 
 339         ZERO_STRUCT(io);
 340         io.in.file.handle = smb->handle;
 341         req = smb2_close_send(smb->tree, &io);
 342         if (req != NULL) {
 343                 /* we don't care if this fails, so just free it if it succeeds */
 344                 req->async.fn = (void (*)(struct smb2_request *))talloc_free;
 345         }
 346 
 347         talloc_free(smb);
 348 
 349         return status;
 350 }
 351 
 352 /*
 353   return SMB server name
 354 */
 355 static const char *smb2_peer_name(struct dcerpc_connection *c)
     /* [<][>][^][v][top][bottom][index][help] */
 356 {
 357         struct smb2_private *smb = talloc_get_type(c->transport.private_data,
 358                                                    struct smb2_private);
 359         return smb->server_name;
 360 }
 361 
 362 /*
 363   return remote name we make the actual connection (good for kerberos) 
 364 */
 365 static const char *smb2_target_hostname(struct dcerpc_connection *c)
     /* [<][>][^][v][top][bottom][index][help] */
 366 {
 367         struct smb2_private *smb = talloc_get_type(c->transport.private_data, 
 368                                                    struct smb2_private);
 369         return smb->tree->session->transport->socket->hostname;
 370 }
 371 
 372 /*
 373   fetch the user session key 
 374 */
 375 static NTSTATUS smb2_session_key(struct dcerpc_connection *c, DATA_BLOB *session_key)
     /* [<][>][^][v][top][bottom][index][help] */
 376 {
 377         struct smb2_private *smb = talloc_get_type(c->transport.private_data,
 378                                                    struct smb2_private);
 379         *session_key = smb->tree->session->session_key;
 380         if (session_key->data == NULL) {
 381                 return NT_STATUS_NO_USER_SESSION_KEY;
 382         }
 383         return NT_STATUS_OK;
 384 }
 385 
 386 struct pipe_open_smb2_state {
 387         struct dcerpc_connection *c;
 388         struct composite_context *ctx;
 389 };
 390 
 391 static void pipe_open_recv(struct smb2_request *req);
 392 
 393 struct composite_context *dcerpc_pipe_open_smb2_send(struct dcerpc_pipe *p, 
     /* [<][>][^][v][top][bottom][index][help] */
 394                                                      struct smb2_tree *tree,
 395                                                      const char *pipe_name)
 396 {
 397         struct composite_context *ctx;
 398         struct pipe_open_smb2_state *state;
 399         struct smb2_create io;
 400         struct smb2_request *req;
 401         struct dcerpc_connection *c = p->conn;
 402 
 403         ctx = composite_create(c, c->event_ctx);
 404         if (ctx == NULL) return NULL;
 405 
 406         state = talloc(ctx, struct pipe_open_smb2_state);
 407         if (composite_nomem(state, ctx)) return ctx;
 408         ctx->private_data = state;
 409 
 410         state->c = c;
 411         state->ctx = ctx;
 412 
 413         ZERO_STRUCT(io);
 414         io.in.desired_access = 
 415                 SEC_STD_READ_CONTROL |
 416                 SEC_FILE_READ_ATTRIBUTE |
 417                 SEC_FILE_WRITE_ATTRIBUTE |
 418                 SEC_STD_SYNCHRONIZE |
 419                 SEC_FILE_READ_EA |
 420                 SEC_FILE_WRITE_EA |
 421                 SEC_FILE_READ_DATA |
 422                 SEC_FILE_WRITE_DATA |
 423                 SEC_FILE_APPEND_DATA;
 424         io.in.share_access = 
 425                 NTCREATEX_SHARE_ACCESS_READ |
 426                 NTCREATEX_SHARE_ACCESS_WRITE;
 427         io.in.create_disposition = NTCREATEX_DISP_OPEN;
 428         io.in.create_options   = 
 429                 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | 
 430                 NTCREATEX_OPTIONS_NO_RECALL;
 431         io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
 432 
 433         if ((strncasecmp(pipe_name, "/pipe/", 6) == 0) || 
 434             (strncasecmp(pipe_name, "\\pipe\\", 6) == 0)) {
 435                 pipe_name += 6;
 436         }
 437         io.in.fname = pipe_name;
 438 
 439         req = smb2_create_send(tree, &io);
 440         composite_continue_smb2(ctx, req, pipe_open_recv, state);
 441         return ctx;
 442 }
 443 
 444 static void pipe_open_recv(struct smb2_request *req)
     /* [<][>][^][v][top][bottom][index][help] */
 445 {
 446         struct pipe_open_smb2_state *state =
 447                 talloc_get_type(req->async.private_data,
 448                                 struct pipe_open_smb2_state);
 449         struct composite_context *ctx = state->ctx;
 450         struct dcerpc_connection *c = state->c;
 451         struct smb2_tree *tree = req->tree;
 452         struct smb2_private *smb;
 453         struct smb2_create io;
 454 
 455         ctx->status = smb2_create_recv(req, state, &io);
 456         if (!composite_is_ok(ctx)) return;
 457 
 458         /*
 459           fill in the transport methods
 460         */
 461         c->transport.transport = NCACN_NP;
 462         c->transport.private_data = NULL;
 463         c->transport.shutdown_pipe = smb2_shutdown_pipe;
 464         c->transport.peer_name = smb2_peer_name;
 465         c->transport.target_hostname = smb2_target_hostname;
 466 
 467         c->transport.send_request = smb2_send_request;
 468         c->transport.send_read = send_read_request;
 469         c->transport.recv_data = NULL;
 470         
 471         /* Over-ride the default session key with the SMB session key */
 472         c->security_state.session_key = smb2_session_key;
 473 
 474         smb = talloc(c, struct smb2_private);
 475         if (composite_nomem(smb, ctx)) return;
 476 
 477         smb->handle     = io.out.file.handle;
 478         smb->tree       = talloc_reference(smb, tree);
 479         smb->server_name= strupper_talloc(smb, 
 480                                           tree->session->transport->socket->hostname);
 481         if (composite_nomem(smb->server_name, ctx)) return;
 482         smb->dead       = false;
 483 
 484         c->transport.private_data = smb;
 485 
 486         composite_done(ctx);
 487 }
 488 
 489 NTSTATUS dcerpc_pipe_open_smb2_recv(struct composite_context *c)
     /* [<][>][^][v][top][bottom][index][help] */
 490 {
 491         NTSTATUS status = composite_wait(c);
 492         talloc_free(c);
 493         return status;
 494 }
 495 
 496 NTSTATUS dcerpc_pipe_open_smb2(struct dcerpc_pipe *p,
     /* [<][>][^][v][top][bottom][index][help] */
 497                                struct smb2_tree *tree,
 498                                const char *pipe_name)
 499 {
 500         struct composite_context *ctx = dcerpc_pipe_open_smb2_send(p, tree, pipe_name);
 501         return dcerpc_pipe_open_smb2_recv(ctx);
 502 }
 503 
 504 /*
 505   return the SMB2 tree used for a dcerpc over SMB2 pipe
 506 */
 507 struct smb2_tree *dcerpc_smb2_tree(struct dcerpc_connection *c)
     /* [<][>][^][v][top][bottom][index][help] */
 508 {
 509         struct smb2_private *smb = talloc_get_type(c->transport.private_data,
 510                                                    struct smb2_private);
 511         return smb->tree;
 512 }

/* [<][>][^][v][top][bottom][index][help] */