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