Coverage Report

Created: 2025-07-23 07:04

/src/samba/third_party/heimdal/lib/krb5/pac.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2006 - 2017 Kungliga Tekniska Högskolan
3
 * (Royal Institute of Technology, Stockholm, Sweden).
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 *
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 *
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * 3. Neither the name of the Institute nor the names of its contributors
18
 *    may be used to endorse or promote products derived from this software
19
 *    without specific prior written permission.
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
 * SUCH DAMAGE.
32
 */
33
34
#include "krb5_locl.h"
35
36
#include <heimbasepriv.h>
37
#include <wind.h>
38
#include <assert.h>
39
40
/*
41
 * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/3341cfa2-6ef5-42e0-b7bc-4544884bf399
42
 */
43
struct PAC_INFO_BUFFER {
44
    uint32_t type;          /* ULONG   ulType       in the original */
45
    uint32_t buffersize;    /* ULONG   cbBufferSize in the original */
46
    uint64_t offset;        /* ULONG64 Offset       in the original
47
                             * this being the offset from the beginning of the
48
                             * struct PACTYPE to the beginning of the buffer
49
                             * containing data of type ulType
50
                             */
51
};
52
53
/*
54
 * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/6655b92f-ab06-490b-845d-037e6987275f
55
 */
56
struct PACTYPE {
57
    uint32_t numbuffers;    /* named cBuffers of type ULONG in the original */
58
    uint32_t version;       /* Named Version  of type ULONG in the original */
59
    struct PAC_INFO_BUFFER buffers[1]; /* an ellipsis (...) in the original */
60
};
61
62
/*
63
 * A PAC starts with a PACTYPE header structure that is followed by an array of
64
 * numbuffers PAC_INFO_BUFFER structures, each of which points to a buffer
65
 * beyond the last PAC_INFO_BUFFER structures.
66
 */
67
68
struct krb5_pac_data {
69
    struct PACTYPE *pac;
70
    krb5_data data;
71
    struct PAC_INFO_BUFFER *server_checksum;
72
    struct PAC_INFO_BUFFER *privsvr_checksum;
73
    struct PAC_INFO_BUFFER *logon_name;
74
    struct PAC_INFO_BUFFER *upn_dns_info;
75
    struct PAC_INFO_BUFFER *ticket_checksum;
76
    struct PAC_INFO_BUFFER *attributes_info;
77
    struct PAC_INFO_BUFFER *full_checksum;
78
    krb5_data ticket_sign_data;
79
80
    /* PAC_UPN_DNS_INFO */
81
    krb5_principal upn_princ;
82
    uint32_t upn_flags;
83
    krb5_principal canon_princ;
84
    krb5_data sid;
85
86
    /* PAC_ATTRIBUTES_INFO */
87
    uint64_t pac_attributes;
88
89
    krb5_boolean is_trusted;
90
};
91
92
0
#define PAC_ALIGNMENT     8
93
94
0
#define PACTYPE_SIZE      8
95
0
#define PAC_INFO_BUFFER_SIZE    16
96
97
#define PAC_LOGON_INFO      1
98
#define PAC_CREDENTIALS_INFO    2
99
0
#define PAC_SERVER_CHECKSUM   6
100
0
#define PAC_PRIVSVR_CHECKSUM    7
101
0
#define PAC_LOGON_NAME      10
102
#define PAC_CONSTRAINED_DELEGATION  11
103
0
#define PAC_UPN_DNS_INFO    12
104
0
#define PAC_TICKET_CHECKSUM   16
105
0
#define PAC_ATTRIBUTES_INFO   17
106
#define PAC_REQUESTOR_SID   18
107
0
#define PAC_FULL_CHECKSUM   19
108
109
/* Flag in PAC_UPN_DNS_INFO */
110
#define PAC_EXTRA_LOGON_INFO_FLAGS_UPN_DEFAULTED  0x1
111
0
#define PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID 0x2
112
113
#define CHECK(r,f,l)            \
114
0
  do {             \
115
0
    if (((r) = f ) != 0) {       \
116
0
      krb5_clear_error_message(context);  \
117
0
      goto l;         \
118
0
    }            \
119
0
  } while(0)
120
121
static const char zeros[PAC_ALIGNMENT];
122
123
static void HEIM_CALLCONV
124
pac_dealloc(void *ctx)
125
0
{
126
0
    krb5_pac pac = (krb5_pac)ctx;
127
128
0
    krb5_data_free(&pac->data);
129
0
    krb5_data_free(&pac->ticket_sign_data);
130
131
0
    if (pac->upn_princ) {
132
0
  free_Principal(pac->upn_princ);
133
0
  free(pac->upn_princ);
134
0
    }
135
0
    if (pac->canon_princ) {
136
0
  free_Principal(pac->canon_princ);
137
0
  free(pac->canon_princ);
138
0
    }
139
0
    krb5_data_free(&pac->sid);
140
141
0
    free(pac->pac);
142
0
}
143
144
static const struct heim_type_data pac_object = {
145
    HEIM_TID_PAC,
146
    "heim-pac",
147
    NULL,
148
    pac_dealloc,
149
    NULL,
150
    NULL,
151
    NULL,
152
    NULL
153
};
154
155
/*
156
 * Returns the size of the PACTYPE header + the PAC_INFO_BUFFER array.  This is
157
 * also the end of the whole thing, and any offsets to buffers from
158
 * the PAC_INFO_BUFFER[] entries have to be beyond it.
159
 */
160
static krb5_error_code
161
pac_header_size(krb5_context context, uint32_t num_buffers, uint32_t *result)
162
0
{
163
0
    krb5_error_code ret;
164
0
    uint32_t header_size;
165
166
    /* Guard against integer overflow */
167
0
    if (num_buffers > UINT32_MAX / PAC_INFO_BUFFER_SIZE) {
168
0
  ret = EOVERFLOW;
169
0
  krb5_set_error_message(context, ret, "PAC has too many buffers");
170
0
  return ret;
171
0
    }
172
0
    header_size = PAC_INFO_BUFFER_SIZE * num_buffers;
173
174
    /* Guard against integer overflow */
175
0
    if (header_size > UINT32_MAX - PACTYPE_SIZE) {
176
0
  ret = EOVERFLOW;
177
0
  krb5_set_error_message(context, ret, "PAC has too many buffers");
178
0
  return ret;
179
0
    }
180
0
    header_size += PACTYPE_SIZE;
181
182
0
    *result = header_size;
183
184
0
    return 0;
185
0
}
186
187
/* Output `size' + `addend' + padding for alignment if it doesn't overflow */
188
static krb5_error_code
189
pac_aligned_size(krb5_context context,
190
                 uint32_t size,
191
                 uint32_t addend,
192
                 uint32_t *aligned_size)
193
0
{
194
0
    krb5_error_code ret;
195
196
0
    if (size > UINT32_MAX - addend ||
197
0
        (size + addend) > UINT32_MAX - (PAC_ALIGNMENT - 1)) {
198
0
  ret = EOVERFLOW;
199
0
  krb5_set_error_message(context, ret, "integer overrun");
200
0
  return ret;
201
0
    }
202
0
    size += addend;
203
0
    size += PAC_ALIGNMENT - 1;
204
0
    size &= ~(PAC_ALIGNMENT - 1);
205
0
    *aligned_size = size;
206
0
    return 0;
207
0
}
208
209
/*
210
 * HMAC-MD5 checksum over any key (needed for the PAC routines)
211
 */
212
213
static krb5_error_code
214
HMAC_MD5_any_checksum(krb5_context context,
215
          const krb5_keyblock *key,
216
          const void *data,
217
          size_t len,
218
          unsigned usage,
219
          Checksum *result)
220
0
{
221
0
    struct _krb5_key_data local_key;
222
0
    struct krb5_crypto_iov iov;
223
0
    krb5_error_code ret;
224
225
0
    memset(&local_key, 0, sizeof(local_key));
226
227
0
    ret = krb5_copy_keyblock(context, key, &local_key.key);
228
0
    if (ret)
229
0
  return ret;
230
231
0
    ret = krb5_data_alloc (&result->checksum, 16);
232
0
    if (ret) {
233
0
  krb5_free_keyblock(context, local_key.key);
234
0
  return ret;
235
0
    }
236
237
0
    result->cksumtype = CKSUMTYPE_HMAC_MD5;
238
0
    iov.data.data = (void *)data;
239
0
    iov.data.length = len;
240
0
    iov.flags = KRB5_CRYPTO_TYPE_DATA;
241
242
0
    ret = _krb5_HMAC_MD5_checksum(context, NULL, &local_key, usage, &iov, 1,
243
0
                                  result);
244
0
    if (ret)
245
0
  krb5_data_free(&result->checksum);
246
247
0
    krb5_free_keyblock(context, local_key.key);
248
0
    return ret;
249
0
}
250
251
252
/*
253
 *
254
 */
255
256
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
257
krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
258
         krb5_pac *pac)
