Coverage Report

Created: 2026-07-25 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/librpc/rpc/dcerpc_sock.c
Line
Count
Source
1
/* 
2
   Unix SMB/CIFS implementation.
3
4
   dcerpc over standard sockets transport
5
6
   Copyright (C) Andrew Tridgell 2003
7
   Copyright (C) Jelmer Vernooij 2004
8
   Copyright (C) Rafal Szczesniak 2006
9
   
10
   This program is free software; you can redistribute it and/or modify
11
   it under the terms of the GNU General Public License as published by
12
   the Free Software Foundation; either version 3 of the License, or
13
   (at your option) any later version.
14
   
15
   This program is distributed in the hope that it will be useful,
16
   but WITHOUT ANY WARRANTY; without even the implied warranty of
17
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
   GNU General Public License for more details.
19
   
20
   You should have received a copy of the GNU General Public License
21
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
*/
23
24
#define SOURCE4_LIBRPC_INTERNALS 1
25
26
#include "includes.h"
27
#include "system/filesys.h"
28
#include "lib/events/events.h"
29
#include "lib/socket/socket.h"
30
#include "lib/tsocket/tsocket.h"
31
#include "libcli/composite/composite.h"
32
#include "librpc/rpc/dcerpc.h"
33
#include "librpc/rpc/dcerpc_proto.h"
34
#include "libcli/resolve/resolve.h"
35
#include "librpc/rpc/rpc_common.h"
36
37
struct pipe_open_socket_state {
38
  struct dcecli_connection *conn;
39
  struct socket_context *socket_ctx;
40
  struct socket_address *localaddr;
41
  struct socket_address *server;
42
  const char *target_hostname;
43
  enum dcerpc_transport_t transport;
44
  struct socket_address *client;
45
};
46
47
48
static void continue_socket_connect(struct composite_context *ctx)
49
0
{
50
0
  struct dcecli_connection *conn;
51
0
  struct composite_context *c = talloc_get_type_abort(
52
0
    ctx->async.private_data, struct composite_context);
53
0
  struct pipe_open_socket_state *s = talloc_get_type_abort(
54
0
    c->private_data, struct pipe_open_socket_state);
55
0
  int rc;
56
0
  int sock_fd;
57
58
  /* make it easier to write a function calls */
59
0
  conn = s->conn;
60
61
0
  c->status = socket_connect_recv(ctx);
62
0
  if (!NT_STATUS_IS_OK(c->status)) {
63
0
    DBG_NOTICE("Failed to connect host %s on port %d - %s\n",
64
0
        s->server->addr, s->server->port,
65
0
        nt_errstr(c->status));
66
0
    composite_error(c, c->status);
67
0
    return;
68
0
  }
69
70
0
  s->client = socket_get_my_addr(s->socket_ctx, s);
71
0
  if (s->client == NULL) {
72
0
    TALLOC_FREE(s->socket_ctx);
73
0
    composite_error(c, NT_STATUS_NO_MEMORY);
74
0
    return;
75
0
  }
76
0
  sock_fd = socket_get_fd(s->socket_ctx);
77
0
  if (sock_fd == -1) {
78
0
    TALLOC_FREE(s->socket_ctx);
79
0
    composite_error(c, NT_STATUS_INVALID_HANDLE);
80
0
    return;
81
0
  }
82
0
  socket_set_flags(s->socket_ctx, SOCKET_FLAG_NOCLOSE);
83
0
  TALLOC_FREE(s->socket_ctx);
84
85
  /*
86
    fill in the transport methods
87
  */
88
0
  conn->transport.transport       = s->transport;
89
0
  conn->transport.private_data    = NULL;
90
91
  /*
92
   * Windows uses 5840 for ncacn_ip_tcp,
93
   * so we also use it (for every transport which uses bsd sockets)
94
   */
95
0
  conn->srv_max_xmit_frag = 5840;
96
0
  conn->srv_max_recv_frag = 5840;
97
98
0
  conn->transport.pending_reads = 0;
99
0
  conn->server_name   = strupper_talloc(conn, s->target_hostname);
100
101
0
  rc = tstream_bsd_existing_socket(conn, sock_fd,
102
0
           &conn->transport.stream);
103
0
  if (rc < 0) {
104
0
    close(sock_fd);
105
0
    composite_error(c, NT_STATUS_NO_MEMORY);
106
0
    return;
107
0
  }
108
109
0
  conn->transport.write_queue =
110
0
    tevent_queue_create(conn, "dcerpc sock write queue");
111
0
  if (conn->transport.write_queue == NULL) {
112
0
    TALLOC_FREE(conn->transport.stream);
113
0
    composite_error(c, NT_STATUS_NO_MEMORY);
114
0
    return;
115
0
  }
116
117
  /* ensure we don't get SIGPIPE */
118
0
  BlockSignals(true, SIGPIPE);
119
120
0
  composite_done(c);
121
0
}
122
123
124
static struct composite_context *dcerpc_pipe_open_socket_send(TALLOC_CTX *mem_ctx,
125
                   struct dcecli_connection *cn,
126
                   struct socket_address *localaddr,
127
                   struct socket_address *server,
128
                   const char *target_hostname,
129
                   const char *full_path,
130
                   enum dcerpc_transport_t transport)
