Coverage Report

Created: 2026-09-03 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/libcli/auth/spnego_parse.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   RFC2478 Compliant SPNEGO implementation
5
6
   Copyright (C) Jim McDonough <jmcd@us.ibm.com>   2003
7
8
   This program is free software; you can redistribute it and/or modify
9
   it under the terms of the GNU General Public License as published by
10
   the Free Software Foundation; either version 3 of the License, or
11
   (at your option) any later version.
12
13
   This program is distributed in the hope that it will be useful,
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
   GNU General Public License for more details.
17
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/auth/spnego.h"
25
#include "../lib/util/asn1.h"
26
27
static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
28
            struct spnego_negTokenInit *token)
29
0
{
30
0
  ZERO_STRUCTP(token);
31
32
0
  if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
33
0
  if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
34
35
0
  while (asn1_tag_remaining(asn1) > 0) {
36
0
    int i;
37
0
    uint8_t context;
38
39
0
    if (!asn1_peek_uint8(asn1, &context)) {
40
0
      asn1_set_error(asn1);
41
0
      break;
42
0
    }
43
44
0
    switch (context) {
45
    /* Read mechTypes */
46
0
    case ASN1_CONTEXT(0): {
47
0
      const char **mechTypes;
48
49
0
      if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
50
0
      if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
51
52
0
      mechTypes = talloc(mem_ctx, const char *);
53
0
      if (mechTypes == NULL) {
54
0
        asn1_set_error(asn1);
55
0
        return false;
56
0
      }
57
0
      for (i = 0; asn1_tag_remaining(asn1) > 0; i++) {
58
0
        char *oid;
59
0
        const char **p;
60
0
        p = talloc_realloc(mem_ctx,
61
0
               mechTypes,
62
0
               const char *, i+2);
63
0
        if (p == NULL) {
64
0
          talloc_free(mechTypes);
65
0
          asn1_set_error(asn1);
66
0
          return false;
67
0
        }
68
0
        mechTypes = p;
69
70
0
        if (!asn1_read_OID(asn1, mechTypes, &oid)) return false;
71
0
        mechTypes[i] = oid;
72
0
      }
73
0
      mechTypes[i] = NULL;
74
0
      token->mechTypes = mechTypes;
75
76
0
      asn1_end_tag(asn1);
77
0
      asn1_end_tag(asn1);
78
0
      break;
79
0
    }
80
    /* Read reqFlags */
81
0
    case ASN1_CONTEXT(1):
82
0
      if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
83
0
      if (!asn1_read_BitString(asn1, mem_ctx, &token->reqFlags,
84
0
              &token->reqFlagsPadding)) return false;
85
0
      if (!asn1_end_tag(asn1)) return false;
86
0
      break;
87
                /* Read mechToken */
88
0
    case ASN1_CONTEXT(2):
89
0
      if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
90
0
      if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechToken)) return false;
91
0
      if (!asn1_end_tag(asn1)) return false;
92
0
      break;
93
    /* Read mecListMIC */
94
0
    case ASN1_CONTEXT(3):
95
0
    {
96
0
      uint8_t type_peek;
97
0
      if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
98
0
      if (!asn1_peek_uint8(asn1, &type_peek)) {
99
0
        asn1_set_error(asn1);
100
0
        break;
101
0
      }
102
0
      if (type_peek == ASN1_OCTET_STRING) {
103
0
        if (!asn1_read_OctetString(asn1, mem_ctx,
104
0
                  &token->mechListMIC)) return false;
105
0
      } else {
106
        /* RFC 2478 says we have an Octet String here,
107
           but W2k sends something different... */
108
0
        char *mechListMIC;
109
0
        if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
110
0
        if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
111
0
        if (!asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC)) return false;
112
0
        if (!asn1_end_tag(asn1)) return false;
113
0
        if (!asn1_end_tag(asn1)) return false;
114
115
0
        token->targetPrincipal = mechListMIC;
116
0
      }
117
0
      if (!asn1_end_tag(asn1)) return false;
118
0
      break;
119
0
    }
