/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- krb5_pac_parse
- krb5_pac_init
- krb5_pac_add_buffer
- krb5_pac_get_buffer
- krb5_pac_get_types
- krb5_pac_free
- verify_checksum
- create_checksum
- unix2nttime
- verify_logonname
- build_logon_name
- krb5_pac_verify
- fill_zeros
- pac_checksum
- _krb5_pac_sign
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,
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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, ©);
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)
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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 }