Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/librpc/ndr/ndr_keycredlink.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   Support routines for packing and unpacking of msDS-KeyCredentialLink
5
   structures.
6
7
   See [MS-ADTS] 2.2.20 Key Credential Link Structures
8
9
   Copyright (C) Gary Lockyer 2025
10
11
   This program is free software; you can redistribute it and/or modify
12
   it under the terms of the GNU General Public License as published by
13
   the Free Software Foundation; either version 3 of the License, or
14
   (at your option) any later version.
15
16
   This program is distributed in the hope that it will be useful,
17
   but WITHOUT ANY WARRANTY; without even the implied warranty of
18
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
   GNU General Public License for more details.
20
21
   You should have received a copy of the GNU General Public License
22
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
*/
24
25
#include "lib/replace/replace.h"
26
27
#include "gen_ndr/ndr_keycredlink.h"
28
#include "lib/util/data_blob.h"
29
#include "libndr.h"
30
#include "librpc/gen_ndr/ndr_bcrypt_rsakey_blob.h"
31
#include "librpc/gen_ndr/ndr_tpm20_rsakey_blob.h"
32
#include "util/asn1.h"
33
#include "util/data_blob.h"
34
#include <assert.h>
35
36
/*
37
 * The KEYCREDENTIALLINK_BLOB consists of the version and a series of variable
38
 * length KEYCREDENTIALLINK_ENTRIES.
39
 */
40
enum ndr_err_code ndr_pull_KEYCREDENTIALLINK_BLOB(
41
  struct ndr_pull *ndr,
42
  ndr_flags_type ndr_flags,
43
  struct KEYCREDENTIALLINK_BLOB *blob)
44
528
{
45
528
  libndr_flags _flags_save_STRUCT = ndr->flags;
46
528
  ndr_set_flags(&ndr->flags, LIBNDR_FLAG_NOALIGN);
47
48
528
  NDR_CHECK(ndr_pull_uint32(ndr, ndr_flags, &blob->version));
49
524
  if (blob->version != 0x0200) {
50
36
    return ndr_pull_error(ndr,
51
36
              NDR_ERR_RANGE,
52
36
              "Invalid version of (0x%04x) "
53
36
              "should be 0x0200, at byte %zu\n",
54
36
              blob->version,
55
36
              (ndr->offset - sizeof(uint32_t)));
56
36
  }
57
488
  blob->count = 0;
58
488
  blob->entries = talloc_array(ndr->current_mem_ctx,
59
488
             struct KEYCREDENTIALLINK_ENTRY,
60
488
             blob->count);
61
488
  if (blob->entries == NULL) {
62
0
    return ndr_pull_error(ndr,
63
0
              NDR_ERR_ALLOC,
64
0
              "Failed to pull KEYCREDENTIALLINK_ENTRY");
65
0
  }
66
125k
  while (ndr->offset < ndr->data_size) {
67
125k
    blob->entries = talloc_realloc(ndr->current_mem_ctx,
68
125k
                 blob->entries,
69
125k
                 struct KEYCREDENTIALLINK_ENTRY,
70
125k
                 blob->count + 1);
71
125k
    if (blob->entries == NULL) {
72
0
      return ndr_pull_error(
73
0
        ndr,
74
0
        NDR_ERR_ALLOC,
75
0
        "Failed to pull KEYCREDENTIALLINK_ENTRY");
76
0
    }
77
125k
    NDR_CHECK(ndr_pull_KEYCREDENTIALLINK_ENTRY(
78
125k
      ndr, ndr_flags, &blob->entries[blob->count]));
79
124k
    blob->count++;
80
124k
  }
81
259
  ndr->flags = _flags_save_STRUCT;
82
259
  return NDR_ERR_SUCCESS;
83
488
}
84
85
enum ndr_err_code ndr_push_KEYCREDENTIALLINK_BLOB(
86
  struct ndr_push *ndr,
87
  ndr_flags_type ndr_flags,
88
  const struct KEYCREDENTIALLINK_BLOB *blob)
89
259
{
90
259
  int i = 0;
91
92
259
  if (blob->version != 0x0200) {
93
0
    return ndr_push_error(ndr,
94
0
              NDR_ERR_RANGE,
95
0
              "Invalid version of (0x%04x) "
96
0
              "should be 0x0200, at byte %zu\n",
97
0
              blob->version,
98
0
              (ndr->offset - sizeof(uint32_t)));
99
0
  }
100
259
  NDR_CHECK(ndr_push_uint32(ndr, ndr_flags, blob->version));
101
102
37.0k
  for (i = 0; i < blob->count; i++) {
103
36.7k
    NDR_CHECK(ndr_push_KEYCREDENTIALLINK_ENTRY(ndr,
104
36.7k
                 ndr_flags,
105
36.7k
                 &blob->entries[i]));
106
36.7k
  }
107
259
  return NDR_ERR_SUCCESS;
108
259
}
109
110
/*
111
 * To pull the CUSTOM_KEY_INFORMATION the length from the enclosing
112
 * KEYCREDENTIALLINK_ENTRY needs to be passed in.
113
 *
114
 * CUSTOM_KEY_INFORMATION has two representations based on the size parameter
115
 *
116
 * If size is 2 only the version and flags are expected.
117
 * If the size is greater than 2 then
118
 *    version, flags, volType, supportsNotification, fekKeyVersion,
119
 *    keyStrength and the reserved bytes are expected
120
 *    Optionally followed by a series of EncodedExtendedCKI entries
121
 *
122
 */
123
static enum ndr_err_code pull_cki(struct ndr_pull *ndr,
124
          ndr_flags_type ndr_flags,
125
          struct CUSTOM_KEY_INFORMATION *info,
126
          uint32_t size)