259
0
{
260
0
    krb5_error_code ret = 0;
261
0
    krb5_pac p;
262
0
    krb5_storage *sp = NULL;
263
0
    uint32_t i, num_buffers, version, header_size = 0;
264
0
    uint32_t prev_start = 0;
265
0
    uint32_t prev_end = 0;
266
267
0
    *pac = NULL;
268
0
    p = _heim_alloc_object(&pac_object, sizeof(*p));
269
0
    if (p)
270
0
        sp = krb5_storage_from_readonly_mem(ptr, len);
271
0
    if (sp == NULL)
272
0
  ret = krb5_enomem(context);
273
0
    if (ret == 0) {
274
0
        krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
275
0
        ret = krb5_ret_uint32(sp, &num_buffers);
276
0
    }
277
0
    if (ret == 0)
278
0
        ret = krb5_ret_uint32(sp, &version);
279
0
    if (ret == 0 && num_buffers < 1)
280
0
  krb5_set_error_message(context, ret = EINVAL,
281
0
                               N_("PAC has too few buffers", ""));
282
0
    if (ret == 0 && num_buffers > 1000)
283
0
  krb5_set_error_message(context, ret = EINVAL,
284
0
                               N_("PAC has too many buffers", ""));
285
0
    if (ret == 0 && version != 0)
286
0
  krb5_set_error_message(context, ret = EINVAL,
287
0
             N_("PAC has wrong version %d", ""),
288
0
             (int)version);
289
0
    if (ret == 0)
290
0
        ret = pac_header_size(context, num_buffers, &header_size);
291
0
    if (ret == 0 && header_size > len)
292
0
        krb5_set_error_message(context, ret = EOVERFLOW,
293
0
                               N_("PAC encoding invalid, would overflow buffers", ""));
294
0
    if (ret == 0)
295
0
        p->pac = calloc(1, header_size);
296
0
    if (ret == 0 && p->pac == NULL)
297
0
  ret = krb5_enomem(context);
298
299
0
    if (ret == 0) {
300
0
        p->pac->numbuffers = num_buffers;
301
0
        p->pac->version = version;
302
0
    }
303
304
0
    for (i = 0; ret == 0 && i < p->pac->numbuffers; i++) {
305
0
        ret = krb5_ret_uint32(sp, &p->pac->buffers[i].type);
306
0
        if (ret == 0)
307
0
            ret = krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize);
308
0
        if (ret == 0)
309
0
            ret = krb5_ret_uint64(sp, &p->pac->buffers[i].offset);
310
0
        if (ret)
311
0
            break;
312
313
  /* Consistency checks (we don't check for wasted space) */
314
0
  if (p->pac->buffers[i].offset & (PAC_ALIGNMENT - 1)) {
315
0
      krb5_set_error_message(context, ret = EINVAL,
316
0
           N_("PAC out of alignment", ""));
317
0
      break;
318
0
  }
319
0
  if (p->pac->buffers[i].offset > len ||
320
0
            p->pac->buffers[i].buffersize > len ||
321
0
            len - p->pac->buffers[i].offset < p->pac->buffers[i].buffersize) {
322
0
      krb5_set_error_message(context, ret = EOVERFLOW,
323
0
           N_("PAC buffer overflow", ""));
324
0
      break;
325
0
  }
326
0
  if (p->pac->buffers[i].offset < header_size) {
327
0
      krb5_set_error_message(context, ret = EINVAL,
328
0
           N_("PAC offset inside header: %lu %lu", ""),
329
0
           (unsigned long)p->pac->buffers[i].offset,
330
0
           (unsigned long)header_size);
331
0
      break;
332
0
  }
333
334
        /*
335
         * We'd like to check for non-overlapping of buffers, but the buffers
336
         * need not be in the same order as the PAC_INFO_BUFFER[] entries
337
         * pointing to them!  To fully check for overlap we'd have to have an
338
         * O(N^2) loop after we parse all the PAC_INFO_BUFFER[].
339
         *
340
         * But we can check that each buffer does not overlap the previous
341
         * buffer.
342
         */
343
0
        if (prev_start) {
344
0
            if (p->pac->buffers[i].offset >= prev_start &&
345
0
                p->pac->buffers[i].offset <  prev_end) {
346
0
                krb5_set_error_message(context, ret = EINVAL,
347
0
                                       N_("PAC overlap", ""));
348
0
                break;
349
0
            }
350
0
            if (p->pac->buffers[i].offset < prev_start &&
351
0
                p->pac->buffers[i].offset +
352
0
                p->pac->buffers[i].buffersize > prev_start) {
353
0
                krb5_set_error_message(context, ret = EINVAL,
354
0
                                       N_("PAC overlap", ""));
355
0
                break;
356
0
            }
357
0
        }
358
0
        prev_start = p->pac->buffers[i].offset;
359
0
        prev_end = p->pac->buffers[i].offset + p->pac->buffers[i].buffersize;
360
361
  /* Let's save pointers to buffers we'll need later */
362
0
        switch (p->pac->buffers[i].type) {
363
0
        case PAC_SERVER_CHECKSUM:
364
0
      if (p->server_checksum)
365
0
    krb5_set_error_message(context, ret = EINVAL,
366
0
               N_("PAC has multiple server checksums", ""));
367
0
      else
368
0
                p->server_checksum = &p->pac->buffers[i];
369
0
            break;
370
0
        case PAC_PRIVSVR_CHECKSUM:
371
0
      if (p->privsvr_checksum)
372
0
                krb5_set_error_message(context, ret = EINVAL,
373
0
                                       N_("PAC has multiple KDC checksums", ""));
374
0
            else
375
0
                p->privsvr_checksum = &p->pac->buffers[i];
376
0
            break;
377
0
        case PAC_LOGON_NAME:
378
0
      if (p->logon_name)
379
0
    krb5_set_error_message(context, ret = EINVAL,
380
0
                                       N_("PAC has multiple logon names", ""));
381
0
            else
382
0
                p->logon_name = &p->pac->buffers[i];
383
0
            break;
384
0
        case PAC_UPN_DNS_INFO:
385
0
      if (p->upn_dns_info)
386
0
    krb5_set_error_message(context, ret = EINVAL,
387
0
               N_("PAC has multiple UPN DNS info buffers", ""));
388
0
            else
389
0
                p->upn_dns_info = &p->pac->buffers[i];
390
0
            break;
391
0
        case PAC_TICKET_CHECKSUM:
392
0
      if (p->ticket_checksum)
393
0
    krb5_set_error_message(context, ret = EINVAL,
394
0
               N_("PAC has multiple ticket checksums", ""));
395
0
            else
396
0
                p->ticket_checksum = &p->pac->buffers[i];
397
0
            break;
398
0
        case PAC_ATTRIBUTES_INFO:
399
0
      if (p->attributes_info)
400
0
    krb5_set_error_message(context, ret = EINVAL,
401
0
               N_("PAC has multiple attributes info buffers", ""));
402
0
            else
403
0
                p->attributes_info = &p->pac->buffers[i];
404
0
            break;
405
0
        case PAC_FULL_CHECKSUM:
406
0
      if (p->full_checksum)
407
0
    krb5_set_error_message(context, ret = EINVAL,
408
0
               N_("PAC has multiple full checksums", ""));
409
0
            else
410
0
                p->full_checksum = &p->pac->buffers[i];
411
0
            break;
412
0
        default: break;
413
0
        }
414
0
    }
415
416
0
    if (ret == 0)
417
0
        ret = krb5_data_copy(&p->data, ptr, len);
418
0
    if (ret == 0) {
419
0
        *pac = p;
420
0
        p = NULL;
421
0
    }
422
0
    if (sp)
423
0
        krb5_storage_free(sp);
424
0
    krb5_pac_free(context, p);
425
0
    return ret;
426
0
}
427
428
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
429
krb5_pac_init(krb5_context context, krb5_pac *pac)
430
0
{
431
0
    krb5_error_code ret;
432
0
    krb5_pac p;
433
434
0
    p = _heim_alloc_object(&pac_object, sizeof(*p));
435
0
    if (p == NULL) {
436
0
  return krb5_enomem(context);
437
0
    }
438
439
0
    p->pac = calloc(1, sizeof(*p->pac));
440
0
    if (p->pac == NULL) {
441
0
  krb5_pac_free(context, p);
442
0
  return krb5_enomem(context);
443
0
    }
444
445
0
    ret = krb5_data_alloc(&p->data, PACTYPE_SIZE);
446
0
    if (ret) {
447
0
  free (p->pac);
448
0
  krb5_pac_free(context, p);
449
0
  return krb5_enomem(context);
450
0
    }
451
0
    memset(p->data.data, 0, p->data.length);
452
453
0
    *pac = p;
454
0
    return 0;
455
0
}
456
457
/**
458
 * Add a PAC buffer `nd' of type `type' to the pac `p'.
459
 *
460
 * @param context
461
 * @param p
462
 * @param type
463
 * @param nd
464
 *
465
 * @return 0 on success or a Kerberos or system error.
466
 */
467
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
468
krb5_pac_add_buffer(krb5_context context, krb5_pac p,
469
        uint32_t type, const krb5_data *nd)
470
0
{
471
0
    krb5_error_code ret;
472
0
    void *ptr;
473
0
    size_t old_len = p->data.length;
474
0
    uint32_t len, offset, header_size;
475
0
    uint32_t i;
476
0
    uint32_t num_buffers;
477
478
0
    assert(nd->data != NULL);
479
480
0
    num_buffers = p->pac->numbuffers;
481
0
    ret = pac_header_size(context, num_buffers + 1, &header_size);
482
0
    if (ret)
483
0
  return ret;
484
485
0
    ptr = realloc(p->pac, header_size);
486
0
    if (ptr == NULL)
487
0
  return krb5_enomem(context);
488
489
0
    p->pac = ptr;
490
0
    p->pac->buffers[num_buffers].type = 0;
491
0
    p->pac->buffers[num_buffers].buffersize = 0;
492
0
    p->pac->buffers[num_buffers].offset = 0;
493
494
    /*
495
     * Check that we can adjust all the buffer offsets in the existing
496
     * PAC_INFO_BUFFERs, since changing the size of PAC_INFO_BUFFER[] means
497
     * changing the offsets of buffers following that array.
498
     *
499
     * We don't adjust them until we can't fail.
500
     */
501
0
    for (i = 0; i < num_buffers; i++) {
502
0
  if (p->pac->buffers[i].offset > UINT32_MAX - PAC_INFO_BUFFER_SIZE) {
503
0
      krb5_set_error_message(context, ret = EOVERFLOW,
504
0
                                   "too many / too large PAC buffers");
505
0
      return ret;
506
0
  }
507
0
    }
508
509
    /*
510
     * The new buffer's offset must be past the end of the buffers we have
511
     * (p->data), which is the sum of the header and p->data.length.
512
     */
513
514
    /* Set offset = p->data.length + PAC_INFO_BUFFER_SIZE + alignment */
515
0
    ret = pac_aligned_size(context, p->data.length, PAC_INFO_BUFFER_SIZE, &offset);
516
0
    if (ret == 0)
517
        /* Set the new length = offset + nd->length + alignment */
518
0
        ret = pac_aligned_size(context, offset, nd->length, &len);
519
0
    if (ret) {
520
0
  krb5_set_error_message(context, ret, "PAC buffer too large");
521
0
        return ret;
522
0
    }
523
0
    ret = krb5_data_realloc(&p->data, len);
524
0
    if (ret) {
525
0
  krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
526
0
  return ret;
527
0
    }
528
529
    /* Zero out the new allocation to zero out any padding */
530
0
    memset((char *)p->data.data + old_len, 0, len - old_len);
531
532
0
    p->pac->buffers[num_buffers].type = type;
533
0
    p->pac->buffers[num_buffers].buffersize = nd->length;
534
0
    p->pac->buffers[num_buffers].offset = offset;
535
536
    /* Adjust all the buffer offsets in the existing PAC_INFO_BUFFERs now */
537
0
    for (i = 0; i < num_buffers; i++)
538
0
  p->pac->buffers[i].offset += PAC_INFO_BUFFER_SIZE;
539
540
    /*
541
     * Make place for new PAC INFO BUFFER header
542
     */
543
0
    header_size -= PAC_INFO_BUFFER_SIZE;
544
0
    memmove((unsigned char *)p->data.data + header_size + PAC_INFO_BUFFER_SIZE,
545
0
      (unsigned char *)p->data.data + header_size ,
546
0
      old_len - header_size);
547
    /* Clear the space where we would put the new PAC_INFO_BUFFER[] element */
548
0
    memset((unsigned char *)p->data.data + header_size, 0,
549
0
           PAC_INFO_BUFFER_SIZE);
550
551
    /*
552
     * Copy in new data part
553
     */
554
0
    memcpy((unsigned char *)p->data.data + offset, nd->data, nd->length);
555
0
    p->pac->numbuffers += 1;
556
0
    return 0;
557
0
}
558
559
/**
560
 * Get the PAC buffer of specific type from the pac.
561
 *
562
 * @param context Kerberos 5 context.
563
 * @param p the pac structure returned by krb5_pac_parse().
564
 * @param type type of buffer to get
565
 * @param data return data, free with krb5_data_free().
566
 *
567
 * @return Returns 0 to indicate success, ENOENT to indicate that a buffer of
568
 * the given type was not found, or a Kerberos or system error code.
569
 *
570
 * @ingroup krb5_pac
571
 */
