Coverage Report

Created: 2026-01-16 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/auth/gensec/gensec_tstream.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   tstream based generic authentication interface
5
6
   Copyright (c) 2010 Stefan Metzmacher
7
   Copyright (c) 2010 Andreas Schneider <asn@redhat.com>
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/network.h"
25
#include "auth/gensec/gensec.h"
26
#include "auth/gensec/gensec_proto.h"
27
#include "auth/gensec/gensec_tstream.h"
28
#include "lib/tsocket/tsocket.h"
29
#include "lib/tsocket/tsocket_internal.h"
30
#include "auth/gensec/gensec_toplevel_proto.h"
31
32
static const struct tstream_context_ops tstream_gensec_ops;
33
34
struct tstream_gensec {
35
  struct tstream_context *plain_stream;
36
37
  struct gensec_security *gensec_security;
38
39
  int error;
40
41
  struct {
42
    size_t max_unwrapped_size;
43
    size_t max_wrapped_size;
44
  } write;
45
46
  struct {
47
    off_t ofs;
48
    size_t left;
49
    DATA_BLOB unwrapped;
50
  } read;
51
};
52
53
_PUBLIC_ NTSTATUS _gensec_create_tstream(TALLOC_CTX *mem_ctx,
54
           struct gensec_security *gensec_security,
55
           struct tstream_context *plain_stream,
56
           struct tstream_context **_gensec_stream,
57
           const char *location)
58
0
{
59
0
  struct tstream_context *gensec_stream;
60
0
  struct tstream_gensec *tgss;
61
62
0
  gensec_stream = tstream_context_create(mem_ctx,
63
0
                 &tstream_gensec_ops,
64
0
                 &tgss,
65
0
                 struct tstream_gensec,
66
0
                 location);
67
0
  if (gensec_stream == NULL) {
68
0
    return NT_STATUS_NO_MEMORY;
69
0
  }
70
71
0
  tgss->plain_stream = plain_stream;
72
0
  tgss->gensec_security = gensec_security;
73
0
  tgss->error = 0;
74
75
0
  if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN) &&
76
0
      !gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
77
0
    talloc_free(gensec_stream);
78
0
    return NT_STATUS_INVALID_PARAMETER;
79
0
  }
80
81
0
  tgss->write.max_unwrapped_size = gensec_max_input_size(gensec_security);
82
0
  tgss->write.max_wrapped_size = gensec_max_wrapped_size(gensec_security);
83
84
0
  ZERO_STRUCT(tgss->read);
85
86
0
  *_gensec_stream = gensec_stream;
87
0
  return NT_STATUS_OK;
88
0
}
89
90
static ssize_t tstream_gensec_pending_bytes(struct tstream_context *stream)
91
0
{
92
0
  struct tstream_gensec *tgss =
93
0
    tstream_context_data(stream,
94
0
    struct tstream_gensec);
95
96
0
  if (tgss->error != 0) {
97
0
    errno = tgss->error;
98
0
    return -1;
99
0
  }
100
101
0
  return tgss->read.left;
102
0
}
103
104
struct tstream_gensec_readv_state {
105
  struct tevent_context *ev;
106
  struct tstream_context *stream;
107
108
  struct iovec *vector;
109
  int count;
110
111
  struct {
112
    bool asked_for_hdr;
113
    uint8_t hdr[4];
114
    bool asked_for_blob;
115
    DATA_BLOB blob;
116
  } wrapped;
117
118
  int ret;
119
};
120
121
static void tstream_gensec_readv_wrapped_next(struct tevent_req *req);
122
123
static struct tevent_req *tstream_gensec_readv_send(TALLOC_CTX *mem_ctx,
124
                struct tevent_context *ev,
125
                struct tstream_context *stream,
126
                struct iovec *vector,
127
                size_t count)
