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