Coverage Report

Created: 2026-06-07 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/librpc/rpc/dcerpc_auth.c
Line
Count
Source
1
/* 
2
   Unix SMB/CIFS implementation.
3
4
   Generic Authentication Interface
5
6
   Copyright (C) Andrew Tridgell 2003
7
   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8
   Copyright (C) Stefan Metzmacher 2004
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 <tevent.h>
28
#include "libcli/composite/composite.h"
29
#include "auth/gensec/gensec.h"
30
#include "librpc/rpc/dcerpc.h"
31
#include "librpc/rpc/dcerpc_proto.h"
32
#include "param/param.h"
33
34
/*
35
  return the rpc syntax and transfer syntax given the pipe uuid and version
36
*/
37
static NTSTATUS dcerpc_init_syntaxes(struct dcerpc_pipe *p,
38
             const struct ndr_interface_table *table,
39
             struct ndr_syntax_id *syntax,
40
             struct ndr_syntax_id *transfer_syntax)
41
0
{
42
0
  struct GUID *object = NULL;
43
44
0
  p->object = dcerpc_binding_get_object(p->binding);
45
0
  if (!GUID_all_zero(&p->object)) {
46
0
    object = &p->object;
47
0
  }
48
49
0
  p->binding_handle = dcerpc_pipe_binding_handle(p, object, table);
50
0
  if (p->binding_handle == NULL) {
51
0
    return NT_STATUS_NO_MEMORY;
52
0
  }
53
54
0
  syntax->uuid = table->syntax_id.uuid;
55
0
  syntax->if_version = table->syntax_id.if_version;
56
57
0
  if (p->conn->flags & DCERPC_NDR64) {
58
0
    *transfer_syntax = ndr_transfer_syntax_ndr64;
59
0
  } else {
60
0
    *transfer_syntax = ndr_transfer_syntax_ndr;
61
0
  }
62
63
0
  return NT_STATUS_OK;
64
0
}
65
66
67
/*
68
  Send request to do a non-authenticated dcerpc bind
69
*/
70
static void dcerpc_bind_auth_none_done(struct tevent_req *subreq);
71
72
struct composite_context *dcerpc_bind_auth_none_send(TALLOC_CTX *mem_ctx,
73
                 struct dcerpc_pipe *p,
74
                 const struct ndr_interface_table *table)
75
0
{
76
0
  struct ndr_syntax_id syntax;
77
0
  struct ndr_syntax_id transfer_syntax;
78
79
0
  struct composite_context *c;
80
0
  struct tevent_req *subreq;
81
82
0
  c = composite_create(mem_ctx, p->conn->event_ctx);
83
0
  if (c == NULL) return NULL;
84
85
0
  c->status = dcerpc_init_syntaxes(p, table,
86
0
           &syntax, &transfer_syntax);
87
0
  if (!NT_STATUS_IS_OK(c->status)) {
88
0
    DEBUG(2,("Invalid uuid string in "
89
0
       "dcerpc_bind_auth_none_send\n"));
90
0
    composite_error(c, c->status);
91
0
    return c;
92
0
  }
93
94
0
  subreq = dcerpc_bind_send(mem_ctx, p->conn->event_ctx, p,
95
0
          &syntax, &transfer_syntax);
96
0
  if (composite_nomem(subreq, c)) return c;
97
0
  tevent_req_set_callback(subreq, dcerpc_bind_auth_none_done, c);
98
99
0
  return c;
100
0
}
101
102
static void dcerpc_bind_auth_none_done(struct tevent_req *subreq)
103
0
{
104
0
  struct composite_context *ctx =
105
0
    tevent_req_callback_data(subreq,
106
0
    struct composite_context);
107
108
0
  ctx->status = dcerpc_bind_recv(subreq);
109
0
  TALLOC_FREE(subreq);
110
0
  if (!composite_is_ok(ctx)) return;
111
112
0
  composite_done(ctx);
113
0
}
114
115
/*
116
  Receive result of a non-authenticated dcerpc bind
117
*/
118
NTSTATUS dcerpc_bind_auth_none_recv(struct composite_context *ctx)
119
0
{
120
0
  NTSTATUS result = composite_wait(ctx);
121
0
  TALLOC_FREE(ctx);
122
0
  return result;
123
0
}
124
125
126
/*
127
  Perform sync non-authenticated dcerpc bind
128
*/
129
_PUBLIC_ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
130
             const struct ndr_interface_table *table)