572
573
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
574
krb5_pac_get_buffer(krb5_context context, krb5_const_pac p,
575
        uint32_t type, krb5_data *data)
576
0
{
577
0
    krb5_error_code ret;
578
0
    uint32_t i;
579
580
0
    for (i = 0; i < p->pac->numbuffers; i++) {
581
0
  size_t len = p->pac->buffers[i].buffersize;
582
0
  size_t offset = p->pac->buffers[i].offset;
583
584
0
  if (p->pac->buffers[i].type != type)
585
0
      continue;
586
587
0
  if (!data)
588
0
            return 0;
589
590
0
        ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
591
0
        if (ret)
592
0
            krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
593
0
  return ret;
594
0
    }
595
0
    krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found",
596
0
         (unsigned long)type);
597
0
    return ENOENT;
598
0
}
599
600
static const struct {
601
    uint32_t type;
602
    krb5_data name;
603
} pac_buffer_name_map[] = {
604
#define PAC_MAP_ENTRY(type, name) { PAC_##type, { sizeof(name) - 1, name } }
605
    PAC_MAP_ENTRY(LOGON_INFO,       "logon-info"  ),
606
    PAC_MAP_ENTRY(CREDENTIALS_INFO,     "credentials-info"  ),
607
    PAC_MAP_ENTRY(SERVER_CHECKSUM,      "server-checksum"   ),
608
    PAC_MAP_ENTRY(PRIVSVR_CHECKSUM,     "privsvr-checksum"  ),
609
    PAC_MAP_ENTRY(LOGON_NAME,       "client-info" ),
610
    PAC_MAP_ENTRY(CONSTRAINED_DELEGATION,   "delegation-info"   ),
611
    PAC_MAP_ENTRY(UPN_DNS_INFO,       "upn-dns-info"  ),
612
    PAC_MAP_ENTRY(TICKET_CHECKSUM,      "ticket-checksum"   ),
613
    PAC_MAP_ENTRY(ATTRIBUTES_INFO,      "attributes-info"   ),
614
    PAC_MAP_ENTRY(REQUESTOR_SID,      "requestor-sid" )
615
};
616
617
/*
618
 *
619
 */
620
621
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
622
_krb5_pac_get_buffer_by_name(krb5_context context, krb5_const_pac p,
623
           const krb5_data *name, krb5_data *data)
624
0
{
625
0
    size_t i;
626
627
0
    for (i = 0;
628
0
   i < sizeof(pac_buffer_name_map) / sizeof(pac_buffer_name_map[0]);
629
0
   i++) {
630
0
  if (krb5_data_cmp(name, &pac_buffer_name_map[i].name) == 0)
631
0
      return krb5_pac_get_buffer(context, p, pac_buffer_name_map[i].type, data);
632
0
    }
633
634
0
    krb5_set_error_message(context, ENOENT, "No PAC buffer with name %.*s was found",
635
0
         (int)name->length, (char *)name->data);
636
0
    return ENOENT;
637
0
}
638
639
/*
640
 *
641
 */
642
643
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
644
krb5_pac_get_types(krb5_context context,
645
       krb5_const_pac p,
646
       size_t *len,
647
       uint32_t **types)
648
0
{
649
0
    size_t i;
650
651
0
    *types = calloc(p->pac->numbuffers, sizeof(**types));
652
0
    if (*types == NULL) {
653
0
  *len = 0;
654
0
  return krb5_enomem(context);
655
0
    }
656
0
    for (i = 0; i < p->pac->numbuffers; i++)
657
0
  (*types)[i] = p->pac->buffers[i].type;
658
0
    *len = p->pac->numbuffers;
659
660
0
    return 0;
661
0
}
662
663
/*
664
 *
665
 */
666
667
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
668
krb5_pac_is_trusted(krb5_const_pac p)
669
0
{
670
0
    return p->is_trusted;
671
0
}
672
673
/*
674
 *
675
 */
676
677
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
678
krb5_pac_set_trusted(krb5_pac p, krb5_boolean is_trusted)
679
0
{
680
0
    p->is_trusted = is_trusted;
681
0
}
682
683
/*
684
 *
685
 */
686
687
/*
688
 *
689
 */
690
691
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
692
krb5_pac_free(krb5_context context, krb5_pac pac)
693
0
{
694
0
    heim_release(pac);
695
0
}
696
697
/*
698
 *
699
 */
700
701
static krb5_error_code
702
verify_checksum(krb5_context context,
703
    const struct PAC_INFO_BUFFER *sig,
704
    const krb5_data *data,
705
    void *ptr, size_t len,
706
    const krb5_keyblock *key,
707
    krb5_boolean strict_cksumtype_match)
708
0
{
709
0
    krb5_storage *sp = NULL;
710
0
    uint32_t type;
711
0
    krb5_error_code ret;
712
0
    Checksum cksum;
713
0
    size_t cksumsize;
714
715
0
    memset(&cksum, 0, sizeof(cksum));
716
717
0
    sp = krb5_storage_from_mem((char *)data->data + sig->offset,
718
0
             sig->buffersize);
719
0
    if (sp == NULL)
720
0
  return krb5_enomem(context);
721
722
0
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
723
724
0
    CHECK(ret, krb5_ret_uint32(sp, &type), out);
725
0
    cksum.cksumtype = type;
726
727
0
    ret = krb5_checksumsize(context, type, &cksumsize);
728
0
    if (ret)
729
0
  goto out;
730
731
    /* Allow for RODCIdentifier trailer, see MS-PAC 2.8 */
732
0
    if (cksumsize > (sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR))) {
733
0
  ret = EINVAL;
734
0
  goto out;
735
0
    }
736
0
    cksum.checksum.length = cksumsize;
737
0
    cksum.checksum.data = malloc(cksum.checksum.length);
738
0
    if (cksum.checksum.data == NULL) {
739
0
  ret = krb5_enomem(context);
740
0
  goto out;
741
0
    }
742
0
    ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length);
743
0
    if (ret != (int)cksum.checksum.length) {
744
0
  ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
745
0
  krb5_set_error_message(context, ret, "PAC checksum missing checksum");
746
0
  goto out;
747
0
    }
748
749
0
    if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) {
750
0
  ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
751
0
  krb5_set_error_message(context, ret, "Checksum type %d not keyed",
752
0
             cksum.cksumtype);
753
0
  goto out;
754
0
    }
755
756
    /* If the checksum is HMAC-MD5, the checksum type is not tied to
757
     * the key type, instead the HMAC-MD5 checksum is applied blindly
758
     * on whatever key is used for this connection, avoiding issues
759
     * with unkeyed checksums on des-cbc-md5 and des-cbc-crc.  See
760
     * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743
761
     * for the same issue in MIT, and
762
     * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx
763
     * for Microsoft's explanation */
764
765
0
    if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5 && !strict_cksumtype_match) {
766
0
  Checksum local_checksum;
767
768
0
  memset(&local_checksum, 0, sizeof(local_checksum));
769
770
0
  ret = HMAC_MD5_any_checksum(context, key, ptr, len,
771
0
            KRB5_KU_OTHER_CKSUM, &local_checksum);
772
773
0
  if (ret != 0 || krb5_data_ct_cmp(&local_checksum.checksum, &cksum.checksum) != 0) {
774
0
      ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
775
0
      krb5_set_error_message(context, ret,
776
0
           N_("PAC integrity check failed for "
777
0
              "hmac-md5 checksum", ""));
778
0
  }
779
0
  krb5_data_free(&local_checksum.checksum);
780
781
0
   } else {
782
0
  krb5_crypto crypto = NULL;
783
784
0
  ret = krb5_crypto_init(context, key, 0, &crypto);
785
0
  if (ret)
786
0
    goto out;
787
788
0
  ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM,
789
0
           ptr, len, &cksum);
790
0
  krb5_crypto_destroy(context, crypto);
791
0
    }
792
0
    free(cksum.checksum.data);
793
0
    krb5_storage_free(sp);
794
795
0
    return ret;
796
797
0
out:
798
0
    if (cksum.checksum.data)
799
0
  free(cksum.checksum.data);
800
0
    if (sp)
801
0
  krb5_storage_free(sp);
802
0
    return ret;
803
0
}
804
805
static krb5_error_code
806
create_checksum(krb5_context context,
807
    const krb5_keyblock *key,
808
    uint32_t cksumtype,
809
    void *data, size_t datalen,
810
    void *sig, size_t siglen)
811
0
{
812
0
    krb5_crypto crypto = NULL;
813
0
    krb5_error_code ret;
814
0
    Checksum cksum;
815
816
    /* If the checksum is HMAC-MD5, the checksum type is not tied to
817
     * the key type, instead the HMAC-MD5 checksum is applied blindly
818
     * on whatever key is used for this connection, avoiding issues
819
     * with unkeyed checksums on des-cbc-md5 and des-cbc-crc.  See
820
     * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743
821
     * for the same issue in MIT, and
822
     * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx
823
     * for Microsoft's explaination */
824
825
0
    if (cksumtype == (uint32_t)CKSUMTYPE_HMAC_MD5) {
826
0
  ret = HMAC_MD5_any_checksum(context, key, data, datalen,
827
0
            KRB5_KU_OTHER_CKSUM, &cksum);
828
0
        if (ret)
829
0
            return ret;
830
0
    } else {
831
0
  ret = krb5_crypto_init(context, key, 0, &crypto);
832
0
  if (ret)
833
0
      return ret;
834
835
0
  ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0,
836
0
           data, datalen, &cksum);
837
0
  krb5_crypto_destroy(context, crypto);
838
0
  if (ret)
839
0
      return ret;
840
0
    }
