s4:librpc: factor out xxx_dead() to dcerpc_transport_dead()
[metze/samba/wip.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 "system/filesys.h"
25 #include <tevent.h>
26 #include "lib/tsocket/tsocket.h"
27 #include "libcli/smb/smb_constants.h"
28 #include "libcli/smb/smbXcli_base.h"
29 #include "libcli/smb/tstream_smbXcli_np.h"
30 #include "libcli/raw/libcliraw.h"
31 #include "libcli/smb2/smb2.h"
32 #include "librpc/rpc/dcerpc.h"
33 #include "librpc/rpc/dcerpc_proto.h"
34 #include "libcli/composite/composite.h"
35
36 /* transport private information used by SMB pipe transport */
37 struct smb_private {
38         DATA_BLOB session_key;
39
40         /*
41          * these are needed to open a secondary connection
42          */
43         struct smbXcli_conn *conn;
44         struct smbXcli_session *session;
45         struct smbXcli_tcon *tcon;
46         uint32_t timeout_msec;
47 };
48
49
50 /*
51   Tell the dcerpc layer that the transport is dead.
52   This function is declared here because it is going to be private.
53 */
54 void dcerpc_transport_dead(struct dcecli_connection *c, NTSTATUS status);
55
56 struct smb_send_read_state {
57         struct dcecli_connection *p;
58 };
59
60 static int smb_send_read_state_destructor(struct smb_send_read_state *state)
61 {
62         struct dcecli_connection *p = state->p;
63
64         p->transport.read_subreq = NULL;
65
66         return 0;
67 }
68
69 static void smb_send_read_done(struct tevent_req *subreq);
70
71 static NTSTATUS smb_send_read(struct dcecli_connection *p)
72 {
73         struct smb_private *sock = talloc_get_type_abort(
74                 p->transport.private_data, struct smb_private);
75         struct smb_send_read_state *state;
76
77         if (p->transport.read_subreq != NULL) {
78                 p->transport.pending_reads++;
79                 return NT_STATUS_OK;
80         }
81
82         state = talloc_zero(sock, struct smb_send_read_state);
83         if (state == NULL) {
84                 return NT_STATUS_NO_MEMORY;
85         }
86         state->p = p;
87
88         talloc_set_destructor(state, smb_send_read_state_destructor);
89
90         p->transport.read_subreq = dcerpc_read_ncacn_packet_send(state,
91                                                           p->event_ctx,
92                                                           p->transport.stream);
93         if (p->transport.read_subreq == NULL) {
94                 return NT_STATUS_NO_MEMORY;
95         }
96         tevent_req_set_callback(p->transport.read_subreq, smb_send_read_done, state);
97
98         return NT_STATUS_OK;
99 }
100
101 static void smb_send_read_done(struct tevent_req *subreq)
102 {
103         struct smb_send_read_state *state =
104                 tevent_req_callback_data(subreq,
105                                          struct smb_send_read_state);
106         struct dcecli_connection *p = state->p;
107         NTSTATUS status;
108         struct ncacn_packet *pkt;
109         DATA_BLOB blob;
110
111         status = dcerpc_read_ncacn_packet_recv(subreq, state,
112                                                &pkt, &blob);
113         TALLOC_FREE(subreq);
114         if (!NT_STATUS_IS_OK(status)) {
115                 TALLOC_FREE(state);
116                 dcerpc_transport_dead(p, status);
117                 return;
118         }
119
120         /*
121          * here we steal into thet connection context,
122          * but p->transport.recv_data() will steal or free it again
123          */
124         talloc_steal(p, blob.data);
125         TALLOC_FREE(state);
126
127         if (p->transport.pending_reads > 0) {
128                 p->transport.pending_reads--;
129
130                 status = smb_send_read(p);
131                 if (!NT_STATUS_IS_OK(status)) {
132                         dcerpc_transport_dead(p, status);
133                         return;
134                 }
135         }
136
137         if (p->transport.recv_data) {
138                 p->transport.recv_data(p, &blob, NT_STATUS_OK);
139         }
140 }
141
142 /*
143    send an initial pdu in a multi-pdu sequence
144 */
145
146 struct smb_send_request_state {
147         struct dcecli_connection *p;
148         DATA_BLOB blob;
149         struct iovec iov;
150 };
151
152 static int smb_send_request_state_destructor(struct smb_send_request_state *state)
153 {
154         state->p->transport.read_subreq = NULL;
155
156         return 0;
157 }
158
159 static void smb_send_request_wait_done(struct tevent_req *subreq);
160 static void smb_send_request_done(struct tevent_req *subreq);
161
162 static NTSTATUS smb_send_request(struct dcecli_connection *p, DATA_BLOB *data,
163                                   bool trigger_read)
164 {
165         struct smb_private *sock = talloc_get_type_abort(
166                 p->transport.private_data, struct smb_private);
167         struct smb_send_request_state *state;
168         struct tevent_req *subreq;
169         bool use_trans = trigger_read;
170
171         if (p->transport.stream == NULL) {
172                 return NT_STATUS_CONNECTION_DISCONNECTED;
173         }
174
175         state = talloc_zero(sock, struct smb_send_request_state);
176         if (state == NULL) {
177                 return NT_STATUS_NO_MEMORY;
178         }
179         state->p = p;
180
181         state->blob = data_blob_talloc(state, data->data, data->length);
182         if (state->blob.data == NULL) {
183                 TALLOC_FREE(state);
184                 return NT_STATUS_NO_MEMORY;
185         }
186         state->iov.iov_base = (void *)state->blob.data;
187         state->iov.iov_len = state->blob.length;
188
189         if (p->transport.read_subreq != NULL) {
190                 use_trans = false;
191         }
192
193         if (use_trans) {
194                 /*
195                  * we need to block reads until our write is
196                  * the next in the write queue.
197                  */
198                 p->transport.read_subreq = tevent_queue_wait_send(state, p->event_ctx,
199                                                         p->transport.write_queue);
200                 if (p->transport.read_subreq == NULL) {
201                         TALLOC_FREE(state);
202                         return NT_STATUS_NO_MEMORY;
203                 }
204                 tevent_req_set_callback(p->transport.read_subreq,
205                                         smb_send_request_wait_done,
206                                         state);
207
208                 talloc_set_destructor(state, smb_send_request_state_destructor);
209
210                 trigger_read = false;
211         }
212
213         subreq = tstream_writev_queue_send(state, p->event_ctx,
214                                            p->transport.stream,
215                                            p->transport.write_queue,
216                                            &state->iov, 1);
217         if (subreq == NULL) {
218                 TALLOC_FREE(state);
219                 return NT_STATUS_NO_MEMORY;
220         }
221         tevent_req_set_callback(subreq, smb_send_request_done, state);
222
223         if (trigger_read) {
224                 smb_send_read(p);
225         }
226
227         return NT_STATUS_OK;
228 }
229
230 static void smb_send_request_wait_done(struct tevent_req *subreq)
231 {
232         struct smb_send_request_state *state =
233                 tevent_req_callback_data(subreq,
234                 struct smb_send_request_state);
235         struct dcecli_connection *p = state->p;
236         NTSTATUS status;
237         bool ok;
238
239         p->transport.read_subreq = NULL;
240         talloc_set_destructor(state, NULL);
241
242         ok = tevent_queue_wait_recv(subreq);
243         if (!ok) {
244                 TALLOC_FREE(state);
245                 dcerpc_transport_dead(p, NT_STATUS_NO_MEMORY);
246                 return;
247         }
248
249         if (tevent_queue_length(p->transport.write_queue) <= 2) {
250                 status = tstream_smbXcli_np_use_trans(p->transport.stream);
251                 if (!NT_STATUS_IS_OK(status)) {
252                         TALLOC_FREE(state);
253                         dcerpc_transport_dead(p, status);
254                         return;
255                 }
256         }
257
258         /* we free subreq after tstream_smbXcli_np_use_trans */
259         TALLOC_FREE(subreq);
260
261         smb_send_read(p);
262 }
263
264 static void smb_send_request_done(struct tevent_req *subreq)
265 {
266         struct smb_send_request_state *state =
267                 tevent_req_callback_data(subreq,
268                 struct smb_send_request_state);
269         int ret;
270         int error;
271
272         ret = tstream_writev_queue_recv(subreq, &error);
273         TALLOC_FREE(subreq);
274         if (ret == -1) {
275                 struct dcecli_connection *p = state->p;
276                 NTSTATUS status = map_nt_error_from_unix_common(error);
277
278                 TALLOC_FREE(state);
279                 dcerpc_transport_dead(p, status);
280                 return;
281         }
282
283         TALLOC_FREE(state);
284 }
285
286 /* 
287    shutdown SMB pipe connection
288 */
289 struct smb_shutdown_pipe_state {
290         struct dcecli_connection *c;
291         NTSTATUS status;
292 };
293
294 static void smb_shutdown_pipe_done(struct tevent_req *subreq);
295
296 static NTSTATUS smb_shutdown_pipe(struct dcecli_connection *c, NTSTATUS status)
297 {
298         struct smb_private *smb = talloc_get_type_abort(
299                 c->transport.private_data, struct smb_private);
300         struct smb_shutdown_pipe_state *state;
301         struct tevent_req *subreq;
302
303         if (c->transport.stream == NULL) {
304                 return NT_STATUS_OK;
305         }
306
307         state = talloc_zero(smb, struct smb_shutdown_pipe_state);
308         if (state == NULL) {
309                 return NT_STATUS_NO_MEMORY;
310         }
311         state->c = c;
312         state->status = status;
313
314         subreq = tstream_disconnect_send(state, c->event_ctx, c->transport.stream);
315         if (subreq == NULL) {
316                 return NT_STATUS_NO_MEMORY;
317         }
318         tevent_req_set_callback(subreq, smb_shutdown_pipe_done, state);
319
320         return status;
321 }
322
323 static void smb_shutdown_pipe_done(struct tevent_req *subreq)
324 {
325         struct smb_shutdown_pipe_state *state =
326                 tevent_req_callback_data(subreq, struct smb_shutdown_pipe_state);
327         struct dcecli_connection *c = state->c;
328         NTSTATUS status = state->status;
329         int error;
330
331         /*
332          * here we ignore the return values...
333          */
334         tstream_disconnect_recv(subreq, &error);
335         TALLOC_FREE(subreq);
336
337         TALLOC_FREE(state);
338
339         dcerpc_transport_dead(c, status);
340 }
341
342 /*
343   fetch the user session key 
344 */
345 static NTSTATUS smb_session_key(struct dcecli_connection *c, DATA_BLOB *session_key)
346 {
347         struct smb_private *smb = talloc_get_type_abort(
348                 c->transport.private_data, struct smb_private);
349
350         if (smb == NULL) return NT_STATUS_CONNECTION_DISCONNECTED;
351
352         if (smb->session_key.length == 0) {
353                 return NT_STATUS_NO_USER_SESSION_KEY;
354         }
355
356         *session_key = smb->session_key;
357         return NT_STATUS_OK;
358 }
359
360 struct dcerpc_pipe_open_smb_state {
361         struct dcecli_connection *c;
362         struct composite_context *ctx;
363
364         const char *fname;
365
366         struct smb_private *smb;
367 };
368
369 static void dcerpc_pipe_open_smb_done(struct tevent_req *subreq);
370
371 struct composite_context *dcerpc_pipe_open_smb_send(struct dcecli_connection *c,
372                                                 struct smbXcli_conn *conn,
373                                                 struct smbXcli_session *session,
374                                                 struct smbXcli_tcon *tcon,
375                                                 uint32_t timeout_msec,
376                                                 const char *pipe_name)
377 {
378         struct composite_context *ctx;
379         struct dcerpc_pipe_open_smb_state *state;
380         uint16_t pid = 0;
381         struct tevent_req *subreq;
382
383         ctx = composite_create(c, c->event_ctx);
384         if (ctx == NULL) return NULL;
385
386         state = talloc(ctx, struct dcerpc_pipe_open_smb_state);
387         if (composite_nomem(state, ctx)) return ctx;
388         ctx->private_data = state;
389
390         state->c = c;
391         state->ctx = ctx;
392
393         if ((strncasecmp(pipe_name, "/pipe/", 6) == 0) || 
394             (strncasecmp(pipe_name, "\\pipe\\", 6) == 0)) {
395                 pipe_name += 6;
396         }
397         if ((strncasecmp(pipe_name, "/", 1) == 0) ||
398             (strncasecmp(pipe_name, "\\", 1) == 0)) {
399                 pipe_name += 1;
400         }
401         state->fname = talloc_strdup(state, pipe_name);
402         if (composite_nomem(state->fname, ctx)) return ctx;
403
404         state->smb = talloc_zero(state, struct smb_private);
405         if (composite_nomem(state->smb, ctx)) return ctx;
406
407         state->smb->conn = conn;
408         state->smb->session = session;
409         state->smb->tcon = tcon;
410         state->smb->timeout_msec = timeout_msec;
411
412         state->c->server_name = strupper_talloc(state->c,
413                 smbXcli_conn_remote_name(conn));
414         if (composite_nomem(state->c->server_name, ctx)) return ctx;
415
416         ctx->status = smbXcli_session_application_key(session,
417                                                       state->smb,
418                                                       &state->smb->session_key);
419         if (NT_STATUS_EQUAL(ctx->status, NT_STATUS_NO_USER_SESSION_KEY)) {
420                 state->smb->session_key = data_blob_null;
421                 ctx->status = NT_STATUS_OK;
422         }
423         if (!composite_is_ok(ctx)) return ctx;
424
425         subreq = tstream_smbXcli_np_open_send(state, c->event_ctx,
426                                               conn, session, tcon, pid,
427                                               timeout_msec, state->fname);
428         if (composite_nomem(subreq, ctx)) return ctx;
429         tevent_req_set_callback(subreq, dcerpc_pipe_open_smb_done, state);
430
431         return ctx;
432 }
433
434 static void dcerpc_pipe_open_smb_done(struct tevent_req *subreq)
435 {
436         struct dcerpc_pipe_open_smb_state *state =
437                 tevent_req_callback_data(subreq,
438                 struct dcerpc_pipe_open_smb_state);
439         struct composite_context *ctx = state->ctx;
440         struct dcecli_connection *c = state->c;
441
442         ctx->status = tstream_smbXcli_np_open_recv(subreq,
443                                                    state->smb,
444                                                    &state->c->transport.stream);
445         TALLOC_FREE(subreq);
446         if (!composite_is_ok(ctx)) return;
447
448         state->c->transport.write_queue =
449                 tevent_queue_create(state->c, "dcerpc_smb write queue");
450         if (composite_nomem(state->c->transport.write_queue, ctx)) return;
451
452         /*
453           fill in the transport methods
454         */
455         c->transport.transport       = NCACN_NP;
456         c->transport.private_data    = NULL;
457         c->transport.shutdown_pipe   = smb_shutdown_pipe;
458
459         c->transport.send_request    = smb_send_request;
460         c->transport.send_read       = smb_send_read;
461         c->transport.recv_data       = NULL;
462
463         /*
464          * Windows uses 4280 for ncacn_np,
465          * so we also use it, this is what our
466          * tstream_smbXcli_np code relies on.
467          */
468         c->srv_max_xmit_frag = 4280;
469         c->srv_max_recv_frag = 4280;
470
471         /* Over-ride the default session key with the SMB session key */
472         c->security_state.session_key = smb_session_key;
473
474         c->transport.private_data = talloc_move(c, &state->smb);
475
476         composite_done(ctx);
477 }
478
479 NTSTATUS dcerpc_pipe_open_smb_recv(struct composite_context *c)
480 {
481         NTSTATUS status = composite_wait(c);
482         talloc_free(c);
483         return status;
484 }
485
486 _PUBLIC_ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe *p,
487                               struct smbcli_tree *t,
488                               const char *pipe_name)
489 {
490         struct smbXcli_conn *conn;
491         struct smbXcli_session *session;
492         struct smbXcli_tcon *tcon;
493         uint32_t timeout_msec;
494         struct composite_context *ctx;
495
496         conn = t->session->transport->conn;
497         session = t->session->smbXcli;
498         tcon = t->smbXcli;
499         smb1cli_tcon_set_id(tcon, t->tid);
500         timeout_msec = t->session->transport->options.request_timeout * 1000;
501
502         /* if we don't have a binding on this pipe yet, then create one */
503         if (p->binding == NULL) {
504                 NTSTATUS status;
505                 const char *r = smbXcli_conn_remote_name(conn);
506                 char *str;
507                 SMB_ASSERT(r != NULL);
508                 str = talloc_asprintf(p, "ncacn_np:%s", r);
509                 if (str == NULL) {
510                         return NT_STATUS_NO_MEMORY;
511                 }
512                 status = dcerpc_parse_binding(p, str,
513                                               &p->binding);
514                 talloc_free(str);
515                 if (!NT_STATUS_IS_OK(status)) {
516                         return status;
517                 }
518         }
519
520         ctx = dcerpc_pipe_open_smb_send(p->conn,
521                                         conn, session,
522                                         tcon, timeout_msec,
523                                         pipe_name);
524         if (ctx == NULL) {
525                 return NT_STATUS_NO_MEMORY;
526         }
527
528         return dcerpc_pipe_open_smb_recv(ctx);
529 }
530
531 _PUBLIC_ NTSTATUS dcerpc_pipe_open_smb2(struct dcerpc_pipe *p,
532                               struct smb2_tree *t,
533                               const char *pipe_name)
534 {
535         struct smbXcli_conn *conn;
536         struct smbXcli_session *session;
537         struct smbXcli_tcon *tcon;
538         uint32_t timeout_msec;
539         struct composite_context *ctx;
540
541         conn = t->session->transport->conn;
542         session = t->session->smbXcli;
543         tcon = t->smbXcli;
544         timeout_msec = t->session->transport->options.request_timeout * 1000;
545
546         /* if we don't have a binding on this pipe yet, then create one */
547         if (p->binding == NULL) {
548                 NTSTATUS status;
549                 const char *r = smbXcli_conn_remote_name(conn);
550                 char *str;
551                 SMB_ASSERT(r != NULL);
552                 str = talloc_asprintf(p, "ncacn_np:%s", r);
553                 if (str == NULL) {
554                         return NT_STATUS_NO_MEMORY;
555                 }
556                 status = dcerpc_parse_binding(p, str,
557                                               &p->binding);
558                 talloc_free(str);
559                 if (!NT_STATUS_IS_OK(status)) {
560                         return status;
561                 }
562         }
563
564         ctx = dcerpc_pipe_open_smb_send(p->conn,
565                                         conn, session,
566                                         tcon, timeout_msec,
567                                         pipe_name);
568         if (ctx == NULL) {
569                 return NT_STATUS_NO_MEMORY;
570         }
571
572         return dcerpc_pipe_open_smb_recv(ctx);
573 }
574
575 struct composite_context *dcerpc_secondary_smb_send(struct dcecli_connection *c1,
576                                                     struct dcecli_connection *c2,
577                                                     const char *pipe_name)
578 {
579         struct smb_private *smb;
580
581         if (c1->transport.transport != NCACN_NP) return NULL;
582
583         smb = talloc_get_type(c1->transport.private_data, struct smb_private);
584         if (!smb) return NULL;
585
586         return dcerpc_pipe_open_smb_send(c2,
587                                          smb->conn,
588                                          smb->session,
589                                          smb->tcon,
590                                          smb->timeout_msec,
591                                          pipe_name);
592 }
593
594 NTSTATUS dcerpc_secondary_smb_recv(struct composite_context *c)
595 {
596         return dcerpc_pipe_open_smb_recv(c);
597 }