~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/heimdal/lib/krb5/pac.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2006 - 2007 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
#include <wind.h>
 
36
 
 
37
RCSID("$Id$");
 
38
 
 
39
struct PAC_INFO_BUFFER {
 
40
    uint32_t type;
 
41
    uint32_t buffersize;
 
42
    uint32_t offset_hi;
 
43
    uint32_t offset_lo;
 
44
};
 
45
 
 
46
struct PACTYPE {
 
47
    uint32_t numbuffers;
 
48
    uint32_t version;
 
49
    struct PAC_INFO_BUFFER buffers[1];
 
50
};
 
51
 
 
52
struct krb5_pac_data {
 
53
    struct PACTYPE *pac;
 
54
    krb5_data data;
 
55
    struct PAC_INFO_BUFFER *server_checksum;
 
56
    struct PAC_INFO_BUFFER *privsvr_checksum;
 
57
    struct PAC_INFO_BUFFER *logon_name;
 
58
};
 
59
 
 
60
#define PAC_ALIGNMENT                   8
 
61
 
 
62
#define PACTYPE_SIZE                    8
 
63
#define PAC_INFO_BUFFER_SIZE            16
 
64
 
 
65
#define PAC_SERVER_CHECKSUM             6
 
66
#define PAC_PRIVSVR_CHECKSUM            7
 
67
#define PAC_LOGON_NAME                  10
 
68
#define PAC_CONSTRAINED_DELEGATION      11
 
69
 
 
70
#define CHECK(r,f,l)                                            \
 
71
        do {                                                    \
 
72
                if (((r) = f ) != 0) {                          \
 
73
                        krb5_clear_error_message(context);      \
 
74
                        goto l;                                 \
 
75
                }                                               \
 
76
        } while(0)
 
77
 
 
78
static const char zeros[PAC_ALIGNMENT] = { 0 };
 
79
 
 
80
/*
 
81
 *
 
82
 */
 
83
 
 
84
krb5_error_code
 
85
krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
 
86
               krb5_pac *pac)
 
87
{
 
88
    krb5_error_code ret;
 
89
    krb5_pac p;
 
90
    krb5_storage *sp = NULL;
 
91
    uint32_t i, tmp, tmp2, header_end;
 
92
 
 
93
    p = calloc(1, sizeof(*p));
 
94
    if (p == NULL) {
 
95
        ret = ENOMEM;
 
96
        krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
97
        goto out;
 
98
    }
 
99
 
 
100
    sp = krb5_storage_from_readonly_mem(ptr, len);
 
101
    if (sp == NULL) {
 
102
        ret = ENOMEM;
 
103
        krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
104
        goto out;
 
105
    }
 
106
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
 
107
 
 
108
    CHECK(ret, krb5_ret_uint32(sp, &tmp), out);
 
109
    CHECK(ret, krb5_ret_uint32(sp, &tmp2), out);
 
110
    if (tmp < 1) {
 
111
        ret = EINVAL; /* Too few buffers */
 
112
        krb5_set_error_message(context, ret, N_("PAC have too few buffer", ""));
 
113
        goto out;
 
114
    }
 
115
    if (tmp2 != 0) {
 
116
        ret = EINVAL; /* Wrong version */
 
117
        krb5_set_error_message(context, ret,
 
118
                               N_("PAC have wrong version %d", ""),
 
119
                               (int)tmp2);
 
120
        goto out;
 
121
    }
 
122
 
 
123
    p->pac = calloc(1,
 
124
                    sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1)));
 
125
    if (p->pac == NULL) {
 
126
        ret = ENOMEM;
 
127
        krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
128
        goto out;
 
129
    }
 
130
 
 
131
    p->pac->numbuffers = tmp;
 
132
    p->pac->version = tmp2;
 
133
 
 
134
    header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
 
135
    if (header_end > len) {
 
136
        ret = EINVAL;
 
137
        goto out;
 
138
    }
 
139
 
 
140
    for (i = 0; i < p->pac->numbuffers; i++) {
 
141
        CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out);
 
142
        CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out);
 
143
        CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out);
 
144
        CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out);
 
145
 
 
146
        /* consistency checks */
 
147
        if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) {
 
148
            ret = EINVAL;
 
149
            krb5_set_error_message(context, ret,
 
150
                                   N_("PAC out of allignment", ""));
 
151
            goto out;
 
152
        }
 
153
        if (p->pac->buffers[i].offset_hi) {
 
154
            ret = EINVAL;
 
155
            krb5_set_error_message(context, ret,
 
156
                                   N_("PAC high offset set", ""));
 
157
            goto out;
 
158
        }
 