120
0
    default:
121
0
      asn1_set_error(asn1);
122
0
      break;
123
0
    }
124
0
  }
125
126
0
  if (!asn1_end_tag(asn1)) return false;
127
0
  if (!asn1_end_tag(asn1)) return false;
128
129
0
  return !asn1_has_error(asn1);
130
0
}
131
132
static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
133
0
{
134
0
  if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
135
0
  if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
136
137
  /* Write mechTypes */
138
0
  if (token->mechTypes && *token->mechTypes) {
139
0
    int i;
140
141
0
    if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
142
0
    if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
143
0
    for (i = 0; token->mechTypes[i]; i++) {
144
0
      if (!asn1_write_OID(asn1, token->mechTypes[i])) return false;
145
0
    }
146
0
    if (!asn1_pop_tag(asn1)) return false;
147
0
    if (!asn1_pop_tag(asn1)) return false;
148
0
  }
149
150
  /* write reqFlags */
151
0
  if (token->reqFlags.length > 0) {
152
0
    if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
153
0
    if (!asn1_write_BitString(asn1, token->reqFlags.data,
154
0
             token->reqFlags.length,
155
0
             token->reqFlagsPadding)) return false;
156
0
    if (!asn1_pop_tag(asn1)) return false;
157
0
  }
158
159
  /* write mechToken */
160
0
  if (token->mechToken.data) {
161
0
    if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
162
0
    if (!asn1_write_OctetString(asn1, token->mechToken.data,
163
0
               token->mechToken.length)) return false;
164
0
    if (!asn1_pop_tag(asn1)) return false;
165
0
  }
166
167
  /* write mechListMIC */
168
0
  if (token->mechListMIC.data) {
169
0
    if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
170
#if 0
171
    /* This is what RFC 2478 says ... */
172
    asn1_write_OctetString(asn1, token->mechListMIC.data,
173
               token->mechListMIC.length);
174
#else
175
    /* ... but unfortunately this is what Windows
176
       sends/expects */
177
0
    if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
178
0
    if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
179
0
    if (!asn1_push_tag(asn1, ASN1_GENERAL_STRING)) return false;
180
0
    if (!asn1_write(asn1, token->mechListMIC.data,
181
0
         token->mechListMIC.length)) return false;
182
0
    if (!asn1_pop_tag(asn1)) return false;
183
0
    if (!asn1_pop_tag(asn1)) return false;
184
0
    if (!asn1_pop_tag(asn1)) return false;
185
0
#endif
186
0
    if (!asn1_pop_tag(asn1)) return false;
187
0
  }
188
189
0
  if (!asn1_pop_tag(asn1)) return false;
190
0
  if (!asn1_pop_tag(asn1)) return false;
191
192
0
  return !asn1_has_error(asn1);
193
0
}
194
195
static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
196
            struct spnego_negTokenTarg *token)
197
0
{
198
0
  ZERO_STRUCTP(token);
199
200
0
  if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
201
0
  if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
202
203
0
  while (asn1_tag_remaining(asn1) > 0) {
204
0
    uint8_t context;
205
0
    uint8_t neg_result;
206
0
    char *oid;
207
208
0
    if (!asn1_peek_uint8(asn1, &context)) {
209
0
      asn1_set_error(asn1);
210
0
      break;
211
0
    }
212
213
0
    switch (context) {
214
0
    case ASN1_CONTEXT(0):
215
0
      if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
216
0
      if (!asn1_start_tag(asn1, ASN1_ENUMERATED)) return false;
217
0
      if (!asn1_read_uint8(asn1, &neg_result)) return false;
218
0
      token->negResult = neg_result;
219
0
      if (!asn1_end_tag(asn1)) return false;
220
0
      if (!asn1_end_tag(asn1)) return false;
221
0
      break;
222
0
    case ASN1_CONTEXT(1):
223
0
      if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
224
0
      if (!asn1_read_OID(asn1, mem_ctx, &oid)) return false;
225
0
      token->supportedMech = oid;
226
0
      if (!asn1_end_tag(asn1)) return false;
227
0
      break;
228
0
    case ASN1_CONTEXT(2):
229
0
      if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
230
0
      if (!asn1_read_OctetString(asn1, mem_ctx, &token->responseToken)) return false;
231
0
      if (!asn1_end_tag(asn1)) return false;
232
0
      break;
233
0
    case ASN1_CONTEXT(3):
234
0
      if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
235
0
      if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC)) return false;
236
0
      if (!asn1_end_tag(asn1)) return false;
237
0
      break;
238
0
    default:
239
0
      asn1_set_error(asn1);
240
0
      break;
241
0
    }