128
0
{
129
0
  struct tstream_gensec *tgss =
130
0
    tstream_context_data(stream,
131
0
    struct tstream_gensec);
132
0
  struct tevent_req *req;
133
0
  struct tstream_gensec_readv_state *state;
134
135
0
  req = tevent_req_create(mem_ctx, &state,
136
0
        struct tstream_gensec_readv_state);
137
0
  if (!req) {
138
0
    return NULL;
139
0
  }
140
141
0
  if (tgss->error != 0) {
142
0
    tevent_req_error(req, tgss->error);
143
0
    return tevent_req_post(req, ev);
144
0
  }
145
146
0
  state->ev = ev;
147
0
  state->stream = stream;
148
0
  state->ret = 0;
149
150
  /*
151
   * we make a copy of the vector so we can change the structure
152
   */
153
0
  state->vector = talloc_array(state, struct iovec, count);
154
0
  if (tevent_req_nomem(state->vector, req)) {
155
0
    return tevent_req_post(req, ev);
156
0
  }
157
0
  memcpy(state->vector, vector, sizeof(struct iovec) * count);
158
0
  state->count = count;
159
160
0
  tstream_gensec_readv_wrapped_next(req);
161
0
  if (!tevent_req_is_in_progress(req)) {
162
0
    return tevent_req_post(req, ev);
163
0
  }
164
165
0
  return req;
166
0
}
167
168
static int tstream_gensec_readv_next_vector(struct tstream_context *unix_stream,
169
              void *private_data,
170
              TALLOC_CTX *mem_ctx,
171
              struct iovec **_vector,
172
              size_t *_count);
173
static void tstream_gensec_readv_wrapped_done(struct tevent_req *subreq);
174
175
static void tstream_gensec_readv_wrapped_next(struct tevent_req *req)
176
0
{
177
0
  struct tstream_gensec_readv_state *state =
178
0
    tevent_req_data(req,
179
0
    struct tstream_gensec_readv_state);
180
0
  struct tstream_gensec *tgss =
181
0
    tstream_context_data(state->stream,
182
0
    struct tstream_gensec);
183
0
  struct tevent_req *subreq;
184
185
  /*
186
   * copy the pending buffer first
187
   */
188
0
  while (tgss->read.left > 0 && state->count > 0) {
189
0
    uint8_t *base = (uint8_t *)state->vector[0].iov_base;
190
0
    size_t len = MIN(tgss->read.left, state->vector[0].iov_len);
191
192
0
    memcpy(base, tgss->read.unwrapped.data + tgss->read.ofs, len);
193
194
0
    base += len;
195
0
    state->vector[0].iov_base = (char *) base;
196
0
    state->vector[0].iov_len -= len;
197
198
0
    tgss->read.ofs += len;
199
0
    tgss->read.left -= len;
200
201
0
    if (state->vector[0].iov_len == 0) {
202
0
      state->vector += 1;
203
0
      state->count -= 1;
204
0
    }
205
206
0
    state->ret += len;
207
0
  }
208
209
0
  if (state->count == 0) {
210
0
    tevent_req_done(req);
211
0
    return;
212
0
  }
213
214
0
  data_blob_free(&tgss->read.unwrapped);
215
0
  ZERO_STRUCT(state->wrapped);
216
217
0
  subreq = tstream_readv_pdu_send(state, state->ev,
218
0
          tgss->plain_stream,
219
0
          tstream_gensec_readv_next_vector,
220
0
          state);
221
0
  if (tevent_req_nomem(subreq, req)) {
222
0
    return;
223
0
  }
224
0
  tevent_req_set_callback(subreq, tstream_gensec_readv_wrapped_done, req);
225
0
}
226
227
static int tstream_gensec_readv_next_vector(struct tstream_context *unix_stream,
228
              void *private_data,
229
              TALLOC_CTX *mem_ctx,
230
              struct iovec **_vector,
231
              size_t *_count)