131
0
{
132
0
  struct composite_context *ctx;
133
134
0
  ctx = dcerpc_bind_auth_none_send(p, p, table);
135
0
  return dcerpc_bind_auth_none_recv(ctx);
136
0
}
137
138
139
struct bind_auth_state {
140
  struct dcerpc_pipe *pipe;
141
  struct ndr_syntax_id syntax;
142
  struct ndr_syntax_id transfer_syntax;
143
  struct dcerpc_auth out_auth_info;
144
  struct dcerpc_auth in_auth_info;
145
  bool more_processing; /* Is there anything more to do after the
146
         * first bind itself received? */
147
};
148
149
static void bind_auth_next_gensec_done(struct tevent_req *subreq);
150
static void bind_auth_recv_alter(struct tevent_req *subreq);
151
152
static void bind_auth_next_step(struct composite_context *c)
153
0
{
154
0
  struct bind_auth_state *state;
155
0
  struct dcecli_security *sec;
156
0
  struct tevent_req *subreq;
157
158
0
  state = talloc_get_type(c->private_data, struct bind_auth_state);
159
0
  sec = &state->pipe->conn->security_state;
160
161
0
  if (state->in_auth_info.auth_type != sec->auth_type) {
162
0
    composite_error(c, NT_STATUS_RPC_PROTOCOL_ERROR);
163
0
    return;
164
0
  }
165
166
0
  if (state->in_auth_info.auth_level != sec->auth_level) {
167
0
    composite_error(c, NT_STATUS_RPC_PROTOCOL_ERROR);
168
0
    return;
169
0
  }
170
171
0
  if (state->in_auth_info.auth_context_id != sec->auth_context_id) {
172
0
    composite_error(c, NT_STATUS_RPC_PROTOCOL_ERROR);
173
0
    return;
174
0
  }
175
176
0
  state->out_auth_info = (struct dcerpc_auth) {
177
0
    .auth_type = sec->auth_type,
178
0
    .auth_level = sec->auth_level,
179
0
    .auth_context_id = sec->auth_context_id,
180
0
  };
181
182
  /* The status value here, from GENSEC is vital to the security
183
   * of the system.  Even if the other end accepts, if GENSEC
184
   * claims 'MORE_PROCESSING_REQUIRED' then you must keep
185
   * feeding it blobs, or else the remote host/attacker might
186
   * avoid mutual authentication requirements.
187
   *
188
   * Likewise, you must not feed GENSEC too much (after the OK),
189
   * it doesn't like that either
190
   */
191
192
0
  state->pipe->inhibit_timeout_processing = true;
193
0
  state->pipe->timed_out = false;
194
195
0
  subreq = gensec_update_send(state,
196
0
            state->pipe->conn->event_ctx,
197
0
            sec->generic_state,
198
0
            state->in_auth_info.credentials);
199
0
  if (composite_nomem(subreq, c)) return;
200
0
  tevent_req_set_callback(subreq, bind_auth_next_gensec_done, c);
201
0
}
202
203
static void bind_auth_next_gensec_done(struct tevent_req *subreq)
204
0
{
205
0
  struct composite_context *c =
206
0
    tevent_req_callback_data(subreq,
207
0
    struct composite_context);
208
0
  struct bind_auth_state *state =
209
0
    talloc_get_type_abort(c->private_data,
210
0
    struct bind_auth_state);
211
0
  struct dcerpc_pipe *p = state->pipe;
212
0
  struct dcecli_security *sec = &p->conn->security_state;
213
0
  bool more_processing = false;
214
215
0
  state->pipe->inhibit_timeout_processing = false;
216
217
0
  c->status = gensec_update_recv(subreq, state,
218
0
               &state->out_auth_info.credentials);
219
0
  TALLOC_FREE(subreq);
220
221
0
  if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
222
0
    more_processing = true;
223
0
    c->status = NT_STATUS_OK;
224
0
  }
225
226
0
  if (!composite_is_ok(c)) return;
227
228
0
  if (!more_processing) {
229
0
    if (state->pipe->conn->flags & DCERPC_HEADER_SIGNING) {
230
0
      gensec_want_feature(sec->generic_state,
231
0
          GENSEC_FEATURE_SIGN_PKT_HEADER);
232
0
    }
233
0
  }
234
235
0
  if (state->out_auth_info.credentials.length == 0) {
236
0
    composite_done(c);
237
0
    return;
238
0
  }
239
240
0
  state->in_auth_info = (struct dcerpc_auth) {
241
0
    .auth_type = DCERPC_AUTH_TYPE_NONE,
242
0
  };