127
6.81k
{
128
  /* Calculate the end of the CUSTOM_KEY_INFORMATION in the raw bytes */
129
6.81k
  uint32_t end_offset = ndr->offset + size;
130
131
  /*
132
   * Initialise the CUSTOM_KEY_INFORMATION, in case this is the
133
   * short form.
134
   */
135
6.81k
  *info = (struct CUSTOM_KEY_INFORMATION){0};
136
137
6.81k
  NDR_CHECK(ndr_pull_uint8(ndr, ndr_flags, &info->version));
138
6.81k
  if (info->version != 0x01) {
139
8
    return ndr_pull_error(ndr,
140
8
              NDR_ERR_RANGE,
141
8
              "Invalid version of (0x%02x) "
142
8
              "should be 0x01, at byte %zu\n",
143
8
              info->version,
144
8
              (ndr->offset - sizeof(uint8_t)));
145
8
  }
146
6.80k
  NDR_CHECK(ndr_pull_CUSTOM_KEY_INFO_Flags(ndr, ndr_flags, &info->flags));
147
148
6.80k
  if (size == 2) {
149
544
    info->isExtended = false;
150
544
    return NDR_ERR_SUCCESS;
151
544
  }
152
6.25k
  info->isExtended = true;
153
6.25k
  NDR_CHECK(ndr_pull_CUSTOM_KEY_INFO_VolType(ndr,
154
6.25k
               ndr_flags,
155
6.25k
               &info->volType));
156
6.23k
  NDR_CHECK(ndr_pull_CUSTOM_KEY_INFO_SupportsNotification(
157
6.23k
    ndr, ndr_flags, &info->supportsNotification));
158
6.23k
  NDR_CHECK(ndr_pull_uint8(ndr, ndr_flags, &info->fekKeyVersion));
159
6.22k
  if (info->fekKeyVersion != 0x01) {
160
19
    return ndr_pull_error(ndr,
161
19
              NDR_ERR_RANGE,
162
19
              "Invalid fekKeyVersion of (0x%02x) "
163
19
              "should be 0x01, at byte %zu\n",
164
19
              info->fekKeyVersion,
165
19
              (ndr->offset - sizeof(uint8_t)));
166
19
  }
167
6.20k
  NDR_CHECK(ndr_pull_CUSTOM_KEY_INFO_KeyStrength(ndr,
168
6.20k
                   ndr_flags,
169
6.20k
                   &info->keyStrength));
170
6.20k
  NDR_CHECK(ndr_pull_array_uint8(ndr, ndr_flags, info->reserved, 10));
171
172
  /* Pull the EncodedExtendedCKI values */
173
6.19k
  info->count = 0;
174
6.19k
  info->cki = talloc_array(ndr->current_mem_ctx,
175
6.19k
         struct EncodedExtendedCKI,
176
6.19k
         info->count);
177
6.19k
  if (info->cki == NULL) {
178
0
    return ndr_pull_error(ndr,
179
0
              NDR_ERR_ALLOC,
180
0
              "Failed to pull EncodedExtendCKI");
181
0
  }
182
300k
  while (ndr->offset < end_offset) {
183
294k
    info->cki = talloc_realloc(ndr->current_mem_ctx,
184
294k
             info->cki,
185
294k
             struct EncodedExtendedCKI,
186
294k
             info->count + 1);
187
294k
    if (info->cki == NULL) {
188
0
      return ndr_pull_error(
189
0
        ndr,
190
0
        NDR_ERR_ALLOC,
191
0
        "Failed to pull EncodedExtendedCKI");
192
0
    }
193
294k
    NDR_CHECK(ndr_pull_EncodedExtendedCKI(ndr,
194
294k
                  ndr_flags,
195
294k
                  &info->cki[info->count]));
196
294k
    info->count++;
197
294k
  }
198
6.11k
  return NDR_ERR_SUCCESS;
199
6.19k
}
200
201
/*
202
 * CUSTOM_KEY-INFORMATION has two representations with differing sizes
203
 * the flag isExtended controls which version is written.
204
 */
205
enum ndr_err_code ndr_push_CUSTOM_KEY_INFORMATION(
206
  struct ndr_push *ndr,
207
  ndr_flags_type ndr_flags,
208
  const struct CUSTOM_KEY_INFORMATION *info)