841
0
    if (cksum.checksum.length != siglen) {
842
0
  krb5_set_error_message(context, EINVAL, "pac checksum wrong length");
843
0
  free_Checksum(&cksum);
844
0
  return EINVAL;
845
0
    }
846
847
0
    memcpy(sig, cksum.checksum.data, siglen);
848
0
    free_Checksum(&cksum);
849
850
0
    return 0;
851
0
}
852
853
static krb5_error_code
854
parse_upn_dns_info(krb5_context context,
855
       const struct PAC_INFO_BUFFER *upndnsinfo,
856
       const krb5_data *data,
857
       krb5_principal *upn_princ,
858
       uint32_t *flags,
859
       krb5_principal *canon_princ,
860
       krb5_data *sid)
861
0
{
862
0
    krb5_error_code ret;
863
0
    krb5_storage *sp = NULL;
864
0
    uint16_t upn_length, upn_offset;
865
0
    uint16_t dns_domain_name_length, dns_domain_name_offset;
866
0
    uint16_t canon_princ_length, canon_princ_offset;
867
0
    uint16_t sid_length, sid_offset;
868
0
    char *upn = NULL;
869
0
    char *dns_domain_name = NULL;
870
0
    char *sam_name = NULL;
871
872
0
    *upn_princ = NULL;
873
0
    *flags = 0;
874
0
    *canon_princ = NULL;
875
0
    krb5_data_zero(sid);
876
877
0
    sp = krb5_storage_from_readonly_mem((const char *)data->data + upndnsinfo->offset,
878
0
          upndnsinfo->buffersize);
879
0
    if (sp == NULL)
880
0
  return krb5_enomem(context);
881
882
0
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
883
884
0
    CHECK(ret, krb5_ret_uint16(sp, &upn_length), out);
885
0
    CHECK(ret, krb5_ret_uint16(sp, &upn_offset), out);
886
0
    CHECK(ret, krb5_ret_uint16(sp, &dns_domain_name_length), out);
887
0
    CHECK(ret, krb5_ret_uint16(sp, &dns_domain_name_offset), out);
888
0
    CHECK(ret, krb5_ret_uint32(sp, flags), out);
889
890
0
    if (*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) {
891
0
  CHECK(ret, krb5_ret_uint16(sp, &canon_princ_length), out);
892
0
  CHECK(ret, krb5_ret_uint16(sp, &canon_princ_offset), out);
893
0
  CHECK(ret, krb5_ret_uint16(sp, &sid_length), out);
894
0
  CHECK(ret, krb5_ret_uint16(sp, &sid_offset), out);
895
0
    } else {
896
0
  canon_princ_length = canon_princ_offset = 0;
897
0
  sid_length = sid_offset = 0;
898
0
    }
899
900
0
    if (upn_offset) {
901
0
  CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, upn_offset,
902
0
              upn_length, &upn), out);
903
0
    }
904
0
    CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, dns_domain_name_offset,
905
0
                dns_domain_name_length, &dns_domain_name), out);
906
0
    if ((*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) && canon_princ_offset) {
907
0
  CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, canon_princ_offset,
908
0
              canon_princ_length, &sam_name), out);
909
0
    }
910
911
0
    if (upn_offset) {
912
0
  ret = krb5_parse_name_flags(context,
913
0
            upn,
914
0
            KRB5_PRINCIPAL_PARSE_ENTERPRISE |
915
0
            KRB5_PRINCIPAL_PARSE_NO_DEF_REALM,
916
0
            upn_princ);
917
0
  if (ret)
918
0
      goto out;
919
920
0
  ret = krb5_principal_set_realm(context, *upn_princ, dns_domain_name);
921
0
  if (ret)
922
0
      goto out;
923
0
    }
924
925
0
    if (canon_princ_offset) {
926
0
  ret = krb5_parse_name_flags(context,
927
0
            sam_name,
928
0
            KRB5_PRINCIPAL_PARSE_NO_REALM |
929
0
            KRB5_PRINCIPAL_PARSE_NO_DEF_REALM,
930
0
            canon_princ);
931
0
  if (ret)
932
0
      goto out;
933
934
0
  ret = krb5_principal_set_realm(context, *canon_princ, dns_domain_name);
935
0
  if (ret)
936
0
      goto out;
937
0
    }
938
939
0
    if (sid_offset)
940
0
  CHECK(ret, _krb5_ret_data_at_offset(sp, sid_offset, sid_length, sid), out);
941
942
0
out:
943
0
    free(upn);
944
0
    free(dns_domain_name);
945
0
    free(sam_name);
946
947
0
    krb5_storage_free(sp);
948
949
0
    return ret;
950
0
}
951
952
/*
953
 *
954
 */
955
956
0
#define NTTIME_EPOCH 0x019DB1DED53E8000LL
957
958
static uint64_t
959
unix2nttime(time_t unix_time)
960
0
{
961
0
    long long wt;
962
0
    wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
963
0
    return wt;
964
0
}
965
966
static krb5_error_code
967
verify_logonname(krb5_context context,
968
     const struct PAC_INFO_BUFFER *logon_name,
969
     const krb5_data *data,
970
     time_t authtime,
971
     krb5_const_principal principal)
972
0
{
973
0
    krb5_error_code ret;
974
0
    uint32_t time1, time2;
975
0
    krb5_storage *sp = NULL;
976
0
    uint16_t len;
977
0
    char *s = NULL;
978
0
    char *principal_string = NULL;
979
0
    char *logon_string = NULL;
980
981
0
    sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset,
982
0
          logon_name->buffersize);
983
0
    if (sp == NULL)
984
0
  return krb5_enomem(context);
985
986
0
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
987
988
0
    CHECK(ret, krb5_ret_uint32(sp, &time1), out);
989
0
    CHECK(ret, krb5_ret_uint32(sp, &time2), out);
990
991
0
    {
992
0
  uint64_t t1, t2;
993
0
  t1 = unix2nttime(authtime);
994
0
  t2 = ((uint64_t)time2 << 32) | time1;
995
  /*
996
   * When neither the ticket nor the PAC set an explicit authtime,
997
   * both times are zero, but relative to different time scales.
998
   * So we must compare "not set" values without converting to a
999
   * common time reference.
1000
         */
1001
0
  if (t1 != t2 && (t2 != 0 && authtime != 0)) {
1002
0
      krb5_storage_free(sp);
1003
0
      krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch");
1004
0
      return EINVAL;
1005
0
  }
1006
0
    }
1007
0
    CHECK(ret, krb5_ret_uint16(sp, &len), out);
1008
0
    if (len == 0) {
1009
0
  krb5_storage_free(sp);
1010
0
  krb5_set_error_message(context, EINVAL, "PAC logon name length missing");
1011
0
  return EINVAL;
1012
0
    }
1013
1014
0
    s = malloc(len);
1015
0
    if (s == NULL) {
1016
0
  krb5_storage_free(sp);
1017
0
  return krb5_enomem(context);
1018
0
    }
1019
0
    ret = krb5_storage_read(sp, s, len);
1020
0
    if (ret != len) {
1021
0
  free(s);
1022
0
  krb5_storage_free(sp);
1023
0
  krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name");
1024
0
  return EINVAL;
1025
0
    }
1026
0
    krb5_storage_free(sp);
1027
0
    {
1028
0
  size_t ucs2len = len / 2;
1029
0
  uint16_t *ucs2;
1030
0
  size_t u8len;
1031
0
  unsigned int flags = WIND_RW_LE;
1032
1033
0
  ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
1034
0
  if (ucs2 == NULL) {
1035
0
      free(s);
1036
0
      return krb5_enomem(context);
1037
0
  }
1038
1039
0
  ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
1040
0
  free(s);
1041
0
  if (ret) {
1042
0
      free(ucs2);
1043
0
      krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
1044
0
      return ret;
1045
0
  }
1046
0
  ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
1047
0
  if (ret) {
1048
0
      free(ucs2);
1049
0
      krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string");
1050
0
      return ret;
1051
0
  }
1052
0
  u8len += 1; /* Add space for NUL */
1053
0
  logon_string = malloc(u8len);
1054
0
  if (logon_string == NULL) {
1055
0
      free(ucs2);
1056
0
      return krb5_enomem(context);
1057
0
  }
1058
0
  ret = wind_ucs2utf8(ucs2, ucs2len, logon_string, &u8len);
1059
0
  free(ucs2);
1060
0
  if (ret) {
1061
0
      free(logon_string);
1062
0
      krb5_set_error_message(context, ret, "Failed to convert to UTF-8");
1063
0
      return ret;
1064
0
  }
1065
0
    }
1066
0
    ret = krb5_unparse_name_flags(context, principal,
1067
0
          KRB5_PRINCIPAL_UNPARSE_NO_REALM |
1068
0
          KRB5_PRINCIPAL_UNPARSE_DISPLAY,
1069
0
          &principal_string);
1070
0
    if (ret) {
1071
0
  free(logon_string);
1072
0
  return ret;
1073
0
    }
1074
1075
0
    if (strcmp(logon_string, principal_string) != 0) {
1076
0
  ret = EINVAL;
1077
0
  krb5_set_error_message(context, ret, "PAC logon name [%s] mismatch principal name [%s]",
1078
0
             logon_string, principal_string);
1079
0
    }
1080
0
    free(logon_string);
1081
0
    free(principal_string);
1082
0
    return ret;
1083
0
out:
1084
0
    krb5_storage_free(sp);
1085
0
    return ret;
1086
0
}
1087
1088
/*
1089
 *
1090
 */
1091
1092
static krb5_error_code
1093
build_logon_name(krb5_context context,
1094
     time_t authtime,
1095
     krb5_const_principal principal,
1096
     krb5_data *logon)
1097
0
{
1098
0
    krb5_error_code ret;
1099
0
    krb5_storage *sp;
1100
0
    uint64_t t;
1101
0
    char *s, *s2 = NULL;
1102
0
    size_t s2_len;
1103
1104
0
    t = unix2nttime(authtime);
1105
1106
0
    krb5_data_zero(logon);
1107
1108
0
    sp = krb5_storage_emem();
1109
0
    if (sp == NULL)
1110
0
  return krb5_enomem(context);
1111
1112
0
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1113
1114
0
    CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out);
1115
0
    CHECK(ret, krb5_store_uint32(sp, t >> 32), out);
