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