159
        if (p->pac->buffers[i].offset_lo > len) {
 
160
            ret = EINVAL;
 
161
            krb5_set_error_message(context, ret,
 
162
                                   N_("PAC offset off end", ""));
 
163
            goto out;
 
164
        }
 
165
        if (p->pac->buffers[i].offset_lo < header_end) {
 
166
            ret = EINVAL;
 
167
            krb5_set_error_message(context, ret,
 
168
                                   N_("PAC offset inside header: %lu %lu", ""),
 
169
                                   (unsigned long)p->pac->buffers[i].offset_lo,
 
170
                                   (unsigned long)header_end);
 
171
            goto out;
 
172
        }
 
173
        if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){
 
174
            ret = EINVAL;
 
175
            krb5_set_error_message(context, ret, N_("PAC length off end", ""));
 
176
            goto out;
 
177
        }
 
178
 
 
179
        /* let save pointer to data we need later */
 
180
        if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
 
181
            if (p->server_checksum) {
 
182
                ret = EINVAL;
 
183
                krb5_set_error_message(context, ret,
 
184
                                       N_("PAC have two server checksums", ""));
 
185
                goto out;
 
186
            }
 
187
            p->server_checksum = &p->pac->buffers[i];
 
188
        } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
 
189
            if (p->privsvr_checksum) {
 
190
                ret = EINVAL;
 
191
                krb5_set_error_message(context, ret,
 
192
                                       N_("PAC have two KDC checksums", ""));
 
193
                goto out;
 
194
            }
 
195
            p->privsvr_checksum = &p->pac->buffers[i];
 
196
        } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
 
197
            if (p->logon_name) {
 
198
                ret = EINVAL;
 
199
                krb5_set_error_message(context, ret,
 
200
                                       N_("PAC have two logon names", ""));
 
201
                goto out;
 
202
            }
 
203
            p->logon_name = &p->pac->buffers[i];
 
204
        }
 
205
    }
 
206
 
 
207
    ret = krb5_data_copy(&p->data, ptr, len);
 
208
    if (ret)
 
209
        goto out;
 
210
 
 
211
    krb5_storage_free(sp);
 
212
 
 
213
    *pac = p;
 
214
    return 0;
 
215
 
 
216
out:
 
217
    if (sp)
 
218
        krb5_storage_free(sp);
 
219
    if (p) {
 
220
        if (p->pac)
 
221
            free(p->pac);
 
222
        free(p);
 
223
    }
 
224
    *pac = NULL;
 
225
 
 
226
    return ret;
 
227
}
 
228
 
 
229
krb5_error_code
 
230
krb5_pac_init(krb5_context context, krb5_pac *pac)
 
231
{
 
232
    krb5_error_code ret;
 
233
    krb5_pac p;
 
234
 
 
235
    p = calloc(1, sizeof(*p));
 
236
    if (p == NULL) {
 
237
        krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
238
        return ENOMEM;
 
239
    }
 
240
 
 
241
    p->pac = calloc(1, sizeof(*p->pac));
 
242
    if (p->pac == NULL) {
 
243
        free(p);
 
244
        krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
245
        return ENOMEM;
 
246
    }
 
247
 
 
248
    ret = krb5_data_alloc(&p->data, PACTYPE_SIZE);
 
249
    if (ret) {
 
250
        free (p->pac);
 
251
        free(p);
 
252
        krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
253
        return ret;
 
254
    }
 
255
 
 
256
 
 
257
    *pac = p;
 
258
    return 0;
 
259
}
 
260
 
 
261
krb5_error_code
 
262
krb5_pac_add_buffer(krb5_context context, krb5_pac p,
 
263
                    uint32_t type, const krb5_data *data)
 
264
{
 
265
    krb5_error_code ret;
 
266
    void *ptr;
 
267
    size_t len, offset, header_end, old_end;
 
268
    uint32_t i;
 
269
 
 
270
    len = p->pac->numbuffers;
 
271
 
 
272
    ptr = realloc(p->pac,
 
273
                  sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len));
 
274
    if (ptr == NULL) {
 
275
        krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
276
        return ENOMEM;
 
277
    }
 
278
    p->pac = ptr;
 
279
 
 
280
    for (i = 0; i < len; i++)
 
281
        p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE;
 
282
 
 
283
    offset = p->data.length + PAC_INFO_BUFFER_SIZE;
 
