xref: /onnv-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/pac.c (revision 13132:9615cdbf7b70)
110598SGlenn.Barry@Sun.COM /*
2*13132SGlenn.Barry@oracle.com  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
310598SGlenn.Barry@Sun.COM  */
410598SGlenn.Barry@Sun.COM /*
510598SGlenn.Barry@Sun.COM  * lib/krb5/krb/pac.c
610598SGlenn.Barry@Sun.COM  *
710598SGlenn.Barry@Sun.COM  * Copyright 2008 by the Massachusetts Institute of Technology.
810598SGlenn.Barry@Sun.COM  * All Rights Reserved.
910598SGlenn.Barry@Sun.COM  *
1010598SGlenn.Barry@Sun.COM  * Export of this software from the United States of America may
1110598SGlenn.Barry@Sun.COM  *   require a specific license from the United States Government.
1210598SGlenn.Barry@Sun.COM  *   It is the responsibility of any person or organization contemplating
1310598SGlenn.Barry@Sun.COM  *   export to obtain such a license before exporting.
1410598SGlenn.Barry@Sun.COM  *
1510598SGlenn.Barry@Sun.COM  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
1610598SGlenn.Barry@Sun.COM  * distribute this software and its documentation for any purpose and
1710598SGlenn.Barry@Sun.COM  * without fee is hereby granted, provided that the above copyright
1810598SGlenn.Barry@Sun.COM  * notice appear in all copies and that both that copyright notice and
1910598SGlenn.Barry@Sun.COM  * this permission notice appear in supporting documentation, and that
2010598SGlenn.Barry@Sun.COM  * the name of M.I.T. not be used in advertising or publicity pertaining
2110598SGlenn.Barry@Sun.COM  * to distribution of the software without specific, written prior
2210598SGlenn.Barry@Sun.COM  * permission.  Furthermore if you modify this software you must label
2310598SGlenn.Barry@Sun.COM  * your software as modified software and not distribute it in such a
2410598SGlenn.Barry@Sun.COM  * fashion that it might be confused with the original M.I.T. software.
2510598SGlenn.Barry@Sun.COM  * M.I.T. makes no representations about the suitability of
2610598SGlenn.Barry@Sun.COM  * this software for any purpose.  It is provided "as is" without express
2710598SGlenn.Barry@Sun.COM  * or implied warranty.
2810598SGlenn.Barry@Sun.COM  *
2910598SGlenn.Barry@Sun.COM  */
3010598SGlenn.Barry@Sun.COM 
3110598SGlenn.Barry@Sun.COM #include "k5-int.h"
3210598SGlenn.Barry@Sun.COM #include "k5-utf8.h"
3310598SGlenn.Barry@Sun.COM 
3410598SGlenn.Barry@Sun.COM /* draft-brezak-win2k-krb-authz-00 */
3510598SGlenn.Barry@Sun.COM 
3610598SGlenn.Barry@Sun.COM /*
3710598SGlenn.Barry@Sun.COM  * A PAC consists of a sequence of PAC_INFO_BUFFERs, preceeded by
3810598SGlenn.Barry@Sun.COM  * a PACTYPE header. Decoding the contents of the buffers is left
3910598SGlenn.Barry@Sun.COM  * to the application (notwithstanding signature verification).
4010598SGlenn.Barry@Sun.COM  */
4110598SGlenn.Barry@Sun.COM 
4210598SGlenn.Barry@Sun.COM /*
4310598SGlenn.Barry@Sun.COM  * SUNW17PACresync
4410598SGlenn.Barry@Sun.COM  * These should eventually go to k5-platform.h or equiv.
4510598SGlenn.Barry@Sun.COM  */
4610598SGlenn.Barry@Sun.COM static inline unsigned short
load_16_le(const void * cvp)4710598SGlenn.Barry@Sun.COM load_16_le (const void *cvp)
4810598SGlenn.Barry@Sun.COM {
4910598SGlenn.Barry@Sun.COM     const unsigned char *p = cvp;
5010598SGlenn.Barry@Sun.COM #if defined(__GNUC__) && defined(K5_LE)
5110598SGlenn.Barry@Sun.COM     return GET(16,p);
5210598SGlenn.Barry@Sun.COM #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP16)
5310598SGlenn.Barry@Sun.COM     return GETSWAPPED(16,p);
5410598SGlenn.Barry@Sun.COM #else
5510598SGlenn.Barry@Sun.COM     return (p[0] | (p[1] << 8));
5610598SGlenn.Barry@Sun.COM #endif
5710598SGlenn.Barry@Sun.COM }
5810598SGlenn.Barry@Sun.COM 
5910598SGlenn.Barry@Sun.COM static inline unsigned int
load_32_le(const void * cvp)6010598SGlenn.Barry@Sun.COM load_32_le (const void *cvp)
6110598SGlenn.Barry@Sun.COM {
6210598SGlenn.Barry@Sun.COM     const unsigned char *p = cvp;
6310598SGlenn.Barry@Sun.COM #if defined(__GNUC__) && defined(K5_LE)
6410598SGlenn.Barry@Sun.COM     return GET(32,p);
6510598SGlenn.Barry@Sun.COM #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP32)
6610598SGlenn.Barry@Sun.COM     return GETSWAPPED(32,p);
6710598SGlenn.Barry@Sun.COM #else
6810598SGlenn.Barry@Sun.COM     return (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
6910598SGlenn.Barry@Sun.COM #endif
7010598SGlenn.Barry@Sun.COM }
7110598SGlenn.Barry@Sun.COM static inline UINT64_TYPE
load_64_le(const void * cvp)7210598SGlenn.Barry@Sun.COM load_64_le (const void *cvp)
7310598SGlenn.Barry@Sun.COM {
7410598SGlenn.Barry@Sun.COM     const unsigned char *p = cvp;
7510598SGlenn.Barry@Sun.COM #if defined(__GNUC__) && defined(K5_LE)
7610598SGlenn.Barry@Sun.COM     return GET(64,p);
7710598SGlenn.Barry@Sun.COM #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP64)
7810598SGlenn.Barry@Sun.COM     return GETSWAPPED(64,p);
7910598SGlenn.Barry@Sun.COM #else
8010598SGlenn.Barry@Sun.COM     return ((UINT64_TYPE)load_32_le(p+4) << 32) | load_32_le(p);
8110598SGlenn.Barry@Sun.COM #endif
8210598SGlenn.Barry@Sun.COM }
8310598SGlenn.Barry@Sun.COM 
8410598SGlenn.Barry@Sun.COM static inline void
store_16_le(unsigned int val,void * vp)8510598SGlenn.Barry@Sun.COM store_16_le (unsigned int val, void *vp)
8610598SGlenn.Barry@Sun.COM {
8710598SGlenn.Barry@Sun.COM     unsigned char *p = vp;
8810598SGlenn.Barry@Sun.COM #if defined(__GNUC__) && defined(K5_LE)
8910598SGlenn.Barry@Sun.COM     PUT(16,p,val);
9010598SGlenn.Barry@Sun.COM #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP16)
9110598SGlenn.Barry@Sun.COM     PUTSWAPPED(16,p,val);
9210598SGlenn.Barry@Sun.COM #else
9310598SGlenn.Barry@Sun.COM     p[1] = (val >>  8) & 0xff;
9410598SGlenn.Barry@Sun.COM     p[0] = (val      ) & 0xff;
9510598SGlenn.Barry@Sun.COM #endif
9610598SGlenn.Barry@Sun.COM }
9710598SGlenn.Barry@Sun.COM 
9810598SGlenn.Barry@Sun.COM static inline void
store_32_le(unsigned int val,void * vp)9910598SGlenn.Barry@Sun.COM store_32_le (unsigned int val, void *vp)
10010598SGlenn.Barry@Sun.COM {
10110598SGlenn.Barry@Sun.COM     unsigned char *p = vp;
10210598SGlenn.Barry@Sun.COM #if defined(__GNUC__) && defined(K5_LE)
10310598SGlenn.Barry@Sun.COM     PUT(32,p,val);
10410598SGlenn.Barry@Sun.COM #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP32)
10510598SGlenn.Barry@Sun.COM     PUTSWAPPED(32,p,val);
10610598SGlenn.Barry@Sun.COM #else
10710598SGlenn.Barry@Sun.COM     p[3] = (val >> 24) & 0xff;
10810598SGlenn.Barry@Sun.COM     p[2] = (val >> 16) & 0xff;
10910598SGlenn.Barry@Sun.COM     p[1] = (val >>  8) & 0xff;
11010598SGlenn.Barry@Sun.COM     p[0] = (val      ) & 0xff;
11110598SGlenn.Barry@Sun.COM #endif
11210598SGlenn.Barry@Sun.COM }
11310598SGlenn.Barry@Sun.COM static inline void
store_64_le(UINT64_TYPE val,void * vp)11410598SGlenn.Barry@Sun.COM store_64_le (UINT64_TYPE val, void *vp)
11510598SGlenn.Barry@Sun.COM {
11610598SGlenn.Barry@Sun.COM     unsigned char *p = vp;
11710598SGlenn.Barry@Sun.COM #if defined(__GNUC__) && defined(K5_LE)
11810598SGlenn.Barry@Sun.COM     PUT(64,p,val);
11910598SGlenn.Barry@Sun.COM #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP64)
12010598SGlenn.Barry@Sun.COM     PUTSWAPPED(64,p,val);
12110598SGlenn.Barry@Sun.COM #else
12210598SGlenn.Barry@Sun.COM     p[7] = (unsigned char)((val >> 56) & 0xff);
12310598SGlenn.Barry@Sun.COM     p[6] = (unsigned char)((val >> 48) & 0xff);
12410598SGlenn.Barry@Sun.COM     p[5] = (unsigned char)((val >> 40) & 0xff);
12510598SGlenn.Barry@Sun.COM     p[4] = (unsigned char)((val >> 32) & 0xff);
12610598SGlenn.Barry@Sun.COM     p[3] = (unsigned char)((val >> 24) & 0xff);
12710598SGlenn.Barry@Sun.COM     p[2] = (unsigned char)((val >> 16) & 0xff);
12810598SGlenn.Barry@Sun.COM     p[1] = (unsigned char)((val >>  8) & 0xff);
12910598SGlenn.Barry@Sun.COM     p[0] = (unsigned char)((val      ) & 0xff);
13010598SGlenn.Barry@Sun.COM #endif
13110598SGlenn.Barry@Sun.COM }
13210598SGlenn.Barry@Sun.COM 
13310598SGlenn.Barry@Sun.COM 
13410598SGlenn.Barry@Sun.COM typedef struct _PAC_INFO_BUFFER {
13510598SGlenn.Barry@Sun.COM     krb5_ui_4 ulType;
13610598SGlenn.Barry@Sun.COM     krb5_ui_4 cbBufferSize;
13710598SGlenn.Barry@Sun.COM     krb5_ui_8 Offset;
13810598SGlenn.Barry@Sun.COM } PAC_INFO_BUFFER;
13910598SGlenn.Barry@Sun.COM 
14010598SGlenn.Barry@Sun.COM #define PAC_INFO_BUFFER_LENGTH	16
14110598SGlenn.Barry@Sun.COM 
14210598SGlenn.Barry@Sun.COM /* ulType */
14310598SGlenn.Barry@Sun.COM #define PAC_LOGON_INFO		1
14410598SGlenn.Barry@Sun.COM #define PAC_SERVER_CHECKSUM	6
14510598SGlenn.Barry@Sun.COM #define PAC_PRIVSVR_CHECKSUM	7
14610598SGlenn.Barry@Sun.COM #define PAC_CLIENT_INFO		10
14710598SGlenn.Barry@Sun.COM 
14810598SGlenn.Barry@Sun.COM typedef struct _PACTYPE {
14910598SGlenn.Barry@Sun.COM     krb5_ui_4 cBuffers;
15010598SGlenn.Barry@Sun.COM     krb5_ui_4 Version;
15110598SGlenn.Barry@Sun.COM     PAC_INFO_BUFFER Buffers[1];
15210598SGlenn.Barry@Sun.COM } PACTYPE;
15310598SGlenn.Barry@Sun.COM 
15410598SGlenn.Barry@Sun.COM #define PAC_ALIGNMENT		    8
15510598SGlenn.Barry@Sun.COM #define PACTYPE_LENGTH		    8U
15610598SGlenn.Barry@Sun.COM #define PAC_SIGNATURE_DATA_LENGTH   4U
15710598SGlenn.Barry@Sun.COM #define PAC_CLIENT_INFO_LENGTH	    10U
15810598SGlenn.Barry@Sun.COM 
15910598SGlenn.Barry@Sun.COM #define NT_TIME_EPOCH		    11644473600LL
16010598SGlenn.Barry@Sun.COM 
16110598SGlenn.Barry@Sun.COM struct krb5_pac_data {
16210598SGlenn.Barry@Sun.COM     PACTYPE *pac;	/* PAC header + info buffer array */
16310598SGlenn.Barry@Sun.COM     krb5_data data;	/* PAC data (including uninitialised header) */
16410598SGlenn.Barry@Sun.COM };
16510598SGlenn.Barry@Sun.COM 
16610598SGlenn.Barry@Sun.COM static krb5_error_code
16710598SGlenn.Barry@Sun.COM k5_pac_locate_buffer(krb5_context context,
16810598SGlenn.Barry@Sun.COM 		     const krb5_pac pac,
16910598SGlenn.Barry@Sun.COM 		     krb5_ui_4 type,
17010598SGlenn.Barry@Sun.COM 		     krb5_data *data);
17110598SGlenn.Barry@Sun.COM 
17210598SGlenn.Barry@Sun.COM /*
17310598SGlenn.Barry@Sun.COM  * Add a buffer to the provided PAC and update header.
17410598SGlenn.Barry@Sun.COM  */
17510598SGlenn.Barry@Sun.COM 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)17610598SGlenn.Barry@Sun.COM k5_pac_add_buffer(krb5_context context,
17710598SGlenn.Barry@Sun.COM 		  krb5_pac pac,
17810598SGlenn.Barry@Sun.COM 		  krb5_ui_4 type,
17910598SGlenn.Barry@Sun.COM 		  const krb5_data *data,
18010598SGlenn.Barry@Sun.COM 		  krb5_boolean zerofill,
18110598SGlenn.Barry@Sun.COM 		  krb5_data *out_data)
18210598SGlenn.Barry@Sun.COM {
18310598SGlenn.Barry@Sun.COM     PACTYPE *header;
18410598SGlenn.Barry@Sun.COM     size_t header_len, i, pad = 0;
18510598SGlenn.Barry@Sun.COM     char *pac_data;
18610598SGlenn.Barry@Sun.COM 
18710598SGlenn.Barry@Sun.COM     assert((data->data == NULL) == zerofill);
18810598SGlenn.Barry@Sun.COM 
18910598SGlenn.Barry@Sun.COM     /* Check there isn't already a buffer of this type */
19010598SGlenn.Barry@Sun.COM     if (k5_pac_locate_buffer(context, pac, type, NULL) == 0) {
191*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
192*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, EINVAL,
193*13132SGlenn.Barry@oracle.com 			    "Duplicate PAC buffer of type %d",
194*13132SGlenn.Barry@oracle.com 			    type);
19510598SGlenn.Barry@Sun.COM 	return EINVAL;
19610598SGlenn.Barry@Sun.COM     }
19710598SGlenn.Barry@Sun.COM 
19810598SGlenn.Barry@Sun.COM     header = (PACTYPE *)realloc(pac->pac,
19910598SGlenn.Barry@Sun.COM 				sizeof(PACTYPE) +
20010598SGlenn.Barry@Sun.COM 				(pac->pac->cBuffers * sizeof(PAC_INFO_BUFFER)));
20110598SGlenn.Barry@Sun.COM     if (header == NULL) {
20210598SGlenn.Barry@Sun.COM 	return ENOMEM;
20310598SGlenn.Barry@Sun.COM     }
20410598SGlenn.Barry@Sun.COM     pac->pac = header;
20510598SGlenn.Barry@Sun.COM 
20610598SGlenn.Barry@Sun.COM     header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
20710598SGlenn.Barry@Sun.COM 
20810598SGlenn.Barry@Sun.COM     if (data->length % PAC_ALIGNMENT)
20910598SGlenn.Barry@Sun.COM 	pad = PAC_ALIGNMENT - (data->length % PAC_ALIGNMENT);
21010598SGlenn.Barry@Sun.COM 
21110598SGlenn.Barry@Sun.COM     pac_data = realloc(pac->data.data,
21210598SGlenn.Barry@Sun.COM 		       pac->data.length + PAC_INFO_BUFFER_LENGTH + data->length + pad);
21310598SGlenn.Barry@Sun.COM     if (pac_data == NULL) {
21410598SGlenn.Barry@Sun.COM 	return ENOMEM;
21510598SGlenn.Barry@Sun.COM     }
21610598SGlenn.Barry@Sun.COM     pac->data.data = pac_data;
21710598SGlenn.Barry@Sun.COM 
21810598SGlenn.Barry@Sun.COM     /* Update offsets of existing buffers */
21910598SGlenn.Barry@Sun.COM     for (i = 0; i < pac->pac->cBuffers; i++)
22010598SGlenn.Barry@Sun.COM 	pac->pac->Buffers[i].Offset += PAC_INFO_BUFFER_LENGTH;
22110598SGlenn.Barry@Sun.COM 
22210598SGlenn.Barry@Sun.COM     /* Make room for new PAC_INFO_BUFFER */
22310598SGlenn.Barry@Sun.COM     memmove(pac->data.data + header_len + PAC_INFO_BUFFER_LENGTH,
22410598SGlenn.Barry@Sun.COM 	    pac->data.data + header_len,
22510598SGlenn.Barry@Sun.COM 	    pac->data.length - header_len);
22610598SGlenn.Barry@Sun.COM     memset(pac->data.data + header_len, 0, PAC_INFO_BUFFER_LENGTH);
22710598SGlenn.Barry@Sun.COM 
22810598SGlenn.Barry@Sun.COM     /* Initialise new PAC_INFO_BUFFER */
22910598SGlenn.Barry@Sun.COM     pac->pac->Buffers[i].ulType = type;
23010598SGlenn.Barry@Sun.COM     pac->pac->Buffers[i].cbBufferSize = data->length;
23110598SGlenn.Barry@Sun.COM     pac->pac->Buffers[i].Offset = pac->data.length + PAC_INFO_BUFFER_LENGTH;
23210598SGlenn.Barry@Sun.COM     assert((pac->pac->Buffers[i].Offset % PAC_ALIGNMENT) == 0);
23310598SGlenn.Barry@Sun.COM 
23410598SGlenn.Barry@Sun.COM     /* Copy in new PAC data and zero padding bytes */
23510598SGlenn.Barry@Sun.COM     if (zerofill)
23610598SGlenn.Barry@Sun.COM 	memset(pac->data.data + pac->pac->Buffers[i].Offset, 0, data->length);
23710598SGlenn.Barry@Sun.COM     else
23810598SGlenn.Barry@Sun.COM 	memcpy(pac->data.data + pac->pac->Buffers[i].Offset, data->data, data->length);
23910598SGlenn.Barry@Sun.COM 
24010598SGlenn.Barry@Sun.COM     memset(pac->data.data + pac->pac->Buffers[i].Offset + data->length, 0, pad);
24110598SGlenn.Barry@Sun.COM 
24210598SGlenn.Barry@Sun.COM     pac->pac->cBuffers++;
24310598SGlenn.Barry@Sun.COM     pac->data.length += PAC_INFO_BUFFER_LENGTH + data->length + pad;
24410598SGlenn.Barry@Sun.COM 
24510598SGlenn.Barry@Sun.COM     if (out_data != NULL) {
24610598SGlenn.Barry@Sun.COM 	out_data->data = pac->data.data + pac->pac->Buffers[i].Offset;
24710598SGlenn.Barry@Sun.COM 	out_data->length = data->length;
24810598SGlenn.Barry@Sun.COM     }
24910598SGlenn.Barry@Sun.COM 
25010598SGlenn.Barry@Sun.COM     return 0;
25110598SGlenn.Barry@Sun.COM }
25210598SGlenn.Barry@Sun.COM 
25310598SGlenn.Barry@Sun.COM krb5_error_code KRB5_CALLCONV
krb5_pac_add_buffer(krb5_context context,krb5_pac pac,krb5_ui_4 type,const krb5_data * data)25410598SGlenn.Barry@Sun.COM krb5_pac_add_buffer(krb5_context context,
25510598SGlenn.Barry@Sun.COM 		    krb5_pac pac,
25610598SGlenn.Barry@Sun.COM 		    krb5_ui_4 type,
25710598SGlenn.Barry@Sun.COM 		    const krb5_data *data)
25810598SGlenn.Barry@Sun.COM {
25910598SGlenn.Barry@Sun.COM     return k5_pac_add_buffer(context, pac, type, data, FALSE, NULL);
26010598SGlenn.Barry@Sun.COM }
26110598SGlenn.Barry@Sun.COM 
26210598SGlenn.Barry@Sun.COM /*
26310598SGlenn.Barry@Sun.COM  * Free a PAC
26410598SGlenn.Barry@Sun.COM  */
26510598SGlenn.Barry@Sun.COM void KRB5_CALLCONV
krb5_pac_free(krb5_context context,krb5_pac pac)26610598SGlenn.Barry@Sun.COM krb5_pac_free(krb5_context context,
26710598SGlenn.Barry@Sun.COM 	      krb5_pac pac)
26810598SGlenn.Barry@Sun.COM {
26910598SGlenn.Barry@Sun.COM     if (pac != NULL) {
27010598SGlenn.Barry@Sun.COM 	if (pac->data.data != NULL) {
27110598SGlenn.Barry@Sun.COM 	    memset(pac->data.data, 0, pac->data.length);
27210598SGlenn.Barry@Sun.COM 	    free(pac->data.data);
27310598SGlenn.Barry@Sun.COM 	}
27410598SGlenn.Barry@Sun.COM 	if (pac->pac != NULL)
27510598SGlenn.Barry@Sun.COM 	    free(pac->pac);
27610598SGlenn.Barry@Sun.COM 	memset(pac, 0, sizeof(*pac));
27710598SGlenn.Barry@Sun.COM 	free(pac);
27810598SGlenn.Barry@Sun.COM     }
27910598SGlenn.Barry@Sun.COM }
28010598SGlenn.Barry@Sun.COM 
28110598SGlenn.Barry@Sun.COM static krb5_error_code
k5_pac_locate_buffer(krb5_context context,const krb5_pac pac,krb5_ui_4 type,krb5_data * data)28210598SGlenn.Barry@Sun.COM k5_pac_locate_buffer(krb5_context context,
28310598SGlenn.Barry@Sun.COM 		     const krb5_pac pac,
28410598SGlenn.Barry@Sun.COM 		     krb5_ui_4 type,
28510598SGlenn.Barry@Sun.COM 		     krb5_data *data)
28610598SGlenn.Barry@Sun.COM {
28710598SGlenn.Barry@Sun.COM     PAC_INFO_BUFFER *buffer = NULL;
28810598SGlenn.Barry@Sun.COM     size_t i;
28910598SGlenn.Barry@Sun.COM 
290*13132SGlenn.Barry@oracle.com     if (pac == NULL) {
291*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
292*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, EINVAL,
293*13132SGlenn.Barry@oracle.com 			    "Invalid argument 'pac' is NULL");
29410598SGlenn.Barry@Sun.COM 	return EINVAL;
295*13132SGlenn.Barry@oracle.com     }
29610598SGlenn.Barry@Sun.COM 
29710598SGlenn.Barry@Sun.COM     for (i = 0; i < pac->pac->cBuffers; i++) {
29810598SGlenn.Barry@Sun.COM 	if (pac->pac->Buffers[i].ulType == type) {
29910598SGlenn.Barry@Sun.COM 	    if (buffer == NULL)
30010598SGlenn.Barry@Sun.COM 		buffer = &pac->pac->Buffers[i];
301*13132SGlenn.Barry@oracle.com 	    else {
302*13132SGlenn.Barry@oracle.com 	        /* Solaris Kerberos */
303*13132SGlenn.Barry@oracle.com 	        krb5_set_error_message(context, EINVAL,
304*13132SGlenn.Barry@oracle.com 				    "Invalid buffer found looping thru PAC buffers (type=%d, i=%d)",
305*13132SGlenn.Barry@oracle.com 				    type, i);
30610598SGlenn.Barry@Sun.COM 		return EINVAL;
307*13132SGlenn.Barry@oracle.com 	    }
30810598SGlenn.Barry@Sun.COM 	}
30910598SGlenn.Barry@Sun.COM     }
31010598SGlenn.Barry@Sun.COM 
311*13132SGlenn.Barry@oracle.com     if (buffer == NULL) {
312*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
313*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, ENOENT,
314*13132SGlenn.Barry@oracle.com 			    "No PAC buffer found (type=%d)",
315*13132SGlenn.Barry@oracle.com 			    type);
316*13132SGlenn.Barry@oracle.com 
31710598SGlenn.Barry@Sun.COM 	return ENOENT;
318*13132SGlenn.Barry@oracle.com     }
31910598SGlenn.Barry@Sun.COM 
32010598SGlenn.Barry@Sun.COM     assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
32110598SGlenn.Barry@Sun.COM 
32210598SGlenn.Barry@Sun.COM     if (data != NULL) {
32310598SGlenn.Barry@Sun.COM 	data->length = buffer->cbBufferSize;
32410598SGlenn.Barry@Sun.COM 	data->data = pac->data.data + buffer->Offset;
32510598SGlenn.Barry@Sun.COM     }
32610598SGlenn.Barry@Sun.COM 
32710598SGlenn.Barry@Sun.COM     return 0;
32810598SGlenn.Barry@Sun.COM }
32910598SGlenn.Barry@Sun.COM 
33010598SGlenn.Barry@Sun.COM /*
33110598SGlenn.Barry@Sun.COM  * Find a buffer and copy data into output
33210598SGlenn.Barry@Sun.COM  */
33310598SGlenn.Barry@Sun.COM krb5_error_code KRB5_CALLCONV
krb5_pac_get_buffer(krb5_context context,krb5_pac pac,krb5_ui_4 type,krb5_data * data)33410598SGlenn.Barry@Sun.COM krb5_pac_get_buffer(krb5_context context,
33510598SGlenn.Barry@Sun.COM 		    krb5_pac pac,
33610598SGlenn.Barry@Sun.COM 		    krb5_ui_4 type,
33710598SGlenn.Barry@Sun.COM 		    krb5_data *data)
33810598SGlenn.Barry@Sun.COM {
33910598SGlenn.Barry@Sun.COM     krb5_data d;
34010598SGlenn.Barry@Sun.COM     krb5_error_code ret;
34110598SGlenn.Barry@Sun.COM 
34210598SGlenn.Barry@Sun.COM     ret = k5_pac_locate_buffer(context, pac, type, &d);
34310598SGlenn.Barry@Sun.COM     if (ret != 0)
34410598SGlenn.Barry@Sun.COM 	return ret;
34510598SGlenn.Barry@Sun.COM 
34610598SGlenn.Barry@Sun.COM     data->data = malloc(d.length);
34710598SGlenn.Barry@Sun.COM     if (data->data == NULL)
34810598SGlenn.Barry@Sun.COM 	return ENOMEM;
34910598SGlenn.Barry@Sun.COM 
35010598SGlenn.Barry@Sun.COM     data->length = d.length;
35110598SGlenn.Barry@Sun.COM     memcpy(data->data, d.data, d.length);
35210598SGlenn.Barry@Sun.COM 
35310598SGlenn.Barry@Sun.COM     return 0;
35410598SGlenn.Barry@Sun.COM }
35510598SGlenn.Barry@Sun.COM 
35610598SGlenn.Barry@Sun.COM /*
35710598SGlenn.Barry@Sun.COM  * Return an array of the types of data in the PAC
35810598SGlenn.Barry@Sun.COM  */
35910598SGlenn.Barry@Sun.COM krb5_error_code KRB5_CALLCONV
krb5_pac_get_types(krb5_context context,krb5_pac pac,size_t * len,krb5_ui_4 ** types)36010598SGlenn.Barry@Sun.COM krb5_pac_get_types(krb5_context context,
36110598SGlenn.Barry@Sun.COM 		   krb5_pac pac,
36210598SGlenn.Barry@Sun.COM 		   size_t *len,
36310598SGlenn.Barry@Sun.COM 		   krb5_ui_4 **types)
36410598SGlenn.Barry@Sun.COM {
36510598SGlenn.Barry@Sun.COM     size_t i;
36610598SGlenn.Barry@Sun.COM 
36710598SGlenn.Barry@Sun.COM     *types = (krb5_ui_4 *)malloc(pac->pac->cBuffers * sizeof(krb5_ui_4));
36810598SGlenn.Barry@Sun.COM     if (*types == NULL)
36910598SGlenn.Barry@Sun.COM 	return ENOMEM;
37010598SGlenn.Barry@Sun.COM 
37110598SGlenn.Barry@Sun.COM     *len = pac->pac->cBuffers;
37210598SGlenn.Barry@Sun.COM 
37310598SGlenn.Barry@Sun.COM     for (i = 0; i < pac->pac->cBuffers; i++)
37410598SGlenn.Barry@Sun.COM 	(*types)[i] = pac->pac->Buffers[i].ulType;
37510598SGlenn.Barry@Sun.COM 
37610598SGlenn.Barry@Sun.COM     return 0;
37710598SGlenn.Barry@Sun.COM }
37810598SGlenn.Barry@Sun.COM 
37910598SGlenn.Barry@Sun.COM /*
38010598SGlenn.Barry@Sun.COM  * Initialize PAC
38110598SGlenn.Barry@Sun.COM  */
38210598SGlenn.Barry@Sun.COM krb5_error_code KRB5_CALLCONV
krb5_pac_init(krb5_context context,krb5_pac * ppac)38310598SGlenn.Barry@Sun.COM krb5_pac_init(krb5_context context,
38410598SGlenn.Barry@Sun.COM 	      krb5_pac *ppac)
38510598SGlenn.Barry@Sun.COM {
38610598SGlenn.Barry@Sun.COM     krb5_pac pac;
38710598SGlenn.Barry@Sun.COM 
38810598SGlenn.Barry@Sun.COM     pac = (krb5_pac)malloc(sizeof(*pac));
38910598SGlenn.Barry@Sun.COM     if (pac == NULL)
39010598SGlenn.Barry@Sun.COM 	return ENOMEM;
39110598SGlenn.Barry@Sun.COM 
39210598SGlenn.Barry@Sun.COM     pac->pac = (PACTYPE *)malloc(sizeof(PACTYPE));
39310598SGlenn.Barry@Sun.COM     if (pac->pac == NULL) {
39410598SGlenn.Barry@Sun.COM 	free( pac);
39510598SGlenn.Barry@Sun.COM 	return ENOMEM;
39610598SGlenn.Barry@Sun.COM     }
39710598SGlenn.Barry@Sun.COM 
39810598SGlenn.Barry@Sun.COM     pac->pac->cBuffers = 0;
39910598SGlenn.Barry@Sun.COM     pac->pac->Version = 0;
40010598SGlenn.Barry@Sun.COM 
40110598SGlenn.Barry@Sun.COM     pac->data.length = PACTYPE_LENGTH;
40210598SGlenn.Barry@Sun.COM     pac->data.data = calloc(1, pac->data.length);
40310598SGlenn.Barry@Sun.COM     if (pac->data.data == NULL) {
40410598SGlenn.Barry@Sun.COM 	krb5_pac_free(context, pac);
40510598SGlenn.Barry@Sun.COM 	return ENOMEM;
40610598SGlenn.Barry@Sun.COM     }
40710598SGlenn.Barry@Sun.COM 
40810598SGlenn.Barry@Sun.COM     *ppac = pac;
40910598SGlenn.Barry@Sun.COM 
41010598SGlenn.Barry@Sun.COM     return 0;
41110598SGlenn.Barry@Sun.COM }
41210598SGlenn.Barry@Sun.COM 
41310598SGlenn.Barry@Sun.COM /*
41410598SGlenn.Barry@Sun.COM  * Parse the supplied data into the PAC allocated by this function
41510598SGlenn.Barry@Sun.COM  */
41610598SGlenn.Barry@Sun.COM krb5_error_code KRB5_CALLCONV
krb5_pac_parse(krb5_context context,const void * ptr,size_t len,krb5_pac * ppac)41710598SGlenn.Barry@Sun.COM krb5_pac_parse(krb5_context context,
41810598SGlenn.Barry@Sun.COM 	       const void *ptr,
41910598SGlenn.Barry@Sun.COM 	       size_t len,
42010598SGlenn.Barry@Sun.COM 	       krb5_pac *ppac)
42110598SGlenn.Barry@Sun.COM {
42210598SGlenn.Barry@Sun.COM     krb5_error_code ret;
42310598SGlenn.Barry@Sun.COM     size_t i;
42410598SGlenn.Barry@Sun.COM     const unsigned char *p = (const unsigned char *)ptr;
42510598SGlenn.Barry@Sun.COM     krb5_pac pac;
42610598SGlenn.Barry@Sun.COM     size_t header_len;
42710598SGlenn.Barry@Sun.COM     krb5_ui_4 cbuffers, version;
42810598SGlenn.Barry@Sun.COM 
42910598SGlenn.Barry@Sun.COM     *ppac = NULL;
43010598SGlenn.Barry@Sun.COM 
431*13132SGlenn.Barry@oracle.com     if (len < PACTYPE_LENGTH) {
432*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
433*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, ERANGE,
434*13132SGlenn.Barry@oracle.com 			    "PAC type length is out of range (len=%d)",
435*13132SGlenn.Barry@oracle.com 			    len);
43610598SGlenn.Barry@Sun.COM 	return ERANGE;
437*13132SGlenn.Barry@oracle.com     }
43810598SGlenn.Barry@Sun.COM 
43910598SGlenn.Barry@Sun.COM     cbuffers = load_32_le(p);
44010598SGlenn.Barry@Sun.COM     p += 4;
44110598SGlenn.Barry@Sun.COM     version = load_32_le(p);
44210598SGlenn.Barry@Sun.COM     p += 4;
44310598SGlenn.Barry@Sun.COM 
444*13132SGlenn.Barry@oracle.com     if (version != 0) {
445*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
446*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, EINVAL,
447*13132SGlenn.Barry@oracle.com 			    "Invalid PAC version is %d, should be 0",
448*13132SGlenn.Barry@oracle.com 			    version);
44910598SGlenn.Barry@Sun.COM 	return EINVAL;
450*13132SGlenn.Barry@oracle.com     }
45110598SGlenn.Barry@Sun.COM 
45210598SGlenn.Barry@Sun.COM     header_len = PACTYPE_LENGTH + (cbuffers * PAC_INFO_BUFFER_LENGTH);
453*13132SGlenn.Barry@oracle.com     if (len < header_len) {
454*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
455*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, ERANGE,
456*13132SGlenn.Barry@oracle.com 			    "PAC header len (%d) out of range",
457*13132SGlenn.Barry@oracle.com 			    len);
45810598SGlenn.Barry@Sun.COM 	return ERANGE;
459*13132SGlenn.Barry@oracle.com     }
46010598SGlenn.Barry@Sun.COM 
46110598SGlenn.Barry@Sun.COM     ret = krb5_pac_init(context, &pac);
46210598SGlenn.Barry@Sun.COM     if (ret != 0)
46310598SGlenn.Barry@Sun.COM 	return ret;
46410598SGlenn.Barry@Sun.COM 
46510598SGlenn.Barry@Sun.COM     pac->pac = (PACTYPE *)realloc(pac->pac,
46610598SGlenn.Barry@Sun.COM 	sizeof(PACTYPE) + ((cbuffers - 1) * sizeof(PAC_INFO_BUFFER)));
46710598SGlenn.Barry@Sun.COM     if (pac->pac == NULL) {
46810598SGlenn.Barry@Sun.COM 	krb5_pac_free(context, pac);
46910598SGlenn.Barry@Sun.COM 	return ENOMEM;
47010598SGlenn.Barry@Sun.COM     }
47110598SGlenn.Barry@Sun.COM 
47210598SGlenn.Barry@Sun.COM     pac->pac->cBuffers = cbuffers;
47310598SGlenn.Barry@Sun.COM     pac->pac->Version = version;
47410598SGlenn.Barry@Sun.COM 
47510598SGlenn.Barry@Sun.COM     for (i = 0; i < pac->pac->cBuffers; i++) {
47610598SGlenn.Barry@Sun.COM 	PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
47710598SGlenn.Barry@Sun.COM 
47810598SGlenn.Barry@Sun.COM 	buffer->ulType = load_32_le(p);
47910598SGlenn.Barry@Sun.COM 	p += 4;
48010598SGlenn.Barry@Sun.COM 	buffer->cbBufferSize = load_32_le(p);
48110598SGlenn.Barry@Sun.COM 	p += 4;
48210598SGlenn.Barry@Sun.COM 	buffer->Offset = load_64_le(p);
48310598SGlenn.Barry@Sun.COM 	p += 8;
48410598SGlenn.Barry@Sun.COM 
48510598SGlenn.Barry@Sun.COM 	if (buffer->Offset % PAC_ALIGNMENT) {
48610598SGlenn.Barry@Sun.COM 	    krb5_pac_free(context, pac);
487*13132SGlenn.Barry@oracle.com 	    /* Solaris Kerberos */
488*13132SGlenn.Barry@oracle.com 	    krb5_set_error_message(context, EINVAL,
489*13132SGlenn.Barry@oracle.com 				"PAC buffer offset mis-aligned");
49010598SGlenn.Barry@Sun.COM 	    return EINVAL;
49110598SGlenn.Barry@Sun.COM 	}
49210598SGlenn.Barry@Sun.COM 	if (buffer->Offset < header_len ||
49310598SGlenn.Barry@Sun.COM 	    buffer->Offset + buffer->cbBufferSize > len) {
49410598SGlenn.Barry@Sun.COM 	    krb5_pac_free(context, pac);
495*13132SGlenn.Barry@oracle.com 	    /* Solaris Kerberos */
496*13132SGlenn.Barry@oracle.com 	    krb5_set_error_message(context, ERANGE,
497*13132SGlenn.Barry@oracle.com 				"PAC offset is out of range");
49810598SGlenn.Barry@Sun.COM 	    return ERANGE;
49910598SGlenn.Barry@Sun.COM 	}
50010598SGlenn.Barry@Sun.COM     }
50110598SGlenn.Barry@Sun.COM 
50210598SGlenn.Barry@Sun.COM     pac->data.data = realloc(pac->data.data, len);
50310598SGlenn.Barry@Sun.COM     if (pac->data.data == NULL) {
50410598SGlenn.Barry@Sun.COM 	krb5_pac_free(context, pac);
50510598SGlenn.Barry@Sun.COM 	return ENOMEM;
50610598SGlenn.Barry@Sun.COM     }
50710598SGlenn.Barry@Sun.COM     memcpy(pac->data.data, ptr, len);
50810598SGlenn.Barry@Sun.COM 
50910598SGlenn.Barry@Sun.COM     pac->data.length = len;
51010598SGlenn.Barry@Sun.COM 
51110598SGlenn.Barry@Sun.COM     *ppac = pac;
51210598SGlenn.Barry@Sun.COM 
51310598SGlenn.Barry@Sun.COM     return 0;
51410598SGlenn.Barry@Sun.COM }
51510598SGlenn.Barry@Sun.COM 
51610598SGlenn.Barry@Sun.COM static krb5_error_code
k5_time_to_seconds_since_1970(krb5_context context,krb5_int64 ntTime,krb5_timestamp * elapsedSeconds)517*13132SGlenn.Barry@oracle.com k5_time_to_seconds_since_1970(krb5_context context, krb5_int64 ntTime, krb5_timestamp *elapsedSeconds)
51810598SGlenn.Barry@Sun.COM {
51910598SGlenn.Barry@Sun.COM     krb5_ui_8 abstime;
52010598SGlenn.Barry@Sun.COM 
52110598SGlenn.Barry@Sun.COM     ntTime /= 10000000;
52210598SGlenn.Barry@Sun.COM 
52310598SGlenn.Barry@Sun.COM     abstime = ntTime > 0 ? ntTime - NT_TIME_EPOCH : -ntTime;
52410598SGlenn.Barry@Sun.COM 
525*13132SGlenn.Barry@oracle.com     if (abstime > KRB5_INT32_MAX) {
52610598SGlenn.Barry@Sun.COM 	return ERANGE;
527*13132SGlenn.Barry@oracle.com     }
52810598SGlenn.Barry@Sun.COM 
52910598SGlenn.Barry@Sun.COM     *elapsedSeconds = abstime;
53010598SGlenn.Barry@Sun.COM 
53110598SGlenn.Barry@Sun.COM     return 0;
53210598SGlenn.Barry@Sun.COM }
53310598SGlenn.Barry@Sun.COM 
53410598SGlenn.Barry@Sun.COM static krb5_error_code
k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds,krb5_ui_8 * ntTime)53510598SGlenn.Barry@Sun.COM k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, krb5_ui_8 *ntTime)
53610598SGlenn.Barry@Sun.COM {
53710598SGlenn.Barry@Sun.COM     *ntTime = elapsedSeconds;
53810598SGlenn.Barry@Sun.COM 
53910598SGlenn.Barry@Sun.COM     if (elapsedSeconds > 0)
54010598SGlenn.Barry@Sun.COM 	*ntTime += NT_TIME_EPOCH;
54110598SGlenn.Barry@Sun.COM 
54210598SGlenn.Barry@Sun.COM     *ntTime *= 10000000;
54310598SGlenn.Barry@Sun.COM 
54410598SGlenn.Barry@Sun.COM     return 0;
54510598SGlenn.Barry@Sun.COM }
54610598SGlenn.Barry@Sun.COM 
54710598SGlenn.Barry@Sun.COM static krb5_error_code
k5_pac_validate_client(krb5_context context,const krb5_pac pac,krb5_timestamp authtime,krb5_const_principal principal)54810598SGlenn.Barry@Sun.COM k5_pac_validate_client(krb5_context context,
54910598SGlenn.Barry@Sun.COM 		       const krb5_pac pac,
55010598SGlenn.Barry@Sun.COM 		       krb5_timestamp authtime,
55110598SGlenn.Barry@Sun.COM 		       krb5_const_principal principal)
55210598SGlenn.Barry@Sun.COM {
55310598SGlenn.Barry@Sun.COM     krb5_error_code ret;
55410598SGlenn.Barry@Sun.COM     krb5_data client_info;
55510598SGlenn.Barry@Sun.COM     char *pac_princname;
55610598SGlenn.Barry@Sun.COM     unsigned char *p;
55710598SGlenn.Barry@Sun.COM     krb5_timestamp pac_authtime;
55810598SGlenn.Barry@Sun.COM     krb5_ui_2 pac_princname_length;
55910598SGlenn.Barry@Sun.COM     krb5_int64 pac_nt_authtime;
56010598SGlenn.Barry@Sun.COM     krb5_principal pac_principal;
56110598SGlenn.Barry@Sun.COM 
56210598SGlenn.Barry@Sun.COM     ret = k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info);
56310598SGlenn.Barry@Sun.COM     if (ret != 0)
56410598SGlenn.Barry@Sun.COM 	return ret;
56510598SGlenn.Barry@Sun.COM 
566*13132SGlenn.Barry@oracle.com     if (client_info.length < PAC_CLIENT_INFO_LENGTH) {
567*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
568*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, ERANGE,
569*13132SGlenn.Barry@oracle.com 			    "PAC client info length out of range",
570*13132SGlenn.Barry@oracle.com 			    client_info.length);
57110598SGlenn.Barry@Sun.COM 	return ERANGE;
572*13132SGlenn.Barry@oracle.com     }
57310598SGlenn.Barry@Sun.COM 
57410598SGlenn.Barry@Sun.COM     p = (unsigned char *)client_info.data;
57510598SGlenn.Barry@Sun.COM     pac_nt_authtime = load_64_le(p);
57610598SGlenn.Barry@Sun.COM     p += 8;
57710598SGlenn.Barry@Sun.COM     pac_princname_length = load_16_le(p);
57810598SGlenn.Barry@Sun.COM     p += 2;
57910598SGlenn.Barry@Sun.COM 
580*13132SGlenn.Barry@oracle.com     ret = k5_time_to_seconds_since_1970(context, pac_nt_authtime, &pac_authtime);
58110598SGlenn.Barry@Sun.COM     if (ret != 0)
58210598SGlenn.Barry@Sun.COM 	return ret;
58310598SGlenn.Barry@Sun.COM 
58410598SGlenn.Barry@Sun.COM     if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
585*13132SGlenn.Barry@oracle.com         pac_princname_length % 2) {
586*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
587*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, ERANGE,
588*13132SGlenn.Barry@oracle.com 			    "PAC client info length is out of range");
58910598SGlenn.Barry@Sun.COM 	return ERANGE;
590*13132SGlenn.Barry@oracle.com     }
59110598SGlenn.Barry@Sun.COM 
59210598SGlenn.Barry@Sun.COM     ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2, &pac_princname, NULL);
59310598SGlenn.Barry@Sun.COM     if (ret != 0)
59410598SGlenn.Barry@Sun.COM 	return ret;
59510598SGlenn.Barry@Sun.COM 
59610598SGlenn.Barry@Sun.COM     ret = krb5_parse_name_flags(context, pac_princname, 0, &pac_principal);
59710598SGlenn.Barry@Sun.COM     if (ret != 0) {
59810598SGlenn.Barry@Sun.COM 	free(pac_princname);
59910598SGlenn.Barry@Sun.COM 	return ret;
60010598SGlenn.Barry@Sun.COM     }
60110598SGlenn.Barry@Sun.COM 
60210598SGlenn.Barry@Sun.COM 
603*13132SGlenn.Barry@oracle.com     if (pac_authtime != authtime) {
604*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
605*13132SGlenn.Barry@oracle.com 	char timestring[17];
606*13132SGlenn.Barry@oracle.com 	char pac_timestring[17];
607*13132SGlenn.Barry@oracle.com 	char fill = ' ';
608*13132SGlenn.Barry@oracle.com 	int err, pac_err;
609*13132SGlenn.Barry@oracle.com 	/* Need better ret code here but don't see one */
61010598SGlenn.Barry@Sun.COM 	ret = KRB5KRB_AP_WRONG_PRINC;
611*13132SGlenn.Barry@oracle.com 	err = krb5_timestamp_to_sfstring(pac_authtime,
612*13132SGlenn.Barry@oracle.com 					timestring,
613*13132SGlenn.Barry@oracle.com 					sizeof (timestring), &fill);
614*13132SGlenn.Barry@oracle.com 	pac_err = krb5_timestamp_to_sfstring(pac_authtime,
615*13132SGlenn.Barry@oracle.com 					pac_timestring,
616*13132SGlenn.Barry@oracle.com 					    sizeof (pac_timestring), &fill);
617*13132SGlenn.Barry@oracle.com 	if (pac_princname && !err && !pac_err) {
618*13132SGlenn.Barry@oracle.com 	    krb5_set_error_message(context, ret,
619*13132SGlenn.Barry@oracle.com 				"PAC verify fail: PAC authtime '%s' does not match authtime '%s'.  PAC principal is '%s'",
620*13132SGlenn.Barry@oracle.com 				pac_timestring, timestring, pac_princname);
621*13132SGlenn.Barry@oracle.com 	}
622*13132SGlenn.Barry@oracle.com     } else if (krb5_principal_compare(context, pac_principal, principal) == FALSE) {
623*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
624*13132SGlenn.Barry@oracle.com 	char *p_name = NULL;
625*13132SGlenn.Barry@oracle.com 	krb5_error_code perr;
626*13132SGlenn.Barry@oracle.com 	ret = KRB5KRB_AP_WRONG_PRINC;
627*13132SGlenn.Barry@oracle.com 	perr = krb5_unparse_name(context, principal, &p_name);
628*13132SGlenn.Barry@oracle.com 	if (pac_princname && !perr) {
629*13132SGlenn.Barry@oracle.com 	    krb5_set_error_message(context, ret,
630*13132SGlenn.Barry@oracle.com 				"Wrong principal in request: PAC verify: Principal in PAC is '%s' and does not match '%s'",
631*13132SGlenn.Barry@oracle.com 				pac_princname, p_name);
632*13132SGlenn.Barry@oracle.com 	}
633*13132SGlenn.Barry@oracle.com 	if (p_name)
634*13132SGlenn.Barry@oracle.com 	    krb5_free_unparsed_name(context, p_name);
635*13132SGlenn.Barry@oracle.com     }
63610598SGlenn.Barry@Sun.COM 
637*13132SGlenn.Barry@oracle.com     free(pac_princname);
63810598SGlenn.Barry@Sun.COM     krb5_free_principal(context, pac_principal);
63910598SGlenn.Barry@Sun.COM 
64010598SGlenn.Barry@Sun.COM     return ret;
64110598SGlenn.Barry@Sun.COM }
64210598SGlenn.Barry@Sun.COM 
64310598SGlenn.Barry@Sun.COM static krb5_error_code
k5_pac_zero_signature(krb5_context context,const krb5_pac pac,krb5_ui_4 type,krb5_data * data)64410598SGlenn.Barry@Sun.COM k5_pac_zero_signature(krb5_context context,
64510598SGlenn.Barry@Sun.COM 		      const krb5_pac pac,
64610598SGlenn.Barry@Sun.COM 		      krb5_ui_4 type,
64710598SGlenn.Barry@Sun.COM 		      krb5_data *data)
64810598SGlenn.Barry@Sun.COM {
64910598SGlenn.Barry@Sun.COM     PAC_INFO_BUFFER *buffer = NULL;
65010598SGlenn.Barry@Sun.COM     size_t i;
65110598SGlenn.Barry@Sun.COM 
65210598SGlenn.Barry@Sun.COM     assert(type == PAC_SERVER_CHECKSUM || type == PAC_PRIVSVR_CHECKSUM);
65310598SGlenn.Barry@Sun.COM     assert(data->length >= pac->data.length);
65410598SGlenn.Barry@Sun.COM 
65510598SGlenn.Barry@Sun.COM     for (i = 0; i < pac->pac->cBuffers; i++) {
65610598SGlenn.Barry@Sun.COM 	if (pac->pac->Buffers[i].ulType == type) {
65710598SGlenn.Barry@Sun.COM 	    buffer = &pac->pac->Buffers[i];
65810598SGlenn.Barry@Sun.COM 	    break;
65910598SGlenn.Barry@Sun.COM 	}
66010598SGlenn.Barry@Sun.COM     }
66110598SGlenn.Barry@Sun.COM 
662*13132SGlenn.Barry@oracle.com     if (buffer == NULL) {
663*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
664*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, ENOENT,
665*13132SGlenn.Barry@oracle.com 			    "No PAC buffer found (type=%d)",
666*13132SGlenn.Barry@oracle.com 			    type);
66710598SGlenn.Barry@Sun.COM 	return ENOENT;
668*13132SGlenn.Barry@oracle.com     }
66910598SGlenn.Barry@Sun.COM 
670*13132SGlenn.Barry@oracle.com     if (buffer->Offset + buffer->cbBufferSize > pac->data.length) {
67110598SGlenn.Barry@Sun.COM 	return ERANGE;
672*13132SGlenn.Barry@oracle.com     }
67310598SGlenn.Barry@Sun.COM 
674*13132SGlenn.Barry@oracle.com     if (buffer->cbBufferSize < PAC_SIGNATURE_DATA_LENGTH) {
67510598SGlenn.Barry@Sun.COM 	return KRB5_BAD_MSIZE;
676*13132SGlenn.Barry@oracle.com     }
67710598SGlenn.Barry@Sun.COM 
67810598SGlenn.Barry@Sun.COM     /* Zero out the data portion of the checksum only */
67910598SGlenn.Barry@Sun.COM     memset(data->data + buffer->Offset + PAC_SIGNATURE_DATA_LENGTH,
68010598SGlenn.Barry@Sun.COM 	   0,
68110598SGlenn.Barry@Sun.COM 	   buffer->cbBufferSize - PAC_SIGNATURE_DATA_LENGTH);
68210598SGlenn.Barry@Sun.COM 
68310598SGlenn.Barry@Sun.COM     return 0;
68410598SGlenn.Barry@Sun.COM }
68510598SGlenn.Barry@Sun.COM 
68610598SGlenn.Barry@Sun.COM static krb5_error_code
k5_pac_verify_server_checksum(krb5_context context,const krb5_pac pac,const krb5_keyblock * server)68710598SGlenn.Barry@Sun.COM k5_pac_verify_server_checksum(krb5_context context,
68810598SGlenn.Barry@Sun.COM 			      const krb5_pac pac,
68910598SGlenn.Barry@Sun.COM 			      const krb5_keyblock *server)
69010598SGlenn.Barry@Sun.COM {
69110598SGlenn.Barry@Sun.COM     krb5_error_code ret;
69210598SGlenn.Barry@Sun.COM     krb5_data pac_data; /* PAC with zeroed checksums */
69310598SGlenn.Barry@Sun.COM     krb5_checksum checksum;
69410598SGlenn.Barry@Sun.COM     krb5_data checksum_data;
69510598SGlenn.Barry@Sun.COM     krb5_boolean valid;
69610598SGlenn.Barry@Sun.COM     krb5_octet *p;
69710598SGlenn.Barry@Sun.COM 
69810598SGlenn.Barry@Sun.COM     ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &checksum_data);
69910598SGlenn.Barry@Sun.COM     if (ret != 0)
70010598SGlenn.Barry@Sun.COM 	return ret;
70110598SGlenn.Barry@Sun.COM 
702*13132SGlenn.Barry@oracle.com     if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH) {
70310598SGlenn.Barry@Sun.COM 	return KRB5_BAD_MSIZE;
704*13132SGlenn.Barry@oracle.com     }
70510598SGlenn.Barry@Sun.COM 
70610598SGlenn.Barry@Sun.COM     p = (krb5_octet *)checksum_data.data;
70710598SGlenn.Barry@Sun.COM     checksum.checksum_type = load_32_le(p);
70810598SGlenn.Barry@Sun.COM     checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
70910598SGlenn.Barry@Sun.COM     checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
71010598SGlenn.Barry@Sun.COM 
71110598SGlenn.Barry@Sun.COM     pac_data.length = pac->data.length;
71210598SGlenn.Barry@Sun.COM     pac_data.data = malloc(pac->data.length);
71310598SGlenn.Barry@Sun.COM     if (pac_data.data == NULL)
71410598SGlenn.Barry@Sun.COM 	return ENOMEM;
71510598SGlenn.Barry@Sun.COM 
71610598SGlenn.Barry@Sun.COM     memcpy(pac_data.data, pac->data.data, pac->data.length);
71710598SGlenn.Barry@Sun.COM 
71810598SGlenn.Barry@Sun.COM     /* Zero out both checksum buffers */
71910598SGlenn.Barry@Sun.COM     ret = k5_pac_zero_signature(context, pac, PAC_SERVER_CHECKSUM, &pac_data);
72010598SGlenn.Barry@Sun.COM     if (ret != 0) {
72110598SGlenn.Barry@Sun.COM 	free(pac_data.data);
72210598SGlenn.Barry@Sun.COM 	return ret;
72310598SGlenn.Barry@Sun.COM     }
72410598SGlenn.Barry@Sun.COM 
72510598SGlenn.Barry@Sun.COM     ret = k5_pac_zero_signature(context, pac, PAC_PRIVSVR_CHECKSUM, &pac_data);
72610598SGlenn.Barry@Sun.COM     if (ret != 0) {
72710598SGlenn.Barry@Sun.COM 	free(pac_data.data);
72810598SGlenn.Barry@Sun.COM 	return ret;
72910598SGlenn.Barry@Sun.COM     }
73010598SGlenn.Barry@Sun.COM 
73110598SGlenn.Barry@Sun.COM     ret = krb5_c_verify_checksum(context, server, KRB5_KEYUSAGE_APP_DATA_CKSUM,
73210598SGlenn.Barry@Sun.COM 				 &pac_data, &checksum, &valid);
73310598SGlenn.Barry@Sun.COM     if (ret != 0) {
73410598SGlenn.Barry@Sun.COM         free(pac_data.data);
73510598SGlenn.Barry@Sun.COM 	return ret;
73610598SGlenn.Barry@Sun.COM     }
73710598SGlenn.Barry@Sun.COM 
738*13132SGlenn.Barry@oracle.com     if (valid == FALSE) {
73910598SGlenn.Barry@Sun.COM 	ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
740*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
741*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, ret,
742*13132SGlenn.Barry@oracle.com 			    "Decrypt integrity check failed for PAC");
743*13132SGlenn.Barry@oracle.com     }
74410598SGlenn.Barry@Sun.COM 
74510598SGlenn.Barry@Sun.COM     free(pac_data.data); /* SUNW17PACresync - mem leak fix */
74610598SGlenn.Barry@Sun.COM     return ret;
74710598SGlenn.Barry@Sun.COM }
74810598SGlenn.Barry@Sun.COM 
74910598SGlenn.Barry@Sun.COM static krb5_error_code
k5_pac_verify_kdc_checksum(krb5_context context,const krb5_pac pac,const krb5_keyblock * privsvr)75010598SGlenn.Barry@Sun.COM k5_pac_verify_kdc_checksum(krb5_context context,
75110598SGlenn.Barry@Sun.COM 			   const krb5_pac pac,
75210598SGlenn.Barry@Sun.COM 			   const krb5_keyblock *privsvr)
75310598SGlenn.Barry@Sun.COM {
75410598SGlenn.Barry@Sun.COM     krb5_error_code ret;
75510598SGlenn.Barry@Sun.COM     krb5_data server_checksum, privsvr_checksum;
75610598SGlenn.Barry@Sun.COM     krb5_checksum checksum;
75710598SGlenn.Barry@Sun.COM     krb5_boolean valid;
75810598SGlenn.Barry@Sun.COM     krb5_octet *p;
75910598SGlenn.Barry@Sun.COM 
76010598SGlenn.Barry@Sun.COM     ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
76110598SGlenn.Barry@Sun.COM     if (ret != 0)
76210598SGlenn.Barry@Sun.COM 	return ret;
76310598SGlenn.Barry@Sun.COM 
764*13132SGlenn.Barry@oracle.com     if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
76510598SGlenn.Barry@Sun.COM 	return KRB5_BAD_MSIZE;
766*13132SGlenn.Barry@oracle.com     }
76710598SGlenn.Barry@Sun.COM 
76810598SGlenn.Barry@Sun.COM     ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_checksum);
76910598SGlenn.Barry@Sun.COM     if (ret != 0)
77010598SGlenn.Barry@Sun.COM 	return ret;
77110598SGlenn.Barry@Sun.COM 
772*13132SGlenn.Barry@oracle.com     if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
77310598SGlenn.Barry@Sun.COM 	return KRB5_BAD_MSIZE;
774*13132SGlenn.Barry@oracle.com     }
77510598SGlenn.Barry@Sun.COM 
77610598SGlenn.Barry@Sun.COM     p = (krb5_octet *)privsvr_checksum.data;
77710598SGlenn.Barry@Sun.COM     checksum.checksum_type = load_32_le(p);
77810598SGlenn.Barry@Sun.COM     checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
77910598SGlenn.Barry@Sun.COM     checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
78010598SGlenn.Barry@Sun.COM 
78110598SGlenn.Barry@Sun.COM     server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
78210598SGlenn.Barry@Sun.COM     server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
78310598SGlenn.Barry@Sun.COM 
78410598SGlenn.Barry@Sun.COM     ret = krb5_c_verify_checksum(context, privsvr, KRB5_KEYUSAGE_APP_DATA_CKSUM,
78510598SGlenn.Barry@Sun.COM 				 &server_checksum, &checksum, &valid);
78610598SGlenn.Barry@Sun.COM     if (ret != 0)
78710598SGlenn.Barry@Sun.COM 	return ret;
78810598SGlenn.Barry@Sun.COM 
789*13132SGlenn.Barry@oracle.com     if (valid == FALSE) {
79010598SGlenn.Barry@Sun.COM 	ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
791*13132SGlenn.Barry@oracle.com 	/* Solaris Kerberos */
792*13132SGlenn.Barry@oracle.com 	krb5_set_error_message(context, ret,
793*13132SGlenn.Barry@oracle.com 			    "Decrypt integrity check failed for PAC");
794*13132SGlenn.Barry@oracle.com     }
79510598SGlenn.Barry@Sun.COM 
79610598SGlenn.Barry@Sun.COM     return ret;
79710598SGlenn.Barry@Sun.COM }
79810598SGlenn.Barry@Sun.COM 
79910598SGlenn.Barry@Sun.COM 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)80010598SGlenn.Barry@Sun.COM krb5_pac_verify(krb5_context context,
80110598SGlenn.Barry@Sun.COM 		const krb5_pac pac,
80210598SGlenn.Barry@Sun.COM 		krb5_timestamp authtime,
80310598SGlenn.Barry@Sun.COM 		krb5_const_principal principal,
80410598SGlenn.Barry@Sun.COM 		const krb5_keyblock *server,
80510598SGlenn.Barry@Sun.COM 		const krb5_keyblock *privsvr)
80610598SGlenn.Barry@Sun.COM {
80710598SGlenn.Barry@Sun.COM     krb5_error_code ret;
80810598SGlenn.Barry@Sun.COM 
809*13132SGlenn.Barry@oracle.com     if (server == NULL) {
81010598SGlenn.Barry@Sun.COM 	return EINVAL;
811*13132SGlenn.Barry@oracle.com     }
81210598SGlenn.Barry@Sun.COM 
81310598SGlenn.Barry@Sun.COM     ret = k5_pac_verify_server_checksum(context, pac, server);
81410598SGlenn.Barry@Sun.COM     if (ret != 0)
81510598SGlenn.Barry@Sun.COM 	return ret;
81610598SGlenn.Barry@Sun.COM 
81710598SGlenn.Barry@Sun.COM     if (privsvr != NULL) {
81810598SGlenn.Barry@Sun.COM 	ret = k5_pac_verify_kdc_checksum(context, pac, privsvr);
81910598SGlenn.Barry@Sun.COM 	if (ret != 0)
82010598SGlenn.Barry@Sun.COM 	    return ret;
82110598SGlenn.Barry@Sun.COM     }
82210598SGlenn.Barry@Sun.COM 
82310598SGlenn.Barry@Sun.COM     if (principal != NULL) {
82410598SGlenn.Barry@Sun.COM 	ret = k5_pac_validate_client(context, pac, authtime, principal);
82510598SGlenn.Barry@Sun.COM 	if (ret != 0)
82610598SGlenn.Barry@Sun.COM 	    return ret;
82710598SGlenn.Barry@Sun.COM     }
82810598SGlenn.Barry@Sun.COM 
82910598SGlenn.Barry@Sun.COM     return 0;
83010598SGlenn.Barry@Sun.COM }
83110598SGlenn.Barry@Sun.COM 
83210598SGlenn.Barry@Sun.COM static krb5_error_code
k5_insert_client_info(krb5_context context,krb5_pac pac,krb5_timestamp authtime,krb5_const_principal principal)83310598SGlenn.Barry@Sun.COM k5_insert_client_info(krb5_context context,
83410598SGlenn.Barry@Sun.COM 		      krb5_pac pac,
83510598SGlenn.Barry@Sun.COM 		      krb5_timestamp authtime,
83610598SGlenn.Barry@Sun.COM 		      krb5_const_principal principal)
83710598SGlenn.Barry@Sun.COM {
83810598SGlenn.Barry@Sun.COM     krb5_error_code ret;
83910598SGlenn.Barry@Sun.COM     krb5_data client_info;
84010598SGlenn.Barry@Sun.COM     char *princ_name_utf8 = NULL;
84110598SGlenn.Barry@Sun.COM     unsigned char *princ_name_ucs2 = NULL, *p;
84210598SGlenn.Barry@Sun.COM     size_t princ_name_ucs2_len = 0;
84310598SGlenn.Barry@Sun.COM     krb5_ui_8 nt_authtime;
84410598SGlenn.Barry@Sun.COM 
84510598SGlenn.Barry@Sun.COM     /* If we already have a CLIENT_INFO buffer, then just validate it */
84610598SGlenn.Barry@Sun.COM     if (k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info) == 0) {
84710598SGlenn.Barry@Sun.COM 	return k5_pac_validate_client(context, pac, authtime, principal);
84810598SGlenn.Barry@Sun.COM     }
84910598SGlenn.Barry@Sun.COM 
85010598SGlenn.Barry@Sun.COM     ret = krb5_unparse_name_flags(context, principal,
85110598SGlenn.Barry@Sun.COM 				  KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_name_utf8);
85210598SGlenn.Barry@Sun.COM     if (ret != 0)
85310598SGlenn.Barry@Sun.COM 	goto cleanup;
85410598SGlenn.Barry@Sun.COM 
85510598SGlenn.Barry@Sun.COM     ret = krb5int_utf8s_to_ucs2les(princ_name_utf8,
85610598SGlenn.Barry@Sun.COM 				   &princ_name_ucs2,
85710598SGlenn.Barry@Sun.COM 				   &princ_name_ucs2_len);
85810598SGlenn.Barry@Sun.COM     if (ret != 0)
85910598SGlenn.Barry@Sun.COM 	goto cleanup;
86010598SGlenn.Barry@Sun.COM 
86110598SGlenn.Barry@Sun.COM     client_info.length = PAC_CLIENT_INFO_LENGTH + princ_name_ucs2_len;
86210598SGlenn.Barry@Sun.COM     client_info.data = NULL;
86310598SGlenn.Barry@Sun.COM 
86410598SGlenn.Barry@Sun.COM     ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO, &client_info, TRUE, &client_info);
86510598SGlenn.Barry@Sun.COM     if (ret != 0)
86610598SGlenn.Barry@Sun.COM 	goto cleanup;
86710598SGlenn.Barry@Sun.COM 
86810598SGlenn.Barry@Sun.COM     p = (unsigned char *)client_info.data;
86910598SGlenn.Barry@Sun.COM 
87010598SGlenn.Barry@Sun.COM     /* copy in authtime converted to a 64-bit NT time */
87110598SGlenn.Barry@Sun.COM     k5_seconds_since_1970_to_time(authtime, &nt_authtime);
87210598SGlenn.Barry@Sun.COM     store_64_le(nt_authtime, p);
87310598SGlenn.Barry@Sun.COM     p += 8;
87410598SGlenn.Barry@Sun.COM 
87510598SGlenn.Barry@Sun.COM     /* copy in number of UCS-2 characters in principal name */
87610598SGlenn.Barry@Sun.COM     store_16_le(princ_name_ucs2_len, p);
87710598SGlenn.Barry@Sun.COM     p += 2;
87810598SGlenn.Barry@Sun.COM 
87910598SGlenn.Barry@Sun.COM     /* copy in principal name */
88010598SGlenn.Barry@Sun.COM     memcpy(p, princ_name_ucs2, princ_name_ucs2_len);
88110598SGlenn.Barry@Sun.COM 
88210598SGlenn.Barry@Sun.COM cleanup:
88310598SGlenn.Barry@Sun.COM     if (princ_name_utf8 != NULL)
88410598SGlenn.Barry@Sun.COM 	free(princ_name_utf8);
88510598SGlenn.Barry@Sun.COM     if (princ_name_ucs2 != NULL)
88610598SGlenn.Barry@Sun.COM 	free(princ_name_ucs2);
88710598SGlenn.Barry@Sun.COM 
88810598SGlenn.Barry@Sun.COM     return ret;
88910598SGlenn.Barry@Sun.COM }
89010598SGlenn.Barry@Sun.COM 
89110598SGlenn.Barry@Sun.COM static krb5_error_code
k5_insert_checksum(krb5_context context,krb5_pac pac,krb5_ui_4 type,const krb5_keyblock * key,krb5_cksumtype * cksumtype)89210598SGlenn.Barry@Sun.COM k5_insert_checksum(krb5_context context,
89310598SGlenn.Barry@Sun.COM 		   krb5_pac pac,
89410598SGlenn.Barry@Sun.COM 		   krb5_ui_4 type,
89510598SGlenn.Barry@Sun.COM 		   const krb5_keyblock *key,
89610598SGlenn.Barry@Sun.COM 		   krb5_cksumtype *cksumtype)
89710598SGlenn.Barry@Sun.COM {
89810598SGlenn.Barry@Sun.COM     krb5_error_code ret;
89910598SGlenn.Barry@Sun.COM     size_t len;
90010598SGlenn.Barry@Sun.COM     krb5_data cksumdata;
90110598SGlenn.Barry@Sun.COM 
90210598SGlenn.Barry@Sun.COM     ret = krb5int_c_mandatory_cksumtype(context, key->enctype, cksumtype);
90310598SGlenn.Barry@Sun.COM     if (ret != 0)
90410598SGlenn.Barry@Sun.COM 	return ret;
90510598SGlenn.Barry@Sun.COM 
90610598SGlenn.Barry@Sun.COM     ret = krb5_c_checksum_length(context, *cksumtype, &len);
90710598SGlenn.Barry@Sun.COM     if (ret != 0)
90810598SGlenn.Barry@Sun.COM 	return ret;
90910598SGlenn.Barry@Sun.COM 
91010598SGlenn.Barry@Sun.COM     ret = k5_pac_locate_buffer(context, pac, type, &cksumdata);
91110598SGlenn.Barry@Sun.COM     if (ret == 0) {
91210598SGlenn.Barry@Sun.COM 	/* If we're resigning PAC, make sure we can fit checksum into existing buffer */
913*13132SGlenn.Barry@oracle.com 	if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len) {
91410598SGlenn.Barry@Sun.COM 	    return ERANGE;
915*13132SGlenn.Barry@oracle.com 	}
91610598SGlenn.Barry@Sun.COM 
91710598SGlenn.Barry@Sun.COM 	memset(cksumdata.data, 0, cksumdata.length);
91810598SGlenn.Barry@Sun.COM     } else {
91910598SGlenn.Barry@Sun.COM 	/* Add a zero filled buffer */
92010598SGlenn.Barry@Sun.COM 	cksumdata.length = PAC_SIGNATURE_DATA_LENGTH + len;
92110598SGlenn.Barry@Sun.COM 	cksumdata.data = NULL;
92210598SGlenn.Barry@Sun.COM 
92310598SGlenn.Barry@Sun.COM 	ret = k5_pac_add_buffer(context, pac, type, &cksumdata, TRUE, &cksumdata);
92410598SGlenn.Barry@Sun.COM 	if (ret != 0)
92510598SGlenn.Barry@Sun.COM 	    return ret;
92610598SGlenn.Barry@Sun.COM     }
92710598SGlenn.Barry@Sun.COM 
92810598SGlenn.Barry@Sun.COM     /* Encode checksum type into buffer */
92910598SGlenn.Barry@Sun.COM     store_32_le((krb5_ui_4)*cksumtype, cksumdata.data);
93010598SGlenn.Barry@Sun.COM 
93110598SGlenn.Barry@Sun.COM     return 0;
93210598SGlenn.Barry@Sun.COM }
93310598SGlenn.Barry@Sun.COM 
93410598SGlenn.Barry@Sun.COM /* in-place encoding of PAC header */
93510598SGlenn.Barry@Sun.COM static krb5_error_code
k5_pac_encode_header(krb5_context context,krb5_pac pac)93610598SGlenn.Barry@Sun.COM k5_pac_encode_header(krb5_context context, krb5_pac pac)
93710598SGlenn.Barry@Sun.COM {
93810598SGlenn.Barry@Sun.COM     size_t i;
93910598SGlenn.Barry@Sun.COM     unsigned char *p;
94010598SGlenn.Barry@Sun.COM     size_t header_len;
94110598SGlenn.Barry@Sun.COM 
94210598SGlenn.Barry@Sun.COM     header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
94310598SGlenn.Barry@Sun.COM     assert(pac->data.length >= header_len);
94410598SGlenn.Barry@Sun.COM 
94510598SGlenn.Barry@Sun.COM     p = (unsigned char *)pac->data.data;
94610598SGlenn.Barry@Sun.COM 
94710598SGlenn.Barry@Sun.COM     store_32_le(pac->pac->cBuffers, p);
94810598SGlenn.Barry@Sun.COM     p += 4;
94910598SGlenn.Barry@Sun.COM     store_32_le(pac->pac->Version, p);
95010598SGlenn.Barry@Sun.COM     p += 4;
95110598SGlenn.Barry@Sun.COM 
95210598SGlenn.Barry@Sun.COM     for (i = 0; i < pac->pac->cBuffers; i++) {
95310598SGlenn.Barry@Sun.COM 	PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
95410598SGlenn.Barry@Sun.COM 
95510598SGlenn.Barry@Sun.COM 	store_32_le(buffer->ulType, p);
95610598SGlenn.Barry@Sun.COM 	p += 4;
95710598SGlenn.Barry@Sun.COM 	store_32_le(buffer->cbBufferSize, p);
95810598SGlenn.Barry@Sun.COM 	p += 4;
95910598SGlenn.Barry@Sun.COM 	store_64_le(buffer->Offset, p);
96010598SGlenn.Barry@Sun.COM 	p += 8;
96110598SGlenn.Barry@Sun.COM 
96210598SGlenn.Barry@Sun.COM 	assert((buffer->Offset % PAC_ALIGNMENT) == 0);
96310598SGlenn.Barry@Sun.COM 	assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
96410598SGlenn.Barry@Sun.COM 	assert(buffer->Offset >= header_len);
96510598SGlenn.Barry@Sun.COM 
96610598SGlenn.Barry@Sun.COM 	if (buffer->Offset % PAC_ALIGNMENT ||
96710598SGlenn.Barry@Sun.COM 	    buffer->Offset + buffer->cbBufferSize > pac->data.length ||
968*13132SGlenn.Barry@oracle.com 	    buffer->Offset < header_len) {
96910598SGlenn.Barry@Sun.COM 	    return ERANGE;
970*13132SGlenn.Barry@oracle.com 	}
97110598SGlenn.Barry@Sun.COM     }
97210598SGlenn.Barry@Sun.COM 
97310598SGlenn.Barry@Sun.COM     return 0;
97410598SGlenn.Barry@Sun.COM }
97510598SGlenn.Barry@Sun.COM 
97610598SGlenn.Barry@Sun.COM 
97710598SGlenn.Barry@Sun.COM #if 0
97810598SGlenn.Barry@Sun.COM /*
97910598SGlenn.Barry@Sun.COM  * SUNW17PACresync
98010598SGlenn.Barry@Sun.COM  * We don't have the new MIT iov interfaces yet and don't need them yet.
98110598SGlenn.Barry@Sun.COM  * We'll need this for full 1.7 resync.
98210598SGlenn.Barry@Sun.COM  */
98310598SGlenn.Barry@Sun.COM krb5_error_code KRB5_CALLCONV
98410598SGlenn.Barry@Sun.COM krb5int_pac_sign(krb5_context context,
98510598SGlenn.Barry@Sun.COM 		 krb5_pac pac,
98610598SGlenn.Barry@Sun.COM 		 krb5_timestamp authtime,
98710598SGlenn.Barry@Sun.COM 		 krb5_const_principal principal,
98810598SGlenn.Barry@Sun.COM 		 const krb5_keyblock *server_key,
98910598SGlenn.Barry@Sun.COM 		 const krb5_keyblock *privsvr_key,
99010598SGlenn.Barry@Sun.COM 		 krb5_data *data)
99110598SGlenn.Barry@Sun.COM {
99210598SGlenn.Barry@Sun.COM     krb5_error_code ret;
99310598SGlenn.Barry@Sun.COM     krb5_data server_cksum, privsvr_cksum;
99410598SGlenn.Barry@Sun.COM     krb5_cksumtype server_cksumtype, privsvr_cksumtype;
99510598SGlenn.Barry@Sun.COM     krb5_crypto_iov iov[2];
99610598SGlenn.Barry@Sun.COM 
99710598SGlenn.Barry@Sun.COM     data->length = 0;
99810598SGlenn.Barry@Sun.COM     data->data = NULL;
99910598SGlenn.Barry@Sun.COM 
100010598SGlenn.Barry@Sun.COM     if (principal != NULL) {
100110598SGlenn.Barry@Sun.COM 	ret = k5_insert_client_info(context, pac, authtime, principal);
100210598SGlenn.Barry@Sun.COM 	if (ret != 0)
100310598SGlenn.Barry@Sun.COM 	    return ret;
100410598SGlenn.Barry@Sun.COM     }
100510598SGlenn.Barry@Sun.COM 
100610598SGlenn.Barry@Sun.COM     /* Create zeroed buffers for both checksums */
100710598SGlenn.Barry@Sun.COM     ret = k5_insert_checksum(context, pac, PAC_SERVER_CHECKSUM,
100810598SGlenn.Barry@Sun.COM 			     server_key, &server_cksumtype);
100910598SGlenn.Barry@Sun.COM     if (ret != 0)
101010598SGlenn.Barry@Sun.COM 	return ret;
101110598SGlenn.Barry@Sun.COM 
101210598SGlenn.Barry@Sun.COM     ret = k5_insert_checksum(context, pac, PAC_PRIVSVR_CHECKSUM,
101310598SGlenn.Barry@Sun.COM 			     privsvr_key, &privsvr_cksumtype);
101410598SGlenn.Barry@Sun.COM     if (ret != 0)
101510598SGlenn.Barry@Sun.COM 	return ret;
101610598SGlenn.Barry@Sun.COM 
101710598SGlenn.Barry@Sun.COM     /* Now, encode the PAC header so that the checksums will include it */
101810598SGlenn.Barry@Sun.COM     ret = k5_pac_encode_header(context, pac);
101910598SGlenn.Barry@Sun.COM     if (ret != 0)
102010598SGlenn.Barry@Sun.COM 	return ret;
102110598SGlenn.Barry@Sun.COM 
102210598SGlenn.Barry@Sun.COM     /* Generate the server checksum over the entire PAC */
102310598SGlenn.Barry@Sun.COM     ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_cksum);
102410598SGlenn.Barry@Sun.COM     if (ret != 0)
102510598SGlenn.Barry@Sun.COM 	return ret;
102610598SGlenn.Barry@Sun.COM 
102710598SGlenn.Barry@Sun.COM     assert(server_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
102810598SGlenn.Barry@Sun.COM 
102910598SGlenn.Barry@Sun.COM     iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
103010598SGlenn.Barry@Sun.COM     iov[0].data = pac->data;
103110598SGlenn.Barry@Sun.COM 
103210598SGlenn.Barry@Sun.COM     iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
103310598SGlenn.Barry@Sun.COM     iov[1].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
103410598SGlenn.Barry@Sun.COM     iov[1].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
103510598SGlenn.Barry@Sun.COM 
103610598SGlenn.Barry@Sun.COM     ret = krb5_c_make_checksum_iov(context, server_cksumtype,
103710598SGlenn.Barry@Sun.COM 				   server_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
103810598SGlenn.Barry@Sun.COM 				   iov, sizeof(iov)/sizeof(iov[0]));
103910598SGlenn.Barry@Sun.COM     if (ret != 0)
104010598SGlenn.Barry@Sun.COM 	return ret;
104110598SGlenn.Barry@Sun.COM 
104210598SGlenn.Barry@Sun.COM     /* Generate the privsvr checksum over the server checksum buffer */
104310598SGlenn.Barry@Sun.COM     ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_cksum);
104410598SGlenn.Barry@Sun.COM     if (ret != 0)
104510598SGlenn.Barry@Sun.COM 	return ret;
104610598SGlenn.Barry@Sun.COM 
104710598SGlenn.Barry@Sun.COM     assert(privsvr_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
104810598SGlenn.Barry@Sun.COM 
104910598SGlenn.Barry@Sun.COM     iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
105010598SGlenn.Barry@Sun.COM     iov[0].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
105110598SGlenn.Barry@Sun.COM     iov[0].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
105210598SGlenn.Barry@Sun.COM 
105310598SGlenn.Barry@Sun.COM     iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
105410598SGlenn.Barry@Sun.COM     iov[1].data.data = privsvr_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
105510598SGlenn.Barry@Sun.COM     iov[1].data.length = privsvr_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
105610598SGlenn.Barry@Sun.COM 
105710598SGlenn.Barry@Sun.COM     ret = krb5_c_make_checksum_iov(context, privsvr_cksumtype,
105810598SGlenn.Barry@Sun.COM 				   privsvr_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
105910598SGlenn.Barry@Sun.COM 				   iov, sizeof(iov)/sizeof(iov[0]));
106010598SGlenn.Barry@Sun.COM     if (ret != 0)
106110598SGlenn.Barry@Sun.COM 	return ret;
106210598SGlenn.Barry@Sun.COM 
106310598SGlenn.Barry@Sun.COM     data->data = malloc(pac->data.length);
106410598SGlenn.Barry@Sun.COM     if (data->data == NULL)
106510598SGlenn.Barry@Sun.COM 	return ENOMEM;
106610598SGlenn.Barry@Sun.COM 
106710598SGlenn.Barry@Sun.COM     data->length = pac->data.length;
106810598SGlenn.Barry@Sun.COM 
106910598SGlenn.Barry@Sun.COM     memcpy(data->data, pac->data.data, pac->data.length);
107010598SGlenn.Barry@Sun.COM     memset(pac->data.data, 0, PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH));
107110598SGlenn.Barry@Sun.COM 
107210598SGlenn.Barry@Sun.COM     return 0;
107310598SGlenn.Barry@Sun.COM }
107410598SGlenn.Barry@Sun.COM #endif
107510598SGlenn.Barry@Sun.COM 
1076