root/source4/heimdal/lib/krb5/pac.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. krb5_pac_parse
  2. krb5_pac_init
  3. krb5_pac_add_buffer
  4. krb5_pac_get_buffer
  5. krb5_pac_get_types
  6. krb5_pac_free
  7. verify_checksum
  8. create_checksum
  9. unix2nttime
  10. verify_logonname
  11. build_logon_name
  12. krb5_pac_verify
  13. fill_zeros
  14. pac_checksum
  15. _krb5_pac_sign

   1 /*
   2  * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
   3  * (Royal Institute of Technology, Stockholm, Sweden).
   4  * All rights reserved.
   5  *
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  *
  10  * 1. Redistributions of source code must retain the above copyright
  11  *    notice, this list of conditions and the following disclaimer.
  12  *
  13  * 2. Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in the
  15  *    documentation and/or other materials provided with the distribution.
  16  *
  17  * 3. Neither the name of the Institute nor the names of its contributors
  18  *    may be used to endorse or promote products derived from this software
  19  *    without specific prior written permission.
  20  *
  21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
  22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
  25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31  * SUCH DAMAGE.
  32  */
  33 
  34 #include "krb5_locl.h"
  35 #include <wind.h>
  36 
  37 RCSID("$Id$");
  38 
  39 struct PAC_INFO_BUFFER {
  40     uint32_t type;
  41     uint32_t buffersize;
  42     uint32_t offset_hi;
  43     uint32_t offset_lo;
  44 };
  45 
  46 struct PACTYPE {
  47     uint32_t numbuffers;
  48     uint32_t version;
  49     struct PAC_INFO_BUFFER buffers[1];
  50 };
  51 
  52 struct krb5_pac_data {
  53     struct PACTYPE *pac;
  54     krb5_data data;
  55     struct PAC_INFO_BUFFER *server_checksum;
  56     struct PAC_INFO_BUFFER *privsvr_checksum;
  57     struct PAC_INFO_BUFFER *logon_name;
  58 };
  59 
  60 #define PAC_ALIGNMENT                   8
  61 
  62 #define PACTYPE_SIZE                    8
  63 #define PAC_INFO_BUFFER_SIZE            16
  64 
  65 #define PAC_SERVER_CHECKSUM             6
  66 #define PAC_PRIVSVR_CHECKSUM            7
  67 #define PAC_LOGON_NAME                  10
  68 #define PAC_CONSTRAINED_DELEGATION      11
  69 
  70 #define CHECK(r,f,l)                                            \
  71         do {                                                    \
  72                 if (((r) = f ) != 0) {                          \
  73                         krb5_clear_error_message(context);      \
  74                         goto l;                                 \
  75                 }                                               \
  76         } while(0)
  77 
  78 static const char zeros[PAC_ALIGNMENT] = { 0 };
  79 
  80 /*
  81  *
  82  */
  83 
  84 krb5_error_code
  85 krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
     /* [<][>][^][v][top][bottom][index][help] */
  86                krb5_pac *pac)
  87 {
  88     krb5_error_code ret;
  89     krb5_pac p;
  90     krb5_storage *sp = NULL;
  91     uint32_t i, tmp, tmp2, header_end;
  92 
  93     p = calloc(1, sizeof(*p));
  94     if (p == NULL) {
  95         ret = ENOMEM;
  96         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
  97         goto out;
  98     }
  99 
 100     sp = krb5_storage_from_readonly_mem(ptr, len);
 101     if (sp == NULL) {
 102         ret = ENOMEM;
 103         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 104         goto out;
 105     }
 106     krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
 107 
 108     CHECK(ret, krb5_ret_uint32(sp, &tmp), out);
 109     CHECK(ret, krb5_ret_uint32(sp, &tmp2), out);
 110     if (tmp < 1) {
 111         ret = EINVAL; /* Too few buffers */
 112         krb5_set_error_message(context, ret, N_("PAC have too few buffer", ""));
 113         goto out;
 114     }
 115     if (tmp2 != 0) {
 116         ret = EINVAL; /* Wrong version */
 117         krb5_set_error_message(context, ret,
 118                                N_("PAC have wrong version %d", ""),
 119                                (int)tmp2);
 120         goto out;
 121     }
 122 
 123     p->pac = calloc(1,
 124                     sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1)));
 125     if (p->pac == NULL) {
 126         ret = ENOMEM;
 127         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 128         goto out;
 129     }
 130 
 131     p->pac->numbuffers = tmp;
 132     p->pac->version = tmp2;
 133 
 134     header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
 135     if (header_end > len) {
 136         ret = EINVAL;
 137         goto out;
 138     }
 139 
 140     for (i = 0; i < p->pac->numbuffers; i++) {
 141         CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out);
 142         CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out);
 143         CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out);
 144         CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out);
 145 
 146         /* consistency checks */
 147         if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) {
 148             ret = EINVAL;
 149             krb5_set_error_message(context, ret,
 150                                    N_("PAC out of allignment", ""));
 151             goto out;
 152         }
 153         if (p->pac->buffers[i].offset_hi) {
 154             ret = EINVAL;
 155             krb5_set_error_message(context, ret,
 156                                    N_("PAC high offset set", ""));
 157             goto out;
 158         }
 159         if (p->pac->buffers[i].offset_lo > len) {
 160             ret = EINVAL;
 161             krb5_set_error_message(context, ret,
 162                                    N_("PAC offset off end", ""));
 163             goto out;
 164         }
 165         if (p->pac->buffers[i].offset_lo < header_end) {
 166             ret = EINVAL;
 167             krb5_set_error_message(context, ret,
 168                                    N_("PAC offset inside header: %lu %lu", ""),
 169                                    (unsigned long)p->pac->buffers[i].offset_lo,
 170                                    (unsigned long)header_end);
 171             goto out;
 172         }
 173         if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){
 174             ret = EINVAL;
 175             krb5_set_error_message(context, ret, N_("PAC length off end", ""));
 176             goto out;
 177         }
 178 
 179         /* let save pointer to data we need later */
 180         if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
 181             if (p->server_checksum) {
 182                 ret = EINVAL;
 183                 krb5_set_error_message(context, ret,
 184                                        N_("PAC have two server checksums", ""));
 185                 goto out;
 186             }
 187             p->server_checksum = &p->pac->buffers[i];
 188         } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
 189             if (p->privsvr_checksum) {
 190                 ret = EINVAL;
 191                 krb5_set_error_message(context, ret,
 192                                        N_("PAC have two KDC checksums", ""));
 193                 goto out;
 194             }
 195             p->privsvr_checksum = &p->pac->buffers[i];
 196         } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
 197             if (p->logon_name) {
 198                 ret = EINVAL;
 199                 krb5_set_error_message(context, ret,
 200                                        N_("PAC have two logon names", ""));
 201                 goto out;
 202             }
 203             p->logon_name = &p->pac->buffers[i];
 204         }
 205     }
 206 
 207     ret = krb5_data_copy(&p->data, ptr, len);
 208     if (ret)
 209         goto out;
 210 
 211     krb5_storage_free(sp);
 212 
 213     *pac = p;
 214     return 0;
 215 
 216 out:
 217     if (sp)
 218         krb5_storage_free(sp);
 219     if (p) {
 220         if (p->pac)
 221             free(p->pac);
 222         free(p);
 223     }
 224     *pac = NULL;
 225 
 226     return ret;
 227 }
 228 
 229 krb5_error_code
 230 krb5_pac_init(krb5_context context, krb5_pac *pac)
     /* [<][>][^][v][top][bottom][index][help] */
 231 {
 232     krb5_error_code ret;
 233     krb5_pac p;
 234 
 235     p = calloc(1, sizeof(*p));
 236     if (p == NULL) {
 237         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 238         return ENOMEM;
 239     }
 240 
 241     p->pac = calloc(1, sizeof(*p->pac));
 242     if (p->pac == NULL) {
 243         free(p);
 244         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 245         return ENOMEM;
 246     }
 247 
 248     ret = krb5_data_alloc(&p->data, PACTYPE_SIZE);
 249     if (ret) {
 250         free (p->pac);
 251         free(p);
 252         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 253         return ret;
 254     }
 255 
 256 
 257     *pac = p;
 258     return 0;
 259 }
 260 
 261 krb5_error_code
 262 krb5_pac_add_buffer(krb5_context context, krb5_pac p,
     /* [<][>][^][v][top][bottom][index][help] */
 263                     uint32_t type, const krb5_data *data)
 264 {
 265     krb5_error_code ret;
 266     void *ptr;
 267     size_t len, offset, header_end, old_end;
 268     uint32_t i;
 269 
 270     len = p->pac->numbuffers;
 271 
 272     ptr = realloc(p->pac,
 273                   sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len));
 274     if (ptr == NULL) {
 275         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 276         return ENOMEM;
 277     }
 278     p->pac = ptr;
 279 
 280     for (i = 0; i < len; i++)
 281         p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE;
 282 
 283     offset = p->data.length + PAC_INFO_BUFFER_SIZE;
 284 
 285     p->pac->buffers[len].type = type;
 286     p->pac->buffers[len].buffersize = data->length;
 287     p->pac->buffers[len].offset_lo = offset;
 288     p->pac->buffers[len].offset_hi = 0;
 289 
 290     old_end = p->data.length;
 291     len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE;
 292     if (len < p->data.length) {
 293         krb5_set_error_message(context, EINVAL, "integer overrun");
 294         return EINVAL;
 295     }
 296 
 297     /* align to PAC_ALIGNMENT */
 298     len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
 299 
 300     ret = krb5_data_realloc(&p->data, len);
 301     if (ret) {
 302         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 303         return ret;
 304     }
 305 
 306     /*
 307      * make place for new PAC INFO BUFFER header
 308      */
 309     header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
 310     memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE,
 311             (unsigned char *)p->data.data + header_end ,
 312             old_end - header_end);
 313     memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE);
 314 
 315     /*
 316      * copy in new data part
 317      */
 318 
 319     memcpy((unsigned char *)p->data.data + offset,
 320            data->data, data->length);
 321     memset((unsigned char *)p->data.data + offset + data->length,
 322            0, p->data.length - offset - data->length);
 323 
 324     p->pac->numbuffers += 1;
 325 
 326     return 0;
 327 }
 328 
 329 /**
 330  * Get the PAC buffer of specific type from the pac.
 331  *
 332  * @param context Kerberos 5 context.
 333  * @param p the pac structure returned by krb5_pac_parse().
 334  * @param type type of buffer to get
 335  * @param data return data, free with krb5_data_free().
 336  *
 337  * @return Returns 0 to indicate success. Otherwise an kerberos et
 338  * error code is returned, see krb5_get_error_message().
 339  *
 340  * @ingroup krb5_pac
 341  */
 342 
 343 krb5_error_code
 344 krb5_pac_get_buffer(krb5_context context, krb5_pac p,
     /* [<][>][^][v][top][bottom][index][help] */
 345                     uint32_t type, krb5_data *data)
 346 {
 347     krb5_error_code ret;
 348     uint32_t i;
 349 
 350     for (i = 0; i < p->pac->numbuffers; i++) {
 351         const size_t len = p->pac->buffers[i].buffersize;
 352         const size_t offset = p->pac->buffers[i].offset_lo;
 353 
 354         if (p->pac->buffers[i].type != type)
 355             continue;
 356 
 357         ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
 358         if (ret) {
 359             krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 360             return ret;
 361         }
 362         return 0;
 363     }
 364     krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found",
 365                            (unsigned long)type);
 366     return ENOENT;
 367 }
 368 
 369 /*
 370  *
 371  */
 372 
 373 krb5_error_code
 374 krb5_pac_get_types(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 375                    krb5_pac p,
 376                    size_t *len,
 377                    uint32_t **types)
 378 {
 379     size_t i;
 380 
 381     *types = calloc(p->pac->numbuffers, sizeof(*types));
 382     if (*types == NULL) {
 383         *len = 0;
 384         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 385         return ENOMEM;
 386     }
 387     for (i = 0; i < p->pac->numbuffers; i++)
 388         (*types)[i] = p->pac->buffers[i].type;
 389     *len = p->pac->numbuffers;
 390 
 391     return 0;
 392 }
 393 
 394 /*
 395  *
 396  */
 397 
 398 void
 399 krb5_pac_free(krb5_context context, krb5_pac pac)
     /* [<][>][^][v][top][bottom][index][help] */
 400 {
 401     krb5_data_free(&pac->data);
 402     free(pac->pac);
 403     free(pac);
 404 }
 405 
 406 /*
 407  *
 408  */
 409 
 410 static krb5_error_code
 411 verify_checksum(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 412                 const struct PAC_INFO_BUFFER *sig,
 413                 const krb5_data *data,
 414                 void *ptr, size_t len,
 415                 const krb5_keyblock *key)
 416 {
 417     krb5_crypto crypto = NULL;
 418     krb5_storage *sp = NULL;
 419     uint32_t type;
 420     krb5_error_code ret;
 421     Checksum cksum;
 422 
 423     memset(&cksum, 0, sizeof(cksum));
 424 
 425     sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo,
 426                                sig->buffersize);
 427     if (sp == NULL) {
 428         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 429         return ENOMEM;
 430     }
 431     krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
 432 
 433     CHECK(ret, krb5_ret_uint32(sp, &type), out);
 434     cksum.cksumtype = type;
 435     cksum.checksum.length =
 436         sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR);
 437     cksum.checksum.data = malloc(cksum.checksum.length);
 438     if (cksum.checksum.data == NULL) {
 439         ret = ENOMEM;
 440         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 441         goto out;
 442     }
 443     ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length);
 444     if (ret != cksum.checksum.length) {
 445         ret = EINVAL;
 446         krb5_set_error_message(context, ret, "PAC checksum missing checksum");
 447         goto out;
 448     }
 449 
 450     if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) {
 451         ret = EINVAL;
 452         krb5_set_error_message(context, ret, "Checksum type %d not keyed",
 453                                cksum.cksumtype);
 454         goto out;
 455     }
 456 
 457     ret = krb5_crypto_init(context, key, 0, &crypto);
 458     if (ret)
 459         goto out;
 460 
 461     ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM,
 462                                ptr, len, &cksum);
 463     free(cksum.checksum.data);
 464     krb5_crypto_destroy(context, crypto);
 465     krb5_storage_free(sp);
 466 
 467     return ret;
 468 
 469 out:
 470     if (cksum.checksum.data)
 471         free(cksum.checksum.data);
 472     if (sp)
 473         krb5_storage_free(sp);
 474     if (crypto)
 475         krb5_crypto_destroy(context, crypto);
 476     return ret;
 477 }
 478 
 479 static krb5_error_code
 480 create_checksum(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 481                 const krb5_keyblock *key,
 482                 void *data, size_t datalen,
 483                 void *sig, size_t siglen)
 484 {
 485     krb5_crypto crypto = NULL;
 486     krb5_error_code ret;
 487     Checksum cksum;
 488 
 489     ret = krb5_crypto_init(context, key, 0, &crypto);
 490     if (ret)
 491         return ret;
 492 
 493     ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0,
 494                                data, datalen, &cksum);
 495     krb5_crypto_destroy(context, crypto);
 496     if (ret)
 497         return ret;
 498 
 499     if (cksum.checksum.length != siglen) {
 500         krb5_set_error_message(context, EINVAL, "pac checksum wrong length");
 501         free_Checksum(&cksum);
 502         return EINVAL;
 503     }
 504 
 505     memcpy(sig, cksum.checksum.data, siglen);
 506     free_Checksum(&cksum);
 507 
 508     return 0;
 509 }
 510 
 511 
 512 /*
 513  *
 514  */
 515 
 516 #define NTTIME_EPOCH 0x019DB1DED53E8000LL
 517 
 518 static uint64_t
 519 unix2nttime(time_t unix_time)
     /* [<][>][^][v][top][bottom][index][help] */
 520 {
 521     long long wt;
 522     wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
 523     return wt;
 524 }
 525 
 526 static krb5_error_code
 527 verify_logonname(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 528                  const struct PAC_INFO_BUFFER *logon_name,
 529                  const krb5_data *data,
 530                  time_t authtime,
 531                  krb5_const_principal principal)
 532 {
 533     krb5_error_code ret;
 534     krb5_principal p2;
 535     uint32_t time1, time2;
 536     krb5_storage *sp;
 537     uint16_t len;
 538     char *s;
 539 
 540     sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo,
 541                                         logon_name->buffersize);
 542     if (sp == NULL) {
 543         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 544         return ENOMEM;
 545     }
 546 
 547     krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
 548 
 549     CHECK(ret, krb5_ret_uint32(sp, &time1), out);
 550     CHECK(ret, krb5_ret_uint32(sp, &time2), out);
 551 
 552     {
 553         uint64_t t1, t2;
 554         t1 = unix2nttime(authtime);
 555         t2 = ((uint64_t)time2 << 32) | time1;
 556         if (t1 != t2) {
 557             krb5_storage_free(sp);
 558             krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch");
 559             return EINVAL;
 560         }
 561     }
 562     CHECK(ret, krb5_ret_uint16(sp, &len), out);
 563     if (len == 0) {
 564         krb5_storage_free(sp);
 565         krb5_set_error_message(context, EINVAL, "PAC logon name length missing");
 566         return EINVAL;
 567     }
 568 
 569     s = malloc(len);
 570     if (s == NULL) {
 571         krb5_storage_free(sp);
 572         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 573         return ENOMEM;
 574     }
 575     ret = krb5_storage_read(sp, s, len);
 576     if (ret != len) {
 577         krb5_storage_free(sp);
 578         krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name");
 579         return EINVAL;
 580     }
 581     krb5_storage_free(sp);
 582     {
 583         size_t ucs2len = len / 2;
 584         uint16_t *ucs2;
 585         size_t u8len;
 586         unsigned int flags = WIND_RW_LE;
 587 
 588         ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
 589         if (ucs2 == NULL) {
 590             krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 591             return ENOMEM;
 592         }
 593         ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
 594         free(s);
 595         if (ret) {
 596             free(ucs2);
 597             krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
 598             return ret;
 599         }
 600         ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
 601         if (ret) {
 602             free(ucs2);
 603             krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string");
 604             return ret;
 605         }
 606         u8len += 1; /* Add space for NUL */
 607         s = malloc(u8len);
 608         if (s == NULL) {
 609             free(ucs2);
 610             krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 611             return ENOMEM;
 612         }
 613         ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len);
 614         free(ucs2);
 615         if (ret) {
 616             krb5_set_error_message(context, ret, "Failed to convert to UTF-8");
 617             return ret;
 618         }
 619     }
 620     ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2);
 621     free(s);
 622     if (ret)
 623         return ret;
 624 
 625     if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) {
 626         ret = EINVAL;
 627         krb5_set_error_message(context, ret, "PAC logon name mismatch");
 628     }
 629     krb5_free_principal(context, p2);
 630     return ret;
 631 out:
 632     return ret;
 633 }
 634 
 635 /*
 636  *
 637  */
 638 
 639 static krb5_error_code
 640 build_logon_name(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 641                  time_t authtime,
 642                  krb5_const_principal principal,
 643                  krb5_data *logon)
 644 {
 645     krb5_error_code ret;
 646     krb5_storage *sp;
 647     uint64_t t;
 648     char *s, *s2;
 649     size_t i, len;
 650 
 651     t = unix2nttime(authtime);
 652 
 653     krb5_data_zero(logon);
 654 
 655     sp = krb5_storage_emem();
 656     if (sp == NULL) {
 657         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 658         return ENOMEM;
 659     }
 660     krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
 661 
 662     CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out);
 663     CHECK(ret, krb5_store_uint32(sp, t >> 32), out);
 664 
 665     ret = krb5_unparse_name_flags(context, principal,
 666                                   KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s);
 667     if (ret)
 668         goto out;
 669 
 670     len = strlen(s);
 671 
 672     CHECK(ret, krb5_store_uint16(sp, len * 2), out);
 673 
 674 #if 1 /* cheat for now */
 675     s2 = malloc(len * 2);
 676     if (s2 == NULL) {
 677         ret = ENOMEM;
 678         free(s);
 679         goto out;
 680     }
 681     for (i = 0; i < len; i++) {
 682         s2[i * 2] = s[i];
 683         s2[i * 2 + 1] = 0;
 684     }
 685     free(s);
 686 #else
 687     /* write libwind code here */
 688 #endif
 689 
 690     ret = krb5_storage_write(sp, s2, len * 2);
 691     free(s2);
 692     if (ret != len * 2) {
 693         ret = ENOMEM;
 694         goto out;
 695     }
 696     ret = krb5_storage_to_data(sp, logon);
 697     if (ret)
 698         goto out;
 699     krb5_storage_free(sp);
 700 
 701     return 0;
 702 out:
 703     krb5_storage_free(sp);
 704     return ret;
 705 }
 706 
 707 
 708 /**
 709  * Verify the PAC.
 710  *
 711  * @param context Kerberos 5 context.
 712  * @param pac the pac structure returned by krb5_pac_parse().
 713  * @param authtime The time of the ticket the PAC belongs to.
 714  * @param principal the principal to verify.
 715  * @param server The service key, most always be given.
 716  * @param privsvr The KDC key, may be given.
 717 
 718  * @return Returns 0 to indicate success. Otherwise an kerberos et
 719  * error code is returned, see krb5_get_error_message().
 720  *
 721  * @ingroup krb5_pac
 722  */
 723 
 724 krb5_error_code
 725 krb5_pac_verify(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 726                 const krb5_pac pac,
 727                 time_t authtime,
 728                 krb5_const_principal principal,
 729                 const krb5_keyblock *server,
 730                 const krb5_keyblock *privsvr)
 731 {
 732     krb5_error_code ret;
 733 
 734     if (pac->server_checksum == NULL) {
 735         krb5_set_error_message(context, EINVAL, "PAC missing server checksum");
 736         return EINVAL;
 737     }
 738     if (pac->privsvr_checksum == NULL) {
 739         krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum");
 740         return EINVAL;
 741     }
 742     if (pac->logon_name == NULL) {
 743         krb5_set_error_message(context, EINVAL, "PAC missing logon name");
 744         return EINVAL;
 745     }
 746 
 747     ret = verify_logonname(context,
 748                            pac->logon_name,
 749                            &pac->data,
 750                            authtime,
 751                            principal);
 752     if (ret)
 753         return ret;
 754 
 755     /*
 756      * in the service case, clean out data option of the privsvr and
 757      * server checksum before checking the checksum.
 758      */
 759     {
 760         krb5_data *copy;
 761 
 762         ret = krb5_copy_data(context, &pac->data, &copy);
 763         if (ret)
 764             return ret;
 765 
 766         if (pac->server_checksum->buffersize < 4)
 767             return EINVAL;
 768         if (pac->privsvr_checksum->buffersize < 4)
 769             return EINVAL;
 770 
 771         memset((char *)copy->data + pac->server_checksum->offset_lo + 4,
 772                0,
 773                pac->server_checksum->buffersize - 4);
 774 
 775         memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4,
 776                0,
 777                pac->privsvr_checksum->buffersize - 4);
 778 
 779         ret = verify_checksum(context,
 780                               pac->server_checksum,
 781                               &pac->data,
 782                               copy->data,
 783                               copy->length,
 784                               server);
 785         krb5_free_data(context, copy);
 786         if (ret)
 787             return ret;
 788     }
 789     if (privsvr) {
 790         /* The priv checksum covers the server checksum */
 791         ret = verify_checksum(context,
 792                               pac->privsvr_checksum,
 793                               &pac->data,
 794                               (char *)pac->data.data
 795                               + pac->server_checksum->offset_lo + 4,
 796                               pac->server_checksum->buffersize - 4,
 797                               privsvr);
 798         if (ret)
 799             return ret;
 800     }
 801 
 802     return 0;
 803 }
 804 
 805 /*
 806  *
 807  */
 808 
 809 static krb5_error_code
 810 fill_zeros(krb5_context context, krb5_storage *sp, size_t len)
     /* [<][>][^][v][top][bottom][index][help] */
 811 {
 812     ssize_t sret;
 813     size_t l;
 814 
 815     while (len) {
 816         l = len;
 817         if (l > sizeof(zeros))
 818             l = sizeof(zeros);
 819         sret = krb5_storage_write(sp, zeros, l);
 820         if (sret <= 0) {
 821             krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 822             return ENOMEM;
 823         }
 824         len -= sret;
 825     }
 826     return 0;
 827 }
 828 
 829 static krb5_error_code
 830 pac_checksum(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 831              const krb5_keyblock *key,
 832              uint32_t *cksumtype,
 833              size_t *cksumsize)
 834 {
 835     krb5_cksumtype cktype;
 836     krb5_error_code ret;
 837     krb5_crypto crypto = NULL;
 838 
 839     ret = krb5_crypto_init(context, key, 0, &crypto);
 840     if (ret)
 841         return ret;
 842 
 843     ret = krb5_crypto_get_checksum_type(context, crypto, &cktype);
 844     krb5_crypto_destroy(context, crypto);
 845     if (ret)
 846         return ret;
 847 
 848     if (krb5_checksum_is_keyed(context, cktype) == FALSE) {
 849         krb5_set_error_message(context, EINVAL, "PAC checksum type is not keyed");
 850         return EINVAL;
 851     }
 852 
 853     ret = krb5_checksumsize(context, cktype, cksumsize);
 854     if (ret)
 855         return ret;
 856 
 857     *cksumtype = (uint32_t)cktype;
 858 
 859     return 0;
 860 }
 861 
 862 krb5_error_code
 863 _krb5_pac_sign(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 864                krb5_pac p,
 865                time_t authtime,
 866                krb5_principal principal,
 867                const krb5_keyblock *server_key,
 868                const krb5_keyblock *priv_key,
 869                krb5_data *data)
 870 {
 871     krb5_error_code ret;
 872     krb5_storage *sp = NULL, *spdata = NULL;
 873     uint32_t end;
 874     size_t server_size, priv_size;
 875     uint32_t server_offset = 0, priv_offset = 0;
 876     uint32_t server_cksumtype = 0, priv_cksumtype = 0;
 877     int i, num = 0;
 878     krb5_data logon, d;
 879 
 880     krb5_data_zero(&logon);
 881 
 882     if (p->logon_name == NULL)
 883         num++;
 884     if (p->server_checksum == NULL)
 885         num++;
 886     if (p->privsvr_checksum == NULL)
 887         num++;
 888 
 889     if (num) {
 890         void *ptr;
 891 
 892         ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1)));
 893         if (ptr == NULL) {
 894             krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 895             return ENOMEM;
 896         }
 897         p->pac = ptr;
 898 
 899         if (p->logon_name == NULL) {
 900             p->logon_name = &p->pac->buffers[p->pac->numbuffers++];
 901             memset(p->logon_name, 0, sizeof(*p->logon_name));
 902             p->logon_name->type = PAC_LOGON_NAME;
 903         }
 904         if (p->server_checksum == NULL) {
 905             p->server_checksum = &p->pac->buffers[p->pac->numbuffers++];
 906             memset(p->server_checksum, 0, sizeof(*p->server_checksum));
 907             p->server_checksum->type = PAC_SERVER_CHECKSUM;
 908         }
 909         if (p->privsvr_checksum == NULL) {
 910             p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++];
 911             memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum));
 912             p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM;
 913         }
 914     }
 915 
 916     /* Calculate LOGON NAME */
 917     ret = build_logon_name(context, authtime, principal, &logon);
 918     if (ret)
 919         goto out;
 920 
 921     /* Set lengths for checksum */
 922     ret = pac_checksum(context, server_key, &server_cksumtype, &server_size);
 923     if (ret)
 924         goto out;
 925     ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size);
 926     if (ret)
 927         goto out;
 928 
 929     /* Encode PAC */
 930     sp = krb5_storage_emem();
 931     if (sp == NULL) {
 932         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 933         return ENOMEM;
 934     }
 935     krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
 936 
 937     spdata = krb5_storage_emem();
 938     if (spdata == NULL) {
 939         krb5_storage_free(sp);
 940         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
 941         return ENOMEM;
 942     }
 943     krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE);
 944 
 945     CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out);
 946     CHECK(ret, krb5_store_uint32(sp, p->pac->version), out);
 947 
 948     end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
 949 
 950     for (i = 0; i < p->pac->numbuffers; i++) {
 951         uint32_t len;
 952         size_t sret;
 953         void *ptr = NULL;
 954 
 955         /* store data */
 956 
 957         if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
 958             len = server_size + 4;
 959             server_offset = end + 4;
 960             CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out);
 961             CHECK(ret, fill_zeros(context, spdata, server_size), out);
 962         } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
 963             len = priv_size + 4;
 964             priv_offset = end + 4;
 965             CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
 966             CHECK(ret, fill_zeros(context, spdata, priv_size), out);
 967         } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
 968             len = krb5_storage_write(spdata, logon.data, logon.length);
 969             if (logon.length != len) {
 970                 ret = EINVAL;
 971                 goto out;
 972             }
 973         } else {
 974             len = p->pac->buffers[i].buffersize;
 975             ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo;
 976 
 977             sret = krb5_storage_write(spdata, ptr, len);
 978             if (sret != len) {
 979                 ret = ENOMEM;
 980                 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
 981                 goto out;
 982             }
 983             /* XXX if not aligned, fill_zeros */
 984         }
 985 
 986         /* write header */
 987         CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out);
 988         CHECK(ret, krb5_store_uint32(sp, len), out);
 989         CHECK(ret, krb5_store_uint32(sp, end), out);
 990         CHECK(ret, krb5_store_uint32(sp, 0), out);
 991 
 992         /* advance data endpointer and align */
 993         {
 994             int32_t e;
 995 
 996             end += len;
 997             e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
 998             if (end != e) {
 999                 CHECK(ret, fill_zeros(context, spdata, e - end), out);
1000             }
1001             end = e;
1002         }
1003 
1004     }
1005 
1006     /* assert (server_offset != 0 && priv_offset != 0); */
1007 
1008     /* export PAC */
1009     ret = krb5_storage_to_data(spdata, &d);
1010     if (ret) {
1011         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1012         goto out;
1013     }
1014     ret = krb5_storage_write(sp, d.data, d.length);
1015     if (ret != d.length) {
1016         krb5_data_free(&d);
1017         ret = ENOMEM;
1018         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1019         goto out;
1020     }
1021     krb5_data_free(&d);
1022 
1023     ret = krb5_storage_to_data(sp, &d);
1024     if (ret) {
1025         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1026         goto out;
1027     }
1028 
1029     /* sign */
1030 
1031     ret = create_checksum(context, server_key,
1032                           d.data, d.length,
1033                           (char *)d.data + server_offset, server_size);
1034     if (ret) {
1035         krb5_data_free(&d);
1036         goto out;
1037     }
1038 
1039     ret = create_checksum(context, priv_key,
1040                           (char *)d.data + server_offset, server_size,
1041                           (char *)d.data + priv_offset, priv_size);
1042     if (ret) {
1043         krb5_data_free(&d);
1044         goto out;
1045     }
1046 
1047     /* done */
1048     *data = d;
1049 
1050     krb5_data_free(&logon);
1051     krb5_storage_free(sp);
1052     krb5_storage_free(spdata);
1053 
1054     return 0;
1055 out:
1056     krb5_data_free(&logon);
1057     if (sp)
1058         krb5_storage_free(sp);
1059     if (spdata)
1060         krb5_storage_free(spdata);
1061     return ret;
1062 }

/* [<][>][^][v][top][bottom][index][help] */