/*++ Copyright (c) 2001 Microsoft Corporation All rights reserved Module Name: krbutils.cxx Abstract: utils Author: Larry Zhu (LZhu) December 1, 2001 Created Environment: User Mode -Win32 Revision History: --*/ #include "precomp.hxx" #pragma hdrstop #include "krbutils.hxx" #include "kerberr.hxx" VOID KerbFreeRealm( IN PKERB_REALM pRealm ) { if (*pRealm != NULL) { MIDL_user_free(*pRealm); *pRealm = NULL; } } VOID KerbFreePrincipalName( IN PKERB_PRINCIPAL_NAME pName ) { PKERB_PRINCIPAL_NAME_ELEM pElem, pNextElem; pElem = pName->name_string; while (pElem != NULL) { if (pElem->value != NULL) { MIDL_user_free(pElem->value); } pNextElem = pElem->next; MIDL_user_free(pElem); pElem = pNextElem; } pName->name_string = NULL; } VOID KerbFreeData( IN ULONG PduValue, IN PVOID pData ) { ASN1decoding_t pDec = NULL; if (pData) { TKerbErr KerbErr; KerbErr DBGCHK = KerbInitAsn( NULL, &pDec // this is a decoded structure ); if (KERB_SUCCESS(KerbErr)) { ASN1_FreeDecoded(pDec, pData, PduValue); } KerbTermAsn(NULL, pDec); } } BOOL fKRB5ModuleStarted = FALSE; KERBERR KerbInitAsn( IN OUT ASN1encoding_t * pEnc, IN OUT ASN1decoding_t * pDec ) { TKerbErr KerbErr = KRB_ERR_GENERIC; ASN1error_e Asn1Err; if (!fKRB5ModuleStarted) { fKRB5ModuleStarted = TRUE; KRB5_Module_Startup(); } if (pEnc != NULL) { Asn1Err = ASN1_CreateEncoder( KRB5_Module, pEnc, NULL, // pbBuf 0, // cbBufSize NULL // pParent ); } else { Asn1Err = ASN1_CreateDecoder( KRB5_Module, pDec, NULL, // pbBuf 0, // cbBufSize NULL // pParent ); } KerbErr DBGCHK = ASN1_SUCCESS == Asn1Err ? KDC_ERR_NONE : KRB_ERR_GENERIC; return KerbErr; } VOID KerbTermAsn( IN ASN1encoding_t pEnc, IN ASN1decoding_t pDec ) { if (pEnc != NULL) { ASN1_CloseEncoder(pEnc); } else if (pDec != NULL) { ASN1_CloseDecoder(pDec); } } KERBERR KerbEncryptDataEx( OUT PKERB_ENCRYPTED_DATA pEncryptedData, IN ULONG cbDataSize, IN PUCHAR Data, IN ULONG KeyVersion, IN ULONG UsageFlags, IN PKERB_ENCRYPTION_KEY pKey ) { PCRYPTO_SYSTEM pcsCrypt = NULL; PCRYPT_STATE_BUFFER psbCryptBuffer = NULL; NTSTATUS Status = STATUS_SUCCESS; Status = CDLocateCSystem(pKey->keytype, &pcsCrypt); if (!NT_SUCCESS(Status)) { return(KDC_ERR_ETYPE_NOTSUPP); } // // Initialize header // pEncryptedData->encryption_type = pKey->keytype; Status = pcsCrypt->Initialize( (PUCHAR) pKey->keyvalue.value, pKey->keyvalue.length, UsageFlags, &psbCryptBuffer ); if (!NT_SUCCESS(Status)) { return(KRB_ERR_GENERIC); } Status = pcsCrypt->Encrypt( psbCryptBuffer, Data, cbDataSize, pEncryptedData->cipher_text.value, &pEncryptedData->cipher_text.length ); (void) pcsCrypt->Discard(&psbCryptBuffer); if (!NT_SUCCESS(Status)) { return(KRB_ERR_GENERIC); } if (KeyVersion != KERB_NO_KEY_VERSION) { pEncryptedData->version = KeyVersion; pEncryptedData->bit_mask |= version_present; } return KDC_ERR_NONE; } KERBERR KerbAllocateEncryptionBuffer( IN ULONG EncryptionType, IN ULONG cbBufferSize, OUT PUINT pcbEncryptionBufferSize, OUT PBYTE* pEncryptionBuffer ) { TKerbErr KerbErr = KDC_ERR_NONE; ULONG cbEncryptionOverhead = 0; ULONG cbBlockSize = 0; KerbErr DBGCHK = KerbGetEncryptionOverhead( EncryptionType, &cbEncryptionOverhead, &cbBlockSize ); if (KERB_SUCCESS(KerbErr)) { *pcbEncryptionBufferSize = (UINT) ROUND_UP_COUNT(cbEncryptionOverhead + cbBufferSize, cbBlockSize); *pEncryptionBuffer = (PBYTE) MIDL_user_allocate(*pcbEncryptionBufferSize); if (*pEncryptionBuffer == NULL) { KerbErr DBGCHK = KRB_ERR_GENERIC; } } return KerbErr; } KERBERR KerbAllocateEncryptionBufferWrapper( IN ULONG EncryptionType, IN ULONG cbBufferSize, OUT ULONG* pcbEncryptionBufferSize, OUT PBYTE* pEncryptionBuffer ) { TKerbErr KerbErr = KDC_ERR_NONE; UINT tempInt = 0; KerbErr DBGCHK = KerbAllocateEncryptionBuffer( EncryptionType, cbBufferSize, &tempInt, pEncryptionBuffer ); if (KERB_SUCCESS(KerbErr)) { *pcbEncryptionBufferSize = tempInt; } return KerbErr; } KERBERR KerbGetEncryptionOverhead( IN ULONG Algorithm, OUT PULONG pcbOverhead, OUT OPTIONAL PULONG pcbBlockSize ) { PCRYPTO_SYSTEM pcsCrypt; NTSTATUS Status = STATUS_SUCCESS; Status = CDLocateCSystem(Algorithm, &pcsCrypt); if (!NT_SUCCESS(Status)) { return (KDC_ERR_ETYPE_NOTSUPP); } *pcbOverhead = pcsCrypt->HeaderSize; if (pcbBlockSize) { *pcbBlockSize = pcsCrypt->BlockSize; } return (KDC_ERR_NONE); } KERBERR NTAPI KerbPackData( IN PVOID Data, IN ULONG PduValue, OUT PULONG pcbDataSize, OUT PUCHAR * MarshalledData ) { TKerbErr KerbErr = KDC_ERR_NONE; ASN1encoding_t pEnc = NULL; ASN1error_e Asn1Err; KerbErr DBGCHK = KerbInitAsn( &pEnc, // we are encoding NULL ); if (KERB_SUCCESS(KerbErr)) { // // Encode the data type. // Asn1Err = ASN1_Encode( pEnc, Data, PduValue, ASN1ENCODE_ALLOCATEBUFFER, NULL, // pbBuf 0 // cbBufSize ); if (!ASN1_SUCCEEDED(Asn1Err)) { DebugPrintf(SSPI_ERROR, "KerbPackData failed to encode data: %d\n", Asn1Err); KerbErr DBGCHK = KRB_ERR_GENERIC; } else { *MarshalledData = (PUCHAR) MIDL_user_allocate(pEnc->len); if (*MarshalledData == NULL) { KerbErr DBGCHK = KRB_ERR_GENERIC; *pcbDataSize = 0; } else { RtlCopyMemory(*MarshalledData, pEnc->buf, pEnc->len); *pcbDataSize = pEnc->len; } ASN1_FreeEncoded(pEnc, pEnc->buf); } } KerbTermAsn(pEnc, NULL); return KerbErr; } KERBERR KerbConvertUnicodeStringToRealm( OUT PKERB_REALM pRealm, IN PUNICODE_STRING pString ) { TKerbErr KerbErr; STRING TempString; RtlInitString( &TempString, NULL ); *pRealm = NULL; KerbErr DBGCHK = KerbUnicodeStringToKerbString( &TempString, pString ); if (KERB_SUCCESS(KerbErr)) { *pRealm = TempString.Buffer; } return KerbErr; } KERBERR KerbUnicodeStringToKerbString( OUT PSTRING pKerbString, IN PUNICODE_STRING pString ) { STRING TempString; if (!pKerbString) { return KRB_ERR_GENERIC; } TempString.Buffer = KerbAllocUtf8StrFromUnicodeString(pString); if (TempString.Buffer == NULL) { return KRB_ERR_GENERIC; } RtlInitString( &TempString, TempString.Buffer ); *pKerbString = TempString; return KDC_ERR_NONE; } KERBERR KerbConvertKdcNameToPrincipalName( OUT PKERB_PRINCIPAL_NAME pPrincipalName, IN PKERB_INTERNAL_NAME pKdcName ) { TKerbErr KerbErr = KDC_ERR_NONE; PKERB_PRINCIPAL_NAME_ELEM pElem; PKERB_PRINCIPAL_NAME_ELEM* pLast; STRING TempKerbString; ULONG Index; pPrincipalName->name_type = (int) pKdcName->NameType; pPrincipalName->name_string = NULL; pLast = &pPrincipalName->name_string; // // Index through the KDC name and add each element to the list // for (Index = 0; KERB_SUCCESS(KerbErr) && (Index < pKdcName->NameCount); Index++) { KerbErr DBGCHK = KerbUnicodeStringToKerbString( &TempKerbString, &pKdcName->Names[Index] ); if (KERB_SUCCESS(KerbErr)) { pElem = (PKERB_PRINCIPAL_NAME_ELEM) MIDL_user_allocate(sizeof(KERB_PRINCIPAL_NAME_ELEM)); if (pElem == NULL) { KerbErr DBGCHK = KRB_ERR_GENERIC; } pElem->value = TempKerbString.Buffer; pElem->next = NULL; *pLast = pElem; pLast = &pElem->next; } } if (!KERB_SUCCESS(KerbErr)) { KerbFreePrincipalName(pPrincipalName); } return KerbErr; } ULONG KerbConvertUlongToFlagUlong( IN ULONG Flag ) { ULONG ReturnFlag; ((PUCHAR) &ReturnFlag)[0] = ((PUCHAR) &Flag)[3]; ((PUCHAR) &ReturnFlag)[1] = ((PUCHAR) &Flag)[2]; ((PUCHAR) &ReturnFlag)[2] = ((PUCHAR) &Flag)[1]; ((PUCHAR) &ReturnFlag)[3] = ((PUCHAR) &Flag)[0]; return ReturnFlag; } VOID KerbConvertLargeIntToGeneralizedTime( OUT PKERB_TIME pClientTime, OUT OPTIONAL INT* pClientUsec, IN PTimeStamp pTimeStamp ) { TIME_FIELDS TimeFields; // // Special case zero time // #ifndef WIN32_CHICAGO if (pTimeStamp->QuadPart == 0) #else // WIN32_CHICAGO if (*pTimeStamp == 0) #endif // WIN32_CHICAGO { RtlZeroMemory( pClientTime, sizeof(KERB_TIME) ); // // For MIT compatibility, time zero is 1/1/70 // pClientTime->year = 1970; pClientTime->month = 1; pClientTime->day = 1; if (pClientUsec) { *pClientUsec = 0; } pClientTime->universal = TRUE; } else { #ifndef WIN32_CHICAGO RtlTimeToTimeFields( pTimeStamp, &TimeFields ); #else // WIN32_CHICAGO RtlTimeToTimeFields( (LARGE_INTEGER*) pTimeStamp, &TimeFields ); #endif // WIN32_CHICAGO // // Generalized times can only contains years up to four digits. // if (TimeFields.Year > 2037) { pClientTime->year = 2037; } else { pClientTime->year = TimeFields.Year; } pClientTime->month = (ASN1uint8_t) TimeFields.Month; pClientTime->day = (ASN1uint8_t) TimeFields.Day; pClientTime->hour = (ASN1uint8_t) TimeFields.Hour; pClientTime->minute = (ASN1uint8_t) TimeFields.Minute; pClientTime->second = (ASN1uint8_t) TimeFields.Second; // MIT kerberos does not support millseconds // pClientTime->millisecond = 0; if (pClientUsec) { // // Since we don't include milliseconds above, use the whole // thing here. // #ifndef WIN32_CHICAGO *pClientUsec = (pTimeStamp->LowPart / 10) % 1000000; #else // WIN32_CHICAGO *pClientUsec = (int) ((*pTimeStamp / 10) % 1000000); #endif // WIN32_CHICAGO } pClientTime->diff = 0; pClientTime->universal = TRUE; } } NTSTATUS KerbMapKerbError( IN KERBERR KerbError ) { NTSTATUS Status; switch(KerbError) { case KDC_ERR_NONE: Status = STATUS_SUCCESS; break; case KDC_ERR_CLIENT_REVOKED: Status = STATUS_ACCOUNT_DISABLED; break; case KDC_ERR_KEY_EXPIRED: Status = STATUS_PASSWORD_EXPIRED; break; case KRB_ERR_GENERIC: Status = STATUS_INSUFFICIENT_RESOURCES; break; case KRB_AP_ERR_SKEW: case KRB_AP_ERR_TKT_NYV: // Note this was added because of the following scenario: // Let's say the dc and the client have the correct time. And the // server's time is off. We aren't going to get rid of the ticket for the // server on the client because it hasn't expired yet. But, the server // thinks it has. If event logging was turned on, then admins could look // at the server's event log and potentially deduce that the server's // time is off relative to the dc. case KRB_AP_ERR_TKT_EXPIRED: Status = STATUS_TIME_DIFFERENCE_AT_DC; break; case KDC_ERR_POLICY: Status = STATUS_ACCOUNT_RESTRICTION; break; case KDC_ERR_C_PRINCIPAL_UNKNOWN: Status = STATUS_NO_SUCH_USER; break; case KDC_ERR_S_PRINCIPAL_UNKNOWN: Status = STATUS_NO_TRUST_SAM_ACCOUNT; break; case KRB_AP_ERR_MODIFIED: case KDC_ERR_PREAUTH_FAILED: Status = STATUS_WRONG_PASSWORD; break; case KRB_ERR_RESPONSE_TOO_BIG: Status = STATUS_INVALID_BUFFER_SIZE; break; case KDC_ERR_PADATA_TYPE_NOSUPP: Status = STATUS_NOT_SUPPORTED; break; case KRB_AP_ERR_NOT_US: Status = SEC_E_WRONG_PRINCIPAL; break; case KDC_ERR_SVC_UNAVAILABLE: Status = STATUS_NO_LOGON_SERVERS; break; case KDC_ERR_WRONG_REALM: Status = STATUS_NO_LOGON_SERVERS; break; case KDC_ERR_CANT_VERIFY_CERTIFICATE: Status = TRUST_E_SYSTEM_ERROR; break; case KDC_ERR_INVALID_CERTIFICATE: Status = STATUS_INVALID_PARAMETER; break; case KDC_ERR_REVOKED_CERTIFICATE: Status = CRYPT_E_REVOKED; break; case KDC_ERR_REVOCATION_STATUS_UNKNOWN: Status = CRYPT_E_NO_REVOCATION_CHECK; break; case KDC_ERR_REVOCATION_STATUS_UNAVAILABLE: Status = CRYPT_E_REVOCATION_OFFLINE; break; case KDC_ERR_CLIENT_NAME_MISMATCH: case KERB_PKINIT_CLIENT_NAME_MISMATCH: case KDC_ERR_KDC_NAME_MISMATCH: Status = STATUS_PKINIT_NAME_MISMATCH; break; case KDC_ERR_PATH_NOT_ACCEPTED: Status = STATUS_TRUST_FAILURE; break; case KDC_ERR_ETYPE_NOTSUPP: Status = STATUS_KDC_UNKNOWN_ETYPE; break; case KDC_ERR_MUST_USE_USER2USER: case KRB_AP_ERR_USER_TO_USER_REQUIRED: Status = STATUS_USER2USER_REQUIRED; break; case KRB_AP_ERR_NOKEY: Status = STATUS_NO_KERB_KEY; break; case KRB_ERR_NAME_TOO_LONG: Status = STATUS_NAME_TOO_LONG; break; default: Status = STATUS_LOGON_FAILURE; } return (Status); } KERBERR NTAPI KerbUnpackData( IN PUCHAR pData, IN ULONG cbDataSize, IN ULONG PduValue, OUT PVOID * pDecodedData ) { TKerbErr KerbErr = KDC_ERR_NONE; ASN1decoding_t pDec = NULL; ASN1error_e Asn1Err; if ((cbDataSize == 0) || (pData == NULL)) { KerbErr DBGCHK = KRB_ERR_GENERIC; } if (KERB_SUCCESS(KerbErr)) { KerbErr DBGCHK = KerbInitAsn( NULL, &pDec // we are decoding ); } if (KERB_SUCCESS(KerbErr)) { *pDecodedData = NULL; Asn1Err = ASN1_Decode( pDec, pDecodedData, PduValue, ASN1DECODE_SETBUFFER, (BYTE *) pData, cbDataSize ); if (!ASN1_SUCCEEDED(Asn1Err)) { if ((ASN1_ERR_BADARGS == Asn1Err) || (ASN1_ERR_EOD == Asn1Err)) { KerbErr DBGCHK = KDC_ERR_MORE_DATA; } else { KerbErr DBGCHK = KRB_ERR_GENERIC; } *pDecodedData = NULL; } } KerbTermAsn(NULL, pDec); return KerbErr; } PSTR KerbAllocUtf8StrFromUnicodeString( IN PUNICODE_STRING pUnicodeString ) { PSTR pUtf8String = NULL; UINT cbUtf8StringLen; // // If the length is zero, return a null string. // if (pUnicodeString->Length == 0) { pUtf8String = (PSTR) MIDL_user_allocate(sizeof(CHAR)); if (pUtf8String != NULL) { *pUtf8String = '\0'; } return pUtf8String; } // // Determine the length of the Unicode string. // cbUtf8StringLen = WideCharToMultiByte( #ifndef WIN32_CHICAGO CP_UTF8, #else // WIN32_CHICAGO CP_OEMCP, #endif // WIN32_CHICAGO 0, // All characters can be mapped. pUnicodeString->Buffer, pUnicodeString->Length / sizeof(WCHAR), pUtf8String, 0, NULL, NULL ); if ( cbUtf8StringLen == 0 ) { return NULL; } // // Allocate a buffer for the Unicode string. // pUtf8String = (PSTR) MIDL_user_allocate( cbUtf8StringLen + 1 ); if (pUtf8String == NULL) { return NULL; } // // Translate the string to Unicode. // cbUtf8StringLen = WideCharToMultiByte( #ifndef WIN32_CHICAGO CP_UTF8, #else // WIN32_CHICAGO CP_OEMCP, #endif // WIN32_CHICAGO 0, // All characters can be mapped. pUnicodeString->Buffer, pUnicodeString->Length / sizeof(WCHAR), pUtf8String, cbUtf8StringLen, NULL, NULL ); if ( cbUtf8StringLen == 0 ) { MIDL_user_free( pUtf8String ); return NULL; } pUtf8String[cbUtf8StringLen] = '\0'; return pUtf8String; }