131
0
{
132
0
  struct composite_context *c;
133
0
  struct pipe_open_socket_state *s;
134
0
  struct composite_context *conn_req;
135
136
0
  c = composite_create(mem_ctx, cn->event_ctx);
137
0
  if (c == NULL) return NULL;
138
139
0
  s = talloc_zero(c, struct pipe_open_socket_state);
140
0
  if (composite_nomem(s, c)) return c;
141
0
  c->private_data = s;
142
143
0
  s->conn      = cn;
144
0
  s->transport = transport;
145
0
  if (localaddr) {
146
0
    s->localaddr = socket_address_copy(s, localaddr);
147
0
    if (composite_nomem(s->localaddr, c)) return c;
148
0
  }
149
0
  s->server = socket_address_copy(s, server);
150
0
  if (composite_nomem(s->server, c)) return c;
151
0
  if (target_hostname) {
152
0
    s->target_hostname = talloc_strdup(s, target_hostname);
153
0
    if (composite_nomem(s->target_hostname, c)) return c;
154
0
  }
155
156
0
  c->status = socket_create(s, server->family, SOCKET_TYPE_STREAM,
157
0
          &s->socket_ctx, 0);
158
0
  if (!composite_is_ok(c)) return c;
159
160
0
  conn_req = socket_connect_send(s->socket_ctx, s->localaddr, s->server, 0,
161
0
               c->event_ctx);
162
0
  composite_continue(c, conn_req, continue_socket_connect, c);
163
0
  return c;
164
0
}
165
166
static NTSTATUS dcerpc_pipe_open_socket_recv(struct composite_context *c,
167
               TALLOC_CTX *mem_ctx,
168
               struct socket_address **localaddr)