1116
1117
0
    ret = krb5_unparse_name_flags(context, principal,
1118
0
          KRB5_PRINCIPAL_UNPARSE_NO_REALM |
1119
0
          KRB5_PRINCIPAL_UNPARSE_DISPLAY,
1120
0
          &s);
1121
0
    if (ret)
1122
0
  goto out;
1123
1124
0
    {
1125
0
  size_t ucs2_len;
1126
0
  uint16_t *ucs2;
1127
0
  unsigned int flags;
1128
1129
0
  ret = wind_utf8ucs2_length(s, &ucs2_len);
1130
0
  if (ret) {
1131
0
      krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s);
1132
0
      free(s);
1133
0
      return ret;
1134
0
  }
1135
1136
0
  ucs2 = malloc(sizeof(ucs2[0]) * ucs2_len);
1137
0
  if (ucs2 == NULL) {
1138
0
      free(s);
1139
0
      return krb5_enomem(context);
1140
0
  }
1141
1142
0
  ret = wind_utf8ucs2(s, ucs2, &ucs2_len);
1143
0
  if (ret) {
1144
0
      free(ucs2);
1145
0
      krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s);
1146
0
      free(s);
1147
0
      return ret;
1148
0
  } else
1149
0
      free(s);
1150
1151
0
  s2_len = (ucs2_len + 1) * 2;
1152
0
  s2 = malloc(s2_len);
1153
0
  if (s2 == NULL) {
1154
0
      free(ucs2);
1155
0
      return krb5_enomem(context);
1156
0
  }
1157
1158
0
  flags = WIND_RW_LE;
1159
0
  ret = wind_ucs2write(ucs2, ucs2_len,
1160
0
           &flags, s2, &s2_len);
1161
0
  free(ucs2);
1162
0
  if (ret) {
1163
0
      free(s2);
1164
0
      krb5_set_error_message(context, ret, "Failed to write to UCS-2 buffer");
1165
0
      return ret;
1166
0
  }
1167
1168
  /*
1169
   * we do not want zero termination
1170
   */
1171
0
  s2_len = ucs2_len * 2;
1172
0
    }
1173
1174
0
    CHECK(ret, krb5_store_uint16(sp, s2_len), out);
1175
1176
0
    ret = krb5_storage_write(sp, s2, s2_len);
1177
0
    if (ret != (int)s2_len) {
1178
0
  ret = krb5_enomem(context);
1179
0
  goto out;
1180
0
    }
1181
0
    ret = krb5_storage_to_data(sp, logon);
1182
1183
0
 out:
1184
0
    free(s2);
1185
0
    krb5_storage_free(sp);
1186
0
    return ret;
1187
0
}
1188
1189
static krb5_error_code
1190
parse_attributes_info(krb5_context context,
1191
          const struct PAC_INFO_BUFFER *attributes_info,
1192
          const krb5_data *data,
1193
          uint64_t *pac_attributes)
1194
0
{
1195
0
    krb5_error_code ret;
1196
0
    krb5_storage *sp = NULL;
1197
0
    uint32_t flags_length;
1198
1199
0
    *pac_attributes = 0;
1200
1201
0
    sp = krb5_storage_from_readonly_mem((const char *)data->data + attributes_info->offset,
1202
0
          attributes_info->buffersize);
1203
0
    if (sp == NULL)
1204
0
  return krb5_enomem(context);
1205
1206
0
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1207
1208
0
    ret = krb5_ret_uint32(sp, &flags_length);
1209
0
    if (ret == 0) {
1210
0
  if (flags_length > 32)
1211
0
      ret = krb5_ret_uint64(sp, pac_attributes);
1212
0
  else {
1213
0
      uint32_t pac_attributes32 = 0;
1214
0
      ret = krb5_ret_uint32(sp, &pac_attributes32);
1215
0
      *pac_attributes = pac_attributes32;
1216
0
  }
1217
0
    }
1218
1219
0
    krb5_storage_free(sp);
1220
1221
0
    return ret;
1222
0
}
1223
1224
/**
1225
 * Verify the PAC.
1226
 *
1227
 * @param context Kerberos 5 context.
1228
 * @param pac the pac structure returned by krb5_pac_parse().
1229
 * @param authtime The time of the ticket the PAC belongs to.
1230
 * @param principal the principal to verify.
1231
 * @param server The service key, may be given.
1232
 * @param privsvr The KDC key, may be given.
1233
1234
 * @return Returns 0 to indicate success. Otherwise an kerberos et
1235
 * error code is returned, see krb5_get_error_message().
1236
 *
1237
 * @ingroup krb5_pac
1238
 */
1239
1240
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1241
krb5_pac_verify(krb5_context context,
1242
    const krb5_pac pac,
1243
    time_t authtime,
1244
    krb5_const_principal principal,
1245
    const krb5_keyblock *server,
1246
    const krb5_keyblock *privsvr)
1247
0
{
1248
0
    krb5_error_code ret;
1249
    /*
1250
     * If we are in the KDC, we expect back a full signature in the PAC
1251
     *
1252
     * This is set up as a separate variable to make it easier if a
1253
     * subsequent patch is added to make this configurable in the
1254
     * krb5.conf (or forced into the krb5_context via Samba)
1255
     */
1256
0
    krb5_boolean expect_full_sig = privsvr != NULL;
1257
1258
    /*
1259
     * If we are on the KDC, then we trust we are not in a realm with
1260
     * buggy Windows 2008 or similar era DCs that give out HMAC-MD5
1261
     * signatures over AES keys.  DES is also already gone.
1262
     */
1263
0
    krb5_boolean strict_cksumtype_match = expect_full_sig;
1264
1265
0
    if (pac->server_checksum == NULL) {
1266
0
  krb5_set_error_message(context, EINVAL, "PAC missing server checksum");
1267
0
  return EINVAL;
1268
0
    }
1269
0
    if (pac->privsvr_checksum == NULL) {
1270
0
  krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum");
1271
0
  return EINVAL;
1272
0
    }
1273
0
    if (pac->logon_name == NULL) {
1274
0
  krb5_set_error_message(context, EINVAL, "PAC missing logon name");
1275
0
  return EINVAL;
1276
0
    }
1277
0
    if (expect_full_sig && pac->full_checksum == NULL) {
1278
0
  krb5_set_error_message(context, EINVAL, "PAC missing full checksum");
1279
0
  return EINVAL;
1280
0
    }
1281
1282
0
    if (principal != NULL) {
1283
0
  ret = verify_logonname(context, pac->logon_name, &pac->data, authtime,
1284
0
             principal);
1285
0
  if (ret)
1286
0
      return ret;
1287
0
    }
1288
1289
0
    if (pac->server_checksum->buffersize < 4 ||
1290
0
        pac->privsvr_checksum->buffersize < 4)
1291
0
  return EINVAL;
1292
1293
0
    if (server != NULL || privsvr != NULL)
1294
0
    {
1295
0
  krb5_data *copy;
1296
1297
  /*
1298
   * in the service case, clean out data option of the privsvr and
1299
   * server checksum before checking the checksum.
1300
   */
1301
1302
0
  ret = krb5_copy_data(context, &pac->data, &copy);
1303
0
  if (ret)
1304
0
      return ret;
1305
1306
0
  memset((char *)copy->data + pac->server_checksum->offset + 4,
1307
0
         0,
1308
0
         pac->server_checksum->buffersize - 4);
1309
1310
0
  memset((char *)copy->data + pac->privsvr_checksum->offset + 4,
1311
0
         0,
1312
0
         pac->privsvr_checksum->buffersize - 4);
1313
1314
0
  if (server != NULL) {
1315
0
      ret = verify_checksum(context,
1316
0
          pac->server_checksum,
1317
0
          &pac->data,
1318
0
          copy->data,
1319
0
          copy->length,
1320
0
          server,
1321
0
          strict_cksumtype_match);
1322
0
      if (ret) {
1323
0
    krb5_free_data(context, copy);
1324
0
    return ret;
1325
0
      }
1326
0
  }
1327
1328
0
  if (privsvr != NULL && pac->full_checksum != NULL) {
1329
      /*
1330
       * in the full checksum case, also clean out the full
1331
       * checksum before verifying it.
1332
       */
1333
0
      memset((char *)copy->data + pac->full_checksum->offset + 4,
1334
0
       0,
1335
0
       pac->full_checksum->buffersize - 4);
1336
1337
0
      ret = verify_checksum(context,
1338
0
          pac->full_checksum,
1339
0
          &pac->data,
1340
0
          copy->data,
1341
0
          copy->length,
1342
0
          privsvr,
1343
0
          strict_cksumtype_match);
1344
0
      if (ret) {
1345
0
    krb5_free_data(context, copy);
1346
0
    return ret;
1347
0
      }
1348
0
  }
1349
1350
0
  krb5_free_data(context, copy);
1351
0
    }
1352
0
    if (privsvr) {
1353
  /* The priv checksum covers the server checksum */
1354
0
  ret = verify_checksum(context,
1355
0
            pac->privsvr_checksum,
1356
0
            &pac->data,
1357
0
            (char *)pac->data.data
1358
0
            + pac->server_checksum->offset + 4,
1359
0
            pac->server_checksum->buffersize - 4,
1360
0
            privsvr,
1361
0
            strict_cksumtype_match);
1362
0
  if (ret)
1363
0
      return ret;
1364
1365
0
  if (pac->ticket_sign_data.length != 0) {
1366
0
      if (pac->ticket_checksum == NULL) {
1367
0
    krb5_set_error_message(context, EINVAL,
1368
0
               "PAC missing ticket checksum");
1369
0
    return EINVAL;
1370
0
      }
1371
1372
0
      ret = verify_checksum(context, pac->ticket_checksum, &pac->data,
1373
0
         pac->ticket_sign_data.data,
1374
0
         pac->ticket_sign_data.length, privsvr,
1375
0
         strict_cksumtype_match);
1376
0
      if (ret)
1377
0
    return ret;
1378
0
  }
1379
0
    }
1380
1381
0
    if (pac->upn_dns_info &&
1382
0
  pac->upn_princ == NULL && pac->canon_princ == NULL && pac->sid.data == NULL) {
1383
0
  ret = parse_upn_dns_info(context, pac->upn_dns_info, &pac->data,
1384
0
         &pac->upn_princ, &pac->upn_flags,
1385
0
         &pac->canon_princ, &pac->sid);
1386
0
  if (ret)
1387
0
      return ret;
1388
0
    }
1389
1390
0
    if (pac->attributes_info) {
1391
0
  ret = parse_attributes_info(context, pac->attributes_info, &pac->data,
1392
0
            &pac->pac_attributes);
1393
0
  if (ret)
1394
0
      return ret;
1395
0
    }
1396
1397
0
    return 0;
1398
0
}
1399
1400
/*
1401
 *
1402
 */
