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