// mdu jobject BuildTGSPrincipal(JNIEnv *env, UNICODE_STRING domainName) { jobject principal = NULL, stringArray; jstring tempString; int nameCount; PUNICODE_STRING scanner; UNICODE_STRING KRBTGT = {0}; InitUnicodeString(&KRBTGT, L"krbtgt"); stringArray = (*env)->NewObjectArray(env, 2, javaLangStringClass, NULL); if (stringArray == NULL) { printf("Can't allocate String array for TGS Principal\n"); return principal; } // OK, got a Char array, so construct a String scanner = &KRBTGT; tempString = (*env)->NewString(env, (const jchar*)scanner->Buffer, scanner->Length/sizeof(WCHAR)); // Set the String into the StringArray (*env)->SetObjectArrayElement(env, stringArray, 0, tempString); // Do I have to worry about storage reclamation here? // OK, got a Char array, so construct a String scanner = &domainName; // The unicode domain name tempString = (*env)->NewString(env, (const jchar*)scanner->Buffer, scanner->Length/sizeof(WCHAR)); // Set the String into the StringArray (*env)->SetObjectArrayElement(env, stringArray, 1, tempString); // Do I have to worry about storage reclamation here? principal = (*env)->NewObject(env, principalNameClass, principalNameConstructor, stringArray); return principal; }
DWORD GetEncodedTicket( HANDLE LogonHandle, ULONG PackageId, wchar_t *Server ) { NTSTATUS Status; PKERB_RETRIEVE_TKT_REQUEST CacheRequest = NULL; PKERB_RETRIEVE_TKT_RESPONSE CacheResponse = NULL; PKERB_EXTERNAL_TICKET Ticket; ULONG ResponseSize; NTSTATUS SubStatus; BOOLEAN Trusted = TRUE; BOOLEAN Success = FALSE; UNICODE_STRING Target = {0}; UNICODE_STRING Target2 = {0}; InitUnicodeString( &Target2, Server); CacheRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LMEM_ZEROINIT, Target2.Length + sizeof(KERB_RETRIEVE_TKT_REQUEST)); CacheRequest->MessageType = KerbRetrieveEncodedTicketMessage ; CacheRequest->LogonId.LowPart = 0; CacheRequest->LogonId.HighPart = 0; Target.Buffer = (LPWSTR) (CacheRequest + 1); Target.Length = Target2.Length; Target.MaximumLength = Target2.MaximumLength; CopyMemory( Target.Buffer, Target2.Buffer, Target2.Length ); CacheRequest->TargetName = Target; Status = LsaCallAuthenticationPackage( LogonHandle, PackageId, CacheRequest, Target2.Length + sizeof(KERB_RETRIEVE_TKT_REQUEST), (PVOID *) &CacheResponse, &ResponseSize, &SubStatus ); if (!SEC_SUCCESS(Status) || !SEC_SUCCESS(SubStatus)) { ShowNTError("LsaCallAuthenticationPackage", Status); printf("Substatus: 0x%x\n",SubStatus); ShowNTError("Substatus:", SubStatus); } else { Ticket = &(CacheResponse->Ticket); printf("\nEncoded Ticket:\n\n"); printf("ServiceName: "); PrintKerbName(Ticket->ServiceName); printf("TargetName: "); PrintKerbName(Ticket->TargetName); printf("ClientName: "); PrintKerbName(Ticket->ClientName); printf("DomainName: %.*S\n", Ticket->DomainName.Length/sizeof(WCHAR),Ticket->DomainName.Buffer); printf("TargetDomainName: %.*S\n", Ticket->TargetDomainName.Length/sizeof(WCHAR),Ticket->TargetDomainName.Buffer); printf("AltTargetDomainName: %.*S\n", Ticket->AltTargetDomainName.Length/sizeof(WCHAR),Ticket->AltTargetDomainName.Buffer); printf("TicketFlags: (0x%x) ",Ticket->TicketFlags); PrintTktFlags(Ticket->TicketFlags); PrintTime("KeyExpirationTime: ",Ticket->KeyExpirationTime); PrintTime("StartTime: ",Ticket->StartTime); PrintTime("EndTime: ",Ticket->EndTime); PrintTime("RenewUntil: ",Ticket->RenewUntil); PrintTime("TimeSkew: ",Ticket->TimeSkew); PrintEType(Ticket->SessionKey.KeyType); Success = TRUE; } if (CacheResponse) { LsaFreeReturnBuffer(CacheResponse); } if (CacheRequest) { LocalFree(CacheRequest); } return Success; }
int _tmain(int argc, TCHAR *argv[]) { BOOL bResult; NTSTATUS Status; NTSTATUS SubStatus; HANDLE hLsa = NULL; HANDLE hProcess = NULL; HANDLE hToken = NULL; HANDLE hTokenS4U = NULL; LSA_STRING Msv1_0Name = { 0 }; LSA_STRING OriginName = { 0 }; PMSV1_0_S4U_LOGON pS4uLogon = NULL; TOKEN_SOURCE TokenSource; ULONG ulAuthenticationPackage; DWORD dwMessageLength; PBYTE pbPosition; PROCESS_INFORMATION pi = { 0 }; STARTUPINFO si = { 0 }; PTOKEN_GROUPS pGroups = NULL; PSID pLogonSid = NULL; PSID pExtraSid = NULL; PVOID pvProfile = NULL; DWORD dwProfile = 0; LUID logonId = { 0 }; QUOTA_LIMITS quotaLimits; LPTSTR szCommandLine = NULL; LPTSTR szSrcCommandLine = TEXT("%systemroot%\\system32\\cmd.exe"); LPTSTR szDomain = NULL; LPTSTR szUsername = NULL; TCHAR seps[] = TEXT("\\"); TCHAR *next_token = NULL; g_hHeap = GetProcessHeap(); if (argc < 2) { fprintf(stderr, "Usage:\n s4u.exe Domain\\Username [Extra SID]\n\n"); goto End; } // // Get DOMAIN and USERNAME from command line. // szDomain = _tcstok_s(argv[1], seps, &next_token); if (szDomain == NULL) { fprintf(stderr, "Unable to parse command line.\n"); goto End; } szUsername = _tcstok_s(NULL, seps, &next_token); if (szUsername == NULL) { fprintf(stderr, "Unable to parse command line.\n"); goto End; } // // Activate the TCB privilege // hProcess = GetCurrentProcess(); OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if (!SetPrivilege(hToken, SE_TCB_NAME, TRUE)) { goto End; } // // Get logon SID // if (!GetLogonSID(hToken, &pLogonSid)) { fprintf(stderr, "Unable to find logon SID.\n"); goto End; } // // Connect (Untrusted) to LSA // Status = LsaConnectUntrusted(&hLsa); if (Status!=STATUS_SUCCESS) { fprintf(stderr, "LsaConnectUntrusted failed (error 0x%x).", Status); hLsa = NULL; goto End; } // // Lookup for the MSV1_0 authentication package (NTLMSSP) // InitLsaString(&Msv1_0Name, MSV1_0_PACKAGE_NAME); Status = LsaLookupAuthenticationPackage(hLsa, &Msv1_0Name, &ulAuthenticationPackage); if (Status!=STATUS_SUCCESS) { fprintf(stderr, "LsaLookupAuthenticationPackage failed (error 0x%x).", Status); hLsa = NULL; goto End; } // // Create MSV1_0_S4U_LOGON structure // dwMessageLength = sizeof(MSV1_0_S4U_LOGON) + (2 + wcslen(szDomain) + wcslen(szUsername)) * sizeof(WCHAR); pS4uLogon = (PMSV1_0_S4U_LOGON)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, dwMessageLength); if (pS4uLogon == NULL) { fprintf(stderr, "HeapAlloc failed (error %u).", GetLastError()); goto End; } pS4uLogon->MessageType = MsV1_0S4ULogon; pbPosition = (PBYTE)pS4uLogon + sizeof(MSV1_0_S4U_LOGON); pbPosition = InitUnicodeString(&pS4uLogon->UserPrincipalName, szUsername, pbPosition); pbPosition = InitUnicodeString(&pS4uLogon->DomainName, szDomain, pbPosition); // // Misc // strcpy_s(TokenSource.SourceName, TOKEN_SOURCE_LENGTH, "S4UWin"); InitLsaString(&OriginName, "S4U for Windows"); AllocateLocallyUniqueId(&TokenSource.SourceIdentifier); // // Add extra SID to token. // // If the application needs to connect to a Windows Desktop, Logon SID must be added to the Token. // pGroups = (PTOKEN_GROUPS)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, sizeof(TOKEN_GROUPS) + 2*sizeof(SID_AND_ATTRIBUTES)); if (pGroups == NULL) { fprintf(stderr, "HeapAlloc failed (error %u).", GetLastError()); goto End; } pGroups->GroupCount = 1; pGroups->Groups[0].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY; pGroups->Groups[0].Sid = pLogonSid; // // If an extra SID is specified to command line, add it to the pGroups structure. // if (argc==3) { bResult = ConvertStringSidToSid(argv[2], &pExtraSid); if (bResult == TRUE) { pGroups->GroupCount = 2; pGroups->Groups[1].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY; pGroups->Groups[1].Sid = pExtraSid; } else { fprintf(stderr, "Unable to convert SID (error %u).", GetLastError()); } } // // Call LSA // Status = LsaLogonUser( hLsa, &OriginName, Network, // Or Batch ulAuthenticationPackage, pS4uLogon, dwMessageLength, pGroups, // LocalGroups &TokenSource, // SourceContext &pvProfile, &dwProfile, &logonId, &hTokenS4U, "aLimits, &SubStatus ); if (Status!=STATUS_SUCCESS) { printf("LsaLogonUser failed (error 0x%x).\n", Status); goto End; } printf("LsaLogonUser: OK, LogonId: 0x%x-0x%x\n", logonId.HighPart, logonId.LowPart); // // Create process with S4U token. // si.cb = sizeof(STARTUPINFO); si.lpDesktop = TEXT("winsta0\\default"); // // Warning: szCommandLine parameter of CreateProcessAsUser() must be writable // szCommandLine = (LPTSTR)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, MAX_PATH * sizeof(TCHAR)); if (szCommandLine == NULL) { fprintf(stderr, "HeapAlloc failed (error %u).", GetLastError()); goto End; } if (ExpandEnvironmentStrings(szSrcCommandLine, szCommandLine, MAX_PATH) == 0) { fprintf(stderr, "ExpandEnvironmentStrings failed (error %u).", GetLastError()); goto End; } // // CreateProcessAsUser required SeAssignPrimaryTokenPrivilege but no need to be activated. // bResult = CreateProcessAsUser( hTokenS4U, NULL, szCommandLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, NULL, TEXT("c:\\"), &si, &pi ); if (bResult == FALSE) { printf("CreateProcessAsUser failed (error %u).\n", GetLastError()); goto End; } End: // // Free resources // if (Msv1_0Name.Buffer) HeapFree(g_hHeap, 0, Msv1_0Name.Buffer); if (OriginName.Buffer) HeapFree(g_hHeap, 0, OriginName.Buffer); if (pLogonSid) HeapFree(g_hHeap, 0, pLogonSid); if (pExtraSid) LocalFree(pExtraSid); if (pS4uLogon) HeapFree(g_hHeap, 0, pS4uLogon); if (pGroups) HeapFree(g_hHeap, 0, pGroups); if (pvProfile) LsaFreeReturnBuffer(pvProfile); if (hLsa) LsaClose(hLsa); if (hToken) CloseHandle(hToken); if (hTokenS4U) CloseHandle(hTokenS4U); if (pi.hProcess) CloseHandle(pi.hProcess); if (pi.hThread) CloseHandle(pi.hThread); return EXIT_SUCCESS; }
/* * 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) { HANDLE LogonHandle = NULL; ULONG PackageId; PKERB_RETRIEVE_TKT_REQUEST CacheRequest = NULL; PKERB_RETRIEVE_TKT_RESPONSE CacheResponse = NULL; ULONG rspSize = 0; DWORD errorCode; NTSTATUS Status,SubStatus; PUCHAR pEncodedTicket = NULL; jobject ticket, clientPrincipal, targetPrincipal, encryptionKey; jobject ticketFlags, startTime, endTime, krbCreds = NULL; jobject authTime, renewTillTime, hostAddresses = NULL; UNICODE_STRING Target = {0}; UNICODE_STRING Target2 = {0}; PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL; WCHAR *tgtName = L"krbtgt"; WCHAR *fullName; 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 com.ibm.security.krb5.Credentials constructor\n"); break; } } //printf("Found KrbCreds constructor\n"); // // 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 */ //InitUnicodeString(&Target2, L"krbtgt"); // this doesn't work 'cause I need the domain name too // OK, I don't give up that easily // Go get the current domain name errorCode = DsGetDcName( (LPCTSTR) NULL, // machine name (LPCTSTR) NULL, // DomainName, if NULL, I'm asking what it is (GUID *) NULL, // DomainGuid, (LPCTSTR) NULL, // SiteName, DS_GC_SERVER_REQUIRED, //Flags &DomainControllerInfo); if (errorCode != NO_ERROR) { printf("DsGetDcName returned %d\n", errorCode); break; } #ifdef DEBUG printf("The domain name is %S\n", DomainControllerInfo->DomainName); #endif /* DEBUG */ // Build a fully-qualified name fullName = (WCHAR *) LocalAlloc(LMEM_ZEROINIT,((wcslen(tgtName)+wcslen(L"/")+wcslen(DomainControllerInfo->DomainName)) * sizeof(WCHAR) + sizeof(UNICODE_NULL))); wcscat(fullName, tgtName); wcscat(fullName, L"/"); wcscat(fullName, DomainControllerInfo->DomainName); #ifdef DEBUG printf("The fully-qualified name is %S\n", fullName); #endif /* DEBUG */ InitUnicodeString(&Target2, fullName); CacheRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LMEM_ZEROINIT, Target2.Length + sizeof(KERB_RETRIEVE_TKT_REQUEST)); CacheRequest->MessageType = KerbRetrieveEncodedTicketMessage ; Target.Buffer = (LPWSTR) (CacheRequest + 1); Target.Length = Target2.Length; Target.MaximumLength = Target2.MaximumLength; CopyMemory( Target.Buffer, Target2.Buffer, Target2.Length ); CacheRequest->TargetName = Target; CacheRequest->EncryptionType = KERB_ETYPE_DES_CBC_MD5; // mdu Status = LsaCallAuthenticationPackage( LogonHandle, PackageId, CacheRequest, Target2.Length + sizeof(KERB_RETRIEVE_TKT_REQUEST), (PVOID *) &CacheResponse, &rspSize, &SubStatus ); #ifdef DEBUG printf("Response size is %d\n", rspSize); #endif /* DEBUG */ LocalFree(fullName); if (!LSA_SUCCESS(Status) || !LSA_SUCCESS(SubStatus)) { if (!LSA_SUCCESS(Status)) { ShowNTError("LsaCallAuthenticationPackage", Status); } else { ShowNTError("Protocol status", SubStatus); } break; } // Now we need to skip over most of the junk in the buffer to get to the ticket // Here's what we're looking at... /* 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.ibm.security.krb5.Ticket ticket = BuildTicket(env, CacheResponse->Ticket.EncodedTicket, CacheResponse->Ticket.EncodedTicketSize); if (ticket == NULL) { break; } // OK, have a Ticket, now need to get the client name clientPrincipal = BuildClientPrincipal(env, CacheResponse->Ticket.ClientName); // mdu if (clientPrincipal == NULL) break; // and the "name" of tgt targetPrincipal = BuildTGSPrincipal(env, CacheResponse->Ticket.TargetDomainName); // mdu if (targetPrincipal == NULL) break; // Get the encryption key encryptionKey = BuildEncryptionKey(env, &(CacheResponse->Ticket.SessionKey)); if (encryptionKey == NULL) break; // and the ticket flags ticketFlags = BuildTicketFlags(env, &(CacheResponse->Ticket.TicketFlags)); if (ticketFlags == NULL) break; // Get the start time startTime = BuildKerberosTime(env, &(CacheResponse->Ticket.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, &(CacheResponse->Ticket.EndTime)); if (endTime == NULL) break; // Get the renew till time renewTillTime = BuildKerberosTime(env, &(CacheResponse->Ticket.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 if (DomainControllerInfo != NULL) { NetApiBufferFree(DomainControllerInfo); } if (CacheResponse != NULL) { LsaFreeReturnBuffer(CacheResponse); } if (CacheRequest) { LocalFree(CacheRequest); } return krbCreds; }