209
2.96k
{
210
2.96k
  int i = 0;
211
212
2.96k
  if (info->version != 0x01) {
213
0
    return ndr_push_error(ndr,
214
0
              NDR_ERR_RANGE,
215
0
              "Invalid version of (0x%02x) "
216
0
              "should be 0x01, at byte %zu\n",
217
0
              info->version,
218
0
              (ndr->offset - sizeof(uint8_t)));
219
0
  }
220
2.96k
  NDR_CHECK(ndr_push_uint8(ndr, ndr_flags, info->version));
221
2.96k
  NDR_CHECK(ndr_push_CUSTOM_KEY_INFO_Flags(ndr, ndr_flags, info->flags));
222
2.96k
  if (!info->isExtended) {
223
1.01k
    return NDR_ERR_SUCCESS;
224
1.01k
  }
225
226
1.94k
  NDR_CHECK(ndr_push_CUSTOM_KEY_INFO_VolType(ndr,
227
1.94k
               ndr_flags,
228
1.94k
               info->volType));
229
1.94k
  NDR_CHECK(ndr_push_CUSTOM_KEY_INFO_SupportsNotification(
230
1.94k
    ndr, ndr_flags, info->supportsNotification));
231
1.94k
  if (info->fekKeyVersion != 0x01) {
232
0
    return ndr_push_error(ndr,
233
0
              NDR_ERR_RANGE,
234
0
              "Invalid fekKeyVersion of (0x%02x) "
235
0
              "should be 0x01, at byte %zu\n",
236
0
              info->fekKeyVersion,
237
0
              (ndr->offset - sizeof(uint8_t)));
238
0
  }
239
1.94k
  NDR_CHECK(ndr_push_uint8(ndr, ndr_flags, info->fekKeyVersion));
240
1.94k
  NDR_CHECK(ndr_push_CUSTOM_KEY_INFO_KeyStrength(ndr,
241
1.94k
                   ndr_flags,
242
1.94k
                   info->keyStrength));
243
1.94k
  NDR_CHECK(ndr_push_array_uint8(ndr, ndr_flags, info->reserved, 10));
244
245
215k
  for (i = 0; i < info->count; i++) {
246
214k
    NDR_CHECK(ndr_push_EncodedExtendedCKI(ndr,
247
214k
                  ndr_flags,
248
214k
                  &info->cki[i]));
249
214k
  }
250
1.94k
  return NDR_ERR_SUCCESS;
251
1.94k
}
252
253
/*
254
 * To pull a KEYCREDENTIALLINK_Value the length from the enclosing
255
 * KEYCREDENTIALLINK_ENTRY needs to be passed in.
256
 *
257
 */
258
static enum ndr_err_code ndr_pull_value(struct ndr_pull *ndr,
259
          ndr_flags_type ndr_flags,
260
          union KEYCREDENTIALLINK_ENTRY_Value *r,
261
          uint32_t size)
262
125k
{
263
125k
  uint32_t level;
264
125k
  const size_t header_len = sizeof(uint16_t) + sizeof(uint8_t);
265
125k
  const size_t identifier_len = sizeof(uint8_t);
266
125k
  libndr_flags flags_save = ndr->flags;
267
268
  /* this function should only be called if NDR_SCALARS is set */
269
125k
  assert(ndr_flags & NDR_SCALARS);
270
271
125k
  ndr_set_flags(&ndr->flags, LIBNDR_FLAG_NOALIGN);
272
273
  /* This token is not used again */
274
125k
  NDR_CHECK(ndr_pull_steal_switch_value(ndr, r, &level));
275
276
125k
  switch (level) {
277
278
  case KeyID: {
278
278
    if (size != 32) {
279
17
      return ndr_pull_error(ndr,
280
17
                NDR_ERR_ARRAY_SIZE,
281
17
                "Invalid size of (%" PRIu32
282
17
                ") for KeyID "
283
17
                "should be (32), at byte %zu\n",
284
17
                size,
285
17
                (ndr->offset - header_len));
286
17
    }
287
261
    NDR_CHECK(
288
261
      ndr_pull_array_uint8(ndr, NDR_SCALARS, r->keyId, size));
289
260
    break;
290
261
  }
291
292
278
  case KeyHash: {
293
278
    if (size != 32) {
294
19
      return ndr_pull_error(ndr,
295
19
                NDR_ERR_ARRAY_SIZE,
296
19
                "Invalid size of (%" PRIu32
297
19
                ") for KeyHash "
298
19
                "should be (32), at byte %zu\n",
299
19
                size,
300
19
                (ndr->offset - header_len));
301
19
    }
302
259
    NDR_CHECK(ndr_pull_array_uint8(
303
259
      ndr, NDR_SCALARS, r->keyHash, size));
304
258
    break;
305
259
  }
306
307
25.4k
  case KeyUsage: {
308
25.4k
    if (size != 1) {
309
18
      return ndr_pull_error(ndr,
310
18
                NDR_ERR_LENGTH,
311
18
                "Invalid length of (%" PRIu32
312
18
                ") for KeyUsage "
313
18
                "should be (1), at byte %zu\n",
314
18
                size,
315
18
                (ndr->offset - header_len));
316
18
    }
317
25.4k
    NDR_CHECK(ndr_pull_KEYCREDENTIALLINK_ENTRY_KeyUsage(
318
25.4k
      ndr, NDR_SCALARS, &r->keyUsage));
319
25.4k
    break;
320
25.4k
  }
321
322
40.1k
  case KeySource: {
323
40.1k
    if (size != 1) {
324
20
      return ndr_pull_error(ndr,
325
20
                NDR_ERR_LENGTH,
326
20
                "Invalid length of (%" PRIu32
327
20
                ") for KeySource "
328
20
                "should be (1), at byte %zu\n",
329
20
                size,
330
20
                (ndr->offset - header_len));
331
20
    }
332
40.1k
    NDR_CHECK(ndr_pull_KEYCREDENTIALLINK_ENTRY_KeySource(
333
40.1k
      ndr, NDR_SCALARS, &r->keySource));
334
40.1k
    break;
335
40.1k
  }
336
337
40.1k
  case KeyMaterial: {
338
15.4k
    if (size == 0) {
339
1
      return ndr_pull_error(
340
1
        ndr,
341
1
        NDR_ERR_LENGTH,
342
1
        "Invalid length of (%" PRIu32
343
1
        ") for keyMaterial "
344
1
        "should be non zero, at byte %zu\n",
345
1
        size,
346
1
        (ndr->offset - header_len));
347
1
    }
348
15.4k
    NDR_PULL_NEED_BYTES(ndr, size);
349
15.4k
    r->keyMaterial = data_blob_talloc(ndr->current_mem_ctx,
350
15.4k
              ndr->data + ndr->offset,
351
15.4k
              size);
352
15.4k
    if (r->keyMaterial.data == NULL) {
353
0
      return ndr_pull_error(ndr,
354
0
                NDR_ERR_ALLOC,
355
0
                "Failed to pull keyMaterial");
356
0
    }
357
15.4k
    ndr->offset += size;
358
15.4k
    break;
359
15.4k
  }
360
361
302
  case DeviceId: {
362
302
    if (size != 16) {
363
16
      return ndr_pull_error(ndr,
364
16
                NDR_ERR_ARRAY_SIZE,
365
16
                "Invalid size of (%" PRIu32
366
16
                ") for KeySource "
367
16
                "should be (1), at byte %zu\n",
368
16
                size,
369
16
                (ndr->offset - header_len));
370
16
    }
371
286
    NDR_CHECK(ndr_pull_array_uint8(
372
286
      ndr, NDR_SCALARS, r->deviceId, size));
373
285
    break;
374
286
  }
375
376
6.81k
  case CustomKeyInformation: {
377
6.81k
    NDR_CHECK(pull_cki(
378
6.81k
      ndr, NDR_SCALARS, &r->customKeyInformation, size));
379
6.65k
    break;
380
6.81k
  }
381
382
6.65k
  case KeyApproximateLastLogonTimeStamp: {
383
980
    if (size != 8) {
384
18
      return ndr_pull_error(
385
18
        ndr,
386
18
        NDR_ERR_LENGTH,
387
18
        "Invalid length of (%" PRIu32 ") for "
388
18
        "KeyApproximateLastLogonTimeStamp "
389
18
        "should be (8), at byte %zu\n",
390
18
        size,
391
18
        (ndr->offset - header_len));
392
18
    }
393
962
    NDR_CHECK(ndr_pull_NTTIME(ndr, NDR_SCALARS, &r->lastLogon));
394
947
    break;
395
962
  }
396
397
35.7k
  case KeyCreationTime: {
398
35.7k
    if (size != 8) {
399
17
      return ndr_pull_error(ndr,
400
17
                NDR_ERR_RANGE,
401
17
                "Invalid size of (%" PRIu32
402
17
                ") for "
403
17
                "KeyCreationTime "
404
17
                "should be (8), at byte %zu\n",
405
17
                size,
406
17
                (ndr->offset - header_len));
407
17
    }
408
35.7k
    NDR_CHECK(ndr_pull_NTTIME(ndr, NDR_SCALARS, &r->created));
409
35.7k
    break;
410
35.7k
  }
411
412
35.7k
  default:
413
58
    return ndr_pull_error(ndr,
414
125k
              NDR_ERR_BAD_SWITCH,
415
125k
              "Bad switch value %02x at byte %zu",
416
125k
              level,
417
125k
              ndr->offset - identifier_len);
418
125k
  }
419
125k
  ndr->flags = flags_save;
420
125k
  return NDR_ERR_SUCCESS;
421
125k
}
422
423
/*
424
 * Need to pass the length element of the KEYCREDENTIALLINK_ENTRY down to
425
 * ndr_pull_value, the code that pulls the KEYCREDENTIALLINK_ENTRY_Value.
426
 */