1403
1404
static krb5_error_code
1405
fill_zeros(krb5_context context, krb5_storage *sp, size_t len)
1406
0
{
1407
0
    ssize_t sret;
1408
0
    size_t l;
1409
1410
0
    while (len) {
1411
0
  l = len;
1412
0
  if (l > sizeof(zeros))
1413
0
      l = sizeof(zeros);
1414
0
  sret = krb5_storage_write(sp, zeros, l);
1415
0
  if (sret != l)
1416
0
      return krb5_enomem(context);
1417
1418
0
  len -= sret;
1419
0
    }
1420
0
    return 0;
1421
0
}
1422
1423
static krb5_error_code
1424
pac_checksum(krb5_context context,
1425
       const krb5_keyblock *key,
1426
       uint32_t *cksumtype,
1427
       size_t *cksumsize)
1428
0
{
1429
0
    krb5_cksumtype cktype;
1430
0
    krb5_error_code ret;
1431
0
    krb5_crypto crypto = NULL;
1432
1433
0
    ret = krb5_crypto_init(context, key, 0, &crypto);
1434
0
    if (ret)
1435
0
  return ret;
1436
1437
0
    ret = krb5_crypto_get_checksum_type(context, crypto, &cktype);
1438
0
    krb5_crypto_destroy(context, crypto);
1439
0
    if (ret)
1440
0
  return ret;
1441
1442
0
    if (krb5_checksum_is_keyed(context, cktype) == FALSE) {
1443
0
  *cksumtype = CKSUMTYPE_HMAC_MD5;
1444
0
  *cksumsize = 16;
1445
0
    }
1446
1447
0
    ret = krb5_checksumsize(context, cktype, cksumsize);
1448
0
    if (ret)
1449
0
  return ret;
1450
1451
0
    *cksumtype = (uint32_t)cktype;
1452
1453
0
    return 0;
1454
0
}
1455
1456
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1457
_krb5_pac_sign(krb5_context context,
1458
         krb5_pac p,
1459
         time_t authtime,
1460
         krb5_const_principal principal,
1461
         const krb5_keyblock *server_key,
1462
         const krb5_keyblock *priv_key,
1463
         uint16_t rodc_id,
1464
         krb5_const_principal upn_princ,
1465
         krb5_const_principal canon_princ,
1466
         krb5_boolean add_full_sig,
1467
         uint64_t *pac_attributes, /* optional */
1468
         krb5_data *data)
1469
0
{
1470
0
    krb5_error_code ret;
1471
0
    krb5_storage *sp = NULL, *spdata = NULL;
1472
0
    uint32_t end;
1473
0
    size_t server_size, priv_size;
1474
0
    uint32_t server_offset = 0, priv_offset = 0, ticket_offset = 0, full_offset = 0;
1475
0
    uint32_t server_cksumtype = 0, priv_cksumtype = 0;
1476
0
    uint32_t num = 0;
1477
0
    uint32_t i, sz;
1478
0
    krb5_data logon, d;
1479
1480
0
    krb5_data_zero(&d);
1481
0
    krb5_data_zero(&logon);
1482
1483
    /*
1484
     * Set convenience buffer pointers.
1485
     *
1486
     * This could really stand to be moved to krb5_pac_add_buffer() and/or
1487
     * utility function, so that when this function gets called they must
1488
     * already have been set.
1489
     */
1490
0
    for (i = 0; i < p->pac->numbuffers; i++) {
1491
0
  if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
1492
0
      if (p->server_checksum == NULL) {
1493
0
    p->server_checksum = &p->pac->buffers[i];
1494
0
      }
1495
0
      if (p->server_checksum != &p->pac->buffers[i]) {
1496
0
    ret = KRB5KDC_ERR_BADOPTION;
1497
0
    krb5_set_error_message(context, ret,
1498
0
               N_("PAC has multiple server checksums", ""));
1499
0
    goto out;
1500
0
      }
1501
0
  } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
1502
0
      if (p->privsvr_checksum == NULL) {
1503
0
    p->privsvr_checksum = &p->pac->buffers[i];
1504
0
      }
1505
0
      if (p->privsvr_checksum != &p->pac->buffers[i]) {
1506
0
    ret = KRB5KDC_ERR_BADOPTION;
1507
0
    krb5_set_error_message(context, ret,
1508
0
               N_("PAC has multiple KDC checksums", ""));
1509
0
    goto out;
1510
0
      }
1511
0
  } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
1512
0
      if (p->logon_name == NULL) {
1513
0
    p->logon_name = &p->pac->buffers[i];
1514
0
      }
1515
0
      if (p->logon_name != &p->pac->buffers[i]) {
1516
0
    ret = KRB5KDC_ERR_BADOPTION;
1517
0
    krb5_set_error_message(context, ret,
1518
0
               N_("PAC has multiple logon names", ""));
1519
0
    goto out;
1520
0
      }
1521
0
  } else if (p->pac->buffers[i].type == PAC_UPN_DNS_INFO) {
1522
0
      if (p->upn_dns_info == NULL) {
1523
0
    p->upn_dns_info = &p->pac->buffers[i];
1524
0
      }
1525
0
      if (p->upn_dns_info != &p->pac->buffers[i]) {
1526
0
    ret = KRB5KDC_ERR_BADOPTION;
1527
0
    krb5_set_error_message(context, ret,
1528
0
               N_("PAC has multiple UPN DNS info buffers", ""));
1529
0
    goto out;
1530
0
      }
1531
0
  } else if (p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) {
1532
0
      if (p->ticket_checksum == NULL) {
1533
0
    p->ticket_checksum = &p->pac->buffers[i];
1534
0
      }
1535
0
      if (p->ticket_checksum != &p->pac->buffers[i]) {
1536
0
    ret = KRB5KDC_ERR_BADOPTION;
1537
0
    krb5_set_error_message(context, ret,
1538
0
               N_("PAC has multiple ticket checksums", ""));
1539
0
    goto out;
1540
0
      }
1541
0
  } else if (p->pac->buffers[i].type == PAC_ATTRIBUTES_INFO) {
1542
0
      if (p->attributes_info == NULL) {
1543
0
    p->attributes_info = &p->pac->buffers[i];
1544
0
      }
1545
0
      if (p->attributes_info != &p->pac->buffers[i]) {
1546
0
    ret = KRB5KDC_ERR_BADOPTION;
1547
0
    krb5_set_error_message(context, ret,
1548
0
               N_("PAC has multiple attributes info buffers", ""));
1549
0
    goto out;
1550
0
      }
1551
0
  } else if (p->pac->buffers[i].type == PAC_FULL_CHECKSUM) {
1552
0
      if (p->full_checksum == NULL) {
1553
0
    p->full_checksum = &p->pac->buffers[i];
1554
0
      }
1555
0
      if (p->full_checksum != &p->pac->buffers[i]) {
1556
0
    ret = KRB5KDC_ERR_BADOPTION;
1557
0
    krb5_set_error_message(context, ret,
1558
0
               N_("PAC has multiple full checksums", ""));
1559
0
    goto out;
1560
0
      }
1561
0
  }
1562
0
    }
1563
1564
    /* Count missing-but-necessary buffers */
1565
0
    if (p->logon_name == NULL)
1566
0
  num++;
1567
0
    if (p->server_checksum == NULL)
1568
0
  num++;
1569
0
    if (p->privsvr_checksum == NULL)
1570
0
  num++;
1571
0
    if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL)
1572
0
  num++;
1573
0
    if (add_full_sig && p->full_checksum == NULL)
1574
0
  num++;
1575
1576
    /* Allocate any missing-but-necessary buffers */
1577
0
    if (num) {
1578
0
  void *ptr;
1579
0
  uint32_t old_len, len;
1580
1581
0
  if (p->pac->numbuffers > UINT32_MAX - num) {
1582
0
      ret = EINVAL;
1583
0
      krb5_set_error_message(context, ret, "integer overrun");
1584
0
      goto out;
1585
0
  }
1586
0
  ret = pac_header_size(context, p->pac->numbuffers, &old_len);
1587
0
        if (ret == 0)
1588
0
            ret = pac_header_size(context, p->pac->numbuffers + num, &len);
1589
0
  if (ret)
1590
0
      goto out;
1591
1592
0
  ptr = realloc(p->pac, len);
1593
0
  if (ptr == NULL) {
1594
0
      ret = krb5_enomem(context);
1595
0
            goto out;
1596
0
        }
1597
0
        memset((char *)ptr + old_len, 0, len - old_len);
1598
0
  p->pac = ptr;
1599
1600
1601
0
  if (p->logon_name == NULL) {
1602
0
      p->logon_name = &p->pac->buffers[p->pac->numbuffers++];
1603
0
      p->logon_name->type = PAC_LOGON_NAME;
1604
0
  }
1605
0
  if (p->server_checksum == NULL) {
1606
0
      p->server_checksum = &p->pac->buffers[p->pac->numbuffers++];
1607
0
      p->server_checksum->type = PAC_SERVER_CHECKSUM;
1608
0
  }
1609
0
  if (p->privsvr_checksum == NULL) {
1610
0
      p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++];
1611
0
      p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM;
1612
0
  }
1613
0
  if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) {
1614
0
      p->ticket_checksum = &p->pac->buffers[p->pac->numbuffers++];
1615
0
      p->ticket_checksum->type = PAC_TICKET_CHECKSUM;
1616
0
  }
1617
0
  if (add_full_sig && p->full_checksum == NULL) {
1618
0
      p->full_checksum = &p->pac->buffers[p->pac->numbuffers++];
1619
0
      memset(p->full_checksum, 0, sizeof(*p->full_checksum));
1620
0
      p->full_checksum->type = PAC_FULL_CHECKSUM;
1621
0
  }
1622
0
    }
1623
1624
    /* Calculate LOGON NAME */
1625
0
    ret = build_logon_name(context, authtime, principal, &logon);
1626
1627
    /* Set lengths for checksum */
1628
0
    if (ret == 0)
1629
0
        ret = pac_checksum(context, server_key, &server_cksumtype, &server_size);
1630
1631
0
    if (ret == 0)
1632
0
        ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size);
1633
1634
    /* Encode PAC */
1635
0
    if (ret == 0) {
1636
0
        sp = krb5_storage_emem();
1637
0
        if (sp == NULL)
1638
0
            ret = krb5_enomem(context);
1639
0
    }