284
 
 
285
    p->pac->buffers[len].type = type;
 
286
    p->pac->buffers[len].buffersize = data->length;
 
287
    p->pac->buffers[len].offset_lo = offset;
 
288
    p->pac->buffers[len].offset_hi = 0;
 
289
 
 
290
    old_end = p->data.length;
 
291
    len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE;
 
292
    if (len < p->data.length) {
 
293
        krb5_set_error_message(context, EINVAL, "integer overrun");
 
294
        return EINVAL;
 
295
    }
 
296
 
 
297
    /* align to PAC_ALIGNMENT */
 
298
    len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
 
299
 
 
300
    ret = krb5_data_realloc(&p->data, len);
 
301
    if (ret) {
 
302
        krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
303
        return ret;
 
304
    }
 
305
 
 
306
    /*
 
307
     * make place for new PAC INFO BUFFER header
 
308
     */
 
309
    header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
 
310
    memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE,
 
311
            (unsigned char *)p->data.data + header_end ,
 
312
            old_end - header_end);
 
313
    memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE);
 
314
 
 
315
    /*
 
316
     * copy in new data part
 
317
     */
 
318
 
 
319
    memcpy((unsigned char *)p->data.data + offset,
 
320
           data->data, data->length);
 
321
    memset((unsigned char *)p->data.data + offset + data->length,
 
322
           0, p->data.length - offset - data->length);
 
323
 
 
324
    p->pac->numbuffers += 1;
 
325
 
 
326
    return 0;
 
327
}
 
328
 
 
329
/**
 
330
 * Get the PAC buffer of specific type from the pac.
 
331
 *
 
332
 * @param context Kerberos 5 context.
 
333
 * @param p the pac structure returned by krb5_pac_parse().
 
334
 * @param type type of buffer to get
 
335
 * @param data return data, free with krb5_data_free().
 
336
 *
 
337
 * @return Returns 0 to indicate success. Otherwise an kerberos et
 
338
 * error code is returned, see krb5_get_error_message().
 
339
 *
 
340
 * @ingroup krb5_pac
 
341
 */
 
342
 
 
343
krb5_error_code
 
344
krb5_pac_get_buffer(krb5_context context, krb5_pac p,
 
345
                    uint32_t type, krb5_data *data)
 
346
{
 
347
    krb5_error_code ret;
 
348
    uint32_t i;
 
349
 
 
350
    for (i = 0; i < p->pac->numbuffers; i++) {
 
351
        const size_t len = p->pac->buffers[i].buffersize;
 
352
        const size_t offset = p->pac->buffers[i].offset_lo;
 
353
 
 
354
        if (p->pac->buffers[i].type != type)
 
355
            continue;
 
356
 
 
357
        ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
 
358
        if (ret) {
 
359
            krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
360
            return ret;
 
361
        }
 
362
        return 0;
 
363
    }
 
364
    krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found",
 
365
                           (unsigned long)type);
 
366
    return ENOENT;
 
367
}
 
368
 
 
369
/*
 
370
 *
 
371
 */
 
372
 
 
373
krb5_error_code
 
374
krb5_pac_get_types(krb5_context context,
 
375
                   krb5_pac p,
 
376
                   size_t *len,
 
377
                   uint32_t **types)
 
378
{
 
379
    size_t i;
 
380
 
 
381
    *types = calloc(p->pac->numbuffers, sizeof(*types));
 
382
    if (*types == NULL) {
 
383
        *len = 0;
 
384
        krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
385
        return ENOMEM;
 
386
    }
 
387
    for (i = 0; i < p->pac->numbuffers; i++)
 
388
        (*types)[i] = p->pac->buffers[i].type;
 
389
    *len = p->pac->numbuffers;
 
390
 
 
391
    return 0;
 
392
}
 
393
 
 
394
/*
 
395
 *
 
396
 */
 
397
 
 
398
void
 
399
krb5_pac_free(krb5_context context, krb5_pac pac)
 
400
{
 
401
    krb5_data_free(&pac->data);
 
402
    free(pac->pac);
 
403
    free(pac);
 
404
}
 
405
 
 
406
/*
 
407
 *
 
408
 */
 
409
 
 
410
static krb5_error_code
 
411
verify_checksum(krb5_context context,
 
412
                const struct PAC_INFO_BUFFER *sig,
 
413
                const krb5_data *data,
 
414
                void *ptr, size_t len,
 
415
                const krb5_keyblock *key)
 
