1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/pac.c */
3 /*
4 * Copyright 2008 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 #include "k5-int.h"
28 #include "int-proto.h"
29 #include "authdata.h"
30 #include "k5-input.h"
31
32 #define MAX_BUFFERS 4096
33
34 /*
35 * Add a buffer containing data to pac's metadata and encoding. If zerofill is
36 * true, data->data must be NULL and the buffer will be zero-filled with length
37 * data->length.
38 */
39 krb5_error_code
k5_pac_add_buffer(krb5_context context,krb5_pac pac,uint32_t type,const krb5_data * data,krb5_boolean zerofill,krb5_data * data_out)40 k5_pac_add_buffer(krb5_context context, krb5_pac pac, uint32_t type,
41 const krb5_data *data, krb5_boolean zerofill,
42 krb5_data *data_out)
43 {
44 struct k5_pac_buffer *nbufs;
45 size_t header_len, i, pad = 0;
46 char *ndata, *bufdata;
47
48 assert((data->data == NULL) == zerofill);
49
50 /* Check for an existing buffer of this type. */
51 if (k5_pac_locate_buffer(context, pac, type, NULL) == 0)
52 return EEXIST;
53
54 if (pac->nbuffers >= MAX_BUFFERS)
55 return ERANGE;
56 nbufs = realloc(pac->buffers, (pac->nbuffers + 1) * sizeof(*pac->buffers));
57 if (nbufs == NULL)
58 return ENOMEM;
59 pac->buffers = nbufs;
60
61 header_len = PACTYPE_LENGTH + pac->nbuffers * PAC_INFO_BUFFER_LENGTH;
62
63 if (data->length % PAC_ALIGNMENT)
64 pad = PAC_ALIGNMENT - (data->length % PAC_ALIGNMENT);
65 ndata = realloc(pac->data.data,
66 pac->data.length + PAC_INFO_BUFFER_LENGTH +
67 data->length + pad);
68 if (ndata == NULL)
69 return ENOMEM;
70 pac->data.data = ndata;
71
72 /* Update the offsets of existing buffers. */
73 for (i = 0; i < pac->nbuffers; i++)
74 pac->buffers[i].offset += PAC_INFO_BUFFER_LENGTH;
75
76 /* Make room for the new buffer's metadata. */
77 memmove(pac->data.data + header_len + PAC_INFO_BUFFER_LENGTH,
78 pac->data.data + header_len,
79 pac->data.length - header_len);
80 memset(pac->data.data + header_len, 0, PAC_INFO_BUFFER_LENGTH);
81
82 /* Initialize the new buffer. */
83 pac->buffers[i].type = type;
84 pac->buffers[i].size = data->length;
85 pac->buffers[i].offset = pac->data.length + PAC_INFO_BUFFER_LENGTH;
86 assert((pac->buffers[i].offset % PAC_ALIGNMENT) == 0);
87
88 /* Copy in new PAC data and zero padding bytes. */
89 bufdata = pac->data.data + pac->buffers[i].offset;
90 if (zerofill)
91 memset(bufdata, 0, data->length);
92 else
93 memcpy(bufdata, data->data, data->length);
94 memset(bufdata + data->length, 0, pad);
95
96 pac->nbuffers++;
97 pac->data.length += PAC_INFO_BUFFER_LENGTH + data->length + pad;
98
99 if (data_out != NULL)
100 *data_out = make_data(bufdata, data->length);
101
102 pac->verified = FALSE;
103
104 return 0;
105 }
106
107 krb5_error_code KRB5_CALLCONV
krb5_pac_add_buffer(krb5_context context,krb5_pac pac,uint32_t type,const krb5_data * data)108 krb5_pac_add_buffer(krb5_context context, krb5_pac pac, uint32_t type,
109 const krb5_data *data)
110 {
111 return k5_pac_add_buffer(context, pac, type, data, FALSE, NULL);
112 }
113
114 /*
115 * Free a PAC
116 */
117 void KRB5_CALLCONV
krb5_pac_free(krb5_context context,krb5_pac pac)118 krb5_pac_free(krb5_context context, krb5_pac pac)
119 {
120 if (pac != NULL) {
121 zapfree(pac->data.data, pac->data.length);
122 free(pac->buffers);
123 zapfree(pac, sizeof(*pac));
124 }
125 }
126
127 krb5_error_code
k5_pac_locate_buffer(krb5_context context,const krb5_pac pac,uint32_t type,krb5_data * data_out)128 k5_pac_locate_buffer(krb5_context context, const krb5_pac pac, uint32_t type,
129 krb5_data *data_out)
130 {
131 struct k5_pac_buffer *buffer = NULL;
132 size_t i;
133
134 if (pac == NULL)
135 return EINVAL;
136
137 for (i = 0; i < pac->nbuffers; i++) {
138 if (pac->buffers[i].type == type) {
139 if (buffer == NULL)
140 buffer = &pac->buffers[i];
141 else
142 return EINVAL;
143 }
144 }
145
146 if (buffer == NULL)
147 return ENOENT;
148
149 assert(buffer->offset <= pac->data.length);
150 assert(buffer->size <= pac->data.length - buffer->offset);
151
152 if (data_out != NULL)
153 *data_out = make_data(pac->data.data + buffer->offset, buffer->size);
154
155 return 0;
156 }
157
158 /*
159 * Find a buffer and copy data into output
160 */
161 krb5_error_code KRB5_CALLCONV
krb5_pac_get_buffer(krb5_context context,krb5_pac pac,uint32_t type,krb5_data * data_out)162 krb5_pac_get_buffer(krb5_context context, krb5_pac pac, uint32_t type,
163 krb5_data *data_out)
164 {
165 krb5_data d;
166 krb5_error_code ret;
167
168 ret = k5_pac_locate_buffer(context, pac, type, &d);
169 if (ret)
170 return ret;
171
172 data_out->data = k5memdup(d.data, d.length, &ret);
173 if (data_out->data == NULL)
174 return ret;
175 data_out->length = d.length;
176 return 0;
177 }
178
179 /*
180 * Return an array of the types of data in the PAC
181 */
182 krb5_error_code KRB5_CALLCONV
krb5_pac_get_types(krb5_context context,krb5_pac pac,size_t * len_out,uint32_t ** types_out)183 krb5_pac_get_types(krb5_context context, krb5_pac pac, size_t *len_out,
184 uint32_t **types_out)
185 {
186 size_t i;
187
188 *types_out = calloc(pac->nbuffers, sizeof(*types_out));
189 if (*types_out == NULL)
190 return ENOMEM;
191
192 *len_out = pac->nbuffers;
193
194 for (i = 0; i < pac->nbuffers; i++)
195 (*types_out)[i] = pac->buffers[i].type;
196
197 return 0;
198 }
199
200 krb5_error_code KRB5_CALLCONV
krb5_pac_init(krb5_context context,krb5_pac * pac_out)201 krb5_pac_init(krb5_context context, krb5_pac *pac_out)
202 {
203 krb5_error_code ret;
204 krb5_pac pac;
205
206 *pac_out = NULL;
207
208 pac = malloc(sizeof(*pac));
209 if (pac == NULL)
210 return ENOMEM;
211
212 pac->nbuffers = 0;
213 pac->buffers = NULL;
214 pac->version = 0;
215
216 pac->data.length = PACTYPE_LENGTH;
217 pac->data.data = k5alloc(pac->data.length, &ret);
218 if (pac->data.data == NULL) {
219 free(pac);
220 return ret;
221 }
222
223 pac->verified = FALSE;
224
225 *pac_out = pac;
226 return 0;
227 }
228
229 static krb5_error_code
copy_pac(krb5_context context,krb5_pac src,krb5_pac * dst_out)230 copy_pac(krb5_context context, krb5_pac src, krb5_pac *dst_out)
231 {
232 krb5_error_code ret;
233 krb5_pac pac;
234
235 *dst_out = NULL;
236
237 pac = k5alloc(sizeof(*pac), &ret);
238 if (pac == NULL)
239 goto fail;
240
241 pac->buffers = k5memdup(src->buffers,
242 src->nbuffers * sizeof(*pac->buffers), &ret);
243 if (pac->buffers == NULL)
244 goto fail;
245
246 ret = krb5int_copy_data_contents(context, &src->data, &pac->data);
247 if (ret)
248 goto fail;
249
250 pac->nbuffers = src->nbuffers;
251 pac->version = src->version;
252 pac->verified = src->verified;
253
254 *dst_out = pac;
255 return 0;
256
257 fail:
258 krb5_pac_free(context, pac);
259 return ret;
260 }
261
262 /* Parse the supplied data into an allocated PAC. */
263 krb5_error_code KRB5_CALLCONV
krb5_pac_parse(krb5_context context,const void * ptr,size_t len,krb5_pac * ppac)264 krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
265 krb5_pac *ppac)
266 {
267 krb5_error_code ret;
268 size_t i;
269 krb5_pac pac;
270 size_t header_len;
271 uint32_t nbuffers, version;
272 struct k5input in;
273 char *ndata;
274
275 *ppac = NULL;
276
277 k5_input_init(&in, ptr, len);
278
279 nbuffers = k5_input_get_uint32_le(&in);
280 version = k5_input_get_uint32_le(&in);
281 if (in.status || version != 0)
282 return EINVAL;
283
284 if (nbuffers < 1 || nbuffers > MAX_BUFFERS)
285 return ERANGE;
286
287 header_len = PACTYPE_LENGTH + (nbuffers * PAC_INFO_BUFFER_LENGTH);
288 if (len < header_len)
289 return ERANGE;
290
291 ret = krb5_pac_init(context, &pac);
292 if (ret)
293 return ret;
294
295 pac->buffers = k5calloc(nbuffers, sizeof(*pac->buffers), &ret);
296 if (ret)
297 goto fail;
298
299 pac->nbuffers = nbuffers;
300 pac->version = version;
301
302 for (i = 0; i < nbuffers; i++) {
303 struct k5_pac_buffer *buffer = &pac->buffers[i];
304
305 buffer->type = k5_input_get_uint32_le(&in);
306 buffer->size = k5_input_get_uint32_le(&in);
307 buffer->offset = k5_input_get_uint64_le(&in);
308
309 if (in.status || buffer->offset % PAC_ALIGNMENT) {
310 ret = EINVAL;
311 goto fail;
312 }
313 if (buffer->offset < header_len || buffer->offset > len ||
314 buffer->size > len - buffer->offset) {
315 ret = ERANGE;
316 goto fail;
317 }
318 }
319
320 ndata = realloc(pac->data.data, len);
321 if (ndata == NULL) {
322 krb5_pac_free(context, pac);
323 return ENOMEM;
324 }
325 pac->data.data = ndata;
326 memcpy(ndata, ptr, len);
327
328 pac->data.length = len;
329
330 *ppac = pac;
331
332 return 0;
333
334 fail:
335 krb5_pac_free(context, pac);
336 return ret;
337 }
338
339 static krb5_error_code
k5_time_to_seconds_since_1970(uint64_t ntTime,krb5_timestamp * elapsedSeconds)340 k5_time_to_seconds_since_1970(uint64_t ntTime, krb5_timestamp *elapsedSeconds)
341 {
342 uint64_t abstime = ntTime / 10000000 - NT_TIME_EPOCH;
343
344 if (abstime > UINT32_MAX)
345 return ERANGE;
346 *elapsedSeconds = abstime;
347 return 0;
348 }
349
350 krb5_error_code
k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds,uint64_t * ntTime)351 k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, uint64_t *ntTime)
352 {
353 *ntTime = (uint32_t)elapsedSeconds;
354 *ntTime += NT_TIME_EPOCH;
355 *ntTime *= 10000000;
356 return 0;
357 }
358
359 krb5_error_code KRB5_CALLCONV
krb5_pac_get_client_info(krb5_context context,const krb5_pac pac,krb5_timestamp * authtime_out,char ** princname_out)360 krb5_pac_get_client_info(krb5_context context, const krb5_pac pac,
361 krb5_timestamp *authtime_out, char **princname_out)
362 {
363 krb5_error_code ret;
364 krb5_data client_info;
365 char *pac_princname;
366 unsigned char *p;
367 krb5_timestamp pac_authtime;
368 uint16_t pac_princname_length;
369 uint64_t pac_nt_authtime;
370
371 if (authtime_out != NULL)
372 *authtime_out = 0;
373 *princname_out = NULL;
374
375 ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_CLIENT_INFO,
376 &client_info);
377 if (ret)
378 return ret;
379
380 if (client_info.length < PAC_CLIENT_INFO_LENGTH)
381 return ERANGE;
382
383 p = (unsigned char *)client_info.data;
384 pac_nt_authtime = load_64_le(p);
385 p += 8;
386 pac_princname_length = load_16_le(p);
387 p += 2;
388
389 ret = k5_time_to_seconds_since_1970(pac_nt_authtime, &pac_authtime);
390 if (ret)
391 return ret;
392
393 if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
394 pac_princname_length % 2)
395 return ERANGE;
396
397 ret = k5_utf16le_to_utf8(p, pac_princname_length, &pac_princname);
398 if (ret)
399 return ret;
400
401 if (authtime_out != NULL)
402 *authtime_out = pac_authtime;
403 *princname_out = pac_princname;
404
405 return 0;
406 }
407
408 krb5_error_code
k5_pac_validate_client(krb5_context context,const krb5_pac pac,krb5_timestamp authtime,krb5_const_principal principal,krb5_boolean with_realm)409 k5_pac_validate_client(krb5_context context, const krb5_pac pac,
410 krb5_timestamp authtime, krb5_const_principal principal,
411 krb5_boolean with_realm)
412 {
413 krb5_error_code ret;
414 char *pac_princname, *princname;
415 krb5_timestamp pac_authtime;
416 int flags = 0;
417
418 ret = krb5_pac_get_client_info(context, pac, &pac_authtime,
419 &pac_princname);
420 if (ret)
421 return ret;
422
423 flags = KRB5_PRINCIPAL_UNPARSE_DISPLAY;
424 if (!with_realm)
425 flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
426
427 ret = krb5_unparse_name_flags(context, principal, flags, &princname);
428 if (ret) {
429 free(pac_princname);
430 return ret;
431 }
432
433 if (pac_authtime != authtime || strcmp(pac_princname, princname) != 0)
434 ret = KRB5KRB_AP_WRONG_PRINC;
435
436 free(pac_princname);
437 krb5_free_unparsed_name(context, princname);
438
439 return ret;
440 }
441
442 /* Zero out the signature in a copy of the PAC data. */
443 static krb5_error_code
zero_signature(krb5_context context,const krb5_pac pac,uint32_t type,krb5_data * data)444 zero_signature(krb5_context context, const krb5_pac pac, uint32_t type,
445 krb5_data *data)
446 {
447 struct k5_pac_buffer *buffer = NULL;
448 size_t i;
449
450 assert(type == KRB5_PAC_SERVER_CHECKSUM ||
451 type == KRB5_PAC_PRIVSVR_CHECKSUM ||
452 type == KRB5_PAC_FULL_CHECKSUM);
453 assert(data->length >= pac->data.length);
454
455 for (i = 0; i < pac->nbuffers; i++) {
456 if (pac->buffers[i].type == type) {
457 buffer = &pac->buffers[i];
458 break;
459 }
460 }
461
462 if (buffer == NULL)
463 return ENOENT;
464
465 if (buffer->size < PAC_SIGNATURE_DATA_LENGTH)
466 return KRB5_BAD_MSIZE;
467 if (buffer->size > pac->data.length ||
468 buffer->offset > pac->data.length - buffer->size)
469 return ERANGE;
470
471 /* Within the copy, zero out the data portion of the checksum only. */
472 memset(data->data + buffer->offset + PAC_SIGNATURE_DATA_LENGTH, 0,
473 buffer->size - PAC_SIGNATURE_DATA_LENGTH);
474
475 return 0;
476 }
477
478 static krb5_error_code
verify_checksum(krb5_context context,const krb5_pac pac,uint32_t buffer_type,const krb5_keyblock * key,krb5_keyusage usage,const krb5_data * data)479 verify_checksum(krb5_context context, const krb5_pac pac, uint32_t buffer_type,
480 const krb5_keyblock *key, krb5_keyusage usage,
481 const krb5_data *data)
482 {
483 krb5_error_code ret;
484 krb5_data buffer;
485 krb5_cksumtype cksumtype;
486 krb5_checksum checksum;
487 krb5_boolean valid;
488 size_t cksumlen;
489
490 ret = k5_pac_locate_buffer(context, pac, buffer_type, &buffer);
491 if (ret)
492 return ret;
493 if (buffer.length < PAC_SIGNATURE_DATA_LENGTH)
494 return KRB5_BAD_MSIZE;
495
496 cksumtype = load_32_le(buffer.data);
497 if (buffer_type == KRB5_PAC_SERVER_CHECKSUM && cksumtype == CKSUMTYPE_SHA1)
498 return KRB5KDC_ERR_SUMTYPE_NOSUPP;
499 if (!krb5_c_is_keyed_cksum(cksumtype))
500 return KRB5KRB_ERR_GENERIC;
501
502 /* There may be an RODCIdentifier trailer (see [MS-PAC] 2.8), so look up
503 * the length of the checksum by its type. */
504 ret = krb5_c_checksum_length(context, cksumtype, &cksumlen);
505 if (ret)
506 return ret;
507 if (cksumlen > buffer.length - PAC_SIGNATURE_DATA_LENGTH)
508 return KRB5_BAD_MSIZE;
509 checksum.checksum_type = cksumtype;
510 checksum.length = cksumlen;
511 checksum.contents = (uint8_t *)buffer.data + PAC_SIGNATURE_DATA_LENGTH;
512
513 ret = krb5_c_verify_checksum(context, key, usage, data, &checksum, &valid);
514 return ret ? ret : (valid ? 0 : KRB5KRB_AP_ERR_MODIFIED);
515 }
516
517 static krb5_error_code
verify_pac_checksums(krb5_context context,const krb5_pac pac,krb5_boolean expect_full_checksum,const krb5_keyblock * server,const krb5_keyblock * privsvr)518 verify_pac_checksums(krb5_context context, const krb5_pac pac,
519 krb5_boolean expect_full_checksum,
520 const krb5_keyblock *server, const krb5_keyblock *privsvr)
521 {
522 krb5_error_code ret;
523 krb5_data copy, server_checksum;
524
525 /* Make a copy of the PAC with zeroed out server and privsvr checksums. */
526 ret = krb5int_copy_data_contents(context, &pac->data, ©);
527 if (ret)
528 return ret;
529
530 ret = zero_signature(context, pac, KRB5_PAC_SERVER_CHECKSUM, ©);
531 if (ret)
532 goto cleanup;
533 ret = zero_signature(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM, ©);
534 if (ret)
535 goto cleanup;
536
537 if (server != NULL) {
538 /* Verify the server checksum over the PAC copy. */
539 ret = verify_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM, server,
540 KRB5_KEYUSAGE_APP_DATA_CKSUM, ©);
541 }
542
543 if (privsvr != NULL && expect_full_checksum) {
544 /* Zero the full checksum buffer in the copy and verify the full
545 * checksum over the copy with all three checksums zeroed. */
546 ret = zero_signature(context, pac, KRB5_PAC_FULL_CHECKSUM, ©);
547 if (ret)
548 goto cleanup;
549 ret = verify_checksum(context, pac, KRB5_PAC_FULL_CHECKSUM, privsvr,
550 KRB5_KEYUSAGE_APP_DATA_CKSUM, ©);
551 if (ret)
552 goto cleanup;
553 }
554
555 if (privsvr != NULL) {
556 /* Verify the privsvr checksum over the server checksum. */
557 ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
558 &server_checksum);
559 if (ret)
560 goto cleanup;
561 if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
562 ret = KRB5_BAD_MSIZE;
563 goto cleanup;
564 }
565 server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
566 server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
567
568 ret = verify_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM, privsvr,
569 KRB5_KEYUSAGE_APP_DATA_CKSUM, &server_checksum);
570 if (ret)
571 goto cleanup;
572 }
573
574 pac->verified = TRUE;
575
576 cleanup:
577 free(copy.data);
578 return ret;
579 }
580
581 /* Per MS-PAC 2.8.3, tickets encrypted to TGS and password change principals
582 * should not have ticket signatures. */
583 krb5_boolean
k5_pac_should_have_ticket_signature(krb5_const_principal sprinc)584 k5_pac_should_have_ticket_signature(krb5_const_principal sprinc)
585 {
586 if (IS_TGS_PRINC(sprinc))
587 return FALSE;
588 if (sprinc->length == 2 && data_eq_string(sprinc->data[0], "kadmin") &&
589 data_eq_string(sprinc->data[1], "changepw"))
590 return FALSE;
591 return TRUE;
592 }
593
594 krb5_error_code KRB5_CALLCONV
krb5_kdc_verify_ticket(krb5_context context,const krb5_enc_tkt_part * enc_tkt,krb5_const_principal server_princ,const krb5_keyblock * server,const krb5_keyblock * privsvr,krb5_pac * pac_out)595 krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
596 krb5_const_principal server_princ,
597 const krb5_keyblock *server,
598 const krb5_keyblock *privsvr, krb5_pac *pac_out)
599 {
600 krb5_error_code ret;
601 krb5_pac pac = NULL;
602 krb5_data *recoded_tkt = NULL;
603 krb5_authdata **authdata = enc_tkt->authorization_data;
604 krb5_authdata *orig, **ifrel = NULL, **recoded_ifrel = NULL;
605 uint8_t z = 0;
606 krb5_authdata zpac = { KV5M_AUTHDATA, KRB5_AUTHDATA_WIN2K_PAC, 1, &z };
607 krb5_boolean is_service_tkt;
608 size_t i, j;
609
610 *pac_out = NULL;
611
612 if (authdata == NULL)
613 return 0;
614
615 /*
616 * Find the position of the PAC in the ticket authdata. ifrel will be the
617 * decoded AD-IF-RELEVANT container at position i containing a PAC, and j
618 * will be the offset within the container.
619 */
620 for (i = 0; authdata[i] != NULL; i++) {
621 if (authdata[i]->ad_type != KRB5_AUTHDATA_IF_RELEVANT)
622 continue;
623
624 ret = krb5_decode_authdata_container(context,
625 KRB5_AUTHDATA_IF_RELEVANT,
626 authdata[i], &ifrel);
627 if (ret)
628 goto cleanup;
629
630 for (j = 0; ifrel[j] != NULL; j++) {
631 if (ifrel[j]->ad_type == KRB5_AUTHDATA_WIN2K_PAC)
632 break;
633 }
634 if (ifrel[j] != NULL)
635 break;
636
637 krb5_free_authdata(context, ifrel);
638 ifrel = NULL;
639 }
640
641 /* Stop and return successfully if we didn't find a PAC. */
642 if (authdata[i] == NULL) {
643 ret = 0;
644 goto cleanup;
645 }
646
647 ret = krb5_pac_parse(context, ifrel[j]->contents, ifrel[j]->length, &pac);
648 if (ret)
649 goto cleanup;
650
651 is_service_tkt = k5_pac_should_have_ticket_signature(server_princ);
652 if (privsvr != NULL && is_service_tkt) {
653 /* To check the PAC ticket signatures, re-encode the ticket with the
654 * PAC contents replaced by a single zero. */
655 orig = ifrel[j];
656 ifrel[j] = &zpac;
657 ret = krb5_encode_authdata_container(context,
658 KRB5_AUTHDATA_IF_RELEVANT,
659 ifrel, &recoded_ifrel);
660 ifrel[j] = orig;
661 if (ret)
662 goto cleanup;
663 orig = authdata[i];
664 authdata[i] = recoded_ifrel[0];
665 ret = encode_krb5_enc_tkt_part(enc_tkt, &recoded_tkt);
666 authdata[i] = orig;
667 if (ret)
668 goto cleanup;
669
670 ret = verify_checksum(context, pac, KRB5_PAC_TICKET_CHECKSUM, privsvr,
671 KRB5_KEYUSAGE_APP_DATA_CKSUM, recoded_tkt);
672 if (ret)
673 goto cleanup;
674 }
675
676 ret = verify_pac_checksums(context, pac, is_service_tkt, server, privsvr);
677 if (ret)
678 goto cleanup;
679
680 *pac_out = pac;
681 pac = NULL;
682
683 cleanup:
684 krb5_pac_free(context, pac);
685 krb5_free_data(context, recoded_tkt);
686 krb5_free_authdata(context, ifrel);
687 krb5_free_authdata(context, recoded_ifrel);
688 return ret;
689 }
690
691 krb5_error_code KRB5_CALLCONV
krb5_pac_verify(krb5_context context,const krb5_pac pac,krb5_timestamp authtime,krb5_const_principal principal,const krb5_keyblock * server,const krb5_keyblock * privsvr)692 krb5_pac_verify(krb5_context context,
693 const krb5_pac pac,
694 krb5_timestamp authtime,
695 krb5_const_principal principal,
696 const krb5_keyblock *server,
697 const krb5_keyblock *privsvr)
698 {
699 return krb5_pac_verify_ext(context, pac, authtime, principal, server,
700 privsvr, FALSE);
701 }
702
703 krb5_error_code KRB5_CALLCONV
krb5_pac_verify_ext(krb5_context context,const krb5_pac pac,krb5_timestamp authtime,krb5_const_principal principal,const krb5_keyblock * server,const krb5_keyblock * privsvr,krb5_boolean with_realm)704 krb5_pac_verify_ext(krb5_context context,
705 const krb5_pac pac,
706 krb5_timestamp authtime,
707 krb5_const_principal principal,
708 const krb5_keyblock *server,
709 const krb5_keyblock *privsvr,
710 krb5_boolean with_realm)
711 {
712 krb5_error_code ret;
713
714 if (server != NULL || privsvr != NULL) {
715 ret = verify_pac_checksums(context, pac, FALSE, server, privsvr);
716 if (ret)
717 return ret;
718 }
719
720 if (principal != NULL) {
721 ret = k5_pac_validate_client(context, pac, authtime,
722 principal, with_realm);
723 if (ret)
724 return ret;
725 }
726
727 return 0;
728 }
729
730 /*
731 * PAC auth data attribute backend
732 */
733 struct mspac_context {
734 krb5_pac pac;
735 };
736
737 static krb5_error_code
mspac_init(krb5_context context,void ** plugin_context)738 mspac_init(krb5_context context, void **plugin_context)
739 {
740 *plugin_context = NULL;
741 return 0;
742 }
743
744 static void
mspac_flags(krb5_context context,void * plugin_context,krb5_authdatatype ad_type,krb5_flags * flags)745 mspac_flags(krb5_context context, void *plugin_context,
746 krb5_authdatatype ad_type, krb5_flags *flags)
747 {
748 *flags = AD_USAGE_TGS_REQ;
749 }
750
751 static void
mspac_fini(krb5_context context,void * plugin_context)752 mspac_fini(krb5_context context, void *plugin_context)
753 {
754 return;
755 }
756
757 static krb5_error_code
mspac_request_init(krb5_context context,krb5_authdata_context actx,void * plugin_context,void ** request_context)758 mspac_request_init(krb5_context context, krb5_authdata_context actx,
759 void *plugin_context, void **request_context)
760 {
761 struct mspac_context *pacctx;
762
763 pacctx = malloc(sizeof(*pacctx));
764 if (pacctx == NULL)
765 return ENOMEM;
766
767 pacctx->pac = NULL;
768
769 *request_context = pacctx;
770
771 return 0;
772 }
773
774 static krb5_error_code
mspac_import_authdata(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,krb5_authdata ** authdata,krb5_boolean kdc_issued,krb5_const_principal kdc_issuer)775 mspac_import_authdata(krb5_context context, krb5_authdata_context actx,
776 void *plugin_context, void *request_context,
777 krb5_authdata **authdata, krb5_boolean kdc_issued,
778 krb5_const_principal kdc_issuer)
779 {
780 struct mspac_context *pacctx = (struct mspac_context *)request_context;
781
782 if (kdc_issued)
783 return EINVAL;
784
785 if (pacctx->pac != NULL) {
786 krb5_pac_free(context, pacctx->pac);
787 pacctx->pac = NULL;
788 }
789
790 assert(authdata[0] != NULL);
791 assert((authdata[0]->ad_type & AD_TYPE_FIELD_TYPE_MASK) ==
792 KRB5_AUTHDATA_WIN2K_PAC);
793
794 return krb5_pac_parse(context, authdata[0]->contents, authdata[0]->length,
795 &pacctx->pac);
796 }
797
798 static krb5_error_code
mspac_export_authdata(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,krb5_flags usage,krb5_authdata *** authdata_out)799 mspac_export_authdata(krb5_context context, krb5_authdata_context actx,
800 void *plugin_context, void *request_context,
801 krb5_flags usage, krb5_authdata ***authdata_out)
802 {
803 struct mspac_context *pacctx = (struct mspac_context *)request_context;
804 krb5_error_code code;
805 krb5_authdata **authdata;
806 krb5_data data;
807
808 if (pacctx->pac == NULL)
809 return 0;
810
811 authdata = calloc(2, sizeof(krb5_authdata *));
812 if (authdata == NULL)
813 return ENOMEM;
814
815 authdata[0] = calloc(1, sizeof(krb5_authdata));
816 if (authdata[0] == NULL) {
817 free(authdata);
818 return ENOMEM;
819 }
820 authdata[1] = NULL;
821
822 code = krb5int_copy_data_contents(context, &pacctx->pac->data, &data);
823 if (code != 0) {
824 krb5_free_authdata(context, authdata);
825 return code;
826 }
827
828 authdata[0]->magic = KV5M_AUTHDATA;
829 authdata[0]->ad_type = KRB5_AUTHDATA_WIN2K_PAC;
830 authdata[0]->length = data.length;
831 authdata[0]->contents = (krb5_octet *)data.data;
832
833 authdata[1] = NULL;
834
835 *authdata_out = authdata;
836
837 return 0;
838 }
839
840 static krb5_error_code
mspac_verify(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,const krb5_auth_context * auth_context,const krb5_keyblock * key,const krb5_ap_req * req)841 mspac_verify(krb5_context context, krb5_authdata_context actx,
842 void *plugin_context, void *request_context,
843 const krb5_auth_context *auth_context, const krb5_keyblock *key,
844 const krb5_ap_req *req)
845 {
846 krb5_error_code ret;
847 struct mspac_context *pacctx = (struct mspac_context *)request_context;
848
849 if (pacctx->pac == NULL)
850 return EINVAL;
851
852 ret = krb5_pac_verify(context, pacctx->pac,
853 req->ticket->enc_part2->times.authtime,
854 req->ticket->enc_part2->client, key, NULL);
855 if (ret)
856 TRACE_MSPAC_VERIFY_FAIL(context, ret);
857
858 /*
859 * If the above verification failed, don't fail the whole authentication,
860 * just don't mark the PAC as verified. A checksum mismatch can occur if
861 * the PAC was copied from a cross-realm TGT by an ignorant KDC, and Apple
862 * macOS Server Open Directory (as of 10.6) generates PACs with no server
863 * checksum at all.
864 */
865 return 0;
866 }
867
868 static void
mspac_request_fini(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context)869 mspac_request_fini(krb5_context context, krb5_authdata_context actx,
870 void *plugin_context, void *request_context)
871 {
872 struct mspac_context *pacctx = (struct mspac_context *)request_context;
873
874 if (pacctx != NULL) {
875 krb5_pac_free(context, pacctx->pac);
876 free(pacctx);
877 }
878 }
879
880 #define STRLENOF(x) (sizeof((x)) - 1)
881
882 static struct {
883 uint32_t type;
884 krb5_data attribute;
885 } mspac_attribute_types[] = {
886 { (uint32_t)-1, { KV5M_DATA, STRLENOF("urn:mspac:"),
887 "urn:mspac:" } },
888 { KRB5_PAC_LOGON_INFO, { KV5M_DATA,
889 STRLENOF("urn:mspac:logon-info"),
890 "urn:mspac:logon-info" } },
891 { KRB5_PAC_CREDENTIALS_INFO, { KV5M_DATA,
892 STRLENOF("urn:mspac:credentials-info"),
893 "urn:mspac:credentials-info" } },
894 { KRB5_PAC_SERVER_CHECKSUM, { KV5M_DATA,
895 STRLENOF("urn:mspac:server-checksum"),
896 "urn:mspac:server-checksum" } },
897 { KRB5_PAC_PRIVSVR_CHECKSUM, { KV5M_DATA,
898 STRLENOF("urn:mspac:privsvr-checksum"),
899 "urn:mspac:privsvr-checksum" } },
900 { KRB5_PAC_CLIENT_INFO, { KV5M_DATA,
901 STRLENOF("urn:mspac:client-info"),
902 "urn:mspac:client-info" } },
903 { KRB5_PAC_DELEGATION_INFO, { KV5M_DATA,
904 STRLENOF("urn:mspac:delegation-info"),
905 "urn:mspac:delegation-info" } },
906 { KRB5_PAC_UPN_DNS_INFO, { KV5M_DATA,
907 STRLENOF("urn:mspac:upn-dns-info"),
908 "urn:mspac:upn-dns-info" } },
909 };
910
911 #define MSPAC_ATTRIBUTE_COUNT (sizeof(mspac_attribute_types)/sizeof(mspac_attribute_types[0]))
912
913 static krb5_error_code
mspac_type2attr(uint32_t type,krb5_data * attr)914 mspac_type2attr(uint32_t type, krb5_data *attr)
915 {
916 unsigned int i;
917
918 for (i = 0; i < MSPAC_ATTRIBUTE_COUNT; i++) {
919 if (mspac_attribute_types[i].type == type) {
920 *attr = mspac_attribute_types[i].attribute;
921 return 0;
922 }
923 }
924
925 return ENOENT;
926 }
927
928 static krb5_error_code
mspac_attr2type(const krb5_data * attr,uint32_t * type)929 mspac_attr2type(const krb5_data *attr, uint32_t *type)
930 {
931 unsigned int i;
932
933 for (i = 0; i < MSPAC_ATTRIBUTE_COUNT; i++) {
934 if (attr->length == mspac_attribute_types[i].attribute.length &&
935 strncasecmp(attr->data, mspac_attribute_types[i].attribute.data, attr->length) == 0) {
936 *type = mspac_attribute_types[i].type;
937 return 0;
938 }
939 }
940
941 if (attr->length > STRLENOF("urn:mspac:") &&
942 strncasecmp(attr->data, "urn:mspac:", STRLENOF("urn:mspac:")) == 0)
943 {
944 char *p = &attr->data[STRLENOF("urn:mspac:")];
945 char *endptr;
946
947 *type = strtoul(p, &endptr, 10);
948 if (*type != 0 && *endptr == '\0')
949 return 0;
950 }
951
952 return ENOENT;
953 }
954
955 static krb5_error_code
mspac_get_attribute_types(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,krb5_data ** attrs_out)956 mspac_get_attribute_types(krb5_context context, krb5_authdata_context actx,
957 void *plugin_context, void *request_context,
958 krb5_data **attrs_out)
959 {
960 struct mspac_context *pacctx = (struct mspac_context *)request_context;
961 size_t i, j;
962 krb5_data *attrs;
963 krb5_error_code ret;
964
965 if (pacctx->pac == NULL)
966 return ENOENT;
967
968 attrs = calloc(1 + pacctx->pac->nbuffers + 1, sizeof(krb5_data));
969 if (attrs == NULL)
970 return ENOMEM;
971
972 j = 0;
973
974 /* The entire PAC */
975 ret = krb5int_copy_data_contents(context,
976 &mspac_attribute_types[0].attribute,
977 &attrs[j++]);
978 if (ret)
979 goto fail;
980
981 /* PAC buffers */
982 for (i = 0; i < pacctx->pac->nbuffers; i++) {
983 krb5_data attr;
984
985 ret = mspac_type2attr(pacctx->pac->buffers[i].type, &attr);
986 if (!ret) {
987 ret = krb5int_copy_data_contents(context, &attr, &attrs[j++]);
988 if (ret)
989 goto fail;
990 } else {
991 int length;
992
993 length = asprintf(&attrs[j].data, "urn:mspac:%d",
994 pacctx->pac->buffers[i].type);
995 if (length < 0) {
996 ret = ENOMEM;
997 goto fail;
998 }
999 attrs[j++].length = length;
1000 }
1001 }
1002 attrs[j].data = NULL;
1003 attrs[j].length = 0;
1004
1005 *attrs_out = attrs;
1006
1007 return 0;
1008
1009 fail:
1010 krb5int_free_data_list(context, attrs);
1011 return ret;
1012 }
1013
1014 static krb5_error_code
mspac_get_attribute(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,const krb5_data * attribute,krb5_boolean * authenticated,krb5_boolean * complete,krb5_data * value,krb5_data * display_value,int * more)1015 mspac_get_attribute(krb5_context context, krb5_authdata_context actx,
1016 void *plugin_context, void *request_context,
1017 const krb5_data *attribute, krb5_boolean *authenticated,
1018 krb5_boolean *complete, krb5_data *value,
1019 krb5_data *display_value, int *more)
1020 {
1021 struct mspac_context *pacctx = (struct mspac_context *)request_context;
1022 krb5_error_code ret;
1023 uint32_t type;
1024
1025 if (display_value != NULL) {
1026 display_value->data = NULL;
1027 display_value->length = 0;
1028 }
1029
1030 if (*more != -1 || pacctx->pac == NULL)
1031 return ENOENT;
1032
1033 /* If it didn't verify, pretend it didn't exist. */
1034 if (!pacctx->pac->verified) {
1035 TRACE_MSPAC_DISCARD_UNVERF(context);
1036 return ENOENT;
1037 }
1038
1039 ret = mspac_attr2type(attribute, &type);
1040 if (ret)
1041 return ret;
1042
1043 /* -1 is a magic type that refers to the entire PAC */
1044 if (type == (uint32_t)-1) {
1045 if (value != NULL)
1046 ret = krb5int_copy_data_contents(context, &pacctx->pac->data,
1047 value);
1048 else
1049 ret = 0;
1050 } else {
1051 if (value != NULL)
1052 ret = krb5_pac_get_buffer(context, pacctx->pac, type, value);
1053 else
1054 ret = k5_pac_locate_buffer(context, pacctx->pac, type, NULL);
1055 }
1056 if (!ret) {
1057 *authenticated = pacctx->pac->verified;
1058 *complete = TRUE;
1059 }
1060
1061 *more = 0;
1062
1063 return ret;
1064 }
1065
1066 static krb5_error_code
mspac_set_attribute(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,krb5_boolean complete,const krb5_data * attribute,const krb5_data * value)1067 mspac_set_attribute(krb5_context context, krb5_authdata_context actx,
1068 void *plugin_context, void *request_context,
1069 krb5_boolean complete, const krb5_data *attribute,
1070 const krb5_data *value)
1071 {
1072 struct mspac_context *pacctx = (struct mspac_context *)request_context;
1073 krb5_error_code ret;
1074 uint32_t type;
1075
1076 if (pacctx->pac == NULL)
1077 return ENOENT;
1078
1079 ret = mspac_attr2type(attribute, &type);
1080 if (ret)
1081 return ret;
1082
1083 /* -1 is a magic type that refers to the entire PAC */
1084 if (type == (uint32_t)-1) {
1085 krb5_pac newpac;
1086
1087 ret = krb5_pac_parse(context, value->data, value->length, &newpac);
1088 if (ret)
1089 return ret;
1090
1091 krb5_pac_free(context, pacctx->pac);
1092 pacctx->pac = newpac;
1093 } else {
1094 ret = krb5_pac_add_buffer(context, pacctx->pac, type, value);
1095 }
1096
1097 return ret;
1098 }
1099
1100 static krb5_error_code
mspac_export_internal(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,krb5_boolean restrict_authenticated,void ** ptr)1101 mspac_export_internal(krb5_context context, krb5_authdata_context actx,
1102 void *plugin_context, void *request_context,
1103 krb5_boolean restrict_authenticated, void **ptr)
1104 {
1105 struct mspac_context *pacctx = (struct mspac_context *)request_context;
1106 krb5_error_code ret;
1107 krb5_pac pac;
1108
1109 *ptr = NULL;
1110
1111 if (pacctx->pac == NULL)
1112 return ENOENT;
1113
1114 if (restrict_authenticated && (pacctx->pac->verified) == FALSE)
1115 return ENOENT;
1116
1117 ret = krb5_pac_parse(context, pacctx->pac->data.data,
1118 pacctx->pac->data.length, &pac);
1119 if (!ret) {
1120 pac->verified = pacctx->pac->verified;
1121 *ptr = pac;
1122 }
1123
1124 return ret;
1125 }
1126
1127 static void
mspac_free_internal(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,void * ptr)1128 mspac_free_internal(krb5_context context, krb5_authdata_context actx,
1129 void *plugin_context, void *request_context, void *ptr)
1130 {
1131 if (ptr != NULL)
1132 krb5_pac_free(context, (krb5_pac)ptr);
1133
1134 return;
1135 }
1136
1137 static krb5_error_code
mspac_size(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,size_t * sizep)1138 mspac_size(krb5_context context, krb5_authdata_context actx,
1139 void *plugin_context, void *request_context, size_t *sizep)
1140 {
1141 struct mspac_context *pacctx = (struct mspac_context *)request_context;
1142
1143 *sizep += sizeof(int32_t);
1144
1145 if (pacctx->pac != NULL)
1146 *sizep += pacctx->pac->data.length;
1147
1148 *sizep += sizeof(int32_t);
1149
1150 return 0;
1151 }
1152
1153 static krb5_error_code
mspac_externalize(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,krb5_octet ** buffer,size_t * lenremain)1154 mspac_externalize(krb5_context context, krb5_authdata_context actx,
1155 void *plugin_context, void *request_context,
1156 krb5_octet **buffer, size_t *lenremain)
1157 {
1158 krb5_error_code ret = 0;
1159 struct mspac_context *pacctx = (struct mspac_context *)request_context;
1160 size_t required = 0;
1161 krb5_octet *bp;
1162 size_t remain;
1163
1164 bp = *buffer;
1165 remain = *lenremain;
1166
1167 if (pacctx->pac != NULL) {
1168 mspac_size(context, actx, plugin_context, request_context, &required);
1169
1170 if (required <= remain) {
1171 krb5_ser_pack_int32(pacctx->pac->data.length, &bp, &remain);
1172 krb5_ser_pack_bytes((krb5_octet *)pacctx->pac->data.data,
1173 (size_t)pacctx->pac->data.length,
1174 &bp, &remain);
1175 krb5_ser_pack_int32(pacctx->pac->verified, &bp, &remain);
1176 } else {
1177 ret = ENOMEM;
1178 }
1179 } else {
1180 krb5_ser_pack_int32(0, &bp, &remain); /* length */
1181 krb5_ser_pack_int32(0, &bp, &remain); /* verified */
1182 }
1183
1184 *buffer = bp;
1185 *lenremain = remain;
1186
1187 return ret;
1188 }
1189
1190 static krb5_error_code
mspac_internalize(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,krb5_octet ** buffer,size_t * lenremain)1191 mspac_internalize(krb5_context context, krb5_authdata_context actx,
1192 void *plugin_context, void *request_context,
1193 krb5_octet **buffer, size_t *lenremain)
1194 {
1195 struct mspac_context *pacctx = (struct mspac_context *)request_context;
1196 krb5_error_code ret;
1197 int32_t ibuf;
1198 uint8_t *bp;
1199 size_t remain;
1200 krb5_pac pac = NULL;
1201
1202 bp = *buffer;
1203 remain = *lenremain;
1204
1205 /* length */
1206 ret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
1207 if (ret)
1208 return ret;
1209
1210 if (ibuf != 0) {
1211 ret = krb5_pac_parse(context, bp, ibuf, &pac);
1212 if (ret)
1213 return ret;
1214
1215 bp += ibuf;
1216 remain -= ibuf;
1217 }
1218
1219 /* verified */
1220 ret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
1221 if (ret) {
1222 krb5_pac_free(context, pac);
1223 return ret;
1224 }
1225
1226 if (pac != NULL)
1227 pac->verified = (ibuf != 0);
1228
1229 if (pacctx->pac != NULL)
1230 krb5_pac_free(context, pacctx->pac);
1231
1232 pacctx->pac = pac;
1233
1234 *buffer = bp;
1235 *lenremain = remain;
1236
1237 return 0;
1238 }
1239
1240 static krb5_error_code
mspac_copy(krb5_context context,krb5_authdata_context actx,void * plugin_context,void * request_context,void * dst_plugin_context,void * dst_request_context)1241 mspac_copy(krb5_context context, krb5_authdata_context actx,
1242 void *plugin_context, void *request_context,
1243 void *dst_plugin_context, void *dst_request_context)
1244 {
1245 struct mspac_context *srcctx = (struct mspac_context *)request_context;
1246 struct mspac_context *dstctx = (struct mspac_context *)dst_request_context;
1247 krb5_error_code ret = 0;
1248
1249 assert(dstctx != NULL);
1250 assert(dstctx->pac == NULL);
1251
1252 if (srcctx->pac != NULL)
1253 ret = copy_pac(context, srcctx->pac, &dstctx->pac);
1254
1255 return ret;
1256 }
1257
1258 static krb5_authdatatype mspac_ad_types[] = { KRB5_AUTHDATA_WIN2K_PAC, 0 };
1259
1260 krb5plugin_authdata_client_ftable_v0 k5_mspac_ad_client_ftable = {
1261 "mspac",
1262 mspac_ad_types,
1263 mspac_init,
1264 mspac_fini,
1265 mspac_flags,
1266 mspac_request_init,
1267 mspac_request_fini,
1268 mspac_get_attribute_types,
1269 mspac_get_attribute,
1270 mspac_set_attribute,
1271 NULL, /* delete_attribute_proc */
1272 mspac_export_authdata,
1273 mspac_import_authdata,
1274 mspac_export_internal,
1275 mspac_free_internal,
1276 mspac_verify,
1277 mspac_size,
1278 mspac_externalize,
1279 mspac_internalize,
1280 mspac_copy
1281 };
1282