1 /*
2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4 /*
5 * lib/krb5/krb/pac.c
6 *
7 * Copyright 2008 by the Massachusetts Institute of Technology.
8 * All Rights Reserved.
9 *
10 * Export of this software from the United States of America may
11 * require a specific license from the United States Government.
12 * It is the responsibility of any person or organization contemplating
13 * export to obtain such a license before exporting.
14 *
15 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
16 * distribute this software and its documentation for any purpose and
17 * without fee is hereby granted, provided that the above copyright
18 * notice appear in all copies and that both that copyright notice and
19 * this permission notice appear in supporting documentation, and that
20 * the name of M.I.T. not be used in advertising or publicity pertaining
21 * to distribution of the software without specific, written prior
22 * permission. Furthermore if you modify this software you must label
23 * your software as modified software and not distribute it in such a
24 * fashion that it might be confused with the original M.I.T. software.
25 * M.I.T. makes no representations about the suitability of
26 * this software for any purpose. It is provided "as is" without express
27 * or implied warranty.
28 *
29 */
30
31 #include "k5-int.h"
32 #include "k5-utf8.h"
33
34 /* draft-brezak-win2k-krb-authz-00 */
35
36 /*
37 * A PAC consists of a sequence of PAC_INFO_BUFFERs, preceeded by
38 * a PACTYPE header. Decoding the contents of the buffers is left
39 * to the application (notwithstanding signature verification).
40 */
41
42 /*
43 * SUNW17PACresync
44 * These should eventually go to k5-platform.h or equiv.
45 */
46 static inline unsigned short
load_16_le(const void * cvp)47 load_16_le (const void *cvp)
48 {
49 const unsigned char *p = cvp;
50 #if defined(__GNUC__) && defined(K5_LE)
51 return GET(16,p);
52 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP16)
53 return GETSWAPPED(16,p);
54 #else
55 return (p[0] | (p[1] << 8));
56 #endif
57 }
58
59 static inline unsigned int
load_32_le(const void * cvp)60 load_32_le (const void *cvp)
61 {
62 const unsigned char *p = cvp;
63 #if defined(__GNUC__) && defined(K5_LE)
64 return GET(32,p);
65 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP32)
66 return GETSWAPPED(32,p);
67 #else
68 return (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
69 #endif
70 }
71 static inline UINT64_TYPE
load_64_le(const void * cvp)72 load_64_le (const void *cvp)
73 {
74 const unsigned char *p = cvp;
75 #if defined(__GNUC__) && defined(K5_LE)
76 return GET(64,p);
77 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP64)
78 return GETSWAPPED(64,p);
79 #else
80 return ((UINT64_TYPE)load_32_le(p+4) << 32) | load_32_le(p);
81 #endif
82 }
83
84 static inline void
store_16_le(unsigned int val,void * vp)85 store_16_le (unsigned int val, void *vp)
86 {
87 unsigned char *p = vp;
88 #if defined(__GNUC__) && defined(K5_LE)
89 PUT(16,p,val);
90 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP16)
91 PUTSWAPPED(16,p,val);
92 #else
93 p[1] = (val >> 8) & 0xff;
94 p[0] = (val ) & 0xff;
95 #endif
96 }
97
98 static inline void
store_32_le(unsigned int val,void * vp)99 store_32_le (unsigned int val, void *vp)
100 {
101 unsigned char *p = vp;
102 #if defined(__GNUC__) && defined(K5_LE)
103 PUT(32,p,val);
104 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP32)
105 PUTSWAPPED(32,p,val);
106 #else
107 p[3] = (val >> 24) & 0xff;
108 p[2] = (val >> 16) & 0xff;
109 p[1] = (val >> 8) & 0xff;
110 p[0] = (val ) & 0xff;
111 #endif
112 }
113 static inline void
store_64_le(UINT64_TYPE val,void * vp)114 store_64_le (UINT64_TYPE val, void *vp)
115 {
116 unsigned char *p = vp;
117 #if defined(__GNUC__) && defined(K5_LE)
118 PUT(64,p,val);
119 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP64)
120 PUTSWAPPED(64,p,val);
121 #else
122 p[7] = (unsigned char)((val >> 56) & 0xff);
123 p[6] = (unsigned char)((val >> 48) & 0xff);
124 p[5] = (unsigned char)((val >> 40) & 0xff);
125 p[4] = (unsigned char)((val >> 32) & 0xff);
126 p[3] = (unsigned char)((val >> 24) & 0xff);
127 p[2] = (unsigned char)((val >> 16) & 0xff);
128 p[1] = (unsigned char)((val >> 8) & 0xff);
129 p[0] = (unsigned char)((val ) & 0xff);
130 #endif
131 }
132
133
134 typedef struct _PAC_INFO_BUFFER {
135 krb5_ui_4 ulType;
136 krb5_ui_4 cbBufferSize;
137 krb5_ui_8 Offset;
138 } PAC_INFO_BUFFER;
139
140 #define PAC_INFO_BUFFER_LENGTH 16
141
142 /* ulType */
143 #define PAC_LOGON_INFO 1
144 #define PAC_SERVER_CHECKSUM 6
145 #define PAC_PRIVSVR_CHECKSUM 7
146 #define PAC_CLIENT_INFO 10
147
148 typedef struct _PACTYPE {
149 krb5_ui_4 cBuffers;
150 krb5_ui_4 Version;
151 PAC_INFO_BUFFER Buffers[1];
152 } PACTYPE;
153
154 #define PAC_ALIGNMENT 8
155 #define PACTYPE_LENGTH 8U
156 #define PAC_SIGNATURE_DATA_LENGTH 4U
157 #define PAC_CLIENT_INFO_LENGTH 10U
158
159 #define NT_TIME_EPOCH 11644473600LL
160
161 struct krb5_pac_data {
162 PACTYPE *pac; /* PAC header + info buffer array */
163 krb5_data data; /* PAC data (including uninitialised header) */
164 };
165
166 static krb5_error_code
167 k5_pac_locate_buffer(krb5_context context,
168 const krb5_pac pac,
169 krb5_ui_4 type,
170 krb5_data *data);
171
172 /*
173 * Add a buffer to the provided PAC and update header.
174 */
175 static krb5_error_code
k5_pac_add_buffer(krb5_context context,krb5_pac pac,krb5_ui_4 type,const krb5_data * data,krb5_boolean zerofill,krb5_data * out_data)176 k5_pac_add_buffer(krb5_context context,
177 krb5_pac pac,
178 krb5_ui_4 type,
179 const krb5_data *data,
180 krb5_boolean zerofill,
181 krb5_data *out_data)
182 {
183 PACTYPE *header;
184 size_t header_len, i, pad = 0;
185 char *pac_data;
186
187 assert((data->data == NULL) == zerofill);
188
189 /* Check there isn't already a buffer of this type */
190 if (k5_pac_locate_buffer(context, pac, type, NULL) == 0) {
191 /* Solaris Kerberos */
192 krb5_set_error_message(context, EINVAL,
193 "Duplicate PAC buffer of type %d",
194 type);
195 return EINVAL;
196 }
197
198 header = (PACTYPE *)realloc(pac->pac,
199 sizeof(PACTYPE) +
200 (pac->pac->cBuffers * sizeof(PAC_INFO_BUFFER)));
201 if (header == NULL) {
202 return ENOMEM;
203 }
204 pac->pac = header;
205
206 header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
207
208 if (data->length % PAC_ALIGNMENT)
209 pad = PAC_ALIGNMENT - (data->length % PAC_ALIGNMENT);
210
211 pac_data = realloc(pac->data.data,
212 pac->data.length + PAC_INFO_BUFFER_LENGTH + data->length + pad);
213 if (pac_data == NULL) {
214 return ENOMEM;
215 }
216 pac->data.data = pac_data;
217
218 /* Update offsets of existing buffers */
219 for (i = 0; i < pac->pac->cBuffers; i++)
220 pac->pac->Buffers[i].Offset += PAC_INFO_BUFFER_LENGTH;
221
222 /* Make room for new PAC_INFO_BUFFER */
223 memmove(pac->data.data + header_len + PAC_INFO_BUFFER_LENGTH,
224 pac->data.data + header_len,
225 pac->data.length - header_len);
226 memset(pac->data.data + header_len, 0, PAC_INFO_BUFFER_LENGTH);
227
228 /* Initialise new PAC_INFO_BUFFER */
229 pac->pac->Buffers[i].ulType = type;
230 pac->pac->Buffers[i].cbBufferSize = data->length;
231 pac->pac->Buffers[i].Offset = pac->data.length + PAC_INFO_BUFFER_LENGTH;
232 assert((pac->pac->Buffers[i].Offset % PAC_ALIGNMENT) == 0);
233
234 /* Copy in new PAC data and zero padding bytes */
235 if (zerofill)
236 memset(pac->data.data + pac->pac->Buffers[i].Offset, 0, data->length);
237 else
238 memcpy(pac->data.data + pac->pac->Buffers[i].Offset, data->data, data->length);
239
240 memset(pac->data.data + pac->pac->Buffers[i].Offset + data->length, 0, pad);
241
242 pac->pac->cBuffers++;
243 pac->data.length += PAC_INFO_BUFFER_LENGTH + data->length + pad;
244
245 if (out_data != NULL) {
246 out_data->data = pac->data.data + pac->pac->Buffers[i].Offset;
247 out_data->length = data->length;
248 }
249
250 return 0;
251 }
252
253 krb5_error_code KRB5_CALLCONV
krb5_pac_add_buffer(krb5_context context,krb5_pac pac,krb5_ui_4 type,const krb5_data * data)254 krb5_pac_add_buffer(krb5_context context,
255 krb5_pac pac,
256 krb5_ui_4 type,
257 const krb5_data *data)
258 {
259 return k5_pac_add_buffer(context, pac, type, data, FALSE, NULL);
260 }
261
262 /*
263 * Free a PAC
264 */
265 void KRB5_CALLCONV
krb5_pac_free(krb5_context context,krb5_pac pac)266 krb5_pac_free(krb5_context context,
267 krb5_pac pac)
268 {
269 if (pac != NULL) {
270 if (pac->data.data != NULL) {
271 memset(pac->data.data, 0, pac->data.length);
272 free(pac->data.data);
273 }
274 if (pac->pac != NULL)
275 free(pac->pac);
276 memset(pac, 0, sizeof(*pac));
277 free(pac);
278 }
279 }
280
281 static krb5_error_code
k5_pac_locate_buffer(krb5_context context,const krb5_pac pac,krb5_ui_4 type,krb5_data * data)282 k5_pac_locate_buffer(krb5_context context,
283 const krb5_pac pac,
284 krb5_ui_4 type,
285 krb5_data *data)
286 {
287 PAC_INFO_BUFFER *buffer = NULL;
288 size_t i;
289
290 if (pac == NULL) {
291 /* Solaris Kerberos */
292 krb5_set_error_message(context, EINVAL,
293 "Invalid argument 'pac' is NULL");
294 return EINVAL;
295 }
296
297 for (i = 0; i < pac->pac->cBuffers; i++) {
298 if (pac->pac->Buffers[i].ulType == type) {
299 if (buffer == NULL)
300 buffer = &pac->pac->Buffers[i];
301 else {
302 /* Solaris Kerberos */
303 krb5_set_error_message(context, EINVAL,
304 "Invalid buffer found looping thru PAC buffers (type=%d, i=%d)",
305 type, i);
306 return EINVAL;
307 }
308 }
309 }
310
311 if (buffer == NULL) {
312 /* Solaris Kerberos */
313 krb5_set_error_message(context, ENOENT,
314 "No PAC buffer found (type=%d)",
315 type);
316
317 return ENOENT;
318 }
319
320 assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
321
322 if (data != NULL) {
323 data->length = buffer->cbBufferSize;
324 data->data = pac->data.data + buffer->Offset;
325 }
326
327 return 0;
328 }
329
330 /*
331 * Find a buffer and copy data into output
332 */
333 krb5_error_code KRB5_CALLCONV
krb5_pac_get_buffer(krb5_context context,krb5_pac pac,krb5_ui_4 type,krb5_data * data)334 krb5_pac_get_buffer(krb5_context context,
335 krb5_pac pac,
336 krb5_ui_4 type,
337 krb5_data *data)
338 {
339 krb5_data d;
340 krb5_error_code ret;
341
342 ret = k5_pac_locate_buffer(context, pac, type, &d);
343 if (ret != 0)
344 return ret;
345
346 data->data = malloc(d.length);
347 if (data->data == NULL)
348 return ENOMEM;
349
350 data->length = d.length;
351 memcpy(data->data, d.data, d.length);
352
353 return 0;
354 }
355
356 /*
357 * Return an array of the types of data in the PAC
358 */
359 krb5_error_code KRB5_CALLCONV
krb5_pac_get_types(krb5_context context,krb5_pac pac,size_t * len,krb5_ui_4 ** types)360 krb5_pac_get_types(krb5_context context,
361 krb5_pac pac,
362 size_t *len,
363 krb5_ui_4 **types)
364 {
365 size_t i;
366
367 *types = (krb5_ui_4 *)malloc(pac->pac->cBuffers * sizeof(krb5_ui_4));
368 if (*types == NULL)
369 return ENOMEM;
370
371 *len = pac->pac->cBuffers;
372
373 for (i = 0; i < pac->pac->cBuffers; i++)
374 (*types)[i] = pac->pac->Buffers[i].ulType;
375
376 return 0;
377 }
378
379 /*
380 * Initialize PAC
381 */
382 krb5_error_code KRB5_CALLCONV
krb5_pac_init(krb5_context context,krb5_pac * ppac)383 krb5_pac_init(krb5_context context,
384 krb5_pac *ppac)
385 {
386 krb5_pac pac;
387
388 pac = (krb5_pac)malloc(sizeof(*pac));
389 if (pac == NULL)
390 return ENOMEM;
391
392 pac->pac = (PACTYPE *)malloc(sizeof(PACTYPE));
393 if (pac->pac == NULL) {
394 free( pac);
395 return ENOMEM;
396 }
397
398 pac->pac->cBuffers = 0;
399 pac->pac->Version = 0;
400
401 pac->data.length = PACTYPE_LENGTH;
402 pac->data.data = calloc(1, pac->data.length);
403 if (pac->data.data == NULL) {
404 krb5_pac_free(context, pac);
405 return ENOMEM;
406 }
407
408 *ppac = pac;
409
410 return 0;
411 }
412
413 /*
414 * Parse the supplied data into the PAC allocated by this function
415 */
416 krb5_error_code KRB5_CALLCONV
krb5_pac_parse(krb5_context context,const void * ptr,size_t len,krb5_pac * ppac)417 krb5_pac_parse(krb5_context context,
418 const void *ptr,
419 size_t len,
420 krb5_pac *ppac)
421 {
422 krb5_error_code ret;
423 size_t i;
424 const unsigned char *p = (const unsigned char *)ptr;
425 krb5_pac pac;
426 size_t header_len;
427 krb5_ui_4 cbuffers, version;
428
429 *ppac = NULL;
430
431 if (len < PACTYPE_LENGTH) {
432 /* Solaris Kerberos */
433 krb5_set_error_message(context, ERANGE,
434 "PAC type length is out of range (len=%d)",
435 len);
436 return ERANGE;
437 }
438
439 cbuffers = load_32_le(p);
440 p += 4;
441 version = load_32_le(p);
442 p += 4;
443
444 if (version != 0) {
445 /* Solaris Kerberos */
446 krb5_set_error_message(context, EINVAL,
447 "Invalid PAC version is %d, should be 0",
448 version);
449 return EINVAL;
450 }
451
452 header_len = PACTYPE_LENGTH + (cbuffers * PAC_INFO_BUFFER_LENGTH);
453 if (len < header_len) {
454 /* Solaris Kerberos */
455 krb5_set_error_message(context, ERANGE,
456 "PAC header len (%d) out of range",
457 len);
458 return ERANGE;
459 }
460
461 ret = krb5_pac_init(context, &pac);
462 if (ret != 0)
463 return ret;
464
465 pac->pac = (PACTYPE *)realloc(pac->pac,
466 sizeof(PACTYPE) + ((cbuffers - 1) * sizeof(PAC_INFO_BUFFER)));
467 if (pac->pac == NULL) {
468 krb5_pac_free(context, pac);
469 return ENOMEM;
470 }
471
472 pac->pac->cBuffers = cbuffers;
473 pac->pac->Version = version;
474
475 for (i = 0; i < pac->pac->cBuffers; i++) {
476 PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
477
478 buffer->ulType = load_32_le(p);
479 p += 4;
480 buffer->cbBufferSize = load_32_le(p);
481 p += 4;
482 buffer->Offset = load_64_le(p);
483 p += 8;
484
485 if (buffer->Offset % PAC_ALIGNMENT) {
486 krb5_pac_free(context, pac);
487 /* Solaris Kerberos */
488 krb5_set_error_message(context, EINVAL,
489 "PAC buffer offset mis-aligned");
490 return EINVAL;
491 }
492 if (buffer->Offset < header_len ||
493 buffer->Offset + buffer->cbBufferSize > len) {
494 krb5_pac_free(context, pac);
495 /* Solaris Kerberos */
496 krb5_set_error_message(context, ERANGE,
497 "PAC offset is out of range");
498 return ERANGE;
499 }
500 }
501
502 pac->data.data = realloc(pac->data.data, len);
503 if (pac->data.data == NULL) {
504 krb5_pac_free(context, pac);
505 return ENOMEM;
506 }
507 memcpy(pac->data.data, ptr, len);
508
509 pac->data.length = len;
510
511 *ppac = pac;
512
513 return 0;
514 }
515
516 static krb5_error_code
k5_time_to_seconds_since_1970(krb5_context context,krb5_int64 ntTime,krb5_timestamp * elapsedSeconds)517 k5_time_to_seconds_since_1970(krb5_context context, krb5_int64 ntTime, krb5_timestamp *elapsedSeconds)
518 {
519 krb5_ui_8 abstime;
520
521 ntTime /= 10000000;
522
523 abstime = ntTime > 0 ? ntTime - NT_TIME_EPOCH : -ntTime;
524
525 if (abstime > KRB5_INT32_MAX) {
526 return ERANGE;
527 }
528
529 *elapsedSeconds = abstime;
530
531 return 0;
532 }
533
534 static krb5_error_code
k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds,krb5_ui_8 * ntTime)535 k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, krb5_ui_8 *ntTime)
536 {
537 *ntTime = elapsedSeconds;
538
539 if (elapsedSeconds > 0)
540 *ntTime += NT_TIME_EPOCH;
541
542 *ntTime *= 10000000;
543
544 return 0;
545 }
546
547 static krb5_error_code
k5_pac_validate_client(krb5_context context,const krb5_pac pac,krb5_timestamp authtime,krb5_const_principal principal)548 k5_pac_validate_client(krb5_context context,
549 const krb5_pac pac,
550 krb5_timestamp authtime,
551 krb5_const_principal principal)
552 {
553 krb5_error_code ret;
554 krb5_data client_info;
555 char *pac_princname;
556 unsigned char *p;
557 krb5_timestamp pac_authtime;
558 krb5_ui_2 pac_princname_length;
559 krb5_int64 pac_nt_authtime;
560 krb5_principal pac_principal;
561
562 ret = k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info);
563 if (ret != 0)
564 return ret;
565
566 if (client_info.length < PAC_CLIENT_INFO_LENGTH) {
567 /* Solaris Kerberos */
568 krb5_set_error_message(context, ERANGE,
569 "PAC client info length out of range",
570 client_info.length);
571 return ERANGE;
572 }
573
574 p = (unsigned char *)client_info.data;
575 pac_nt_authtime = load_64_le(p);
576 p += 8;
577 pac_princname_length = load_16_le(p);
578 p += 2;
579
580 ret = k5_time_to_seconds_since_1970(context, pac_nt_authtime, &pac_authtime);
581 if (ret != 0)
582 return ret;
583
584 if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
585 pac_princname_length % 2) {
586 /* Solaris Kerberos */
587 krb5_set_error_message(context, ERANGE,
588 "PAC client info length is out of range");
589 return ERANGE;
590 }
591
592 ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2, &pac_princname, NULL);
593 if (ret != 0)
594 return ret;
595
596 ret = krb5_parse_name_flags(context, pac_princname, 0, &pac_principal);
597 if (ret != 0) {
598 free(pac_princname);
599 return ret;
600 }
601
602
603 if (pac_authtime != authtime) {
604 /* Solaris Kerberos */
605 char timestring[17];
606 char pac_timestring[17];
607 char fill = ' ';
608 int err, pac_err;
609 /* Need better ret code here but don't see one */
610 ret = KRB5KRB_AP_WRONG_PRINC;
611 err = krb5_timestamp_to_sfstring(pac_authtime,
612 timestring,
613 sizeof (timestring), &fill);
614 pac_err = krb5_timestamp_to_sfstring(pac_authtime,
615 pac_timestring,
616 sizeof (pac_timestring), &fill);
617 if (pac_princname && !err && !pac_err) {
618 krb5_set_error_message(context, ret,
619 "PAC verify fail: PAC authtime '%s' does not match authtime '%s'. PAC principal is '%s'",
620 pac_timestring, timestring, pac_princname);
621 }
622 } else if (krb5_principal_compare(context, pac_principal, principal) == FALSE) {
623 /* Solaris Kerberos */
624 char *p_name = NULL;
625 krb5_error_code perr;
626 ret = KRB5KRB_AP_WRONG_PRINC;
627 perr = krb5_unparse_name(context, principal, &p_name);
628 if (pac_princname && !perr) {
629 krb5_set_error_message(context, ret,
630 "Wrong principal in request: PAC verify: Principal in PAC is '%s' and does not match '%s'",
631 pac_princname, p_name);
632 }
633 if (p_name)
634 krb5_free_unparsed_name(context, p_name);
635 }
636
637 free(pac_princname);
638 krb5_free_principal(context, pac_principal);
639
640 return ret;
641 }
642
643 static krb5_error_code
k5_pac_zero_signature(krb5_context context,const krb5_pac pac,krb5_ui_4 type,krb5_data * data)644 k5_pac_zero_signature(krb5_context context,
645 const krb5_pac pac,
646 krb5_ui_4 type,
647 krb5_data *data)
648 {
649 PAC_INFO_BUFFER *buffer = NULL;
650 size_t i;
651
652 assert(type == PAC_SERVER_CHECKSUM || type == PAC_PRIVSVR_CHECKSUM);
653 assert(data->length >= pac->data.length);
654
655 for (i = 0; i < pac->pac->cBuffers; i++) {
656 if (pac->pac->Buffers[i].ulType == type) {
657 buffer = &pac->pac->Buffers[i];
658 break;
659 }
660 }
661
662 if (buffer == NULL) {
663 /* Solaris Kerberos */
664 krb5_set_error_message(context, ENOENT,
665 "No PAC buffer found (type=%d)",
666 type);
667 return ENOENT;
668 }
669
670 if (buffer->Offset + buffer->cbBufferSize > pac->data.length) {
671 return ERANGE;
672 }
673
674 if (buffer->cbBufferSize < PAC_SIGNATURE_DATA_LENGTH) {
675 return KRB5_BAD_MSIZE;
676 }
677
678 /* Zero out the data portion of the checksum only */
679 memset(data->data + buffer->Offset + PAC_SIGNATURE_DATA_LENGTH,
680 0,
681 buffer->cbBufferSize - PAC_SIGNATURE_DATA_LENGTH);
682
683 return 0;
684 }
685
686 static krb5_error_code
k5_pac_verify_server_checksum(krb5_context context,const krb5_pac pac,const krb5_keyblock * server)687 k5_pac_verify_server_checksum(krb5_context context,
688 const krb5_pac pac,
689 const krb5_keyblock *server)
690 {
691 krb5_error_code ret;
692 krb5_data pac_data; /* PAC with zeroed checksums */
693 krb5_checksum checksum;
694 krb5_data checksum_data;
695 krb5_boolean valid;
696 krb5_octet *p;
697
698 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &checksum_data);
699 if (ret != 0)
700 return ret;
701
702 if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH) {
703 return KRB5_BAD_MSIZE;
704 }
705
706 p = (krb5_octet *)checksum_data.data;
707 checksum.checksum_type = load_32_le(p);
708 checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
709 checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
710
711 pac_data.length = pac->data.length;
712 pac_data.data = malloc(pac->data.length);
713 if (pac_data.data == NULL)
714 return ENOMEM;
715
716 memcpy(pac_data.data, pac->data.data, pac->data.length);
717
718 /* Zero out both checksum buffers */
719 ret = k5_pac_zero_signature(context, pac, PAC_SERVER_CHECKSUM, &pac_data);
720 if (ret != 0) {
721 free(pac_data.data);
722 return ret;
723 }
724
725 ret = k5_pac_zero_signature(context, pac, PAC_PRIVSVR_CHECKSUM, &pac_data);
726 if (ret != 0) {
727 free(pac_data.data);
728 return ret;
729 }
730
731 ret = krb5_c_verify_checksum(context, server, KRB5_KEYUSAGE_APP_DATA_CKSUM,
732 &pac_data, &checksum, &valid);
733 if (ret != 0) {
734 free(pac_data.data);
735 return ret;
736 }
737
738 if (valid == FALSE) {
739 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
740 /* Solaris Kerberos */
741 krb5_set_error_message(context, ret,
742 "Decrypt integrity check failed for PAC");
743 }
744
745 free(pac_data.data); /* SUNW17PACresync - mem leak fix */
746 return ret;
747 }
748
749 static krb5_error_code
k5_pac_verify_kdc_checksum(krb5_context context,const krb5_pac pac,const krb5_keyblock * privsvr)750 k5_pac_verify_kdc_checksum(krb5_context context,
751 const krb5_pac pac,
752 const krb5_keyblock *privsvr)
753 {
754 krb5_error_code ret;
755 krb5_data server_checksum, privsvr_checksum;
756 krb5_checksum checksum;
757 krb5_boolean valid;
758 krb5_octet *p;
759
760 ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
761 if (ret != 0)
762 return ret;
763
764 if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
765 return KRB5_BAD_MSIZE;
766 }
767
768 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_checksum);
769 if (ret != 0)
770 return ret;
771
772 if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
773 return KRB5_BAD_MSIZE;
774 }
775
776 p = (krb5_octet *)privsvr_checksum.data;
777 checksum.checksum_type = load_32_le(p);
778 checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
779 checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
780
781 server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
782 server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
783
784 ret = krb5_c_verify_checksum(context, privsvr, KRB5_KEYUSAGE_APP_DATA_CKSUM,
785 &server_checksum, &checksum, &valid);
786 if (ret != 0)
787 return ret;
788
789 if (valid == FALSE) {
790 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
791 /* Solaris Kerberos */
792 krb5_set_error_message(context, ret,
793 "Decrypt integrity check failed for PAC");
794 }
795
796 return ret;
797 }
798
799 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)800 krb5_pac_verify(krb5_context context,
801 const krb5_pac pac,
802 krb5_timestamp authtime,
803 krb5_const_principal principal,
804 const krb5_keyblock *server,
805 const krb5_keyblock *privsvr)
806 {
807 krb5_error_code ret;
808
809 if (server == NULL) {
810 return EINVAL;
811 }
812
813 ret = k5_pac_verify_server_checksum(context, pac, server);
814 if (ret != 0)
815 return ret;
816
817 if (privsvr != NULL) {
818 ret = k5_pac_verify_kdc_checksum(context, pac, privsvr);
819 if (ret != 0)
820 return ret;
821 }
822
823 if (principal != NULL) {
824 ret = k5_pac_validate_client(context, pac, authtime, principal);
825 if (ret != 0)
826 return ret;
827 }
828
829 return 0;
830 }
831
832 static krb5_error_code
k5_insert_client_info(krb5_context context,krb5_pac pac,krb5_timestamp authtime,krb5_const_principal principal)833 k5_insert_client_info(krb5_context context,
834 krb5_pac pac,
835 krb5_timestamp authtime,
836 krb5_const_principal principal)
837 {
838 krb5_error_code ret;
839 krb5_data client_info;
840 char *princ_name_utf8 = NULL;
841 unsigned char *princ_name_ucs2 = NULL, *p;
842 size_t princ_name_ucs2_len = 0;
843 krb5_ui_8 nt_authtime;
844
845 /* If we already have a CLIENT_INFO buffer, then just validate it */
846 if (k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info) == 0) {
847 return k5_pac_validate_client(context, pac, authtime, principal);
848 }
849
850 ret = krb5_unparse_name_flags(context, principal,
851 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_name_utf8);
852 if (ret != 0)
853 goto cleanup;
854
855 ret = krb5int_utf8s_to_ucs2les(princ_name_utf8,
856 &princ_name_ucs2,
857 &princ_name_ucs2_len);
858 if (ret != 0)
859 goto cleanup;
860
861 client_info.length = PAC_CLIENT_INFO_LENGTH + princ_name_ucs2_len;
862 client_info.data = NULL;
863
864 ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO, &client_info, TRUE, &client_info);
865 if (ret != 0)
866 goto cleanup;
867
868 p = (unsigned char *)client_info.data;
869
870 /* copy in authtime converted to a 64-bit NT time */
871 k5_seconds_since_1970_to_time(authtime, &nt_authtime);
872 store_64_le(nt_authtime, p);
873 p += 8;
874
875 /* copy in number of UCS-2 characters in principal name */
876 store_16_le(princ_name_ucs2_len, p);
877 p += 2;
878
879 /* copy in principal name */
880 memcpy(p, princ_name_ucs2, princ_name_ucs2_len);
881
882 cleanup:
883 if (princ_name_utf8 != NULL)
884 free(princ_name_utf8);
885 if (princ_name_ucs2 != NULL)
886 free(princ_name_ucs2);
887
888 return ret;
889 }
890
891 static krb5_error_code
k5_insert_checksum(krb5_context context,krb5_pac pac,krb5_ui_4 type,const krb5_keyblock * key,krb5_cksumtype * cksumtype)892 k5_insert_checksum(krb5_context context,
893 krb5_pac pac,
894 krb5_ui_4 type,
895 const krb5_keyblock *key,
896 krb5_cksumtype *cksumtype)
897 {
898 krb5_error_code ret;
899 size_t len;
900 krb5_data cksumdata;
901
902 ret = krb5int_c_mandatory_cksumtype(context, key->enctype, cksumtype);
903 if (ret != 0)
904 return ret;
905
906 ret = krb5_c_checksum_length(context, *cksumtype, &len);
907 if (ret != 0)
908 return ret;
909
910 ret = k5_pac_locate_buffer(context, pac, type, &cksumdata);
911 if (ret == 0) {
912 /* If we're resigning PAC, make sure we can fit checksum into existing buffer */
913 if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len) {
914 return ERANGE;
915 }
916
917 memset(cksumdata.data, 0, cksumdata.length);
918 } else {
919 /* Add a zero filled buffer */
920 cksumdata.length = PAC_SIGNATURE_DATA_LENGTH + len;
921 cksumdata.data = NULL;
922
923 ret = k5_pac_add_buffer(context, pac, type, &cksumdata, TRUE, &cksumdata);
924 if (ret != 0)
925 return ret;
926 }
927
928 /* Encode checksum type into buffer */
929 store_32_le((krb5_ui_4)*cksumtype, cksumdata.data);
930
931 return 0;
932 }
933
934 /* in-place encoding of PAC header */
935 static krb5_error_code
k5_pac_encode_header(krb5_context context,krb5_pac pac)936 k5_pac_encode_header(krb5_context context, krb5_pac pac)
937 {
938 size_t i;
939 unsigned char *p;
940 size_t header_len;
941
942 header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
943 assert(pac->data.length >= header_len);
944
945 p = (unsigned char *)pac->data.data;
946
947 store_32_le(pac->pac->cBuffers, p);
948 p += 4;
949 store_32_le(pac->pac->Version, p);
950 p += 4;
951
952 for (i = 0; i < pac->pac->cBuffers; i++) {
953 PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
954
955 store_32_le(buffer->ulType, p);
956 p += 4;
957 store_32_le(buffer->cbBufferSize, p);
958 p += 4;
959 store_64_le(buffer->Offset, p);
960 p += 8;
961
962 assert((buffer->Offset % PAC_ALIGNMENT) == 0);
963 assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
964 assert(buffer->Offset >= header_len);
965
966 if (buffer->Offset % PAC_ALIGNMENT ||
967 buffer->Offset + buffer->cbBufferSize > pac->data.length ||
968 buffer->Offset < header_len) {
969 return ERANGE;
970 }
971 }
972
973 return 0;
974 }
975
976
977 #if 0
978 /*
979 * SUNW17PACresync
980 * We don't have the new MIT iov interfaces yet and don't need them yet.
981 * We'll need this for full 1.7 resync.
982 */
983 krb5_error_code KRB5_CALLCONV
984 krb5int_pac_sign(krb5_context context,
985 krb5_pac pac,
986 krb5_timestamp authtime,
987 krb5_const_principal principal,
988 const krb5_keyblock *server_key,
989 const krb5_keyblock *privsvr_key,
990 krb5_data *data)
991 {
992 krb5_error_code ret;
993 krb5_data server_cksum, privsvr_cksum;
994 krb5_cksumtype server_cksumtype, privsvr_cksumtype;
995 krb5_crypto_iov iov[2];
996
997 data->length = 0;
998 data->data = NULL;
999
1000 if (principal != NULL) {
1001 ret = k5_insert_client_info(context, pac, authtime, principal);
1002 if (ret != 0)
1003 return ret;
1004 }
1005
1006 /* Create zeroed buffers for both checksums */
1007 ret = k5_insert_checksum(context, pac, PAC_SERVER_CHECKSUM,
1008 server_key, &server_cksumtype);
1009 if (ret != 0)
1010 return ret;
1011
1012 ret = k5_insert_checksum(context, pac, PAC_PRIVSVR_CHECKSUM,
1013 privsvr_key, &privsvr_cksumtype);
1014 if (ret != 0)
1015 return ret;
1016
1017 /* Now, encode the PAC header so that the checksums will include it */
1018 ret = k5_pac_encode_header(context, pac);
1019 if (ret != 0)
1020 return ret;
1021
1022 /* Generate the server checksum over the entire PAC */
1023 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_cksum);
1024 if (ret != 0)
1025 return ret;
1026
1027 assert(server_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
1028
1029 iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
1030 iov[0].data = pac->data;
1031
1032 iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
1033 iov[1].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
1034 iov[1].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
1035
1036 ret = krb5_c_make_checksum_iov(context, server_cksumtype,
1037 server_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
1038 iov, sizeof(iov)/sizeof(iov[0]));
1039 if (ret != 0)
1040 return ret;
1041
1042 /* Generate the privsvr checksum over the server checksum buffer */
1043 ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_cksum);
1044 if (ret != 0)
1045 return ret;
1046
1047 assert(privsvr_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
1048
1049 iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
1050 iov[0].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
1051 iov[0].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
1052
1053 iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
1054 iov[1].data.data = privsvr_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
1055 iov[1].data.length = privsvr_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
1056
1057 ret = krb5_c_make_checksum_iov(context, privsvr_cksumtype,
1058 privsvr_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
1059 iov, sizeof(iov)/sizeof(iov[0]));
1060 if (ret != 0)
1061 return ret;
1062
1063 data->data = malloc(pac->data.length);
1064 if (data->data == NULL)
1065 return ENOMEM;
1066
1067 data->length = pac->data.length;
1068
1069 memcpy(data->data, pac->data.data, pac->data.length);
1070 memset(pac->data.data, 0, PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH));
1071
1072 return 0;
1073 }
1074 #endif
1075
1076