427
enum ndr_err_code ndr_pull_KEYCREDENTIALLINK_ENTRY(
428
  struct ndr_pull *ndr,
429
  ndr_flags_type ndr_flags,
430
  struct KEYCREDENTIALLINK_ENTRY *r)
431
125k
{
432
125k
  libndr_flags _flags_save_STRUCT = ndr->flags;
433
125k
  ndr_set_flags(&ndr->flags, LIBNDR_FLAG_NOALIGN);
434
125k
  if (ndr_flags & NDR_SCALARS) {
435
125k
    NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->length));
436
125k
    NDR_CHECK(ndr_pull_KEYCREDENTIALLINK_ENTRY_Identifier(
437
125k
      ndr, NDR_SCALARS, &r->identifier));
438
125k
    NDR_CHECK(ndr_pull_set_switch_value(ndr,
439
125k
                &r->value,
440
125k
                r->identifier));
441
125k
    NDR_CHECK(
442
125k
      ndr_pull_value(ndr, NDR_SCALARS, &r->value, r->length));
443
125k
  }
444
125k
  ndr->flags = _flags_save_STRUCT;
445
125k
  return NDR_ERR_SUCCESS;
446
125k
}
447
448
/* @brief Check that the AlgorithmIdentifier element is correct
449
 *
450
 * AlgorithmIdentifier ::= SEQUENCE {
451
 *     algorithm       OBJECT IDENTIFIER,
452
 *     parameters      ANY DEFINED BY algorithm OPTIONAL
453
 *                     -- Should be NULL for RSA
454
 * }
455
 *
456
 * @param[in]     ndr ndr pull context
457
 * @param[in,out] asn ASN data context
458
 *
459
 * @return NDR_ERR_SUCCESS if the element is valid.
460
 */
461
static enum ndr_err_code check_algorithm_identifier(struct ndr_pull *ndr,
462
                struct asn1_data *asn)
