s4:librpc/rpc: remove unused dcerpc_smb_tree()
[mat/samba.git] / source4 / librpc / rpc / dcerpc_smb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    dcerpc over SMB transport
5
6    Copyright (C) Tim Potter 2003
7    Copyright (C) Andrew Tridgell 2003
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/composite/composite.h"
26 #include "librpc/rpc/dcerpc.h"
27 #include "librpc/rpc/dcerpc_proto.h"
28 #include "librpc/rpc/rpc_common.h"
29 #include "../libcli/smb/smbXcli_base.h"
30
31 /* transport private information used by SMB pipe transport */
32 struct smb_private {
33         uint16_t fnum;
34         struct smbcli_tree *tree;
35         DATA_BLOB session_key;
36         const char *server_name;
37         bool dead;
38 };
39
40
41 /*
42   tell the dcerpc layer that the transport is dead
43 */
44 static void pipe_dead(struct dcecli_connection *c, NTSTATUS status)
45 {
46         struct smb_private *smb = (struct smb_private *)c->transport.private_data;
47
48         if (smb->dead) {
49                 return;
50         }
51
52         smb->dead = true;
53
54         if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
55                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
56         }
57
58         if (NT_STATUS_EQUAL(NT_STATUS_OK, status)) {
59                 status = NT_STATUS_END_OF_FILE;
60         }
61
62         if (c->transport.recv_data) {
63                 c->transport.recv_data(c, NULL, status);
64         }
65 }
66
67
68 /* 
69    this holds the state of an in-flight call
70 */
71 struct smb_read_state {
72         struct dcecli_connection *c;
73         struct smbcli_request *req;
74         size_t received;
75         DATA_BLOB data;
76         union smb_read *io;
77 };
78
79 /*
80   called when a read request has completed
81 */
82 static void smb_read_callback(struct smbcli_request *req)
83 {
84         struct dcecli_connection *c;
85         struct smb_private *smb;
86         struct smb_read_state *state;
87         union smb_read *io;
88         uint16_t frag_length;
89         NTSTATUS status;
90
91         state = talloc_get_type(req->async.private_data, struct smb_read_state);
92         smb = talloc_get_type(state->c->transport.private_data, struct smb_private);
93         io = state->io;
94         c = state->c;
95
96         status = smb_raw_read_recv(state->req, io);
97         if (NT_STATUS_IS_ERR(status)) {
98                 talloc_free(state);
99                 pipe_dead(c, status);
100                 return;
101         }
102
103         state->received += io->readx.out.nread;
104
105         if (state->received < 16) {
106                 DEBUG(0,("dcerpc_smb: short packet (length %d) in read callback!\n",
107                          (int)state->received));
108                 talloc_free(state);
109                 pipe_dead(c, NT_STATUS_INFO_LENGTH_MISMATCH);
110                 return;
111         }
112
113         frag_length = dcerpc_get_frag_length(&state->data);
114
115         if (frag_length <= state->received) {
116                 DATA_BLOB data = state->data;
117                 data.length = state->received;
118                 talloc_steal(state->c, data.data);
119                 talloc_free(state);
120                 c->transport.recv_data(c, &data, NT_STATUS_OK);
121                 return;
122         }
123
124         /* initiate another read request, as we only got part of a fragment */
125         state->data.data = talloc_realloc(state, state->data.data, uint8_t, frag_length);
126
127         io->readx.in.mincnt = MIN(state->c->srv_max_xmit_frag, 
128                                   frag_length - state->received);
129         io->readx.in.maxcnt = io->readx.in.mincnt;
130         io->readx.out.data = state->data.data + state->received;
131
132         state->req = smb_raw_read_send(smb->tree, io);
133         if (state->req == NULL) {
134                 talloc_free(state);
135                 pipe_dead(c, NT_STATUS_NO_MEMORY);
136                 return;
137         }
138
139         state->req->async.fn = smb_read_callback;
140         state->req->async.private_data = state;
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 dcecli_connection *c, DATA_BLOB *blob)
148 {
149         struct smb_private *smb = (struct smb_private *)c->transport.private_data;
150         union smb_read *io;
151         struct smb_read_state *state;
152         struct smbcli_request *req;
153
154         state = talloc(smb, struct smb_read_state);
155         if (state == NULL) {
156                 return NT_STATUS_NO_MEMORY;
157         }
158
159         state->c = c;
160         if (blob == NULL) {
161                 state->received = 0;
162                 state->data = data_blob_talloc(state, NULL, 0x2000);
163         } else {
164                 uint32_t frag_length = blob->length>=16?
165                         dcerpc_get_frag_length(blob):0x2000;
166                 state->received = blob->length;
167                 state->data = data_blob_talloc(state, NULL, frag_length);
168                 if (!state->data.data) {
169                         talloc_free(state);
170                         return NT_STATUS_NO_MEMORY;
171                 }
172                 memcpy(state->data.data, blob->data, blob->length);
173         }
174
175         state->io = talloc(state, union smb_read);
176
177         io = state->io;
178         io->generic.level = RAW_READ_READX;
179         io->readx.in.file.fnum = smb->fnum;
180         io->readx.in.mincnt = state->data.length - state->received;
181         io->readx.in.maxcnt = io->readx.in.mincnt;
182         io->readx.in.offset = 0;
183         io->readx.in.remaining = 0;
184         io->readx.in.read_for_execute = false;
185         io->readx.out.data = state->data.data + state->received;
186         req = smb_raw_read_send(smb->tree, io);
187         if (req == NULL) {
188                 return NT_STATUS_NO_MEMORY;
189         }
190
191         req->async.fn = smb_read_callback;
192         req->async.private_data = state;
193
194         state->req = req;
195
196         return NT_STATUS_OK;
197 }
198
199
200 /*
201   trigger a read request from the server
202 */
203 static NTSTATUS send_read_request(struct dcecli_connection *c)
204 {
205         struct smb_private *smb = (struct smb_private *)c->transport.private_data;
206
207         if (smb->dead) {
208                 return NT_STATUS_CONNECTION_DISCONNECTED;
209         }
210
211         return send_read_request_continue(c, NULL);
212 }
213
214 /* 
215    this holds the state of an in-flight trans call
216 */
217 struct smb_trans_state {
218         struct dcecli_connection *c;
219         struct smbcli_request *req;
220         struct smb_trans2 *trans;
221 };
222
223 /*
224   called when a trans request has completed
225 */
226 static void smb_trans_callback(struct smbcli_request *req)
227 {
228         struct smb_trans_state *state = (struct smb_trans_state *)req->async.private_data;
229         struct dcecli_connection *c = state->c;
230         NTSTATUS status;
231
232         status = smb_raw_trans_recv(req, state, state->trans);
233
234         if (NT_STATUS_IS_ERR(status)) {
235                 pipe_dead(c, status);
236                 return;
237         }
238
239         if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
240                 DATA_BLOB data = state->trans->out.data;
241                 talloc_steal(c, data.data);
242                 talloc_free(state);
243                 c->transport.recv_data(c, &data, NT_STATUS_OK);
244                 return;
245         }
246
247         /* there is more to receive - setup a readx */
248         send_read_request_continue(c, &state->trans->out.data);
249         talloc_free(state);
250 }
251
252 /*
253   send a SMBtrans style request
254 */
255 static NTSTATUS smb_send_trans_request(struct dcecli_connection *c, DATA_BLOB *blob)
256 {
257         struct smb_private *smb = (struct smb_private *)c->transport.private_data;
258         struct smb_trans2 *trans;
259         uint16_t setup[2];
260         struct smb_trans_state *state;
261         uint16_t max_data;
262
263         state = talloc(c, struct smb_trans_state);
264         if (state == NULL) {
265                 return NT_STATUS_NO_MEMORY;
266         }
267
268         state->c = c;
269         state->trans = talloc(state, struct smb_trans2);
270         trans = state->trans;
271
272         trans->in.data = *blob;
273         trans->in.params = data_blob(NULL, 0);
274         
275         setup[0] = TRANSACT_DCERPCCMD;
276         setup[1] = smb->fnum;
277
278         if (c->srv_max_xmit_frag > 0) {
279                 max_data = MIN(UINT16_MAX, c->srv_max_xmit_frag);
280         } else {
281                 max_data = UINT16_MAX;
282         }
283
284         trans->in.max_param = 0;
285         trans->in.max_data = max_data;
286         trans->in.max_setup = 0;
287         trans->in.setup_count = 2;
288         trans->in.flags = 0;
289         trans->in.timeout = 0;
290         trans->in.setup = setup;
291         trans->in.trans_name = "\\PIPE\\";
292
293         state->req = smb_raw_trans_send(smb->tree, trans);
294         if (state->req == NULL) {
295                 talloc_free(state);
296                 return NT_STATUS_NO_MEMORY;
297         }
298
299         state->req->async.fn = smb_trans_callback;
300         state->req->async.private_data = state;
301
302         talloc_steal(state, state->req);
303
304         return NT_STATUS_OK;
305 }
306
307 /*
308   called when a write request has completed
309 */
310 static void smb_write_callback(struct smbcli_request *req)
311 {
312         struct dcecli_connection *c = (struct dcecli_connection *)req->async.private_data;
313         union smb_write io;
314         NTSTATUS status;
315
316         ZERO_STRUCT(io);
317         io.generic.level = RAW_WRITE_WRITEX;
318
319         status = smb_raw_write_recv(req, &io);
320         if (!NT_STATUS_IS_OK(status)) {
321                 DEBUG(0,("dcerpc_smb: write callback error: %s\n",
322                         nt_errstr(status)));
323                 pipe_dead(c, status);
324         }
325 }
326
327 /* 
328    send a packet to the server
329 */
330 static NTSTATUS smb_send_request(struct dcecli_connection *c, DATA_BLOB *blob, 
331                                  bool trigger_read)
332 {
333         struct smb_private *smb = (struct smb_private *)c->transport.private_data;
334         union smb_write io;
335         struct smbcli_request *req;
336
337         if (!smb || smb->dead) {
338                 return NT_STATUS_CONNECTION_DISCONNECTED;
339         }
340
341         if (trigger_read) {
342                 return smb_send_trans_request(c, blob);
343         }
344
345         io.generic.level = RAW_WRITE_WRITEX;
346         io.writex.in.file.fnum = smb->fnum;
347         io.writex.in.offset = 0;
348         io.writex.in.wmode = PIPE_START_MESSAGE;
349         io.writex.in.remaining = blob->length;
350         io.writex.in.count = blob->length;
351         io.writex.in.data = blob->data;
352
353         /* we must not timeout at the smb level for rpc requests, as otherwise
354            signing/sealing can be messed up */
355         smb->tree->session->transport->options.request_timeout = 0;
356
357         req = smb_raw_write_send(smb->tree, &io);
358         if (req == NULL) {
359                 return NT_STATUS_NO_MEMORY;
360         }
361
362         req->async.fn = smb_write_callback;
363         req->async.private_data = c;
364
365         return NT_STATUS_OK;
366 }
367
368
369 static void free_request(struct smbcli_request *req)
370 {
371         talloc_free(req);
372 }
373
374 /* 
375    shutdown SMB pipe connection
376 */
377 static NTSTATUS smb_shutdown_pipe(struct dcecli_connection *c, NTSTATUS status)
378 {
379         struct smb_private *smb = (struct smb_private *)c->transport.private_data;
380         union smb_close io;
381         struct smbcli_request *req;
382
383         /* maybe we're still starting up */
384         if (!smb) return status;
385
386         io.close.level = RAW_CLOSE_CLOSE;
387         io.close.in.file.fnum = smb->fnum;
388         io.close.in.write_time = 0;
389         req = smb_raw_close_send(smb->tree, &io);
390         if (req != NULL) {
391                 /* we don't care if this fails, so just free it if it succeeds */
392                 req->async.fn = free_request;
393         }
394
395         talloc_free(smb);
396         c->transport.private_data = NULL;
397
398         return status;
399 }
400
401 /*
402   return SMB server name (called name)
403 */
404 static const char *smb_peer_name(struct dcecli_connection *c)
405 {
406         struct smb_private *smb = (struct smb_private *)c->transport.private_data;
407         if (smb == NULL) return "";
408         return smb->server_name;
409 }
410
411 /*
412   return remote name we make the actual connection (good for kerberos) 
413 */
414 static const char *smb_target_hostname(struct dcecli_connection *c)
415 {
416         struct smb_private *smb = talloc_get_type(c->transport.private_data, struct smb_private);
417         if (smb == NULL) return "";
418         return smbXcli_conn_remote_name(smb->tree->session->transport->conn);
419 }
420
421 /*
422   fetch the user session key 
423 */
424 static NTSTATUS smb_session_key(struct dcecli_connection *c, DATA_BLOB *session_key)
425 {
426         struct smb_private *smb = (struct smb_private *)c->transport.private_data;
427
428         if (smb == NULL) return NT_STATUS_CONNECTION_DISCONNECTED;
429
430         if (smb->session_key.length == 0) {
431                 return NT_STATUS_NO_USER_SESSION_KEY;
432         }
433
434         *session_key = smb->session_key;
435         return NT_STATUS_OK;
436 }
437
438 struct pipe_open_smb_state {
439         union smb_open *open;
440         struct dcecli_connection *c;
441         struct smbcli_tree *tree;
442         struct composite_context *ctx;
443 };
444
445 static void pipe_open_recv(struct smbcli_request *req);
446
447 struct composite_context *dcerpc_pipe_open_smb_send(struct dcerpc_pipe *p, 
448                                                     struct smbcli_tree *tree,
449                                                     const char *pipe_name)
450 {
451         struct composite_context *ctx;
452         struct pipe_open_smb_state *state;
453         struct smbcli_request *req;
454         struct dcecli_connection *c = p->conn;
455
456         /* if we don't have a binding on this pipe yet, then create one */
457         if (p->binding == NULL) {
458                 NTSTATUS status;
459                 const char *r = smbXcli_conn_remote_name(tree->session->transport->conn);
460                 char *s;
461                 SMB_ASSERT(r != NULL);
462                 s = talloc_asprintf(p, "ncacn_np:%s", r);
463                 if (s == NULL) return NULL;
464                 status = dcerpc_parse_binding(p, s, &p->binding);
465                 talloc_free(s);
466                 if (!NT_STATUS_IS_OK(status)) {
467                         return NULL;
468                 }
469         }
470
471         ctx = composite_create(c, c->event_ctx);
472         if (ctx == NULL) return NULL;
473
474         state = talloc(ctx, struct pipe_open_smb_state);
475         if (composite_nomem(state, ctx)) return ctx;
476         ctx->private_data = state;
477
478         state->c = c;
479         state->tree = tree;
480         state->ctx = ctx;
481
482         state->open = talloc(state, union smb_open);
483         if (composite_nomem(state->open, ctx)) return ctx;
484
485         state->open->ntcreatex.level = RAW_OPEN_NTCREATEX;
486         state->open->ntcreatex.in.flags = 0;
487         state->open->ntcreatex.in.root_fid.fnum = 0;
488         state->open->ntcreatex.in.access_mask = 
489                 SEC_STD_READ_CONTROL |
490                 SEC_FILE_WRITE_ATTRIBUTE |
491                 SEC_FILE_WRITE_EA |
492                 SEC_FILE_READ_DATA |
493                 SEC_FILE_WRITE_DATA;
494         state->open->ntcreatex.in.file_attr = 0;
495         state->open->ntcreatex.in.alloc_size = 0;
496         state->open->ntcreatex.in.share_access = 
497                 NTCREATEX_SHARE_ACCESS_READ |
498                 NTCREATEX_SHARE_ACCESS_WRITE;
499         state->open->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
500         state->open->ntcreatex.in.create_options = 0;
501         state->open->ntcreatex.in.impersonation =
502                 NTCREATEX_IMPERSONATION_IMPERSONATION;
503         state->open->ntcreatex.in.security_flags = 0;
504
505         if ((strncasecmp(pipe_name, "/pipe/", 6) == 0) || 
506             (strncasecmp(pipe_name, "\\pipe\\", 6) == 0)) {
507                 pipe_name += 6;
508         }
509         state->open->ntcreatex.in.fname =
510                 (pipe_name[0] == '\\') ?
511                 talloc_strdup(state->open, pipe_name) :
512                 talloc_asprintf(state->open, "\\%s", pipe_name);
513         if (composite_nomem(state->open->ntcreatex.in.fname, ctx)) return ctx;
514
515         req = smb_raw_open_send(tree, state->open);
516         composite_continue_smb(ctx, req, pipe_open_recv, state);
517         return ctx;
518 }
519
520 static void pipe_open_recv(struct smbcli_request *req)
521 {
522         struct pipe_open_smb_state *state = talloc_get_type(req->async.private_data,
523                                             struct pipe_open_smb_state);
524         struct composite_context *ctx = state->ctx;
525         struct dcecli_connection *c = state->c;
526         struct smb_private *smb;
527         
528         ctx->status = smb_raw_open_recv(req, state, state->open);
529         if (!composite_is_ok(ctx)) return;
530
531         /*
532           fill in the transport methods
533         */
534         c->transport.transport       = NCACN_NP;
535         c->transport.private_data    = NULL;
536         c->transport.shutdown_pipe   = smb_shutdown_pipe;
537         c->transport.peer_name       = smb_peer_name;
538         c->transport.target_hostname = smb_target_hostname;
539
540         c->transport.send_request    = smb_send_request;
541         c->transport.send_read       = send_read_request;
542         c->transport.recv_data       = NULL;
543         
544         /* Over-ride the default session key with the SMB session key */
545         c->security_state.session_key = smb_session_key;
546
547         smb = talloc(c, struct smb_private);
548         if (composite_nomem(smb, ctx)) return;
549
550         smb->fnum       = state->open->ntcreatex.out.file.fnum;
551         smb->tree       = talloc_reference(smb, state->tree);
552         smb->server_name= strupper_talloc(smb,
553                 smbXcli_conn_remote_name(state->tree->session->transport->conn));
554         if (composite_nomem(smb->server_name, ctx)) return;
555         smb->dead       = false;
556
557         ctx->status = smbXcli_session_application_key(state->tree->session->smbXcli,
558                                                       smb, &smb->session_key);
559         if (NT_STATUS_EQUAL(ctx->status, NT_STATUS_NO_USER_SESSION_KEY)) {
560                 smb->session_key = data_blob_null;
561                 ctx->status = NT_STATUS_OK;
562         }
563         if (!composite_is_ok(ctx)) return;
564
565         c->transport.private_data = smb;
566
567         composite_done(ctx);
568 }
569
570 NTSTATUS dcerpc_pipe_open_smb_recv(struct composite_context *c)
571 {
572         NTSTATUS status = composite_wait(c);
573         talloc_free(c);
574         return status;
575 }
576
577 _PUBLIC_ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe *p,
578                               struct smbcli_tree *tree,
579                               const char *pipe_name)
580 {
581         struct composite_context *ctx = dcerpc_pipe_open_smb_send(p, tree,
582                                                                   pipe_name);
583         return dcerpc_pipe_open_smb_recv(ctx);
584 }
585
586 struct composite_context *dcerpc_secondary_smb_send(struct dcecli_connection *c1,
587                                                     struct dcerpc_pipe *p2,
588                                                     const char *pipe_name)
589 {
590         struct smb_private *smb;
591
592         if (c1->transport.transport != NCACN_NP) return NULL;
593
594         smb = talloc_get_type(c1->transport.private_data, struct smb_private);
595         if (!smb) return NULL;
596
597         return dcerpc_pipe_open_smb_send(p2, smb->tree, pipe_name);
598 }
599
600 NTSTATUS dcerpc_secondary_smb_recv(struct composite_context *c)
601 {
602         return dcerpc_pipe_open_smb_recv(c);
603 }