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