169
0
{
170
0
  NTSTATUS status = composite_wait(c);
171
172
0
  if (NT_STATUS_IS_OK(status)) {
173
0
    struct pipe_open_socket_state *s =
174
0
      talloc_get_type_abort(c->private_data,
175
0
      struct pipe_open_socket_state);
176
177
0
    if (localaddr != NULL) {
178
0
      *localaddr = talloc_move(mem_ctx, &s->client);
179
0
    }
180
0
  }
181
182
0
  talloc_free(c);
183
0
  return status;
184
0
}
185
186
struct pipe_tcp_state {
187
  const char *server;
188
  const char *target_hostname;
189
  const char **addresses;
190
  uint32_t index;
191
  uint32_t port;
192
  struct socket_address *localaddr;
193
  struct socket_address *srvaddr;
194
  struct resolve_context *resolve_ctx;
195
  struct dcecli_connection *conn;
196
  struct nbt_name name;
197
  char *local_address;
198
  char *remote_address;
199
};
200
201
202
static void continue_ip_open_socket(struct composite_context *ctx);
203
static void continue_ip_resolve_name(struct composite_context *ctx);
204
205
static void continue_ip_resolve_name(struct composite_context *ctx)
206
0
{
207
0
  struct composite_context *c = talloc_get_type_abort(
208
0
    ctx->async.private_data, struct composite_context);
209
0
  struct pipe_tcp_state *s = talloc_get_type_abort(
210
0
    c->private_data, struct pipe_tcp_state);
211
0
  struct composite_context *sock_ip_req;
212
213
0
  c->status = resolve_name_multiple_recv(ctx, s, &s->addresses);
214
0
  if (!composite_is_ok(c)) return;
215
216
  /* prepare server address using host ip:port and transport name */
217
0
  s->srvaddr = socket_address_from_strings(s->conn, "ip", s->addresses[s->index], s->port);
218
0
  s->index++;
219
0
  if (composite_nomem(s->srvaddr, c)) return;
220
221
0
  sock_ip_req = dcerpc_pipe_open_socket_send(c, s->conn, s->localaddr,
222
0
                 s->srvaddr, s->target_hostname,
223
0
                 NULL,
224
0
                 NCACN_IP_TCP);
225
0
  composite_continue(c, sock_ip_req, continue_ip_open_socket, c);
226
0
}
227
228
229
/*
230
  Stage 2 of dcerpc_pipe_open_tcp_send: receive result of pipe open request
231
  on IP transport.
232
*/
233
static void continue_ip_open_socket(struct composite_context *ctx)
234
0
{
235
0
  struct composite_context *c = talloc_get_type_abort(
236
0
    ctx->async.private_data, struct composite_context);
237
0
  struct pipe_tcp_state *s = talloc_get_type_abort(
238
0
    c->private_data, struct pipe_tcp_state);
239
0
  struct socket_address *localaddr = NULL;
240
241
  /* receive result socket open request */
242
0
  c->status = dcerpc_pipe_open_socket_recv(ctx, s, &localaddr);
243
0
  if (!NT_STATUS_IS_OK(c->status)) {
244
    /* something went wrong... */
245
0
    DBG_NOTICE("Failed to connect host %s (%s) on port %d - %s.\n",
246
0
        s->addresses[s->index - 1], s->target_hostname,
247
0
        s->port, nt_errstr(c->status));
248
0
    if (s->addresses[s->index]) {
249
0
      struct composite_context *sock_ip_req;
250
0
      talloc_free(s->srvaddr);
251
      /* prepare server address using host ip:port and transport name */
252
0
      s->srvaddr = socket_address_from_strings(s->conn, "ip", s->addresses[s->index], s->port);
253
0
      s->index++;
254
0
      if (composite_nomem(s->srvaddr, c)) return;
255
256
0
      sock_ip_req = dcerpc_pipe_open_socket_send(c, s->conn, s->localaddr,
257
0
                s->srvaddr, s->target_hostname,
258
0
                NULL,
259
0
                NCACN_IP_TCP);
260
0
      composite_continue(c, sock_ip_req, continue_ip_open_socket, c);
261
262
0
      return;
263
0
    } else {
264
0
      composite_error(c, c->status);
265
0
      return;
266
0
    }
267
0
  }
268
269
0
  s->local_address = talloc_strdup(s, localaddr->addr);
270
0
  if (composite_nomem(s->local_address, c)) return;
271
0
  s->remote_address = talloc_strdup(s, s->addresses[s->index - 1]);
272
0
  if (composite_nomem(s->remote_address, c)) return;
273
274
0
  composite_done(c);
275
0
}
276
277
/*
278
  Send rpc pipe open request to given host:port using
279
  tcp/ip transport
280
*/
281
struct composite_context* dcerpc_pipe_open_tcp_send(struct dcecli_connection *conn,
282
                const char *localaddr,
283
                const char *server,
284
                const char *target_hostname,
285
                uint32_t port,
286
                struct resolve_context *resolve_ctx)