243
0
  sec->tmp_auth_info.in = &state->in_auth_info;
244
0
  sec->tmp_auth_info.mem = state;
245
0
  sec->tmp_auth_info.out = &state->out_auth_info;
246
247
0
  if (!more_processing) {
248
    /* NO reply expected, so just send it */
249
0
    c->status = dcerpc_auth3(state->pipe, state);
250
0
    if (!composite_is_ok(c)) return;
251
252
0
    composite_done(c);
253
0
    return;
254
0
  }
255
256
  /* We are demanding a reply, so use a request that will get us one */
257
258
0
  subreq = dcerpc_alter_context_send(state, state->pipe->conn->event_ctx,
259
0
             state->pipe,
260
0
             &state->pipe->syntax,
261
0
             &state->pipe->transfer_syntax);
262
0
  if (composite_nomem(subreq, c)) return;
263
0
  tevent_req_set_callback(subreq, bind_auth_recv_alter, c);
264
0
}
265
266
267
static void bind_auth_recv_alter(struct tevent_req *subreq)
268
0
{
269
0
  struct composite_context *c =
270
0
    tevent_req_callback_data(subreq,
271
0
    struct composite_context);
272
0
  struct bind_auth_state *state = talloc_get_type(c->private_data,
273
0
              struct bind_auth_state);
274
0
  struct dcecli_security *sec = &state->pipe->conn->security_state;
275
276
0
  ZERO_STRUCT(sec->tmp_auth_info);
277
278
0
  c->status = dcerpc_alter_context_recv(subreq);
279
0
  TALLOC_FREE(subreq);
280
0
  if (!composite_is_ok(c)) return;
281
282
0
  bind_auth_next_step(c);
283
0
}
284
285
286
static void bind_auth_recv_bindreply(struct tevent_req *subreq)
287
0
{
288
0
  struct composite_context *c =
289
0
    tevent_req_callback_data(subreq,
290
0
    struct composite_context);
291
0
  struct bind_auth_state *state = talloc_get_type(c->private_data,
292
0
              struct bind_auth_state);
293
0
  struct dcecli_security *sec = &state->pipe->conn->security_state;
294
295
0
  ZERO_STRUCT(sec->tmp_auth_info);
296
297
0
  c->status = dcerpc_bind_recv(subreq);
298
0
  TALLOC_FREE(subreq);
299
0
  if (!composite_is_ok(c)) return;
300
301
0
  if (!state->more_processing) {
302
    /* The first gensec_update has not requested a second run, so
303
     * we're done here. */
304
0
    composite_done(c);
305
0
    return;
306
0
  }
307
308
0
  bind_auth_next_step(c);
309
0
}
310
311
312
static void dcerpc_bind_auth_gensec_done(struct tevent_req *subreq);
313
314
/**
315
   Bind to a DCE/RPC pipe, send async request
316
   @param mem_ctx TALLOC_CTX for the allocation of the composite_context
317
   @param p The dcerpc_pipe to bind (must already be connected)
318
   @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
319
   @param credentials The credentials of the account to connect with 
320
   @param auth_type Select the authentication scheme to use
321
   @param auth_level Chooses between unprotected (connect), signed or sealed
322
   @param service The service (used by Kerberos to select the service principal to contact)
323
   @retval A composite context describing the partial state of the bind
324
*/
325
326
struct composite_context *dcerpc_bind_auth_send(TALLOC_CTX *mem_ctx,
327
            struct dcerpc_pipe *p,
328
            const struct ndr_interface_table *table,
329
            struct cli_credentials *credentials,
330
            struct gensec_settings *gensec_settings,
331
            uint8_t auth_type, uint8_t auth_level,
332
            const char *service)