242
0
  }
243
244
0
  if (!asn1_end_tag(asn1)) return false;
245
0
  if (!asn1_end_tag(asn1)) return false;
246
247
0
  return !asn1_has_error(asn1);
248
0
}
249
250
static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
251
0
{
252
0
  if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
253
0
  if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
254
255
0
  if (token->negResult != SPNEGO_NONE_RESULT) {
256
0
    if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
257
0
    if (!asn1_write_enumerated(asn1, token->negResult)) return false;
258
0
    if (!asn1_pop_tag(asn1)) return false;
259
0
  }
260
261
0
  if (token->supportedMech) {
262
0
    if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
263
0
    if (!asn1_write_OID(asn1, token->supportedMech)) return false;
264
0
    if (!asn1_pop_tag(asn1)) return false;
265
0
  }
266
267
0
  if (token->responseToken.data) {
268
0
    if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
269
0
    if (!asn1_write_OctetString(asn1, token->responseToken.data,
270
0
               token->responseToken.length)) return false;
271
0
    if (!asn1_pop_tag(asn1)) return false;
272
0
  }
273
274
0
  if (token->mechListMIC.data) {
275
0
    if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
276
0
    if (!asn1_write_OctetString(asn1, token->mechListMIC.data,
277
0
              token->mechListMIC.length)) return false;
278
0
    if (!asn1_pop_tag(asn1)) return false;
279
0
  }
280
281
0
  if (!asn1_pop_tag(asn1)) return false;
282
0
  if (!asn1_pop_tag(asn1)) return false;
283
284
0
  return !asn1_has_error(asn1);
285
0
}
286
287
ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
288
0
{
289
0
  struct asn1_data *asn1;
290
0
  ssize_t ret = -1;
291
0
  uint8_t context;
292
293
0
  ZERO_STRUCTP(token);
294
295
0
  if (data.length == 0) {
296
0
    return ret;
297
0
  }
298
299
0
  asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
300
0
  if (asn1 == NULL) {
301
0
    return -1;
302
0
  }
303
304
0
  if (!asn1_load(asn1, data)) goto err;
305
306
0
  if (!asn1_peek_uint8(asn1, &context)) {
307
0
    asn1_set_error(asn1);
308
0
  } else {
309
0
    switch (context) {
310
0
    case ASN1_APPLICATION(0):
311
0
      if (!asn1_start_tag(asn1, ASN1_APPLICATION(0))) goto err;
312
0
      if (!asn1_check_OID(asn1, OID_SPNEGO)) goto err;
313
0
      if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
314
0
        token->type = SPNEGO_NEG_TOKEN_INIT;
315
0
      }
316
0
      if (!asn1_end_tag(asn1)) goto err;
317
0
      break;
318
0
    case ASN1_CONTEXT(1):
319
0
      if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
320
0
        token->type = SPNEGO_NEG_TOKEN_TARG;
321
0
      }
322
0
      break;
323
0
    default:
324
0
      asn1_set_error(asn1);
325
0
      break;
326
0
    }
327
0
  }
