dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / gss_mechs / mech_krb5 / krb5 / krb / pac.c
blobeb2bb07512bb2a3e6afef964f14b7722976f7c8a
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 free(pac->pac);
275 memset(pac, 0, sizeof(*pac));
276 free(pac);
280 static krb5_error_code
281 k5_pac_locate_buffer(krb5_context context,
282 const krb5_pac pac,
283 krb5_ui_4 type,
284 krb5_data *data)
286 PAC_INFO_BUFFER *buffer = NULL;
287 size_t i;
289 if (pac == NULL) {
290 /* Solaris Kerberos */
291 krb5_set_error_message(context, EINVAL,
292 "Invalid argument 'pac' is NULL");
293 return EINVAL;
296 for (i = 0; i < pac->pac->cBuffers; i++) {
297 if (pac->pac->Buffers[i].ulType == type) {
298 if (buffer == NULL)
299 buffer = &pac->pac->Buffers[i];
300 else {
301 /* Solaris Kerberos */
302 krb5_set_error_message(context, EINVAL,
303 "Invalid buffer found looping thru PAC buffers (type=%d, i=%d)",
304 type, i);
305 return EINVAL;
310 if (buffer == NULL) {
311 /* Solaris Kerberos */
312 krb5_set_error_message(context, ENOENT,
313 "No PAC buffer found (type=%d)",
314 type);
316 return ENOENT;
319 assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
321 if (data != NULL) {
322 data->length = buffer->cbBufferSize;
323 data->data = pac->data.data + buffer->Offset;
326 return 0;
330 * Find a buffer and copy data into output
332 krb5_error_code KRB5_CALLCONV
333 krb5_pac_get_buffer(krb5_context context,
334 krb5_pac pac,
335 krb5_ui_4 type,
336 krb5_data *data)
338 krb5_data d;
339 krb5_error_code ret;
341 ret = k5_pac_locate_buffer(context, pac, type, &d);
342 if (ret != 0)
343 return ret;
345 data->data = malloc(d.length);
346 if (data->data == NULL)
347 return ENOMEM;
349 data->length = d.length;
350 memcpy(data->data, d.data, d.length);
352 return 0;
356 * Return an array of the types of data in the PAC
358 krb5_error_code KRB5_CALLCONV
359 krb5_pac_get_types(krb5_context context,
360 krb5_pac pac,
361 size_t *len,
362 krb5_ui_4 **types)
364 size_t i;
366 *types = (krb5_ui_4 *)malloc(pac->pac->cBuffers * sizeof(krb5_ui_4));
367 if (*types == NULL)
368 return ENOMEM;
370 *len = pac->pac->cBuffers;
372 for (i = 0; i < pac->pac->cBuffers; i++)
373 (*types)[i] = pac->pac->Buffers[i].ulType;
375 return 0;
379 * Initialize PAC
381 krb5_error_code KRB5_CALLCONV
382 krb5_pac_init(krb5_context context,
383 krb5_pac *ppac)
385 krb5_pac pac;
387 pac = (krb5_pac)malloc(sizeof(*pac));
388 if (pac == NULL)
389 return ENOMEM;
391 pac->pac = (PACTYPE *)malloc(sizeof(PACTYPE));
392 if (pac->pac == NULL) {
393 free( pac);
394 return ENOMEM;
397 pac->pac->cBuffers = 0;
398 pac->pac->Version = 0;
400 pac->data.length = PACTYPE_LENGTH;
401 pac->data.data = calloc(1, pac->data.length);
402 if (pac->data.data == NULL) {
403 krb5_pac_free(context, pac);
404 return ENOMEM;
407 *ppac = pac;
409 return 0;
413 * Parse the supplied data into the PAC allocated by this function
415 krb5_error_code KRB5_CALLCONV
416 krb5_pac_parse(krb5_context context,
417 const void *ptr,
418 size_t len,
419 krb5_pac *ppac)
421 krb5_error_code ret;
422 size_t i;
423 const unsigned char *p = (const unsigned char *)ptr;
424 krb5_pac pac;
425 size_t header_len;
426 krb5_ui_4 cbuffers, version;
428 *ppac = NULL;
430 if (len < PACTYPE_LENGTH) {
431 /* Solaris Kerberos */
432 krb5_set_error_message(context, ERANGE,
433 "PAC type length is out of range (len=%d)",
434 len);
435 return ERANGE;
438 cbuffers = load_32_le(p);
439 p += 4;
440 version = load_32_le(p);
441 p += 4;
443 if (version != 0) {
444 /* Solaris Kerberos */
445 krb5_set_error_message(context, EINVAL,
446 "Invalid PAC version is %d, should be 0",
447 version);
448 return EINVAL;
451 header_len = PACTYPE_LENGTH + (cbuffers * PAC_INFO_BUFFER_LENGTH);
452 if (len < header_len) {
453 /* Solaris Kerberos */
454 krb5_set_error_message(context, ERANGE,
455 "PAC header len (%d) out of range",
456 len);
457 return ERANGE;
460 ret = krb5_pac_init(context, &pac);
461 if (ret != 0)
462 return ret;
464 pac->pac = (PACTYPE *)realloc(pac->pac,
465 sizeof(PACTYPE) + ((cbuffers - 1) * sizeof(PAC_INFO_BUFFER)));
466 if (pac->pac == NULL) {
467 krb5_pac_free(context, pac);
468 return ENOMEM;
471 pac->pac->cBuffers = cbuffers;
472 pac->pac->Version = version;
474 for (i = 0; i < pac->pac->cBuffers; i++) {
475 PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
477 buffer->ulType = load_32_le(p);
478 p += 4;
479 buffer->cbBufferSize = load_32_le(p);
480 p += 4;
481 buffer->Offset = load_64_le(p);
482 p += 8;
484 if (buffer->Offset % PAC_ALIGNMENT) {
485 krb5_pac_free(context, pac);
486 /* Solaris Kerberos */
487 krb5_set_error_message(context, EINVAL,
488 "PAC buffer offset mis-aligned");
489 return EINVAL;
491 if (buffer->Offset < header_len ||
492 buffer->Offset + buffer->cbBufferSize > len) {
493 krb5_pac_free(context, pac);
494 /* Solaris Kerberos */
495 krb5_set_error_message(context, ERANGE,
496 "PAC offset is out of range");
497 return ERANGE;
501 pac->data.data = realloc(pac->data.data, len);
502 if (pac->data.data == NULL) {
503 krb5_pac_free(context, pac);
504 return ENOMEM;
506 memcpy(pac->data.data, ptr, len);
508 pac->data.length = len;
510 *ppac = pac;
512 return 0;
515 static krb5_error_code
516 k5_time_to_seconds_since_1970(krb5_context context, krb5_int64 ntTime, krb5_timestamp *elapsedSeconds)
518 krb5_ui_8 abstime;
520 ntTime /= 10000000;
522 abstime = ntTime > 0 ? ntTime - NT_TIME_EPOCH : -ntTime;
524 if (abstime > KRB5_INT32_MAX) {
525 return ERANGE;
528 *elapsedSeconds = abstime;
530 return 0;
533 static krb5_error_code
534 k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, krb5_ui_8 *ntTime)
536 *ntTime = elapsedSeconds;
538 if (elapsedSeconds > 0)
539 *ntTime += NT_TIME_EPOCH;
541 *ntTime *= 10000000;
543 return 0;
546 static krb5_error_code
547 k5_pac_validate_client(krb5_context context,
548 const krb5_pac pac,
549 krb5_timestamp authtime,
550 krb5_const_principal principal)
552 krb5_error_code ret;
553 krb5_data client_info;
554 char *pac_princname;
555 unsigned char *p;
556 krb5_timestamp pac_authtime;
557 krb5_ui_2 pac_princname_length;
558 krb5_int64 pac_nt_authtime;
559 krb5_principal pac_principal;
561 ret = k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info);
562 if (ret != 0)
563 return ret;
565 if (client_info.length < PAC_CLIENT_INFO_LENGTH) {
566 /* Solaris Kerberos */
567 krb5_set_error_message(context, ERANGE,
568 "PAC client info length out of range",
569 client_info.length);
570 return ERANGE;
573 p = (unsigned char *)client_info.data;
574 pac_nt_authtime = load_64_le(p);
575 p += 8;
576 pac_princname_length = load_16_le(p);
577 p += 2;
579 ret = k5_time_to_seconds_since_1970(context, pac_nt_authtime, &pac_authtime);
580 if (ret != 0)
581 return ret;
583 if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
584 pac_princname_length % 2) {
585 /* Solaris Kerberos */
586 krb5_set_error_message(context, ERANGE,
587 "PAC client info length is out of range");
588 return ERANGE;
591 ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2, &pac_princname, NULL);
592 if (ret != 0)
593 return ret;
595 ret = krb5_parse_name_flags(context, pac_princname, 0, &pac_principal);
596 if (ret != 0) {
597 free(pac_princname);
598 return ret;
602 if (pac_authtime != authtime) {
603 /* Solaris Kerberos */
604 char timestring[17];
605 char pac_timestring[17];
606 char fill = ' ';
607 int err, pac_err;
608 /* Need better ret code here but don't see one */
609 ret = KRB5KRB_AP_WRONG_PRINC;
610 err = krb5_timestamp_to_sfstring(pac_authtime,
611 timestring,
612 sizeof (timestring), &fill);
613 pac_err = krb5_timestamp_to_sfstring(pac_authtime,
614 pac_timestring,
615 sizeof (pac_timestring), &fill);
616 if (pac_princname && !err && !pac_err) {
617 krb5_set_error_message(context, ret,
618 "PAC verify fail: PAC authtime '%s' does not match authtime '%s'. PAC principal is '%s'",
619 pac_timestring, timestring, pac_princname);
621 } else if (krb5_principal_compare(context, pac_principal, principal) == FALSE) {
622 /* Solaris Kerberos */
623 char *p_name = NULL;
624 krb5_error_code perr;
625 ret = KRB5KRB_AP_WRONG_PRINC;
626 perr = krb5_unparse_name(context, principal, &p_name);
627 if (pac_princname && !perr) {
628 krb5_set_error_message(context, ret,
629 "Wrong principal in request: PAC verify: Principal in PAC is '%s' and does not match '%s'",
630 pac_princname, p_name);
632 if (p_name)
633 krb5_free_unparsed_name(context, p_name);
636 free(pac_princname);
637 krb5_free_principal(context, pac_principal);
639 return ret;
642 static krb5_error_code
643 k5_pac_zero_signature(krb5_context context,
644 const krb5_pac pac,
645 krb5_ui_4 type,
646 krb5_data *data)
648 PAC_INFO_BUFFER *buffer = NULL;
649 size_t i;
651 assert(type == PAC_SERVER_CHECKSUM || type == PAC_PRIVSVR_CHECKSUM);
652 assert(data->length >= pac->data.length);
654 for (i = 0; i < pac->pac->cBuffers; i++) {
655 if (pac->pac->Buffers[i].ulType == type) {
656 buffer = &pac->pac->Buffers[i];
657 break;
661 if (buffer == NULL) {
662 /* Solaris Kerberos */
663 krb5_set_error_message(context, ENOENT,
664 "No PAC buffer found (type=%d)",
665 type);
666 return ENOENT;
669 if (buffer->Offset + buffer->cbBufferSize > pac->data.length) {
670 return ERANGE;
673 if (buffer->cbBufferSize < PAC_SIGNATURE_DATA_LENGTH) {
674 return KRB5_BAD_MSIZE;
677 /* Zero out the data portion of the checksum only */
678 memset(data->data + buffer->Offset + PAC_SIGNATURE_DATA_LENGTH,
680 buffer->cbBufferSize - PAC_SIGNATURE_DATA_LENGTH);
682 return 0;
685 static krb5_error_code
686 k5_pac_verify_server_checksum(krb5_context context,
687 const krb5_pac pac,
688 const krb5_keyblock *server)
690 krb5_error_code ret;
691 krb5_data pac_data; /* PAC with zeroed checksums */
692 krb5_checksum checksum;
693 krb5_data checksum_data;
694 krb5_boolean valid;
695 krb5_octet *p;
697 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &checksum_data);
698 if (ret != 0)
699 return ret;
701 if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH) {
702 return KRB5_BAD_MSIZE;
705 p = (krb5_octet *)checksum_data.data;
706 checksum.checksum_type = load_32_le(p);
707 checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
708 checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
710 pac_data.length = pac->data.length;
711 pac_data.data = malloc(pac->data.length);
712 if (pac_data.data == NULL)
713 return ENOMEM;
715 memcpy(pac_data.data, pac->data.data, pac->data.length);
717 /* Zero out both checksum buffers */
718 ret = k5_pac_zero_signature(context, pac, PAC_SERVER_CHECKSUM, &pac_data);
719 if (ret != 0) {
720 free(pac_data.data);
721 return ret;
724 ret = k5_pac_zero_signature(context, pac, PAC_PRIVSVR_CHECKSUM, &pac_data);
725 if (ret != 0) {
726 free(pac_data.data);
727 return ret;
730 ret = krb5_c_verify_checksum(context, server, KRB5_KEYUSAGE_APP_DATA_CKSUM,
731 &pac_data, &checksum, &valid);
732 if (ret != 0) {
733 free(pac_data.data);
734 return ret;
737 if (valid == FALSE) {
738 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
739 /* Solaris Kerberos */
740 krb5_set_error_message(context, ret,
741 "Decrypt integrity check failed for PAC");
744 free(pac_data.data); /* SUNW17PACresync - mem leak fix */
745 return ret;
748 static krb5_error_code
749 k5_pac_verify_kdc_checksum(krb5_context context,
750 const krb5_pac pac,
751 const krb5_keyblock *privsvr)
753 krb5_error_code ret;
754 krb5_data server_checksum, privsvr_checksum;
755 krb5_checksum checksum;
756 krb5_boolean valid;
757 krb5_octet *p;
759 ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
760 if (ret != 0)
761 return ret;
763 if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
764 return KRB5_BAD_MSIZE;
767 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_checksum);
768 if (ret != 0)
769 return ret;
771 if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
772 return KRB5_BAD_MSIZE;
775 p = (krb5_octet *)privsvr_checksum.data;
776 checksum.checksum_type = load_32_le(p);
777 checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
778 checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
780 server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
781 server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
783 ret = krb5_c_verify_checksum(context, privsvr, KRB5_KEYUSAGE_APP_DATA_CKSUM,
784 &server_checksum, &checksum, &valid);
785 if (ret != 0)
786 return ret;
788 if (valid == FALSE) {
789 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
790 /* Solaris Kerberos */
791 krb5_set_error_message(context, ret,
792 "Decrypt integrity check failed for PAC");
795 return ret;
798 krb5_error_code KRB5_CALLCONV
799 krb5_pac_verify(krb5_context context,
800 const krb5_pac pac,
801 krb5_timestamp authtime,
802 krb5_const_principal principal,
803 const krb5_keyblock *server,
804 const krb5_keyblock *privsvr)
806 krb5_error_code ret;
808 if (server == NULL) {
809 return EINVAL;
812 ret = k5_pac_verify_server_checksum(context, pac, server);
813 if (ret != 0)
814 return ret;
816 if (privsvr != NULL) {
817 ret = k5_pac_verify_kdc_checksum(context, pac, privsvr);
818 if (ret != 0)
819 return ret;
822 if (principal != NULL) {
823 ret = k5_pac_validate_client(context, pac, authtime, principal);
824 if (ret != 0)
825 return ret;
828 return 0;
831 static krb5_error_code
832 k5_insert_client_info(krb5_context context,
833 krb5_pac pac,
834 krb5_timestamp authtime,
835 krb5_const_principal principal)
837 krb5_error_code ret;
838 krb5_data client_info;
839 char *princ_name_utf8 = NULL;
840 unsigned char *princ_name_ucs2 = NULL, *p;
841 size_t princ_name_ucs2_len = 0;
842 krb5_ui_8 nt_authtime;
844 /* If we already have a CLIENT_INFO buffer, then just validate it */
845 if (k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info) == 0) {
846 return k5_pac_validate_client(context, pac, authtime, principal);
849 ret = krb5_unparse_name_flags(context, principal,
850 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_name_utf8);
851 if (ret != 0)
852 goto cleanup;
854 ret = krb5int_utf8s_to_ucs2les(princ_name_utf8,
855 &princ_name_ucs2,
856 &princ_name_ucs2_len);
857 if (ret != 0)
858 goto cleanup;
860 client_info.length = PAC_CLIENT_INFO_LENGTH + princ_name_ucs2_len;
861 client_info.data = NULL;
863 ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO, &client_info, TRUE, &client_info);
864 if (ret != 0)
865 goto cleanup;
867 p = (unsigned char *)client_info.data;
869 /* copy in authtime converted to a 64-bit NT time */
870 k5_seconds_since_1970_to_time(authtime, &nt_authtime);
871 store_64_le(nt_authtime, p);
872 p += 8;
874 /* copy in number of UCS-2 characters in principal name */
875 store_16_le(princ_name_ucs2_len, p);
876 p += 2;
878 /* copy in principal name */
879 memcpy(p, princ_name_ucs2, princ_name_ucs2_len);
881 cleanup:
882 free(princ_name_utf8);
883 free(princ_name_ucs2);
885 return ret;
888 static krb5_error_code
889 k5_insert_checksum(krb5_context context,
890 krb5_pac pac,
891 krb5_ui_4 type,
892 const krb5_keyblock *key,
893 krb5_cksumtype *cksumtype)
895 krb5_error_code ret;
896 size_t len;
897 krb5_data cksumdata;
899 ret = krb5int_c_mandatory_cksumtype(context, key->enctype, cksumtype);
900 if (ret != 0)
901 return ret;
903 ret = krb5_c_checksum_length(context, *cksumtype, &len);
904 if (ret != 0)
905 return ret;
907 ret = k5_pac_locate_buffer(context, pac, type, &cksumdata);
908 if (ret == 0) {
909 /* If we're resigning PAC, make sure we can fit checksum into existing buffer */
910 if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len) {
911 return ERANGE;
914 memset(cksumdata.data, 0, cksumdata.length);
915 } else {
916 /* Add a zero filled buffer */
917 cksumdata.length = PAC_SIGNATURE_DATA_LENGTH + len;
918 cksumdata.data = NULL;
920 ret = k5_pac_add_buffer(context, pac, type, &cksumdata, TRUE, &cksumdata);
921 if (ret != 0)
922 return ret;
925 /* Encode checksum type into buffer */
926 store_32_le((krb5_ui_4)*cksumtype, cksumdata.data);
928 return 0;
931 /* in-place encoding of PAC header */
932 static krb5_error_code
933 k5_pac_encode_header(krb5_context context, krb5_pac pac)
935 size_t i;
936 unsigned char *p;
937 size_t header_len;
939 header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
940 assert(pac->data.length >= header_len);
942 p = (unsigned char *)pac->data.data;
944 store_32_le(pac->pac->cBuffers, p);
945 p += 4;
946 store_32_le(pac->pac->Version, p);
947 p += 4;
949 for (i = 0; i < pac->pac->cBuffers; i++) {
950 PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
952 store_32_le(buffer->ulType, p);
953 p += 4;
954 store_32_le(buffer->cbBufferSize, p);
955 p += 4;
956 store_64_le(buffer->Offset, p);
957 p += 8;
959 assert((buffer->Offset % PAC_ALIGNMENT) == 0);
960 assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
961 assert(buffer->Offset >= header_len);
963 if (buffer->Offset % PAC_ALIGNMENT ||
964 buffer->Offset + buffer->cbBufferSize > pac->data.length ||
965 buffer->Offset < header_len) {
966 return ERANGE;
970 return 0;
974 #if 0
976 * SUNW17PACresync
977 * We don't have the new MIT iov interfaces yet and don't need them yet.
978 * We'll need this for full 1.7 resync.
980 krb5_error_code KRB5_CALLCONV
981 krb5int_pac_sign(krb5_context context,
982 krb5_pac pac,
983 krb5_timestamp authtime,
984 krb5_const_principal principal,
985 const krb5_keyblock *server_key,
986 const krb5_keyblock *privsvr_key,
987 krb5_data *data)
989 krb5_error_code ret;
990 krb5_data server_cksum, privsvr_cksum;
991 krb5_cksumtype server_cksumtype, privsvr_cksumtype;
992 krb5_crypto_iov iov[2];
994 data->length = 0;
995 data->data = NULL;
997 if (principal != NULL) {
998 ret = k5_insert_client_info(context, pac, authtime, principal);
999 if (ret != 0)
1000 return ret;
1003 /* Create zeroed buffers for both checksums */
1004 ret = k5_insert_checksum(context, pac, PAC_SERVER_CHECKSUM,
1005 server_key, &server_cksumtype);
1006 if (ret != 0)
1007 return ret;
1009 ret = k5_insert_checksum(context, pac, PAC_PRIVSVR_CHECKSUM,
1010 privsvr_key, &privsvr_cksumtype);
1011 if (ret != 0)
1012 return ret;
1014 /* Now, encode the PAC header so that the checksums will include it */
1015 ret = k5_pac_encode_header(context, pac);
1016 if (ret != 0)
1017 return ret;
1019 /* Generate the server checksum over the entire PAC */
1020 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_cksum);
1021 if (ret != 0)
1022 return ret;
1024 assert(server_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
1026 iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
1027 iov[0].data = pac->data;
1029 iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
1030 iov[1].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
1031 iov[1].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
1033 ret = krb5_c_make_checksum_iov(context, server_cksumtype,
1034 server_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
1035 iov, sizeof(iov)/sizeof(iov[0]));
1036 if (ret != 0)
1037 return ret;
1039 /* Generate the privsvr checksum over the server checksum buffer */
1040 ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_cksum);
1041 if (ret != 0)
1042 return ret;
1044 assert(privsvr_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
1046 iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
1047 iov[0].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
1048 iov[0].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
1050 iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
1051 iov[1].data.data = privsvr_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
1052 iov[1].data.length = privsvr_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
1054 ret = krb5_c_make_checksum_iov(context, privsvr_cksumtype,
1055 privsvr_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
1056 iov, sizeof(iov)/sizeof(iov[0]));
1057 if (ret != 0)
1058 return ret;
1060 data->data = malloc(pac->data.length);
1061 if (data->data == NULL)
1062 return ENOMEM;
1064 data->length = pac->data.length;
1066 memcpy(data->data, pac->data.data, pac->data.length);
1067 memset(pac->data.data, 0, PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH));
1069 return 0;
1071 #endif