1 /*
   2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * ===========================================================================
  28  * (C) Copyright IBM Corp. 2000 All Rights Reserved.
  29  * ===========================================================================
  30  */
  31 
  32 #define UNICODE
  33 #define _UNICODE
  34 
  35 #include <windows.h>
  36 #include <stdio.h>
  37 #include <string.h>
  38 #define SECURITY_WIN32
  39 #include <security.h>
  40 #include <ntsecapi.h>
  41 #include <dsgetdc.h>
  42 #include <lmcons.h>
  43 #include <lmapibuf.h>
  44 #include <jni.h>
  45 #include <winsock.h>
  46 
  47 #undef LSA_SUCCESS
  48 #define LSA_SUCCESS(Status) ((Status) >= 0)
  49 #define EXIT_FAILURE -1 // mdu
  50 
  51 /*
  52  * Library-wide static references
  53  */
  54 
  55 jclass derValueClass = NULL;
  56 jclass ticketClass = NULL;
  57 jclass principalNameClass = NULL;
  58 jclass encryptionKeyClass = NULL;
  59 jclass ticketFlagsClass = NULL;
  60 jclass kerberosTimeClass = NULL;
  61 jclass javaLangStringClass = NULL;
  62 
  63 jmethodID derValueConstructor = 0;
  64 jmethodID ticketConstructor = 0;
  65 jmethodID principalNameConstructor = 0;
  66 jmethodID encryptionKeyConstructor = 0;
  67 jmethodID ticketFlagsConstructor = 0;
  68 jmethodID kerberosTimeConstructor = 0;
  69 jmethodID krbcredsConstructor = 0;
  70 
  71 /*
  72  * Function prototypes for internal routines
  73  *
  74  */
  75 BOOL native_debug = 0;
  76 
  77 BOOL PackageConnectLookup(PHANDLE,PULONG);
  78 
  79 NTSTATUS ConstructTicketRequest(UNICODE_STRING DomainName,
  80                                 PKERB_RETRIEVE_TKT_REQUEST *outRequest,
  81                                 ULONG *outSize);
  82 
  83 DWORD ConcatenateUnicodeStrings(UNICODE_STRING *pTarget,
  84                                 UNICODE_STRING Source1,
  85                                 UNICODE_STRING Source2);
  86 
  87 VOID ShowNTError(LPSTR,NTSTATUS);
  88 
  89 VOID
  90 InitUnicodeString(
  91     PUNICODE_STRING DestinationString,
  92     PCWSTR SourceString OPTIONAL
  93 );
  94 
  95 jobject BuildTicket(JNIEnv *env, PUCHAR encodedTicket, ULONG encodedTicketSize);
  96 
  97 //mdu
  98 jobject BuildPrincipal(JNIEnv *env, PKERB_EXTERNAL_NAME principalName,
  99                                 UNICODE_STRING domainName);
 100 
 101 jobject BuildEncryptionKey(JNIEnv *env, PKERB_CRYPTO_KEY cryptoKey);
 102 jobject BuildTicketFlags(JNIEnv *env, PULONG flags);
 103 jobject BuildKerberosTime(JNIEnv *env, PLARGE_INTEGER kerbtime);
 104 
 105 /*
 106  * Class:     sun_security_krb5_KrbCreds
 107  * Method:    JNI_OnLoad
 108  */
 109 
 110 JNIEXPORT jint JNICALL JNI_OnLoad(
 111         JavaVM  *jvm,
 112         void    *reserved) {
 113 
 114     jclass cls;
 115     JNIEnv *env;
 116     jfieldID fldDEBUG;
 117 
 118     if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2)) {
 119         return JNI_EVERSION; /* JNI version not supported */
 120     }
 121 
 122     cls = (*env)->FindClass(env,"sun/security/krb5/internal/Krb5");
 123     if (cls == NULL) {
 124         printf("LSA: Couldn't find Krb5\n");
 125         return JNI_ERR;
 126     }
 127     fldDEBUG = (*env)->GetStaticFieldID(env, cls, "DEBUG", "Z");
 128     if (fldDEBUG == NULL) {
 129         printf("LSA: Krb5 has no DEBUG field\n");
 130         return JNI_ERR;
 131     }
 132     native_debug = (*env)->GetStaticBooleanField(env, cls, fldDEBUG);
 133 
 134     cls = (*env)->FindClass(env,"sun/security/krb5/internal/Ticket");
 135 
 136     if (cls == NULL) {
 137         printf("LSA: Couldn't find Ticket\n");
 138         return JNI_ERR;
 139     }
 140     if (native_debug) {
 141         printf("LSA: Found Ticket\n");
 142     }
 143 
 144     ticketClass = (*env)->NewWeakGlobalRef(env,cls);
 145     if (ticketClass == NULL) {
 146         return JNI_ERR;
 147     }
 148     if (native_debug) {
 149         printf("LSA: Made NewWeakGlobalRef\n");
 150     }
 151 
 152     cls = (*env)->FindClass(env, "sun/security/krb5/PrincipalName");
 153 
 154     if (cls == NULL) {
 155         printf("LSA: Couldn't find PrincipalName\n");
 156         return JNI_ERR;
 157     }
 158     if (native_debug) {
 159         printf("LSA: Found PrincipalName\n");
 160     }
 161 
 162     principalNameClass = (*env)->NewWeakGlobalRef(env,cls);
 163     if (principalNameClass == NULL) {
 164         return JNI_ERR;
 165     }
 166     if (native_debug) {
 167         printf("LSA: Made NewWeakGlobalRef\n");
 168     }
 169 
 170     cls = (*env)->FindClass(env,"sun/security/util/DerValue");
 171 
 172     if (cls == NULL) {
 173         printf("LSA: Couldn't find DerValue\n");
 174         return JNI_ERR;
 175     }
 176     if (native_debug) {
 177         printf("LSA: Found DerValue\n");
 178     }
 179 
 180     derValueClass = (*env)->NewWeakGlobalRef(env,cls);
 181     if (derValueClass == NULL) {
 182         return JNI_ERR;
 183     }
 184     if (native_debug) {
 185         printf("LSA: Made NewWeakGlobalRef\n");
 186     }
 187 
 188     cls = (*env)->FindClass(env,"sun/security/krb5/EncryptionKey");
 189 
 190     if (cls == NULL) {
 191         printf("LSA: Couldn't find EncryptionKey\n");
 192         return JNI_ERR;
 193     }
 194     if (native_debug) {
 195         printf("LSA: Found EncryptionKey\n");
 196     }
 197 
 198     encryptionKeyClass = (*env)->NewWeakGlobalRef(env,cls);
 199     if (encryptionKeyClass == NULL) {
 200         return JNI_ERR;
 201     }
 202     if (native_debug) {
 203         printf("LSA: Made NewWeakGlobalRef\n");
 204     }
 205 
 206     cls = (*env)->FindClass(env,"sun/security/krb5/internal/TicketFlags");
 207 
 208     if (cls == NULL) {
 209         printf("LSA: Couldn't find TicketFlags\n");
 210         return JNI_ERR;
 211     }
 212     if (native_debug) {
 213         printf("LSA: Found TicketFlags\n");
 214     }
 215 
 216     ticketFlagsClass = (*env)->NewWeakGlobalRef(env,cls);
 217     if (ticketFlagsClass == NULL) {
 218         return JNI_ERR;
 219     }
 220     if (native_debug) {
 221         printf("LSA: Made NewWeakGlobalRef\n");
 222     }
 223 
 224     cls = (*env)->FindClass(env,"sun/security/krb5/internal/KerberosTime");
 225 
 226     if (cls == NULL) {
 227         printf("LSA: Couldn't find KerberosTime\n");
 228         return JNI_ERR;
 229     }
 230     if (native_debug) {
 231         printf("LSA: Found KerberosTime\n");
 232     }
 233 
 234     kerberosTimeClass = (*env)->NewWeakGlobalRef(env,cls);
 235     if (kerberosTimeClass == NULL) {
 236         return JNI_ERR;
 237     }
 238     if (native_debug) {
 239         printf("LSA: Made NewWeakGlobalRef\n");
 240     }
 241 
 242     cls = (*env)->FindClass(env,"java/lang/String");
 243 
 244     if (cls == NULL) {
 245         printf("LSA: Couldn't find String\n");
 246         return JNI_ERR;
 247     }
 248     if (native_debug) {
 249         printf("LSA: Found String\n");
 250     }
 251 
 252     javaLangStringClass = (*env)->NewWeakGlobalRef(env,cls);
 253     if (javaLangStringClass == NULL) {
 254         return JNI_ERR;
 255     }
 256     if (native_debug) {
 257         printf("LSA: Made NewWeakGlobalRef\n");
 258     }
 259 
 260     derValueConstructor = (*env)->GetMethodID(env, derValueClass,
 261                                             "<init>", "([B)V");
 262     if (derValueConstructor == 0) {
 263         printf("LSA: Couldn't find DerValue constructor\n");
 264         return JNI_ERR;
 265     }
 266     if (native_debug) {
 267         printf("LSA: Found DerValue constructor\n");
 268     }
 269 
 270     ticketConstructor = (*env)->GetMethodID(env, ticketClass,
 271                             "<init>", "(Lsun/security/util/DerValue;)V");
 272     if (ticketConstructor == 0) {
 273         printf("LSA: Couldn't find Ticket constructor\n");
 274         return JNI_ERR;
 275     }
 276     if (native_debug) {
 277         printf("LSA: Found Ticket constructor\n");
 278     }
 279 
 280     principalNameConstructor = (*env)->GetMethodID(env, principalNameClass,
 281                         "<init>", "([Ljava/lang/String;Ljava/lang/String;)V");
 282     if (principalNameConstructor == 0) {
 283         printf("LSA: Couldn't find PrincipalName constructor\n");
 284         return JNI_ERR;
 285     }
 286     if (native_debug) {
 287         printf("LSA: Found PrincipalName constructor\n");
 288     }
 289 
 290     encryptionKeyConstructor = (*env)->GetMethodID(env, encryptionKeyClass,
 291                                             "<init>", "(I[B)V");
 292     if (encryptionKeyConstructor == 0) {
 293         printf("LSA: Couldn't find EncryptionKey constructor\n");
 294         return JNI_ERR;
 295     }
 296     if (native_debug) {
 297         printf("LSA: Found EncryptionKey constructor\n");
 298     }
 299 
 300     ticketFlagsConstructor = (*env)->GetMethodID(env, ticketFlagsClass,
 301                                             "<init>", "(I[B)V");
 302     if (ticketFlagsConstructor == 0) {
 303         printf("LSA: Couldn't find TicketFlags constructor\n");
 304         return JNI_ERR;
 305     }
 306     if (native_debug) {
 307         printf("LSA: Found TicketFlags constructor\n");
 308     }
 309 
 310     kerberosTimeConstructor = (*env)->GetMethodID(env, kerberosTimeClass,
 311                                     "<init>", "(Ljava/lang/String;)V");
 312     if (kerberosTimeConstructor == 0) {
 313         printf("LSA: Couldn't find KerberosTime constructor\n");
 314         return JNI_ERR;
 315     }
 316     if (native_debug) {
 317         printf("LSA: Found KerberosTime constructor\n");
 318     }
 319 
 320     if (native_debug) {
 321         printf("LSA: Finished OnLoad processing\n");
 322     }
 323 
 324     return JNI_VERSION_1_2;
 325 }
 326 
 327 /*
 328  * Class:     sun_security_jgss_KrbCreds
 329  * Method:    JNI_OnUnload
 330  */
 331 
 332 JNIEXPORT void JNICALL JNI_OnUnload(
 333         JavaVM  *jvm,
 334         void    *reserved) {
 335 
 336     JNIEnv *env;
 337 
 338     if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2)) {
 339         return; /* Nothing else we can do */
 340     }
 341 
 342     if (ticketClass != NULL) {
 343         (*env)->DeleteWeakGlobalRef(env,ticketClass);
 344     }
 345     if (derValueClass != NULL) {
 346         (*env)->DeleteWeakGlobalRef(env,derValueClass);
 347     }
 348     if (principalNameClass != NULL) {
 349         (*env)->DeleteWeakGlobalRef(env,principalNameClass);
 350     }
 351     if (encryptionKeyClass != NULL) {
 352         (*env)->DeleteWeakGlobalRef(env,encryptionKeyClass);
 353     }
 354     if (ticketFlagsClass != NULL) {
 355         (*env)->DeleteWeakGlobalRef(env,ticketFlagsClass);
 356     }
 357     if (kerberosTimeClass != NULL) {
 358         (*env)->DeleteWeakGlobalRef(env,kerberosTimeClass);
 359     }
 360     if (javaLangStringClass != NULL) {
 361         (*env)->DeleteWeakGlobalRef(env,javaLangStringClass);
 362     }
 363 
 364     return;
 365 }
 366 
 367 /*
 368  * Class:     sun_security_krb5_Credentials
 369  * Method:    acquireDefaultNativeCreds
 370  * Signature: ([I])Lsun/security/krb5/Credentials;
 371  */
 372 JNIEXPORT jobject JNICALL Java_sun_security_krb5_Credentials_acquireDefaultNativeCreds(
 373         JNIEnv *env,
 374         jclass krbcredsClass,
 375         jintArray jetypes) {
 376 
 377     KERB_QUERY_TKT_CACHE_REQUEST CacheRequest;
 378     PKERB_RETRIEVE_TKT_RESPONSE TktCacheResponse = NULL;
 379     PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL;
 380     PKERB_RETRIEVE_TKT_RESPONSE pTicketResponse = NULL;
 381     NTSTATUS Status, SubStatus;
 382     ULONG requestSize = 0;
 383     ULONG responseSize = 0;
 384     ULONG rspSize = 0;
 385     HANDLE LogonHandle = NULL;
 386     ULONG PackageId;
 387     jobject ticket, clientPrincipal, targetPrincipal, encryptionKey;
 388     jobject ticketFlags, startTime, endTime, krbCreds = NULL;
 389     jobject authTime, renewTillTime, hostAddresses = NULL;
 390     KERB_EXTERNAL_TICKET *msticket;
 391     int found = 0;
 392     FILETIME Now, EndTime, LocalEndTime;
 393 
 394     int i, netypes;
 395     jint *etypes = NULL;
 396 
 397     while (TRUE) {
 398 
 399         if (krbcredsConstructor == 0) {
 400             krbcredsConstructor = (*env)->GetMethodID(env, krbcredsClass, "<init>",
 401                     "(Lsun/security/krb5/internal/Ticket;"
 402                     "Lsun/security/krb5/PrincipalName;"
 403                     "Lsun/security/krb5/PrincipalName;"
 404                     "Lsun/security/krb5/EncryptionKey;"
 405                     "Lsun/security/krb5/internal/TicketFlags;"
 406                     "Lsun/security/krb5/internal/KerberosTime;"
 407                     "Lsun/security/krb5/internal/KerberosTime;"
 408                     "Lsun/security/krb5/internal/KerberosTime;"
 409                     "Lsun/security/krb5/internal/KerberosTime;"
 410                     "Lsun/security/krb5/internal/HostAddresses;)V");
 411             if (krbcredsConstructor == 0) {
 412                 printf("LSA: Couldn't find sun.security.krb5.Credentials constructor\n");
 413                 break;
 414             }
 415         }
 416 
 417         if (native_debug) {
 418             printf("LSA: Found KrbCreds constructor\n");
 419         }
 420 
 421         //
 422         // Get the logon handle and package ID from the
 423         // Kerberos package
 424         //
 425         if (!PackageConnectLookup(&LogonHandle, &PackageId))
 426             break;
 427 
 428         if (native_debug) {
 429             printf("LSA: Got handle to Kerberos package\n");
 430         }
 431 
 432         // Get the MS TGT from cache
 433         CacheRequest.MessageType = KerbRetrieveTicketMessage;
 434         CacheRequest.LogonId.LowPart = 0;
 435         CacheRequest.LogonId.HighPart = 0;
 436 
 437         Status = LsaCallAuthenticationPackage(
 438                         LogonHandle,
 439                         PackageId,
 440                         &CacheRequest,
 441                         sizeof(CacheRequest),
 442                         &TktCacheResponse,
 443                         &rspSize,
 444                         &SubStatus
 445                         );
 446 
 447         if (native_debug) {
 448             printf("LSA: Response size is %d\n", rspSize);
 449         }
 450 
 451         if (!LSA_SUCCESS(Status) || !LSA_SUCCESS(SubStatus)) {
 452             if (!LSA_SUCCESS(Status)) {
 453                 ShowNTError("LsaCallAuthenticationPackage", Status);
 454             } else {
 455                 ShowNTError("Protocol status", SubStatus);
 456             }
 457             break;
 458         }
 459 
 460         // got the native MS TGT
 461         msticket = &(TktCacheResponse->Ticket);
 462 
 463         netypes = (*env)->GetArrayLength(env, jetypes);
 464         etypes = (jint *) (*env)->GetIntArrayElements(env, jetypes, NULL);
 465 
 466         if (etypes == NULL) {
 467             break;
 468         }
 469 
 470         // check TGT validity
 471         if (native_debug) {
 472             printf("LSA: TICKET SessionKey KeyType is %d\n", msticket->SessionKey.KeyType);
 473         }
 474 
 475         if ((msticket->TicketFlags & KERB_TICKET_FLAGS_invalid) == 0) {
 476             GetSystemTimeAsFileTime(&Now);
 477             EndTime.dwLowDateTime = msticket->EndTime.LowPart;
 478             EndTime.dwHighDateTime = msticket->EndTime.HighPart;
 479             FileTimeToLocalFileTime(&EndTime, &LocalEndTime);
 480             if (CompareFileTime(&Now, &LocalEndTime) < 0) {
 481                 for (i=0; i<netypes; i++) {
 482                     if (etypes[i] == msticket->SessionKey.KeyType) {
 483                         found = 1;
 484                         if (native_debug) {
 485                             printf("LSA: Valid etype found: %d\n", etypes[i]);
 486                         }
 487                         break;
 488                     }
 489                 }
 490             }
 491         }
 492 
 493         if (!found) {
 494             if (native_debug) {
 495                 printf("LSA: MS TGT in cache is invalid/not supported; request new ticket\n");
 496             }
 497 
 498             // use domain to request Ticket
 499             Status = ConstructTicketRequest(msticket->TargetDomainName,
 500                                 &pTicketRequest, &requestSize);
 501             if (!LSA_SUCCESS(Status)) {
 502                 ShowNTError("ConstructTicketRequest status", Status);
 503                 break;
 504             }
 505 
 506             pTicketRequest->MessageType = KerbRetrieveEncodedTicketMessage;
 507             pTicketRequest->CacheOptions = KERB_RETRIEVE_TICKET_DONT_USE_CACHE;
 508 
 509             for (i=0; i<netypes; i++) {
 510                 pTicketRequest->EncryptionType = etypes[i];
 511                 Status = LsaCallAuthenticationPackage(
 512                             LogonHandle,
 513                             PackageId,
 514                             pTicketRequest,
 515                             requestSize,
 516                             &pTicketResponse,
 517                             &responseSize,
 518                             &SubStatus
 519                             );
 520 
 521                 if (native_debug) {
 522                     printf("LSA: Response size is %d for %d\n", responseSize, etypes[i]);
 523                 }
 524 
 525                 if (!LSA_SUCCESS(Status) || !LSA_SUCCESS(SubStatus)) {
 526                     if (!LSA_SUCCESS(Status)) {
 527                         ShowNTError("LsaCallAuthenticationPackage", Status);
 528                     } else {
 529                         ShowNTError("Protocol status", SubStatus);
 530                     }
 531                     continue;
 532                 }
 533 
 534                 // got the native MS Kerberos TGT
 535                 msticket = &(pTicketResponse->Ticket);
 536 
 537                 if (msticket->SessionKey.KeyType != etypes[i]) {
 538                     if (native_debug) {
 539                         printf("LSA: Response etype is %d for %d. Retry.\n", msticket->SessionKey.KeyType, etypes[i]);
 540                     }
 541                     continue;
 542                 }
 543                 found = 1;
 544                 break;
 545             }
 546         }
 547 
 548         if (etypes != NULL) {
 549             (*env)->ReleaseIntArrayElements(env, jetypes, etypes, 0);
 550         }
 551 
 552         /*
 553 
 554         typedef struct _KERB_RETRIEVE_TKT_RESPONSE {
 555             KERB_EXTERNAL_TICKET Ticket;
 556         } KERB_RETRIEVE_TKT_RESPONSE, *PKERB_RETRIEVE_TKT_RESPONSE;
 557 
 558         typedef struct _KERB_EXTERNAL_TICKET {
 559             PKERB_EXTERNAL_NAME ServiceName;
 560             PKERB_EXTERNAL_NAME TargetName;
 561             PKERB_EXTERNAL_NAME ClientName;
 562             UNICODE_STRING DomainName;
 563             UNICODE_STRING TargetDomainName;
 564             UNICODE_STRING AltTargetDomainName;
 565             KERB_CRYPTO_KEY SessionKey;
 566             ULONG TicketFlags;
 567             ULONG Flags;
 568             LARGE_INTEGER KeyExpirationTime;
 569             LARGE_INTEGER StartTime;
 570             LARGE_INTEGER EndTime;
 571             LARGE_INTEGER RenewUntil;
 572             LARGE_INTEGER TimeSkew;
 573             ULONG EncodedTicketSize;
 574             PUCHAR EncodedTicket; <========== Here's the good stuff
 575         } KERB_EXTERNAL_TICKET, *PKERB_EXTERNAL_TICKET;
 576 
 577         typedef struct _KERB_EXTERNAL_NAME {
 578             SHORT NameType;
 579             USHORT NameCount;
 580             UNICODE_STRING Names[ANYSIZE_ARRAY];
 581         } KERB_EXTERNAL_NAME, *PKERB_EXTERNAL_NAME;
 582 
 583         typedef struct _LSA_UNICODE_STRING {
 584             USHORT Length;
 585             USHORT MaximumLength;
 586             PWSTR  Buffer;
 587         } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING;
 588 
 589         typedef LSA_UNICODE_STRING UNICODE_STRING, *PUNICODE_STRING;
 590 
 591         typedef struct KERB_CRYPTO_KEY {
 592             LONG KeyType;
 593             ULONG Length;
 594             PUCHAR Value;
 595         } KERB_CRYPTO_KEY, *PKERB_CRYPTO_KEY;
 596 
 597         */
 598         if (!found) {
 599             break;
 600         }
 601 
 602         // Build a com.sun.security.krb5.Ticket
 603         ticket = BuildTicket(env, msticket->EncodedTicket,
 604                                 msticket->EncodedTicketSize);
 605         if (ticket == NULL) {
 606             break;
 607         }
 608         // OK, have a Ticket, now need to get the client name
 609         clientPrincipal = BuildPrincipal(env, msticket->ClientName,
 610                                 msticket->TargetDomainName); // mdu
 611         if (clientPrincipal == NULL) {
 612             break;
 613         }
 614 
 615         // and the "name" of tgt
 616         targetPrincipal = BuildPrincipal(env, msticket->ServiceName,
 617                         msticket->DomainName);
 618         if (targetPrincipal == NULL) {
 619             break;
 620         }
 621 
 622         // Get the encryption key
 623         encryptionKey = BuildEncryptionKey(env, &(msticket->SessionKey));
 624         if (encryptionKey == NULL) {
 625             break;
 626         }
 627 
 628         // and the ticket flags
 629         ticketFlags = BuildTicketFlags(env, &(msticket->TicketFlags));
 630         if (ticketFlags == NULL) {
 631             break;
 632         }
 633 
 634         // Get the start time
 635         startTime = BuildKerberosTime(env, &(msticket->StartTime));
 636         if (startTime == NULL) {
 637             break;
 638         }
 639 
 640         /*
 641          * mdu: No point storing the eky expiration time in the auth
 642          * time field. Set it to be same as startTime. Looks like
 643          * windows does not have post-dated tickets.
 644          */
 645         authTime = startTime;
 646 
 647         // and the end time
 648         endTime = BuildKerberosTime(env, &(msticket->EndTime));
 649         if (endTime == NULL) {
 650             break;
 651         }
 652 
 653         // Get the renew till time
 654         renewTillTime = BuildKerberosTime(env, &(msticket->RenewUntil));
 655         if (renewTillTime == NULL) {
 656             break;
 657         }
 658 
 659         // and now go build a KrbCreds object
 660         krbCreds = (*env)->NewObject(
 661                 env,
 662                 krbcredsClass,
 663                 krbcredsConstructor,
 664                 ticket,
 665                 clientPrincipal,
 666                 targetPrincipal,
 667                 encryptionKey,
 668                 ticketFlags,
 669                 authTime, // mdu
 670                 startTime,
 671                 endTime,
 672                 renewTillTime, //mdu
 673                 hostAddresses);
 674 
 675         break;
 676     } // end of WHILE. This WHILE will never loop.
 677 
 678     // clean up resources
 679     if (TktCacheResponse != NULL) {
 680         LsaFreeReturnBuffer(TktCacheResponse);
 681     }
 682     if (pTicketRequest) {
 683         LocalFree(pTicketRequest);
 684     }
 685     if (pTicketResponse != NULL) {
 686         LsaFreeReturnBuffer(pTicketResponse);
 687     }
 688 
 689     return krbCreds;
 690 }
 691 
 692 static NTSTATUS
 693 ConstructTicketRequest(UNICODE_STRING DomainName,
 694                 PKERB_RETRIEVE_TKT_REQUEST *outRequest, ULONG *outSize)
 695 {
 696     NTSTATUS Status;
 697     UNICODE_STRING TargetPrefix;
 698     USHORT TargetSize;
 699     ULONG RequestSize;
 700     ULONG Length;
 701     PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL;
 702 
 703     *outRequest = NULL;
 704     *outSize = 0;
 705 
 706     //
 707     // Set up the "krbtgt/" target prefix into a UNICODE_STRING so we
 708     // can easily concatenate it later.
 709     //
 710 
 711     TargetPrefix.Buffer = L"krbtgt/";
 712     Length = (ULONG)wcslen(TargetPrefix.Buffer) * sizeof(WCHAR);
 713     TargetPrefix.Length = (USHORT)Length;
 714     TargetPrefix.MaximumLength = TargetPrefix.Length;
 715 
 716     //
 717     // We will need to concatenate the "krbtgt/" prefix and the
 718     // Logon Session's DnsDomainName into our request's target name.
 719     //
 720     // Therefore, first compute the necessary buffer size for that.
 721     //
 722     // Note that we might theoretically have integer overflow.
 723     //
 724 
 725     TargetSize = TargetPrefix.Length + DomainName.Length;
 726 
 727     //
 728     // The ticket request buffer needs to be a single buffer.  That buffer
 729     // needs to include the buffer for the target name.
 730     //
 731 
 732     RequestSize = sizeof (*pTicketRequest) + TargetSize;
 733 
 734     //
 735     // Allocate the request buffer and make sure it's zero-filled.
 736     //
 737 
 738     pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST)
 739                     LocalAlloc(LMEM_ZEROINIT, RequestSize);
 740     if (!pTicketRequest)
 741         return GetLastError();
 742 
 743     //
 744     // Concatenate the target prefix with the previous response's
 745     // target domain.
 746     //
 747 
 748     pTicketRequest->TargetName.Length = 0;
 749     pTicketRequest->TargetName.MaximumLength = TargetSize;
 750     pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1);
 751     Status = ConcatenateUnicodeStrings(&(pTicketRequest->TargetName),
 752                                     TargetPrefix,
 753                                     DomainName);
 754     *outRequest = pTicketRequest;
 755     *outSize    = RequestSize;
 756     return Status;
 757 }
 758 
 759 DWORD
 760 ConcatenateUnicodeStrings(
 761     UNICODE_STRING *pTarget,
 762     UNICODE_STRING Source1,
 763     UNICODE_STRING Source2
 764     )
 765 {
 766     //
 767     // The buffers for Source1 and Source2 cannot overlap pTarget's
 768     // buffer.  Source1.Length + Source2.Length must be <= 0xFFFF,
 769     // otherwise we overflow...
 770     //
 771 
 772     USHORT TotalSize = Source1.Length + Source2.Length;
 773     PBYTE buffer = (PBYTE) pTarget->Buffer;
 774 
 775     if (TotalSize > pTarget->MaximumLength)
 776         return ERROR_INSUFFICIENT_BUFFER;
 777 
 778     pTarget->Length = TotalSize;
 779     memcpy(buffer, Source1.Buffer, Source1.Length);
 780     memcpy(buffer + Source1.Length, Source2.Buffer, Source2.Length);
 781     return ERROR_SUCCESS;
 782 }
 783 
 784 BOOL
 785 PackageConnectLookup(
 786     HANDLE *pLogonHandle,
 787     ULONG *pPackageId
 788     )
 789 {
 790     LSA_STRING Name;
 791     NTSTATUS Status;
 792 
 793     Status = LsaConnectUntrusted(
 794                 pLogonHandle
 795                 );
 796 
 797     if (!LSA_SUCCESS(Status))
 798     {
 799         ShowNTError("LsaConnectUntrusted", Status);
 800         return FALSE;
 801     }
 802 
 803     Name.Buffer = MICROSOFT_KERBEROS_NAME_A;
 804     Name.Length = (USHORT)strlen(Name.Buffer);
 805     Name.MaximumLength = Name.Length + 1;
 806 
 807     Status = LsaLookupAuthenticationPackage(
 808                 *pLogonHandle,
 809                 &Name,
 810                 pPackageId
 811                 );
 812 
 813     if (!LSA_SUCCESS(Status))
 814     {
 815         ShowNTError("LsaLookupAuthenticationPackage", Status);
 816         return FALSE;
 817     }
 818 
 819     return TRUE;
 820 
 821 }
 822 
 823 VOID
 824 ShowLastError(
 825         LPSTR szAPI,
 826         DWORD dwError
 827         )
 828 {
 829     #define MAX_MSG_SIZE 256
 830 
 831     static WCHAR szMsgBuf[MAX_MSG_SIZE];
 832     DWORD dwRes;
 833 
 834     if (native_debug) {
 835         printf("LSA: Error calling function %s: %lu\n", szAPI, dwError);
 836     }
 837 
 838     dwRes = FormatMessage (
 839             FORMAT_MESSAGE_FROM_SYSTEM,
 840             NULL,
 841             dwError,
 842             0,
 843             szMsgBuf,
 844             MAX_MSG_SIZE,
 845             NULL);
 846     if (native_debug) {
 847         if (0 == dwRes) {
 848             printf("LSA: FormatMessage failed with %d\n", GetLastError());
 849             // ExitProcess(EXIT_FAILURE);
 850         } else {
 851             printf("LSA: %S",szMsgBuf);
 852         }
 853     }
 854 }
 855 
 856 VOID
 857 ShowNTError(
 858         LPSTR szAPI,
 859         NTSTATUS Status
 860         )
 861 {
 862     //
 863     // Convert the NTSTATUS to Winerror. Then call ShowLastError().
 864     //
 865     ShowLastError(szAPI, LsaNtStatusToWinError(Status));
 866 }
 867 
 868 VOID
 869 InitUnicodeString(
 870         PUNICODE_STRING DestinationString,
 871     PCWSTR SourceString OPTIONAL
 872     )
 873 {
 874     ULONG Length;
 875 
 876     DestinationString->Buffer = (PWSTR)SourceString;
 877     if (SourceString != NULL) {
 878         Length = (ULONG)wcslen( SourceString ) * sizeof( WCHAR );
 879         DestinationString->Length = (USHORT)Length;
 880         DestinationString->MaximumLength = (USHORT)(Length + sizeof(UNICODE_NULL));
 881     }
 882     else {
 883         DestinationString->MaximumLength = 0;
 884         DestinationString->Length = 0;
 885     }
 886 }
 887 
 888 jobject BuildTicket(JNIEnv *env, PUCHAR encodedTicket, ULONG encodedTicketSize) {
 889 
 890     /* To build a Ticket, we first need to build a DerValue out of the EncodedTicket.
 891      * But before we can do that, we need to make a byte array out of the ET.
 892      */
 893 
 894     jobject derValue, ticket;
 895     jbyteArray ary;
 896 
 897     ary = (*env)->NewByteArray(env,encodedTicketSize);
 898     if ((*env)->ExceptionOccurred(env)) {
 899         return (jobject) NULL;
 900     }
 901 
 902     (*env)->SetByteArrayRegion(env, ary, (jsize) 0, encodedTicketSize,
 903                                     (jbyte *)encodedTicket);
 904     if ((*env)->ExceptionOccurred(env)) {
 905         (*env)->DeleteLocalRef(env, ary);
 906         return (jobject) NULL;
 907     }
 908 
 909     derValue = (*env)->NewObject(env, derValueClass, derValueConstructor, ary);
 910     if ((*env)->ExceptionOccurred(env)) {
 911         (*env)->DeleteLocalRef(env, ary);
 912         return (jobject) NULL;
 913     }
 914 
 915     (*env)->DeleteLocalRef(env, ary);
 916     ticket = (*env)->NewObject(env, ticketClass, ticketConstructor, derValue);
 917     if ((*env)->ExceptionOccurred(env)) {
 918         (*env)->DeleteLocalRef(env, derValue);
 919         return (jobject) NULL;
 920     }
 921     (*env)->DeleteLocalRef(env, derValue);
 922     return ticket;
 923 }
 924 
 925 // mdu
 926 jobject BuildPrincipal(JNIEnv *env, PKERB_EXTERNAL_NAME principalName,
 927                                 UNICODE_STRING domainName) {
 928 
 929     /*
 930      * To build the Principal, we need to get the names out of
 931      * this goofy MS structure
 932      */
 933     jobject principal = NULL;
 934     jobject realmStr = NULL;
 935     jobjectArray stringArray;
 936     jstring tempString;
 937     int nameCount,i;
 938     PUNICODE_STRING scanner;
 939     WCHAR *realm;
 940     ULONG realmLen;
 941 
 942     realm = (WCHAR *) LocalAlloc(LMEM_ZEROINIT,
 943             ((domainName.Length)*sizeof(WCHAR) + sizeof(UNICODE_NULL)));
 944     wcsncpy(realm, domainName.Buffer, domainName.Length/sizeof(WCHAR));
 945 
 946     if (native_debug) {
 947         printf("LSA: Principal domain is %S\n", realm);
 948         printf("LSA: Name type is %x\n", principalName->NameType);
 949         printf("LSA: Name count is %x\n", principalName->NameCount);
 950     }
 951 
 952     nameCount = principalName->NameCount;
 953     stringArray = (*env)->NewObjectArray(env, nameCount,
 954                             javaLangStringClass, NULL);
 955     if (stringArray == NULL) {
 956         if (native_debug) {
 957             printf("LSA: Can't allocate String array for Principal\n");
 958         }
 959         goto cleanup;
 960     }
 961 
 962     for (i=0; i<nameCount; i++) {
 963         // get the principal name
 964         scanner = &(principalName->Names[i]);
 965 
 966         // OK, got a Char array, so construct a String
 967         tempString = (*env)->NewString(env, (const jchar*)scanner->Buffer,
 968                             scanner->Length/sizeof(WCHAR));
 969 
 970         if (tempString == NULL) {
 971             goto cleanup;
 972         }
 973 
 974         // Set the String into the StringArray
 975         (*env)->SetObjectArrayElement(env, stringArray, i, tempString);
 976 
 977         if ((*env)->ExceptionCheck(env)) {
 978             goto cleanup;
 979         }
 980 
 981         // Do I have to worry about storage reclamation here?
 982     }
 983     // now set the realm in the principal
 984     realmLen = (ULONG)wcslen((PWCHAR)realm);
 985     realmStr = (*env)->NewString(env, (PWCHAR)realm, (USHORT)realmLen);
 986 
 987     if (realmStr == NULL) {
 988         goto cleanup;
 989     }
 990 
 991     principal = (*env)->NewObject(env, principalNameClass,
 992                     principalNameConstructor, stringArray, realmStr);
 993 
 994 cleanup:
 995     // free local resources
 996     LocalFree(realm);
 997 
 998     return principal;
 999 }
