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 204194 : pac_dealloc(void *ctx)
125 : {
126 204194 : krb5_pac pac = (krb5_pac)ctx;
127 :
128 204194 : krb5_data_free(&pac->data);
129 204194 : krb5_data_free(&pac->ticket_sign_data);
130 :
131 204194 : if (pac->upn_princ) {
132 71543 : free_Principal(pac->upn_princ);
133 71543 : free(pac->upn_princ);
134 : }
135 204194 : if (pac->canon_princ) {
136 71543 : free_Principal(pac->canon_princ);
137 71543 : free(pac->canon_princ);
138 : }
139 204194 : krb5_data_free(&pac->sid);
140 :
141 204194 : free(pac->pac);
142 204194 : }
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 867162 : pac_header_size(krb5_context context, uint32_t num_buffers, uint32_t *result)
162 : {
163 28874 : krb5_error_code ret;
164 28874 : uint32_t header_size;
165 :
166 : /* Guard against integer overflow */
167 867162 : 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 867162 : header_size = PAC_INFO_BUFFER_SIZE * num_buffers;
173 :
174 : /* Guard against integer overflow */
175 867162 : 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 867162 : header_size += PACTYPE_SIZE;
181 :
182 867162 : *result = header_size;
183 :
184 843503 : return 0;
185 : }
186 :
187 : /* Output `size' + `addend' + padding for alignment if it doesn't overflow */
188 : static krb5_error_code
189 1687583 : pac_aligned_size(krb5_context context,
190 : uint32_t size,
191 : uint32_t addend,
192 : uint32_t *aligned_size)
193 : {
194 60700 : krb5_error_code ret;
195 :
196 1748283 : if (size > UINT32_MAX - addend ||
197 1687583 : (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 1687583 : size += addend;
203 1687583 : size += PAC_ALIGNMENT - 1;
204 1687583 : size &= ~(PAC_ALIGNMENT - 1);
205 1687583 : *aligned_size = size;
206 1645921 : 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 24790 : 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 24790 : memset(&local_key, 0, sizeof(local_key));
226 :
227 24790 : ret = krb5_copy_keyblock(context, key, &local_key.key);
228 24790 : if (ret)
229 0 : return ret;
230 :
231 24790 : ret = krb5_data_alloc (&result->checksum, 16);
232 24790 : if (ret) {
233 0 : krb5_free_keyblock(context, local_key.key);
234 0 : return ret;
235 : }
236 :
237 24790 : result->cksumtype = CKSUMTYPE_HMAC_MD5;
238 24790 : iov.data.data = (void *)data;
239 24790 : iov.data.length = len;
240 24790 : iov.flags = KRB5_CRYPTO_TYPE_DATA;
241 :
242 24790 : ret = _krb5_HMAC_MD5_checksum(context, NULL, &local_key, usage, &iov, 1,
243 : result);
244 24790 : if (ret)
245 0 : krb5_data_free(&result->checksum);
246 :
247 24790 : krb5_free_keyblock(context, local_key.key);
248 24790 : return ret;
249 : }
250 :
251 :
252 : /*
253 : *
254 : */
255 :
256 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
257 156955 : krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
258 : krb5_pac *pac)
259 : {
260 156955 : krb5_error_code ret = 0;
261 3422 : krb5_pac p;
262 156955 : krb5_storage *sp = NULL;
263 156955 : uint32_t i, num_buffers, version, header_size = 0;
264 156955 : uint32_t prev_start = 0;
265 156955 : uint32_t prev_end = 0;
266 :
267 156955 : *pac = NULL;
268 156955 : p = _heim_alloc_object(&pac_object, sizeof(*p));
269 156955 : if (p)
270 156955 : sp = krb5_storage_from_readonly_mem(ptr, len);
271 156955 : if (sp == NULL)
272 0 : ret = krb5_enomem(context);
273 153533 : if (ret == 0) {
274 156955 : krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
275 156955 : ret = krb5_ret_uint32(sp, &num_buffers);
276 : }
277 156955 : if (ret == 0)
278 156955 : ret = krb5_ret_uint32(sp, &version);
279 156955 : if (ret == 0 && num_buffers < 1)
280 0 : krb5_set_error_message(context, ret = EINVAL,
281 0 : N_("PAC has too few buffers", ""));
282 156955 : if (ret == 0 && num_buffers > 1000)
283 0 : krb5_set_error_message(context, ret = EINVAL,
284 0 : N_("PAC has too many buffers", ""));
285 156955 : 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 156955 : if (ret == 0)
290 156955 : ret = pac_header_size(context, num_buffers, &header_size);
291 156955 : 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 156955 : if (ret == 0)
295 156955 : p->pac = calloc(1, header_size);
296 156955 : if (ret == 0 && p->pac == NULL)
297 0 : ret = krb5_enomem(context);
298 :
299 156955 : if (ret == 0) {
300 156955 : p->pac->numbuffers = num_buffers;
301 156955 : p->pac->version = version;
302 : }
303 :
304 1412346 : for (i = 0; ret == 0 && i < p->pac->numbuffers; i++) {
305 1255391 : ret = krb5_ret_uint32(sp, &p->pac->buffers[i].type);
306 1255391 : if (ret == 0)
307 1255391 : ret = krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize);
308 1255391 : if (ret == 0)
309 1255391 : ret = krb5_ret_uint64(sp, &p->pac->buffers[i].offset);
310 1255391 : if (ret)
311 0 : break;
312 :
313 : /* Consistency checks (we don't check for wasted space) */
314 1255391 : 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 1255391 : if (p->pac->buffers[i].offset > len ||
320 1255391 : p->pac->buffers[i].buffersize > len ||
321 1255391 : 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 1255391 : 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 1255391 : if (prev_start) {
344 1098436 : if (p->pac->buffers[i].offset >= prev_start &&
345 1098436 : 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 1098436 : 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 1255391 : prev_start = p->pac->buffers[i].offset;
359 1255391 : 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 1255391 : switch (p->pac->buffers[i].type) {
363 156949 : case PAC_SERVER_CHECKSUM:
364 156949 : if (p->server_checksum)
365 0 : krb5_set_error_message(context, ret = EINVAL,
366 0 : N_("PAC has multiple server checksums", ""));
367 : else
368 156949 : p->server_checksum = &p->pac->buffers[i];
369 153527 : break;
370 156949 : case PAC_PRIVSVR_CHECKSUM:
371 156949 : if (p->privsvr_checksum)
372 0 : krb5_set_error_message(context, ret = EINVAL,
373 0 : N_("PAC has multiple KDC checksums", ""));
374 : else
375 156949 : p->privsvr_checksum = &p->pac->buffers[i];
376 153527 : break;
377 156955 : case PAC_LOGON_NAME:
378 156955 : if (p->logon_name)
379 0 : krb5_set_error_message(context, ret = EINVAL,
380 0 : N_("PAC has multiple logon names", ""));
381 : else
382 156955 : p->logon_name = &p->pac->buffers[i];
383 153533 : break;
384 156909 : case PAC_UPN_DNS_INFO:
385 156909 : 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 156909 : p->upn_dns_info = &p->pac->buffers[i];
390 153489 : break;
391 106436 : case PAC_TICKET_CHECKSUM:
392 106436 : if (p->ticket_checksum)
393 0 : krb5_set_error_message(context, ret = EINVAL,
394 0 : N_("PAC has multiple ticket checksums", ""));
395 : else
396 106436 : p->ticket_checksum = &p->pac->buffers[i];
397 104674 : break;
398 50371 : case PAC_ATTRIBUTES_INFO:
399 50371 : 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 50371 : p->attributes_info = &p->pac->buffers[i];
404 48713 : break;
405 106436 : case PAC_FULL_CHECKSUM:
406 106436 : if (p->full_checksum)
407 0 : krb5_set_error_message(context, ret = EINVAL,
408 0 : N_("PAC has multiple full checksums", ""));
409 : else
410 106436 : p->full_checksum = &p->pac->buffers[i];
411 104674 : break;
412 355886 : default: break;
413 : }
414 : }
415 :
416 156955 : if (ret == 0)
417 156955 : ret = krb5_data_copy(&p->data, ptr, len);
418 156955 : if (ret == 0) {
419 156955 : *pac = p;
420 156955 : p = NULL;
421 : }
422 156955 : if (sp)
423 156955 : krb5_storage_free(sp);
424 156955 : krb5_pac_free(context, p);
425 156955 : return ret;
426 : }
427 :
428 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
429 79664 : krb5_pac_init(krb5_context context, krb5_pac *pac)
430 : {
431 2828 : krb5_error_code ret;
432 2828 : krb5_pac p;
433 :
434 79664 : p = _heim_alloc_object(&pac_object, sizeof(*p));
435 79664 : if (p == NULL) {
436 0 : return krb5_enomem(context);
437 : }
438 :
439 79664 : p->pac = calloc(1, sizeof(*p->pac));
440 79664 : if (p->pac == NULL) {
441 0 : krb5_pac_free(context, p);
442 0 : return krb5_enomem(context);
443 : }
444 :
445 79664 : ret = krb5_data_alloc(&p->data, PACTYPE_SIZE);
446 79664 : if (ret) {
447 0 : free (p->pac);
448 0 : krb5_pac_free(context, p);
449 0 : return krb5_enomem(context);
450 : }
451 79664 : memset(p->data.data, 0, p->data.length);
452 :
453 79664 : *pac = p;
454 79664 : 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 527843 : krb5_pac_add_buffer(krb5_context context, krb5_pac p,
469 : uint32_t type, const krb5_data *nd)
470 : {
471 19038 : krb5_error_code ret;
472 19038 : void *ptr;
473 527843 : size_t old_len = p->data.length;
474 19038 : uint32_t len, offset, header_size;
475 19038 : uint32_t i;
476 19038 : uint32_t num_buffers;
477 :
478 527843 : assert(nd->data != NULL);
479 :
480 527843 : num_buffers = p->pac->numbuffers;
481 527843 : ret = pac_header_size(context, num_buffers + 1, &header_size);
482 527843 : if (ret)
483 0 : return ret;
484 :
485 527843 : ptr = realloc(p->pac, header_size);
486 527843 : if (ptr == NULL)
487 0 : return krb5_enomem(context);
488 :
489 527843 : p->pac = ptr;
490 527843 : p->pac->buffers[num_buffers].type = 0;
491 527843 : p->pac->buffers[num_buffers].buffersize = 0;
492 527843 : 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 2061706 : for (i = 0; i < num_buffers; i++) {
502 1533863 : 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 527843 : ret = pac_aligned_size(context, p->data.length, PAC_INFO_BUFFER_SIZE, &offset);
516 508805 : if (ret == 0)
517 : /* Set the new length = offset + nd->length + alignment */
518 527843 : ret = pac_aligned_size(context, offset, nd->length, &len);
519 527843 : if (ret) {
520 0 : krb5_set_error_message(context, ret, "PAC buffer too large");
521 0 : return ret;
522 : }
523 527843 : ret = krb5_data_realloc(&p->data, len);
524 527843 : 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 527843 : memset((char *)p->data.data + old_len, 0, len - old_len);
531 :
532 527843 : p->pac->buffers[num_buffers].type = type;
533 527843 : p->pac->buffers[num_buffers].buffersize = nd->length;
534 527843 : p->pac->buffers[num_buffers].offset = offset;
535 :
536 : /* Adjust all the buffer offsets in the existing PAC_INFO_BUFFERs now */
537 2061706 : for (i = 0; i < num_buffers; i++)
538 1533863 : p->pac->buffers[i].offset += PAC_INFO_BUFFER_SIZE;
539 :
540 : /*
541 : * Make place for new PAC INFO BUFFER header
542 : */
543 527843 : header_size -= PAC_INFO_BUFFER_SIZE;
544 527843 : memmove((unsigned char *)p->data.data + header_size + PAC_INFO_BUFFER_SIZE,
545 527843 : (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 527843 : memset((unsigned char *)p->data.data + header_size, 0,
549 : PAC_INFO_BUFFER_SIZE);
550 :
551 : /*
552 : * Copy in new data part
553 : */
554 527843 : memcpy((unsigned char *)p->data.data + offset, nd->data, nd->length);
555 527843 : p->pac->numbuffers += 1;
556 527843 : 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 533360 : krb5_pac_get_buffer(krb5_context context, krb5_const_pac p,
575 : uint32_t type, krb5_data *data)
576 : {
577 15290 : krb5_error_code ret;
578 15290 : uint32_t i;
579 :
580 2300251 : for (i = 0; i < p->pac->numbuffers; i++) {
581 2246562 : size_t len = p->pac->buffers[i].buffersize;
582 2246562 : size_t offset = p->pac->buffers[i].offset;
583 :
584 2246562 : if (p->pac->buffers[i].type != type)
585 1766891 : continue;
586 :
587 479671 : if (!data)
588 47692 : return 0;
589 :
590 430321 : ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
591 430321 : if (ret)
592 0 : krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
593 417574 : return ret;
594 : }
595 53689 : krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found",
596 : (unsigned long)type);
597 53689 : 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 99088 : krb5_pac_get_types(krb5_context context,
645 : krb5_const_pac p,
646 : size_t *len,
647 : uint32_t **types)
648 : {
649 3316 : size_t i;
650 :
651 99088 : *types = calloc(p->pac->numbuffers, sizeof(**types));
652 99088 : if (*types == NULL) {
653 0 : *len = 0;
654 0 : return krb5_enomem(context);
655 : }
656 891958 : for (i = 0; i < p->pac->numbuffers; i++)
657 792870 : (*types)[i] = p->pac->buffers[i].type;
658 99088 : *len = p->pac->numbuffers;
659 :
660 99088 : return 0;
661 : }
662 :
663 : /*
664 : *
665 : */
666 :
667 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
668 450738 : krb5_pac_is_trusted(krb5_const_pac p)
669 : {
670 450738 : return p->is_trusted;
671 : }
672 :
673 : /*
674 : *
675 : */
676 :
677 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
678 50550 : krb5_pac_set_trusted(krb5_pac p, krb5_boolean is_trusted)
679 : {
680 50550 : p->is_trusted = is_trusted;
681 50550 : }
682 :
683 : /*
684 : *
685 : */
686 :
687 : /*
688 : *
689 : */
690 :
691 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
692 446937 : krb5_pac_free(krb5_context context, krb5_pac pac)
693 : {
694 446937 : heim_release(pac);
695 446937 : }
696 :
697 : /*
698 : *
699 : */
700 :
701 : static krb5_error_code
702 104541 : 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 104541 : krb5_storage *sp = NULL;
710 2539 : uint32_t type;
711 2539 : krb5_error_code ret;
712 2539 : Checksum cksum;
713 2539 : size_t cksumsize;
714 :
715 104541 : memset(&cksum, 0, sizeof(cksum));
716 :
717 107080 : sp = krb5_storage_from_mem((char *)data->data + sig->offset,
718 104541 : sig->buffersize);
719 104541 : if (sp == NULL)
720 0 : return krb5_enomem(context);
721 :
722 104541 : krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
723 :
724 104541 : CHECK(ret, krb5_ret_uint32(sp, &type), out);
725 104541 : cksum.cksumtype = type;
726 :
727 104541 : ret = krb5_checksumsize(context, type, &cksumsize);
728 104541 : if (ret)
729 12 : goto out;
730 :
731 : /* Allow for RODCIdentifier trailer, see MS-PAC 2.8 */
732 104529 : if (cksumsize > (sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR))) {
733 0 : ret = EINVAL;
734 0 : goto out;
735 : }
736 104529 : cksum.checksum.length = cksumsize;
737 104529 : cksum.checksum.data = malloc(cksum.checksum.length);
738 104529 : if (cksum.checksum.data == NULL) {
739 0 : ret = krb5_enomem(context);
740 0 : goto out;
741 : }
742 104529 : ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length);
743 104529 : 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 104529 : 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 112432 : if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5 && !strict_cksumtype_match) {
766 0 : Checksum local_checksum;
767 :
768 7927 : memset(&local_checksum, 0, sizeof(local_checksum));
769 :
770 7927 : ret = HMAC_MD5_any_checksum(context, key, ptr, len,
771 : KRB5_KU_OTHER_CKSUM, &local_checksum);
772 :
773 7927 : 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 7927 : krb5_data_free(&local_checksum.checksum);
780 :
781 : } else {
782 96578 : krb5_crypto crypto = NULL;
783 :
784 96578 : ret = krb5_crypto_init(context, key, 0, &crypto);
785 96578 : if (ret)
786 0 : goto out;
787 :
788 96578 : ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM,
789 : ptr, len, &cksum);
790 96578 : krb5_crypto_destroy(context, crypto);
791 : }
792 104505 : free(cksum.checksum.data);
793 104505 : krb5_storage_free(sp);
794 :
795 104505 : 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 201610 : 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 201610 : krb5_crypto crypto = NULL;
813 6902 : krb5_error_code ret;
814 6902 : 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 201610 : if (cksumtype == (uint32_t)CKSUMTYPE_HMAC_MD5) {
826 16863 : ret = HMAC_MD5_any_checksum(context, key, data, datalen,
827 : KRB5_KU_OTHER_CKSUM, &cksum);
828 16863 : if (ret)
829 0 : return ret;
830 : } else {
831 184747 : ret = krb5_crypto_init(context, key, 0, &crypto);
832 184747 : if (ret)
833 0 : return ret;
834 :
835 184747 : ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0,
836 : data, datalen, &cksum);
837 184747 : krb5_crypto_destroy(context, crypto);
838 184747 : if (ret)
839 0 : return ret;
840 : }
841 201610 : 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 201610 : memcpy(sig, cksum.checksum.data, siglen);
848 201610 : free_Checksum(&cksum);
849 :
850 201610 : return 0;
851 : }
852 :
853 : static krb5_error_code
854 103951 : 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 2539 : krb5_error_code ret;
863 103951 : krb5_storage *sp = NULL;
864 2539 : uint16_t upn_length, upn_offset;
865 2539 : uint16_t dns_domain_name_length, dns_domain_name_offset;
866 2539 : uint16_t canon_princ_length, canon_princ_offset;
867 2539 : uint16_t sid_length, sid_offset;
868 103951 : char *upn = NULL;
869 103951 : char *dns_domain_name = NULL;
870 103951 : char *sam_name = NULL;
871 :
872 103951 : *upn_princ = NULL;
873 103951 : *flags = 0;
874 103951 : *canon_princ = NULL;
875 103951 : krb5_data_zero(sid);
876 :
877 106490 : sp = krb5_storage_from_readonly_mem((const char *)data->data + upndnsinfo->offset,
878 103951 : upndnsinfo->buffersize);
879 103951 : if (sp == NULL)
880 0 : return krb5_enomem(context);
881 :
882 103951 : krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
883 :
884 103951 : CHECK(ret, krb5_ret_uint16(sp, &upn_length), out);
885 103951 : CHECK(ret, krb5_ret_uint16(sp, &upn_offset), out);
886 103951 : CHECK(ret, krb5_ret_uint16(sp, &dns_domain_name_length), out);
887 103951 : CHECK(ret, krb5_ret_uint16(sp, &dns_domain_name_offset), out);
888 103951 : CHECK(ret, krb5_ret_uint32(sp, flags), out);
889 :
890 103951 : if (*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) {
891 103951 : CHECK(ret, krb5_ret_uint16(sp, &canon_princ_length), out);
892 103951 : CHECK(ret, krb5_ret_uint16(sp, &canon_princ_offset), out);
893 103951 : CHECK(ret, krb5_ret_uint16(sp, &sid_length), out);
894 103951 : 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 103951 : if (upn_offset) {
901 103951 : CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, upn_offset,
902 : upn_length, &upn), out);
903 : }
904 103951 : CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, dns_domain_name_offset,
905 : dns_domain_name_length, &dns_domain_name), out);
906 103951 : if ((*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) && canon_princ_offset) {
907 103951 : CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, canon_princ_offset,
908 : canon_princ_length, &sam_name), out);
909 : }
910 :
911 103951 : if (upn_offset) {
912 103951 : ret = krb5_parse_name_flags(context,
913 : upn,
914 : KRB5_PRINCIPAL_PARSE_ENTERPRISE |
915 : KRB5_PRINCIPAL_PARSE_NO_DEF_REALM,
916 : upn_princ);
917 103951 : if (ret)
918 0 : goto out;
919 :
920 103951 : ret = krb5_principal_set_realm(context, *upn_princ, dns_domain_name);
921 103951 : if (ret)
922 0 : goto out;
923 : }
924 :
925 103951 : if (canon_princ_offset) {
926 103951 : 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 103951 : if (ret)
932 0 : goto out;
933 :
934 103951 : ret = krb5_principal_set_realm(context, *canon_princ, dns_domain_name);
935 103951 : if (ret)
936 0 : goto out;
937 : }
938 :
939 103951 : if (sid_offset)
940 103951 : CHECK(ret, _krb5_ret_data_at_offset(sp, sid_offset, sid_length, sid), out);
941 :
942 103951 : out:
943 103951 : free(upn);
944 103951 : free(dns_domain_name);
945 103951 : free(sam_name);
946 :
947 103951 : krb5_storage_free(sp);
948 :
949 103951 : return ret;
950 : }
951 :
952 : /*
953 : *
954 : */
955 :
956 : #define NTTIME_EPOCH 0x019DB1DED53E8000LL
957 :
958 : static uint64_t
959 183605 : unix2nttime(time_t unix_time)
960 : {
961 5367 : long long wt;
962 183605 : wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
963 183605 : return wt;
964 : }
965 :
966 : static krb5_error_code
967 104019 : 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 2539 : krb5_error_code ret;
974 2539 : uint32_t time1, time2;
975 104019 : krb5_storage *sp = NULL;
976 2539 : uint16_t len;
977 104019 : char *s = NULL;
978 104019 : char *principal_string = NULL;
979 104019 : char *logon_string = NULL;
980 :
981 106558 : sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset,
982 104019 : logon_name->buffersize);
983 104019 : if (sp == NULL)
984 0 : return krb5_enomem(context);
985 :
986 104019 : krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
987 :
988 104019 : CHECK(ret, krb5_ret_uint32(sp, &time1), out);
989 104019 : CHECK(ret, krb5_ret_uint32(sp, &time2), out);
990 :
991 : {
992 2539 : uint64_t t1, t2;
993 104019 : t1 = unix2nttime(authtime);
994 104019 : 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 104019 : 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 104019 : CHECK(ret, krb5_ret_uint16(sp, &len), out);
1008 104019 : 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 104019 : s = malloc(len);
1015 104019 : if (s == NULL) {
1016 0 : krb5_storage_free(sp);
1017 0 : return krb5_enomem(context);
1018 : }
1019 104019 : ret = krb5_storage_read(sp, s, len);
1020 104019 : 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 104019 : krb5_storage_free(sp);
1027 : {
1028 104019 : size_t ucs2len = len / 2;
1029 2539 : uint16_t *ucs2;
1030 2539 : size_t u8len;
1031 104019 : unsigned int flags = WIND_RW_LE;
1032 :
1033 104019 : ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
1034 104019 : if (ucs2 == NULL) {
1035 0 : free(s);
1036 0 : return krb5_enomem(context);
1037 : }
1038 :
1039 104019 : ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
1040 104019 : free(s);
1041 104019 : 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 104019 : ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
1047 104019 : 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 104019 : u8len += 1; /* Add space for NUL */
1053 104019 : logon_string = malloc(u8len);
1054 104019 : if (logon_string == NULL) {
1055 0 : free(ucs2);
1056 0 : return krb5_enomem(context);
1057 : }
1058 104019 : ret = wind_ucs2utf8(ucs2, ucs2len, logon_string, &u8len);
1059 104019 : free(ucs2);
1060 104019 : 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 104019 : ret = krb5_unparse_name_flags(context, principal,
1067 : KRB5_PRINCIPAL_UNPARSE_NO_REALM |
1068 : KRB5_PRINCIPAL_UNPARSE_DISPLAY,
1069 : &principal_string);
1070 104019 : if (ret) {
1071 0 : free(logon_string);
1072 0 : return ret;
1073 : }
1074 :
1075 104019 : 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 104019 : free(logon_string);
1081 104019 : free(principal_string);
1082 104019 : 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 79586 : build_logon_name(krb5_context context,
1094 : time_t authtime,
1095 : krb5_const_principal principal,
1096 : krb5_data *logon)
1097 : {
1098 2828 : krb5_error_code ret;
1099 2828 : krb5_storage *sp;
1100 2828 : uint64_t t;
1101 79586 : char *s, *s2 = NULL;
1102 2828 : size_t s2_len;
1103 :
1104 79586 : t = unix2nttime(authtime);
1105 :
1106 79586 : krb5_data_zero(logon);
1107 :
1108 79586 : sp = krb5_storage_emem();
1109 79586 : if (sp == NULL)
1110 0 : return krb5_enomem(context);
1111 :
1112 79586 : krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1113 :
1114 79586 : CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out);
1115 79586 : CHECK(ret, krb5_store_uint32(sp, t >> 32), out);
1116 :
1117 79586 : ret = krb5_unparse_name_flags(context, principal,
1118 : KRB5_PRINCIPAL_UNPARSE_NO_REALM |
1119 : KRB5_PRINCIPAL_UNPARSE_DISPLAY,
1120 : &s);
1121 79586 : if (ret)
1122 0 : goto out;
1123 :
1124 : {
1125 2828 : size_t ucs2_len;
1126 2828 : uint16_t *ucs2;
1127 2828 : unsigned int flags;
1128 :
1129 79586 : ret = wind_utf8ucs2_length(s, &ucs2_len);
1130 79586 : 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 79586 : ucs2 = malloc(sizeof(ucs2[0]) * ucs2_len);
1137 79586 : if (ucs2 == NULL) {
1138 0 : free(s);
1139 0 : return krb5_enomem(context);
1140 : }
1141 :
1142 79586 : ret = wind_utf8ucs2(s, ucs2, &ucs2_len);
1143 79586 : 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 79586 : free(s);
1150 :
1151 79586 : s2_len = (ucs2_len + 1) * 2;
1152 79586 : s2 = malloc(s2_len);
1153 79586 : if (s2 == NULL) {
1154 0 : free(ucs2);
1155 0 : return krb5_enomem(context);
1156 : }
1157 :
1158 79586 : flags = WIND_RW_LE;
1159 79586 : ret = wind_ucs2write(ucs2, ucs2_len,
1160 : &flags, s2, &s2_len);
1161 79586 : free(ucs2);
1162 79586 : 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 79586 : s2_len = ucs2_len * 2;
1172 : }
1173 :
1174 79586 : CHECK(ret, krb5_store_uint16(sp, s2_len), out);
1175 :
1176 79586 : ret = krb5_storage_write(sp, s2, s2_len);
1177 79586 : if (ret != (int)s2_len) {
1178 0 : ret = krb5_enomem(context);
1179 0 : goto out;
1180 : }
1181 79586 : ret = krb5_storage_to_data(sp, logon);
1182 :
1183 79586 : out:
1184 79586 : free(s2);
1185 79586 : krb5_storage_free(sp);
1186 79586 : return ret;
1187 : }
1188 :
1189 : static krb5_error_code
1190 50349 : 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 1658 : krb5_error_code ret;
1196 50349 : krb5_storage *sp = NULL;
1197 1658 : uint32_t flags_length;
1198 :
1199 50349 : *pac_attributes = 0;
1200 :
1201 52007 : sp = krb5_storage_from_readonly_mem((const char *)data->data + attributes_info->offset,
1202 50349 : attributes_info->buffersize);
1203 50349 : if (sp == NULL)
1204 0 : return krb5_enomem(context);
1205 :
1206 50349 : krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1207 :
1208 50349 : ret = krb5_ret_uint32(sp, &flags_length);
1209 50349 : if (ret == 0) {
1210 50349 : if (flags_length > 32)
1211 0 : ret = krb5_ret_uint64(sp, pac_attributes);
1212 : else {
1213 50349 : uint32_t pac_attributes32 = 0;
1214 50349 : ret = krb5_ret_uint32(sp, &pac_attributes32);
1215 50349 : *pac_attributes = pac_attributes32;
1216 : }
1217 : }
1218 :
1219 50349 : krb5_storage_free(sp);
1220 :
1221 50349 : 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 104219 : 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 2539 : 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 104219 : 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 104219 : krb5_boolean strict_cksumtype_match = expect_full_sig;
1264 :
1265 104219 : if (pac->server_checksum == NULL) {
1266 6 : krb5_set_error_message(context, EINVAL, "PAC missing server checksum");
1267 6 : return EINVAL;
1268 : }
1269 104213 : if (pac->privsvr_checksum == NULL) {
1270 6 : krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum");
1271 6 : return EINVAL;
1272 : }
1273 104207 : if (pac->logon_name == NULL) {
1274 0 : krb5_set_error_message(context, EINVAL, "PAC missing logon name");
1275 0 : return EINVAL;
1276 : }
1277 104207 : 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 104204 : if (principal != NULL) {
1283 104019 : ret = verify_logonname(context, pac->logon_name, &pac->data, authtime,
1284 : principal);
1285 104019 : if (ret)
1286 0 : return ret;
1287 : }
1288 :
1289 104204 : if (pac->server_checksum->buffersize < 4 ||
1290 104204 : pac->privsvr_checksum->buffersize < 4)
1291 0 : return EINVAL;
1292 :
1293 104204 : if (server != NULL || privsvr != NULL)
1294 : {
1295 2539 : 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 104204 : ret = krb5_copy_data(context, &pac->data, ©);
1303 104204 : if (ret)
1304 39 : return ret;
1305 :
1306 104204 : memset((char *)copy->data + pac->server_checksum->offset + 4,
1307 : 0,
1308 104204 : pac->server_checksum->buffersize - 4);
1309 :
1310 104204 : memset((char *)copy->data + pac->privsvr_checksum->offset + 4,
1311 : 0,
1312 104204 : pac->privsvr_checksum->buffersize - 4);
1313 :
1314 104204 : if (server != NULL) {
1315 106558 : ret = verify_checksum(context,
1316 104019 : pac->server_checksum,
1317 101480 : &pac->data,
1318 101480 : copy->data,
1319 104019 : copy->length,
1320 : server,
1321 : strict_cksumtype_match);
1322 104019 : if (ret) {
1323 24 : krb5_free_data(context, copy);
1324 24 : return ret;
1325 : }
1326 : }
1327 :
1328 104180 : 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 104165 : krb5_free_data(context, copy);
1351 : }
1352 104165 : 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 104150 : if (pac->upn_dns_info &&
1382 104106 : pac->upn_princ == NULL && pac->canon_princ == NULL && pac->sid.data == NULL) {
1383 103951 : 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 103951 : if (ret)
1387 0 : return ret;
1388 : }
1389 :
1390 104150 : if (pac->attributes_info) {
1391 50349 : ret = parse_attributes_info(context, pac->attributes_info, &pac->data,
1392 : &pac->pac_attributes);
1393 50349 : if (ret)
1394 0 : return ret;
1395 : }
1396 :
1397 101611 : return 0;
1398 : }
1399 :
1400 : /*
1401 : *
1402 : */
1403 :
1404 : static krb5_error_code
1405 358179 : fill_zeros(krb5_context context, krb5_storage *sp, size_t len)
1406 : {
1407 11847 : ssize_t sret;
1408 11847 : size_t l;
1409 :
1410 917968 : while (len) {
1411 559789 : l = len;
1412 559789 : if (l > sizeof(zeros))
1413 194708 : l = sizeof(zeros);
1414 559789 : sret = krb5_storage_write(sp, zeros, l);
1415 559789 : if (sret != l)
1416 0 : return krb5_enomem(context);
1417 :
1418 559789 : len -= sret;
1419 : }
1420 346332 : return 0;
1421 : }
1422 :
1423 : static krb5_error_code
1424 159172 : pac_checksum(krb5_context context,
1425 : const krb5_keyblock *key,
1426 : uint32_t *cksumtype,
1427 : size_t *cksumsize)
1428 : {
1429 5656 : krb5_cksumtype cktype;
1430 5656 : krb5_error_code ret;
1431 159172 : krb5_crypto crypto = NULL;
1432 :
1433 159172 : ret = krb5_crypto_init(context, key, 0, &crypto);
1434 159172 : if (ret)
1435 0 : return ret;
1436 :
1437 159172 : ret = krb5_crypto_get_checksum_type(context, crypto, &cktype);
1438 159172 : krb5_crypto_destroy(context, crypto);
1439 159172 : if (ret)
1440 0 : return ret;
1441 :
1442 159172 : if (krb5_checksum_is_keyed(context, cktype) == FALSE) {
1443 0 : *cksumtype = CKSUMTYPE_HMAC_MD5;
1444 0 : *cksumsize = 16;
1445 : }
1446 :
1447 159172 : ret = krb5_checksumsize(context, cktype, cksumsize);
1448 159172 : if (ret)
1449 0 : return ret;
1450 :
1451 159172 : *cksumtype = (uint32_t)cktype;
1452 :
1453 159172 : return 0;
1454 : }
1455 :
1456 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1457 79586 : _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 2828 : krb5_error_code ret;
1471 79586 : krb5_storage *sp = NULL, *spdata = NULL;
1472 2828 : uint32_t end;
1473 2828 : size_t server_size, priv_size;
1474 79586 : uint32_t server_offset = 0, priv_offset = 0, ticket_offset = 0, full_offset = 0;
1475 79586 : uint32_t server_cksumtype = 0, priv_cksumtype = 0;
1476 79586 : uint32_t num = 0;
1477 2828 : uint32_t i, sz;
1478 2828 : krb5_data logon, d;
1479 :
1480 79586 : krb5_data_zero(&d);
1481 79586 : 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 610233 : for (i = 0; i < p->pac->numbuffers; i++) {
1491 527819 : if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
1492 48636 : if (p->server_checksum == NULL) {
1493 48636 : p->server_checksum = &p->pac->buffers[i];
1494 : }
1495 48636 : 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 479183 : } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
1502 48636 : if (p->privsvr_checksum == NULL) {
1503 48636 : p->privsvr_checksum = &p->pac->buffers[i];
1504 : }
1505 48636 : 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 414825 : } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
1512 79586 : if (p->logon_name == NULL) {
1513 79586 : p->logon_name = &p->pac->buffers[i];
1514 : }
1515 79586 : 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 338067 : } else if (p->pac->buffers[i].type == PAC_UPN_DNS_INFO) {
1522 79586 : if (p->upn_dns_info == NULL) {
1523 79586 : p->upn_dns_info = &p->pac->buffers[i];
1524 : }
1525 79586 : 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 261309 : } else if (p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) {
1532 130 : if (p->ticket_checksum == NULL) {
1533 130 : p->ticket_checksum = &p->pac->buffers[i];
1534 : }
1535 130 : 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 261179 : } else if (p->pac->buffers[i].type == PAC_ATTRIBUTES_INFO) {
1542 55769 : if (p->attributes_info == NULL) {
1543 55769 : p->attributes_info = &p->pac->buffers[i];
1544 : }
1545 55769 : 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 207615 : } else if (p->pac->buffers[i].type == PAC_FULL_CHECKSUM) {
1552 130 : if (p->full_checksum == NULL) {
1553 130 : p->full_checksum = &p->pac->buffers[i];
1554 : }
1555 130 : 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 79586 : if (p->logon_name == NULL)
1566 0 : num++;
1567 79586 : if (p->server_checksum == NULL)
1568 30950 : num++;
1569 79586 : if (p->privsvr_checksum == NULL)
1570 30950 : num++;
1571 79586 : if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL)
1572 21089 : num++;
1573 79586 : if (add_full_sig && p->full_checksum == NULL)
1574 21089 : num++;
1575 :
1576 : /* Allocate any missing-but-necessary buffers */
1577 79586 : if (num) {
1578 1793 : void *ptr;
1579 1793 : uint32_t old_len, len;
1580 :
1581 51389 : 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 51389 : ret = pac_header_size(context, p->pac->numbuffers, &old_len);
1587 49596 : if (ret == 0)
1588 51389 : ret = pac_header_size(context, p->pac->numbuffers + num, &len);
1589 51389 : if (ret)
1590 0 : goto out;
1591 :
1592 51389 : ptr = realloc(p->pac, len);
1593 51389 : if (ptr == NULL) {
1594 0 : ret = krb5_enomem(context);
1595 0 : goto out;
1596 : }
1597 51389 : memset((char *)ptr + old_len, 0, len - old_len);
1598 51389 : p->pac = ptr;
1599 :
1600 :
1601 51389 : 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 51389 : if (p->server_checksum == NULL) {
1606 30950 : p->server_checksum = &p->pac->buffers[p->pac->numbuffers++];
1607 30950 : p->server_checksum->type = PAC_SERVER_CHECKSUM;
1608 : }
1609 51389 : if (p->privsvr_checksum == NULL) {
1610 30950 : p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++];
1611 30950 : p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM;
1612 : }
1613 51389 : if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) {
1614 21089 : p->ticket_checksum = &p->pac->buffers[p->pac->numbuffers++];
1615 21089 : p->ticket_checksum->type = PAC_TICKET_CHECKSUM;
1616 : }
1617 51389 : if (add_full_sig && p->full_checksum == NULL) {
1618 21089 : p->full_checksum = &p->pac->buffers[p->pac->numbuffers++];
1619 21089 : memset(p->full_checksum, 0, sizeof(*p->full_checksum));
1620 21089 : p->full_checksum->type = PAC_FULL_CHECKSUM;
1621 : }
1622 : }
1623 :
1624 : /* Calculate LOGON NAME */
1625 79586 : ret = build_logon_name(context, authtime, principal, &logon);
1626 :
1627 : /* Set lengths for checksum */
1628 79586 : if (ret == 0)
1629 79586 : ret = pac_checksum(context, server_key, &server_cksumtype, &server_size);
1630 :
1631 79586 : if (ret == 0)
1632 79586 : ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size);
1633 :
1634 : /* Encode PAC */
1635 79586 : if (ret == 0) {
1636 79586 : sp = krb5_storage_emem();
1637 79586 : if (sp == NULL)
1638 0 : ret = krb5_enomem(context);
1639 : }
1640 :
1641 79586 : if (ret == 0) {
1642 79586 : krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1643 79586 : spdata = krb5_storage_emem();
1644 79586 : if (spdata == NULL)
1645 0 : ret = krb5_enomem(context);
1646 : }
1647 :
1648 79586 : if (ret)
1649 0 : goto out;
1650 :
1651 79586 : krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE);
1652 :
1653 : /* `sp' has the header, `spdata' has the buffers */
1654 79586 : CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out);
1655 79586 : CHECK(ret, krb5_store_uint32(sp, p->pac->version), out);
1656 :
1657 79586 : ret = pac_header_size(context, p->pac->numbuffers, &end);
1658 79586 : 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 711483 : for (i = 0; i < p->pac->numbuffers; i++) {
1684 22624 : uint32_t len;
1685 22624 : size_t sret;
1686 631897 : void *ptr = NULL;
1687 :
1688 : /* store data */
1689 :
1690 631897 : if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
1691 79586 : 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 79586 : len = server_size + 4;
1697 79586 : 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 79586 : server_offset = end + 4;
1703 79586 : CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out);
1704 79586 : CHECK(ret, fill_zeros(context, spdata, server_size), out);
1705 552311 : } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
1706 79586 : 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 79586 : len = priv_size + 4;
1712 79586 : 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 79586 : priv_offset = end + 4;
1718 79586 : CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
1719 79586 : CHECK(ret, fill_zeros(context, spdata, priv_size), out);
1720 79586 : if (rodc_id != 0) {
1721 2862 : 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 2862 : len += sizeof(rodc_id);
1727 2862 : CHECK(ret, fill_zeros(context, spdata, sizeof(rodc_id)), out);
1728 : }
1729 472725 : } else if (p->ticket_sign_data.length != 0 &&
1730 123892 : p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) {
1731 21219 : 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 21219 : len = priv_size + 4;
1737 21219 : 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 21219 : ticket_offset = end + 4;
1743 21219 : CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
1744 21219 : CHECK(ret, fill_zeros(context, spdata, priv_size), out);
1745 21219 : 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 451506 : } else if (add_full_sig &&
1755 103296 : p->pac->buffers[i].type == PAC_FULL_CHECKSUM) {
1756 21219 : 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 21219 : len = priv_size + 4;
1762 21219 : 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 21219 : full_offset = end + 4;
1768 21219 : CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
1769 21219 : CHECK(ret, fill_zeros(context, spdata, priv_size), out);
1770 21219 : 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 430287 : } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
1780 79586 : len = krb5_storage_write(spdata, logon.data, logon.length);
1781 79586 : if (logon.length != len) {
1782 0 : ret = KRB5KDC_ERR_BADOPTION;
1783 0 : goto out;
1784 : }
1785 : } else {
1786 350701 : len = p->pac->buffers[i].buffersize;
1787 350701 : ptr = (char *)p->data.data + p->pac->buffers[i].offset;
1788 :
1789 350701 : sret = krb5_storage_write(spdata, ptr, len);
1790 350701 : 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 631897 : CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out);
1799 631897 : CHECK(ret, krb5_store_uint32(sp, len), out);
1800 631897 : CHECK(ret, krb5_store_uint64(sp, end), out); /* offset */
1801 :
1802 : /* advance data endpointer and align */
1803 : {
1804 22624 : uint32_t e;
1805 :
1806 631897 : ret = pac_aligned_size(context, end, len, &e);
1807 631897 : if (ret == 0 && end + len != e)
1808 152133 : ret = fill_zeros(context, spdata, e - (end + len));
1809 631897 : if (ret)
1810 0 : goto out;
1811 631897 : end = e;
1812 : }
1813 :
1814 : }
1815 :
1816 : /* assert (server_offset != 0 && priv_offset != 0); */
1817 :
1818 : /* export PAC */
1819 79586 : if (ret == 0)
1820 79586 : ret = krb5_storage_to_data(spdata, &d);
1821 79586 : if (ret == 0) {
1822 79586 : sz = krb5_storage_write(sp, d.data, d.length);
1823 79586 : if (sz != d.length) {
1824 0 : krb5_data_free(&d);
1825 0 : ret = krb5_enomem(context);
1826 0 : goto out;
1827 : }
1828 : }
1829 79586 : krb5_data_free(&d);
1830 :
1831 79586 : if (ret == 0)
1832 79586 : ret = krb5_storage_to_data(sp, &d);
1833 :
1834 : /* sign */
1835 79586 : if (ret == 0 && p->ticket_sign_data.length)
1836 21219 : ret = create_checksum(context, priv_key, priv_cksumtype,
1837 : p->ticket_sign_data.data,
1838 : p->ticket_sign_data.length,
1839 21219 : (char *)d.data + ticket_offset, priv_size);
1840 79586 : if (ret == 0 && add_full_sig)
1841 21219 : ret = create_checksum(context, priv_key, priv_cksumtype,
1842 : d.data, d.length,
1843 21219 : (char *)d.data + full_offset, priv_size);
1844 79586 : 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 79586 : if (ret == 0)
1856 79586 : ret = create_checksum(context, server_key, server_cksumtype,
1857 : d.data, d.length,
1858 79586 : (char *)d.data + server_offset, server_size);
1859 79586 : if (ret == 0)
1860 79586 : ret = create_checksum(context, priv_key, priv_cksumtype,
1861 76758 : (char *)d.data + server_offset, server_size,
1862 79586 : (char *)d.data + priv_offset, priv_size);
1863 79586 : if (ret == 0 && rodc_id != 0) {
1864 2862 : void *buf = (char *)d.data + priv_offset + priv_size;
1865 2862 : krb5_storage *rs = krb5_storage_from_mem(buf, sizeof(rodc_id));
1866 2862 : if (rs == NULL)
1867 0 : ret = krb5_enomem(context);
1868 : else
1869 2862 : krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE);
1870 2862 : if (ret == 0)
1871 2862 : ret = krb5_store_uint16(rs, rodc_id);
1872 2862 : krb5_storage_free(rs);
1873 : }
1874 :
1875 79586 : if (ret)
1876 0 : goto out;
1877 :
1878 : /* done */
1879 79586 : *data = d;
1880 :
1881 79586 : krb5_data_free(&logon);
1882 79586 : krb5_storage_free(sp);
1883 79586 : krb5_storage_free(spdata);
1884 :
1885 79586 : 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 49637 : _krb5_pac_get_canon_principal(krb5_context context,
1953 : krb5_const_pac pac,
1954 : krb5_principal *canon_princ)
1955 : {
1956 49637 : *canon_princ = NULL;
1957 :
1958 49637 : 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 49637 : return krb5_copy_principal(context, pac->canon_princ, canon_princ);
1965 : }
1966 :
1967 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1968 49637 : _krb5_pac_get_attributes_info(krb5_context context,
1969 : krb5_const_pac pac,
1970 : uint64_t *pac_attributes)
1971 : {
1972 49637 : *pac_attributes = 0;
1973 :
1974 49637 : 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 49467 : *pac_attributes = pac->pac_attributes;
1981 :
1982 49467 : 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 50630 : _krb5_kdc_pac_ticket_parse(krb5_context context,
1990 : EncTicketPart *tkt,
1991 : krb5_boolean *signedticket,
1992 : krb5_pac *ppac)
1993 : {
1994 50630 : AuthorizationData *ad = tkt->authorization_data;
1995 50630 : krb5_pac pac = NULL;
1996 1658 : unsigned i, j;
1997 50630 : size_t len = 0;
1998 50630 : krb5_error_code ret = 0;
1999 :
2000 50630 : *signedticket = FALSE;
2001 50630 : *ppac = NULL;
2002 :
2003 50630 : if (ad == NULL || ad->len == 0)
2004 26 : return 0;
2005 :
2006 101225 : for (i = 0; i < ad->len; i++) {
2007 1658 : AuthorizationData child;
2008 :
2009 50621 : if (ad->val[i].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
2010 0 : ret = KRB5KDC_ERR_BADOPTION;
2011 0 : goto out;
2012 : }
2013 :
2014 50621 : if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
2015 1 : continue;
2016 :
2017 50620 : ret = decode_AuthorizationData(ad->val[i].ad_data.data,
2018 48962 : ad->val[i].ad_data.length,
2019 : &child,
2020 : NULL);
2021 50620 : 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 101233 : for (j = 0; j < child.len; j++) {
2028 50613 : krb5_data adifr_data = ad->val[i].ad_data;
2029 50613 : krb5_data pac_data = child.val[j].ad_data;
2030 1658 : krb5_data recoded_adifr;
2031 :
2032 50613 : if (child.val[j].ad_type != KRB5_AUTHDATA_WIN2K_PAC)
2033 50387 : continue;
2034 :
2035 50597 : if (pac != NULL) {
2036 0 : free_AuthorizationData(&child);
2037 0 : ret = KRB5KDC_ERR_BADOPTION;
2038 0 : goto out;
2039 : }
2040 :
2041 50597 : ret = krb5_pac_parse(context,
2042 48939 : pac_data.data,
2043 : pac_data.length,
2044 : &pac);
2045 50597 : if (ret) {
2046 0 : free_AuthorizationData(&child);
2047 0 : goto out;
2048 : }
2049 :
2050 50597 : if (pac->ticket_checksum == NULL)
2051 50371 : 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 226 : child.val[j].ad_data = single_zero_pac;
2059 :
2060 226 : ASN1_MALLOC_ENCODE(AuthorizationData, recoded_adifr.data,
2061 : recoded_adifr.length, &child, &len, ret);
2062 226 : if (recoded_adifr.length != len)
2063 0 : krb5_abortx(context, "Internal error in ASN.1 encoder");
2064 :
2065 226 : child.val[j].ad_data = pac_data;
2066 :
2067 226 : if (ret) {
2068 0 : free_AuthorizationData(&child);
2069 0 : goto out;
2070 : }
2071 :
2072 226 : ad->val[i].ad_data = recoded_adifr;
2073 :
2074 226 : ASN1_MALLOC_ENCODE(EncTicketPart,
2075 : pac->ticket_sign_data.data,
2076 : pac->ticket_sign_data.length, tkt, &len,
2077 : ret);
2078 226 : if (pac->ticket_sign_data.length != len)
2079 0 : krb5_abortx(context, "Internal error in ASN.1 encoder");
2080 :
2081 226 : ad->val[i].ad_data = adifr_data;
2082 226 : krb5_data_free(&recoded_adifr);
2083 :
2084 226 : if (ret) {
2085 0 : free_AuthorizationData(&child);
2086 0 : goto out;
2087 : }
2088 :
2089 226 : *signedticket = TRUE;
2090 : }
2091 50620 : free_AuthorizationData(&child);
2092 : }
2093 :
2094 50604 : out:
2095 50604 : if (ret) {
2096 0 : krb5_pac_free(context, pac);
2097 0 : return ret;
2098 : }
2099 :
2100 50604 : *ppac = pac;
2101 :
2102 50604 : return 0;
2103 : }
2104 :
2105 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2106 49286 : _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 1658 : krb5_error_code ret;
2120 1658 : krb5_data tkt_data;
2121 1658 : krb5_data rspac;
2122 :
2123 49286 : krb5_data_zero(&rspac);
2124 49286 : krb5_data_zero(&tkt_data);
2125 :
2126 49286 : krb5_data_free(&pac->ticket_sign_data);
2127 :
2128 49286 : if (add_ticket_sig) {
2129 21219 : size_t len = 0;
2130 :
2131 21219 : ret = _kdc_tkt_insert_pac(context, tkt, &single_zero_pac);
2132 21219 : if (ret)
2133 0 : return ret;
2134 :
2135 21219 : ASN1_MALLOC_ENCODE(EncTicketPart, tkt_data.data, tkt_data.length,
2136 : tkt, &len, ret);
2137 21219 : if(tkt_data.length != len)
2138 0 : krb5_abortx(context, "Internal error in ASN.1 encoder");
2139 21219 : if (ret)
2140 0 : return ret;
2141 :
2142 21219 : ret = remove_AuthorizationData(tkt->authorization_data, 0);
2143 21219 : if (ret) {
2144 0 : krb5_data_free(&tkt_data);
2145 0 : return ret;
2146 : }
2147 :
2148 21219 : pac->ticket_sign_data = tkt_data;
2149 : }
2150 :
2151 49286 : 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 49286 : if (ret == 0)
2156 49286 : ret = _kdc_tkt_insert_pac(context, tkt, &rspac);
2157 49286 : krb5_data_free(&rspac);
2158 49286 : return ret;
2159 : }
|