232
0
{
233
0
  struct tstream_gensec_readv_state *state =
234
0
    talloc_get_type_abort(private_data,
235
0
    struct tstream_gensec_readv_state);
236
0
  struct iovec *vector;
237
0
  size_t count = 1;
238
239
  /* we need to get a message header */
240
0
  vector = talloc_array(mem_ctx, struct iovec, count);
241
0
  if (!vector) {
242
0
    return -1;
243
0
  }
244
245
0
  if (!state->wrapped.asked_for_hdr) {
246
0
    state->wrapped.asked_for_hdr = true;
247
0
    vector[0].iov_base = (char *)state->wrapped.hdr;
248
0
    vector[0].iov_len = sizeof(state->wrapped.hdr);
249
0
  } else if (!state->wrapped.asked_for_blob) {
250
0
    uint32_t msg_len;
251
252
0
    state->wrapped.asked_for_blob = true;
253
254
0
    msg_len = RIVAL(state->wrapped.hdr, 0);
255
256
    /*
257
     * I got a Windows 2012R2 server responding with
258
     * a message of 0x1b28a33.
259
     */
260
0
    if (msg_len > 0x0FFFFFFF) {
261
0
      errno = EMSGSIZE;
262
0
      return -1;
263
0
    }
264
265
0
    if (msg_len == 0) {
266
0
      errno = EMSGSIZE;
267
0
      return -1;
268
0
    }
269
270
0
    state->wrapped.blob = data_blob_talloc(state, NULL, msg_len);
271
0
    if (state->wrapped.blob.data == NULL) {
272
0
      return -1;
273
0
    }
274
275
0
    vector[0].iov_base = (char *)state->wrapped.blob.data;
276
0
    vector[0].iov_len = state->wrapped.blob.length;
277
0
  } else {
278
0
    *_vector = NULL;
279
0
    *_count = 0;
280
0
    return 0;
281
0
  }
282
283
0
  *_vector = vector;
284
0
  *_count = count;
285
0
  return 0;
286
0
}
287
288
static void tstream_gensec_readv_wrapped_done(struct tevent_req *subreq)
289
0
{
290
0
  struct tevent_req *req =
291
0
    tevent_req_callback_data(subreq,
292
0
    struct tevent_req);
293
0
  struct tstream_gensec_readv_state *state =
294
0
    tevent_req_data(req,
295
0
    struct tstream_gensec_readv_state);
296
0
  struct tstream_gensec *tgss =
297
0
    tstream_context_data(state->stream,
298
0
    struct tstream_gensec);
299
0
  int ret;
300
0
  int sys_errno;
301
0
  NTSTATUS status;
302
303
0
  ret = tstream_readv_pdu_recv(subreq, &sys_errno);
304
0
  TALLOC_FREE(subreq);
305
0
  if (ret == -1) {
306
0
    tgss->error = sys_errno;
307
0
    tevent_req_error(req, sys_errno);
308
0
    return;
309
0
  }
310
311
0
  status = gensec_unwrap(tgss->gensec_security,
312
0
             state,
313
0
             &state->wrapped.blob,
314
0
             &tgss->read.unwrapped);
315
0
  if (!NT_STATUS_IS_OK(status)) {
316
0
    tgss->error = EIO;
317
0
    tevent_req_error(req, tgss->error);
318
0
    return;
319
0
  }
320
321
0
  data_blob_free(&state->wrapped.blob);
322
323
0
  talloc_steal(tgss, tgss->read.unwrapped.data);
324
0
  tgss->read.left = tgss->read.unwrapped.length;
325
0
  tgss->read.ofs = 0;
326
327
0
  tstream_gensec_readv_wrapped_next(req);
328
0
}
329
330
static int tstream_gensec_readv_recv(struct tevent_req *req, int *perrno)
331
0
{
332
0
  struct tstream_gensec_readv_state *state =
333
0
    tevent_req_data(req,
334
0
    struct tstream_gensec_readv_state);
335
0
  int ret;
336
337
0
  ret = tsocket_simple_int_recv(req, perrno);
338
0
  if (ret == 0) {
339
0
    ret = state->ret;
340
0
  }
341
342
0
  tevent_req_received(req);
343
0
  return ret;
344
0
}
345
346
struct tstream_gensec_writev_state {
347
  struct tevent_context *ev;
348
  struct tstream_context *stream;
349
350
  struct iovec *vector;
351
  int count;
352
353
  struct {
354
    off_t ofs;
355
    size_t left;
356
    DATA_BLOB blob;
357
  } unwrapped;
358
359
  struct {
360
    uint8_t hdr[4];
361
    DATA_BLOB blob;
362
    struct iovec iov[2];
363
  } wrapped;
364
365
  int ret;
366
};
367
368
static void tstream_gensec_writev_wrapped_next(struct tevent_req *req);
369
370
static struct tevent_req *tstream_gensec_writev_send(TALLOC_CTX *mem_ctx,
371
          struct tevent_context *ev,
372
          struct tstream_context *stream,
373
          const struct iovec *vector,
374
          size_t count)