463
242
{
464
242
  static const char *RSA_ENCRYPTION_OID = "1.2.840.113549.1.1.1";
465
242
  uint8_t asn1_null[2];
466
242
  if (!asn1_start_tag(asn, ASN1_SEQUENCE(0))) {
467
27
    return ndr_pull_error(
468
27
      ndr,
469
27
      NDR_ERR_VALIDATE,
470
27
      "Invalid ASN1 tag, expecting SEQUENCE 0x30");
471
27
  }
472
215
  if (!asn1_check_OID(asn, RSA_ENCRYPTION_OID)) {
473
215
    return ndr_pull_error(
474
215
      ndr,
475
215
      NDR_ERR_VALIDATE,
476
215
      "Invalid ASN1 algorithm OID, expecting %s",
477
215
      RSA_ENCRYPTION_OID);
478
215
  }
479
480
  /* For an RSA public key, parameters should be null 0x0500 */
481
0
  if (!asn1_read(asn, asn1_null, 2)) {
482
0
    return ndr_pull_error(
483
0
      ndr,
484
0
      NDR_ERR_VALIDATE,
485
0
      "Unexpected ASN1 element, expecting NULL 0x05");
486
0
  }
487
0
  if (!asn1_end_tag(asn)) { /* AlgorithmIdentifier */
488
0
    return ndr_pull_error(ndr,
489
0
              NDR_ERR_UNREAD_BYTES,
490
0
              "ASN1 element AlgorithmIdentifier");
491
0
  }
492
0
  return NDR_ERR_SUCCESS;
493
0
}
494
495
/**
496
 * @brief start processing a BIT STRING
497
 *
498
 * The caller will need to call asn1_end_tag
499
 *
500
 * @param[in]     ndr         ndr pull context
501
 * @param[in,out] asn         ASN data context
502
 * @param[out]    unused_bits the number of unused bits in the least
503
 *                            significant byte (LSB) of the BIT String
504
 *
505
 * @return NDR_ERR_SUCCESS if successful
506
 *         The contents of unused_bits are undefined on an error
507
 */
508
static enum ndr_err_code start_bit_string(struct ndr_pull *ndr,
509
            struct asn1_data *asn,
510
            uint8_t *unused_bits)
511
0
{
512
0
  if (!asn1_start_tag(asn, ASN1_BIT_STRING)) {
513
0
    return ndr_pull_error(
514
0
      ndr,
515
0
      NDR_ERR_VALIDATE,
516
0
      "Invalid ASN1 tag, expecting BIT STRING 0x03");
517
0
  }
518
519
  /*
520
   * The first byte of a BIT STRING contains the number of unused bits
521
   * in the final byte.
522
   */
523
0
  if (!asn1_read_uint8(asn, unused_bits)) {
524
0
    return ndr_pull_error(ndr,
525
0
              NDR_ERR_VALIDATE,
526
0
              "Invalid ASN1 BIT STRING, unable to read "
527
0
              "number of unused bits");
528
0
  }
529
0
  if (*unused_bits > 8) {
530
0
    return ndr_pull_error(ndr,
531
0
              NDR_ERR_RANGE,
532
0
              "Invalid ASN1 BIT STRING, "
533
0
              "number of unused bits exceeds 9");
534
0
  }
535
0
  return NDR_ERR_SUCCESS;
536
0
}
537
538
/**
539
 * @brief Read a DER encoded INTEGER into a data_blob
540
 *
541
 * @param[in]     mem_ctx memory context to allocate the data_blob data on
542
 * @param[in]     ndr     ndr pull context
543
 * @param[in,out] asn     ASN data context
544
 * @param[in]     name    the name of the INTEGER for diagnostic messages
545
 * @param[out]    blob    the data blob to populate
546
 *                        using mem_ctx for allocation
547
 *
548
 * @return NDR_ERR_SUCCESS if successful
549
 *         The contents of blob are undefined on an error
550
 */
551
static enum ndr_err_code read_integer(TALLOC_CTX *mem_ctx,
552
              struct ndr_pull *ndr,
553
              struct asn1_data *asn,
554
              const char *name,
555
              DATA_BLOB *blob)
556
0
{
557
0
  static const int MAX_SIZE = 2 * 2048; /* 16384 bits */
558
0
  uint8_t msb = 0;
559
0
  int tag_size = 0;
560
561
0
  if (!asn1_start_tag(asn, ASN1_INTEGER)) {
562
0
    return ndr_pull_error(
563
0
      ndr,
564
0
      NDR_ERR_VALIDATE,
565
0
      "Invalid ASN1 tag, expecting INTEGER 0x02");
566
0
  }
567
0
  if (!asn1_peek_uint8(asn, &msb)) {
568
0
    return ndr_pull_error(
569
0
      ndr,
570
0
      NDR_ERR_VALIDATE,
571
0
      "Invalid ASN1 tag, unable to inspect first byte of %s",
572
0
      name);
573
0
  }
574
  /* skip a leading 0 byte if present */
575
0
  if (msb == 0) {
576
0
    if (!asn1_read_uint8(asn, &msb)) {
577
0
      return ndr_pull_error(ndr,
578
0
                NDR_ERR_VALIDATE,
579
0
                "Invalid ASN1 tag, unable to "
580
0
                "read first byte of %s",
581
0
                name);
582
0
    }
583
0
  }
584
585
0
  tag_size = asn1_tag_remaining(asn);
586
0
  if (tag_size > MAX_SIZE) {
587
0
    return ndr_pull_error(ndr,
588
0
              NDR_ERR_LENGTH,
589
0
              "INTEGER %s size of %d "
590
0
              "bytes is too large",
591
0
              name,
592
0
              tag_size);
593
0
  }
594
0
  if (tag_size <= 0) {
595
0
    return ndr_pull_error(ndr,
596
0
              NDR_ERR_LENGTH,
597
0
              "INTEGER %s size of %d "
598
0
              "bytes is too small",
599
0
              name,
600
0
              tag_size);
601
0
  }
602
0
  *blob = data_blob_talloc(mem_ctx, NULL, tag_size);
603
0
  if (blob->data == NULL) {
604
0
    return ndr_pull_error(ndr,
605
0
              NDR_ERR_ALLOC,
606
0
              "Unable to allocate DATA_BLOB for %s",
607
0
              name);
608
0
  }
609
610
0
  if (!asn1_read(asn, blob->data, tag_size)) {
611
0
    return ndr_pull_error(ndr,
612
0
              NDR_ERR_VALIDATE,
613
0
              "Unable to read %s",
614
0
              name);
615
0
  }
616
0
  if (!asn1_end_tag(asn)) {
617
0
    return ndr_pull_error(ndr,
618
0
              NDR_ERR_UNREAD_BYTES,
619
0
              "ASN1 INTEGER element %s",
620
0
              name);
621
0
  }
622
0
  return NDR_ERR_SUCCESS;
623
0
}
624
625
/**
626
 * @brief Convert a DER encoded X509 PublicKey into the Internal public key
627
 *        representation
628
 *
629
 * publicKey BIT STRING -- containing an RSAPublicKey
630
 * RSAPublicKey ::= SEQUENCE {
631
 *     modulus            INTEGER,
632
 *     publicExponent     INTEGER
633
 * }
634
 *
635
 * @param[in,out] ndr       ndr pull context
636
 * @param[in]     ndr_flags
637
 * @param[out]    kmi       the KeyMaterialInternal structure to populate
638
 *
639
 * @return NDR_ERR_SUCCESS if successful
640
 *         The contents of kmi are undefined on an error
641
 */
