Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

811 lines
18 KiB

/*++
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;
}