416
{
 
417
    krb5_crypto crypto = NULL;
 
418
    krb5_storage *sp = NULL;
 
419
    uint32_t type;
 
420
    krb5_error_code ret;
 
421
    Checksum cksum;
 
422
 
 
423
    memset(&cksum, 0, sizeof(cksum));
 
424
 
 
425
    sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo,
 
426
                               sig->buffersize);
 
427
    if (sp == NULL) {
 
428
        krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
429
        return ENOMEM;
 
430
    }
 
431
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
 
432
 
 
433
    CHECK(ret, krb5_ret_uint32(sp, &type), out);
 
434
    cksum.cksumtype = type;
 
435
    cksum.checksum.length =
 
436
        sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR);
 
437
    cksum.checksum.data = malloc(cksum.checksum.length);
 
438
    if (cksum.checksum.data == NULL) {
 
439
        ret = ENOMEM;
 
440
        krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
441
        goto out;
 
442
    }
 
443
    ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length);
 
444
    if (ret != cksum.checksum.length) {
 
445
        ret = EINVAL;
 
446
        krb5_set_error_message(context, ret, "PAC checksum missing checksum");
 
447
        goto out;
 
448
    }
 
449
 
 
450
    if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) {
 
451
        ret = EINVAL;
 
452
        krb5_set_error_message(context, ret, "Checksum type %d not keyed",
 
453
                               cksum.cksumtype);
 
454
        goto out;
 
455
    }
 
456
 
 
457
    ret = krb5_crypto_init(context, key, 0, &crypto);
 
458
    if (ret)
 
459
        goto out;
 
460
 
 
461
    ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM,
 
462
                               ptr, len, &cksum);
 
463
    free(cksum.checksum.data);
 
464
    krb5_crypto_destroy(context, crypto);
 
465
    krb5_storage_free(sp);
 
466
 
 
467
    return ret;
 
468
 
 
469
out:
 
470
    if (cksum.checksum.data)
 
471
        free(cksum.checksum.data);
 
472
    if (sp)
 
473
        krb5_storage_free(sp);
 
474
    if (crypto)
 
475
        krb5_crypto_destroy(context, crypto);
 
476
    return ret;
 
477
}
 
478
 
 
479
static krb5_error_code
 
480
create_checksum(krb5_context context,
 
481
                const krb5_keyblock *key,
 
482
                void *data, size_t datalen,
 
483
                void *sig, size_t siglen)
 
484
{
 
485
    krb5_crypto crypto = NULL;
 
486
    krb5_error_code ret;
 
487
    Checksum cksum;
 
488
 
 
489
    ret = krb5_crypto_init(context, key, 0, &crypto);
 
490
    if (ret)
 
491
        return ret;
 
492
 
 
493
    ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0,
 
494
                               data, datalen, &cksum);
 
495
    krb5_crypto_destroy(context, crypto);
 
496
    if (ret)
 
497
        return ret;
 
498
 
 
499
    if (cksum.checksum.length != siglen) {
 
500
        krb5_set_error_message(context, EINVAL, "pac checksum wrong length");
 
501
        free_Checksum(&cksum);
 
502
        return EINVAL;
 
503
    }
 
504
 
 
505
    memcpy(sig, cksum.checksum.data, siglen);
 
506
    free_Checksum(&cksum);
 
507
 
 
508
    return 0;
 
509
}
 
510
 
 
511
 
 
512
/*
 
513
 *
 
514
 */
 
515
 
 
516
#define NTTIME_EPOCH 0x019DB1DED53E8000LL
 
517
 
 
518
static uint64_t
 
519
unix2nttime(time_t unix_time)
 
520
{
 
521
    long long wt;
 
522
    wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
 
523
    return wt;
 
524
}
 
525
 
 
526
static krb5_error_code
 
527
verify_logonname(krb5_context context,
 
528
                 const struct PAC_INFO_BUFFER *logon_name,
 
529
                 const krb5_data *data,
 
530
                 time_t authtime,
 
531
                 krb5_const_principal principal)
 
532
{
 
533
    krb5_error_code ret;
 
534
    krb5_principal p2;
 
535
    uint32_t time1, time2;
 
536
    krb5_storage *sp;
 
537
    uint16_t len;
 
538
    char *s;
 
539
 
 
540
    sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo,
 
541
                                        logon_name->buffersize);
 
542
    if (sp == NULL) {
 
543
        krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
544
        return ENOMEM;
 
545
    }
 
546
 
 
547
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
 
548
 
 
549
    CHECK(ret, krb5_ret_uint32(sp, &time1), out);
 