642
static enum ndr_err_code read_public_key(struct ndr_pull *ndr,
643
           struct asn1_data *asn,
644
           struct KeyMaterialInternal *kmi)
645
0
{
646
0
  uint8_t unused_bits = 0;
647
648
  /*
649
   * publicKey BIT STRING
650
   * The RSAPublicKey is encoded in a BIT STRING
651
   */
652
0
  NDR_CHECK(start_bit_string(ndr, asn, &unused_bits));
653
654
  /* RSAPublicKey ::= SEQUENCE {
655
   *     modulus            INTEGER,    -- n
656
   *     publicExponent     INTEGER  }  -- e
657
   */
658
0
  if (!asn1_start_tag(asn, ASN1_SEQUENCE(0))) {
659
0
    return ndr_pull_error(
660
0
      ndr,
661
0
      NDR_ERR_VALIDATE,
662
0
      "Invalid ASN1 tag, expecting SEQUENCE 0x30");
663
0
  }
664
665
  /* modulus INTEGER  */
666
0
  NDR_CHECK(read_integer(
667
0
    ndr->current_mem_ctx, ndr, asn, "MODULUS", &kmi->modulus));
668
0
  kmi->bit_size = (kmi->modulus.length * 8) - unused_bits;
669
670
  /* public exponent INTEGER */
671
0
  NDR_CHECK(read_integer(
672
0
    ndr->current_mem_ctx, ndr, asn, "EXPONENT", &kmi->exponent));
673
674
0
  if (!asn1_end_tag(asn)) { /* RSAPublicKey */
675
0
    return ndr_pull_error(ndr,
676
0
              NDR_ERR_UNREAD_BYTES,
677
0
              "ASN1 element RSAPublicKey");
678
0
  }
679
0
  if (!asn1_end_tag(asn)) { /* PublicKey */
680
0
    return ndr_pull_error(ndr,
681
0
              NDR_ERR_UNREAD_BYTES,
682
0
              "ASN1 element PublicKey");
683
0
  }
684
0
  return NDR_ERR_SUCCESS;
685
0
}
686
687
/**
688
 * @brief Convert a DER encoded X509 public key into the Internal public key
689
 *        representation
690
 *
691
 * @param[in,out] ndr ndr pull context
692
 * @param[in]     ndr_flags
693
 * @param[out]    kmi the KeyMaterialInternal structure to populate
694
 * @param[in]     size number of bytes to process from the ndr context
695
 *
696
 * @return NDR_ERR_SUCCESS if successful
697
 *         The contents of r are undefined on an error
698
 */
699
static enum ndr_err_code pull_DER_RSA_KEY(struct ndr_pull *ndr,
700
            ndr_flags_type ndr_flags,
701
            struct KeyMaterialInternal *kmi,
702
            uint32_t size)
703
417
{
704
417
  enum ndr_err_code ret = NDR_ERR_SUCCESS;
705
417
  struct asn1_data *asn = NULL;
706
707
417
  TALLOC_CTX *tmp_ctx = talloc_new(ndr->current_mem_ctx);
708
417
  if (tmp_ctx == NULL) {
709
0
    return ndr_pull_error(ndr,
710
0
              NDR_ERR_ALLOC,
711
0
              "Unable to allocate temporary memory "
712
0
              "context");
713
0
  }
714
417
  asn = asn1_init(tmp_ctx, 5);
715
417
  if (asn == NULL) {
716
0
    TALLOC_FREE(tmp_ctx);
717
0
    return ndr_pull_error(ndr,
718
0
              NDR_ERR_ALLOC,
719
0
              "Unable to initialize ASN1 context");
720
0
  }
721
417
  asn1_load_nocopy(asn, ndr->data, size);
722
723
  /*
724
   * PublicKeyInfo  ::=  SEQUENCE  {
725
   *     algorithm  AlgorithmIdentifier,
726
   *     publicKey  BIT STRING
727
   *     }
728
   */
729
417
  if (!asn1_start_tag(asn, ASN1_SEQUENCE(0))) {
730
175
    ret = ndr_pull_error(
731
175
      ndr,
732
175
      NDR_ERR_VALIDATE,
733
175
      "Invalid ASN1 tag, expecting SEQUENCE 0x30");
734
175
    goto out;
735
175
  }
736
737
242
  ret = check_algorithm_identifier(ndr, asn);
738
242
  if (ret != NDR_ERR_SUCCESS) {
739
242
    goto out;
740
242
  }
741
742
0
  ret = read_public_key(ndr, asn, kmi);
743
0
  if (ret != NDR_ERR_SUCCESS) {
744
0
    goto out;
745
0
  }
746
0
  if (!asn1_end_tag(asn)) { /* PublicKeyInfo */
747
0
    ret = ndr_pull_error(ndr,
748
0
             NDR_ERR_UNREAD_BYTES,
749
0
             "ASN1 element PublicKeyInfo");
750
0
    goto out;
751
0
  }
752
753
  /* Successfully parsed the key data */
754
0
  ret = NDR_ERR_SUCCESS;
755
0
  ndr->offset += size; /* signal to NDR that the data has been consumed */
756
757
417
out:
758
417
  asn1_free(asn);
759
417
  TALLOC_FREE(tmp_ctx);
760
417
  return ret;
761
0
}
762
763
/**
764
 * @brief Convert a TPM20_RSA_KEY_BLOB into the Internal public key
765
 *        representation
766
 * @param[in,out] ndr       ndr pull context
767
 * @param[in]     ndr_flags
768
 * @param[out]    kmi       the KeyMaterialInternal structure to populate
769
 *
770
 * @return NDR_ERR_SUCCESS if successful
771
 *         The contents of kmi are undefined on an error
772
 */