287
0
{
288
0
  struct composite_context *c;
289
0
  struct pipe_tcp_state *s;
290
0
  struct composite_context *resolve_req;
291
292
  /* composite context allocation and setup */
293
0
  c = composite_create(conn, conn->event_ctx);
294
0
  if (c == NULL) return NULL;
295
296
0
  s = talloc_zero(c, struct pipe_tcp_state);
297
0
  if (composite_nomem(s, c)) return c;
298
0
  c->private_data = s;
299
300
  /* store input parameters in state structure */
301
0
  s->server          = talloc_strdup(c, server);
302
0
  if (composite_nomem(s->server, c)) return c;
303
0
  if (target_hostname) {
304
0
    s->target_hostname = talloc_strdup(c, target_hostname);
305
0
    if (composite_nomem(s->target_hostname, c)) return c;
306
0
  }
307
0
  s->port            = port;
308
0
  s->conn            = conn;
309
0
  s->resolve_ctx     = resolve_ctx;
310
0
  if (localaddr) {
311
0
    s->localaddr = socket_address_from_strings(s, "ip", localaddr, 0);
312
    /* if there is no localaddr, we pass NULL for
313
       s->localaddr, which is handled by the socket libraries as
314
       meaning no local binding address specified */
315
0
  }
316
317
0
  make_nbt_name_server(&s->name, s->server);
318
0
  resolve_req = resolve_name_send(resolve_ctx, s, &s->name, c->event_ctx);
319
0
  composite_continue(c, resolve_req, continue_ip_resolve_name, c);
320
0
  return c;
321
0
}
322
323
/*
324
  Receive result of pipe open request on tcp/ip
325
*/
326
NTSTATUS dcerpc_pipe_open_tcp_recv(struct composite_context *c,
327
           TALLOC_CTX *mem_ctx,
328
           char **localaddr,
329
           char **remoteaddr)
330
0
{
331
0
  NTSTATUS status;
332
0
  status = composite_wait(c);
333
334
0
  if (NT_STATUS_IS_OK(status)) {
335
0
    struct pipe_tcp_state *s = talloc_get_type_abort(
336
0
      c->private_data, struct pipe_tcp_state);
337
338
0
    if (localaddr != NULL) {
339
0
      *localaddr = talloc_move(mem_ctx, &s->local_address);
340
0
    }
341
0
    if (remoteaddr != NULL) {
342
0
      *remoteaddr = talloc_move(mem_ctx, &s->remote_address);
343
0
    }
344
0
  }
345
346
0
  talloc_free(c);
347
0
  return status;
348
0
}
349
350
351
struct pipe_unix_state {
352
  const char *path;
353
  struct socket_address *srvaddr;
354
  struct dcecli_connection *conn;
355
};
356
357
358
/*
359
  Stage 2 of dcerpc_pipe_open_unix_stream_send: receive result of pipe open
360
  request on unix socket.
361
*/
362
static void continue_unix_open_socket(struct composite_context *ctx)
363
0
{
364
0
  struct composite_context *c = talloc_get_type_abort(
365
0
    ctx->async.private_data, struct composite_context);
366
367
0
  c->status = dcerpc_pipe_open_socket_recv(ctx, NULL, NULL);
368
0
  if (NT_STATUS_IS_OK(c->status)) {
369
0
    composite_done(c);
370
0
    return;
371
0
  }
372
373
0
  composite_error(c, c->status);
374
0
}
375
376
377
/*
378
  Send pipe open request on unix socket
379
*/
380
struct composite_context *dcerpc_pipe_open_unix_stream_send(struct dcecli_connection *conn,
381
                  const char *path)