550
    CHECK(ret, krb5_ret_uint32(sp, &time2), out);
 
551
 
 
552
    {
 
553
        uint64_t t1, t2;
 
554
        t1 = unix2nttime(authtime);
 
555
        t2 = ((uint64_t)time2 << 32) | time1;
 
556
        if (t1 != t2) {
 
557
            krb5_storage_free(sp);
 
558
            krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch");
 
559
            return EINVAL;
 
560
        }
 
561
    }
 
562
    CHECK(ret, krb5_ret_uint16(sp, &len), out);
 
563
    if (len == 0) {
 
564
        krb5_storage_free(sp);
 
565
        krb5_set_error_message(context, EINVAL, "PAC logon name length missing");
 
566
        return EINVAL;
 
567
    }
 
568
 
 
569
    s = malloc(len);
 
570
    if (s == NULL) {
 
571
        krb5_storage_free(sp);
 
572
        krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
573
        return ENOMEM;
 
574
    }
 
575
    ret = krb5_storage_read(sp, s, len);
 
576
    if (ret != len) {
 
577
        krb5_storage_free(sp);
 
578
        krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name");
 
579
        return EINVAL;
 
580
    }
 
581
    krb5_storage_free(sp);
 
582
    {
 
583
        size_t ucs2len = len / 2;
 
584
        uint16_t *ucs2;
 
585
        size_t u8len;
 
586
        unsigned int flags = WIND_RW_LE;
 
587
 
 
588
        ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
 
589
        if (ucs2 == NULL) {
 
590
            krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
591
            return ENOMEM;
 
592
        }
 
593
        ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
 
594
        free(s);
 
595
        if (ret) {
 
596
            free(ucs2);
 
597
            krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
 
598
            return ret;
 
599
        }
 
600
        ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
 
601
        if (ret) {
 
602
            free(ucs2);
 
603
            krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string");
 
604
            return ret;
 
605
        }
 
606
        u8len += 1; /* Add space for NUL */
 
607
        s = malloc(u8len);
 
608
        if (s == NULL) {
 
609
            free(ucs2);
 
610
            krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
611
            return ENOMEM;
 
612
        }
 
613
        ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len);
 
614
        free(ucs2);
 
615
        if (ret) {
 
616
            krb5_set_error_message(context, ret, "Failed to convert to UTF-8");
 
617
            return ret;
 
618
        }
 
619
    }
 
620
    ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2);
 
621
    free(s);
 
622
    if (ret)
 
623
        return ret;
 
624
 
 
625
    if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) {
 
626
        ret = EINVAL;
 
627
        krb5_set_error_message(context, ret, "PAC logon name mismatch");
 
628
    }
 
629
    krb5_free_principal(context, p2);
 
630
    return ret;
 
631
out:
 
632
    return ret;
 
633
}
 
634
 
 
635
/*
 
636
 *
 
637
 */
 
638
 
 
639
static krb5_error_code
 
640
build_logon_name(krb5_context context,
 
641
                 time_t authtime,
 
642
                 krb5_const_principal principal,
 
643
                 krb5_data *logon)
 
644
{
 
645
    krb5_error_code ret;
 
646
    krb5_storage *sp;
 
647
    uint64_t t;
 
648
    char *s, *s2;
 
649
    size_t i, len;
 
650
 
 
651
    t = unix2nttime(authtime);
 
652
 
 
653
    krb5_data_zero(logon);
 
654
 
 
655
    sp = krb5_storage_emem();
 
656
    if (sp == NULL) {
 
657
        krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
658
        return ENOMEM;
 
659
    }
 
660
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
 
661
 
 
662
    CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out);
 
663
    CHECK(ret, krb5_store_uint32(sp, t >> 32), out);
 
664
 
 
665
    ret = krb5_unparse_name_flags(context, principal,
 
666
                                  KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s);
 
667
    if (ret)
 
668
        goto out;
 
669
 
 
670
    len = strlen(s);
 
671
 
 
672
    CHECK(ret, krb5_store_uint16(sp, len * 2), out);
 
673
 
 
674
#if 1 /* cheat for now */
 
675
    s2 = malloc(len * 2);
 
676
    if (s2 == NULL) {
 
677
        ret = ENOMEM;
 
678
        free(s);
 
679
        goto out;
 
680
    }
 
681
    for (i = 0; i < len; i++) {
 
682
        s2[i * 2] = s[i];
 
683
        s2[i * 2 + 1] = 0;
 
684
    }
 
685
    free(s);
 
