8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / gss_mechs / mech_krb5 / krb5 / krb / pac.c
blob35a4306a569d36fcc417fc5b7f113525af70824a
1 /*
2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4 /*
5 * lib/krb5/krb/pac.c
7 * Copyright 2008 by the Massachusetts Institute of Technology.
8 * All Rights Reserved.
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.
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.
31 #include "k5-int.h"
32 #include "k5-utf8.h"
34 /* draft-brezak-win2k-krb-authz-00 */
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).
43 * SUNW17PACresync
44 * These should eventually go to k5-platform.h or equiv.
46 static inline unsigned short
47 load_16_le (const void *cvp)
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
59 static inline unsigned int
60 load_32_le (const void *cvp)
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
71 static inline UINT64_TYPE
72 load_64_le (const void *cvp)
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
84 static inline void
85 store_16_le (unsigned int val, void *vp)
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
98 static inline void
99 store_32_le (unsigned int val, void *vp)
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
113 static inline void
114 store_64_le (UINT64_TYPE val, void *vp)
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
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;
140 #define PAC_INFO_BUFFER_LENGTH 16
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
148 typedef struct _PACTYPE {
149 krb5_ui_4 cBuffers;
150 krb5_ui_4 Version;
151 PAC_INFO_BUFFER Buffers[1];
152 } PACTYPE;
154 #define PAC_ALIGNMENT 8
155 #define PACTYPE_LENGTH 8U
156 #define PAC_SIGNATURE_DATA_LENGTH 4U
157 #define PAC_CLIENT_INFO_LENGTH 10U
159 #define NT_TIME_EPOCH 11644473600LL
161 struct krb5_pac_data {
162 PACTYPE *pac; /* PAC header + info buffer array */
163 krb5_data data; /* PAC data (including uninitialised header) */
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);
173 * Add a buffer to the provided PAC and update header.
175 static krb5_error_code
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)
183 PACTYPE *header;
184 size_t header_len, i, pad = 0;
185 char *pac_data;
187 assert((data->data == NULL) == zerofill);
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;
198 header = (PACTYPE *)realloc(pac->pac,
199 sizeof(PACTYPE) +
200 (pac->pac->cBuffers * sizeof(PAC_INFO_BUFFER)));
201 if (header == NULL) {
202 return ENOMEM;
204 pac->pac = header;
206 header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
208 if (data->length % PAC_ALIGNMENT)
209 pad = PAC_ALIGNMENT - (data->length % PAC_ALIGNMENT);
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;
216 pac->data.data = pac_data;
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;
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);
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);
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);
240 memset(pac->data.data + pac->pac->Buffers[i].Offset + data->length, 0, pad);
242 pac->pac->cBuffers++;
243 pac->data.length += PAC_INFO_BUFFER_LENGTH + data->length + pad;
245 if (out_data != NULL) {
246 out_data->data = pac->data.data + pac->pac->Buffers[i].Offset;
247 out_data->length = data->length;
250 return 0;
253 krb5_error_code KRB5_CALLCONV
254 krb5_pac_add_buffer(krb5_context context,
255 krb5_pac pac,
256 krb5_ui_4 type,
257 const krb5_data *data)
259 return k5_pac_add_buffer(context, pac, type, data, FALSE, NULL);
263 * Free a PAC
265 void KRB5_CALLCONV
266 krb5_pac_free(krb5_context context,
267 krb5_pac pac)
269 if (pac != NULL) {
270 if (pac->data.data != NULL) {
271 memset(pac->data.data, 0, pac->data.length);
272 free(pac->data.data);
274 if (pac->pac != NULL)
275 free(pac->pac);
276 memset(pac, 0, sizeof(*pac));
277 free(pac);
281 static krb5_error_code
282 k5_pac_locate_buffer(krb5_context context,
283 const krb5_pac pac,
284 krb5_ui_4 type,
285 krb5_data *data)
287 PAC_INFO_BUFFER *buffer = NULL;
288 size_t i;
290 if (pac == NULL) {
291 /* Solaris Kerberos */
292 krb5_set_error_message(context, EINVAL,
293 "Invalid argument 'pac' is NULL");
294 return EINVAL;
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;
311 if (buffer == NULL) {
312 /* Solaris Kerberos */
313 krb5_set_error_message(context, ENOENT,
314 "No PAC buffer found (type=%d)",
315 type);
317 return ENOENT;
320 assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
322 if (data != NULL) {
323 data->length = buffer->cbBufferSize;
324 data->data = pac->data.data + buffer->Offset;
327 return 0;
331 * Find a buffer and copy data into output
333 krb5_error_code KRB5_CALLCONV
334 krb5_pac_get_buffer(krb5_context context,
335 krb5_pac pac,
336 krb5_ui_4 type,
337 krb5_data *data)
339 krb5_data d;
340 krb5_error_code ret;
342 ret = k5_pac_locate_buffer(context, pac, type, &d);
343 if (ret != 0)
344 return ret;
346 data->data = malloc(d.length);
347 if (data->data == NULL)
348 return ENOMEM;
350 data->length = d.length;
351 memcpy(data->data, d.data, d.length);
353 return 0;
357 * Return an array of the types of data in the PAC
359 krb5_error_code KRB5_CALLCONV
360 krb5_pac_get_types(krb5_context context,
361 krb5_pac pac,
362 size_t *len,
363 krb5_ui_4 **types)
365 size_t i;
367 *types = (krb5_ui_4 *)malloc(pac->pac->cBuffers * sizeof(krb5_ui_4));
368 if (*types == NULL)
369 return ENOMEM;
371 *len = pac->pac->cBuffers;
373 for (i = 0; i < pac->pac->cBuffers; i++)
374 (*types)[i] = pac->pac->Buffers[i].ulType;
376 return 0;
380 * Initialize PAC
382 krb5_error_code KRB5_CALLCONV
383 krb5_pac_init(krb5_context context,
384 krb5_pac *ppac)
386 krb5_pac pac;
388 pac = (krb5_pac)malloc(sizeof(*pac));
389 if (pac == NULL)
390 return ENOMEM;
392 pac->pac = (PACTYPE *)malloc(sizeof(PACTYPE));
393 if (pac->pac == NULL) {
394 free( pac);
395 return ENOMEM;
398 pac->pac->cBuffers = 0;
399 pac->pac->Version = 0;
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;
408 *ppac = pac;
410 return 0;
414 * Parse the supplied data into the PAC allocated by this function
416 krb5_error_code KRB5_CALLCONV
417 krb5_pac_parse(krb5_context context,
418 const void *ptr,
419 size_t len,
420 krb5_pac *ppac)
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;
429 *ppac = NULL;
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;
439 cbuffers = load_32_le(p);
440 p += 4;
441 version = load_32_le(p);
442 p += 4;
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;
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;
461 ret = krb5_pac_init(context, &pac);
462 if (ret != 0)
463 return ret;
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;
472 pac->pac->cBuffers = cbuffers;
473 pac->pac->Version = version;
475 for (i = 0; i < pac->pac->cBuffers; i++) {
476 PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
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;
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;
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;
502 pac->data.data = realloc(pac->data.data, len);
503 if (pac->data.data == NULL) {
504 krb5_pac_free(context, pac);
505 return ENOMEM;
507 memcpy(pac->data.data, ptr, len);
509 pac->data.length = len;
511 *ppac = pac;
513 return 0;
516 static krb5_error_code
517 k5_time_to_seconds_since_1970(krb5_context context, krb5_int64 ntTime, krb5_timestamp *elapsedSeconds)
519 krb5_ui_8 abstime;
521 ntTime /= 10000000;
523 abstime = ntTime > 0 ? ntTime - NT_TIME_EPOCH : -ntTime;
525 if (abstime > KRB5_INT32_MAX) {
526 return ERANGE;
529 *elapsedSeconds = abstime;
531 return 0;
534 static krb5_error_code
535 k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, krb5_ui_8 *ntTime)
537 *ntTime = elapsedSeconds;
539 if (elapsedSeconds > 0)
540 *ntTime += NT_TIME_EPOCH;
542 *ntTime *= 10000000;
544 return 0;
547 static krb5_error_code
548 k5_pac_validate_client(krb5_context context,
549 const krb5_pac pac,
550 krb5_timestamp authtime,
551 krb5_const_principal principal)
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;
562 ret = k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info);
563 if (ret != 0)
564 return ret;
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;
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;
580 ret = k5_time_to_seconds_since_1970(context, pac_nt_authtime, &pac_authtime);
581 if (ret != 0)
582 return ret;
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;
592 ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2, &pac_princname, NULL);
593 if (ret != 0)
594 return ret;
596 ret = krb5_parse_name_flags(context, pac_princname, 0, &pac_principal);
597 if (ret != 0) {
598 free(pac_princname);
599 return ret;
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);
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);
633 if (p_name)
634 krb5_free_unparsed_name(context, p_name);
637 free(pac_princname);
638 krb5_free_principal(context, pac_principal);
640 return ret;
643 static krb5_error_code
644 k5_pac_zero_signature(krb5_context context,
645 const krb5_pac pac,
646 krb5_ui_4 type,
647 krb5_data *data)
649 PAC_INFO_BUFFER *buffer = NULL;
650 size_t i;
652 assert(type == PAC_SERVER_CHECKSUM || type == PAC_PRIVSVR_CHECKSUM);
653 assert(data->length >= pac->data.length);
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;
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;
670 if (buffer->Offset + buffer->cbBufferSize > pac->data.length) {
671 return ERANGE;
674 if (buffer->cbBufferSize < PAC_SIGNATURE_DATA_LENGTH) {
675 return KRB5_BAD_MSIZE;
678 /* Zero out the data portion of the checksum only */
679 memset(data->data + buffer->Offset + PAC_SIGNATURE_DATA_LENGTH,
681 buffer->cbBufferSize - PAC_SIGNATURE_DATA_LENGTH);
683 return 0;
686 static krb5_error_code
687 k5_pac_verify_server_checksum(krb5_context context,
688 const krb5_pac pac,
689 const krb5_keyblock *server)
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;
698 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &checksum_data);
699 if (ret != 0)
700 return ret;
702 if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH) {
703 return KRB5_BAD_MSIZE;
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;
711 pac_data.length = pac->data.length;
712 pac_data.data = malloc(pac->data.length);
713 if (pac_data.data == NULL)
714 return ENOMEM;
716 memcpy(pac_data.data, pac->data.data, pac->data.length);
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;
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;
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;
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");
745 free(pac_data.data); /* SUNW17PACresync - mem leak fix */
746 return ret;
749 static krb5_error_code
750 k5_pac_verify_kdc_checksum(krb5_context context,
751 const krb5_pac pac,
752 const krb5_keyblock *privsvr)
754 krb5_error_code ret;
755 krb5_data server_checksum, privsvr_checksum;
756 krb5_checksum checksum;
757 krb5_boolean valid;
758 krb5_octet *p;
760 ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
761 if (ret != 0)
762 return ret;
764 if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
765 return KRB5_BAD_MSIZE;
768 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_checksum);
769 if (ret != 0)
770 return ret;
772 if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
773 return KRB5_BAD_MSIZE;
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;
781 server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
782 server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
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;
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");
796 return ret;
799 krb5_error_code KRB5_CALLCONV
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)
807 krb5_error_code ret;
809 if (server == NULL) {
810 return EINVAL;
813 ret = k5_pac_verify_server_checksum(context, pac, server);
814 if (ret != 0)
815 return ret;
817 if (privsvr != NULL) {
818 ret = k5_pac_verify_kdc_checksum(context, pac, privsvr);
819 if (ret != 0)
820 return ret;
823 if (principal != NULL) {
824 ret = k5_pac_validate_client(context, pac, authtime, principal);
825 if (ret != 0)
826 return ret;
829 return 0;
832 static krb5_error_code
833 k5_insert_client_info(krb5_context context,
834 krb5_pac pac,
835 krb5_timestamp authtime,
836 krb5_const_principal principal)
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;
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);
850 ret = krb5_unparse_name_flags(context, principal,
851 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_name_utf8);
852 if (ret != 0)
853 goto cleanup;
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;
861 client_info.length = PAC_CLIENT_INFO_LENGTH + princ_name_ucs2_len;
862 client_info.data = NULL;
864 ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO, &client_info, TRUE, &client_info);
865 if (ret != 0)
866 goto cleanup;
868 p = (unsigned char *)client_info.data;
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;
875 /* copy in number of UCS-2 characters in principal name */
876 store_16_le(princ_name_ucs2_len, p);
877 p += 2;
879 /* copy in principal name */
880 memcpy(p, princ_name_ucs2, princ_name_ucs2_len);
882 cleanup:
883 if (princ_name_utf8 != NULL)
884 free(princ_name_utf8);
885 if (princ_name_ucs2 != NULL)
886 free(princ_name_ucs2);
888 return ret;
891 static krb5_error_code
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)
898 krb5_error_code ret;
899 size_t len;
900 krb5_data cksumdata;
902 ret = krb5int_c_mandatory_cksumtype(context, key->enctype, cksumtype);
903 if (ret != 0)
904 return ret;
906 ret = krb5_c_checksum_length(context, *cksumtype, &len);
907 if (ret != 0)
908 return ret;
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;
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;
923 ret = k5_pac_add_buffer(context, pac, type, &cksumdata, TRUE, &cksumdata);
924 if (ret != 0)
925 return ret;
928 /* Encode checksum type into buffer */
929 store_32_le((krb5_ui_4)*cksumtype, cksumdata.data);
931 return 0;
934 /* in-place encoding of PAC header */
935 static krb5_error_code
936 k5_pac_encode_header(krb5_context context, krb5_pac pac)
938 size_t i;
939 unsigned char *p;
940 size_t header_len;
942 header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
943 assert(pac->data.length >= header_len);
945 p = (unsigned char *)pac->data.data;
947 store_32_le(pac->pac->cBuffers, p);
948 p += 4;
949 store_32_le(pac->pac->Version, p);
950 p += 4;
952 for (i = 0; i < pac->pac->cBuffers; i++) {
953 PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
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;
962 assert((buffer->Offset % PAC_ALIGNMENT) == 0);
963 assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
964 assert(buffer->Offset >= header_len);
966 if (buffer->Offset % PAC_ALIGNMENT ||
967 buffer->Offset + buffer->cbBufferSize > pac->data.length ||
968 buffer->Offset < header_len) {
969 return ERANGE;
973 return 0;
977 #if 0
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.
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)
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];
997 data->length = 0;
998 data->data = NULL;
1000 if (principal != NULL) {
1001 ret = k5_insert_client_info(context, pac, authtime, principal);
1002 if (ret != 0)
1003 return ret;
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;
1012 ret = k5_insert_checksum(context, pac, PAC_PRIVSVR_CHECKSUM,
1013 privsvr_key, &privsvr_cksumtype);
1014 if (ret != 0)
1015 return ret;
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;
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;
1027 assert(server_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
1029 iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
1030 iov[0].data = pac->data;
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;
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;
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;
1047 assert(privsvr_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
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;
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;
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;
1063 data->data = malloc(pac->data.length);
1064 if (data->data == NULL)
1065 return ENOMEM;
1067 data->length = pac->data.length;
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));
1072 return 0;
1074 #endif