jdk/src/windows/native/sun/security/krb5/NativeCreds.c
changeset 2 90ce3da70b43
child 73 cf334423502b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/native/sun/security/krb5/NativeCreds.c	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1019 @@
+/*
+ * Portions Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * ===========================================================================
+ * (C) Copyright IBM Corp. 2000 All Rights Reserved.
+ * ===========================================================================
+ */
+
+#define UNICODE
+#define _UNICODE
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <ntsecapi.h>
+#include <dsgetdc.h>
+#include <lmcons.h>
+#include <lmapibuf.h>
+#include <jni.h>
+#include <winsock.h>
+
+#undef LSA_SUCCESS
+#define LSA_SUCCESS(Status) ((Status) >= 0)
+#define EXIT_FAILURE -1 // mdu
+
+/*
+ * Library-wide static references
+ */
+
+jclass derValueClass = NULL;
+jclass ticketClass = NULL;
+jclass principalNameClass = NULL;
+jclass encryptionKeyClass = NULL;
+jclass ticketFlagsClass = NULL;
+jclass kerberosTimeClass = NULL;
+jclass javaLangStringClass = NULL;
+
+jmethodID derValueConstructor = 0;
+jmethodID ticketConstructor = 0;
+jmethodID principalNameConstructor = 0;
+jmethodID encryptionKeyConstructor = 0;
+jmethodID ticketFlagsConstructor = 0;
+jmethodID kerberosTimeConstructor = 0;
+jmethodID krbcredsConstructor = 0;
+jmethodID setRealmMethod = 0;
+
+/*
+ * Function prototypes for internal routines
+ *
+ */
+
+BOOL PackageConnectLookup(PHANDLE,PULONG);
+
+NTSTATUS ConstructTicketRequest(UNICODE_STRING DomainName,
+                                PKERB_RETRIEVE_TKT_REQUEST *outRequest,
+                                ULONG *outSize);
+
+DWORD ConcatenateUnicodeStrings(UNICODE_STRING *pTarget,
+                                UNICODE_STRING Source1,
+                                UNICODE_STRING Source2);
+
+VOID ShowNTError(LPSTR,NTSTATUS);
+
+VOID
+InitUnicodeString(
+        PUNICODE_STRING DestinationString,
+    PCWSTR SourceString OPTIONAL
+    );
+
+jobject BuildTicket(JNIEnv *env, PUCHAR encodedTicket, ULONG encodedTicketSize);
+
+//mdu
+jobject BuildPrincipal(JNIEnv *env, PKERB_EXTERNAL_NAME principalName,
+                                UNICODE_STRING domainName);
+
+jobject BuildEncryptionKey(JNIEnv *env, PKERB_CRYPTO_KEY cryptoKey);
+jobject BuildTicketFlags(JNIEnv *env, PULONG flags);
+jobject BuildKerberosTime(JNIEnv *env, PLARGE_INTEGER kerbtime);
+
+/*
+ * Class:     sun_security_krb5_KrbCreds
+ * Method:    JNI_OnLoad
+ */
+
+JNIEXPORT jint JNICALL JNI_OnLoad(
+                JavaVM  *jvm,
+                void    *reserved) {
+
+        jclass cls;
+        JNIEnv *env;
+
+        if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2)) {
+                return JNI_EVERSION; /* JNI version not supported */
+        }
+
+        cls = (*env)->FindClass(env,"sun/security/krb5/internal/Ticket");
+
+        if (cls == NULL) {
+                printf("Couldn't find Ticket\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found Ticket\n");
+        #endif /* DEBUG */
+
+        ticketClass = (*env)->NewWeakGlobalRef(env,cls);
+        if (ticketClass == NULL) {
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Made NewWeakGlobalRef\n");
+        #endif /* DEBUG */
+
+        cls = (*env)->FindClass(env, "sun/security/krb5/PrincipalName");
+
+        if (cls == NULL) {
+                printf("Couldn't find PrincipalName\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found PrincipalName\n");
+        #endif /* DEBUG */
+
+        principalNameClass = (*env)->NewWeakGlobalRef(env,cls);
+        if (principalNameClass == NULL) {
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Made NewWeakGlobalRef\n");
+        #endif /* DEBUG */
+
+        cls = (*env)->FindClass(env,"sun/security/util/DerValue");
+
+        if (cls == NULL) {
+                printf("Couldn't find DerValue\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found DerValue\n");
+        #endif /* DEBUG */
+
+        derValueClass = (*env)->NewWeakGlobalRef(env,cls);
+        if (derValueClass == NULL) {
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Made NewWeakGlobalRef\n");
+        #endif /* DEBUG */
+
+        cls = (*env)->FindClass(env,"sun/security/krb5/EncryptionKey");
+
+        if (cls == NULL) {
+                printf("Couldn't find EncryptionKey\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found EncryptionKey\n");
+        #endif /* DEBUG */
+
+        encryptionKeyClass = (*env)->NewWeakGlobalRef(env,cls);
+        if (encryptionKeyClass == NULL) {
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Made NewWeakGlobalRef\n");
+        #endif /* DEBUG */
+
+        cls = (*env)->FindClass(env,"sun/security/krb5/internal/TicketFlags");
+
+        if (cls == NULL) {
+                printf("Couldn't find TicketFlags\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found TicketFlags\n");
+        #endif /* DEBUG */
+
+        ticketFlagsClass = (*env)->NewWeakGlobalRef(env,cls);
+        if (ticketFlagsClass == NULL) {
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Made NewWeakGlobalRef\n");
+        #endif /* DEBUG */
+
+        cls = (*env)->FindClass(env,"sun/security/krb5/internal/KerberosTime");
+
+        if (cls == NULL) {
+                printf("Couldn't find KerberosTime\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found KerberosTime\n");
+        #endif /* DEBUG */
+
+        kerberosTimeClass = (*env)->NewWeakGlobalRef(env,cls);
+        if (kerberosTimeClass == NULL) {
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Made NewWeakGlobalRef\n");
+        #endif /* DEBUG */
+
+        cls = (*env)->FindClass(env,"java/lang/String");
+
+        if (cls == NULL) {
+                printf("Couldn't find String\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found String\n");
+        #endif /* DEBUG */
+
+        javaLangStringClass = (*env)->NewWeakGlobalRef(env,cls);
+        if (javaLangStringClass == NULL) {
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Made NewWeakGlobalRef\n");
+        #endif /* DEBUG */
+
+        derValueConstructor = (*env)->GetMethodID(env, derValueClass,
+                                                "<init>", "([B)V");
+        if (derValueConstructor == 0) {
+                printf("Couldn't find DerValue constructor\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found DerValue constructor\n");
+        #endif /* DEBUG */
+
+        ticketConstructor = (*env)->GetMethodID(env, ticketClass,
+                                "<init>", "(Lsun/security/util/DerValue;)V");
+        if (ticketConstructor == 0) {
+                printf("Couldn't find Ticket constructor\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found Ticket constructor\n");
+        #endif /* DEBUG */
+
+        principalNameConstructor = (*env)->GetMethodID(env, principalNameClass,
+                                        "<init>", "([Ljava/lang/String;)V");
+        if (principalNameConstructor == 0) {
+                printf("Couldn't find PrincipalName constructor\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found PrincipalName constructor\n");
+        #endif /* DEBUG */
+
+        encryptionKeyConstructor = (*env)->GetMethodID(env, encryptionKeyClass,
+                                                "<init>", "(I[B)V");
+        if (encryptionKeyConstructor == 0) {
+                printf("Couldn't find EncryptionKey constructor\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found EncryptionKey constructor\n");
+        #endif /* DEBUG */
+
+        ticketFlagsConstructor = (*env)->GetMethodID(env, ticketFlagsClass,
+                                                "<init>", "(I[B)V");
+        if (ticketFlagsConstructor == 0) {
+                printf("Couldn't find TicketFlags constructor\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found TicketFlags constructor\n");
+        #endif /* DEBUG */
+
+        kerberosTimeConstructor = (*env)->GetMethodID(env, kerberosTimeClass,
+                                        "<init>", "(Ljava/lang/String;)V");
+        if (kerberosTimeConstructor == 0) {
+                printf("Couldn't find KerberosTime constructor\n");
+                return JNI_ERR;
+        }
+        #ifdef DEBUG
+        printf("Found KerberosTime constructor\n");
+        #endif /* DEBUG */
+
+        // load the setRealm method in PrincipalName
+        setRealmMethod = (*env)->GetMethodID(env, principalNameClass,
+                                        "setRealm", "(Ljava/lang/String;)V");
+        if (setRealmMethod == 0) {
+                printf("Couldn't find setRealm in PrincipalName\n");
+                return JNI_ERR;
+        }
+
+        #ifdef DEBUG
+        printf("Finished OnLoad processing\n");
+        #endif /* DEBUG */
+
+        return JNI_VERSION_1_2;
+}
+
+/*
+ * Class:     sun_security_jgss_KrbCreds
+ * Method:    JNI_OnUnload
+ */
+
+JNIEXPORT void JNICALL JNI_OnUnload(
+                JavaVM  *jvm,
+                void    *reserved) {
+
+        JNIEnv *env;
+
+        if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2)) {
+                return; /* Nothing else we can do */
+        }
+
+        if (ticketClass != NULL) {
+                (*env)->DeleteWeakGlobalRef(env,ticketClass);
+        }
+        if (derValueClass != NULL) {
+                (*env)->DeleteWeakGlobalRef(env,derValueClass);
+        }
+        if (principalNameClass != NULL) {
+                (*env)->DeleteWeakGlobalRef(env,principalNameClass);
+        }
+        if (encryptionKeyClass != NULL) {
+                (*env)->DeleteWeakGlobalRef(env,encryptionKeyClass);
+        }
+        if (ticketFlagsClass != NULL) {
+                (*env)->DeleteWeakGlobalRef(env,ticketFlagsClass);
+        }
+        if (kerberosTimeClass != NULL) {
+                (*env)->DeleteWeakGlobalRef(env,kerberosTimeClass);
+        }
+        if (javaLangStringClass != NULL) {
+                (*env)->DeleteWeakGlobalRef(env,javaLangStringClass);
+        }
+
+        return;
+}
+
+/*
+ * Class:     sun_security_krb5_Credentials
+ * Method:    acquireDefaultNativeCreds
+ * Signature: ()Lsun/security/krb5/Credentials;
+ */
+JNIEXPORT jobject JNICALL Java_sun_security_krb5_Credentials_acquireDefaultNativeCreds(
+                JNIEnv *env,
+                jclass krbcredsClass) {
+
+        KERB_QUERY_TKT_CACHE_REQUEST CacheRequest;
+        PKERB_RETRIEVE_TKT_RESPONSE TktCacheResponse = NULL;
+        PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL;
+        PKERB_RETRIEVE_TKT_RESPONSE pTicketResponse = NULL;
+        NTSTATUS Status, SubStatus;
+        ULONG requestSize = 0;
+        ULONG responseSize = 0;
+        ULONG rspSize = 0;
+        HANDLE LogonHandle = NULL;
+        ULONG PackageId;
+        jobject ticket, clientPrincipal, targetPrincipal, encryptionKey;
+        jobject ticketFlags, startTime, endTime, krbCreds = NULL;
+        jobject authTime, renewTillTime, hostAddresses = NULL;
+        KERB_EXTERNAL_TICKET *msticket;
+        int ignore_cache = 0;
+        FILETIME Now, EndTime, LocalEndTime;
+
+        while (TRUE) {
+
+        if (krbcredsConstructor == 0) {
+                krbcredsConstructor = (*env)->GetMethodID(env, krbcredsClass, "<init>",
+        "(Lsun/security/krb5/internal/Ticket;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/EncryptionKey;Lsun/security/krb5/internal/TicketFlags;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/HostAddresses;)V");
+            if (krbcredsConstructor == 0) {
+                printf("Couldn't find sun.security.krb5.Credentials constructor\n");
+                break;
+            }
+        }
+
+        #ifdef DEBUG
+        printf("Found KrbCreds constructor\n");
+        #endif
+
+        //
+        // Get the logon handle and package ID from the
+        // Kerberos package
+        //
+        if (!PackageConnectLookup(&LogonHandle, &PackageId))
+            break;
+
+        #ifdef DEBUG
+        printf("Got handle to Kerberos package\n");
+        #endif /* DEBUG */
+
+        // Get the MS TGT from cache
+        CacheRequest.MessageType = KerbRetrieveTicketMessage;
+        CacheRequest.LogonId.LowPart = 0;
+        CacheRequest.LogonId.HighPart = 0;
+
+        Status = LsaCallAuthenticationPackage(
+                        LogonHandle,
+                        PackageId,
+                        &CacheRequest,
+                        sizeof(CacheRequest),
+                        &TktCacheResponse,
+                        &rspSize,
+                        &SubStatus
+                        );
+
+        #ifdef DEBUG
+        printf("Response size is %d\n", rspSize);
+        #endif
+
+        if (!LSA_SUCCESS(Status) || !LSA_SUCCESS(SubStatus)) {
+            if (!LSA_SUCCESS(Status)) {
+                ShowNTError("LsaCallAuthenticationPackage", Status);
+            } else {
+                ShowNTError("Protocol status", SubStatus);
+            }
+            break;
+        }
+
+        // got the native MS TGT
+        msticket = &(TktCacheResponse->Ticket);
+
+        // check TGT validity
+        switch (msticket->SessionKey.KeyType) {
+            case KERB_ETYPE_DES_CBC_CRC:
+            case KERB_ETYPE_DES_CBC_MD5:
+            case KERB_ETYPE_NULL:
+            case KERB_ETYPE_RC4_HMAC_NT:
+                GetSystemTimeAsFileTime(&Now);
+                EndTime.dwLowDateTime = msticket->EndTime.LowPart;
+                EndTime.dwHighDateTime = msticket->EndTime.HighPart;
+                FileTimeToLocalFileTime(&EndTime, &LocalEndTime);
+                if (CompareFileTime(&Now, &LocalEndTime) >= 0) {
+                    ignore_cache = 1;
+                }
+                if (msticket->TicketFlags & KERB_TICKET_FLAGS_invalid) {
+                    ignore_cache = 1;
+                }
+                break;
+            case KERB_ETYPE_RC4_MD4:
+            default:
+                // not supported
+                ignore_cache = 1;
+                break;
+        }
+
+        if (ignore_cache) {
+            #ifdef DEBUG
+            printf("MS TGT in cache is invalid/not supported; request new ticket\n");
+            #endif /* DEBUG */
+
+            // use domain to request Ticket
+            Status = ConstructTicketRequest(msticket->TargetDomainName,
+                                &pTicketRequest, &requestSize);
+            if (!LSA_SUCCESS(Status)) {
+                ShowNTError("ConstructTicketRequest status", Status);
+                break;
+            }
+
+            pTicketRequest->MessageType = KerbRetrieveEncodedTicketMessage;
+            pTicketRequest->EncryptionType = KERB_ETYPE_DES_CBC_MD5;
+            pTicketRequest->CacheOptions = KERB_RETRIEVE_TICKET_DONT_USE_CACHE;
+
+            Status = LsaCallAuthenticationPackage(
+                        LogonHandle,
+                        PackageId,
+                        pTicketRequest,
+                        requestSize,
+                        &pTicketResponse,
+                        &responseSize,
+                        &SubStatus
+                        );
+
+            #ifdef DEBUG
+            printf("Response size is %d\n", responseSize);
+            #endif /* DEBUG */
+
+            if (!LSA_SUCCESS(Status) || !LSA_SUCCESS(SubStatus)) {
+                if (!LSA_SUCCESS(Status)) {
+                    ShowNTError("LsaCallAuthenticationPackage", Status);
+                } else {
+                    ShowNTError("Protocol status", SubStatus);
+                }
+                break;
+            }
+
+            // got the native MS Kerberos TGT
+            msticket = &(pTicketResponse->Ticket);
+        }
+
+/*
+
+typedef struct _KERB_RETRIEVE_TKT_RESPONSE {
+    KERB_EXTERNAL_TICKET Ticket;
+} KERB_RETRIEVE_TKT_RESPONSE, *PKERB_RETRIEVE_TKT_RESPONSE;
+
+typedef struct _KERB_EXTERNAL_TICKET {
+    PKERB_EXTERNAL_NAME ServiceName;
+    PKERB_EXTERNAL_NAME TargetName;
+    PKERB_EXTERNAL_NAME ClientName;
+    UNICODE_STRING DomainName;
+    UNICODE_STRING TargetDomainName;
+    UNICODE_STRING AltTargetDomainName;
+    KERB_CRYPTO_KEY SessionKey;
+    ULONG TicketFlags;
+    ULONG Flags;
+    LARGE_INTEGER KeyExpirationTime;
+    LARGE_INTEGER StartTime;
+    LARGE_INTEGER EndTime;
+    LARGE_INTEGER RenewUntil;
+    LARGE_INTEGER TimeSkew;
+    ULONG EncodedTicketSize;
+    PUCHAR EncodedTicket; <========== Here's the good stuff
+} KERB_EXTERNAL_TICKET, *PKERB_EXTERNAL_TICKET;
+
+typedef struct _KERB_EXTERNAL_NAME {
+    SHORT NameType;
+    USHORT NameCount;
+    UNICODE_STRING Names[ANYSIZE_ARRAY];
+} KERB_EXTERNAL_NAME, *PKERB_EXTERNAL_NAME;
+
+typedef struct _LSA_UNICODE_STRING {
+    USHORT Length;
+    USHORT MaximumLength;
+    PWSTR  Buffer;
+} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING;
+
+typedef LSA_UNICODE_STRING UNICODE_STRING, *PUNICODE_STRING;
+
+typedef struct KERB_CRYPTO_KEY {
+    LONG KeyType;
+    ULONG Length;
+    PUCHAR Value;
+} KERB_CRYPTO_KEY, *PKERB_CRYPTO_KEY;
+
+*/
+        // Build a com.sun.security.krb5.Ticket
+        ticket = BuildTicket(env, msticket->EncodedTicket,
+                                msticket->EncodedTicketSize);
+        if (ticket == NULL) {
+                break;
+        }
+        // OK, have a Ticket, now need to get the client name
+        clientPrincipal = BuildPrincipal(env, msticket->ClientName,
+                                msticket->TargetDomainName); // mdu
+        if (clientPrincipal == NULL) {
+                break;
+        }
+
+        // and the "name" of tgt
+        targetPrincipal = BuildPrincipal(env, msticket->ServiceName,
+                        msticket->DomainName);
+        if (targetPrincipal == NULL) {
+                break;
+        }
+
+        // Get the encryption key
+        encryptionKey = BuildEncryptionKey(env, &(msticket->SessionKey));
+        if (encryptionKey == NULL) {
+                break;
+        }
+
+        // and the ticket flags
+        ticketFlags = BuildTicketFlags(env, &(msticket->TicketFlags));
+        if (ticketFlags == NULL) {
+                break;
+        }
+
+        // Get the start time
+        startTime = BuildKerberosTime(env, &(msticket->StartTime));
+        if (startTime == NULL) {
+                break;
+        }
+
+        /*
+         * mdu: No point storing the eky expiration time in the auth
+         * time field. Set it to be same as startTime. Looks like
+         * windows does not have post-dated tickets.
+         */
+        authTime = startTime;
+
+        // and the end time
+        endTime = BuildKerberosTime(env, &(msticket->EndTime));
+        if (endTime == NULL) {
+                break;
+        }
+
+        // Get the renew till time
+        renewTillTime = BuildKerberosTime(env, &(msticket->RenewUntil));
+        if (renewTillTime == NULL) {
+                break;
+        }
+
+        // and now go build a KrbCreds object
+        krbCreds = (*env)->NewObject(
+                env,
+                krbcredsClass,
+                krbcredsConstructor,
+                ticket,
+                clientPrincipal,
+                targetPrincipal,
+                encryptionKey,
+                ticketFlags,
+                authTime, // mdu
+                startTime,
+                endTime,
+                renewTillTime, //mdu
+                hostAddresses);
+
+        break;
+        } // end of WHILE
+
+        // clean up resources
+        if (TktCacheResponse != NULL) {
+                LsaFreeReturnBuffer(TktCacheResponse);
+        }
+        if (pTicketRequest) {
+                LocalFree(pTicketRequest);
+        }
+        if (pTicketResponse != NULL) {
+                LsaFreeReturnBuffer(pTicketResponse);
+        }
+
+        return krbCreds;
+}
+
+static NTSTATUS
+ConstructTicketRequest(UNICODE_STRING DomainName,
+                PKERB_RETRIEVE_TKT_REQUEST *outRequest, ULONG *outSize)
+{
+        NTSTATUS Status;
+        UNICODE_STRING TargetPrefix;
+        USHORT TargetSize;
+        ULONG RequestSize;
+        ULONG Length;
+        PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL;
+
+        *outRequest = NULL;
+        *outSize = 0;
+
+        //
+        // Set up the "krbtgt/" target prefix into a UNICODE_STRING so we
+        // can easily concatenate it later.
+        //
+
+        TargetPrefix.Buffer = L"krbtgt/";
+        Length = (ULONG)wcslen(TargetPrefix.Buffer) * sizeof(WCHAR);
+        TargetPrefix.Length = (USHORT)Length;
+        TargetPrefix.MaximumLength = TargetPrefix.Length;
+
+        //
+        // We will need to concatenate the "krbtgt/" prefix and the
+        // Logon Session's DnsDomainName into our request's target name.
+        //
+        // Therefore, first compute the necessary buffer size for that.
+        //
+        // Note that we might theoretically have integer overflow.
+        //
+
+        TargetSize = TargetPrefix.Length + DomainName.Length;
+
+        //
+        // The ticket request buffer needs to be a single buffer.  That buffer
+        // needs to include the buffer for the target name.
+        //
+
+        RequestSize = sizeof (*pTicketRequest) + TargetSize;
+
+        //
+        // Allocate the request buffer and make sure it's zero-filled.
+        //
+
+        pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST)
+                        LocalAlloc(LMEM_ZEROINIT, RequestSize);
+        if (!pTicketRequest)
+            return GetLastError();
+
+        //
+        // Concatenate the target prefix with the previous reponse's
+        // target domain.
+        //
+
+        pTicketRequest->TargetName.Length = 0;
+        pTicketRequest->TargetName.MaximumLength = TargetSize;
+        pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1);
+        Status = ConcatenateUnicodeStrings(&(pTicketRequest->TargetName),
+                                        TargetPrefix,
+                                        DomainName);
+        *outRequest = pTicketRequest;
+        *outSize    = RequestSize;
+        return Status;
+}
+
+DWORD
+ConcatenateUnicodeStrings(
+    UNICODE_STRING *pTarget,
+    UNICODE_STRING Source1,
+    UNICODE_STRING Source2
+    )
+{
+        //
+        // The buffers for Source1 and Source2 cannot overlap pTarget's
+        // buffer.  Source1.Length + Source2.Length must be <= 0xFFFF,
+        // otherwise we overflow...
+        //
+
+        USHORT TotalSize = Source1.Length + Source2.Length;
+        PBYTE buffer = (PBYTE) pTarget->Buffer;
+
+        if (TotalSize > pTarget->MaximumLength)
+            return ERROR_INSUFFICIENT_BUFFER;
+
+        pTarget->Length = TotalSize;
+        memcpy(buffer, Source1.Buffer, Source1.Length);
+        memcpy(buffer + Source1.Length, Source2.Buffer, Source2.Length);
+        return ERROR_SUCCESS;
+}
+
+BOOL
+PackageConnectLookup(
+    HANDLE *pLogonHandle,
+    ULONG *pPackageId
+    )
+{
+    LSA_STRING Name;
+    NTSTATUS Status;
+
+    Status = LsaConnectUntrusted(
+                pLogonHandle
+                );
+
+    if (!LSA_SUCCESS(Status))
+    {
+        ShowNTError("LsaConnectUntrusted", Status);
+        return FALSE;
+    }
+
+    Name.Buffer = MICROSOFT_KERBEROS_NAME_A;
+    Name.Length = (USHORT)strlen(Name.Buffer);
+    Name.MaximumLength = Name.Length + 1;
+
+    Status = LsaLookupAuthenticationPackage(
+                *pLogonHandle,
+                &Name,
+                pPackageId
+                );
+
+    if (!LSA_SUCCESS(Status))
+    {
+        ShowNTError("LsaLookupAuthenticationPackage", Status);
+        return FALSE;
+    }
+
+    return TRUE;
+
+}
+
+VOID
+ShowLastError(
+        LPSTR szAPI,
+        DWORD dwError
+        )
+{
+   #define MAX_MSG_SIZE 256
+
+   static WCHAR szMsgBuf[MAX_MSG_SIZE];
+   DWORD dwRes;
+
+   printf("Error calling function %s: %lu\n", szAPI, dwError);
+
+   dwRes = FormatMessage (
+      FORMAT_MESSAGE_FROM_SYSTEM,
+      NULL,
+      dwError,
+      0,
+      szMsgBuf,
+      MAX_MSG_SIZE,
+      NULL);
+   if (0 == dwRes) {
+      printf("FormatMessage failed with %d\n", GetLastError());
+      // ExitProcess(EXIT_FAILURE);
+   } else {
+      printf("%S",szMsgBuf);
+   }
+}
+
+VOID
+ShowNTError(
+        LPSTR szAPI,
+        NTSTATUS Status
+        )
+{
+    //
+    // Convert the NTSTATUS to Winerror. Then call ShowLastError().
+    //
+    ShowLastError(szAPI, LsaNtStatusToWinError(Status));
+}
+
+VOID
+InitUnicodeString(
+        PUNICODE_STRING DestinationString,
+    PCWSTR SourceString OPTIONAL
+    )
+{
+    ULONG Length;
+
+    DestinationString->Buffer = (PWSTR)SourceString;
+    if (SourceString != NULL) {
+        Length = (ULONG)wcslen( SourceString ) * sizeof( WCHAR );
+        DestinationString->Length = (USHORT)Length;
+        DestinationString->MaximumLength = (USHORT)(Length + sizeof(UNICODE_NULL));
+        }
+    else {
+        DestinationString->MaximumLength = 0;
+        DestinationString->Length = 0;
+        }
+}
+
+jobject BuildTicket(JNIEnv *env, PUCHAR encodedTicket, ULONG encodedTicketSize) {
+
+        /* To build a Ticket, we first need to build a DerValue out of the EncodedTicket.
+         * But before we can do that, we need to make a byte array out of the ET.
+         */
+
+        jobject derValue, ticket;
+        jbyteArray ary;
+
+        ary = (*env)->NewByteArray(env,encodedTicketSize);
+        if ((*env)->ExceptionOccurred(env)) {
+                return (jobject) NULL;
+        }
+
+        (*env)->SetByteArrayRegion(env, ary, (jsize) 0, encodedTicketSize,
+                                        (jbyte *)encodedTicket);
+        if ((*env)->ExceptionOccurred(env)) {
+                (*env)->DeleteLocalRef(env, ary);
+                return (jobject) NULL;
+        }
+
+        derValue = (*env)->NewObject(env, derValueClass, derValueConstructor, ary);
+        if ((*env)->ExceptionOccurred(env)) {
+                (*env)->DeleteLocalRef(env, ary);
+                return (jobject) NULL;
+        }
+
+        (*env)->DeleteLocalRef(env, ary);
+        ticket = (*env)->NewObject(env, ticketClass, ticketConstructor, derValue);
+        if ((*env)->ExceptionOccurred(env)) {
+                (*env)->DeleteLocalRef(env, derValue);
+                return (jobject) NULL;
+        }
+        (*env)->DeleteLocalRef(env, derValue);
+        return ticket;
+}
+
+// mdu
+jobject BuildPrincipal(JNIEnv *env, PKERB_EXTERNAL_NAME principalName,
+                                UNICODE_STRING domainName) {
+
+        /*
+         * To build the Principal, we need to get the names out of
+         * this goofy MS structure
+         */
+        jobject principal = NULL;
+        jobject realmStr = NULL;
+        jobjectArray stringArray;
+        jstring tempString;
+        int nameCount,i;
+        PUNICODE_STRING scanner;
+        WCHAR *realm;
+        ULONG realmLen;
+
+        realm = (WCHAR *) LocalAlloc(LMEM_ZEROINIT,
+                ((domainName.Length)*sizeof(WCHAR) + sizeof(UNICODE_NULL)));
+        wcsncpy(realm, domainName.Buffer, domainName.Length/sizeof(WCHAR));
+
+        #ifdef DEBUG
+        printf("Principal domain is %S\n", realm);
+        printf("Name type is %x\n", principalName->NameType);
+        printf("Name count is %x\n", principalName->NameCount);
+        #endif
+
+        nameCount = principalName->NameCount;
+        stringArray = (*env)->NewObjectArray(env, nameCount,
+                                javaLangStringClass, NULL);
+        if (stringArray == NULL) {
+            printf("Can't allocate String array for Principal\n");
+            LocalFree(realm);
+            return principal;
+        }
+
+        for (i=0; i<nameCount; i++) {
+            // get the principal name
+            scanner = &(principalName->Names[i]);
+
+            // OK, got a Char array, so construct a String
+            tempString = (*env)->NewString(env, (const jchar*)scanner->Buffer,
+                                scanner->Length/sizeof(WCHAR));
+            // Set the String into the StringArray
+            (*env)->SetObjectArrayElement(env, stringArray, i, tempString);
+
+            // Do I have to worry about storage reclamation here?
+        }
+        principal = (*env)->NewObject(env, principalNameClass,
+                        principalNameConstructor, stringArray);
+
+        // now set the realm in the principal
+        realmLen = (ULONG)wcslen((PWCHAR)realm);
+        realmStr = (*env)->NewString(env, (PWCHAR)realm, (USHORT)realmLen);
+        (*env)->CallVoidMethod(env, principal, setRealmMethod, realmStr);
+
+        // free local resources
+        LocalFree(realm);
+
+        return principal;
+}
+
+jobject BuildEncryptionKey(JNIEnv *env, PKERB_CRYPTO_KEY cryptoKey) {
+        // First, need to build a byte array
+        jbyteArray ary;
+        jobject encryptionKey = NULL;
+
+        ary = (*env)->NewByteArray(env,cryptoKey->Length);
+        (*env)->SetByteArrayRegion(env, ary, (jsize) 0, cryptoKey->Length,
+                                        (jbyte *)cryptoKey->Value);
+        if ((*env)->ExceptionOccurred(env)) {
+                (*env)->DeleteLocalRef(env, ary);
+        } else {
+                encryptionKey = (*env)->NewObject(env, encryptionKeyClass,
+                        encryptionKeyConstructor, cryptoKey->KeyType, ary);
+        }
+
+        return encryptionKey;
+}
+
+jobject BuildTicketFlags(JNIEnv *env, PULONG flags) {
+        jobject ticketFlags = NULL;
+        jbyteArray ary;
+        /*
+         * mdu: Convert the bytes to nework byte order before copying
+         * them to a Java byte array.
+         */
+        ULONG nlflags = htonl(*flags);
+
+        ary = (*env)->NewByteArray(env, sizeof(*flags));
+        (*env)->SetByteArrayRegion(env, ary, (jsize) 0, sizeof(*flags),
+                                        (jbyte *)&nlflags);
+        if ((*env)->ExceptionOccurred(env)) {
+                (*env)->DeleteLocalRef(env, ary);
+        } else {
+                ticketFlags = (*env)->NewObject(env, ticketFlagsClass,
+                        ticketFlagsConstructor, sizeof(*flags)*8, ary);
+        }
+
+        return ticketFlags;
+}
+
+jobject BuildKerberosTime(JNIEnv *env, PLARGE_INTEGER kerbtime) {
+        jobject kerberosTime = NULL;
+        jstring stringTime = NULL;
+        SYSTEMTIME systemTime;
+        WCHAR timeString[16];
+        WCHAR month[3];
+        WCHAR day[3];
+        WCHAR hour[3];
+        WCHAR minute[3];
+        WCHAR second[3];
+
+        if (FileTimeToSystemTime((FILETIME *)kerbtime, &systemTime)) {
+// XXX Cannot use %02.2ld, because the leading 0 is ignored for integers.
+// So, print them to strings, and then print them to the master string with a
+// format pattern that makes it two digits and prefix with a 0 if necessary.
+                swprintf( (wchar_t *)month, L"%2.2d", systemTime.wMonth);
+                swprintf( (wchar_t *)day, L"%2.2d", systemTime.wDay);
+                swprintf( (wchar_t *)hour, L"%2.2d", systemTime.wHour);
+                swprintf( (wchar_t *)minute, L"%2.2d", systemTime.wMinute);
+                swprintf( (wchar_t *)second, L"%2.2d", systemTime.wSecond);
+                swprintf( (wchar_t *)timeString,
+                                L"%ld%02.2s%02.2s%02.2s%02.2s%02.2sZ",
+                systemTime.wYear,
+                month,
+                day,
+                hour,
+                minute,
+                second );
+                #ifdef DEBUG
+                printf("%S\n", (wchar_t *)timeString);
+                #endif /* DEBUG */
+                stringTime = (*env)->NewString(env, timeString,
+                                (sizeof(timeString)/sizeof(WCHAR))-1);
+                if (stringTime != NULL) { // everything's OK so far
+                        kerberosTime = (*env)->NewObject(env, kerberosTimeClass,
+                                kerberosTimeConstructor, stringTime);
+                }
+        }
+        return kerberosTime;
+}