375
0
{
376
0
  struct tstream_gensec *tgss =
377
0
    tstream_context_data(stream,
378
0
    struct tstream_gensec);
379
0
  struct tevent_req *req;
380
0
  struct tstream_gensec_writev_state *state;
381
0
  size_t i;
382
0
  int total;
383
0
  int chunk;
384
385
0
  req = tevent_req_create(mem_ctx, &state,
386
0
        struct tstream_gensec_writev_state);
387
0
  if (req == NULL) {
388
0
    return NULL;
389
0
  }
390
391
0
  if (tgss->error != 0) {
392
0
    tevent_req_error(req, tgss->error);
393
0
    return tevent_req_post(req, ev);
394
0
  }
395
396
0
  state->ev = ev;
397
0
  state->stream = stream;
398
0
  state->ret = 0;
399
400
  /*
401
   * we make a copy of the vector so we can change the structure
402
   */
403
0
  state->vector = talloc_array(state, struct iovec, count);
404
0
  if (tevent_req_nomem(state->vector, req)) {
405
0
    return tevent_req_post(req, ev);
406
0
  }
407
0
  memcpy(state->vector, vector, sizeof(struct iovec) * count);
408
0
  state->count = count;
409
410
0
  total = 0;
411
0
  for (i = 0; i < count; i++) {
412
    /*
413
     * the generic tstream code makes sure that
414
     * this never wraps.
415
     */
416
0
    total += vector[i].iov_len;
417
0
  }
418
419
  /*
420
   * We may need to send data in chunks.
421
   */
422
0
  chunk = MIN(total, tgss->write.max_unwrapped_size);
423
424
0
  state->unwrapped.blob = data_blob_talloc(state, NULL, chunk);
425
0
  if (tevent_req_nomem(state->unwrapped.blob.data, req)) {
426
0
    return tevent_req_post(req, ev);
427
0
  }
428
429
0
  tstream_gensec_writev_wrapped_next(req);
430
0
  if (!tevent_req_is_in_progress(req)) {
431
0
    return tevent_req_post(req, ev);
432
0
  }
433
434
0
  return req;
435
0
}
436
437
static void tstream_gensec_writev_wrapped_done(struct tevent_req *subreq);
438
439
static void tstream_gensec_writev_wrapped_next(struct tevent_req *req)
440
0
{
441
0
  struct tstream_gensec_writev_state *state =
442
0
    tevent_req_data(req,
443
0
    struct tstream_gensec_writev_state);
444
0
  struct tstream_gensec *tgss =
445
0
    tstream_context_data(state->stream,
446
0
    struct tstream_gensec);
447
0
  struct tevent_req *subreq;
448
0
  NTSTATUS status;
449
450
0
  data_blob_free(&state->wrapped.blob);
451
452
0
  state->unwrapped.left = state->unwrapped.blob.length;
453
0
  state->unwrapped.ofs = 0;
454
455
  /*
456
   * first fill our buffer
457
   */
458
0
  while (state->unwrapped.left > 0 && state->count > 0) {
459
0
    uint8_t *base = (uint8_t *)state->vector[0].iov_base;
460
0
    size_t len = MIN(state->unwrapped.left, state->vector[0].iov_len);
461
462
0
    memcpy(state->unwrapped.blob.data + state->unwrapped.ofs, base, len);
463
464
0
    base += len;
465
0
    state->vector[0].iov_base = (char *) base;
466
0
    state->vector[0].iov_len -= len;
467
468
0
    state->unwrapped.ofs += len;
469
0
    state->unwrapped.left -= len;
470
471
0
    if (state->vector[0].iov_len == 0) {
472
0
      state->vector += 1;
473
0
      state->count -= 1;
474
0
    }
475
476
0
    state->ret += len;
477
0
  }
478
479
0
  if (state->unwrapped.ofs == 0) {
480
0
    tevent_req_done(req);
481
0
    return;
482
0
  }
483
484
0
  state->unwrapped.blob.length = state->unwrapped.ofs;
485
486
0
  status = gensec_wrap(tgss->gensec_security,
487
0
           state,
488
0
           &state->unwrapped.blob,
489
0
           &state->wrapped.blob);
490
0
  if (!NT_STATUS_IS_OK(status)) {
491
0
    tgss->error = EIO;
492
0
    tevent_req_error(req, tgss->error);
493
0
    return;
494
0
  }
495
496
0
  RSIVAL(state->wrapped.hdr, 0, state->wrapped.blob.length);
497
498
0
  state->wrapped.iov[0].iov_base = (void *)state->wrapped.hdr;
499
0
  state->wrapped.iov[0].iov_len = sizeof(state->wrapped.hdr);
500
0
  state->wrapped.iov[1].iov_base = (void *)state->wrapped.blob.data;
501
0
  state->wrapped.iov[1].iov_len = state->wrapped.blob.length;
502
503
0
  subreq = tstream_writev_send(state, state->ev,
504
0
              tgss->plain_stream,
505
0
              state->wrapped.iov, 2);
506
0
  if (tevent_req_nomem(subreq, req)) {
507
0
    return;
508
0
  }
509
0
  tevent_req_set_callback(subreq,
510
0
        tstream_gensec_writev_wrapped_done,
511
0
        req);
512
0
}
513
514
static void tstream_gensec_writev_wrapped_done(struct tevent_req *subreq)
515
0
{
516
0
  struct tevent_req *req =
517
0
    tevent_req_callback_data(subreq,
518
0
    struct tevent_req);
519
0
  struct tstream_gensec_writev_state *state =
520
0
    tevent_req_data(req,
521
0
    struct tstream_gensec_writev_state);
522
0
  struct tstream_gensec *tgss =
523
0
    tstream_context_data(state->stream,
524
0
    struct tstream_gensec);
525
0
  int sys_errno;
526
0
  int ret;
527
528
0
  ret = tstream_writev_recv(subreq, &sys_errno);
529
0
  TALLOC_FREE(subreq);
530
0
  if (ret == -1) {
531
0
    tgss->error = sys_errno;
532
0
    tevent_req_error(req, sys_errno);
533
0
    return;
534
0
  }
535
536
0
  tstream_gensec_writev_wrapped_next(req);
537
0
}
538
539
static int tstream_gensec_writev_recv(struct tevent_req *req,
540
           int *perrno)