686
#else
 
687
    /* write libwind code here */
 
688
#endif
 
689
 
 
690
    ret = krb5_storage_write(sp, s2, len * 2);
 
691
    free(s2);
 
692
    if (ret != len * 2) {
 
693
        ret = ENOMEM;
 
694
        goto out;
 
695
    }
 
696
    ret = krb5_storage_to_data(sp, logon);
 
697
    if (ret)
 
698
        goto out;
 
699
    krb5_storage_free(sp);
 
700
 
 
701
    return 0;
 
702
out:
 
703
    krb5_storage_free(sp);
 
704
    return ret;
 
705
}
 
706
 
 
707
 
 
708
/**
 
709
 * Verify the PAC.
 
710
 *
 
711
 * @param context Kerberos 5 context.
 
712
 * @param pac the pac structure returned by krb5_pac_parse().
 
713
 * @param authtime The time of the ticket the PAC belongs to.
 
714
 * @param principal the principal to verify.
 
715
 * @param server The service key, most always be given.
 
716
 * @param privsvr The KDC key, may be given.
 
717
 
 
718
 * @return Returns 0 to indicate success. Otherwise an kerberos et
 
719
 * error code is returned, see krb5_get_error_message().
 
720
 *
 
721
 * @ingroup krb5_pac
 
722
 */
 
723
 
 
724
krb5_error_code
 
725
krb5_pac_verify(krb5_context context,
 
726
                const krb5_pac pac,
 
727
                time_t authtime,
 
728
                krb5_const_principal principal,
 
729
                const krb5_keyblock *server,
 
730
                const krb5_keyblock *privsvr)
 
731
{
 
732
    krb5_error_code ret;
 
733
 
 
734
    if (pac->server_checksum == NULL) {
 
735
        krb5_set_error_message(context, EINVAL, "PAC missing server checksum");
 
736
        return EINVAL;
 
737
    }
 
738
    if (pac->privsvr_checksum == NULL) {
 
739
        krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum");
 
740
        return EINVAL;
 
741
    }
 
742
    if (pac->logon_name == NULL) {
 
743
        krb5_set_error_message(context, EINVAL, "PAC missing logon name");
 
744
        return EINVAL;
 
745
    }
 
746
 
 
747
    ret = verify_logonname(context,
 
748
                           pac->logon_name,
 
749
                           &pac->data,
 
750
                           authtime,
 
751
                           principal);
 
752
    if (ret)
 
753
        return ret;
 
754
 
 
755
    /*
 
756
     * in the service case, clean out data option of the privsvr and
 
757
     * server checksum before checking the checksum.
 
758
     */
 
759
    {
 
760
        krb5_data *copy;
 
761
 
 
762
        ret = krb5_copy_data(context, &pac->data, &copy);
 
763
        if (ret)
 
764
            return ret;
 
765
 
 
766
        if (pac->server_checksum->buffersize < 4)
 
767
            return EINVAL;
 
768
        if (pac->privsvr_checksum->buffersize < 4)
 
769
            return EINVAL;
 
770
 
 
771
        memset((char *)copy->data + pac->server_checksum->offset_lo + 4,
 
772
               0,
 
773
               pac->server_checksum->buffersize - 4);
 
774
 
 
775
        memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4,
 
776
               0,
 
777
               pac->privsvr_checksum->buffersize - 4);
 
778
 
 
779
        ret = verify_checksum(context,
 
780
                              pac->server_checksum,
 
781
                              &pac->data,
 
782
                              copy->data,
 
783
                              copy->length,
 
784
                              server);
 
785
        krb5_free_data(context, copy);
 
786
        if (ret)
 
787
            return ret;
 
788
    }
 
789
    if (privsvr) {
 
790
        /* The priv checksum covers the server checksum */
 
791
        ret = verify_checksum(context,
 
792
                              pac->privsvr_checksum,
 
793
                              &pac->data,
 
794
                              (char *)pac->data.data
 
795
                              + pac->server_checksum->offset_lo + 4,
 
796
                              pac->server_checksum->buffersize - 4,
 
797
                              privsvr);
 
798
        if (ret)
 
799
            return ret;
 
800
    }
 
801
 
 
802
    return 0;
 
803
}
 
804
 
 
805
/*
 
806
 *
 
807
 */
 
808
 
 
809
static krb5_error_code
 
810
fill_zeros(krb5_context context, krb5_storage *sp, size_t len)
 
