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