windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/lls/server/certdb.c

1507 lines
46 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
certdb.c
Abstract:
License Logging Service certificate database implementation. This database
tracks license certificates to help ensure that no more licenses from a
single certificate are installe don the license enterprise than are allowed
by the certificate's license agreement.
The certificate database at the top level is an unsorted array of
certificate headers. There is exactly one header per unique certificate.
A unique certificate is identified by a combination of product name,
certificate ID, certificate capacity (max. licenses legally installable),
and expiration date.
Each header has an attached array of certificate claims. There is exactly
one claim per machine that (a) replicates to this machine, directly or
indirectly, and (b) has licenses from this certificate installed. Each
claim contains the server name to which it corresponds, the number of
licenses installed on it, and the date this information was replicated.
If a claim is not updated after LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX
seconds (3 days as of this writing), the claim is considered forfeit and
is erased.
Author:
Jeff Parham (jeffparh) 08-Dec-1995
Revision History:
--*/
#include <stdlib.h>
#include <limits.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <rpc.h>
#include <rpcndr.h>
#include <dsgetdc.h>
#include "debug.h"
#include "llsutil.h"
#include "llssrv.h"
#include "llsapi.h"
#include "llsevent.h"
#include "llsrpc_s.h"
#include "certdb.h"
#include "purchase.h"
#include "registry.h"
RTL_RESOURCE CertDbHeaderListLock;
static PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderList = NULL;
static DWORD CertDbHeaderListSize = 0;
static HANDLE CertDbFile = NULL;
///////////////////////////////////////////////////////////////////////////////
NTSTATUS CertDbClaimEnter( LPTSTR pszServerName,
PLLS_LICENSE_INFO_1 pLicense,
BOOL bIsTotal,
DWORD ReplicationDate )
/*++
Routine Description:
Enter a claim into the database.
Arguments:
pszServerName (LPTSTR)
The server for which to enter this claim. A value of NULL indicates the
local server.
pLicense (PLLS_LICENSE_INFO_1)
License information to enter into the database.
bIsTotal (BOOL)
If TRUE, indicates that this license information represents the total
licenses installed on the machine and should therefore replace the
current claim (if any). Otherwise, indicates this license information
should be added to the current claim (if any).
ReplicationDate (DWORD)
Indicates the date which this information was last replicated. A value
of 0 will be replaced with the current system time.
Return Value:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_INVALID_COMPUTER_NAME
STATUS_NO_MEMORY
--*/
{
NTSTATUS nt;
if ( ( NULL == pLicense ) || ( 0 == pLicense->CertificateID ) )
{
ASSERT( FALSE );
nt = STATUS_INVALID_PARAMETER;
}
else
{
TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
if ( NULL == pszServerName )
{
// use local server name
DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
BOOL ok;
ok = GetComputerName( szComputerName, &cchComputerName );
ASSERT( ok );
if ( ok )
{
pszServerName = szComputerName;
}
}
else
{
// remove leading backslashes (if any) from server name
while ( TEXT('\\') == *pszServerName )
{
pszServerName++;
}
}
if ( ( NULL == pszServerName ) || !*pszServerName || ( lstrlen( pszServerName ) > MAX_COMPUTERNAME_LENGTH ) )
{
ASSERT( FALSE );
nt = STATUS_INVALID_COMPUTER_NAME;
}
else
{
PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
// is the certificate in the db?
pHeader = CertDbHeaderFind( pLicense );
if ( NULL == pHeader )
{
// certificate not yet in db; add it
pHeader = CertDbHeaderAdd( pLicense );
}
if ( NULL == pHeader )
{
// could not find or add header
ASSERT( FALSE );
nt = STATUS_NO_MEMORY;
}
else
{
// now have header; is this claim already filed?
int iClaim;
iClaim = CertDbClaimFind( pHeader, pszServerName );
if ( iClaim < 0 )
{
PLLS_CERT_DB_CERTIFICATE_CLAIM pClaimsTmp;
// claim does not yet exist; add it
if ( NULL == pHeader->Claims )
{
pClaimsTmp = LocalAlloc( LPTR, ( 1 + pHeader->NumClaims ) * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ) );
}
else
{
pClaimsTmp = LocalReAlloc( pHeader->Claims, ( 1 + pHeader->NumClaims ) * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ), LHND );
}
if ( NULL != pClaimsTmp )
{
// memory allocation succeeded
// claim list expanded; save server name
pHeader->Claims = pClaimsTmp;
iClaim = pHeader->NumClaims;
lstrcpy( pHeader->Claims[ iClaim ].ServerName, pszServerName );
pHeader->Claims[ iClaim ].Quantity = 0;
pHeader->NumClaims++;
}
}
if ( iClaim < 0 )
{
// could not find or add claim to header
ASSERT( FALSE );
nt = STATUS_NO_MEMORY;
}
else
{
// claim found or added; update info
ASSERT( !lstrcmpi( pszServerName, pHeader->Claims[ iClaim ].ServerName ) );
pHeader->Claims[ iClaim ].ReplicationDate = ReplicationDate ? ReplicationDate : DateSystemGet();
if ( bIsTotal )
{
// the given value is the new total
pHeader->Claims[ iClaim ].Quantity = pLicense->Quantity;
nt = STATUS_SUCCESS;
}
else if ( pHeader->Claims[ iClaim ].Quantity + pLicense->Quantity >= 0 )
{
// the given value is added to the current sum to make the total
pHeader->Claims[ iClaim ].Quantity += pLicense->Quantity;
nt = STATUS_SUCCESS;
}
else
{
// overflow
nt = STATUS_INVALID_PARAMETER;
}
}
}
RtlReleaseResource( &CertDbHeaderListLock );
if ( STATUS_SUCCESS == nt )
{
// any product that has licenses with non-0 certificate IDs
// must be secure; this code is here such that when certificates
// are replicated, the "product is secure" info is replicated, too
// this will also help recover from the case where someone deletes
// the registry key that lists all secure products
ServiceSecuritySet( pLicense->Product );
}
}
}
return nt;
}
///////////////////////////////////////////////////////////////////////////////
BOOL CertDbClaimApprove( PLLS_LICENSE_INFO_1 pLicense )
/*++
Routine Description:
Check to see if adding the given licenses is legal. This call is typically
made before adding a license into the system to verify that doing so does
not violate the certificate's license agreement.
Arguments:
pLicense (PLLS_LICENSE_INFO_1)
License information for which approval is sought.
Return Value:
TRUE (approved) or FALSE (rejected).
--*/
{
BOOL bOkToAdd = TRUE;
PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
BOOL ok;
if ( ( pLicense->Quantity > 0 ) && ( (DWORD)pLicense->Quantity > pLicense->MaxQuantity ) )
{
// certificate add request exceeds its capacity all by itself!
bOkToAdd = FALSE;
}
else
{
ok = GetComputerName( szComputerName, &cchComputerName );
ASSERT( ok );
if ( !ok )
{
// deletions will fail...
*szComputerName = TEXT( '\0' );
}
// do we have a record of this certificate?
RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
pHeader = CertDbHeaderFind( pLicense );
if ( NULL == pHeader )
{
// don't have any record of this certificate; ok to add if Quantity > 0
bOkToAdd = ( pLicense->Quantity > 0 );
}
else
{
LONG lTotalQuantity = 0;
int iClaim;
// we have seen this certificate; are there enough licenses available?
for ( iClaim=0; (DWORD)iClaim < pHeader->NumClaims; iClaim++ )
{
// for license remove requests, tally only local licenses
// for license add requests, tally all licenses
if ( ( ( pLicense->Quantity > 0 )
|| ( !lstrcmpi( pHeader->Claims[ iClaim ].ServerName, szComputerName ) ) )
&& ( lTotalQuantity + pHeader->Claims[ iClaim ].Quantity >= 0 ) )
{
// add to tally
lTotalQuantity += pHeader->Claims[ iClaim ].Quantity;
}
}
if ( lTotalQuantity + pLicense->Quantity < 0 )
{
// overflow or underflow
bOkToAdd = FALSE;
}
else if ( (DWORD)(lTotalQuantity + pLicense->Quantity) > pHeader->MaxQuantity )
{
// exceeds certificate capacity
bOkToAdd = FALSE;
}
else
{
// okay by me
bOkToAdd = TRUE;
}
}
RtlReleaseResource( &CertDbHeaderListLock );
}
return bOkToAdd;
}
///////////////////////////////////////////////////////////////////////////////
PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderFind( PLLS_LICENSE_INFO_1 pLicense )
/*++
Routine Description:
Find a certificate header in the database.
Arguments:
pLicense (PLLS_LICENSE_INFO_1)
License information for which to find the appropriate header.
Return Value:
A pointer to the found header, or NULL if not found.
--*/
{
// assumes db is already locked for shared or exclusive access
PLLS_CERT_DB_CERTIFICATE_HEADER pHeader = NULL;
int iHeader;
for ( iHeader=0; ( NULL == pHeader ) && ( (DWORD)iHeader < CertDbHeaderListSize ); iHeader++ )
{
if ( ( CertDbHeaderList[ iHeader ].CertificateID == pLicense->CertificateID )
&& ( CertDbHeaderList[ iHeader ].MaxQuantity == pLicense->MaxQuantity )
&& ( CertDbHeaderList[ iHeader ].ExpirationDate == pLicense->ExpirationDate )
&& ( !lstrcmpi( CertDbHeaderList[ iHeader ].Product, pLicense->Product ) ) )
{
// header found!
pHeader = &CertDbHeaderList[ iHeader ];
}
}
return pHeader;
}
///////////////////////////////////////////////////////////////////////////////
PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderAdd( PLLS_LICENSE_INFO_1 pLicense )
/*++
Routine Description:
Add a certificate header to the database.
Arguments:
pLicense (PLLS_LICENSE_INFO_1)
License information for which to add the header.
Return Value:
A pointer to the added header, or NULL if memory could not be allocated.
--*/
{
// assumes caller has made sure the header does not already exist
// assumes db is locked for exclusive access
PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
if ( CertDbHeaderListSize )
{
pHeader = LocalReAlloc( CertDbHeaderList, ( 1 + CertDbHeaderListSize ) * sizeof( LLS_CERT_DB_CERTIFICATE_HEADER ), LHND );
}
else
{
pHeader = LocalAlloc( LPTR, ( 1 + CertDbHeaderListSize ) * sizeof( LLS_CERT_DB_CERTIFICATE_HEADER ) );
}
if ( NULL != pHeader )
{
CertDbHeaderList = pHeader;
// allocate space for product name
CertDbHeaderList[ CertDbHeaderListSize ].Product = LocalAlloc( LPTR, sizeof( TCHAR ) * ( 1 + lstrlen( pLicense->Product ) ) );
if ( NULL == CertDbHeaderList[ CertDbHeaderListSize ].Product )
{
// memory allocation failed
ASSERT( FALSE );
pHeader = NULL;
}
else
{
// success!
pHeader = &CertDbHeaderList[ CertDbHeaderListSize ];
CertDbHeaderListSize++;
lstrcpy( pHeader->Product, pLicense->Product );
pHeader->CertificateID = pLicense->CertificateID;
pHeader->MaxQuantity = pLicense->MaxQuantity;
pHeader->ExpirationDate = pLicense->ExpirationDate;
}
}
return pHeader;
}
///////////////////////////////////////////////////////////////////////////////
int CertDbClaimFind( PLLS_CERT_DB_CERTIFICATE_HEADER pHeader, LPTSTR pszServerName )
/*++
Routine Description:
Find a certificate claim for a specific server in the claim list.
Arguments:
pHeader (PLLS_CERT_DB_CERTIFICATE_HEADER)
Header containing the claim list to search.
pszServerName (LPTSTR)
Name of the server for which the claim is sought.
Return Value:
The index of the found claim, or -1 if not found.
--*/
{
// assumes db is already locked for shared or exclusive access
int iClaim;
for ( iClaim=0; (DWORD)iClaim < pHeader->NumClaims; iClaim++ )
{
if ( !lstrcmpi( pHeader->Claims[ iClaim ].ServerName, pszServerName ) )
{
break;
}
}
if ( (DWORD)iClaim >= pHeader->NumClaims )
{
iClaim = -1;
}
return iClaim;
}
///////////////////////////////////////////////////////////////////////////////
void CertDbPrune()
/*++
Routine Description:
Remove entries in the database which have expired. Entries expire if they
have not been re-replicated in LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX
seconds (3 days as of this writing).
Arguments:
None.
Return Value:
None.
--*/
{
int iHeader;
int iClaim;
DWORD CurrentDate;
DWORD MinimumDate;
TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ] = TEXT("");
DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
BOOL ok;
ok = GetComputerName( szComputerName, &cchComputerName );
ASSERT( ok );
if ( ok )
{
RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
CurrentDate = DateSystemGet();
MinimumDate = CurrentDate - LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX;
for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
{
for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; )
{
// Note that we prune entries made in the future, too, to avoid having an incorrect date
// forcing us to keep an entry forever.
//
// For this application, it's better to keep fewer entries rather than more, as the
// fewer entries we have, the less restrictive the system is.
//
// Don't prune local entries.
if ( ( ( CertDbHeaderList[ iHeader ].Claims[ iClaim ].ReplicationDate < MinimumDate )
|| ( CertDbHeaderList[ iHeader ].Claims[ iClaim ].ReplicationDate > CurrentDate ) )
&& lstrcmpi( szComputerName, CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName ) )
{
// remove claim
MoveMemory( &CertDbHeaderList[ iHeader ].Claims[ iClaim ],
&CertDbHeaderList[ iHeader ].Claims[ iClaim+1 ],
CertDbHeaderList[ iHeader ].NumClaims - ( iClaim + 1 ) );
CertDbHeaderList[ iHeader ].NumClaims--;
}
else
{
// keep this claim
iClaim++;
}
}
}
RtlReleaseResource( &CertDbHeaderListLock );
}
}
///////////////////////////////////////////////////////////////////////////////
void CertDbRemoveLocalClaims()
/*++
Routine Description:
Remove entries in the database corresponding to the local server.
Arguments:
None.
Return Value:
None.
--*/
{
int iHeader;
int iClaim;
TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ] = TEXT("");
DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
BOOL ok;
ok = GetComputerName( szComputerName, &cchComputerName );
ASSERT( ok );
if ( ok )
{
RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
{
for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; )
{
if ( !lstrcmpi( szComputerName, CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName ) )
{
// remove claim
MoveMemory( &CertDbHeaderList[ iHeader ].Claims[ iClaim ],
&CertDbHeaderList[ iHeader ].Claims[ iClaim+1 ],
CertDbHeaderList[ iHeader ].NumClaims - ( iClaim + 1 ) );
CertDbHeaderList[ iHeader ].NumClaims--;
}
else
{
// keep this claim
iClaim++;
}
}
}
RtlReleaseResource( &CertDbHeaderListLock );
}
}
///////////////////////////////////////////////////////////////////////////////
void CertDbLogViolations()
/*++
Routine Description:
Log violations of certificate license agreements to the event log of the
local server.
Arguments:
None.
Return Value:
None.
--*/
{
int iHeader;
int iClaim;
HANDLE hEventLog;
DWORD dwTotalQuantity;
HINSTANCE hDll;
DWORD cch;
LPTSTR pszViolationServerEntryFormat;
LPTSTR pszViolationFormat;
LPTSTR pszViolationServerEntryList;
LPTSTR pszNextViolationServerEntry;
TCHAR szNumLicenses[ 20 ];
TCHAR szMaxLicenses[ 20 ];
TCHAR szCertificateID[ 20 ];
LPTSTR apszSubstStrings[ 5 ];
DWORD cbViolationServerList;
LPTSTR pszViolationServerList;
// get rid of out-dated entries
CertDbPrune();
hDll = LoadLibrary( TEXT( "LLSRPC.DLL" ) );
ASSERT( NULL != hDll );
if ( NULL != hDll )
{
// format for part of logged message that lists server and #licenses
cch = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_FROM_HMODULE,
hDll,
LLS_EVENT_CERT_VIOLATION_SERVER_ENTRY,
GetSystemDefaultLangID(),
(LPVOID) &pszViolationServerEntryFormat,
0,
NULL );
if ( 0 != cch )
{
hEventLog = RegisterEventSource( NULL, TEXT("LicenseService") );
if ( NULL != hEventLog )
{
RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
{
dwTotalQuantity = 0;
// tally the number of licenses claimed against this certificate
for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; iClaim++ )
{
if ( dwTotalQuantity + (DWORD)CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity < dwTotalQuantity )
{
// overflow!
dwTotalQuantity = ULONG_MAX;
break;
}
else
{
// add to tally
dwTotalQuantity += CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity;
}
}
if ( dwTotalQuantity > CertDbHeaderList[ iHeader ].MaxQuantity )
{
// this certificate is in violation
// create message we're going to log
cbViolationServerList = CertDbHeaderList[ iHeader ].NumClaims
* sizeof( TCHAR )
* ( lstrlen( pszViolationServerEntryFormat )
+ 20
+ MAX_COMPUTERNAME_LENGTH );
pszViolationServerList = LocalAlloc( LPTR, cbViolationServerList );
ASSERT( NULL != pszViolationServerList );
if ( NULL != pszViolationServerList )
{
// create an entry for each server in violation, stringing them
// together in pszViolationServerList
pszNextViolationServerEntry = pszViolationServerList;
for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; iClaim++ )
{
_ltow( CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity, szNumLicenses, 10 );
apszSubstStrings[ 0 ] = CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName;
apszSubstStrings[ 1 ] = szNumLicenses;
cch = FormatMessage( FORMAT_MESSAGE_FROM_STRING
| FORMAT_MESSAGE_ARGUMENT_ARRAY,
pszViolationServerEntryFormat,
(DWORD)0,
(DWORD)0,
pszNextViolationServerEntry,
cbViolationServerList - (DWORD)( pszNextViolationServerEntry - pszViolationServerList ),
(va_list *) apszSubstStrings );
ASSERT( 0 != cch );
pszNextViolationServerEntry += lstrlen( pszNextViolationServerEntry );
}
_ultow( CertDbHeaderList[ iHeader ].CertificateID, szCertificateID, 10 );
_ultow( dwTotalQuantity, szNumLicenses, 10 );
_ultow( CertDbHeaderList[ iHeader ].MaxQuantity, szMaxLicenses, 10 );
apszSubstStrings[ 0 ] = CertDbHeaderList[ iHeader ].Product;
apszSubstStrings[ 1 ] = szCertificateID;
apszSubstStrings[ 2 ] = szNumLicenses;
apszSubstStrings[ 3 ] = szMaxLicenses;
apszSubstStrings[ 4 ] = pszViolationServerList;
// log the violation
if ( NULL != hEventLog )
{
ReportEvent( hEventLog,
EVENTLOG_ERROR_TYPE,
0,
LLS_EVENT_CERT_VIOLATION,
NULL,
5,
0,
apszSubstStrings,
NULL );
}
LocalFree( pszViolationServerList );
}
}
}
RtlReleaseResource( &CertDbHeaderListLock );
LocalFree( pszViolationServerEntryFormat );
DeregisterEventSource( hEventLog );
}
}
FreeLibrary( hDll );
}
}
///////////////////////////////////////////////////////////////////////////////
NTSTATUS CertDbPack( LPDWORD pcchProductStrings,
LPTSTR * ppchProductStrings,
LPDWORD pdwNumHeaders,
PREPL_CERT_DB_CERTIFICATE_HEADER_0 * ppHeaders,
LPDWORD pdwNumClaims,
PREPL_CERT_DB_CERTIFICATE_CLAIM_0 * ppClaims )
/*++
Routine Description:
Pack the certificate database into manageable chunks that can be saved or
replicated.
Arguments:
pcchProductStrings (LPDWORD)
On return, holds the size (in characters) of the buffer pointed to by
*ppchProductStrings.
ppchProductStrings (LPTSTR *)
On return, points to the buffer containing the product strings component
of the database.
pdwNumHeaders (LPDWORD)
On return, holds the number of certificate headers in the array pointed
to by *ppHeaders.
ppHeaders (PREPL_CERT_DB_CERTIFICATE_HEADER_0 *)
On return, holds a pointer to the certificate header array component of
the database.
pdwNumClaims (LPDWORD)
On return, holds the number of certificate claims in the array pointed
to by *ppHeaders.
ppClaims (PREPL_CERT_DB_CERTIFICATE_CLAIM_0 *)
On return, holds a pointer to the certificate claim array component of
the database.
Return Value:
STATUS_SUCCESS or STATUS_NO_MEMORY.
--*/
{
NTSTATUS nt = STATUS_SUCCESS;
DWORD cchProductStrings = 0;
LPTSTR pchProductStrings = NULL;
DWORD dwNumHeaders = 0;
PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders = NULL;
DWORD dwNumClaims = 0;
PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims = NULL;
LPTSTR pchNextProductString;
int iHeader;
int iClaim;
CertDbPrune();
CertDbUpdateLocalClaims();
RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
if ( 0 != CertDbHeaderListSize )
{
// how big are all of our strings put together?
// hom many certificate claims are there?
for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
{
cchProductStrings += 1 + lstrlen( CertDbHeaderList[ iHeader ].Product );
dwNumClaims += CertDbHeaderList[ iHeader ].NumClaims;
}
dwNumHeaders = CertDbHeaderListSize;
pchProductStrings = LocalAlloc( LMEM_FIXED, cchProductStrings * sizeof( TCHAR ) );
pHeaders = LocalAlloc( LMEM_FIXED, dwNumHeaders * sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) );
pClaims = LocalAlloc( LMEM_FIXED, dwNumClaims * sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) );
if ( ( NULL == pchProductStrings ) || ( NULL == pHeaders ) || ( NULL == pClaims ) )
{
ASSERT( FALSE );
nt = STATUS_NO_MEMORY;
}
else
{
// pack the product strings
pchNextProductString = pchProductStrings;
for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
{
lstrcpy( pchNextProductString, CertDbHeaderList[ iHeader ].Product );
pchNextProductString += 1 + lstrlen( pchNextProductString );
}
// now pack away the rest of our structures
iClaim = 0;
for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
{
pHeaders[ iHeader ].CertificateID = CertDbHeaderList[ iHeader ].CertificateID;
pHeaders[ iHeader ].MaxQuantity = CertDbHeaderList[ iHeader ].MaxQuantity;
pHeaders[ iHeader ].ExpirationDate = CertDbHeaderList[ iHeader ].ExpirationDate;
pHeaders[ iHeader ].NumClaims = CertDbHeaderList[ iHeader ].NumClaims;
if ( CertDbHeaderList[ iHeader ].NumClaims )
{
memcpy( &pClaims[ iClaim ],
CertDbHeaderList[ iHeader ].Claims,
CertDbHeaderList[ iHeader ].NumClaims * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ) );
iClaim += CertDbHeaderList[ iHeader ].NumClaims;
}
}
// all done!
nt = STATUS_SUCCESS;
}
}
if ( STATUS_SUCCESS == nt )
{
*pcchProductStrings = cchProductStrings;
*ppchProductStrings = pchProductStrings;
*pdwNumHeaders = dwNumHeaders;
*ppHeaders = pHeaders;
*pdwNumClaims = dwNumClaims;
*ppClaims = pClaims;
}
else
{
if ( NULL != pchProductStrings ) LocalFree( pchProductStrings );
if ( NULL != pHeaders ) LocalFree( pHeaders );
if ( NULL != pClaims ) LocalFree( pClaims );
}
RtlReleaseResource( &CertDbHeaderListLock );
return nt;
}
///////////////////////////////////////////////////////////////////////////////
NTSTATUS CertDbUnpack( DWORD cchProductStrings,
LPTSTR pchProductStrings,
DWORD dwNumHeaders,
PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders,
DWORD dwNumClaims,
PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims,
BOOL bReplicated )
/*++
Routine Description:
Pack the certificate database into manageable chunks that can be saved or
replicated.
Arguments:
cchProductStrings (DWORD)
The size (in characters) of the buffer pointed to by pchProductStrings.
pchProductStrings (LPTSTR)
The buffer containing the product strings component of the database.
dwNumHeaders (DWORD)
The number of certificate headers in the array pointed to by pHeaders.
pHeaders (PREPL_CERT_DB_CERTIFICATE_HEADER_0)
The certificate header array component of the database.
dwNumClaims (DWORD)
The number of certificate claims in the array pointed to by pHeaders.
pClaims (PREPL_CERT_DB_CERTIFICATE_CLAIM_0)
The certificate claim array component of the database.
bReplicated (BOOL)
Indicates whether this information was replicated. This is used to
determine the time at which this information will expire.
Return Value:
STATUS_SUCCESS or NTSTATUS error code.
--*/
{
NTSTATUS nt = STATUS_SUCCESS;
LPTSTR pchNextProductString;
LPBYTE pb;
int iHeader;
int iClaim;
int iClaimBase;
LLS_LICENSE_INFO_1 lic;
TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
BOOL ok;
ok = GetComputerName( szComputerName, &cchComputerName );
ASSERT( ok );
if ( !ok )
{
// in this case, we'll just add in the local entries, too
// under normal circumstances (i.e., as long as the cert db isn't corrupt),
// this is harmless and is preferrable to failing to unpack
*szComputerName = TEXT( '\0' );
}
RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
pchNextProductString = pchProductStrings;
// these fields are irrelevant!
lic.Date = 0;
lic.Admin = NULL;
lic.Comment = NULL;
lic.Vendor = NULL;
lic.Source = NULL;
lic.AllowedModes = 0;
iClaimBase = 0;
for ( iHeader=0; (DWORD)iHeader < dwNumHeaders; iHeader++ )
{
if ( 0 != pHeaders[ iHeader ].NumClaims )
{
// certificate-specific fields
lic.Product = pchNextProductString;
lic.CertificateID = pHeaders[ iHeader ].CertificateID;
lic.MaxQuantity = pHeaders[ iHeader ].MaxQuantity;
lic.ExpirationDate = pHeaders[ iHeader ].ExpirationDate;
for ( iClaim=0; (DWORD)iClaim < pHeaders[ iHeader ].NumClaims; iClaim++ )
{
if ( lstrcmpi( szComputerName, pClaims[ iClaimBase + iClaim ].ServerName ) )
{
// not the local server
// claim-specific field
lic.Quantity = pClaims[ iClaimBase + iClaim ].Quantity;
nt = CertDbClaimEnter( pClaims[ iClaimBase + iClaim ].ServerName, &lic, TRUE, bReplicated ? 0 : pClaims[ iClaimBase + iClaim ].ReplicationDate );
ASSERT( STATUS_SUCCESS == nt );
// even if we encounter an error, go ahead and unpack the rest of the records
}
}
iClaimBase += pHeaders[ iHeader ].NumClaims;
}
pchNextProductString += 1 + lstrlen( pchNextProductString );
}
RtlReleaseResource( &CertDbHeaderListLock );
return nt;
}
///////////////////////////////////////////////////////////////////////////////
NTSTATUS CertDbSave()
/*++
Routine Description:
Save the certificate database.
Arguments:
None.
Return Value:
STATUS_SUCCESS, Windows error, or NTSTATUS error code.
--*/
{
NTSTATUS nt;
LLS_CERT_DB_FILE_HEADER FileHeader;
DWORD cchProductStrings = 0;
LPTSTR pchProductStrings = NULL;
DWORD dwNumHeaders = 0;
PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders = NULL;
DWORD dwNumClaims = 0;
PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims = NULL;
DWORD dwBytesWritten;
BOOL ok;
nt = CertDbPack( &cchProductStrings, &pchProductStrings, &dwNumHeaders, &pHeaders, &dwNumClaims, &pClaims );
if ( STATUS_SUCCESS == nt )
{
if ( dwNumHeaders )
{
nt = EBlock( pchProductStrings, cchProductStrings * sizeof( TCHAR ) );
if ( STATUS_SUCCESS == nt )
{
nt = EBlock( pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * dwNumHeaders );
if ( STATUS_SUCCESS == nt )
{
nt = EBlock( pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * dwNumClaims );
if ( STATUS_SUCCESS == nt )
{
if ( NULL != CertDbFile )
{
CloseHandle( CertDbFile );
}
CertDbFile = LlsFileInit( CertDbFileName, LLS_CERT_DB_FILE_VERSION, sizeof( LLS_CERT_DB_FILE_HEADER ) );
if ( NULL == CertDbFile )
{
nt = GetLastError();
}
else
{
FileHeader.NumCertificates = dwNumHeaders;
FileHeader.ProductStringSize = cchProductStrings;
FileHeader.NumClaims = dwNumClaims;
ok = WriteFile( CertDbFile, &FileHeader, sizeof( FileHeader ), &dwBytesWritten, NULL );
if ( ok )
{
ok = WriteFile( CertDbFile, pchProductStrings, FileHeader.ProductStringSize * sizeof( TCHAR ), &dwBytesWritten, NULL );
if ( ok )
{
ok = WriteFile( CertDbFile, pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates, &dwBytesWritten, NULL );
if ( ok )
{
ok = WriteFile( CertDbFile, pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims, &dwBytesWritten, NULL );
}
}
}
if ( !ok )
{
nt = GetLastError();
}
}
}
}
}
LocalFree( pchProductStrings );
LocalFree( pHeaders );
LocalFree( pClaims );
}
}
if ( STATUS_SUCCESS != nt )
{
LogEvent( LLS_EVENT_SAVE_CERT_DB, 0, NULL, nt );
}
return nt;
}
///////////////////////////////////////////////////////////////////////////////
NTSTATUS CertDbLoad()
/*++
Routine Description:
Load the certificate database.
Arguments:
None.
Return Value:
STATUS_SUCCESS, Windows error, or NTSTATUS error code.
--*/
{
NTSTATUS nt = STATUS_SUCCESS;
DWORD dwVersion;
DWORD dwDataSize;
LLS_CERT_DB_FILE_HEADER FileHeader;
LPTSTR pchProductStrings = NULL;
PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders = NULL;
PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims = NULL;
DWORD dwBytesRead;
BOOL ok;
if ( NULL != CertDbFile )
{
CloseHandle( CertDbFile );
CertDbFile = NULL;
}
if ( FileExists( CertDbFileName ) )
{
CertDbFile = LlsFileCheck( CertDbFileName, &dwVersion, &dwDataSize );
if ( NULL == CertDbFile )
{
nt = GetLastError();
}
else if ( ( LLS_CERT_DB_FILE_VERSION != dwVersion )
|| ( sizeof( FileHeader ) != dwDataSize ) )
{
nt = STATUS_FILE_INVALID;
}
else
{
ok = ReadFile( CertDbFile, &FileHeader, sizeof( FileHeader ), &dwBytesRead, NULL );
if ( !ok )
{
nt = GetLastError();
}
else if ( FileHeader.NumCertificates )
{
pchProductStrings = LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * FileHeader.ProductStringSize );
pHeaders = LocalAlloc( LMEM_FIXED, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates );
pClaims = LocalAlloc( LMEM_FIXED, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims );
if ( ( NULL == pchProductStrings ) || ( NULL == pHeaders ) || ( NULL == pClaims ) )
{
ASSERT( FALSE );
nt = STATUS_NO_MEMORY;
}
else
{
ok = ReadFile( CertDbFile, pchProductStrings, FileHeader.ProductStringSize * sizeof( TCHAR ), &dwBytesRead, NULL );
if ( ok )
{
ok = ReadFile( CertDbFile, pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates, &dwBytesRead, NULL );
if ( ok )
{
ok = ReadFile( CertDbFile, pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims, &dwBytesRead, NULL );
}
}
if ( !ok )
{
nt = GetLastError();
}
else
{
nt = DeBlock( pchProductStrings, sizeof( TCHAR ) * FileHeader.ProductStringSize );
if ( STATUS_SUCCESS == nt )
{
nt = DeBlock( pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates );
if ( STATUS_SUCCESS == nt )
{
nt = DeBlock( pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims );
if ( STATUS_SUCCESS == nt )
{
nt = CertDbUnpack( FileHeader.ProductStringSize,
pchProductStrings,
FileHeader.NumCertificates,
pHeaders,
FileHeader.NumClaims,
pClaims,
FALSE );
}
}
}
}
}
}
}
}
if ( NULL != pchProductStrings ) LocalFree( pchProductStrings );
if ( NULL != pHeaders ) LocalFree( pHeaders );
if ( NULL != pClaims ) LocalFree( pClaims );
if ( STATUS_SUCCESS != nt )
{
LogEvent( LLS_EVENT_LOAD_CERT_DB, 0, NULL, nt );
}
else
{
CertDbPrune();
}
return nt;
}
///////////////////////////////////////////////////////////////////////////////
NTSTATUS CertDbInit()
/*++
Routine Description:
Initialize the certificate database.
Arguments:
None.
Return Value:
STATUS_SUCCESS.
--*/
{
CertDbFile = NULL;
try
{
RtlInitializeResource( &CertDbHeaderListLock );
} except(EXCEPTION_EXECUTE_HANDLER ) {
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
void CertDbUpdateLocalClaims()
/*++
Routine Description:
Synchronize the certificate database with the purchase history.
Arguments:
None.
Return Value:
None.
--*/
{
DWORD dwPurchaseNdx;
LLS_LICENSE_INFO_1 lic;
PLICENSE_PURCHASE_RECORD pPurchase;
RtlAcquireResourceExclusive( &LicenseListLock, TRUE );
RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
// first dump all current entries for the local server
CertDbRemoveLocalClaims();
// these fields are irrelevant!
lic.Date = 0;
lic.Admin = NULL;
lic.Comment = NULL;
lic.Source = NULL;
lic.Vendor = NULL;
lic.AllowedModes = 0;
// add in all secure purchases
for ( dwPurchaseNdx = 0; dwPurchaseNdx < PurchaseListSize; dwPurchaseNdx++ )
{
pPurchase = &PurchaseList[ dwPurchaseNdx ];
if ( 0 != pPurchase->CertificateID )
{
lic.Product = ( pPurchase->AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
? pPurchase->Service->ServiceName
: pPurchase->PerServerService->ServiceName;
lic.CertificateID = pPurchase->CertificateID;
lic.MaxQuantity = pPurchase->MaxQuantity;
lic.ExpirationDate = pPurchase->ExpirationDate;
lic.Quantity = pPurchase->NumberLicenses;
CertDbClaimEnter( NULL, &lic, FALSE, 0 );
}
}
RtlReleaseResource( &CertDbHeaderListLock );
RtlReleaseResource( &LicenseListLock );
}
///////////////////////////////////////////////////////////////////////////////
NTSTATUS CertDbClaimsGet( PLLS_LICENSE_INFO_1 pLicense,
LPDWORD pdwNumClaims,
PLLS_CERTIFICATE_CLAIM_INFO_0 * ppTargets )
/*++
Routine Description:
Retrieve a list of all servers with licenses installed from a given
certificate and the number of licenses installed on each.
Arguments:
pLicense (PLLS_LICENSE_INFO_1)
License describing the certificate for which the claims are sought.
pdwNumClaims (LPDWORD)
On return, holds the number of claims in the array pointed to by
*ppTargets.
ppTargets (PLLS_CERTIFICATE_CLAIM_INFO_0 *)
On return, holds an array describing all claims made on this
certificate.
Return Value:
STATUS_SUCCESS
STATUS_NOT_FOUND
STATUS_NO_MEMORY
--*/
{
NTSTATUS nt;
PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
int iClaim;
// is the certificate in the db?
pHeader = CertDbHeaderFind( pLicense );
if ( NULL == pHeader )
{
// not here!
nt = STATUS_NOT_FOUND;
}
else
{
*ppTargets = MIDL_user_allocate( pHeader->NumClaims * sizeof( LLS_CERTIFICATE_CLAIM_INFO_0 ) );
if ( NULL == *ppTargets )
{
nt = STATUS_NO_MEMORY;
}
else
{
*pdwNumClaims = pHeader->NumClaims;
for ( iClaim=0; (DWORD)iClaim < pHeader->NumClaims; iClaim++ )
{
lstrcpy( (*ppTargets)[ iClaim ].ServerName, pHeader->Claims[ iClaim ].ServerName );
(*ppTargets)[ iClaim ].Quantity = pHeader->Claims[ iClaim ].Quantity;
}
nt = STATUS_SUCCESS;
}
}
return nt;
}
#if DBG
/////////////////////////////////////////////////////////////////////////
void CertDbDebugDump()
/*++
Routine Description:
Dump contents of certificate database to debug console.
Arguments:
None.
Return Value:
None.
--*/
{
int iHeader;
int iClaim;
RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
{
dprintf( TEXT("\n(%3d) Product : %s\n"), iHeader, CertDbHeaderList[ iHeader ].Product );
dprintf( TEXT(" CertificateID : %d\n"), CertDbHeaderList[ iHeader ].CertificateID );
dprintf( TEXT(" MaxQuantity : %d\n"), CertDbHeaderList[ iHeader ].MaxQuantity );
dprintf( TEXT(" ExpirationDate : %s\n"), TimeToString( CertDbHeaderList[ iHeader ].ExpirationDate ) );
for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; iClaim++ )
{
dprintf( TEXT("\n (%3d) ServerName : %s\n"), iClaim, CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName );
dprintf( TEXT(" ReplicationDate : %s\n"), TimeToString( CertDbHeaderList[ iHeader ].Claims[ iClaim ].ReplicationDate ) );
dprintf( TEXT(" Quantity : %d\n"), CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity );
}
}
RtlReleaseResource( &CertDbHeaderListLock );
} // CertDbDebugDump
#endif