811
{
 
812
    ssize_t sret;
 
813
    size_t l;
 
814
 
 
815
    while (len) {
 
816
        l = len;
 
817
        if (l > sizeof(zeros))
 
818
            l = sizeof(zeros);
 
819
        sret = krb5_storage_write(sp, zeros, l);
 
820
        if (sret <= 0) {
 
821
            krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
822
            return ENOMEM;
 
823
        }
 
824
        len -= sret;
 
825
    }
 
826
    return 0;
 
827
}
 
828
 
 
829
static krb5_error_code
 
830
pac_checksum(krb5_context context,
 
831
             const krb5_keyblock *key,
 
832
             uint32_t *cksumtype,
 
833
             size_t *cksumsize)
 
834
{
 
835
    krb5_cksumtype cktype;
 
836
    krb5_error_code ret;
 
837
    krb5_crypto crypto = NULL;
 
838
 
 
839
    ret = krb5_crypto_init(context, key, 0, &crypto);
 
840
    if (ret)
 
841
        return ret;
 
842
 
 
843
    ret = krb5_crypto_get_checksum_type(context, crypto, &cktype);
 
844
    krb5_crypto_destroy(context, crypto);
 
845
    if (ret)
 
846
        return ret;
 
847
 
 
848
    if (krb5_checksum_is_keyed(context, cktype) == FALSE) {
 
849
        krb5_set_error_message(context, EINVAL, "PAC checksum type is not keyed");
 
850
        return EINVAL;
 
851
    }
 
852
 
 
853
    ret = krb5_checksumsize(context, cktype, cksumsize);
 
854
    if (ret)
 
855
        return ret;
 
856
 
 
857
    *cksumtype = (uint32_t)cktype;
 
858
 
 
859
    return 0;
 
860
}
 
861
 
 
862
krb5_error_code
 
863
_krb5_pac_sign(krb5_context context,
 
864
               krb5_pac p,
 
865
               time_t authtime,
 
866
               krb5_principal principal,
 
867
               const krb5_keyblock *server_key,
 
868
               const krb5_keyblock *priv_key,
 
869
               krb5_data *data)
 
870
{
 
871
    krb5_error_code ret;
 
872
    krb5_storage *sp = NULL, *spdata = NULL;
 
873
    uint32_t end;
 
874
    size_t server_size, priv_size;
 
875
    uint32_t server_offset = 0, priv_offset = 0;
 
876
    uint32_t server_cksumtype = 0, priv_cksumtype = 0;
 
877
    int i, num = 0;
 
878
    krb5_data logon, d;
 
879
 
 
880
    krb5_data_zero(&logon);
 
881
 
 
882
    if (p->logon_name == NULL)
 
883
        num++;
 
884
    if (p->server_checksum == NULL)
 
885
        num++;
 
886
    if (p->privsvr_checksum == NULL)
 
887
        num++;
 
888
 
 
889
    if (num) {
 
890
        void *ptr;
 
891
 
 
892
        ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1)));
 
893
        if (ptr == NULL) {
 
894
            krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
895
            return ENOMEM;
 
896
        }
 
897
        p->pac = ptr;
 
898
 
 
899
        if (p->logon_name == NULL) {
 
900
            p->logon_name = &p->pac->buffers[p->pac->numbuffers++];
 
901
            memset(p->logon_name, 0, sizeof(*p->logon_name));
 
902
            p->logon_name->type = PAC_LOGON_NAME;
 
903
        }
 
904
        if (p->server_checksum == NULL) {
 
905
            p->server_checksum = &p->pac->buffers[p->pac->numbuffers++];
 
906
            memset(p->server_checksum, 0, sizeof(*p->server_checksum));
 
907
            p->server_checksum->type = PAC_SERVER_CHECKSUM;
 
908
        }
 
909
        if (p->privsvr_checksum == NULL) {
 
910
            p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++];
 
911
            memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum));
 
912
            p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM;
 
913
        }
 
914
    }
 
915
 
 
916
    /* Calculate LOGON NAME */
 
917
    ret = build_logon_name(context, authtime, principal, &logon);
 
918
    if (ret)
 
919
        goto out;
 
920
 
 
921
    /* Set lengths for checksum */
 
922
    ret = pac_checksum(context, server_key, &server_cksumtype, &server_size);
 
923
    if (ret)
 
924
        goto out;
 
925
    ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size);
 
926
    if (ret)
 
927
        goto out;
 
928
 
 
929
    /* Encode PAC */
 
930
    sp = krb5_storage_emem();
 
931
    if (sp == NULL) {
 
932
        krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
933
        return ENOMEM;
 
934
    }
 
