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