382
0
{
383
0
  struct composite_context *c;
384
0
  struct composite_context *sock_unix_req;
385
0
  struct pipe_unix_state *s;
386
387
  /* composite context allocation and setup */
388
0
  c = composite_create(conn, conn->event_ctx);
389
0
  if (c == NULL) return NULL;
390
391
0
  s = talloc_zero(c, struct pipe_unix_state);
392
0
  if (composite_nomem(s, c)) return c;
393
0
  c->private_data = s;
394
395
  /* store parameters in state structure */
396
0
  s->path = talloc_strdup(c, path);
397
0
  if (composite_nomem(s->path, c)) return c;
398
0
  s->conn = conn;
399
400
  /* prepare server address using socket path and transport name */
401
0
  s->srvaddr = socket_address_from_strings(conn, "unix", s->path, 0);
402
0
  if (composite_nomem(s->srvaddr, c)) return c;
403
404
  /* send socket open request */
405
0
  sock_unix_req = dcerpc_pipe_open_socket_send(c, s->conn, NULL,
406
0
                 s->srvaddr, NULL,
407
0
                 s->path,
408
0
                 NCALRPC);
409
0
  composite_continue(c, sock_unix_req, continue_unix_open_socket, c);
410
0
  return c;
411
0
}
412
413
414
/*
415
  Receive result of pipe open request on unix socket
416
*/
417
NTSTATUS dcerpc_pipe_open_unix_stream_recv(struct composite_context *c)
418
0
{
419
0
  NTSTATUS status = composite_wait(c);
420
421
0
  talloc_free(c);
422
0
  return status;
423
0
}
424
425
426
/*
427
  Stage 2 of dcerpc_pipe_open_pipe_send: receive socket open request
428
*/
429
static void continue_np_open_socket(struct composite_context *ctx)
430
0
{
431
0
  struct composite_context *c = talloc_get_type_abort(
432
0
    ctx->async.private_data, struct composite_context);
433
434
0
  c->status = dcerpc_pipe_open_socket_recv(ctx, NULL, NULL);
435
0
  if (!composite_is_ok(c)) return;
436
437
0
  composite_done(c);
438
0
}
439
440
441
/*
442
  Send pipe open request on ncalrpc
443
*/
444
struct composite_context* dcerpc_pipe_open_pipe_send(struct dcecli_connection *conn,
445
                 const char *ncalrpc_dir,
446
                 const char *identifier)
447
0
{
448
0
  char *canon = NULL;
449
450
0
  struct composite_context *c;
451
0
  struct composite_context *sock_np_req;
452
0
  struct pipe_unix_state *s;
453
454
  /* composite context allocation and setup */
455
0
  c = composite_create(conn, conn->event_ctx);
456
0
  if (c == NULL) return NULL;
457
458
0
  s = talloc_zero(c, struct pipe_unix_state);
459
0
  if (composite_nomem(s, c)) return c;
460
0
  c->private_data = s;
461
462
  /* store parameters in state structure */
463
0
  canon = talloc_strdup(s, identifier);
464
0
  if (composite_nomem(canon, c)) return c;
465
0
  s->conn = conn;
466
467
0
  string_replace(canon, '/', '\\');
468
0
  s->path = talloc_asprintf(canon, "%s/%s", ncalrpc_dir, canon);
469
0
  if (composite_nomem(s->path, c)) return c;
470
471
  /* prepare server address using path and transport name */
472
0
  s->srvaddr = socket_address_from_strings(conn, "unix", s->path, 0);
473
0
  if (composite_nomem(s->srvaddr, c)) return c;
474
475
  /* send socket open request */
476
0
  sock_np_req = dcerpc_pipe_open_socket_send(c, s->conn, NULL, s->srvaddr, NULL, s->path, NCALRPC);
477
0
  composite_continue(c, sock_np_req, continue_np_open_socket, c);
478
0
  return c;
479
0
}
480
481
482
/*
483
  Receive result of pipe open request on ncalrpc
484
*/
485
NTSTATUS dcerpc_pipe_open_pipe_recv(struct composite_context *c)
486
0
{
487
0
  NTSTATUS status = composite_wait(c);
488
  
489
0
  talloc_free(c);
490
0
  return status;
491
0
}
492
493
494
/*
495
  Open a rpc pipe on a named pipe - sync version
496
*/
497
NTSTATUS dcerpc_pipe_open_pipe(struct dcecli_connection *conn, const char *ncalrpc_dir, const char *identifier)
498
0
{
499
0
  struct composite_context *c = dcerpc_pipe_open_pipe_send(conn, ncalrpc_dir, identifier);
500
0
  return dcerpc_pipe_open_pipe_recv(c);
501
0
}