541
0
{
542
0
  struct tstream_gensec_writev_state *state =
543
0
    tevent_req_data(req,
544
0
    struct tstream_gensec_writev_state);
545
0
  int ret;
546
547
0
  ret = tsocket_simple_int_recv(req, perrno);
548
0
  if (ret == 0) {
549
0
    ret = state->ret;
550
0
  }
551
552
0
  tevent_req_received(req);
553
0
  return ret;
554
0
}
555
556
struct tstream_gensec_disconnect_state {
557
  uint8_t _dummy;
558
};
559
560
static struct tevent_req *tstream_gensec_disconnect_send(TALLOC_CTX *mem_ctx,
561
               struct tevent_context *ev,
562
               struct tstream_context *stream)
563
0
{
564
0
  struct tstream_gensec *tgss =
565
0
    tstream_context_data(stream,
566
0
    struct tstream_gensec);
567
0
  struct tevent_req *req;
568
0
  struct tstream_gensec_disconnect_state *state;
569
570
0
  req = tevent_req_create(mem_ctx, &state,
571
0
        struct tstream_gensec_disconnect_state);
572
0
  if (req == NULL) {
573
0
    return NULL;
574
0
  }
575
576
0
  if (tgss->error != 0) {
577
0
    tevent_req_error(req, tgss->error);
578
0
    return tevent_req_post(req, ev);
579
0
  }
580
581
  /*
582
   * The caller is responsible to do the real disconnect
583
   * on the plain stream!
584
   */
585
0
  tgss->plain_stream = NULL;
586
0
  tgss->error = ENOTCONN;
587
588
0
  tevent_req_done(req);
589
0
  return tevent_req_post(req, ev);
590
0
}
591
592
static int tstream_gensec_disconnect_recv(struct tevent_req *req,
593
               int *perrno)
594
0
{
595
0
  int ret;
596
597
0
  ret = tsocket_simple_int_recv(req, perrno);
598
599
0
  tevent_req_received(req);
600
0
  return ret;
601
0
}
602
603
static const struct tstream_context_ops tstream_gensec_ops = {
604
  .name                   = "gensec",
605
606
  .pending_bytes          = tstream_gensec_pending_bytes,
607
608
  .readv_send             = tstream_gensec_readv_send,
609
  .readv_recv             = tstream_gensec_readv_recv,
610
611
  .writev_send            = tstream_gensec_writev_send,
612
  .writev_recv            = tstream_gensec_writev_recv,
613
614
  .disconnect_send        = tstream_gensec_disconnect_send,
615
  .disconnect_recv        = tstream_gensec_disconnect_recv,
616
};