1178825Sdfr/* 2233294Sstas * Copyright (c) 2006 - 2007 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 5178825Sdfr * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 9178825Sdfr * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 20178825Sdfr * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34178825Sdfr#include "krb5_locl.h" 35233294Sstas#include <wind.h> 36178825Sdfr 37178825Sdfrstruct PAC_INFO_BUFFER { 38178825Sdfr uint32_t type; 39178825Sdfr uint32_t buffersize; 40178825Sdfr uint32_t offset_hi; 41178825Sdfr uint32_t offset_lo; 42178825Sdfr}; 43178825Sdfr 44178825Sdfrstruct PACTYPE { 45178825Sdfr uint32_t numbuffers; 46233294Sstas uint32_t version; 47178825Sdfr struct PAC_INFO_BUFFER buffers[1]; 48178825Sdfr}; 49178825Sdfr 50178825Sdfrstruct krb5_pac_data { 51178825Sdfr struct PACTYPE *pac; 52178825Sdfr krb5_data data; 53178825Sdfr struct PAC_INFO_BUFFER *server_checksum; 54178825Sdfr struct PAC_INFO_BUFFER *privsvr_checksum; 55178825Sdfr struct PAC_INFO_BUFFER *logon_name; 56178825Sdfr}; 57178825Sdfr 58178825Sdfr#define PAC_ALIGNMENT 8 59178825Sdfr 60178825Sdfr#define PACTYPE_SIZE 8 61178825Sdfr#define PAC_INFO_BUFFER_SIZE 16 62178825Sdfr 63178825Sdfr#define PAC_SERVER_CHECKSUM 6 64178825Sdfr#define PAC_PRIVSVR_CHECKSUM 7 65178825Sdfr#define PAC_LOGON_NAME 10 66178825Sdfr#define PAC_CONSTRAINED_DELEGATION 11 67178825Sdfr 68178825Sdfr#define CHECK(r,f,l) \ 69178825Sdfr do { \ 70178825Sdfr if (((r) = f ) != 0) { \ 71233294Sstas krb5_clear_error_message(context); \ 72178825Sdfr goto l; \ 73178825Sdfr } \ 74178825Sdfr } while(0) 75178825Sdfr 76178825Sdfrstatic const char zeros[PAC_ALIGNMENT] = { 0 }; 77178825Sdfr 78178825Sdfr/* 79233294Sstas * HMAC-MD5 checksum over any key (needed for the PAC routines) 80233294Sstas */ 81233294Sstas 82233294Sstasstatic krb5_error_code 83233294SstasHMAC_MD5_any_checksum(krb5_context context, 84233294Sstas const krb5_keyblock *key, 85233294Sstas const void *data, 86233294Sstas size_t len, 87233294Sstas unsigned usage, 88233294Sstas Checksum *result) 89233294Sstas{ 90233294Sstas struct _krb5_key_data local_key; 91233294Sstas krb5_error_code ret; 92233294Sstas 93233294Sstas memset(&local_key, 0, sizeof(local_key)); 94233294Sstas 95233294Sstas ret = krb5_copy_keyblock(context, key, &local_key.key); 96233294Sstas if (ret) 97233294Sstas return ret; 98233294Sstas 99233294Sstas ret = krb5_data_alloc (&result->checksum, 16); 100233294Sstas if (ret) { 101233294Sstas krb5_free_keyblock(context, local_key.key); 102233294Sstas return ret; 103233294Sstas } 104233294Sstas 105233294Sstas result->cksumtype = CKSUMTYPE_HMAC_MD5; 106233294Sstas ret = _krb5_HMAC_MD5_checksum(context, &local_key, data, len, usage, result); 107233294Sstas if (ret) 108233294Sstas krb5_data_free(&result->checksum); 109233294Sstas 110233294Sstas krb5_free_keyblock(context, local_key.key); 111233294Sstas return ret; 112233294Sstas} 113233294Sstas 114233294Sstas 115233294Sstas/* 116178825Sdfr * 117178825Sdfr */ 118178825Sdfr 119233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 120178825Sdfrkrb5_pac_parse(krb5_context context, const void *ptr, size_t len, 121178825Sdfr krb5_pac *pac) 122178825Sdfr{ 123178825Sdfr krb5_error_code ret; 124178825Sdfr krb5_pac p; 125178825Sdfr krb5_storage *sp = NULL; 126178825Sdfr uint32_t i, tmp, tmp2, header_end; 127178825Sdfr 128178825Sdfr p = calloc(1, sizeof(*p)); 129178825Sdfr if (p == NULL) { 130233294Sstas ret = krb5_enomem(context); 131178825Sdfr goto out; 132178825Sdfr } 133178825Sdfr 134178825Sdfr sp = krb5_storage_from_readonly_mem(ptr, len); 135178825Sdfr if (sp == NULL) { 136233294Sstas ret = krb5_enomem(context); 137178825Sdfr goto out; 138178825Sdfr } 139178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 140178825Sdfr 141178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &tmp), out); 142178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &tmp2), out); 143178825Sdfr if (tmp < 1) { 144178825Sdfr ret = EINVAL; /* Too few buffers */ 145233294Sstas krb5_set_error_message(context, ret, N_("PAC have too few buffer", "")); 146178825Sdfr goto out; 147178825Sdfr } 148178825Sdfr if (tmp2 != 0) { 149178825Sdfr ret = EINVAL; /* Wrong version */ 150233294Sstas krb5_set_error_message(context, ret, 151233294Sstas N_("PAC have wrong version %d", ""), 152233294Sstas (int)tmp2); 153178825Sdfr goto out; 154178825Sdfr } 155178825Sdfr 156233294Sstas p->pac = calloc(1, 157178825Sdfr sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1))); 158178825Sdfr if (p->pac == NULL) { 159233294Sstas ret = krb5_enomem(context); 160178825Sdfr goto out; 161178825Sdfr } 162178825Sdfr 163178825Sdfr p->pac->numbuffers = tmp; 164178825Sdfr p->pac->version = tmp2; 165178825Sdfr 166178825Sdfr header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); 167178825Sdfr if (header_end > len) { 168178825Sdfr ret = EINVAL; 169178825Sdfr goto out; 170178825Sdfr } 171178825Sdfr 172178825Sdfr for (i = 0; i < p->pac->numbuffers; i++) { 173178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out); 174178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out); 175178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out); 176178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out); 177178825Sdfr 178178825Sdfr /* consistency checks */ 179178825Sdfr if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) { 180178825Sdfr ret = EINVAL; 181233294Sstas krb5_set_error_message(context, ret, 182233294Sstas N_("PAC out of allignment", "")); 183178825Sdfr goto out; 184178825Sdfr } 185178825Sdfr if (p->pac->buffers[i].offset_hi) { 186178825Sdfr ret = EINVAL; 187233294Sstas krb5_set_error_message(context, ret, 188233294Sstas N_("PAC high offset set", "")); 189178825Sdfr goto out; 190178825Sdfr } 191178825Sdfr if (p->pac->buffers[i].offset_lo > len) { 192178825Sdfr ret = EINVAL; 193233294Sstas krb5_set_error_message(context, ret, 194233294Sstas N_("PAC offset off end", "")); 195178825Sdfr goto out; 196178825Sdfr } 197178825Sdfr if (p->pac->buffers[i].offset_lo < header_end) { 198178825Sdfr ret = EINVAL; 199233294Sstas krb5_set_error_message(context, ret, 200233294Sstas N_("PAC offset inside header: %lu %lu", ""), 201233294Sstas (unsigned long)p->pac->buffers[i].offset_lo, 202233294Sstas (unsigned long)header_end); 203178825Sdfr goto out; 204178825Sdfr } 205178825Sdfr if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){ 206178825Sdfr ret = EINVAL; 207233294Sstas krb5_set_error_message(context, ret, N_("PAC length off end", "")); 208178825Sdfr goto out; 209178825Sdfr } 210178825Sdfr 211178825Sdfr /* let save pointer to data we need later */ 212178825Sdfr if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { 213178825Sdfr if (p->server_checksum) { 214178825Sdfr ret = EINVAL; 215233294Sstas krb5_set_error_message(context, ret, 216233294Sstas N_("PAC have two server checksums", "")); 217178825Sdfr goto out; 218178825Sdfr } 219178825Sdfr p->server_checksum = &p->pac->buffers[i]; 220178825Sdfr } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { 221178825Sdfr if (p->privsvr_checksum) { 222178825Sdfr ret = EINVAL; 223233294Sstas krb5_set_error_message(context, ret, 224233294Sstas N_("PAC have two KDC checksums", "")); 225178825Sdfr goto out; 226178825Sdfr } 227178825Sdfr p->privsvr_checksum = &p->pac->buffers[i]; 228178825Sdfr } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { 229178825Sdfr if (p->logon_name) { 230178825Sdfr ret = EINVAL; 231233294Sstas krb5_set_error_message(context, ret, 232233294Sstas N_("PAC have two logon names", "")); 233178825Sdfr goto out; 234178825Sdfr } 235178825Sdfr p->logon_name = &p->pac->buffers[i]; 236178825Sdfr } 237178825Sdfr } 238178825Sdfr 239178825Sdfr ret = krb5_data_copy(&p->data, ptr, len); 240178825Sdfr if (ret) 241178825Sdfr goto out; 242178825Sdfr 243178825Sdfr krb5_storage_free(sp); 244178825Sdfr 245178825Sdfr *pac = p; 246178825Sdfr return 0; 247178825Sdfr 248178825Sdfrout: 249178825Sdfr if (sp) 250178825Sdfr krb5_storage_free(sp); 251178825Sdfr if (p) { 252178825Sdfr if (p->pac) 253178825Sdfr free(p->pac); 254178825Sdfr free(p); 255178825Sdfr } 256178825Sdfr *pac = NULL; 257178825Sdfr 258178825Sdfr return ret; 259178825Sdfr} 260178825Sdfr 261233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 262178825Sdfrkrb5_pac_init(krb5_context context, krb5_pac *pac) 263178825Sdfr{ 264178825Sdfr krb5_error_code ret; 265178825Sdfr krb5_pac p; 266178825Sdfr 267178825Sdfr p = calloc(1, sizeof(*p)); 268178825Sdfr if (p == NULL) { 269233294Sstas return krb5_enomem(context); 270178825Sdfr } 271178825Sdfr 272178825Sdfr p->pac = calloc(1, sizeof(*p->pac)); 273178825Sdfr if (p->pac == NULL) { 274178825Sdfr free(p); 275233294Sstas return krb5_enomem(context); 276178825Sdfr } 277178825Sdfr 278178825Sdfr ret = krb5_data_alloc(&p->data, PACTYPE_SIZE); 279178825Sdfr if (ret) { 280178825Sdfr free (p->pac); 281178825Sdfr free(p); 282233294Sstas return krb5_enomem(context); 283178825Sdfr } 284178825Sdfr 285178825Sdfr *pac = p; 286178825Sdfr return 0; 287178825Sdfr} 288178825Sdfr 289233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 290178825Sdfrkrb5_pac_add_buffer(krb5_context context, krb5_pac p, 291178825Sdfr uint32_t type, const krb5_data *data) 292178825Sdfr{ 293178825Sdfr krb5_error_code ret; 294178825Sdfr void *ptr; 295178825Sdfr size_t len, offset, header_end, old_end; 296178825Sdfr uint32_t i; 297178825Sdfr 298178825Sdfr len = p->pac->numbuffers; 299178825Sdfr 300178825Sdfr ptr = realloc(p->pac, 301178825Sdfr sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len)); 302233294Sstas if (ptr == NULL) 303233294Sstas return krb5_enomem(context); 304233294Sstas 305178825Sdfr p->pac = ptr; 306178825Sdfr 307178825Sdfr for (i = 0; i < len; i++) 308178825Sdfr p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE; 309178825Sdfr 310178825Sdfr offset = p->data.length + PAC_INFO_BUFFER_SIZE; 311178825Sdfr 312178825Sdfr p->pac->buffers[len].type = type; 313178825Sdfr p->pac->buffers[len].buffersize = data->length; 314178825Sdfr p->pac->buffers[len].offset_lo = offset; 315178825Sdfr p->pac->buffers[len].offset_hi = 0; 316178825Sdfr 317178825Sdfr old_end = p->data.length; 318178825Sdfr len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE; 319178825Sdfr if (len < p->data.length) { 320233294Sstas krb5_set_error_message(context, EINVAL, "integer overrun"); 321178825Sdfr return EINVAL; 322178825Sdfr } 323233294Sstas 324178825Sdfr /* align to PAC_ALIGNMENT */ 325178825Sdfr len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; 326178825Sdfr 327178825Sdfr ret = krb5_data_realloc(&p->data, len); 328178825Sdfr if (ret) { 329233294Sstas krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 330178825Sdfr return ret; 331178825Sdfr } 332178825Sdfr 333233294Sstas /* 334178825Sdfr * make place for new PAC INFO BUFFER header 335178825Sdfr */ 336178825Sdfr header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); 337178825Sdfr memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE, 338178825Sdfr (unsigned char *)p->data.data + header_end , 339178825Sdfr old_end - header_end); 340178825Sdfr memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE); 341178825Sdfr 342178825Sdfr /* 343178825Sdfr * copy in new data part 344178825Sdfr */ 345178825Sdfr 346178825Sdfr memcpy((unsigned char *)p->data.data + offset, 347178825Sdfr data->data, data->length); 348178825Sdfr memset((unsigned char *)p->data.data + offset + data->length, 349178825Sdfr 0, p->data.length - offset - data->length); 350178825Sdfr 351178825Sdfr p->pac->numbuffers += 1; 352178825Sdfr 353178825Sdfr return 0; 354178825Sdfr} 355178825Sdfr 356233294Sstas/** 357233294Sstas * Get the PAC buffer of specific type from the pac. 358233294Sstas * 359233294Sstas * @param context Kerberos 5 context. 360233294Sstas * @param p the pac structure returned by krb5_pac_parse(). 361233294Sstas * @param type type of buffer to get 362233294Sstas * @param data return data, free with krb5_data_free(). 363233294Sstas * 364233294Sstas * @return Returns 0 to indicate success. Otherwise an kerberos et 365233294Sstas * error code is returned, see krb5_get_error_message(). 366233294Sstas * 367233294Sstas * @ingroup krb5_pac 368233294Sstas */ 369233294Sstas 370233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 371178825Sdfrkrb5_pac_get_buffer(krb5_context context, krb5_pac p, 372178825Sdfr uint32_t type, krb5_data *data) 373178825Sdfr{ 374178825Sdfr krb5_error_code ret; 375178825Sdfr uint32_t i; 376178825Sdfr 377178825Sdfr for (i = 0; i < p->pac->numbuffers; i++) { 378233294Sstas const size_t len = p->pac->buffers[i].buffersize; 379233294Sstas const size_t offset = p->pac->buffers[i].offset_lo; 380178825Sdfr 381178825Sdfr if (p->pac->buffers[i].type != type) 382178825Sdfr continue; 383178825Sdfr 384178825Sdfr ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len); 385178825Sdfr if (ret) { 386233294Sstas krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 387178825Sdfr return ret; 388178825Sdfr } 389178825Sdfr return 0; 390178825Sdfr } 391233294Sstas krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found", 392233294Sstas (unsigned long)type); 393178825Sdfr return ENOENT; 394178825Sdfr} 395178825Sdfr 396178825Sdfr/* 397178825Sdfr * 398178825Sdfr */ 399178825Sdfr 400233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 401178825Sdfrkrb5_pac_get_types(krb5_context context, 402178825Sdfr krb5_pac p, 403178825Sdfr size_t *len, 404178825Sdfr uint32_t **types) 405178825Sdfr{ 406178825Sdfr size_t i; 407178825Sdfr 408178825Sdfr *types = calloc(p->pac->numbuffers, sizeof(*types)); 409178825Sdfr if (*types == NULL) { 410178825Sdfr *len = 0; 411233294Sstas return krb5_enomem(context); 412178825Sdfr } 413178825Sdfr for (i = 0; i < p->pac->numbuffers; i++) 414178825Sdfr (*types)[i] = p->pac->buffers[i].type; 415178825Sdfr *len = p->pac->numbuffers; 416178825Sdfr 417178825Sdfr return 0; 418178825Sdfr} 419178825Sdfr 420178825Sdfr/* 421178825Sdfr * 422178825Sdfr */ 423178825Sdfr 424233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL 425178825Sdfrkrb5_pac_free(krb5_context context, krb5_pac pac) 426178825Sdfr{ 427178825Sdfr krb5_data_free(&pac->data); 428178825Sdfr free(pac->pac); 429178825Sdfr free(pac); 430178825Sdfr} 431178825Sdfr 432178825Sdfr/* 433178825Sdfr * 434178825Sdfr */ 435178825Sdfr 436178825Sdfrstatic krb5_error_code 437178825Sdfrverify_checksum(krb5_context context, 438178825Sdfr const struct PAC_INFO_BUFFER *sig, 439178825Sdfr const krb5_data *data, 440178825Sdfr void *ptr, size_t len, 441178825Sdfr const krb5_keyblock *key) 442178825Sdfr{ 443178825Sdfr krb5_storage *sp = NULL; 444178825Sdfr uint32_t type; 445178825Sdfr krb5_error_code ret; 446178825Sdfr Checksum cksum; 447178825Sdfr 448178825Sdfr memset(&cksum, 0, sizeof(cksum)); 449178825Sdfr 450178825Sdfr sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo, 451178825Sdfr sig->buffersize); 452233294Sstas if (sp == NULL) 453233294Sstas return krb5_enomem(context); 454233294Sstas 455178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 456178825Sdfr 457178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &type), out); 458178825Sdfr cksum.cksumtype = type; 459233294Sstas cksum.checksum.length = 460178825Sdfr sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR); 461178825Sdfr cksum.checksum.data = malloc(cksum.checksum.length); 462178825Sdfr if (cksum.checksum.data == NULL) { 463233294Sstas ret = krb5_enomem(context); 464178825Sdfr goto out; 465178825Sdfr } 466178825Sdfr ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length); 467233294Sstas if (ret != (int)cksum.checksum.length) { 468178825Sdfr ret = EINVAL; 469233294Sstas krb5_set_error_message(context, ret, "PAC checksum missing checksum"); 470178825Sdfr goto out; 471178825Sdfr } 472178825Sdfr 473178825Sdfr if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) { 474233294Sstas ret = EINVAL; 475233294Sstas krb5_set_error_message(context, ret, "Checksum type %d not keyed", 476178825Sdfr cksum.cksumtype); 477178825Sdfr goto out; 478178825Sdfr } 479178825Sdfr 480233294Sstas /* If the checksum is HMAC-MD5, the checksum type is not tied to 481233294Sstas * the key type, instead the HMAC-MD5 checksum is applied blindly 482233294Sstas * on whatever key is used for this connection, avoiding issues 483233294Sstas * with unkeyed checksums on des-cbc-md5 and des-cbc-crc. See 484233294Sstas * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743 485233294Sstas * for the same issue in MIT, and 486233294Sstas * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx 487233294Sstas * for Microsoft's explaination */ 488178825Sdfr 489233294Sstas if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5) { 490233294Sstas Checksum local_checksum; 491233294Sstas 492233294Sstas memset(&local_checksum, 0, sizeof(local_checksum)); 493233294Sstas 494233294Sstas ret = HMAC_MD5_any_checksum(context, key, ptr, len, 495233294Sstas KRB5_KU_OTHER_CKSUM, &local_checksum); 496233294Sstas 497233294Sstas if (ret != 0 || krb5_data_ct_cmp(&local_checksum.checksum, &cksum.checksum) != 0) { 498233294Sstas ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; 499233294Sstas krb5_set_error_message(context, ret, 500233294Sstas N_("PAC integrity check failed for " 501233294Sstas "hmac-md5 checksum", "")); 502233294Sstas } 503233294Sstas krb5_data_free(&local_checksum.checksum); 504233294Sstas 505233294Sstas } else { 506233294Sstas krb5_crypto crypto = NULL; 507233294Sstas 508233294Sstas ret = krb5_crypto_init(context, key, 0, &crypto); 509233294Sstas if (ret) 510233294Sstas goto out; 511233294Sstas 512233294Sstas ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 513233294Sstas ptr, len, &cksum); 514233294Sstas krb5_crypto_destroy(context, crypto); 515233294Sstas } 516178825Sdfr free(cksum.checksum.data); 517178825Sdfr krb5_storage_free(sp); 518178825Sdfr 519178825Sdfr return ret; 520178825Sdfr 521178825Sdfrout: 522178825Sdfr if (cksum.checksum.data) 523178825Sdfr free(cksum.checksum.data); 524178825Sdfr if (sp) 525178825Sdfr krb5_storage_free(sp); 526178825Sdfr return ret; 527178825Sdfr} 528178825Sdfr 529178825Sdfrstatic krb5_error_code 530178825Sdfrcreate_checksum(krb5_context context, 531178825Sdfr const krb5_keyblock *key, 532233294Sstas uint32_t cksumtype, 533178825Sdfr void *data, size_t datalen, 534178825Sdfr void *sig, size_t siglen) 535178825Sdfr{ 536178825Sdfr krb5_crypto crypto = NULL; 537178825Sdfr krb5_error_code ret; 538178825Sdfr Checksum cksum; 539178825Sdfr 540233294Sstas /* If the checksum is HMAC-MD5, the checksum type is not tied to 541233294Sstas * the key type, instead the HMAC-MD5 checksum is applied blindly 542233294Sstas * on whatever key is used for this connection, avoiding issues 543233294Sstas * with unkeyed checksums on des-cbc-md5 and des-cbc-crc. See 544233294Sstas * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743 545233294Sstas * for the same issue in MIT, and 546233294Sstas * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx 547233294Sstas * for Microsoft's explaination */ 548178825Sdfr 549233294Sstas if (cksumtype == (uint32_t)CKSUMTYPE_HMAC_MD5) { 550233294Sstas ret = HMAC_MD5_any_checksum(context, key, data, datalen, 551233294Sstas KRB5_KU_OTHER_CKSUM, &cksum); 552233294Sstas } else { 553233294Sstas ret = krb5_crypto_init(context, key, 0, &crypto); 554233294Sstas if (ret) 555233294Sstas return ret; 556178825Sdfr 557233294Sstas ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, 558233294Sstas data, datalen, &cksum); 559233294Sstas krb5_crypto_destroy(context, crypto); 560233294Sstas if (ret) 561233294Sstas return ret; 562233294Sstas } 563178825Sdfr if (cksum.checksum.length != siglen) { 564233294Sstas krb5_set_error_message(context, EINVAL, "pac checksum wrong length"); 565178825Sdfr free_Checksum(&cksum); 566178825Sdfr return EINVAL; 567178825Sdfr } 568178825Sdfr 569178825Sdfr memcpy(sig, cksum.checksum.data, siglen); 570178825Sdfr free_Checksum(&cksum); 571178825Sdfr 572178825Sdfr return 0; 573178825Sdfr} 574178825Sdfr 575178825Sdfr 576178825Sdfr/* 577178825Sdfr * 578178825Sdfr */ 579178825Sdfr 580178825Sdfr#define NTTIME_EPOCH 0x019DB1DED53E8000LL 581178825Sdfr 582178825Sdfrstatic uint64_t 583178825Sdfrunix2nttime(time_t unix_time) 584178825Sdfr{ 585178825Sdfr long long wt; 586178825Sdfr wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH; 587178825Sdfr return wt; 588178825Sdfr} 589178825Sdfr 590178825Sdfrstatic krb5_error_code 591178825Sdfrverify_logonname(krb5_context context, 592178825Sdfr const struct PAC_INFO_BUFFER *logon_name, 593178825Sdfr const krb5_data *data, 594178825Sdfr time_t authtime, 595178825Sdfr krb5_const_principal principal) 596178825Sdfr{ 597178825Sdfr krb5_error_code ret; 598178825Sdfr krb5_principal p2; 599178825Sdfr uint32_t time1, time2; 600178825Sdfr krb5_storage *sp; 601178825Sdfr uint16_t len; 602178825Sdfr char *s; 603178825Sdfr 604178825Sdfr sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo, 605178825Sdfr logon_name->buffersize); 606233294Sstas if (sp == NULL) 607233294Sstas return krb5_enomem(context); 608178825Sdfr 609178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 610178825Sdfr 611178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &time1), out); 612178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &time2), out); 613178825Sdfr 614178825Sdfr { 615178825Sdfr uint64_t t1, t2; 616178825Sdfr t1 = unix2nttime(authtime); 617178825Sdfr t2 = ((uint64_t)time2 << 32) | time1; 618178825Sdfr if (t1 != t2) { 619178825Sdfr krb5_storage_free(sp); 620233294Sstas krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch"); 621178825Sdfr return EINVAL; 622178825Sdfr } 623178825Sdfr } 624178825Sdfr CHECK(ret, krb5_ret_uint16(sp, &len), out); 625178825Sdfr if (len == 0) { 626178825Sdfr krb5_storage_free(sp); 627233294Sstas krb5_set_error_message(context, EINVAL, "PAC logon name length missing"); 628178825Sdfr return EINVAL; 629178825Sdfr } 630178825Sdfr 631178825Sdfr s = malloc(len); 632178825Sdfr if (s == NULL) { 633178825Sdfr krb5_storage_free(sp); 634233294Sstas return krb5_enomem(context); 635178825Sdfr } 636178825Sdfr ret = krb5_storage_read(sp, s, len); 637178825Sdfr if (ret != len) { 638178825Sdfr krb5_storage_free(sp); 639233294Sstas krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name"); 640178825Sdfr return EINVAL; 641178825Sdfr } 642178825Sdfr krb5_storage_free(sp); 643178825Sdfr { 644233294Sstas size_t ucs2len = len / 2; 645178825Sdfr uint16_t *ucs2; 646178825Sdfr size_t u8len; 647233294Sstas unsigned int flags = WIND_RW_LE; 648178825Sdfr 649233294Sstas ucs2 = malloc(sizeof(ucs2[0]) * ucs2len); 650233294Sstas if (ucs2 == NULL) 651233294Sstas return krb5_enomem(context); 652233294Sstas 653233294Sstas ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len); 654178825Sdfr free(s); 655233294Sstas if (ret) { 656233294Sstas free(ucs2); 657233294Sstas krb5_set_error_message(context, ret, "Failed to convert string to UCS-2"); 658233294Sstas return ret; 659233294Sstas } 660233294Sstas ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len); 661233294Sstas if (ret) { 662233294Sstas free(ucs2); 663233294Sstas krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string"); 664233294Sstas return ret; 665233294Sstas } 666233294Sstas u8len += 1; /* Add space for NUL */ 667233294Sstas s = malloc(u8len); 668233294Sstas if (s == NULL) { 669233294Sstas free(ucs2); 670233294Sstas return krb5_enomem(context); 671233294Sstas } 672233294Sstas ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len); 673178825Sdfr free(ucs2); 674233294Sstas if (ret) { 675233294Sstas free(s); 676233294Sstas krb5_set_error_message(context, ret, "Failed to convert to UTF-8"); 677233294Sstas return ret; 678233294Sstas } 679178825Sdfr } 680178825Sdfr ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); 681178825Sdfr free(s); 682178825Sdfr if (ret) 683178825Sdfr return ret; 684233294Sstas 685178825Sdfr if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) { 686178825Sdfr ret = EINVAL; 687233294Sstas krb5_set_error_message(context, ret, "PAC logon name mismatch"); 688178825Sdfr } 689178825Sdfr krb5_free_principal(context, p2); 690178825Sdfr return ret; 691178825Sdfrout: 692178825Sdfr return ret; 693178825Sdfr} 694178825Sdfr 695178825Sdfr/* 696178825Sdfr * 697178825Sdfr */ 698178825Sdfr 699178825Sdfrstatic krb5_error_code 700233294Sstasbuild_logon_name(krb5_context context, 701178825Sdfr time_t authtime, 702233294Sstas krb5_const_principal principal, 703178825Sdfr krb5_data *logon) 704178825Sdfr{ 705178825Sdfr krb5_error_code ret; 706178825Sdfr krb5_storage *sp; 707178825Sdfr uint64_t t; 708178825Sdfr char *s, *s2; 709234027Sstas size_t s2_len; 710178825Sdfr 711178825Sdfr t = unix2nttime(authtime); 712178825Sdfr 713178825Sdfr krb5_data_zero(logon); 714178825Sdfr 715178825Sdfr sp = krb5_storage_emem(); 716233294Sstas if (sp == NULL) 717233294Sstas return krb5_enomem(context); 718233294Sstas 719178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 720178825Sdfr 721178825Sdfr CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out); 722178825Sdfr CHECK(ret, krb5_store_uint32(sp, t >> 32), out); 723178825Sdfr 724178825Sdfr ret = krb5_unparse_name_flags(context, principal, 725178825Sdfr KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s); 726178825Sdfr if (ret) 727178825Sdfr goto out; 728178825Sdfr 729234027Sstas { 730234027Sstas size_t ucs2_len; 731234027Sstas uint16_t *ucs2; 732234027Sstas unsigned int flags; 733233294Sstas 734234027Sstas ret = wind_utf8ucs2_length(s, &ucs2_len); 735234027Sstas if (ret) { 736234027Sstas free(s); 737234027Sstas krb5_set_error_message(context, ret, "Failed to count length of UTF-8 string"); 738234027Sstas return ret; 739234027Sstas } 740178825Sdfr 741234027Sstas ucs2 = malloc(sizeof(ucs2[0]) * ucs2_len); 742234027Sstas if (ucs2 == NULL) { 743234027Sstas free(s); 744234027Sstas return krb5_enomem(context); 745234027Sstas } 746234027Sstas 747234027Sstas ret = wind_utf8ucs2(s, ucs2, &ucs2_len); 748178825Sdfr free(s); 749234027Sstas if (ret) { 750234027Sstas free(ucs2); 751234027Sstas krb5_set_error_message(context, ret, "Failed to convert string to UCS-2"); 752234027Sstas return ret; 753234027Sstas } 754234027Sstas 755234027Sstas s2_len = (ucs2_len + 1) * 2; 756234027Sstas s2 = malloc(s2_len); 757234027Sstas if (ucs2 == NULL) { 758234027Sstas free(ucs2); 759234027Sstas return krb5_enomem(context); 760234027Sstas } 761234027Sstas 762234027Sstas flags = WIND_RW_LE; 763234027Sstas ret = wind_ucs2write(ucs2, ucs2_len, 764234027Sstas &flags, s2, &s2_len); 765234027Sstas free(ucs2); 766234027Sstas if (ret) { 767234027Sstas free(s2); 768234027Sstas krb5_set_error_message(context, ret, "Failed to write to UCS-2 buffer"); 769234027Sstas return ret; 770234027Sstas } 771234027Sstas 772234027Sstas /* 773234027Sstas * we do not want zero termination 774234027Sstas */ 775234027Sstas s2_len = ucs2_len * 2; 776178825Sdfr } 777178825Sdfr 778234027Sstas CHECK(ret, krb5_store_uint16(sp, s2_len), out); 779234027Sstas 780234027Sstas ret = krb5_storage_write(sp, s2, s2_len); 781178825Sdfr free(s2); 782234027Sstas if (ret != (int)s2_len) { 783233294Sstas ret = krb5_enomem(context); 784178825Sdfr goto out; 785178825Sdfr } 786178825Sdfr ret = krb5_storage_to_data(sp, logon); 787178825Sdfr if (ret) 788178825Sdfr goto out; 789178825Sdfr krb5_storage_free(sp); 790178825Sdfr 791178825Sdfr return 0; 792178825Sdfrout: 793178825Sdfr krb5_storage_free(sp); 794178825Sdfr return ret; 795178825Sdfr} 796178825Sdfr 797178825Sdfr 798233294Sstas/** 799233294Sstas * Verify the PAC. 800178825Sdfr * 801233294Sstas * @param context Kerberos 5 context. 802233294Sstas * @param pac the pac structure returned by krb5_pac_parse(). 803233294Sstas * @param authtime The time of the ticket the PAC belongs to. 804233294Sstas * @param principal the principal to verify. 805233294Sstas * @param server The service key, most always be given. 806233294Sstas * @param privsvr The KDC key, may be given. 807233294Sstas 808233294Sstas * @return Returns 0 to indicate success. Otherwise an kerberos et 809233294Sstas * error code is returned, see krb5_get_error_message(). 810233294Sstas * 811233294Sstas * @ingroup krb5_pac 812178825Sdfr */ 813178825Sdfr 814233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 815233294Sstaskrb5_pac_verify(krb5_context context, 816178825Sdfr const krb5_pac pac, 817178825Sdfr time_t authtime, 818178825Sdfr krb5_const_principal principal, 819178825Sdfr const krb5_keyblock *server, 820178825Sdfr const krb5_keyblock *privsvr) 821178825Sdfr{ 822178825Sdfr krb5_error_code ret; 823178825Sdfr 824178825Sdfr if (pac->server_checksum == NULL) { 825233294Sstas krb5_set_error_message(context, EINVAL, "PAC missing server checksum"); 826178825Sdfr return EINVAL; 827178825Sdfr } 828178825Sdfr if (pac->privsvr_checksum == NULL) { 829233294Sstas krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum"); 830178825Sdfr return EINVAL; 831178825Sdfr } 832178825Sdfr if (pac->logon_name == NULL) { 833233294Sstas krb5_set_error_message(context, EINVAL, "PAC missing logon name"); 834178825Sdfr return EINVAL; 835178825Sdfr } 836178825Sdfr 837233294Sstas ret = verify_logonname(context, 838178825Sdfr pac->logon_name, 839178825Sdfr &pac->data, 840178825Sdfr authtime, 841178825Sdfr principal); 842178825Sdfr if (ret) 843178825Sdfr return ret; 844178825Sdfr 845233294Sstas /* 846178825Sdfr * in the service case, clean out data option of the privsvr and 847178825Sdfr * server checksum before checking the checksum. 848178825Sdfr */ 849178825Sdfr { 850178825Sdfr krb5_data *copy; 851178825Sdfr 852178825Sdfr ret = krb5_copy_data(context, &pac->data, ©); 853178825Sdfr if (ret) 854178825Sdfr return ret; 855178825Sdfr 856178825Sdfr if (pac->server_checksum->buffersize < 4) 857178825Sdfr return EINVAL; 858178825Sdfr if (pac->privsvr_checksum->buffersize < 4) 859178825Sdfr return EINVAL; 860178825Sdfr 861178825Sdfr memset((char *)copy->data + pac->server_checksum->offset_lo + 4, 862178825Sdfr 0, 863178825Sdfr pac->server_checksum->buffersize - 4); 864178825Sdfr 865178825Sdfr memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4, 866178825Sdfr 0, 867178825Sdfr pac->privsvr_checksum->buffersize - 4); 868178825Sdfr 869178825Sdfr ret = verify_checksum(context, 870178825Sdfr pac->server_checksum, 871178825Sdfr &pac->data, 872178825Sdfr copy->data, 873178825Sdfr copy->length, 874178825Sdfr server); 875178825Sdfr krb5_free_data(context, copy); 876178825Sdfr if (ret) 877178825Sdfr return ret; 878178825Sdfr } 879178825Sdfr if (privsvr) { 880233294Sstas /* The priv checksum covers the server checksum */ 881178825Sdfr ret = verify_checksum(context, 882178825Sdfr pac->privsvr_checksum, 883178825Sdfr &pac->data, 884178825Sdfr (char *)pac->data.data 885178825Sdfr + pac->server_checksum->offset_lo + 4, 886178825Sdfr pac->server_checksum->buffersize - 4, 887178825Sdfr privsvr); 888178825Sdfr if (ret) 889178825Sdfr return ret; 890178825Sdfr } 891178825Sdfr 892178825Sdfr return 0; 893178825Sdfr} 894178825Sdfr 895178825Sdfr/* 896178825Sdfr * 897178825Sdfr */ 898178825Sdfr 899178825Sdfrstatic krb5_error_code 900178825Sdfrfill_zeros(krb5_context context, krb5_storage *sp, size_t len) 901178825Sdfr{ 902178825Sdfr ssize_t sret; 903178825Sdfr size_t l; 904178825Sdfr 905178825Sdfr while (len) { 906178825Sdfr l = len; 907178825Sdfr if (l > sizeof(zeros)) 908178825Sdfr l = sizeof(zeros); 909178825Sdfr sret = krb5_storage_write(sp, zeros, l); 910233294Sstas if (sret <= 0) 911233294Sstas return krb5_enomem(context); 912233294Sstas 913178825Sdfr len -= sret; 914178825Sdfr } 915178825Sdfr return 0; 916178825Sdfr} 917178825Sdfr 918178825Sdfrstatic krb5_error_code 919233294Sstaspac_checksum(krb5_context context, 920178825Sdfr const krb5_keyblock *key, 921178825Sdfr uint32_t *cksumtype, 922178825Sdfr size_t *cksumsize) 923178825Sdfr{ 924178825Sdfr krb5_cksumtype cktype; 925178825Sdfr krb5_error_code ret; 926178825Sdfr krb5_crypto crypto = NULL; 927178825Sdfr 928178825Sdfr ret = krb5_crypto_init(context, key, 0, &crypto); 929178825Sdfr if (ret) 930178825Sdfr return ret; 931178825Sdfr 932178825Sdfr ret = krb5_crypto_get_checksum_type(context, crypto, &cktype); 933233294Sstas krb5_crypto_destroy(context, crypto); 934178825Sdfr if (ret) 935178825Sdfr return ret; 936178825Sdfr 937178825Sdfr if (krb5_checksum_is_keyed(context, cktype) == FALSE) { 938233294Sstas *cksumtype = CKSUMTYPE_HMAC_MD5; 939233294Sstas *cksumsize = 16; 940178825Sdfr } 941178825Sdfr 942178825Sdfr ret = krb5_checksumsize(context, cktype, cksumsize); 943178825Sdfr if (ret) 944178825Sdfr return ret; 945233294Sstas 946178825Sdfr *cksumtype = (uint32_t)cktype; 947178825Sdfr 948178825Sdfr return 0; 949178825Sdfr} 950178825Sdfr 951178825Sdfrkrb5_error_code 952178825Sdfr_krb5_pac_sign(krb5_context context, 953178825Sdfr krb5_pac p, 954178825Sdfr time_t authtime, 955178825Sdfr krb5_principal principal, 956178825Sdfr const krb5_keyblock *server_key, 957178825Sdfr const krb5_keyblock *priv_key, 958178825Sdfr krb5_data *data) 959178825Sdfr{ 960178825Sdfr krb5_error_code ret; 961178825Sdfr krb5_storage *sp = NULL, *spdata = NULL; 962178825Sdfr uint32_t end; 963178825Sdfr size_t server_size, priv_size; 964178825Sdfr uint32_t server_offset = 0, priv_offset = 0; 965178825Sdfr uint32_t server_cksumtype = 0, priv_cksumtype = 0; 966233294Sstas int num = 0; 967233294Sstas size_t i; 968178825Sdfr krb5_data logon, d; 969178825Sdfr 970178825Sdfr krb5_data_zero(&logon); 971178825Sdfr 972178825Sdfr if (p->logon_name == NULL) 973178825Sdfr num++; 974178825Sdfr if (p->server_checksum == NULL) 975178825Sdfr num++; 976178825Sdfr if (p->privsvr_checksum == NULL) 977178825Sdfr num++; 978178825Sdfr 979178825Sdfr if (num) { 980178825Sdfr void *ptr; 981178825Sdfr 982178825Sdfr ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1))); 983233294Sstas if (ptr == NULL) 984233294Sstas return krb5_enomem(context); 985233294Sstas 986178825Sdfr p->pac = ptr; 987178825Sdfr 988178825Sdfr if (p->logon_name == NULL) { 989178825Sdfr p->logon_name = &p->pac->buffers[p->pac->numbuffers++]; 990178825Sdfr memset(p->logon_name, 0, sizeof(*p->logon_name)); 991178825Sdfr p->logon_name->type = PAC_LOGON_NAME; 992178825Sdfr } 993178825Sdfr if (p->server_checksum == NULL) { 994178825Sdfr p->server_checksum = &p->pac->buffers[p->pac->numbuffers++]; 995178825Sdfr memset(p->server_checksum, 0, sizeof(*p->server_checksum)); 996178825Sdfr p->server_checksum->type = PAC_SERVER_CHECKSUM; 997178825Sdfr } 998178825Sdfr if (p->privsvr_checksum == NULL) { 999178825Sdfr p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++]; 1000178825Sdfr memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum)); 1001178825Sdfr p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM; 1002178825Sdfr } 1003178825Sdfr } 1004178825Sdfr 1005178825Sdfr /* Calculate LOGON NAME */ 1006178825Sdfr ret = build_logon_name(context, authtime, principal, &logon); 1007178825Sdfr if (ret) 1008178825Sdfr goto out; 1009178825Sdfr 1010178825Sdfr /* Set lengths for checksum */ 1011178825Sdfr ret = pac_checksum(context, server_key, &server_cksumtype, &server_size); 1012178825Sdfr if (ret) 1013178825Sdfr goto out; 1014178825Sdfr ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); 1015178825Sdfr if (ret) 1016178825Sdfr goto out; 1017178825Sdfr 1018178825Sdfr /* Encode PAC */ 1019178825Sdfr sp = krb5_storage_emem(); 1020233294Sstas if (sp == NULL) 1021233294Sstas return krb5_enomem(context); 1022233294Sstas 1023178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 1024178825Sdfr 1025178825Sdfr spdata = krb5_storage_emem(); 1026178825Sdfr if (spdata == NULL) { 1027178825Sdfr krb5_storage_free(sp); 1028233294Sstas return krb5_enomem(context); 1029178825Sdfr } 1030178825Sdfr krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE); 1031178825Sdfr 1032178825Sdfr CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out); 1033178825Sdfr CHECK(ret, krb5_store_uint32(sp, p->pac->version), out); 1034178825Sdfr 1035178825Sdfr end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); 1036178825Sdfr 1037178825Sdfr for (i = 0; i < p->pac->numbuffers; i++) { 1038178825Sdfr uint32_t len; 1039178825Sdfr size_t sret; 1040178825Sdfr void *ptr = NULL; 1041178825Sdfr 1042178825Sdfr /* store data */ 1043178825Sdfr 1044178825Sdfr if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { 1045178825Sdfr len = server_size + 4; 1046178825Sdfr server_offset = end + 4; 1047178825Sdfr CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out); 1048178825Sdfr CHECK(ret, fill_zeros(context, spdata, server_size), out); 1049178825Sdfr } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { 1050178825Sdfr len = priv_size + 4; 1051178825Sdfr priv_offset = end + 4; 1052178825Sdfr CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out); 1053178825Sdfr CHECK(ret, fill_zeros(context, spdata, priv_size), out); 1054178825Sdfr } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { 1055178825Sdfr len = krb5_storage_write(spdata, logon.data, logon.length); 1056178825Sdfr if (logon.length != len) { 1057178825Sdfr ret = EINVAL; 1058178825Sdfr goto out; 1059178825Sdfr } 1060178825Sdfr } else { 1061178825Sdfr len = p->pac->buffers[i].buffersize; 1062178825Sdfr ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo; 1063178825Sdfr 1064178825Sdfr sret = krb5_storage_write(spdata, ptr, len); 1065178825Sdfr if (sret != len) { 1066233294Sstas ret = krb5_enomem(context); 1067178825Sdfr goto out; 1068178825Sdfr } 1069178825Sdfr /* XXX if not aligned, fill_zeros */ 1070178825Sdfr } 1071178825Sdfr 1072178825Sdfr /* write header */ 1073178825Sdfr CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out); 1074178825Sdfr CHECK(ret, krb5_store_uint32(sp, len), out); 1075178825Sdfr CHECK(ret, krb5_store_uint32(sp, end), out); 1076178825Sdfr CHECK(ret, krb5_store_uint32(sp, 0), out); 1077178825Sdfr 1078178825Sdfr /* advance data endpointer and align */ 1079178825Sdfr { 1080178825Sdfr int32_t e; 1081178825Sdfr 1082178825Sdfr end += len; 1083178825Sdfr e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; 1084233294Sstas if ((int32_t)end != e) { 1085178825Sdfr CHECK(ret, fill_zeros(context, spdata, e - end), out); 1086178825Sdfr } 1087178825Sdfr end = e; 1088178825Sdfr } 1089178825Sdfr 1090178825Sdfr } 1091178825Sdfr 1092178825Sdfr /* assert (server_offset != 0 && priv_offset != 0); */ 1093178825Sdfr 1094178825Sdfr /* export PAC */ 1095178825Sdfr ret = krb5_storage_to_data(spdata, &d); 1096178825Sdfr if (ret) { 1097233294Sstas krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 1098178825Sdfr goto out; 1099178825Sdfr } 1100178825Sdfr ret = krb5_storage_write(sp, d.data, d.length); 1101233294Sstas if (ret != (int)d.length) { 1102178825Sdfr krb5_data_free(&d); 1103233294Sstas ret = krb5_enomem(context); 1104178825Sdfr goto out; 1105178825Sdfr } 1106178825Sdfr krb5_data_free(&d); 1107178825Sdfr 1108178825Sdfr ret = krb5_storage_to_data(sp, &d); 1109178825Sdfr if (ret) { 1110233294Sstas ret = krb5_enomem(context); 1111178825Sdfr goto out; 1112178825Sdfr } 1113178825Sdfr 1114178825Sdfr /* sign */ 1115233294Sstas ret = create_checksum(context, server_key, server_cksumtype, 1116178825Sdfr d.data, d.length, 1117178825Sdfr (char *)d.data + server_offset, server_size); 1118178825Sdfr if (ret) { 1119178825Sdfr krb5_data_free(&d); 1120178825Sdfr goto out; 1121178825Sdfr } 1122233294Sstas ret = create_checksum(context, priv_key, priv_cksumtype, 1123178825Sdfr (char *)d.data + server_offset, server_size, 1124178825Sdfr (char *)d.data + priv_offset, priv_size); 1125178825Sdfr if (ret) { 1126178825Sdfr krb5_data_free(&d); 1127178825Sdfr goto out; 1128178825Sdfr } 1129178825Sdfr 1130178825Sdfr /* done */ 1131178825Sdfr *data = d; 1132178825Sdfr 1133178825Sdfr krb5_data_free(&logon); 1134178825Sdfr krb5_storage_free(sp); 1135178825Sdfr krb5_storage_free(spdata); 1136178825Sdfr 1137178825Sdfr return 0; 1138178825Sdfrout: 1139178825Sdfr krb5_data_free(&logon); 1140178825Sdfr if (sp) 1141178825Sdfr krb5_storage_free(sp); 1142178825Sdfr if (spdata) 1143178825Sdfr krb5_storage_free(spdata); 1144178825Sdfr return ret; 1145178825Sdfr} 1146