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