773
static enum ndr_err_code pull_TPM20_RSAKEY_BLOB(struct ndr_pull *ndr,
774
            ndr_flags_type ndr_flags,
775
            struct KeyMaterialInternal *kmi)
776
268
{
777
268
  enum ndr_err_code ret = NDR_ERR_SUCCESS;
778
268
  struct TPM20_RSAKEY_BLOB *km = NULL;
779
780
268
  TALLOC_CTX *tmp_ctx = talloc_new(ndr->current_mem_ctx);
781
268
  if (tmp_ctx == NULL) {
782
0
    return ndr_pull_error(
783
0
      ndr,
784
0
      NDR_ERR_ALLOC,
785
0
      "Unable to allocate temporary memory context");
786
0
  }
787
788
268
  km = talloc_zero(tmp_ctx, struct TPM20_RSAKEY_BLOB);
789
268
  if (km == NULL) {
790
0
    ret = ndr_pull_error(ndr,
791
0
             NDR_ERR_ALLOC,
792
0
             "Unable to allocate TPM20_RSAKEY_BLOB");
793
0
    goto out;
794
0
  }
795
796
268
  ret = ndr_pull_TPM20_RSAKEY_BLOB(ndr, ndr_flags, km);
797
268
  if (ret != NDR_ERR_SUCCESS) {
798
187
    goto out_km;
799
187
  }
800
81
  kmi->bit_size = km->public_key.rsa_detail.keyBits;
801
81
  kmi->modulus = data_blob_talloc(ndr->current_mem_ctx,
802
81
          km->public_key.rsa.buffer,
803
81
          km->public_key.rsa.size);
804
81
  if (kmi->modulus.data == NULL) {
805
0
    ret = ndr_pull_error(
806
0
      ndr,
807
0
      NDR_ERR_ALLOC,
808
0
      "Unable to allocate TPM20_RSAKEY_BLOB modulus");
809
0
    goto out_km;
810
0
  }
811
812
81
  kmi->exponent = data_blob_talloc(ndr->current_mem_ctx,
813
81
           km->public_key.rsa_detail.exponent,
814
81
           TPM_RSA_EXPONENT_SIZE);
815
81
  if (kmi->exponent.data == NULL) {
816
0
    ret = ndr_pull_error(
817
0
      ndr,
818
0
      NDR_ERR_ALLOC,
819
0
      "Unable to allocate TPM20_RSAKEY_BLOB exponent");
820
0
    goto out_km;
821
0
  }
822
81
  ret = NDR_ERR_SUCCESS;
823
824
268
out_km:
825
268
  TALLOC_FREE(km);
826
268
out:
827
268
  TALLOC_FREE(tmp_ctx);
828
268
  return ret;
829
268
}
830
831
832
/**
833
 * @brief Convert a BCRYPT_RSAPUBLIC_BLOB public key into the Internal public key
834
 *        representation
835
 *
836
 * @param[in,out] ndr       ndr pull context
837
 * @param[in]     ndr_flags
838
 * @param[out]    kmi       the KeyMaterialInternal structure to populate
839
 *
840
 * @return NDR_ERR_SUCCESS if successful
841
 *         The contents of kmi are undefined on an error
842
 */
843
static enum ndr_err_code pull_BCRYPT_RSAPUBLIC_BLOB(
844
  struct ndr_pull *ndr,
845
  ndr_flags_type ndr_flags,
846
  struct KeyMaterialInternal *kmi)