935
    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
 
936
 
 
937
    spdata = krb5_storage_emem();
 
938
    if (spdata == NULL) {
 
939
        krb5_storage_free(sp);
 
940
        krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 
941
        return ENOMEM;
 
942
    }
 
943
    krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE);
 
944
 
 
945
    CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out);
 
946
    CHECK(ret, krb5_store_uint32(sp, p->pac->version), out);
 
947
 
 
948
    end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
 
949
 
 
950
    for (i = 0; i < p->pac->numbuffers; i++) {
 
951
        uint32_t len;
 
952
        size_t sret;
 
953
        void *ptr = NULL;
 
954
 
 
955
        /* store data */
 
956
 
 
957
        if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
 
958
            len = server_size + 4;
 
959
            server_offset = end + 4;
 
960
            CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out);
 
961
            CHECK(ret, fill_zeros(context, spdata, server_size), out);
 
962
        } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
 
963
            len = priv_size + 4;
 
964
            priv_offset = end + 4;
 
965
            CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
 
966
            CHECK(ret, fill_zeros(context, spdata, priv_size), out);
 
967
        } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
 
968
            len = krb5_storage_write(spdata, logon.data, logon.length);
 
969
            if (logon.length != len) {
 
970
                ret = EINVAL;
 
971
                goto out;
 
972
            }
 
973
        } else {
 
974
            len = p->pac->buffers[i].buffersize;
 
975
            ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo;
 
976
 
 
977
            sret = krb5_storage_write(spdata, ptr, len);
 
978
            if (sret != len) {
 
979
                ret = ENOMEM;
 
980
                krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
981
                goto out;
 
982
            }
 
983
            /* XXX if not aligned, fill_zeros */
 
984
        }
 
985
 
 
986
        /* write header */
 
987
        CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out);
 
988
        CHECK(ret, krb5_store_uint32(sp, len), out);
 
989
        CHECK(ret, krb5_store_uint32(sp, end), out);
 
990
        CHECK(ret, krb5_store_uint32(sp, 0), out);
 
991
 
 
992
        /* advance data endpointer and align */
 
993
        {
 
994
            int32_t e;
 
995
 
 
996
            end += len;
 
997
            e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
 
998
            if (end != e) {
 
999
                CHECK(ret, fill_zeros(context, spdata, e - end), out);
 
1000
            }
 
1001
            end = e;
 
1002
        }
 
1003
 
 
1004
    }
 
1005
 
 
1006
    /* assert (server_offset != 0 && priv_offset != 0); */
 
1007
 
 
1008
    /* export PAC */
 
1009
    ret = krb5_storage_to_data(spdata, &d);
 
1010
    if (ret) {
 
1011
        krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
1012
        goto out;
 
1013
    }
 
1014
    ret = krb5_storage_write(sp, d.data, d.length);
 
1015
    if (ret != d.length) {
 
1016
        krb5_data_free(&d);
 
1017
        ret = ENOMEM;
 
1018
        krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
1019
        goto out;
 
1020
    }
 
1021
    krb5_data_free(&d);
 
1022
 
 
1023
    ret = krb5_storage_to_data(sp, &d);
 
1024
    if (ret) {
 
1025
        krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 
1026
        goto out;
 
1027
    }
 
1028
 
 
1029
    /* sign */
 
1030
 
 
1031
    ret = create_checksum(context, server_key,
 
1032
                          d.data, d.length,
 
1033
                          (char *)d.data + server_offset, server_size);
 
1034
    if (ret) {
 
1035
        krb5_data_free(&d);
 
1036
        goto out;
 
1037
    }
 
1038
 
 
1039
    ret = create_checksum(context, priv_key,
 
1040
                          (char *)d.data + server_offset, server_size,
 
1041
                          (char *)d.data + priv_offset, priv_size);
 
1042
    if (ret) {
 
1043
        krb5_data_free(&d);
 
1044
        goto out;
 
1045
    }
 
1046
 
 
1047
    /* done */
 
1048
    *data = d;
 
1049
 
 
1050
    krb5_data_free(&logon);
 
1051
    krb5_storage_free(sp);
 
1052
    krb5_storage_free(spdata);
 
1053
 
 
1054
    return 0;
 
1055
out:
 
1056
    krb5_data_free(&logon);
 
1057
    if (sp)
 
1058
        krb5_storage_free(sp);
 
1059
    if (spdata)
 
1060
        krb5_storage_free(spdata);
 
1061
    return ret;
 
1062
}