1000 
1001 jobject BuildEncryptionKey(JNIEnv *env, PKERB_CRYPTO_KEY cryptoKey) {
1002     // First, need to build a byte array
1003     jbyteArray ary;
1004     jobject encryptionKey = NULL;
1005     unsigned int i;
1006 
1007     for (i=0; i<cryptoKey->Length; i++) {
1008         if (cryptoKey->Value[i]) break;
1009     }
1010     if (i == cryptoKey->Length) {
1011         if (native_debug) {
1012             printf("LSA: Session key all zero. Stop.\n");
1013         }
1014         return NULL;
1015     }
1016 
1017     ary = (*env)->NewByteArray(env,cryptoKey->Length);
1018     (*env)->SetByteArrayRegion(env, ary, (jsize) 0, cryptoKey->Length,
1019                                     (jbyte *)cryptoKey->Value);
1020     if ((*env)->ExceptionOccurred(env)) {
1021         (*env)->DeleteLocalRef(env, ary);
1022     } else {
1023         encryptionKey = (*env)->NewObject(env, encryptionKeyClass,
1024                 encryptionKeyConstructor, cryptoKey->KeyType, ary);
1025     }
1026 
1027     return encryptionKey;
1028 }
1029 
1030 jobject BuildTicketFlags(JNIEnv *env, PULONG flags) {
1031     jobject ticketFlags = NULL;
1032     jbyteArray ary;
1033     /*
1034      * mdu: Convert the bytes to nework byte order before copying
1035      * them to a Java byte array.
1036      */
1037     ULONG nlflags = htonl(*flags);
1038 
1039     ary = (*env)->NewByteArray(env, sizeof(*flags));
1040     (*env)->SetByteArrayRegion(env, ary, (jsize) 0, sizeof(*flags),
1041                                     (jbyte *)&nlflags);
1042     if ((*env)->ExceptionOccurred(env)) {
1043         (*env)->DeleteLocalRef(env, ary);
1044     } else {
1045         ticketFlags = (*env)->NewObject(env, ticketFlagsClass,
1046                 ticketFlagsConstructor, sizeof(*flags)*8, ary);
1047     }
1048 
1049     return ticketFlags;
1050 }
1051 
1052 jobject BuildKerberosTime(JNIEnv *env, PLARGE_INTEGER kerbtime) {
1053     jobject kerberosTime = NULL;
1054     jstring stringTime = NULL;
1055     SYSTEMTIME systemTime;
1056     WCHAR timeString[16];
1057     WCHAR month[3];
1058     WCHAR day[3];
1059     WCHAR hour[3];
1060     WCHAR minute[3];
1061     WCHAR second[3];
1062 
1063     if (FileTimeToSystemTime((FILETIME *)kerbtime, &systemTime)) {
1064         // XXX Cannot use %02.2ld, because the leading 0 is ignored for integers.
1065         // So, print them to strings, and then print them to the master string with a
1066         // format pattern that makes it two digits and prefix with a 0 if necessary.
1067         swprintf( (wchar_t *)month, 3, L"%2.2d", systemTime.wMonth);
1068         swprintf( (wchar_t *)day, 3, L"%2.2d", systemTime.wDay);
1069         swprintf( (wchar_t *)hour, 3, L"%2.2d", systemTime.wHour);
1070         swprintf( (wchar_t *)minute, 3, L"%2.2d", systemTime.wMinute);
1071         swprintf( (wchar_t *)second, 3, L"%2.2d", systemTime.wSecond);
1072         swprintf( (wchar_t *)timeString, 16,
1073                 L"%ld%02.2s%02.2s%02.2s%02.2s%02.2sZ",
1074                 systemTime.wYear,
1075                 month,
1076                 day,
1077                 hour,
1078                 minute,
1079                 second );
1080         if (native_debug) {
1081             printf("LSA: %S\n", (wchar_t *)timeString);
1082         }
1083         stringTime = (*env)->NewString(env, timeString,
1084                 (sizeof(timeString)/sizeof(WCHAR))-1);
1085         if (stringTime != NULL) { // everything's OK so far
1086             kerberosTime = (*env)->NewObject(env, kerberosTimeClass,
1087                     kerberosTimeConstructor, stringTime);
1088         }
1089     }
1090     return kerberosTime;
1091 }