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