windows-nt/Source/XPSP1/NT/net/rras/ras/rassfm/cleartxt.c

664 lines
14 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1998, Microsoft Corp. All rights reserved.
//
// FILE
//
// cleartxt.c
//
// SYNOPSIS
//
// Defines functions for storing and retrieving cleartext passwords.
//
// MODIFICATION HISTORY
//
// 08/31/1998 Original version.
//
///////////////////////////////////////////////////////////////////////////////
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntlsa.h>
#include <windows.h>
#include <wincrypt.h>
#include <rassfmhp.h>
#include <usrprop.h>
#include <cleartxt.h>
// Name of the private key stored as LSA private data.
UNICODE_STRING PRIVATE_KEY_NAME = { 34, 36, L"G$MSRADIUSPRIVKEY" };
// Length of the private key.
#define PRIVATE_KEY_LENGTH 256
// Length of the user-specific key.
#define USER_KEY_LENGTH 16
// Properties stored in UserParameters.
#define PROPERTY_USER_KEY L"G$RADIUSCHAPKEY"
#define PROPERTY_PASSWORD L"G$RADIUSCHAP"
// Fixed key used for decrypting the private key.
BYTE FIXED_KEY[] =
{
0x05, 0x56, 0xF6, 0x07, 0xC6, 0x56, 0x02, 0x94, 0x02,
0xC6, 0xF6, 0x67, 0x56, 0x02, 0xC6, 0x96, 0xB6, 0x56,
0x02, 0x34, 0x86, 0x16, 0xE6, 0x46, 0x27, 0x16, 0xC2,
0x02, 0x14, 0x46, 0x96, 0x47, 0x96, 0xC2, 0x02, 0x74,
0x27, 0x56, 0x47, 0x16, 0x02, 0x16, 0x27, 0x56, 0x02,
0x47, 0x86, 0x56, 0x02, 0x07, 0x56, 0xF6, 0x07, 0xC6,
0x56, 0x02, 0x94, 0x02, 0x47, 0x27, 0x57, 0x37, 0x47
};
// Shared handle to the cryptographic provider.
HCRYPTPROV theContext;
// Private key used for encrypting/decrypting cleartext passwords.
PLSA_UNICODE_STRING thePrivateKey;
// TRUE if this API has been successfully initialized.
static BOOL theInitFlag;
// Non-zero if the API is locked.
static LONG theLock;
//////////
// Macros to lock/unlock the API during intialization.
//////////
#define API_LOCK() \
while (InterlockedExchange(&theLock, 1)) Sleep(5)
#define API_UNLOCK() \
InterlockedExchange(&theLock, 0)
//////////
// Macro that ensures the API has been initialized and bails on failure.
//////////
#define CHECK_INIT() \
if (!theInitFlag) { \
status = IASParmsInitialize(); \
if (status != NO_ERROR) { return status; } \
}
//////////
// Creates the private key. Should only be called if the key doesn't exist.
//////////
DWORD
WINAPI
IASCreatePrivateKey(
IN LSA_HANDLE hPolicy
)
{
DWORD status;
BYTE newKey[PRIVATE_KEY_LENGTH];
LSA_UNICODE_STRING privateData;
//////////
// Generate a random key.
//////////
if (!CryptGenRandom(
theContext,
sizeof(newKey),
newKey
))
{ return GetLastError(); }
//////////
// Store it as LSA private data.
//////////
privateData.Length = sizeof(newKey);
privateData.MaximumLength = sizeof(newKey);
privateData.Buffer = (PWSTR)newKey;
status = LsaStorePrivateData(
hPolicy,
&PRIVATE_KEY_NAME,
&privateData
);
if (NT_SUCCESS(status))
{
status = LsaRetrievePrivateData(
hPolicy,
&PRIVATE_KEY_NAME,
&thePrivateKey
);
}
return NT_SUCCESS(status) ? NO_ERROR : RtlNtStatusToDosError(status);
}
//////////
// Derives a cryptographic key from an octet string.
//////////
BOOL
WINAPI
IASDeriveUserCryptKey(
IN PBYTE pbUserKey,
OUT HCRYPTKEY *phKey
)
{
BOOL success;
HCRYPTHASH hHash;
success = CryptCreateHash(
theContext,
CALG_MD5,
0,
0,
&hHash
);
if (!success) { goto exit; }
success = CryptHashData(
hHash,
(PBYTE)thePrivateKey->Buffer,
thePrivateKey->Length,
0
);
if (!success) { goto destroy_hash; }
success = CryptHashData(
hHash,
pbUserKey,
USER_KEY_LENGTH,
0
);
if (!success) { goto destroy_hash; }
success = CryptDeriveKey(
theContext,
CALG_RC4,
hHash,
CRYPT_EXPORTABLE,
phKey
);
destroy_hash:
CryptDestroyHash(hHash);
exit:
return success;
}
DWORD
WINAPI
IASParmsInitialize( VOID )
{
DWORD status, nbyte;
OBJECT_ATTRIBUTES objAttribs;
LSA_HANDLE hPolicy;
HCRYPTHASH hHash;
HCRYPTKEY hKey;
API_LOCK();
// If we've already been initialized, there's nothing to do.
if (theInitFlag)
{
status = NO_ERROR;
goto exit;
}
/////////
// Acquire a cryptographic context.
/////////
if (!CryptAcquireContext(
&theContext,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT
))
{
status = GetLastError();
goto exit;
}
/////////
// Open a handle to the LSA.
/////////
InitializeObjectAttributes(
&objAttribs,
NULL,
0,
NULL,
NULL
);
status = LsaOpenPolicy(
NULL,
&objAttribs,
POLICY_ALL_ACCESS,
&hPolicy
);
if (!NT_SUCCESS(status))
{
status = RtlNtStatusToDosError(status);
goto exit;
}
/////////
// Retrieve the private key.
/////////
status = LsaRetrievePrivateData(
hPolicy,
&PRIVATE_KEY_NAME,
&thePrivateKey
);
if (status == STATUS_OBJECT_NAME_NOT_FOUND ||
(NT_SUCCESS(status) && thePrivateKey->Length == 0))
{
// If it doesn't exist, create a new one.
status = IASCreatePrivateKey(
hPolicy
);
}
else if (!NT_SUCCESS(status))
{
status = RtlNtStatusToDosError(status);
}
if (status != NO_ERROR) { goto close_policy; }
/////////
// Derive a crypto key from the fixed key.
/////////
if (!CryptCreateHash(
theContext,
CALG_MD5,
0,
0,
&hHash
))
{
status = GetLastError();
goto close_policy;
}
if (!CryptHashData(
hHash,
FIXED_KEY,
sizeof(FIXED_KEY),
0
))
{
status = GetLastError();
goto destroy_hash;
}
if (!CryptDeriveKey(
theContext,
CALG_RC4,
hHash,
CRYPT_EXPORTABLE,
&hKey
))
{
status = GetLastError();
goto destroy_hash;
}
/////////
// Decrypt the private key.
/////////
nbyte = thePrivateKey->Length;
if (!CryptDecrypt(
hKey,
0,
TRUE,
0,
(PBYTE)thePrivateKey->Buffer,
&nbyte
))
{
status = GetLastError();
goto destroy_key;
}
thePrivateKey->Length = (USHORT)nbyte;
destroy_key:
CryptDestroyKey(hKey);
destroy_hash:
CryptDestroyHash(hHash);
close_policy:
LsaClose(hPolicy);
exit:
if (status == NO_ERROR)
{
// We succeeded, so set theInitFlag.
theInitFlag = TRUE;
}
else
{
// We failed, so clean up.
if (thePrivateKey)
{
LsaFreeMemory(thePrivateKey);
thePrivateKey = NULL;
}
if (theContext)
{
CryptReleaseContext(theContext, 0);
theContext = 0;
}
}
API_UNLOCK();
return status;
}
DWORD
WINAPI
IASParmsClearUserPassword(
IN PCWSTR szUserParms,
OUT PWSTR *pszNewUserParms
)
{
DWORD status;
UNICODE_STRING property;
PWSTR tempUserParms;
BOOL updateKey, updatePwd;
// Check the in parameters.
if (pszNewUserParms == NULL) { return ERROR_INVALID_PARAMETER; }
/////////
// Write a null string to the relevant properties.
/////////
memset(&property, 0, sizeof(property));
status = NetpParmsSetUserProperty(
(PWSTR)szUserParms,
PROPERTY_PASSWORD,
property,
0,
&tempUserParms,
&updatePwd
);
if (!NT_SUCCESS(status)) { return RtlNtStatusToDosError(status); }
status = NetpParmsSetUserProperty(
tempUserParms,
PROPERTY_USER_KEY,
property,
0,
pszNewUserParms,
&updateKey
);
NetpParmsUserPropertyFree(tempUserParms);
if (NT_SUCCESS(status))
{
if (!updatePwd && !updateKey)
{
// Nothing changed so don't return the NewUserParms.
NetpParmsUserPropertyFree(*pszNewUserParms);
*pszNewUserParms = NULL;
}
return NO_ERROR;
}
return RtlNtStatusToDosError(status);
}
DWORD
WINAPI
IASParmsGetUserPassword(
IN PCWSTR szUserParms,
OUT PWSTR *pszPassword
)
{
DWORD status, nbyte;
UNICODE_STRING userKey, encryptedPwd;
WCHAR propFlag;
HCRYPTKEY hKey;
// Check the in parameters.
if (pszPassword == NULL) { return ERROR_INVALID_PARAMETER; }
// Make sure we're initialized.
CHECK_INIT();
// Read the user key.
status = NetpParmsQueryUserProperty(
(PWSTR)szUserParms,
PROPERTY_USER_KEY,
&propFlag,
&userKey
);
if (!NT_SUCCESS(status))
{
status = RtlNtStatusToDosError(status);
goto exit;
}
// Read the encrypted password.
status = NetpParmsQueryUserProperty(
(PWSTR)szUserParms,
PROPERTY_PASSWORD,
&propFlag,
&encryptedPwd
);
if (!NT_SUCCESS(status))
{
status = RtlNtStatusToDosError(status);
goto free_key;
}
// If they're both NULL, it's not an error. It just means the cleartext
// password was never set.
if (userKey.Buffer == NULL && encryptedPwd.Buffer == NULL)
{
*pszPassword = NULL;
goto exit;
}
// Make sure the user key is the correct length.
if (userKey.Length != USER_KEY_LENGTH)
{
status = ERROR_INVALID_DATA;
goto free_password;
}
// Convert the user key to a cryptographic key.
if (!IASDeriveUserCryptKey(
(PBYTE)userKey.Buffer,
&hKey
))
{
status = GetLastError();
goto free_password;
}
// Decrypt the password.
nbyte = encryptedPwd.Length;
if (!CryptDecrypt(
hKey,
0,
TRUE,
0,
(PBYTE)encryptedPwd.Buffer,
&nbyte
))
{
status = GetLastError();
goto destroy_key;
}
// We encrypted the terminating null, so it should still be there.
if (encryptedPwd.Buffer[nbyte / sizeof(WCHAR) - 1] != L'\0')
{
status = ERROR_INVALID_DATA;
goto destroy_key;
}
// Return the cleartext password to the caller.
*pszPassword = encryptedPwd.Buffer;
encryptedPwd.Buffer = NULL;
destroy_key:
CryptDestroyKey(hKey);
free_password:
LocalFree(encryptedPwd.Buffer);
free_key:
LocalFree(userKey.Buffer);
exit:
return status;
}
DWORD
WINAPI
IASParmsSetUserPassword(
IN PCWSTR szUserParms,
IN PCWSTR szPassword,
OUT PWSTR *pszNewUserParms
)
{
DWORD status;
BYTE userKey[USER_KEY_LENGTH];
HCRYPTKEY hKey;
DWORD nbyte;
PBYTE encryptedPwd;
UNICODE_STRING property;
PWSTR tempUserParms;
BOOL update;
// Check the in parameters.
if (szPassword == NULL) { return ERROR_INVALID_PARAMETER; }
// Make sure we're initialized.
CHECK_INIT();
// Generate a user key.
if (!CryptGenRandom(
theContext,
USER_KEY_LENGTH,
userKey
))
{
status = GetLastError();
goto exit;
}
// Convert the user key to a cryptographic key.
if (!IASDeriveUserCryptKey(
userKey,
&hKey
))
{
status = GetLastError();
goto exit;
}
// Allocate a buffer for the encrypted password.
nbyte = sizeof(WCHAR) * (lstrlenW(szPassword) + 1);
encryptedPwd = RtlAllocateHeap(
RasSfmHeap(),
0,
nbyte
);
if (encryptedPwd == NULL)
{
status = ERROR_NOT_ENOUGH_MEMORY;
goto destroy_key;
}
memcpy(encryptedPwd, szPassword, nbyte);
// Encrypt the password.
if (!CryptEncrypt(
hKey,
0,
TRUE,
0,
encryptedPwd,
&nbyte,
nbyte
))
{
status = GetLastError();
goto free_encrypted_password;
}
/////////
// Store the encrypted password.
/////////
property.Buffer = (PWCHAR)encryptedPwd;
property.Length = (USHORT)nbyte;
property.MaximumLength = (USHORT)nbyte;
status = NetpParmsSetUserProperty(
(PWSTR)szUserParms,
PROPERTY_PASSWORD,
property,
0,
&tempUserParms,
&update
);
if (!NT_SUCCESS(status))
{
status = RtlNtStatusToDosError(status);
goto free_encrypted_password;
}
/////////
// Store the user key.
/////////
property.Buffer = (PWSTR)userKey;
property.Length = USER_KEY_LENGTH;
property.MaximumLength = USER_KEY_LENGTH;
status = NetpParmsSetUserProperty(
tempUserParms,
PROPERTY_USER_KEY,
property,
0,
pszNewUserParms,
&update
);
if (!NT_SUCCESS(status)) { status = RtlNtStatusToDosError(status); }
NetpParmsUserPropertyFree(tempUserParms);
free_encrypted_password:
RtlFreeHeap(RasSfmHeap(), 0, encryptedPwd);
destroy_key:
CryptDestroyKey(hKey);
exit:
return status;
}
VOID
WINAPI
IASParmsFreeUserParms(
IN LPWSTR szNewUserParms
)
{
NetpParmsUserPropertyFree(szNewUserParms);
}