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