1640
1641
0
    if (ret == 0) {
1642
0
        krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1643
0
        spdata = krb5_storage_emem();
1644
0
        if (spdata == NULL)
1645
0
            ret = krb5_enomem(context);
1646
0
    }
1647
1648
0
    if (ret)
1649
0
        goto out;
1650
1651
0
    krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE);
1652
1653
    /* `sp' has the header, `spdata' has the buffers */
1654
0
    CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out);
1655
0
    CHECK(ret, krb5_store_uint32(sp, p->pac->version), out);
1656
1657
0
    ret = pac_header_size(context, p->pac->numbuffers, &end);
1658
0
    if (ret)
1659
0
        goto out;
1660
1661
    /*
1662
     * For each buffer we write its contents to `spdata' and then append the
1663
     * PAC_INFO_BUFFER for that buffer into the header in `sp'.  The logical
1664
     * end of the whole thing is kept in `end', which functions as the offset
1665
     * to write in the buffer's PAC_INFO_BUFFER, then we update it at the
1666
     * bottom so that the next buffer can be written there.
1667
     *
1668
     * TODO?  Maybe rewrite all of this so that:
1669
     *
1670
     *  - we use krb5_pac_add_buffer() to add the buffers we produce
1671
     *  - we use the krb5_data of the concatenated buffers that's maintained by
1672
     *    krb5_pac_add_buffer() so we don't need `spdata' here
1673
     *
1674
     * We do way too much here, and that makes this code hard to read.  Plus we
1675
     * throw away all the work done in krb5_pac_add_buffer().  On the other
1676
     * hand, krb5_pac_add_buffer() has to loop over all the buffers, so if we
1677
     * call krb5_pac_add_buffer() here in a loop, we'll be accidentally
1678
     * quadratic, but we only need to loop over adding the buffers we add,
1679
     * which is very few, so not quite quadratic.  We should also cap the
1680
     * number of buffers we're willing to accept in a PAC we parse to something
1681
     * reasonable, like a few tens.
1682
     */
1683
0
    for (i = 0; i < p->pac->numbuffers; i++) {
1684
0
  uint32_t len;
1685
0
  size_t sret;
1686
0
  void *ptr = NULL;
1687
1688
  /* store data */
1689
1690
0
  if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
1691
0
      if (server_size > UINT32_MAX - 4) {
1692
0
    ret = EINVAL;
1693
0
    krb5_set_error_message(context, ret, "integer overrun");
1694
0
    goto out;
1695
0
      }
1696
0
      len = server_size + 4;
1697
0
      if (end > UINT32_MAX - 4) {
1698
0
    ret = EINVAL;
1699
0
    krb5_set_error_message(context, ret, "integer overrun");
1700
0
    goto out;
1701
0
      }
1702
0
      server_offset = end + 4;
1703
0
      CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out);
1704
0
      CHECK(ret, fill_zeros(context, spdata, server_size), out);
1705
0
  } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
1706
0
      if (priv_size > UINT32_MAX - 4) {
1707
0
    ret = EINVAL;
1708
0
    krb5_set_error_message(context, ret, "integer overrun");
1709
0
    goto out;
1710
0
      }
1711
0
      len = priv_size + 4;
1712
0
      if (end > UINT32_MAX - 4) {
1713
0
    ret = EINVAL;
1714
0
    krb5_set_error_message(context, ret, "integer overrun");
1715
0
    goto out;
1716
0
      }
1717
0
      priv_offset = end + 4;
1718
0
      CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
1719
0
      CHECK(ret, fill_zeros(context, spdata, priv_size), out);
1720
0
      if (rodc_id != 0) {
1721
0
    if (len > UINT32_MAX - sizeof(rodc_id)) {
1722
0
        ret = EINVAL;
1723
0
        krb5_set_error_message(context, ret, "integer overrun");
1724
0
        goto out;
1725
0
    }
1726
0
    len += sizeof(rodc_id);
1727
0
    CHECK(ret, fill_zeros(context, spdata, sizeof(rodc_id)), out);
1728
0
      }
1729
0
  } else if (p->ticket_sign_data.length != 0 &&
1730
0
       p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) {
1731
0
      if (priv_size > UINT32_MAX - 4) {
1732
0
    ret = EINVAL;
1733
0
    krb5_set_error_message(context, ret, "integer overrun");
1734
0
    goto out;
1735
0
      }
1736
0
      len = priv_size + 4;
1737
0
      if (end > UINT32_MAX - 4) {
1738
0
    ret = EINVAL;
1739
0
    krb5_set_error_message(context, ret, "integer overrun");
1740
0
    goto out;
1741
0
      }
1742
0
      ticket_offset = end + 4;
1743
0
      CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
1744
0
      CHECK(ret, fill_zeros(context, spdata, priv_size), out);
1745
0
      if (rodc_id != 0) {
1746
0
    if (len > UINT32_MAX - sizeof(rodc_id)) {
1747
0
        ret = EINVAL;
1748
0
        krb5_set_error_message(context, ret, "integer overrun");
1749
0
        goto out;
1750
0
    }
1751
0
    len += sizeof(rodc_id);
1752
0
    CHECK(ret, krb5_store_uint16(spdata, rodc_id), out);
1753
0
      }
1754
0
  } else if (add_full_sig &&
1755
0
       p->pac->buffers[i].type == PAC_FULL_CHECKSUM) {
1756
0
      if (priv_size > UINT32_MAX - 4) {
1757
0
    ret = EINVAL;
1758
0
    krb5_set_error_message(context, ret, "integer overrun");
1759
0
    goto out;
1760
0
      }
1761
0
      len = priv_size + 4;
1762
0
      if (end > UINT32_MAX - 4) {
1763
0
    ret = EINVAL;
1764
0
    krb5_set_error_message(context, ret, "integer overrun");
1765
0
    goto out;
1766
0
      }
1767
0
      full_offset = end + 4;
1768
0
      CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
1769
0
      CHECK(ret, fill_zeros(context, spdata, priv_size), out);
1770
0
      if (rodc_id != 0) {
1771
0
    if (len > UINT32_MAX - sizeof(rodc_id)) {
1772
0
        ret = EINVAL;
1773
0
        krb5_set_error_message(context, ret, "integer overrun");
1774
0
        goto out;
1775
0
    }
1776
0
    len += sizeof(rodc_id);
1777
0
    CHECK(ret, fill_zeros(context, spdata, sizeof(rodc_id)), out);
1778
0
      }
1779
0
  } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
1780
0
      len = krb5_storage_write(spdata, logon.data, logon.length);
1781
0
      if (logon.length != len) {
1782
0
    ret = KRB5KDC_ERR_BADOPTION;
1783
0
    goto out;
1784
0
      }
1785
0
  } else {
1786
0
      len = p->pac->buffers[i].buffersize;
1787
0
      ptr = (char *)p->data.data + p->pac->buffers[i].offset;
1788
1789
0
      sret = krb5_storage_write(spdata, ptr, len);
1790
0
      if (sret != len) {
1791
0
    ret = krb5_enomem(context);
1792
0
    goto out;
1793
0
      }
1794
      /* XXX if not aligned, fill_zeros */
1795
0
  }
1796
1797
  /* write header */
1798
0
  CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out);
1799
0
  CHECK(ret, krb5_store_uint32(sp, len), out);
1800
0
  CHECK(ret, krb5_store_uint64(sp, end), out); /* offset */
1801
1802
  /* advance data endpointer and align */
1803
0
  {
1804
0
      uint32_t e;
1805
1806
0
      ret = pac_aligned_size(context, end, len, &e);
1807
0
            if (ret == 0 && end + len != e)
1808
0
                ret = fill_zeros(context, spdata, e - (end + len));
1809
0
      if (ret)
1810
0
    goto out;
1811
0
      end = e;
1812
0
  }
1813
1814
0
    }
1815
1816
    /* assert (server_offset != 0 && priv_offset != 0); */
1817
1818
    /* export PAC */
1819
0
    if (ret == 0)
1820
0
        ret = krb5_storage_to_data(spdata, &d);
1821
0
    if (ret == 0) {
1822
0
        sz = krb5_storage_write(sp, d.data, d.length);
1823
0
        if (sz != d.length) {
1824
0
            krb5_data_free(&d);
1825
0
            ret = krb5_enomem(context);
1826
0
            goto out;
1827
0
        }
1828
0
    }
1829
0
    krb5_data_free(&d);
1830
1831
0
    if (ret == 0)
1832
0
        ret = krb5_storage_to_data(sp, &d);
1833
1834
    /* sign */
1835
0
    if (ret == 0 && p->ticket_sign_data.length)
1836
0
  ret = create_checksum(context, priv_key, priv_cksumtype,
1837
0
            p->ticket_sign_data.data,
1838
0
            p->ticket_sign_data.length,
1839
0
            (char *)d.data + ticket_offset, priv_size);
1840
0
    if (ret == 0 && add_full_sig)
1841
0
        ret = create_checksum(context, priv_key, priv_cksumtype,
1842
0
                              d.data, d.length,
1843
0
                              (char *)d.data + full_offset, priv_size);
1844
0
    if (ret == 0 && add_full_sig && rodc_id != 0) {
1845
0
  void *buf = (char *)d.data + full_offset + priv_size;
1846
0
  krb5_storage *rs = krb5_storage_from_mem(buf, sizeof(rodc_id));
1847
0
  if (rs == NULL)
1848
0
      ret = krb5_enomem(context);
1849
0
        else
1850
0
            krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE);
1851
0
        if (ret == 0)
1852
0
            ret = krb5_store_uint16(rs, rodc_id);
1853
0
  krb5_storage_free(rs);
1854
0
    }
1855
0
    if (ret == 0)
1856
0
        ret = create_checksum(context, server_key, server_cksumtype,
1857
0
                              d.data, d.length,
1858
0
                              (char *)d.data + server_offset, server_size);
1859
0
    if (ret == 0)
1860
0
        ret = create_checksum(context, priv_key, priv_cksumtype,
1861
0
                              (char *)d.data + server_offset, server_size,
1862
0
                              (char *)d.data + priv_offset, priv_size);
1863
0
    if (ret == 0 && rodc_id != 0) {
1864
0
  void *buf = (char *)d.data + priv_offset + priv_size;
1865
0
  krb5_storage *rs = krb5_storage_from_mem(buf, sizeof(rodc_id));
1866
0
  if (rs == NULL)
1867
0
      ret = krb5_enomem(context);
1868
0
        else
1869
0
            krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE);
1870
0
        if (ret == 0)
1871
0
            ret = krb5_store_uint16(rs, rodc_id);