333
0
{
334
0
  struct composite_context *c;
335
0
  struct bind_auth_state *state;
336
0
  struct dcecli_security *sec;
337
0
  struct tevent_req *subreq;
338
0
  const char *target_principal = NULL;
339
340
  /* composite context allocation and setup */
341
0
  c = composite_create(mem_ctx, p->conn->event_ctx);
342
0
  if (c == NULL) return NULL;
343
344
0
  state = talloc(c, struct bind_auth_state);
345
0
  if (composite_nomem(state, c)) return c;
346
0
  c->private_data = state;
347
348
0
  state->pipe = p;
349
350
0
  c->status = dcerpc_init_syntaxes(p, table,
351
0
           &state->syntax,
352
0
           &state->transfer_syntax);
353
0
  if (!composite_is_ok(c)) return c;
354
355
0
  sec = &p->conn->security_state;
356
357
0
  c->status = gensec_client_start(p, &sec->generic_state,
358
0
          gensec_settings);
359
0
  if (!NT_STATUS_IS_OK(c->status)) {
360
0
    DEBUG(1, ("Failed to start GENSEC client mode: %s\n",
361
0
        nt_errstr(c->status)));
362
0
    composite_error(c, c->status);
363
0
    return c;
364
0
  }
365
366
0
  c->status = gensec_set_credentials(sec->generic_state, credentials);
367
0
  if (!NT_STATUS_IS_OK(c->status)) {
368
0
    DEBUG(1, ("Failed to set GENSEC client credentials: %s\n",
369
0
        nt_errstr(c->status)));
370
0
    composite_error(c, c->status);
371
0
    return c;
372
0
  }
373
374
0
  c->status = gensec_set_target_hostname(sec->generic_state,
375
0
                 dcerpc_server_name(p));
376
0
  if (!NT_STATUS_IS_OK(c->status)) {
377
0
    DEBUG(1, ("Failed to set GENSEC target hostname: %s\n", 
378
0
        nt_errstr(c->status)));
379
0
    composite_error(c, c->status);
380
0
    return c;
381
0
  }
382
383
0
  if (p->conn->flags & DCERPC_SCHANNEL) {
384
0
    service = "netlogon";
385
0
    gensec_want_feature(sec->generic_state,
386
0
            GENSEC_FEATURE_NO_DELEGATION);
387
0
  }
388
389
0
  if (service != NULL) {
390
0
    c->status = gensec_set_target_service(sec->generic_state,
391
0
                  service);
392
0
    if (!NT_STATUS_IS_OK(c->status)) {
393
0
      DEBUG(1, ("Failed to set GENSEC target service: %s\n",
394
0
          nt_errstr(c->status)));
395
0
      composite_error(c, c->status);
396
0
      return c;
397
0
    }
398
0
  }
399
400
0
  if (p->binding != NULL) {
401
0
    target_principal = dcerpc_binding_get_string_option(p->binding,
402
0
              "target_principal");
403
0
  }
404
0
  if (target_principal != NULL) {
405
0
    c->status = gensec_set_target_principal(sec->generic_state,
406
0
              target_principal);
407
0
    if (!NT_STATUS_IS_OK(c->status)) {
408
0
      DEBUG(1, ("Failed to set GENSEC target principal to %s: %s\n",
409
0
          target_principal, nt_errstr(c->status)));
410
0
      composite_error(c, c->status);
411
0
      return c;
412
0
    }
413
0
  }
414
415
0
  c->status = gensec_start_mech_by_authtype(sec->generic_state,
416
0
              auth_type, auth_level);
417
0
  if (!NT_STATUS_IS_OK(c->status)) {
418
0
    DEBUG(1, ("Failed to start GENSEC client mechanism %s: %s\n",
419
0
        gensec_get_name_by_authtype(sec->generic_state, auth_type),
420
0
        nt_errstr(c->status)));
421
0
    composite_error(c, c->status);
422
0
    return c;
423
0
  }
424
425
0
  sec->auth_type = auth_type;
426
0
  sec->auth_level = auth_level,
427
  /*
428
   * We use auth_context_id = 1 as some older
429
   * Samba versions (<= 4.2.3) use that value hardcoded
430
   * in a response.
431
   */
432
0
  sec->auth_context_id = 1;
433
434
0
  state->out_auth_info = (struct dcerpc_auth) {
435
0
    .auth_type = sec->auth_type,
436
0
    .auth_level = sec->auth_level,
437
0
    .auth_context_id = sec->auth_context_id,
438
0
  };
439
440
  /* The status value here, from GENSEC is vital to the security
441
   * of the system.  Even if the other end accepts, if GENSEC
442
   * claims 'MORE_PROCESSING_REQUIRED' then you must keep
443
   * feeding it blobs, or else the remote host/attacker might
444
   * avoid mutual authentication requirements.
445
   *
446
   * Likewise, you must not feed GENSEC too much (after the OK),
447
   * it doesn't like that either
448
   */
449
450
0
  state->pipe->inhibit_timeout_processing = true;
451
0
  state->pipe->timed_out = false;
452
453
0
  subreq = gensec_update_send(state,
454
0
            p->conn->event_ctx,
455
0
            sec->generic_state,
456
0
            data_blob_null);
457
0
  if (composite_nomem(subreq, c)) return c;
458
0
  tevent_req_set_callback(subreq, dcerpc_bind_auth_gensec_done, c);
459
460
0
  return c;
461
0
}
462
463
static void dcerpc_bind_auth_gensec_done(struct tevent_req *subreq)
464
0
{
465
0
  struct composite_context *c =
466
0
    tevent_req_callback_data(subreq,
467
0
    struct composite_context);
468
0
  struct bind_auth_state *state =
469
0
    talloc_get_type_abort(c->private_data,
470
0
    struct bind_auth_state);
471
0
  struct dcerpc_pipe *p = state->pipe;
472
0
  struct dcecli_security *sec = &p->conn->security_state;
473
474
0
  state->pipe->inhibit_timeout_processing = false;
475
476
0
  c->status = gensec_update_recv(subreq, state,
477
0
               &state->out_auth_info.credentials);
478
0
  TALLOC_FREE(subreq);
479
0
  if (!NT_STATUS_IS_OK(c->status) &&
480
0
      !NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
481
0
    composite_error(c, c->status);
482
0
    return;
483
0
  }
484
485
0
  state->more_processing = NT_STATUS_EQUAL(c->status,
486
0
             NT_STATUS_MORE_PROCESSING_REQUIRED);
487
488
0
  if (state->out_auth_info.credentials.length == 0) {
489
0
    composite_done(c);
490
0
    return;
491
0
  }
492
493
0
  if (gensec_have_feature(sec->generic_state, GENSEC_FEATURE_SIGN_PKT_HEADER)) {
494
0
    if (sec->auth_level >= DCERPC_AUTH_LEVEL_PACKET) {
495
0
      state->pipe->conn->flags |= DCERPC_PROPOSE_HEADER_SIGNING;
496
0
    }
497
0
  }
498
499
0
  state->in_auth_info = (struct dcerpc_auth) {
500
0
    .auth_type = DCERPC_AUTH_TYPE_NONE,
501
0
  };
502
0
  sec->tmp_auth_info.in = &state->in_auth_info;
503
0
  sec->tmp_auth_info.mem = state;
504
0
  sec->tmp_auth_info.out = &state->out_auth_info;
505
506
  /* The first request always is a dcerpc_bind. The subsequent ones
507
   * depend on gensec results */
508
0
  subreq = dcerpc_bind_send(state, p->conn->event_ctx, p,
509
0
          &state->syntax, &state->transfer_syntax);
510
0
  if (composite_nomem(subreq, c)) return;
511
0
  tevent_req_set_callback(subreq, bind_auth_recv_bindreply, c);
512
513
0
  return;
514
0
}
515
516
517
/**
518
   Bind to a DCE/RPC pipe, receive result
519
   @param creq A composite context describing state of async call
520
   @retval NTSTATUS code
521
*/
522
523
NTSTATUS dcerpc_bind_auth_recv(struct composite_context *creq)
524
0
{
525
0
  NTSTATUS result = composite_wait(creq);
526
0
  struct bind_auth_state *state = talloc_get_type(creq->private_data,
527
0
              struct bind_auth_state);
528
529
0
  if (NT_STATUS_IS_OK(result)) {
530
    /*
531
      after a successful authenticated bind the session
532
      key reverts to the generic session key
533
    */
534
0
    state->pipe->conn->security_state.session_key = dcecli_generic_session_key;
535
0
  }
536
  
537
0
  talloc_free(creq);
538
0
  return result;
539
0
}
540
541
542
/**
543
   Perform a GENSEC authenticated bind to a DCE/RPC pipe, sync
544
   @param p The dcerpc_pipe to bind (must already be connected)
545
   @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
546
   @param credentials The credentials of the account to connect with 
547
   @param auth_type Select the authentication scheme to use
548
   @param auth_level Chooses between unprotected (connect), signed or sealed
549
   @param service The service (used by Kerberos to select the service principal to contact)
550
   @retval NTSTATUS status code
551
*/
552
553
_PUBLIC_ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
554
        const struct ndr_interface_table *table,
555
        struct cli_credentials *credentials,
556
        struct gensec_settings *gensec_settings,
557
        uint8_t auth_type, uint8_t auth_level,
558
        const char *service)
559
0
{
560
0
  struct composite_context *creq;
561
0
  creq = dcerpc_bind_auth_send(p, p, table, credentials, gensec_settings,
562
0
             auth_type, auth_level, service);
563
0
  return dcerpc_bind_auth_recv(creq);
564
0
}