328
329
0
  if (!asn1_has_error(asn1)) {
330
0
    ret = asn1_current_ofs(asn1);
331
0
  }
332
333
0
  err:
334
335
0
  asn1_free(asn1);
336
337
0
  return ret;
338
0
}
339
340
ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
341
0
{
342
0
  struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
343
0
  ssize_t ret = -1;
344
345
0
  if (asn1 == NULL) {
346
0
    return -1;
347
0
  }
348
349
0
  switch (spnego->type) {
350
0
  case SPNEGO_NEG_TOKEN_INIT:
351
0
    if (!asn1_push_tag(asn1, ASN1_APPLICATION(0))) goto err;
352
0
    if (!asn1_write_OID(asn1, OID_SPNEGO)) goto err;
353
0
    if (!write_negTokenInit(asn1, &spnego->negTokenInit)) goto err;
354
0
    if (!asn1_pop_tag(asn1)) goto err;
355
0
    break;
356
0
  case SPNEGO_NEG_TOKEN_TARG:
357
0
    write_negTokenTarg(asn1, &spnego->negTokenTarg);
358
0
    break;
359
0
  default:
360
0
    asn1_set_error(asn1);
361
0
    break;
362
0
  }
363
364
0
  if (!asn1_extract_blob(asn1, mem_ctx, blob)) {
365
0
    goto err;
366
0
  }
367
368
0
  ret = asn1_current_ofs(asn1);
369
370
0
  err:
371
372
0
  asn1_free(asn1);
373
374
0
  return ret;
375
0
}
376
377
bool spnego_free_data(struct spnego_data *spnego)
378
0
{
379
0
  bool ret = true;
380
381
0
  if (!spnego) goto out;
382
383
0
  switch(spnego->type) {
384
0
  case SPNEGO_NEG_TOKEN_INIT:
385
0
    if (spnego->negTokenInit.mechTypes) {
386
0
      talloc_free(discard_const(spnego->negTokenInit.mechTypes));
387
0
    }
388
0
    data_blob_free(&spnego->negTokenInit.reqFlags);
389
0
    data_blob_free(&spnego->negTokenInit.mechToken);
390
0
    data_blob_free(&spnego->negTokenInit.mechListMIC);
391
0
    talloc_free(spnego->negTokenInit.targetPrincipal);
392
0
    break;
393
0
  case SPNEGO_NEG_TOKEN_TARG:
394
0
    if (spnego->negTokenTarg.supportedMech) {
395
0
      talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
396
0
    }
397
0
    data_blob_free(&spnego->negTokenTarg.responseToken);
398
0
    data_blob_free(&spnego->negTokenTarg.mechListMIC);
399
0
    break;
400
0
  default:
401
0
    ret = false;
402
0
    break;
403
0
  }
404
0
  ZERO_STRUCTP(spnego);
405
0
out:
406
0
  return ret;
407
0
}
408
409
bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
410
           const char * const *mech_types,
411
           DATA_BLOB *blob)
412
0
{
413
0
  bool ret = false;
414
0
  struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
415
416
0
  if (asn1 == NULL) {
417
0
    return false;
418
0
  }
419
420
  /* Write mechTypes */
421
0
  if (mech_types && *mech_types) {
422
0
    int i;
423
424
0
    if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) goto err;
425
0
    for (i = 0; mech_types[i]; i++) {
426
0
      if (!asn1_write_OID(asn1, mech_types[i])) goto err;
427
0
    }
428
0
    if (!asn1_pop_tag(asn1)) goto err;
429
0
  }
430
431
0
  if (asn1_has_error(asn1)) {
432
0
    goto err;
433
0
  }
434
435
0
  if (!asn1_extract_blob(asn1, mem_ctx, blob)) {
436
0
    goto err;
437
0
  }
438
439
0
  ret = true;
440
441
0
  err:
442
443
0
  asn1_free(asn1);
444
445
0
  return ret;
446
0
}