1872
0
  krb5_storage_free(rs);
1873
0
    }
1874
1875
0
    if (ret)
1876
0
        goto out;
1877
1878
    /* done */
1879
0
    *data = d;
1880
1881
0
    krb5_data_free(&logon);
1882
0
    krb5_storage_free(sp);
1883
0
    krb5_storage_free(spdata);
1884
1885
0
    return 0;
1886
0
out:
1887
0
    krb5_data_free(&d);
1888
0
    krb5_data_free(&logon);
1889
0
    if (sp)
1890
0
  krb5_storage_free(sp);
1891
0
    if (spdata)
1892
0
  krb5_storage_free(spdata);
1893
0
    return ret;
1894
0
}
1895
1896
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1897
krb5_pac_get_kdc_checksum_info(krb5_context context,
1898
             krb5_const_pac pac,
1899
             krb5_cksumtype *cstype,
1900
             uint16_t *rodc_id)
1901
0
{
1902
0
    krb5_error_code ret;
1903
0
    krb5_storage *sp = NULL;
1904
0
    const struct PAC_INFO_BUFFER *sig;
1905
0
    size_t cksumsize, prefix;
1906
0
    uint32_t type = 0;
1907
1908
0
    *cstype = 0;
1909
0
    *rodc_id = 0;
1910
1911
0
    sig = pac->privsvr_checksum;
1912
0
    if (sig == NULL) {
1913
0
  krb5_set_error_message(context, KRB5KDC_ERR_BADOPTION,
1914
0
             "PAC missing kdc checksum");
1915
0
  return KRB5KDC_ERR_BADOPTION;
1916
0
    }
1917
1918
0
    sp = krb5_storage_from_mem((char *)pac->data.data + sig->offset,
1919
0
             sig->buffersize);
1920
0
    if (sp == NULL)
1921
0
  return krb5_enomem(context);
1922
1923
0
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1924
1925
0
    ret = krb5_ret_uint32(sp, &type);
1926
0
    if (ret)
1927
0
  goto out;
1928
1929
0
    ret = krb5_checksumsize(context, type, &cksumsize);
1930
0
    if (ret)
1931
0
  goto out;
1932
1933
0
    prefix = krb5_storage_seek(sp, 0, SEEK_CUR);
1934
1935
0
    if ((sig->buffersize - prefix) >= cksumsize + 2) {
1936
0
  krb5_storage_seek(sp, cksumsize, SEEK_CUR);
1937
0
  ret = krb5_ret_uint16(sp, rodc_id);
1938
0
  if (ret)
1939
0
      goto out;
1940
0
    }
1941
1942
0
    *cstype = type;
1943
1944
0
out:
1945
0
    krb5_storage_free(sp);
1946
1947
0
    return ret;
1948
0
}
1949
1950
1951
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1952
_krb5_pac_get_canon_principal(krb5_context context,
1953
            krb5_const_pac pac,
1954
            krb5_principal *canon_princ)
1955
0
{
1956
0
    *canon_princ = NULL;
1957
1958
0
    if (pac->canon_princ == NULL) {
1959
0
  krb5_set_error_message(context, ENOENT,
1960
0
             "PAC missing UPN DNS info buffer");
1961
0
  return ENOENT;
1962
0
    }
1963
1964
0
    return krb5_copy_principal(context, pac->canon_princ, canon_princ);
1965
0
}
1966
1967
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1968
_krb5_pac_get_attributes_info(krb5_context context,
1969
            krb5_const_pac pac,
1970
            uint64_t *pac_attributes)
1971
0
{
1972
0
    *pac_attributes = 0;
1973
1974
0
    if (pac->attributes_info == NULL) {
1975
0
  krb5_set_error_message(context, ENOENT,
1976
0
             "PAC missing attributes info buffer");
1977
0
  return ENOENT;
1978
0
    }
1979
1980
0
    *pac_attributes = pac->pac_attributes;
1981
1982
0
    return 0;
1983
0
}
1984
1985
static const unsigned char single_zero = '\0';
1986
static const krb5_data single_zero_pac = { 1, rk_UNCONST(&single_zero) };
1987
1988
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1989
_krb5_kdc_pac_ticket_parse(krb5_context context,
1990
         EncTicketPart *tkt,
1991
         krb5_boolean *signedticket,
1992
         krb5_pac *ppac)
1993
0
{
1994
0
    AuthorizationData *ad = tkt->authorization_data;
1995
0
    krb5_pac pac = NULL;
1996
0
    unsigned i, j;
1997
0
    size_t len = 0;
1998
0
    krb5_error_code ret = 0;
1999
2000
0
    *signedticket = FALSE;
2001
0
    *ppac = NULL;
2002
2003
0
    if (ad == NULL || ad->len == 0)
2004
0
  return 0;
2005
2006
0
    for (i = 0; i < ad->len; i++) {
2007
0
  AuthorizationData child;
2008
2009
0
  if (ad->val[i].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
2010
0
      ret = KRB5KDC_ERR_BADOPTION;
2011
0
      goto out;
2012
0
  }
2013
2014
0
  if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
2015
0
      continue;
2016
2017
0
  ret = decode_AuthorizationData(ad->val[i].ad_data.data,
2018
0
               ad->val[i].ad_data.length,
2019
0
               &child,
2020
0
               NULL);
2021
0
  if (ret) {
2022
0
      krb5_set_error_message(context, ret, "Failed to decode "
2023
0
           "AD-IF-RELEVANT with %d", ret);
2024
0
      goto out;
2025
0
  }
2026
2027
0
  for (j = 0; j < child.len; j++) {
2028
0
      krb5_data adifr_data = ad->val[i].ad_data;
2029
0
      krb5_data pac_data = child.val[j].ad_data;
2030
0
      krb5_data recoded_adifr;
2031
2032
0
      if (child.val[j].ad_type != KRB5_AUTHDATA_WIN2K_PAC)
2033
0
    continue;
2034
2035
0
      if (pac != NULL) {
2036
0
    free_AuthorizationData(&child);
2037
0
    ret = KRB5KDC_ERR_BADOPTION;
2038
0
    goto out;
2039
0
      }
2040
2041
0
      ret = krb5_pac_parse(context,
2042
0
         pac_data.data,
2043
0
         pac_data.length,
2044
0
         &pac);
2045
0
      if (ret) {
2046
0
    free_AuthorizationData(&child);
2047
0
    goto out;
2048
0
      }
2049
2050
0
      if (pac->ticket_checksum == NULL)
2051
0
    continue;
2052
2053
      /*
2054
       * Encode the ticket with the PAC replaced with a single zero
2055
       * byte, to be used as input data to the ticket signature.
2056
       */
2057
2058
0
      child.val[j].ad_data = single_zero_pac;
2059
2060
0
      ASN1_MALLOC_ENCODE(AuthorizationData, recoded_adifr.data,
2061
0
             recoded_adifr.length, &child, &len, ret);
2062
0
      if (recoded_adifr.length != len)
2063
0
    krb5_abortx(context, "Internal error in ASN.1 encoder");
2064
2065
0
      child.val[j].ad_data = pac_data;
2066
2067
0
      if (ret) {
2068
0
    free_AuthorizationData(&child);
2069
0
    goto out;
2070
0
      }
2071
2072
0
      ad->val[i].ad_data = recoded_adifr;
2073
2074
0
      ASN1_MALLOC_ENCODE(EncTicketPart,
2075
0
             pac->ticket_sign_data.data,
2076
0
             pac->ticket_sign_data.length, tkt, &len,
2077
0
             ret);
2078
0
      if (pac->ticket_sign_data.length != len)
2079
0
    krb5_abortx(context, "Internal error in ASN.1 encoder");
2080
2081
0
      ad->val[i].ad_data = adifr_data;
2082
0
      krb5_data_free(&recoded_adifr);
2083
2084
0
      if (ret) {
2085
0
    free_AuthorizationData(&child);
2086
0
    goto out;
2087
0
      }
2088
2089
0
      *signedticket = TRUE;
2090
0
  }
2091
0
  free_AuthorizationData(&child);
2092
0
    }
2093
2094
0
out:
2095
0
    if (ret) {
2096
0
  krb5_pac_free(context, pac);
2097
0
  return ret;
2098
0
    }
2099
2100
0
    *ppac = pac;
2101
2102
0
    return 0;
2103
0
}
2104
2105
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2106
_krb5_kdc_pac_sign_ticket(krb5_context context,
2107
        const krb5_pac pac,
2108
        krb5_const_principal client,
2109
        const krb5_keyblock *server_key,
2110
        const krb5_keyblock *kdc_key,
2111
        uint16_t rodc_id,
2112
        krb5_const_principal upn,
2113
        krb5_const_principal canon_name,
2114
        krb5_boolean add_ticket_sig,
2115
        krb5_boolean add_full_sig,
2116
        EncTicketPart *tkt,
2117
        uint64_t *pac_attributes) /* optional */
2118
0
{
2119
0
    krb5_error_code ret;
2120
0
    krb5_data tkt_data;
2121
0
    krb5_data rspac;
2122
2123
0
    krb5_data_zero(&rspac);
2124
0
    krb5_data_zero(&tkt_data);
2125
2126
0
    krb5_data_free(&pac->ticket_sign_data);
2127
2128
0
    if (add_ticket_sig) {
2129
0
  size_t len = 0;
2130
2131
0
  ret = _kdc_tkt_insert_pac(context, tkt, &single_zero_pac);
2132
0
  if (ret)
2133
0
      return ret;
2134
2135
0
  ASN1_MALLOC_ENCODE(EncTicketPart, tkt_data.data, tkt_data.length,
2136
0
         tkt, &len, ret);
2137
0
  if(tkt_data.length != len)
2138
0
      krb5_abortx(context, "Internal error in ASN.1 encoder");
2139
0
  if (ret)
2140
0
      return ret;
2141
2142
0
  ret = remove_AuthorizationData(tkt->authorization_data, 0);
2143
0
  if (ret) {
2144
0
      krb5_data_free(&tkt_data);
2145
0
      return ret;
2146
0
  }
2147
2148
0
  pac->ticket_sign_data = tkt_data;
2149
0
    }
2150
2151
0
    ret = _krb5_pac_sign(context, pac, tkt->authtime, client, server_key,
2152
0
       kdc_key, rodc_id, upn, canon_name,
2153
0
       add_full_sig,
2154
0
       pac_attributes, &rspac);
2155
0
    if (ret == 0)
2156
0
        ret = _kdc_tkt_insert_pac(context, tkt, &rspac);
2157
0
    krb5_data_free(&rspac);
2158
0
    return ret;
2159
0
}