1178825Sdfr/* 2178825Sdfr * Copyright (c) 2006 - 2007 Kungliga Tekniska H�gskolan 3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden). 4178825Sdfr * All rights reserved. 5178825Sdfr * 6178825Sdfr * Redistribution and use in source and binary forms, with or without 7178825Sdfr * modification, are permitted provided that the following conditions 8178825Sdfr * are met: 9178825Sdfr * 10178825Sdfr * 1. Redistributions of source code must retain the above copyright 11178825Sdfr * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright 14178825Sdfr * notice, this list of conditions and the following disclaimer in the 15178825Sdfr * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors 18178825Sdfr * may be used to endorse or promote products derived from this software 19178825Sdfr * without specific prior written permission. 20178825Sdfr * 21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24178825Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31178825Sdfr * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34178825Sdfr#include "krb5_locl.h" 35178825Sdfr 36178825SdfrRCSID("$Id: pac.c 21934 2007-08-27 14:21:04Z lha $"); 37178825Sdfr 38178825Sdfrstruct PAC_INFO_BUFFER { 39178825Sdfr uint32_t type; 40178825Sdfr uint32_t buffersize; 41178825Sdfr uint32_t offset_hi; 42178825Sdfr uint32_t offset_lo; 43178825Sdfr}; 44178825Sdfr 45178825Sdfrstruct PACTYPE { 46178825Sdfr uint32_t numbuffers; 47178825Sdfr uint32_t version; 48178825Sdfr struct PAC_INFO_BUFFER buffers[1]; 49178825Sdfr}; 50178825Sdfr 51178825Sdfrstruct krb5_pac_data { 52178825Sdfr struct PACTYPE *pac; 53178825Sdfr krb5_data data; 54178825Sdfr struct PAC_INFO_BUFFER *server_checksum; 55178825Sdfr struct PAC_INFO_BUFFER *privsvr_checksum; 56178825Sdfr struct PAC_INFO_BUFFER *logon_name; 57178825Sdfr}; 58178825Sdfr 59178825Sdfr#define PAC_ALIGNMENT 8 60178825Sdfr 61178825Sdfr#define PACTYPE_SIZE 8 62178825Sdfr#define PAC_INFO_BUFFER_SIZE 16 63178825Sdfr 64178825Sdfr#define PAC_SERVER_CHECKSUM 6 65178825Sdfr#define PAC_PRIVSVR_CHECKSUM 7 66178825Sdfr#define PAC_LOGON_NAME 10 67178825Sdfr#define PAC_CONSTRAINED_DELEGATION 11 68178825Sdfr 69178825Sdfr#define CHECK(r,f,l) \ 70178825Sdfr do { \ 71178825Sdfr if (((r) = f ) != 0) { \ 72178825Sdfr krb5_clear_error_string(context); \ 73178825Sdfr goto l; \ 74178825Sdfr } \ 75178825Sdfr } while(0) 76178825Sdfr 77178825Sdfrstatic const char zeros[PAC_ALIGNMENT] = { 0 }; 78178825Sdfr 79178825Sdfr/* 80178825Sdfr * 81178825Sdfr */ 82178825Sdfr 83178825Sdfrkrb5_error_code 84178825Sdfrkrb5_pac_parse(krb5_context context, const void *ptr, size_t len, 85178825Sdfr krb5_pac *pac) 86178825Sdfr{ 87178825Sdfr krb5_error_code ret; 88178825Sdfr krb5_pac p; 89178825Sdfr krb5_storage *sp = NULL; 90178825Sdfr uint32_t i, tmp, tmp2, header_end; 91178825Sdfr 92178825Sdfr p = calloc(1, sizeof(*p)); 93178825Sdfr if (p == NULL) { 94178825Sdfr ret = ENOMEM; 95178825Sdfr krb5_set_error_string(context, "out of memory"); 96178825Sdfr goto out; 97178825Sdfr } 98178825Sdfr 99178825Sdfr sp = krb5_storage_from_readonly_mem(ptr, len); 100178825Sdfr if (sp == NULL) { 101178825Sdfr ret = ENOMEM; 102178825Sdfr krb5_set_error_string(context, "out of memory"); 103178825Sdfr goto out; 104178825Sdfr } 105178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 106178825Sdfr 107178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &tmp), out); 108178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &tmp2), out); 109178825Sdfr if (tmp < 1) { 110178825Sdfr krb5_set_error_string(context, "PAC have too few buffer"); 111178825Sdfr ret = EINVAL; /* Too few buffers */ 112178825Sdfr goto out; 113178825Sdfr } 114178825Sdfr if (tmp2 != 0) { 115178825Sdfr krb5_set_error_string(context, "PAC have wrong version"); 116178825Sdfr ret = EINVAL; /* Wrong version */ 117178825Sdfr goto out; 118178825Sdfr } 119178825Sdfr 120178825Sdfr p->pac = calloc(1, 121178825Sdfr sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1))); 122178825Sdfr if (p->pac == NULL) { 123178825Sdfr krb5_set_error_string(context, "out of memory"); 124178825Sdfr ret = ENOMEM; 125178825Sdfr goto out; 126178825Sdfr } 127178825Sdfr 128178825Sdfr p->pac->numbuffers = tmp; 129178825Sdfr p->pac->version = tmp2; 130178825Sdfr 131178825Sdfr header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); 132178825Sdfr if (header_end > len) { 133178825Sdfr ret = EINVAL; 134178825Sdfr goto out; 135178825Sdfr } 136178825Sdfr 137178825Sdfr for (i = 0; i < p->pac->numbuffers; i++) { 138178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out); 139178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out); 140178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out); 141178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out); 142178825Sdfr 143178825Sdfr /* consistency checks */ 144178825Sdfr if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) { 145178825Sdfr krb5_set_error_string(context, "PAC out of allignment"); 146178825Sdfr ret = EINVAL; 147178825Sdfr goto out; 148178825Sdfr } 149178825Sdfr if (p->pac->buffers[i].offset_hi) { 150178825Sdfr krb5_set_error_string(context, "PAC high offset set"); 151178825Sdfr ret = EINVAL; 152178825Sdfr goto out; 153178825Sdfr } 154178825Sdfr if (p->pac->buffers[i].offset_lo > len) { 155178825Sdfr krb5_set_error_string(context, "PAC offset off end"); 156178825Sdfr ret = EINVAL; 157178825Sdfr goto out; 158178825Sdfr } 159178825Sdfr if (p->pac->buffers[i].offset_lo < header_end) { 160178825Sdfr krb5_set_error_string(context, "PAC offset inside header: %d %d", 161178825Sdfr p->pac->buffers[i].offset_lo, header_end); 162178825Sdfr ret = EINVAL; 163178825Sdfr goto out; 164178825Sdfr } 165178825Sdfr if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){ 166178825Sdfr krb5_set_error_string(context, "PAC length off end"); 167178825Sdfr ret = EINVAL; 168178825Sdfr goto out; 169178825Sdfr } 170178825Sdfr 171178825Sdfr /* let save pointer to data we need later */ 172178825Sdfr if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { 173178825Sdfr if (p->server_checksum) { 174178825Sdfr krb5_set_error_string(context, "PAC have two server checksums"); 175178825Sdfr ret = EINVAL; 176178825Sdfr goto out; 177178825Sdfr } 178178825Sdfr p->server_checksum = &p->pac->buffers[i]; 179178825Sdfr } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { 180178825Sdfr if (p->privsvr_checksum) { 181178825Sdfr krb5_set_error_string(context, "PAC have two KDC checksums"); 182178825Sdfr ret = EINVAL; 183178825Sdfr goto out; 184178825Sdfr } 185178825Sdfr p->privsvr_checksum = &p->pac->buffers[i]; 186178825Sdfr } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { 187178825Sdfr if (p->logon_name) { 188178825Sdfr krb5_set_error_string(context, "PAC have two logon names"); 189178825Sdfr ret = EINVAL; 190178825Sdfr goto out; 191178825Sdfr } 192178825Sdfr p->logon_name = &p->pac->buffers[i]; 193178825Sdfr } 194178825Sdfr } 195178825Sdfr 196178825Sdfr ret = krb5_data_copy(&p->data, ptr, len); 197178825Sdfr if (ret) 198178825Sdfr goto out; 199178825Sdfr 200178825Sdfr krb5_storage_free(sp); 201178825Sdfr 202178825Sdfr *pac = p; 203178825Sdfr return 0; 204178825Sdfr 205178825Sdfrout: 206178825Sdfr if (sp) 207178825Sdfr krb5_storage_free(sp); 208178825Sdfr if (p) { 209178825Sdfr if (p->pac) 210178825Sdfr free(p->pac); 211178825Sdfr free(p); 212178825Sdfr } 213178825Sdfr *pac = NULL; 214178825Sdfr 215178825Sdfr return ret; 216178825Sdfr} 217178825Sdfr 218178825Sdfrkrb5_error_code 219178825Sdfrkrb5_pac_init(krb5_context context, krb5_pac *pac) 220178825Sdfr{ 221178825Sdfr krb5_error_code ret; 222178825Sdfr krb5_pac p; 223178825Sdfr 224178825Sdfr p = calloc(1, sizeof(*p)); 225178825Sdfr if (p == NULL) { 226178825Sdfr krb5_set_error_string(context, "out of memory"); 227178825Sdfr return ENOMEM; 228178825Sdfr } 229178825Sdfr 230178825Sdfr p->pac = calloc(1, sizeof(*p->pac)); 231178825Sdfr if (p->pac == NULL) { 232178825Sdfr free(p); 233178825Sdfr krb5_set_error_string(context, "out of memory"); 234178825Sdfr return ENOMEM; 235178825Sdfr } 236178825Sdfr 237178825Sdfr ret = krb5_data_alloc(&p->data, PACTYPE_SIZE); 238178825Sdfr if (ret) { 239178825Sdfr free (p->pac); 240178825Sdfr free(p); 241178825Sdfr krb5_set_error_string(context, "out of memory"); 242178825Sdfr return ret; 243178825Sdfr } 244178825Sdfr 245178825Sdfr 246178825Sdfr *pac = p; 247178825Sdfr return 0; 248178825Sdfr} 249178825Sdfr 250178825Sdfrkrb5_error_code 251178825Sdfrkrb5_pac_add_buffer(krb5_context context, krb5_pac p, 252178825Sdfr uint32_t type, const krb5_data *data) 253178825Sdfr{ 254178825Sdfr krb5_error_code ret; 255178825Sdfr void *ptr; 256178825Sdfr size_t len, offset, header_end, old_end; 257178825Sdfr uint32_t i; 258178825Sdfr 259178825Sdfr len = p->pac->numbuffers; 260178825Sdfr 261178825Sdfr ptr = realloc(p->pac, 262178825Sdfr sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len)); 263178825Sdfr if (ptr == NULL) { 264178825Sdfr krb5_set_error_string(context, "out of memory"); 265178825Sdfr return ENOMEM; 266178825Sdfr } 267178825Sdfr p->pac = ptr; 268178825Sdfr 269178825Sdfr for (i = 0; i < len; i++) 270178825Sdfr p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE; 271178825Sdfr 272178825Sdfr offset = p->data.length + PAC_INFO_BUFFER_SIZE; 273178825Sdfr 274178825Sdfr p->pac->buffers[len].type = type; 275178825Sdfr p->pac->buffers[len].buffersize = data->length; 276178825Sdfr p->pac->buffers[len].offset_lo = offset; 277178825Sdfr p->pac->buffers[len].offset_hi = 0; 278178825Sdfr 279178825Sdfr old_end = p->data.length; 280178825Sdfr len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE; 281178825Sdfr if (len < p->data.length) { 282178825Sdfr krb5_set_error_string(context, "integer overrun"); 283178825Sdfr return EINVAL; 284178825Sdfr } 285178825Sdfr 286178825Sdfr /* align to PAC_ALIGNMENT */ 287178825Sdfr len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; 288178825Sdfr 289178825Sdfr ret = krb5_data_realloc(&p->data, len); 290178825Sdfr if (ret) { 291178825Sdfr krb5_set_error_string(context, "out of memory"); 292178825Sdfr return ret; 293178825Sdfr } 294178825Sdfr 295178825Sdfr /* 296178825Sdfr * make place for new PAC INFO BUFFER header 297178825Sdfr */ 298178825Sdfr header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); 299178825Sdfr memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE, 300178825Sdfr (unsigned char *)p->data.data + header_end , 301178825Sdfr old_end - header_end); 302178825Sdfr memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE); 303178825Sdfr 304178825Sdfr /* 305178825Sdfr * copy in new data part 306178825Sdfr */ 307178825Sdfr 308178825Sdfr memcpy((unsigned char *)p->data.data + offset, 309178825Sdfr data->data, data->length); 310178825Sdfr memset((unsigned char *)p->data.data + offset + data->length, 311178825Sdfr 0, p->data.length - offset - data->length); 312178825Sdfr 313178825Sdfr p->pac->numbuffers += 1; 314178825Sdfr 315178825Sdfr return 0; 316178825Sdfr} 317178825Sdfr 318178825Sdfrkrb5_error_code 319178825Sdfrkrb5_pac_get_buffer(krb5_context context, krb5_pac p, 320178825Sdfr uint32_t type, krb5_data *data) 321178825Sdfr{ 322178825Sdfr krb5_error_code ret; 323178825Sdfr uint32_t i; 324178825Sdfr 325178825Sdfr /* 326178825Sdfr * Hide the checksums from external consumers 327178825Sdfr */ 328178825Sdfr 329178825Sdfr if (type == PAC_PRIVSVR_CHECKSUM || type == PAC_SERVER_CHECKSUM) { 330178825Sdfr ret = krb5_data_alloc(data, 16); 331178825Sdfr if (ret) { 332178825Sdfr krb5_set_error_string(context, "out of memory"); 333178825Sdfr return ret; 334178825Sdfr } 335178825Sdfr memset(data->data, 0, data->length); 336178825Sdfr return 0; 337178825Sdfr } 338178825Sdfr 339178825Sdfr for (i = 0; i < p->pac->numbuffers; i++) { 340178825Sdfr size_t len = p->pac->buffers[i].buffersize; 341178825Sdfr size_t offset = p->pac->buffers[i].offset_lo; 342178825Sdfr 343178825Sdfr if (p->pac->buffers[i].type != type) 344178825Sdfr continue; 345178825Sdfr 346178825Sdfr ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len); 347178825Sdfr if (ret) { 348178825Sdfr krb5_set_error_string(context, "Out of memory"); 349178825Sdfr return ret; 350178825Sdfr } 351178825Sdfr return 0; 352178825Sdfr } 353178825Sdfr krb5_set_error_string(context, "No PAC buffer of type %lu was found", 354178825Sdfr (unsigned long)type); 355178825Sdfr return ENOENT; 356178825Sdfr} 357178825Sdfr 358178825Sdfr/* 359178825Sdfr * 360178825Sdfr */ 361178825Sdfr 362178825Sdfrkrb5_error_code 363178825Sdfrkrb5_pac_get_types(krb5_context context, 364178825Sdfr krb5_pac p, 365178825Sdfr size_t *len, 366178825Sdfr uint32_t **types) 367178825Sdfr{ 368178825Sdfr size_t i; 369178825Sdfr 370178825Sdfr *types = calloc(p->pac->numbuffers, sizeof(*types)); 371178825Sdfr if (*types == NULL) { 372178825Sdfr *len = 0; 373178825Sdfr krb5_set_error_string(context, "out of memory"); 374178825Sdfr return ENOMEM; 375178825Sdfr } 376178825Sdfr for (i = 0; i < p->pac->numbuffers; i++) 377178825Sdfr (*types)[i] = p->pac->buffers[i].type; 378178825Sdfr *len = p->pac->numbuffers; 379178825Sdfr 380178825Sdfr return 0; 381178825Sdfr} 382178825Sdfr 383178825Sdfr/* 384178825Sdfr * 385178825Sdfr */ 386178825Sdfr 387178825Sdfrvoid 388178825Sdfrkrb5_pac_free(krb5_context context, krb5_pac pac) 389178825Sdfr{ 390178825Sdfr krb5_data_free(&pac->data); 391178825Sdfr free(pac->pac); 392178825Sdfr free(pac); 393178825Sdfr} 394178825Sdfr 395178825Sdfr/* 396178825Sdfr * 397178825Sdfr */ 398178825Sdfr 399178825Sdfrstatic krb5_error_code 400178825Sdfrverify_checksum(krb5_context context, 401178825Sdfr const struct PAC_INFO_BUFFER *sig, 402178825Sdfr const krb5_data *data, 403178825Sdfr void *ptr, size_t len, 404178825Sdfr const krb5_keyblock *key) 405178825Sdfr{ 406178825Sdfr krb5_crypto crypto = NULL; 407178825Sdfr krb5_storage *sp = NULL; 408178825Sdfr uint32_t type; 409178825Sdfr krb5_error_code ret; 410178825Sdfr Checksum cksum; 411178825Sdfr 412178825Sdfr memset(&cksum, 0, sizeof(cksum)); 413178825Sdfr 414178825Sdfr sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo, 415178825Sdfr sig->buffersize); 416178825Sdfr if (sp == NULL) { 417178825Sdfr krb5_set_error_string(context, "out of memory"); 418178825Sdfr return ENOMEM; 419178825Sdfr } 420178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 421178825Sdfr 422178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &type), out); 423178825Sdfr cksum.cksumtype = type; 424178825Sdfr cksum.checksum.length = 425178825Sdfr sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR); 426178825Sdfr cksum.checksum.data = malloc(cksum.checksum.length); 427178825Sdfr if (cksum.checksum.data == NULL) { 428178825Sdfr krb5_set_error_string(context, "out of memory"); 429178825Sdfr ret = ENOMEM; 430178825Sdfr goto out; 431178825Sdfr } 432178825Sdfr ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length); 433178825Sdfr if (ret != cksum.checksum.length) { 434178825Sdfr krb5_set_error_string(context, "PAC checksum missing checksum"); 435178825Sdfr ret = EINVAL; 436178825Sdfr goto out; 437178825Sdfr } 438178825Sdfr 439178825Sdfr if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) { 440178825Sdfr krb5_set_error_string (context, "Checksum type %d not keyed", 441178825Sdfr cksum.cksumtype); 442178825Sdfr ret = EINVAL; 443178825Sdfr goto out; 444178825Sdfr } 445178825Sdfr 446178825Sdfr ret = krb5_crypto_init(context, key, 0, &crypto); 447178825Sdfr if (ret) 448178825Sdfr goto out; 449178825Sdfr 450178825Sdfr ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 451178825Sdfr ptr, len, &cksum); 452178825Sdfr free(cksum.checksum.data); 453178825Sdfr krb5_crypto_destroy(context, crypto); 454178825Sdfr krb5_storage_free(sp); 455178825Sdfr 456178825Sdfr return ret; 457178825Sdfr 458178825Sdfrout: 459178825Sdfr if (cksum.checksum.data) 460178825Sdfr free(cksum.checksum.data); 461178825Sdfr if (sp) 462178825Sdfr krb5_storage_free(sp); 463178825Sdfr if (crypto) 464178825Sdfr krb5_crypto_destroy(context, crypto); 465178825Sdfr return ret; 466178825Sdfr} 467178825Sdfr 468178825Sdfrstatic krb5_error_code 469178825Sdfrcreate_checksum(krb5_context context, 470178825Sdfr const krb5_keyblock *key, 471178825Sdfr void *data, size_t datalen, 472178825Sdfr void *sig, size_t siglen) 473178825Sdfr{ 474178825Sdfr krb5_crypto crypto = NULL; 475178825Sdfr krb5_error_code ret; 476178825Sdfr Checksum cksum; 477178825Sdfr 478178825Sdfr ret = krb5_crypto_init(context, key, 0, &crypto); 479178825Sdfr if (ret) 480178825Sdfr return ret; 481178825Sdfr 482178825Sdfr ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, 483178825Sdfr data, datalen, &cksum); 484178825Sdfr krb5_crypto_destroy(context, crypto); 485178825Sdfr if (ret) 486178825Sdfr return ret; 487178825Sdfr 488178825Sdfr if (cksum.checksum.length != siglen) { 489178825Sdfr krb5_set_error_string(context, "pac checksum wrong length"); 490178825Sdfr free_Checksum(&cksum); 491178825Sdfr return EINVAL; 492178825Sdfr } 493178825Sdfr 494178825Sdfr memcpy(sig, cksum.checksum.data, siglen); 495178825Sdfr free_Checksum(&cksum); 496178825Sdfr 497178825Sdfr return 0; 498178825Sdfr} 499178825Sdfr 500178825Sdfr 501178825Sdfr/* 502178825Sdfr * 503178825Sdfr */ 504178825Sdfr 505178825Sdfr#define NTTIME_EPOCH 0x019DB1DED53E8000LL 506178825Sdfr 507178825Sdfrstatic uint64_t 508178825Sdfrunix2nttime(time_t unix_time) 509178825Sdfr{ 510178825Sdfr long long wt; 511178825Sdfr wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH; 512178825Sdfr return wt; 513178825Sdfr} 514178825Sdfr 515178825Sdfrstatic krb5_error_code 516178825Sdfrverify_logonname(krb5_context context, 517178825Sdfr const struct PAC_INFO_BUFFER *logon_name, 518178825Sdfr const krb5_data *data, 519178825Sdfr time_t authtime, 520178825Sdfr krb5_const_principal principal) 521178825Sdfr{ 522178825Sdfr krb5_error_code ret; 523178825Sdfr krb5_principal p2; 524178825Sdfr uint32_t time1, time2; 525178825Sdfr krb5_storage *sp; 526178825Sdfr uint16_t len; 527178825Sdfr char *s; 528178825Sdfr 529178825Sdfr sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo, 530178825Sdfr logon_name->buffersize); 531178825Sdfr if (sp == NULL) { 532178825Sdfr krb5_set_error_string(context, "Out of memory"); 533178825Sdfr return ENOMEM; 534178825Sdfr } 535178825Sdfr 536178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 537178825Sdfr 538178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &time1), out); 539178825Sdfr CHECK(ret, krb5_ret_uint32(sp, &time2), out); 540178825Sdfr 541178825Sdfr { 542178825Sdfr uint64_t t1, t2; 543178825Sdfr t1 = unix2nttime(authtime); 544178825Sdfr t2 = ((uint64_t)time2 << 32) | time1; 545178825Sdfr if (t1 != t2) { 546178825Sdfr krb5_storage_free(sp); 547178825Sdfr krb5_set_error_string(context, "PAC timestamp mismatch"); 548178825Sdfr return EINVAL; 549178825Sdfr } 550178825Sdfr } 551178825Sdfr CHECK(ret, krb5_ret_uint16(sp, &len), out); 552178825Sdfr if (len == 0) { 553178825Sdfr krb5_storage_free(sp); 554178825Sdfr krb5_set_error_string(context, "PAC logon name length missing"); 555178825Sdfr return EINVAL; 556178825Sdfr } 557178825Sdfr 558178825Sdfr s = malloc(len); 559178825Sdfr if (s == NULL) { 560178825Sdfr krb5_storage_free(sp); 561178825Sdfr krb5_set_error_string(context, "Out of memory"); 562178825Sdfr return ENOMEM; 563178825Sdfr } 564178825Sdfr ret = krb5_storage_read(sp, s, len); 565178825Sdfr if (ret != len) { 566178825Sdfr krb5_storage_free(sp); 567178825Sdfr krb5_set_error_string(context, "Failed to read pac logon name"); 568178825Sdfr return EINVAL; 569178825Sdfr } 570178825Sdfr krb5_storage_free(sp); 571178825Sdfr#if 1 /* cheat for now */ 572178825Sdfr { 573178825Sdfr size_t i; 574178825Sdfr 575178825Sdfr if (len & 1) { 576178825Sdfr krb5_set_error_string(context, "PAC logon name malformed"); 577178825Sdfr return EINVAL; 578178825Sdfr } 579178825Sdfr 580178825Sdfr for (i = 0; i < len / 2; i++) { 581178825Sdfr if (s[(i * 2) + 1]) { 582178825Sdfr krb5_set_error_string(context, "PAC logon name not ASCII"); 583178825Sdfr return EINVAL; 584178825Sdfr } 585178825Sdfr s[i] = s[i * 2]; 586178825Sdfr } 587178825Sdfr s[i] = '\0'; 588178825Sdfr } 589178825Sdfr#else 590178825Sdfr { 591178825Sdfr uint16_t *ucs2; 592178825Sdfr ssize_t ucs2len; 593178825Sdfr size_t u8len; 594178825Sdfr 595178825Sdfr ucs2 = malloc(sizeof(ucs2[0]) * len / 2); 596178825Sdfr if (ucs2) 597178825Sdfr abort(); 598178825Sdfr ucs2len = wind_ucs2read(s, len / 2, ucs2); 599178825Sdfr free(s); 600178825Sdfr if (len < 0) 601178825Sdfr return -1; 602178825Sdfr ret = wind_ucs2toutf8(ucs2, ucs2len, NULL, &u8len); 603178825Sdfr if (ret < 0) 604178825Sdfr abort(); 605178825Sdfr s = malloc(u8len + 1); 606178825Sdfr if (s == NULL) 607178825Sdfr abort(); 608178825Sdfr wind_ucs2toutf8(ucs2, ucs2len, s, &u8len); 609178825Sdfr free(ucs2); 610178825Sdfr } 611178825Sdfr#endif 612178825Sdfr ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); 613178825Sdfr free(s); 614178825Sdfr if (ret) 615178825Sdfr return ret; 616178825Sdfr 617178825Sdfr if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) { 618178825Sdfr krb5_set_error_string(context, "PAC logon name mismatch"); 619178825Sdfr ret = EINVAL; 620178825Sdfr } 621178825Sdfr krb5_free_principal(context, p2); 622178825Sdfr return ret; 623178825Sdfrout: 624178825Sdfr return ret; 625178825Sdfr} 626178825Sdfr 627178825Sdfr/* 628178825Sdfr * 629178825Sdfr */ 630178825Sdfr 631178825Sdfrstatic krb5_error_code 632178825Sdfrbuild_logon_name(krb5_context context, 633178825Sdfr time_t authtime, 634178825Sdfr krb5_const_principal principal, 635178825Sdfr krb5_data *logon) 636178825Sdfr{ 637178825Sdfr krb5_error_code ret; 638178825Sdfr krb5_storage *sp; 639178825Sdfr uint64_t t; 640178825Sdfr char *s, *s2; 641178825Sdfr size_t i, len; 642178825Sdfr 643178825Sdfr t = unix2nttime(authtime); 644178825Sdfr 645178825Sdfr krb5_data_zero(logon); 646178825Sdfr 647178825Sdfr sp = krb5_storage_emem(); 648178825Sdfr if (sp == NULL) { 649178825Sdfr krb5_set_error_string(context, "out of memory"); 650178825Sdfr return ENOMEM; 651178825Sdfr } 652178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 653178825Sdfr 654178825Sdfr CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out); 655178825Sdfr CHECK(ret, krb5_store_uint32(sp, t >> 32), out); 656178825Sdfr 657178825Sdfr ret = krb5_unparse_name_flags(context, principal, 658178825Sdfr KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s); 659178825Sdfr if (ret) 660178825Sdfr goto out; 661178825Sdfr 662178825Sdfr len = strlen(s); 663178825Sdfr 664178825Sdfr CHECK(ret, krb5_store_uint16(sp, len * 2), out); 665178825Sdfr 666178825Sdfr#if 1 /* cheat for now */ 667178825Sdfr s2 = malloc(len * 2); 668178825Sdfr if (s2 == NULL) { 669178825Sdfr ret = ENOMEM; 670178825Sdfr free(s); 671178825Sdfr goto out; 672178825Sdfr } 673178825Sdfr for (i = 0; i < len; i++) { 674178825Sdfr s2[i * 2] = s[i]; 675178825Sdfr s2[i * 2 + 1] = 0; 676178825Sdfr } 677178825Sdfr free(s); 678178825Sdfr#else 679178825Sdfr /* write libwind code here */ 680178825Sdfr#endif 681178825Sdfr 682178825Sdfr ret = krb5_storage_write(sp, s2, len * 2); 683178825Sdfr free(s2); 684178825Sdfr if (ret != len * 2) { 685178825Sdfr ret = ENOMEM; 686178825Sdfr goto out; 687178825Sdfr } 688178825Sdfr ret = krb5_storage_to_data(sp, logon); 689178825Sdfr if (ret) 690178825Sdfr goto out; 691178825Sdfr krb5_storage_free(sp); 692178825Sdfr 693178825Sdfr return 0; 694178825Sdfrout: 695178825Sdfr krb5_storage_free(sp); 696178825Sdfr return ret; 697178825Sdfr} 698178825Sdfr 699178825Sdfr 700178825Sdfr/* 701178825Sdfr * 702178825Sdfr */ 703178825Sdfr 704178825Sdfrkrb5_error_code 705178825Sdfrkrb5_pac_verify(krb5_context context, 706178825Sdfr const krb5_pac pac, 707178825Sdfr time_t authtime, 708178825Sdfr krb5_const_principal principal, 709178825Sdfr const krb5_keyblock *server, 710178825Sdfr const krb5_keyblock *privsvr) 711178825Sdfr{ 712178825Sdfr krb5_error_code ret; 713178825Sdfr 714178825Sdfr if (pac->server_checksum == NULL) { 715178825Sdfr krb5_set_error_string(context, "PAC missing server checksum"); 716178825Sdfr return EINVAL; 717178825Sdfr } 718178825Sdfr if (pac->privsvr_checksum == NULL) { 719178825Sdfr krb5_set_error_string(context, "PAC missing kdc checksum"); 720178825Sdfr return EINVAL; 721178825Sdfr } 722178825Sdfr if (pac->logon_name == NULL) { 723178825Sdfr krb5_set_error_string(context, "PAC missing logon name"); 724178825Sdfr return EINVAL; 725178825Sdfr } 726178825Sdfr 727178825Sdfr ret = verify_logonname(context, 728178825Sdfr pac->logon_name, 729178825Sdfr &pac->data, 730178825Sdfr authtime, 731178825Sdfr principal); 732178825Sdfr if (ret) 733178825Sdfr return ret; 734178825Sdfr 735178825Sdfr /* 736178825Sdfr * in the service case, clean out data option of the privsvr and 737178825Sdfr * server checksum before checking the checksum. 738178825Sdfr */ 739178825Sdfr { 740178825Sdfr krb5_data *copy; 741178825Sdfr 742178825Sdfr ret = krb5_copy_data(context, &pac->data, ©); 743178825Sdfr if (ret) 744178825Sdfr return ret; 745178825Sdfr 746178825Sdfr if (pac->server_checksum->buffersize < 4) 747178825Sdfr return EINVAL; 748178825Sdfr if (pac->privsvr_checksum->buffersize < 4) 749178825Sdfr return EINVAL; 750178825Sdfr 751178825Sdfr memset((char *)copy->data + pac->server_checksum->offset_lo + 4, 752178825Sdfr 0, 753178825Sdfr pac->server_checksum->buffersize - 4); 754178825Sdfr 755178825Sdfr memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4, 756178825Sdfr 0, 757178825Sdfr pac->privsvr_checksum->buffersize - 4); 758178825Sdfr 759178825Sdfr ret = verify_checksum(context, 760178825Sdfr pac->server_checksum, 761178825Sdfr &pac->data, 762178825Sdfr copy->data, 763178825Sdfr copy->length, 764178825Sdfr server); 765178825Sdfr krb5_free_data(context, copy); 766178825Sdfr if (ret) 767178825Sdfr return ret; 768178825Sdfr } 769178825Sdfr if (privsvr) { 770178825Sdfr ret = verify_checksum(context, 771178825Sdfr pac->privsvr_checksum, 772178825Sdfr &pac->data, 773178825Sdfr (char *)pac->data.data 774178825Sdfr + pac->server_checksum->offset_lo + 4, 775178825Sdfr pac->server_checksum->buffersize - 4, 776178825Sdfr privsvr); 777178825Sdfr if (ret) 778178825Sdfr return ret; 779178825Sdfr } 780178825Sdfr 781178825Sdfr return 0; 782178825Sdfr} 783178825Sdfr 784178825Sdfr/* 785178825Sdfr * 786178825Sdfr */ 787178825Sdfr 788178825Sdfrstatic krb5_error_code 789178825Sdfrfill_zeros(krb5_context context, krb5_storage *sp, size_t len) 790178825Sdfr{ 791178825Sdfr ssize_t sret; 792178825Sdfr size_t l; 793178825Sdfr 794178825Sdfr while (len) { 795178825Sdfr l = len; 796178825Sdfr if (l > sizeof(zeros)) 797178825Sdfr l = sizeof(zeros); 798178825Sdfr sret = krb5_storage_write(sp, zeros, l); 799178825Sdfr if (sret <= 0) { 800178825Sdfr krb5_set_error_string(context, "out of memory"); 801178825Sdfr return ENOMEM; 802178825Sdfr } 803178825Sdfr len -= sret; 804178825Sdfr } 805178825Sdfr return 0; 806178825Sdfr} 807178825Sdfr 808178825Sdfrstatic krb5_error_code 809178825Sdfrpac_checksum(krb5_context context, 810178825Sdfr const krb5_keyblock *key, 811178825Sdfr uint32_t *cksumtype, 812178825Sdfr size_t *cksumsize) 813178825Sdfr{ 814178825Sdfr krb5_cksumtype cktype; 815178825Sdfr krb5_error_code ret; 816178825Sdfr krb5_crypto crypto = NULL; 817178825Sdfr 818178825Sdfr ret = krb5_crypto_init(context, key, 0, &crypto); 819178825Sdfr if (ret) 820178825Sdfr return ret; 821178825Sdfr 822178825Sdfr ret = krb5_crypto_get_checksum_type(context, crypto, &cktype); 823178825Sdfr ret = krb5_crypto_destroy(context, crypto); 824178825Sdfr if (ret) 825178825Sdfr return ret; 826178825Sdfr 827178825Sdfr if (krb5_checksum_is_keyed(context, cktype) == FALSE) { 828178825Sdfr krb5_set_error_string(context, "PAC checksum type is not keyed"); 829178825Sdfr return EINVAL; 830178825Sdfr } 831178825Sdfr 832178825Sdfr ret = krb5_checksumsize(context, cktype, cksumsize); 833178825Sdfr if (ret) 834178825Sdfr return ret; 835178825Sdfr 836178825Sdfr *cksumtype = (uint32_t)cktype; 837178825Sdfr 838178825Sdfr return 0; 839178825Sdfr} 840178825Sdfr 841178825Sdfrkrb5_error_code 842178825Sdfr_krb5_pac_sign(krb5_context context, 843178825Sdfr krb5_pac p, 844178825Sdfr time_t authtime, 845178825Sdfr krb5_principal principal, 846178825Sdfr const krb5_keyblock *server_key, 847178825Sdfr const krb5_keyblock *priv_key, 848178825Sdfr krb5_data *data) 849178825Sdfr{ 850178825Sdfr krb5_error_code ret; 851178825Sdfr krb5_storage *sp = NULL, *spdata = NULL; 852178825Sdfr uint32_t end; 853178825Sdfr size_t server_size, priv_size; 854178825Sdfr uint32_t server_offset = 0, priv_offset = 0; 855178825Sdfr uint32_t server_cksumtype = 0, priv_cksumtype = 0; 856178825Sdfr int i, num = 0; 857178825Sdfr krb5_data logon, d; 858178825Sdfr 859178825Sdfr krb5_data_zero(&logon); 860178825Sdfr 861178825Sdfr if (p->logon_name == NULL) 862178825Sdfr num++; 863178825Sdfr if (p->server_checksum == NULL) 864178825Sdfr num++; 865178825Sdfr if (p->privsvr_checksum == NULL) 866178825Sdfr num++; 867178825Sdfr 868178825Sdfr if (num) { 869178825Sdfr void *ptr; 870178825Sdfr 871178825Sdfr ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1))); 872178825Sdfr if (ptr == NULL) { 873178825Sdfr krb5_set_error_string(context, "out of memory"); 874178825Sdfr return ENOMEM; 875178825Sdfr } 876178825Sdfr p->pac = ptr; 877178825Sdfr 878178825Sdfr if (p->logon_name == NULL) { 879178825Sdfr p->logon_name = &p->pac->buffers[p->pac->numbuffers++]; 880178825Sdfr memset(p->logon_name, 0, sizeof(*p->logon_name)); 881178825Sdfr p->logon_name->type = PAC_LOGON_NAME; 882178825Sdfr } 883178825Sdfr if (p->server_checksum == NULL) { 884178825Sdfr p->server_checksum = &p->pac->buffers[p->pac->numbuffers++]; 885178825Sdfr memset(p->server_checksum, 0, sizeof(*p->server_checksum)); 886178825Sdfr p->server_checksum->type = PAC_SERVER_CHECKSUM; 887178825Sdfr } 888178825Sdfr if (p->privsvr_checksum == NULL) { 889178825Sdfr p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++]; 890178825Sdfr memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum)); 891178825Sdfr p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM; 892178825Sdfr } 893178825Sdfr } 894178825Sdfr 895178825Sdfr /* Calculate LOGON NAME */ 896178825Sdfr ret = build_logon_name(context, authtime, principal, &logon); 897178825Sdfr if (ret) 898178825Sdfr goto out; 899178825Sdfr 900178825Sdfr /* Set lengths for checksum */ 901178825Sdfr ret = pac_checksum(context, server_key, &server_cksumtype, &server_size); 902178825Sdfr if (ret) 903178825Sdfr goto out; 904178825Sdfr ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); 905178825Sdfr if (ret) 906178825Sdfr goto out; 907178825Sdfr 908178825Sdfr /* Encode PAC */ 909178825Sdfr sp = krb5_storage_emem(); 910178825Sdfr if (sp == NULL) { 911178825Sdfr krb5_set_error_string(context, "out of memory"); 912178825Sdfr return ENOMEM; 913178825Sdfr } 914178825Sdfr krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 915178825Sdfr 916178825Sdfr spdata = krb5_storage_emem(); 917178825Sdfr if (spdata == NULL) { 918178825Sdfr krb5_storage_free(sp); 919178825Sdfr krb5_set_error_string(context, "out of memory"); 920178825Sdfr return ENOMEM; 921178825Sdfr } 922178825Sdfr krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE); 923178825Sdfr 924178825Sdfr CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out); 925178825Sdfr CHECK(ret, krb5_store_uint32(sp, p->pac->version), out); 926178825Sdfr 927178825Sdfr end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); 928178825Sdfr 929178825Sdfr for (i = 0; i < p->pac->numbuffers; i++) { 930178825Sdfr uint32_t len; 931178825Sdfr size_t sret; 932178825Sdfr void *ptr = NULL; 933178825Sdfr 934178825Sdfr /* store data */ 935178825Sdfr 936178825Sdfr if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { 937178825Sdfr len = server_size + 4; 938178825Sdfr server_offset = end + 4; 939178825Sdfr CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out); 940178825Sdfr CHECK(ret, fill_zeros(context, spdata, server_size), out); 941178825Sdfr } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { 942178825Sdfr len = priv_size + 4; 943178825Sdfr priv_offset = end + 4; 944178825Sdfr CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out); 945178825Sdfr CHECK(ret, fill_zeros(context, spdata, priv_size), out); 946178825Sdfr } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { 947178825Sdfr len = krb5_storage_write(spdata, logon.data, logon.length); 948178825Sdfr if (logon.length != len) { 949178825Sdfr ret = EINVAL; 950178825Sdfr goto out; 951178825Sdfr } 952178825Sdfr } else { 953178825Sdfr len = p->pac->buffers[i].buffersize; 954178825Sdfr ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo; 955178825Sdfr 956178825Sdfr sret = krb5_storage_write(spdata, ptr, len); 957178825Sdfr if (sret != len) { 958178825Sdfr krb5_set_error_string(context, "out of memory"); 959178825Sdfr ret = ENOMEM; 960178825Sdfr goto out; 961178825Sdfr } 962178825Sdfr /* XXX if not aligned, fill_zeros */ 963178825Sdfr } 964178825Sdfr 965178825Sdfr /* write header */ 966178825Sdfr CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out); 967178825Sdfr CHECK(ret, krb5_store_uint32(sp, len), out); 968178825Sdfr CHECK(ret, krb5_store_uint32(sp, end), out); 969178825Sdfr CHECK(ret, krb5_store_uint32(sp, 0), out); 970178825Sdfr 971178825Sdfr /* advance data endpointer and align */ 972178825Sdfr { 973178825Sdfr int32_t e; 974178825Sdfr 975178825Sdfr end += len; 976178825Sdfr e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; 977178825Sdfr if (end != e) { 978178825Sdfr CHECK(ret, fill_zeros(context, spdata, e - end), out); 979178825Sdfr } 980178825Sdfr end = e; 981178825Sdfr } 982178825Sdfr 983178825Sdfr } 984178825Sdfr 985178825Sdfr /* assert (server_offset != 0 && priv_offset != 0); */ 986178825Sdfr 987178825Sdfr /* export PAC */ 988178825Sdfr ret = krb5_storage_to_data(spdata, &d); 989178825Sdfr if (ret) { 990178825Sdfr krb5_set_error_string(context, "out of memory"); 991178825Sdfr goto out; 992178825Sdfr } 993178825Sdfr ret = krb5_storage_write(sp, d.data, d.length); 994178825Sdfr if (ret != d.length) { 995178825Sdfr krb5_data_free(&d); 996178825Sdfr krb5_set_error_string(context, "out of memory"); 997178825Sdfr ret = ENOMEM; 998178825Sdfr goto out; 999178825Sdfr } 1000178825Sdfr krb5_data_free(&d); 1001178825Sdfr 1002178825Sdfr ret = krb5_storage_to_data(sp, &d); 1003178825Sdfr if (ret) { 1004178825Sdfr krb5_set_error_string(context, "out of memory"); 1005178825Sdfr goto out; 1006178825Sdfr } 1007178825Sdfr 1008178825Sdfr /* sign */ 1009178825Sdfr 1010178825Sdfr ret = create_checksum(context, server_key, 1011178825Sdfr d.data, d.length, 1012178825Sdfr (char *)d.data + server_offset, server_size); 1013178825Sdfr if (ret) { 1014178825Sdfr krb5_data_free(&d); 1015178825Sdfr goto out; 1016178825Sdfr } 1017178825Sdfr 1018178825Sdfr ret = create_checksum(context, priv_key, 1019178825Sdfr (char *)d.data + server_offset, server_size, 1020178825Sdfr (char *)d.data + priv_offset, priv_size); 1021178825Sdfr if (ret) { 1022178825Sdfr krb5_data_free(&d); 1023178825Sdfr goto out; 1024178825Sdfr } 1025178825Sdfr 1026178825Sdfr /* done */ 1027178825Sdfr *data = d; 1028178825Sdfr 1029178825Sdfr krb5_data_free(&logon); 1030178825Sdfr krb5_storage_free(sp); 1031178825Sdfr krb5_storage_free(spdata); 1032178825Sdfr 1033178825Sdfr return 0; 1034178825Sdfrout: 1035178825Sdfr krb5_data_free(&logon); 1036178825Sdfr if (sp) 1037178825Sdfr krb5_storage_free(sp); 1038178825Sdfr if (spdata) 1039178825Sdfr krb5_storage_free(spdata); 1040178825Sdfr return ret; 1041178825Sdfr} 1042