windows-nt/Source/XPSP1/NT/net/dhcp/server/server/secretk.c
2020-09-26 16:20:57 +08:00

723 lines
16 KiB
C

/*++
Copyright (C) 1998 Microsoft Corporation
Module Name:
SecretK.C
Abstract:
This module implements reading and writing secret keys using
LSA secrets API. Two APIs are provided to retrieve the values
and set the values respectively.
This is preliminary version for use by the DHCP server to store
persistent information on whether Rogue detection ever succeeded
and if so, when.
Author:
Ramesh V (RameshV) 29-July-1998
--*/
#include <dhcppch.h>
#include <ntlsa.h>
#define DHCP_SECRET_KEY L"DhcpServer Secret Key For Rogue Detection"
#define DHCP_SECRET_UNAME_KEY L"DhcpServer Uname Key"
#define DHCP_SECRET_DOMAIN_KEY L"DhcpServer Domain Key"
#define DHCP_SECRET_DNS_PASSWD_KEY L"DhcpServer Passwd Key"
#define FTIME_TO_SEC_FACTOR 10000000
#define DHCP_SERVER_AUTHORIZATION_VALIDITY_PERIOD (2*24*60*60)
#define MAX_STRING_SIZE 260
#define AUTH_FLAG_JUST_UPGRADED 0x01
#define AUTH_FLAG_UNAUTHORIZED 0x02
#ifndef DHCP_ENCODE_SEED
#define DHCP_ENCODE_SEED ((UCHAR)0xA5)
#endif
typedef struct {
ULONG Flags;
FILETIME TimeStamp;
//
// This is followed directly by WCHAR string
// for the domain that the server was authorized/unauthorized
// in the last time.
} AUTH_CACHE_INFO, *PAUTH_CACHE_INFO;
static
LSA_HANDLE GlobalPolicyHandle;
static
ULONG Initialized = 0;
DWORD _inline
OpenPolicy(
OUT LSA_HANDLE *PolicyHandle
)
{
LSA_HANDLE hPolicy;
DWORD status;
OBJECT_ATTRIBUTES objectAttributes;
(*PolicyHandle) = NULL;
InitializeObjectAttributes(
&objectAttributes,
NULL,
0L,
NULL,
NULL
);
status = LsaOpenPolicy(
NULL,
&objectAttributes,
POLICY_WRITE | POLICY_READ |
POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES,
&hPolicy
);
if (status != STATUS_SUCCESS) {
return LsaNtStatusToWinError(status);
}
(*PolicyHandle) = hPolicy;
return ERROR_SUCCESS;
}
DWORD _fastcall
SetSecretValue(
IN LPCWSTR KeyName,
IN PVOID Buffer,
IN LONG BufSize
)
/*++
Routine Description
Set the secret value associated with the keyname using local
system security policy.
Arguments
KeyName - key name to use to set secret value
Buffer - the secret value to set ..
BufSize - the size of the buffer in bytes. If this is zero
the value would be deleted.
Return Value
Win32 errors
--*/
{
UNICODE_STRING LKey, Value;
DWORD status;
RtlInitUnicodeString(&LKey, KeyName);
Value.Length = Value.MaximumLength = (USHORT)BufSize;
Value.Buffer = Buffer;
status = LsaStorePrivateData( GlobalPolicyHandle, &LKey, &Value);
return LsaNtStatusToWinError(status);
}
DWORD _inline
GetSecretValue(
IN LPCWSTR KeyName,
OUT PVOID Buffer,
IN OUT PLONG BufSize
)
/*++
Routine Description
Retrive the secret value with keyname as provided using local
system security policy.
If the return value requires more space than the buffer provided
(BufSize initially has the space provided via Buffer) then return
the space required in bytes in the BufSize parameter and return
ERROR_INSUFFICIENT_BUFFER.
Arguments
KeyName - provide the name of the key of interest.
Buffer - provide the buffer that needs to be filled in with secret value.
BufSize - on input this will the size of Buffer in bytes provided.
In case of successful return, this will hold the actual number of
bytes used. If the routine returns ERROR_INSUFFICIENT_BUFFER then
this will hold the size required.
Return Value
ERROR_SUCCESS -- successfully copied the value. If no value exists,
(*BufSize) would be zero on return and function would return success.
ERROR_INSUFFICIENT_BUFFER -- The size as provided by BufSize is not
sufficient to do the full copy of the value. On return BufSize will
hold the actual size required.
Other Win32 errors.
--*/
{
UNICODE_STRING LKey, *pValue = NULL;
DWORD status;
RtlInitUnicodeString(&LKey, KeyName);
status = LsaRetrievePrivateData( GlobalPolicyHandle, &LKey, &pValue);
if( STATUS_SUCCESS != status ) {
return LsaNtStatusToWinError(status);
}
if( *BufSize >= pValue->Length ) {
RtlCopyMemory( Buffer, pValue->Buffer, pValue->Length );
} else {
status = ERROR_INSUFFICIENT_BUFFER;
}
*BufSize = pValue->Length;
LsaFreeMemory(pValue);
return status;
}
BOOL
DhcpGetAuthStatus(
IN LPWSTR DomainName,
OUT BOOL *fUpgraded,
OUT BOOL *fAuthorized
)
/*++
Routine Description:
This routine checks to see if there is a registry cache entry for the
given domain name. If there isn't one for the given domain name, it
returns FALSE. (In this case, the value for *fAuthorized is FALSE).
If there is a registry cache entry for the given domain name, then the
fAuthorized flag contains information on whether it is authorized or
unauthorized.
If the machine was just upgraded, then fAuthorized is fUpgraded is set
to TRUE (This is independent of the return value of the function).
Arguments:
DomainName -- name of domain to check authorization information
fUpgraded -- was the machine just upgraded to NT5 ?
fAuthorized -- is it authorized or unauthorized ?
Return Value:
TRUE indicates a cache entry for the given domain was found (and the
flag fAuthorized can be checked to see authorization status).
--*/
{
ULONG Error, AuthInfoSize;
PAUTH_CACHE_INFO AuthInfo;
BOOL fResult;
LPWSTR CachedDomainName;
FILETIME Diff, CurrentTime;
(*fUpgraded) = (*fAuthorized) = FALSE;
AuthInfo = NULL; AuthInfoSize = 0;
Error = GetSecretValue(
DHCP_SECRET_KEY,
AuthInfo,
&AuthInfoSize
);
if( ERROR_INSUFFICIENT_BUFFER != Error ) return FALSE;
if( AuthInfoSize < sizeof(*AuthInfo)) return FALSE;
AuthInfo = LocalAlloc( LPTR, AuthInfoSize );
if( NULL == AuthInfo ) return FALSE;
fResult = FALSE;
do {
Error = GetSecretValue(
DHCP_SECRET_KEY,
AuthInfo,
&AuthInfoSize
);
if( ERROR_SUCCESS != Error ) break;
if( AuthInfoSize < sizeof(*AuthInfo) ) break;
(*fUpgraded) = ( AuthInfo->Flags & AUTH_FLAG_JUST_UPGRADED );
CachedDomainName = (LPWSTR)(sizeof(*AuthInfo) + (LPBYTE)AuthInfo);
if( NULL == DomainName ) break;
if( (1 + wcslen(DomainName)) *sizeof(WCHAR)
!= AuthInfoSize - sizeof(*AuthInfo) ) {
break;
}
if( 0 != _wcsicmp(
DomainName,
CachedDomainName
) ) {
break;
}
*(ULONGLONG *)&Diff = DHCP_SERVER_AUTHORIZATION_VALIDITY_PERIOD;
*(ULONGLONG *)&Diff *= FTIME_TO_SEC_FACTOR;
GetSystemTimeAsFileTime(&CurrentTime);
(*(ULONGLONG *)&CurrentTime) += *(ULONGLONG *)&Diff;
if( CompareFileTime( &AuthInfo->TimeStamp, &CurrentTime ) < 0 ) {
//
// We've gone past the cache life
//
break;
}
(*fAuthorized) = (0 == ( AuthInfo->Flags & AUTH_FLAG_UNAUTHORIZED ));
fResult = TRUE;
} while ( 0 );
LocalFree( AuthInfo );
return fResult ;
}
DWORD
DhcpSetAuthStatus(
IN LPWSTR DomainName OPTIONAL,
IN BOOL fUpgraded,
IN BOOL fAuthorized
)
/*++
Routine Description:
This routine sets the registry cache information for authorization.
Arguments:
DomainName -- name of domain to set in the authorization info
fUpgraded -- was this just upgraded to NT5?
fAuthorized -- was this authorized or unauthorized ?
Return Value:
Status
--*/
{
AUTH_CACHE_INFO AuthInfoTmp;
PAUTH_CACHE_INFO AuthInfo;
ULONG Error;
ULONG StringSize;
StringSize = DomainName? (sizeof(WCHAR)*(1+wcslen(DomainName))):0;
if( 0 == StringSize ) {
AuthInfo = &AuthInfoTmp;
} else {
AuthInfo = LocalAlloc( LPTR, sizeof(*AuthInfo) + StringSize );
}
if( NULL == AuthInfo ) return ERROR_NOT_ENOUGH_MEMORY;
AuthInfo->Flags = 0;
if( fUpgraded ) AuthInfo -> Flags |= AUTH_FLAG_JUST_UPGRADED;
if( !fAuthorized ) AuthInfo ->Flags |= AUTH_FLAG_UNAUTHORIZED;
GetSystemTimeAsFileTime( &AuthInfo->TimeStamp );
RtlCopyMemory(
sizeof(AuthInfo) + (LPBYTE)AuthInfo,
DomainName,
StringSize
);
Error = SetSecretValue(
DHCP_SECRET_KEY,
AuthInfo,
StringSize + sizeof(*AuthInfo)
);
if( AuthInfo != &AuthInfoTmp ) {
LocalFree(AuthInfo);
}
return Error;
}
VOID
DhcpSetAuthStatusUpgradedFlag(
IN BOOL fUpgraded
)
/*++
Routine Description:
This routine does not alter any cache information other
than the just upgraded flag.
--*/
{
ULONG Error, AuthInfoSize;
PAUTH_CACHE_INFO AuthInfo;
BOOL fResult;
AuthInfo = NULL; AuthInfoSize = 0;
Error = GetSecretValue(
DHCP_SECRET_KEY,
AuthInfo,
&AuthInfoSize
);
if( ERROR_INSUFFICIENT_BUFFER != Error ||
AuthInfoSize < sizeof( *AuthInfo ) ) {
DhcpSetAuthStatus( NULL, fUpgraded, FALSE );
return;
}
AuthInfo = LocalAlloc( LPTR, AuthInfoSize );
if( NULL == AuthInfo ) {
DhcpSetAuthStatus( NULL, fUpgraded, FALSE );
return;
}
fResult = FALSE;
do{
Error = GetSecretValue(
DHCP_SECRET_KEY,
AuthInfo,
&AuthInfoSize
);
if( ERROR_SUCCESS != Error ) break;
if( AuthInfoSize < sizeof(*AuthInfo) ) break;
if( fUpgraded ) {
AuthInfo->Flags |= AUTH_FLAG_JUST_UPGRADED;
} else {
AuthInfo->Flags &= ~AUTH_FLAG_JUST_UPGRADED;
}
SetSecretValue(
DHCP_SECRET_KEY,
AuthInfo,
AuthInfoSize
);
fResult = TRUE;
} while ( 0 );
if( FALSE == fResult ) {
DhcpSetAuthStatus( NULL, fUpgraded, FALSE );
}
LocalFree( AuthInfo );
}
VOID
WINAPI
DhcpMarkUpgrade(
VOID
)
/*++
Routine Description:
This routine is to be called by the UPGRADE setup path when the machine
has been upgraded to NT5 (and dhcp server is installed etc).
It MUST NOT BE CALLED within DHCP.
It automatically initializes this module and cleans up.
--*/
{
ULONG Error;
Error = DhcpInitSecrets();
if( ERROR_SUCCESS == Error ) {
Error = DhcpSetAuthStatus( NULL, TRUE, TRUE );
ASSERT( ERROR_SUCCESS == Error );
DhcpCleanupSecrets();
}
}
DWORD
DhcpQuerySecretUname(
IN OUT LPWSTR Uname,
IN ULONG UnameSize, // size in BYTES not WCHARS
IN OUT LPWSTR DomainName,
IN ULONG DomainNameSize,
IN OUT LPWSTR Passwd,
IN ULONG PasswdSize
)
{
DWORD Error;
ZeroMemory( Uname, UnameSize );
ZeroMemory( DomainName, DomainNameSize );
ZeroMemory( Passwd, PasswdSize );
if( UnameSize <= sizeof(WCHAR) ) return ERROR_INSUFFICIENT_BUFFER;
if( DomainNameSize <= sizeof(WCHAR) ) return ERROR_INSUFFICIENT_BUFFER;
Error = GetSecretValue(
DHCP_SECRET_UNAME_KEY, Uname, &UnameSize );
if( ERROR_FILE_NOT_FOUND == Error ) {
Uname[0] = L'\0';
DomainName[0] = L'\0';
return NO_ERROR;
}
if(NO_ERROR != Error ) return Error;
Error = GetSecretValue(
DHCP_SECRET_DOMAIN_KEY, DomainName, &DomainNameSize );
if( ERROR_FILE_NOT_FOUND == Error ) {
DomainName[0] = L'\0';
return NO_ERROR;
}
if ( NO_ERROR != Error ) return Error;
Error = GetSecretValue(
DHCP_SECRET_DNS_PASSWD_KEY, Passwd, &PasswdSize );
if ( ERROR_FILE_NOT_FOUND == Error ) {
Passwd[ 0 ] = L'\0';
return NO_ERROR;
}
return Error;
}
DWORD
GetAccountSid(
IN LPWSTR AccountName,
IN OUT PSID *pSid
)
{
BOOL fSuccess;
DWORD Error, Size, DomSize;
SID_NAME_USE unused;
WCHAR DomainName[512];
Size = 0;
DomSize = sizeof(DomainName)/sizeof(DomainName[0]);
fSuccess = LookupAccountName(
NULL, AccountName, (*pSid), &Size, DomainName, &DomSize,
&unused );
Error = NO_ERROR;
if( FALSE == fSuccess ) Error = GetLastError();
if( ERROR_INSUFFICIENT_BUFFER != Error ) return Error;
(*pSid ) = LocalAlloc( LPTR, Size );
if( NULL == (*pSid )) return GetLastError();
fSuccess = LookupAccountName(
NULL, AccountName, (*pSid), &Size, DomainName, &DomSize,
&unused );
Error = NO_ERROR;
if( FALSE == fSuccess ) Error = GetLastError();
if( NO_ERROR != Error ) {
LocalFree(*pSid);
(*pSid) = NULL;
}
return Error;
}
DWORD
SetPrivilegeOnAccount(
IN PSID pSid,
IN LPWSTR Privilege
)
{
BOOL fEnable = TRUE;
DWORD Error;
LSA_UNICODE_STRING Str;
Str.Buffer = Privilege;
Str.Length = (USHORT)(lstrlenW(Privilege)*sizeof(WCHAR));
Str.MaximumLength = Str.Length + sizeof(WCHAR);
Error = LsaAddAccountRights(
GlobalPolicyHandle, pSid, &Str, 1 );
return LsaNtStatusToWinError( Error );
}
DWORD
VerifyAccount(
IN LPWSTR Uname,
IN LPWSTR DomainName,
IN LPWSTR Passwd
)
{
DWORD Error;
WCHAR AccountName[512];
PSID pSid = NULL;
HANDLE hToken;
wcscpy(AccountName, DomainName );
wcscat(AccountName, L"\\");
wcscat(AccountName, Uname );
//
// Check if lookup succeeds
//
Error = GetAccountSid( AccountName, &pSid );
if( NO_ERROR != Error ) return Error;
Error = SetPrivilegeOnAccount(pSid, L"SeServiceLogonRight");
if( NO_ERROR != Error ) {
if( NULL != pSid ) LocalFree( pSid );
return Error;
}
Error = LogonUser(
Uname, DomainName, Passwd, LOGON32_LOGON_SERVICE,
LOGON32_PROVIDER_WINNT50, &hToken );
if( FALSE == Error ) {
Error = GetLastError();
} else {
Error = NO_ERROR;
}
if( NULL != pSid ) LocalFree( pSid );
CloseHandle( hToken );
return Error;
}
DWORD
DhcpSetSecretUnamePasswd(
IN LPWSTR Uname,
IN LPWSTR DomainName,
IN LPWSTR Passwd
)
{
DWORD Error, Size;
UNICODE_STRING Str;
WCHAR LocalDomainName[300];
if( NULL == Uname || Uname[0] == L'\0' ) {
Error = SetSecretValue(
DHCP_SECRET_UNAME_KEY, NULL, 0 );
if( NO_ERROR != Error ) return Error;
Error = SetSecretValue(
DHCP_SECRET_DOMAIN_KEY, NULL, 0 );
if( NO_ERROR != Error ) return Error;
Error = SetSecretValue(
DHCP_SECRET_DNS_PASSWD_KEY, NULL, 0 );
return Error;
}
if( NULL == Passwd ) Passwd = L"";
if( NULL == DomainName || DomainName[0] == L'\0' ) {
//
// Empty domain is local domain.
//
Size = sizeof(LocalDomainName)/sizeof(WCHAR);
Error = GetComputerNameEx(
ComputerNameDnsHostname, LocalDomainName, &Size );
if( FALSE == Error ) return GetLastError();
DomainName = (LPWSTR)LocalDomainName;
}
Str.Length = (USHORT)(wcslen(Passwd)*sizeof(WCHAR));
Str.MaximumLength = Str.Length;
Str.Buffer = (PVOID)Passwd;
RtlRunDecodeUnicodeString( DHCP_ENCODE_SEED, &Str );
Error = VerifyAccount(Uname, DomainName, Passwd );
if( NO_ERROR != Error ) goto Cleanup;
Size = sizeof(WCHAR)*(1+wcslen(Uname));
Error = SetSecretValue(
DHCP_SECRET_UNAME_KEY, Uname, Size );
if( NO_ERROR != Error ) goto Cleanup;
Size = sizeof(WCHAR)*(1+wcslen(DomainName));
Error = SetSecretValue(
DHCP_SECRET_DOMAIN_KEY, DomainName, Size );
if( NO_ERROR != Error ) goto Cleanup;
Size = sizeof(WCHAR)*(wcslen(Passwd));
Error = SetSecretValue(
DHCP_SECRET_DNS_PASSWD_KEY, Passwd, Size );
Cleanup:
ZeroMemory( Passwd, wcslen(Passwd)*sizeof(WCHAR));
return Error;
}
DWORD
DhcpInitSecrets(
VOID
)
/*++
Routine description
Initialize this module, take care of multiple initializations..
NOT Thread safe (if multiple people intialize won't work well).
Return Value
Win32 error codes
--*/
{
DWORD Error;
if( Initialized ) return ERROR_SUCCESS;
Error = OpenPolicy(&GlobalPolicyHandle);
if( ERROR_SUCCESS == Error ) Initialized ++;
return Error;
}
VOID
DhcpCleanupSecrets(
VOID
)
/*++
Routine description
Undo the effect of DhcpInitSecrets -- keep track of # of calls to init & cleanup.
NOT Thread safe
Return Value
Win32 error code
--*/
{
if( 0 == Initialized ) return;
Initialized --;
if( 0 == Initialized ) {
LsaClose(GlobalPolicyHandle);
GlobalPolicyHandle = NULL;
}
}