847
253
{
848
253
  enum ndr_err_code ret = NDR_ERR_SUCCESS;
849
253
  struct BCRYPT_RSAPUBLIC_BLOB *km = NULL;
850
851
253
  TALLOC_CTX *tmp_ctx = talloc_new(ndr->current_mem_ctx);
852
253
  if (tmp_ctx == NULL) {
853
0
    return ndr_pull_error(
854
0
      ndr,
855
0
      NDR_ERR_ALLOC,
856
0
      "Unable to allocate temporary memory context");
857
0
  }
858
859
253
  km = talloc_zero(tmp_ctx, struct BCRYPT_RSAPUBLIC_BLOB);
860
253
  if (km == NULL) {
861
0
    ret = ndr_pull_error(ndr,
862
0
             NDR_ERR_ALLOC,
863
0
             "Unable to allocate BCRYPT_RSAPUBLIC_BLOB");
864
0
    goto out;
865
0
  }
866
867
253
  ret = ndr_pull_BCRYPT_RSAPUBLIC_BLOB(ndr, ndr_flags, km);
868
253
  if (ret != NDR_ERR_SUCCESS) {
869
243
    goto out_km;
870
243
  }
871
872
10
  kmi->bit_size = km->bit_length;
873
874
10
  kmi->modulus = data_blob_talloc(ndr->current_mem_ctx,
875
10
          km->modulus,
876
10
          km->modulus_len);
877
10
  if (kmi->modulus.data == NULL) {
878
0
    ret = ndr_pull_error(
879
0
      ndr,
880
0
      NDR_ERR_ALLOC,
881
0
      "Unable to allocate BCRYPT_RSAPUBLIC_BLOB modulus");
882
0
    goto out_km;
883
0
  }
884
885
10
  kmi->exponent = data_blob_talloc(ndr->current_mem_ctx,
886
10
           km->public_exponent,
887
10
           km->public_exponent_len);
888
10
  if (kmi->exponent.data == NULL) {
889
0
    ret = ndr_pull_error(
890
0
      ndr,
891
0
      NDR_ERR_ALLOC,
892
0
      "Unable to allocate BCRYPT_RSAPUBLIC_BLOB exponent");
893
0
    goto out_km;
894
0
  }
895
896
10
  ret = NDR_ERR_SUCCESS;
897
898
253
out_km:
899
253
  TALLOC_FREE(km);
900
253
out:
901
253
  TALLOC_FREE(tmp_ctx);
902
253
  return ret;
903
253
}
904
905
906
/**
907
 * @brief Convert a KeyMaterial blob into the Internal public key
908
 *        representation KeyMaterialInternal
909
 *
910
 * @param[in,out] ndr       ndr pull context
911
 * @param[in]     ndr_flags
912
 * @param[out]    kmi       the KeyMaterialInternal structure to populate
913
 *
914
 * @return NDR_ERR_SUCCESS if successful
915
 *         The contents of kmi are undefined on an error
916
 */
917
enum ndr_err_code ndr_pull_KeyMaterialInternal(struct ndr_pull *ndr,
918
                 ndr_flags_type ndr_flags,
919
                 struct KeyMaterialInternal *kmi)
920
985
{
921
985
  static const uint8_t BCRYPT_HEADER[] = {'R', 'S', 'A', '1'};
922
985
  static const uint8_t TPM20_HEADER[] = {'P', 'C', 'P', 'M'};
923
985
  static const uint32_t MIN_KEY_MATERIAL_SIZE = 5;
924
985
  static const uint32_t MAX_KEY_MATERIAL_SIZE = 64 * 1024;
925
926
985
  uint32_t size = 0;
927
928
985
  if (ndr->offset > ndr->data_size) {
929
0
    return ndr_pull_error(ndr,
930
0
              NDR_ERR_LENGTH,
931
0
              "ndr->offset (%" PRIu32
932
0
              ") is greater than "
933
0
              "ndr->data_size (%" PRIu32 ")",
934
0
              ndr->offset,
935
0
              ndr->data_size);
936
0
  }
937
985
  size = ndr->data_size - ndr->offset;
938
985
  if (size < MIN_KEY_MATERIAL_SIZE) {
939
5
    return ndr_pull_error(ndr,
940
5
              NDR_ERR_LENGTH,
941
5
              "KeyMaterial size of %" PRIu32
942
5
              " bytes is too small",
943
5
              size);
944
5
  }
945
980
  if (size > MAX_KEY_MATERIAL_SIZE) {
946
10
    return ndr_pull_error(ndr,
947
10
              NDR_ERR_LENGTH,
948
10
              "KeyMaterial size of %" PRIu32
949
10
              " bytes is too large",
950
10
              size);
951
10
  }
952
953
970
  if (memcmp(BCRYPT_HEADER, ndr->data, sizeof(BCRYPT_HEADER)) == 0) {
954
253
    return pull_BCRYPT_RSAPUBLIC_BLOB(ndr, ndr_flags, kmi);
955
717
  } else if (memcmp(TPM20_HEADER, ndr->data, sizeof(TPM20_HEADER)) == 0) {
956
268
    return pull_TPM20_RSAKEY_BLOB(ndr, ndr_flags, kmi);
957
449
  } else if (*ndr->data == ASN1_SEQUENCE(0)) {
958
    /*
959
     * If the first byte is an ASN1 sequence marker assume that
960
     * this is an x509 public key
961
     */
962
417
    return pull_DER_RSA_KEY(ndr, ndr_flags, kmi, size);
963
417
  } else {
964
32
    return ndr_pull_error(
965
32
      ndr,
966
32
      NDR_ERR_VALIDATE,
967
32
      "Unknown KeyMaterial type, could not be decoded");
968
32
  }
969
970
}
970
971
/**
972
 * @brief Push a representation of a KeyMaterialInternal onto the
973
 *        ndr_push context.
974
 *
975
 * @param[in,out] ndr       ndr push context
976
 * @param[in]     ndr_flags
977
 * @param[out]    kmi       the KeyMaterialInternal structure to populate
978
 *
979
 * @note This is not currently implemented and will always return
980
 *       NDR_ERR_VALIDATE
981
 *
982
 * @return NDR_ERR_VALIDATE
983
 *
984
 */
985
enum ndr_err_code ndr_push_KeyMaterialInternal(
986
  struct ndr_push *ndr,
987
  ndr_flags_type ndr_flags,
988
  const struct KeyMaterialInternal *kmi)
989
91
{
990
  return ndr_push_error(
991
91
    ndr,
992
91
    NDR_ERR_VALIDATE,
993
91
    "NDR Push for KeyMaterialInternal not currently supported");
994
91
}