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